From 1a9d8c750be83e6abb65769d312361fe9742de02 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 24 Jan 2023 22:39:13 +0000 Subject: [PATCH 001/225] gh-98831: rewrite pattern matching opcodes in the instruction definition DSL (#101287) --- Python/bytecodes.c | 50 ++++++---------------- Python/generated_cases.c.h | 57 +++++++++++++------------ Python/opcode_metadata.h | 8 ++-- Tools/cases_generator/generate_cases.py | 8 ++-- Tools/cases_generator/test_generator.py | 14 ++++++ 5 files changed, 64 insertions(+), 73 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index ac54791d67e439..d3e242b81e608d 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2092,61 +2092,37 @@ dummy_func( PUSH(len_o); } - // stack effect: (__0, __1 -- ) - inst(MATCH_CLASS) { + inst(MATCH_CLASS, (subject, type, names -- attrs)) { // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or // None on failure. - PyObject *names = POP(); - PyObject *type = POP(); - PyObject *subject = TOP(); assert(PyTuple_CheckExact(names)); - PyObject *attrs = match_class(tstate, subject, type, oparg, names); - Py_DECREF(names); - Py_DECREF(type); + attrs = match_class(tstate, subject, type, oparg, names); + DECREF_INPUTS(); if (attrs) { - // Success! - assert(PyTuple_CheckExact(attrs)); - SET_TOP(attrs); - } - else if (_PyErr_Occurred(tstate)) { - // Error! - goto error; + assert(PyTuple_CheckExact(attrs)); // Success! } else { - // Failure! - SET_TOP(Py_NewRef(Py_None)); + ERROR_IF(_PyErr_Occurred(tstate), error); // Error! + attrs = Py_NewRef(Py_None); // Failure! } - Py_DECREF(subject); } - // stack effect: ( -- __0) - inst(MATCH_MAPPING) { - PyObject *subject = TOP(); + inst(MATCH_MAPPING, (subject -- subject, res)) { int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; - PyObject *res = match ? Py_True : Py_False; - PUSH(Py_NewRef(res)); + res = Py_NewRef(match ? Py_True : Py_False); PREDICT(POP_JUMP_IF_FALSE); } - // stack effect: ( -- __0) - inst(MATCH_SEQUENCE) { - PyObject *subject = TOP(); + inst(MATCH_SEQUENCE, (subject -- subject, res)) { int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; - PyObject *res = match ? Py_True : Py_False; - PUSH(Py_NewRef(res)); + res = Py_NewRef(match ? Py_True : Py_False); PREDICT(POP_JUMP_IF_FALSE); } - // stack effect: ( -- __0) - inst(MATCH_KEYS) { + inst(MATCH_KEYS, (subject, keys -- subject, keys, values_or_none)) { // On successful match, PUSH(values). Otherwise, PUSH(None). - PyObject *keys = TOP(); - PyObject *subject = SECOND(); - PyObject *values_or_none = match_keys(tstate, subject, keys); - if (values_or_none == NULL) { - goto error; - } - PUSH(values_or_none); + values_or_none = match_keys(tstate, subject, keys); + ERROR_IF(values_or_none == NULL, error); } // stack effect: ( -- ) diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 5dcc8eeec19f89..7d3396ad6bdec3 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2479,59 +2479,60 @@ } TARGET(MATCH_CLASS) { + PyObject *names = PEEK(1); + PyObject *type = PEEK(2); + PyObject *subject = PEEK(3); + PyObject *attrs; // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or // None on failure. - PyObject *names = POP(); - PyObject *type = POP(); - PyObject *subject = TOP(); assert(PyTuple_CheckExact(names)); - PyObject *attrs = match_class(tstate, subject, type, oparg, names); - Py_DECREF(names); + attrs = match_class(tstate, subject, type, oparg, names); + Py_DECREF(subject); Py_DECREF(type); + Py_DECREF(names); if (attrs) { - // Success! - assert(PyTuple_CheckExact(attrs)); - SET_TOP(attrs); - } - else if (_PyErr_Occurred(tstate)) { - // Error! - goto error; + assert(PyTuple_CheckExact(attrs)); // Success! } else { - // Failure! - SET_TOP(Py_NewRef(Py_None)); + if (_PyErr_Occurred(tstate)) goto pop_3_error; + attrs = Py_NewRef(Py_None); // Failure! } - Py_DECREF(subject); + STACK_SHRINK(2); + POKE(1, attrs); DISPATCH(); } TARGET(MATCH_MAPPING) { - PyObject *subject = TOP(); + PyObject *subject = PEEK(1); + PyObject *res; int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; - PyObject *res = match ? Py_True : Py_False; - PUSH(Py_NewRef(res)); + res = Py_NewRef(match ? Py_True : Py_False); + STACK_GROW(1); + POKE(1, res); PREDICT(POP_JUMP_IF_FALSE); DISPATCH(); } TARGET(MATCH_SEQUENCE) { - PyObject *subject = TOP(); + PyObject *subject = PEEK(1); + PyObject *res; int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; - PyObject *res = match ? Py_True : Py_False; - PUSH(Py_NewRef(res)); + res = Py_NewRef(match ? Py_True : Py_False); + STACK_GROW(1); + POKE(1, res); PREDICT(POP_JUMP_IF_FALSE); DISPATCH(); } TARGET(MATCH_KEYS) { + PyObject *keys = PEEK(1); + PyObject *subject = PEEK(2); + PyObject *values_or_none; // On successful match, PUSH(values). Otherwise, PUSH(None). - PyObject *keys = TOP(); - PyObject *subject = SECOND(); - PyObject *values_or_none = match_keys(tstate, subject, keys); - if (values_or_none == NULL) { - goto error; - } - PUSH(values_or_none); + values_or_none = match_keys(tstate, subject, keys); + if (values_or_none == NULL) goto error; + STACK_GROW(1); + POKE(1, values_or_none); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 1fb0acceb511c7..f9c640187afe09 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -133,10 +133,10 @@ static const struct { [JUMP_IF_TRUE_OR_POP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [JUMP_BACKWARD_NO_INTERRUPT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [GET_LEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [MATCH_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [MATCH_MAPPING] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [MATCH_SEQUENCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [MATCH_KEYS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [MATCH_CLASS] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MATCH_MAPPING] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [MATCH_SEQUENCE] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [MATCH_KEYS] = { 2, 3, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_YIELD_FROM_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [FOR_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 90f101adceeb47..429c9d34d60601 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -26,7 +26,7 @@ ) BEGIN_MARKER = "// BEGIN BYTECODES //" END_MARKER = "// END BYTECODES //" -RE_PREDICTED = r"^\s*(?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);\s*$" +RE_PREDICTED = r"^\s*(?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);\s*(?://.*)?$" UNUSED = "unused" BITS_PER_CODE_UNIT = 16 @@ -354,7 +354,7 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None assert dedent <= 0 extra = " " * -dedent for line in self.block_text: - if m := re.match(r"(\s*)ERROR_IF\((.+), (\w+)\);\s*$", line): + if m := re.match(r"(\s*)ERROR_IF\((.+), (\w+)\);\s*(?://.*)?$", line): space, cond, label = m.groups() # ERROR_IF() must pop the inputs from the stack. # The code block is responsible for DECREF()ing them. @@ -378,7 +378,7 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None ) else: out.write_raw(f"{extra}{space}if ({cond}) goto {label};\n") - elif m := re.match(r"(\s*)DECREF_INPUTS\(\);\s*$", line): + elif m := re.match(r"(\s*)DECREF_INPUTS\(\);\s*(?://.*)?$", line): if not self.register: space = m.group(1) for ieff in self.input_effects: @@ -964,7 +964,7 @@ def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]: # Separate PREDICT(...) macros from end predictions: list[str] = [] - while blocklines and (m := re.match(r"^\s*PREDICT\((\w+)\);\s*$", blocklines[-1])): + while blocklines and (m := re.match(r"^\s*PREDICT\((\w+)\);\s*(?://.*)?$", blocklines[-1])): predictions.insert(0, m.group(1)) blocklines.pop() diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index ae0c1e2f97c35e..c59aae878cfa70 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -215,6 +215,20 @@ def test_error_if_plain(): """ run_cases_test(input, output) +def test_error_if_plain_with_comment(): + input = """ + inst(OP, (--)) { + ERROR_IF(cond, label); // Comment is ok + } + """ + output = """ + TARGET(OP) { + if (cond) goto label; + DISPATCH(); + } + """ + run_cases_test(input, output) + def test_error_if_pop(): input = """ inst(OP, (left, right -- res)) { From 498598e8c2d64232d26c075de87c513415176bbf Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 24 Jan 2023 14:58:18 -0800 Subject: [PATCH 002/225] Fix some comments in ceval.c and fix lltrace output (#101297) The comment at the top was rather outdated. :-) Also added a note about the dangers of dump_stack(). --- Python/ceval.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 95eb99b453345b..2e6fed580dede4 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1,10 +1,5 @@ /* Execute compiled code */ -/* XXX TO DO: - XXX speed up searching for keywords by using a dictionary - XXX document it! - */ - #define _PY_INTERPRETER #include "Python.h" @@ -133,6 +128,9 @@ lltrace_instruction(_PyInterpreterFrame *frame, PyObject **stack_pointer, _Py_CODEUNIT *next_instr) { + /* This dump_stack() operation is risky, since the repr() of some + objects enters the interpreter recursively. It is also slow. + So you might want to comment it out. */ dump_stack(frame, stack_pointer); int oparg = _Py_OPARG(*next_instr); int opcode = _Py_OPCODE(*next_instr); @@ -155,7 +153,7 @@ lltrace_resume_frame(_PyInterpreterFrame *frame) fobj == NULL || !PyFunction_Check(fobj) ) { - printf("\nResuming frame."); + printf("\nResuming frame.\n"); return; } PyFunctionObject *f = (PyFunctionObject *)fobj; From 395871e511d6d9634399598cae8f0c35fe72d79b Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 25 Jan 2023 08:55:46 -0800 Subject: [PATCH 003/225] GH-98831: Elaborate some cases_generator tests (#101299) * Make macro test more elaborate * Add test for 'register inst()' --- Tools/cases_generator/test_generator.py | 54 +++++++++++++++++++------ 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index c59aae878cfa70..cf58e6aaf2b37c 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -333,20 +333,21 @@ def test_super_instruction(): def test_macro_instruction(): input = """ - inst(OP1, (counter/1, arg --)) { - op1(); + inst(OP1, (counter/1, arg1 -- interim)) { + interim = op1(arg1); } - op(OP2, (extra/2, arg --)) { - op2(); + op(OP2, (extra/2, arg2, interim -- res)) { + res = op2(arg2, interim); } macro(OP) = OP1 + cache/2 + OP2; """ output = """ TARGET(OP1) { - PyObject *arg = PEEK(1); + PyObject *arg1 = PEEK(1); + PyObject *interim; uint16_t counter = read_u16(&next_instr[0].cache); - op1(); - STACK_SHRINK(1); + interim = op1(arg1); + POKE(1, interim); JUMPBY(1); DISPATCH(); } @@ -355,17 +356,24 @@ def test_macro_instruction(): PyObject *_tmp_1 = PEEK(1); PyObject *_tmp_2 = PEEK(2); { - PyObject *arg = _tmp_1; - uint16_t counter = read_u16(&next_instr[0].cache); - op1(); + PyObject *arg1 = _tmp_1; + PyObject *interim; + uint16_t counter = re + ad_u16(&next_instr[0].cache); + interim = op1(arg1); + _tmp_1 = interim; } { - PyObject *arg = _tmp_2; + PyObject *interim = _tmp_1; + PyObject *arg2 = _tmp_2; + PyObject *res; uint32_t extra = read_u32(&next_instr[3].cache); - op2(); + res = op2(arg2, interim); + _tmp_2 = res; } JUMPBY(5); - STACK_SHRINK(2); + STACK_SHRINK(1); + POKE(1, _tmp_2); DISPATCH(); } """ @@ -448,3 +456,23 @@ def test_array_error_if(): } """ run_cases_test(input, output) + +def test_register(): + input = """ + register inst(OP, (counter/1, left, right -- result)) { + result = op(left, right); + } + """ + output = """ + TARGET(OP) { + PyObject *left = REG(oparg1); + PyObject *right = REG(oparg2); + PyObject *result; + uint16_t counter = read_u16(&next_instr[0].cache); + result = op(left, right); + Py_XSETREF(REG(oparg3), result); + JUMPBY(1); + DISPATCH(); + } + """ + run_cases_test(input, output) From 14177128126f688856099c8ec138ac4693a8cf85 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 25 Jan 2023 09:28:14 -0800 Subject: [PATCH 004/225] Add advice how to freeze fewer modules (#101298) (And fix a bug that only occurs when you follow the advice.) --- Tools/build/freeze_modules.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Tools/build/freeze_modules.py b/Tools/build/freeze_modules.py index 810224b28f2faa..ee4dd2f8682ba4 100644 --- a/Tools/build/freeze_modules.py +++ b/Tools/build/freeze_modules.py @@ -32,6 +32,9 @@ OS_PATH = 'ntpath' if os.name == 'nt' else 'posixpath' # These are modules that get frozen. +# If you're debugging new bytecode instructions, +# you can delete all sections except 'import system'. +# This also speeds up building somewhat. TESTS_SECTION = 'Test module' FROZEN = [ # See parse_frozen_spec() for the format. @@ -45,6 +48,7 @@ # on a builtin zip file instead of a filesystem. 'zipimport', ]), + # (You can delete entries from here down to the end of the list.) ('stdlib - startup, without site (python -S)', [ 'abc', 'codecs', @@ -80,6 +84,7 @@ '<__phello__.**.*>', f'frozen_only : __hello_only__ = {FROZEN_ONLY}', ]), + # (End of stuff you could delete.) ] BOOTSTRAP = { 'importlib._bootstrap', @@ -520,7 +525,7 @@ def regen_frozen(modules, frozen_modules: bool): for lines in (bootstraplines, stdliblines, testlines): # TODO: Is this necessary any more? - if not lines[0]: + if lines and not lines[0]: del lines[0] for i, line in enumerate(lines): if line: From 952a1d9cc970508b280af475c0be1809692f0c76 Mon Sep 17 00:00:00 2001 From: achhina Date: Wed, 25 Jan 2023 12:39:42 -0500 Subject: [PATCH 005/225] GH-88597: Rename uuid's new CLI args to be in line with uuidgen. (#101248) this way they match an existing uuidgen command line tool. --- Doc/library/uuid.rst | 23 ++++++++++++----------- Lib/test/test_uuid.py | 8 ++++---- Lib/uuid.py | 28 ++++++++++++++++------------ 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/Doc/library/uuid.rst b/Doc/library/uuid.rst index 804884dee0a2eb..38b6434f467fd6 100644 --- a/Doc/library/uuid.rst +++ b/Doc/library/uuid.rst @@ -272,7 +272,7 @@ The :mod:`uuid` module can be executed as a script from the command line. .. code-block:: sh - python -m uuid [-h] [-u {uuid1,uuid3,uuid4,uuid5}] [-ns NAMESPACE] [-n NAME] + python -m uuid [-h] [-u {uuid1,uuid3,uuid4,uuid5}] [-n NAMESPACE] [-N NAME] The following options are accepted: @@ -288,13 +288,14 @@ The following options are accepted: Specify the function name to use to generate the uuid. By default :func:`uuid4` is used. -.. cmdoption:: -ns +.. cmdoption:: -n --namespace - The namespace used as part of generating the uuid. Only required for - :func:`uuid3` / :func:`uuid5` functions. + The namespace is a ``UUID``, or ``@ns`` where ``ns`` is a well-known predefined UUID + addressed by namespace name. Such as ``@dns``, ``@url``, ``@oid``, and ``@x500``. + Only required for :func:`uuid3` / :func:`uuid5` functions. -.. cmdoption:: -n +.. cmdoption:: -N --name The name used as part of generating the uuid. Only required for @@ -351,12 +352,12 @@ Here are some examples of typical usage of the :mod:`uuid` command line interfac .. code-block:: shell - # generate a random uuid - by default uuid4() is used - $ python -m uuid + # generate a random uuid - by default uuid4() is used + $ python -m uuid - # generate a uuid using uuid1() - $ python -m uuid -u uuid1 + # generate a uuid using uuid1() + $ python -m uuid -u uuid1 - # generate a uuid using uuid5 - $ python -m uuid -u uuid5 -ns NAMESPACE_URL -n example.com + # generate a uuid using uuid5 + $ python -m uuid -u uuid5 -n @url -N example.com diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index 61ae2567c90d2a..b2c229cd634e31 100755 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -675,7 +675,7 @@ def test_uuid_weakref(self): weak = weakref.ref(strong) self.assertIs(strong, weak()) - @mock.patch.object(sys, "argv", ["", "-u", "uuid3", "-ns", "NAMESPACE_DNS"]) + @mock.patch.object(sys, "argv", ["", "-u", "uuid3", "-n", "@dns"]) def test_cli_namespace_required_for_uuid3(self): with self.assertRaises(SystemExit) as cm: self.uuid.main() @@ -683,7 +683,7 @@ def test_cli_namespace_required_for_uuid3(self): # Check that exception code is the same as argparse.ArgumentParser.error self.assertEqual(cm.exception.code, 2) - @mock.patch.object(sys, "argv", ["", "-u", "uuid3", "-n", "python.org"]) + @mock.patch.object(sys, "argv", ["", "-u", "uuid3", "-N", "python.org"]) def test_cli_name_required_for_uuid3(self): with self.assertRaises(SystemExit) as cm: self.uuid.main() @@ -705,7 +705,7 @@ def test_cli_uuid4_outputted_with_no_args(self): self.assertEqual(uuid_output.version, 4) @mock.patch.object(sys, "argv", - ["", "-u", "uuid3", "-ns", "NAMESPACE_DNS", "-n", "python.org"]) + ["", "-u", "uuid3", "-n", "@dns", "-N", "python.org"]) def test_cli_uuid3_ouputted_with_valid_namespace_and_name(self): stdout = io.StringIO() with contextlib.redirect_stdout(stdout): @@ -719,7 +719,7 @@ def test_cli_uuid3_ouputted_with_valid_namespace_and_name(self): self.assertEqual(uuid_output.version, 3) @mock.patch.object(sys, "argv", - ["", "-u", "uuid5", "-ns", "NAMESPACE_DNS", "-n", "python.org"]) + ["", "-u", "uuid5", "-n", "@dns", "-N", "python.org"]) def test_cli_uuid5_ouputted_with_valid_namespace_and_name(self): stdout = io.StringIO() with contextlib.redirect_stdout(stdout): diff --git a/Lib/uuid.py b/Lib/uuid.py index 2904b9c4af6405..1c5578bf1f05c2 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -731,16 +731,18 @@ def uuid5(namespace, name): def main(): """Run the uuid command line interface.""" - uuid_funcs = {"uuid1": uuid1, - "uuid3": uuid3, - "uuid4": uuid4, - "uuid5": uuid5} + uuid_funcs = { + "uuid1": uuid1, + "uuid3": uuid3, + "uuid4": uuid4, + "uuid5": uuid5 + } uuid_namespace_funcs = ("uuid3", "uuid5") namespaces = { - "NAMESPACE_DNS": NAMESPACE_DNS, - "NAMESPACE_URL": NAMESPACE_URL, - "NAMESPACE_OID": NAMESPACE_OID, - "NAMESPACE_X500": NAMESPACE_X500 + "@dns": NAMESPACE_DNS, + "@url": NAMESPACE_URL, + "@oid": NAMESPACE_OID, + "@x500": NAMESPACE_X500 } import argparse @@ -748,11 +750,13 @@ def main(): description="Generates a uuid using the selected uuid function.") parser.add_argument("-u", "--uuid", choices=uuid_funcs.keys(), default="uuid4", help="The function to use to generate the uuid. " - "By default uuid4 function is used.") - parser.add_argument("-ns", "--namespace", - help="The namespace used as part of generating the uuid. " + "By default uuid4 function is used.") + parser.add_argument("-n", "--namespace", + help="The namespace is a UUID, or '@ns' where 'ns' is a " + "well-known predefined UUID addressed by namespace name. " + "Such as @dns, @url, @oid, and @x500. " "Only required for uuid3/uuid5 functions.") - parser.add_argument("-n", "--name", + parser.add_argument("-N", "--name", help="The name used as part of generating the uuid. " "Only required for uuid3/uuid5 functions.") From a178ba82bfe2f2fb6f6ff0e67cb734fd7c4321e3 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 25 Jan 2023 12:01:01 -0800 Subject: [PATCH 006/225] gh-101326: Fix regression when passing None to FutureIter.throw (#101327) --- Lib/test/test_asyncio/test_futures.py | 2 ++ .../Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst | 1 + Modules/_asynciomodule.c | 7 ++++++- 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index 56b0b864de2ddf..2184b2091f84ee 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -612,6 +612,8 @@ def test_future_iter_throw(self): Exception, Exception("elephant"), 32) self.assertRaises(TypeError, fi.throw, Exception("elephant"), Exception("elephant")) + # https://github.com/python/cpython/issues/101326 + self.assertRaises(ValueError, fi.throw, ValueError, None, None) self.assertRaises(TypeError, fi.throw, list) def test_future_del_collect(self): diff --git a/Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst b/Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst new file mode 100644 index 00000000000000..54b69b9430910d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst @@ -0,0 +1 @@ +Fix regression when passing ``None`` as second or third argument to ``FutureIter.throw``. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 6fe4ca46947526..055dded05431df 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1694,7 +1694,12 @@ FutureIter_throw(futureiterobject *self, PyObject *const *args, Py_ssize_t nargs val = args[1]; } - if (tb != NULL && !PyTraceBack_Check(tb)) { + if (val == Py_None) { + val = NULL; + } + if (tb == Py_None ) { + tb = NULL; + } else if (tb != NULL && !PyTraceBack_Check(tb)) { PyErr_SetString(PyExc_TypeError, "throw() third argument must be a traceback"); return NULL; } From 19f90d6b97120eafe6dc6689d5a946f50c0c8ef8 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Wed, 25 Jan 2023 20:41:03 +0000 Subject: [PATCH 007/225] gh-98831: add variable stack effect support to cases generator (#101309) --- Python/compile.c | 12 +- Python/opcode_metadata.h | 1028 +++++++++++++++++++---- Tools/cases_generator/generate_cases.py | 78 +- 3 files changed, 924 insertions(+), 194 deletions(-) diff --git a/Python/compile.c b/Python/compile.c index 9fc997cdf525e9..c31f08c0a1797b 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -36,7 +36,7 @@ #include "pycore_pymem.h" // _PyMem_IsPtrFreed() #include "pycore_symtable.h" // PySTEntryObject -#include "opcode_metadata.h" // _PyOpcode_opcode_metadata +#include "opcode_metadata.h" // _PyOpcode_opcode_metadata, _PyOpcode_num_popped/pushed #define DEFAULT_BLOCK_SIZE 16 @@ -8651,13 +8651,15 @@ no_redundant_jumps(cfg_builder *g) { static bool opcode_metadata_is_sane(cfg_builder *g) { + bool result = true; for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { for (int i = 0; i < b->b_iused; i++) { struct instr *instr = &b->b_instr[i]; int opcode = instr->i_opcode; + int oparg = instr->i_oparg; assert(opcode <= MAX_REAL_OPCODE); - int pushed = _PyOpcode_opcode_metadata[opcode].n_pushed; - int popped = _PyOpcode_opcode_metadata[opcode].n_popped; + int popped = _PyOpcode_num_popped(opcode, oparg); + int pushed = _PyOpcode_num_pushed(opcode, oparg); assert((pushed < 0) == (popped < 0)); if (pushed >= 0) { assert(_PyOpcode_opcode_metadata[opcode].valid_entry); @@ -8666,12 +8668,12 @@ opcode_metadata_is_sane(cfg_builder *g) { fprintf(stderr, "op=%d: stack_effect (%d) != pushed (%d) - popped (%d)\n", opcode, effect, pushed, popped); - return false; + result = false; } } } } - return true; + return result; } static bool diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index f9c640187afe09..46fd9673e8fb64 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -1,183 +1,869 @@ // This file is generated by Tools/cases_generator/generate_cases.py --metadata // from Python/bytecodes.c // Do not edit! + +static int +_PyOpcode_num_popped(int opcode, int oparg) { + switch(opcode) { + case NOP: + return 0; + case RESUME: + return 0; + case LOAD_CLOSURE: + return 0; + case LOAD_FAST_CHECK: + return 0; + case LOAD_FAST: + return 0; + case LOAD_CONST: + return 0; + case STORE_FAST: + return 1; + case LOAD_FAST__LOAD_FAST: + return 0+0; + case LOAD_FAST__LOAD_CONST: + return 0+0; + case STORE_FAST__LOAD_FAST: + return 1+0; + case STORE_FAST__STORE_FAST: + return 1+1; + case LOAD_CONST__LOAD_FAST: + return 0+0; + case POP_TOP: + return 1; + case PUSH_NULL: + return 0; + case END_FOR: + return 1+1; + case UNARY_NEGATIVE: + return 1; + case UNARY_NOT: + return 1; + case UNARY_INVERT: + return 1; + case BINARY_OP_MULTIPLY_INT: + return 2; + case BINARY_OP_MULTIPLY_FLOAT: + return 2; + case BINARY_OP_SUBTRACT_INT: + return 2; + case BINARY_OP_SUBTRACT_FLOAT: + return 2; + case BINARY_OP_ADD_UNICODE: + return 2; + case BINARY_OP_INPLACE_ADD_UNICODE: + return 2; + case BINARY_OP_ADD_FLOAT: + return 2; + case BINARY_OP_ADD_INT: + return 2; + case BINARY_SUBSCR: + return 2; + case BINARY_SLICE: + return 3; + case STORE_SLICE: + return 4; + case BINARY_SUBSCR_LIST_INT: + return 2; + case BINARY_SUBSCR_TUPLE_INT: + return 2; + case BINARY_SUBSCR_DICT: + return 2; + case BINARY_SUBSCR_GETITEM: + return 2; + case LIST_APPEND: + return (oparg-1) + 2; + case SET_ADD: + return (oparg-1) + 2; + case STORE_SUBSCR: + return 3; + case STORE_SUBSCR_LIST_INT: + return 3; + case STORE_SUBSCR_DICT: + return 3; + case DELETE_SUBSCR: + return 2; + case CALL_INTRINSIC_1: + return 1; + case RAISE_VARARGS: + return -1; + case INTERPRETER_EXIT: + return 1; + case RETURN_VALUE: + return 1; + case GET_AITER: + return 1; + case GET_ANEXT: + return 1; + case GET_AWAITABLE: + return 1; + case SEND: + return -1; + case YIELD_VALUE: + return 1; + case POP_EXCEPT: + return 1; + case RERAISE: + return -1; + case PREP_RERAISE_STAR: + return 2; + case END_ASYNC_FOR: + return -1; + case CLEANUP_THROW: + return -1; + case LOAD_ASSERTION_ERROR: + return 0; + case LOAD_BUILD_CLASS: + return 0; + case STORE_NAME: + return 1; + case DELETE_NAME: + return 0; + case UNPACK_SEQUENCE: + return -1; + case UNPACK_SEQUENCE_TWO_TUPLE: + return -1; + case UNPACK_SEQUENCE_TUPLE: + return -1; + case UNPACK_SEQUENCE_LIST: + return -1; + case UNPACK_EX: + return -1; + case STORE_ATTR: + return 2; + case DELETE_ATTR: + return 1; + case STORE_GLOBAL: + return 1; + case DELETE_GLOBAL: + return 0; + case LOAD_NAME: + return 0; + case LOAD_GLOBAL: + return -1; + case LOAD_GLOBAL_MODULE: + return -1; + case LOAD_GLOBAL_BUILTIN: + return -1; + case DELETE_FAST: + return 0; + case MAKE_CELL: + return 0; + case DELETE_DEREF: + return 0; + case LOAD_CLASSDEREF: + return 0; + case LOAD_DEREF: + return 0; + case STORE_DEREF: + return 1; + case COPY_FREE_VARS: + return 0; + case BUILD_STRING: + return oparg; + case BUILD_TUPLE: + return oparg; + case BUILD_LIST: + return oparg; + case LIST_EXTEND: + return (oparg-1) + 2; + case SET_UPDATE: + return (oparg-1) + 2; + case BUILD_SET: + return oparg; + case BUILD_MAP: + return oparg*2; + case SETUP_ANNOTATIONS: + return 0; + case BUILD_CONST_KEY_MAP: + return oparg + 1; + case DICT_UPDATE: + return 1; + case DICT_MERGE: + return 1; + case MAP_ADD: + return 2; + case LOAD_ATTR: + return -1; + case LOAD_ATTR_INSTANCE_VALUE: + return -1; + case LOAD_ATTR_MODULE: + return -1; + case LOAD_ATTR_WITH_HINT: + return -1; + case LOAD_ATTR_SLOT: + return -1; + case LOAD_ATTR_CLASS: + return -1; + case LOAD_ATTR_PROPERTY: + return -1; + case LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN: + return -1; + case STORE_ATTR_INSTANCE_VALUE: + return 2; + case STORE_ATTR_WITH_HINT: + return 2; + case STORE_ATTR_SLOT: + return 2; + case COMPARE_OP: + return 2; + case COMPARE_AND_BRANCH: + return 2; + case COMPARE_AND_BRANCH_FLOAT: + return 2; + case COMPARE_AND_BRANCH_INT: + return 2; + case COMPARE_AND_BRANCH_STR: + return 2; + case IS_OP: + return 2; + case CONTAINS_OP: + return 2; + case CHECK_EG_MATCH: + return 2; + case CHECK_EXC_MATCH: + return 2; + case IMPORT_NAME: + return 2; + case IMPORT_FROM: + return 1; + case JUMP_FORWARD: + return 0; + case JUMP_BACKWARD: + return 0; + case POP_JUMP_IF_FALSE: + return -1; + case POP_JUMP_IF_TRUE: + return -1; + case POP_JUMP_IF_NOT_NONE: + return -1; + case POP_JUMP_IF_NONE: + return -1; + case JUMP_IF_FALSE_OR_POP: + return -1; + case JUMP_IF_TRUE_OR_POP: + return -1; + case JUMP_BACKWARD_NO_INTERRUPT: + return -1; + case GET_LEN: + return -1; + case MATCH_CLASS: + return 3; + case MATCH_MAPPING: + return 1; + case MATCH_SEQUENCE: + return 1; + case MATCH_KEYS: + return 2; + case GET_ITER: + return -1; + case GET_YIELD_FROM_ITER: + return -1; + case FOR_ITER: + return -1; + case FOR_ITER_LIST: + return -1; + case FOR_ITER_TUPLE: + return -1; + case FOR_ITER_RANGE: + return -1; + case FOR_ITER_GEN: + return -1; + case BEFORE_ASYNC_WITH: + return -1; + case BEFORE_WITH: + return -1; + case WITH_EXCEPT_START: + return 4; + case PUSH_EXC_INFO: + return -1; + case LOAD_ATTR_METHOD_WITH_VALUES: + return -1; + case LOAD_ATTR_METHOD_NO_DICT: + return -1; + case LOAD_ATTR_METHOD_LAZY_DICT: + return -1; + case CALL_BOUND_METHOD_EXACT_ARGS: + return -1; + case KW_NAMES: + return -1; + case CALL: + return -1; + case CALL_PY_EXACT_ARGS: + return -1; + case CALL_PY_WITH_DEFAULTS: + return -1; + case CALL_NO_KW_TYPE_1: + return -1; + case CALL_NO_KW_STR_1: + return -1; + case CALL_NO_KW_TUPLE_1: + return -1; + case CALL_BUILTIN_CLASS: + return -1; + case CALL_NO_KW_BUILTIN_O: + return -1; + case CALL_NO_KW_BUILTIN_FAST: + return -1; + case CALL_BUILTIN_FAST_WITH_KEYWORDS: + return -1; + case CALL_NO_KW_LEN: + return -1; + case CALL_NO_KW_ISINSTANCE: + return -1; + case CALL_NO_KW_LIST_APPEND: + return -1; + case CALL_NO_KW_METHOD_DESCRIPTOR_O: + return -1; + case CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: + return -1; + case CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS: + return -1; + case CALL_NO_KW_METHOD_DESCRIPTOR_FAST: + return -1; + case CALL_FUNCTION_EX: + return -1; + case MAKE_FUNCTION: + return -1; + case RETURN_GENERATOR: + return -1; + case BUILD_SLICE: + return -1; + case FORMAT_VALUE: + return -1; + case COPY: + return -1; + case BINARY_OP: + return 2; + case SWAP: + return -1; + case EXTENDED_ARG: + return -1; + case CACHE: + return -1; + default: + Py_UNREACHABLE(); + } +} + +static int +_PyOpcode_num_pushed(int opcode, int oparg) { + switch(opcode) { + case NOP: + return 0; + case RESUME: + return 0; + case LOAD_CLOSURE: + return 1; + case LOAD_FAST_CHECK: + return 1; + case LOAD_FAST: + return 1; + case LOAD_CONST: + return 1; + case STORE_FAST: + return 0; + case LOAD_FAST__LOAD_FAST: + return 1+1; + case LOAD_FAST__LOAD_CONST: + return 1+1; + case STORE_FAST__LOAD_FAST: + return 0+1; + case STORE_FAST__STORE_FAST: + return 0+0; + case LOAD_CONST__LOAD_FAST: + return 1+1; + case POP_TOP: + return 0; + case PUSH_NULL: + return 1; + case END_FOR: + return 0+0; + case UNARY_NEGATIVE: + return 1; + case UNARY_NOT: + return 1; + case UNARY_INVERT: + return 1; + case BINARY_OP_MULTIPLY_INT: + return 1; + case BINARY_OP_MULTIPLY_FLOAT: + return 1; + case BINARY_OP_SUBTRACT_INT: + return 1; + case BINARY_OP_SUBTRACT_FLOAT: + return 1; + case BINARY_OP_ADD_UNICODE: + return 1; + case BINARY_OP_INPLACE_ADD_UNICODE: + return 0; + case BINARY_OP_ADD_FLOAT: + return 1; + case BINARY_OP_ADD_INT: + return 1; + case BINARY_SUBSCR: + return 1; + case BINARY_SLICE: + return 1; + case STORE_SLICE: + return 0; + case BINARY_SUBSCR_LIST_INT: + return 1; + case BINARY_SUBSCR_TUPLE_INT: + return 1; + case BINARY_SUBSCR_DICT: + return 1; + case BINARY_SUBSCR_GETITEM: + return 1; + case LIST_APPEND: + return (oparg-1) + 1; + case SET_ADD: + return (oparg-1) + 1; + case STORE_SUBSCR: + return 0; + case STORE_SUBSCR_LIST_INT: + return 0; + case STORE_SUBSCR_DICT: + return 0; + case DELETE_SUBSCR: + return 0; + case CALL_INTRINSIC_1: + return 1; + case RAISE_VARARGS: + return -1; + case INTERPRETER_EXIT: + return 0; + case RETURN_VALUE: + return 0; + case GET_AITER: + return 1; + case GET_ANEXT: + return 2; + case GET_AWAITABLE: + return 1; + case SEND: + return -1; + case YIELD_VALUE: + return 1; + case POP_EXCEPT: + return 0; + case RERAISE: + return -1; + case PREP_RERAISE_STAR: + return 1; + case END_ASYNC_FOR: + return -1; + case CLEANUP_THROW: + return -1; + case LOAD_ASSERTION_ERROR: + return 1; + case LOAD_BUILD_CLASS: + return 1; + case STORE_NAME: + return 0; + case DELETE_NAME: + return 0; + case UNPACK_SEQUENCE: + return -1; + case UNPACK_SEQUENCE_TWO_TUPLE: + return -1; + case UNPACK_SEQUENCE_TUPLE: + return -1; + case UNPACK_SEQUENCE_LIST: + return -1; + case UNPACK_EX: + return -1; + case STORE_ATTR: + return 0; + case DELETE_ATTR: + return 0; + case STORE_GLOBAL: + return 0; + case DELETE_GLOBAL: + return 0; + case LOAD_NAME: + return 1; + case LOAD_GLOBAL: + return -1; + case LOAD_GLOBAL_MODULE: + return -1; + case LOAD_GLOBAL_BUILTIN: + return -1; + case DELETE_FAST: + return 0; + case MAKE_CELL: + return 0; + case DELETE_DEREF: + return 0; + case LOAD_CLASSDEREF: + return 1; + case LOAD_DEREF: + return 1; + case STORE_DEREF: + return 0; + case COPY_FREE_VARS: + return 0; + case BUILD_STRING: + return 1; + case BUILD_TUPLE: + return 1; + case BUILD_LIST: + return 1; + case LIST_EXTEND: + return (oparg-1) + 1; + case SET_UPDATE: + return (oparg-1) + 1; + case BUILD_SET: + return 1; + case BUILD_MAP: + return 1; + case SETUP_ANNOTATIONS: + return 0; + case BUILD_CONST_KEY_MAP: + return 1; + case DICT_UPDATE: + return 0; + case DICT_MERGE: + return 0; + case MAP_ADD: + return 0; + case LOAD_ATTR: + return -1; + case LOAD_ATTR_INSTANCE_VALUE: + return -1; + case LOAD_ATTR_MODULE: + return -1; + case LOAD_ATTR_WITH_HINT: + return -1; + case LOAD_ATTR_SLOT: + return -1; + case LOAD_ATTR_CLASS: + return -1; + case LOAD_ATTR_PROPERTY: + return -1; + case LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN: + return -1; + case STORE_ATTR_INSTANCE_VALUE: + return 0; + case STORE_ATTR_WITH_HINT: + return 0; + case STORE_ATTR_SLOT: + return 0; + case COMPARE_OP: + return 1; + case COMPARE_AND_BRANCH: + return 0; + case COMPARE_AND_BRANCH_FLOAT: + return 0; + case COMPARE_AND_BRANCH_INT: + return 0; + case COMPARE_AND_BRANCH_STR: + return 0; + case IS_OP: + return 1; + case CONTAINS_OP: + return 1; + case CHECK_EG_MATCH: + return 2; + case CHECK_EXC_MATCH: + return 2; + case IMPORT_NAME: + return 1; + case IMPORT_FROM: + return 2; + case JUMP_FORWARD: + return 0; + case JUMP_BACKWARD: + return 0; + case POP_JUMP_IF_FALSE: + return -1; + case POP_JUMP_IF_TRUE: + return -1; + case POP_JUMP_IF_NOT_NONE: + return -1; + case POP_JUMP_IF_NONE: + return -1; + case JUMP_IF_FALSE_OR_POP: + return -1; + case JUMP_IF_TRUE_OR_POP: + return -1; + case JUMP_BACKWARD_NO_INTERRUPT: + return -1; + case GET_LEN: + return -1; + case MATCH_CLASS: + return 1; + case MATCH_MAPPING: + return 2; + case MATCH_SEQUENCE: + return 2; + case MATCH_KEYS: + return 3; + case GET_ITER: + return -1; + case GET_YIELD_FROM_ITER: + return -1; + case FOR_ITER: + return -1; + case FOR_ITER_LIST: + return -1; + case FOR_ITER_TUPLE: + return -1; + case FOR_ITER_RANGE: + return -1; + case FOR_ITER_GEN: + return -1; + case BEFORE_ASYNC_WITH: + return -1; + case BEFORE_WITH: + return -1; + case WITH_EXCEPT_START: + return 5; + case PUSH_EXC_INFO: + return -1; + case LOAD_ATTR_METHOD_WITH_VALUES: + return -1; + case LOAD_ATTR_METHOD_NO_DICT: + return -1; + case LOAD_ATTR_METHOD_LAZY_DICT: + return -1; + case CALL_BOUND_METHOD_EXACT_ARGS: + return -1; + case KW_NAMES: + return -1; + case CALL: + return -1; + case CALL_PY_EXACT_ARGS: + return -1; + case CALL_PY_WITH_DEFAULTS: + return -1; + case CALL_NO_KW_TYPE_1: + return -1; + case CALL_NO_KW_STR_1: + return -1; + case CALL_NO_KW_TUPLE_1: + return -1; + case CALL_BUILTIN_CLASS: + return -1; + case CALL_NO_KW_BUILTIN_O: + return -1; + case CALL_NO_KW_BUILTIN_FAST: + return -1; + case CALL_BUILTIN_FAST_WITH_KEYWORDS: + return -1; + case CALL_NO_KW_LEN: + return -1; + case CALL_NO_KW_ISINSTANCE: + return -1; + case CALL_NO_KW_LIST_APPEND: + return -1; + case CALL_NO_KW_METHOD_DESCRIPTOR_O: + return -1; + case CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: + return -1; + case CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS: + return -1; + case CALL_NO_KW_METHOD_DESCRIPTOR_FAST: + return -1; + case CALL_FUNCTION_EX: + return -1; + case MAKE_FUNCTION: + return -1; + case RETURN_GENERATOR: + return -1; + case BUILD_SLICE: + return -1; + case FORMAT_VALUE: + return -1; + case COPY: + return -1; + case BINARY_OP: + return 1; + case SWAP: + return -1; + case EXTENDED_ARG: + return -1; + case CACHE: + return -1; + default: + Py_UNREACHABLE(); + } +} enum Direction { DIR_NONE, DIR_READ, DIR_WRITE }; enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 }; -static const struct { - short n_popped; - short n_pushed; +struct opcode_metadata { enum Direction dir_op1; enum Direction dir_op2; enum Direction dir_op3; bool valid_entry; enum InstructionFormat instr_format; } _PyOpcode_opcode_metadata[256] = { - [NOP] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [RESUME] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_CLOSURE] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_FAST_CHECK] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_FAST] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_CONST] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [STORE_FAST] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_FAST__LOAD_FAST] = { 0, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, - [LOAD_FAST__LOAD_CONST] = { 0, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, - [STORE_FAST__LOAD_FAST] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, - [STORE_FAST__STORE_FAST] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, - [LOAD_CONST__LOAD_FAST] = { 0, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, - [POP_TOP] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [PUSH_NULL] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [END_FOR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNARY_NEGATIVE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [UNARY_NOT] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [UNARY_INVERT] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [BINARY_OP_MULTIPLY_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [BINARY_OP_MULTIPLY_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [BINARY_OP_SUBTRACT_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [BINARY_OP_SUBTRACT_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [BINARY_OP_ADD_UNICODE] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [BINARY_OP_INPLACE_ADD_UNICODE] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [BINARY_OP_ADD_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [BINARY_OP_ADD_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [BINARY_SUBSCR] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, - [BINARY_SLICE] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [STORE_SLICE] = { 4, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [BINARY_SUBSCR_LIST_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, - [BINARY_SUBSCR_TUPLE_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, - [BINARY_SUBSCR_DICT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, - [BINARY_SUBSCR_GETITEM] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, - [LIST_APPEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [SET_ADD] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [STORE_SUBSCR] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [STORE_SUBSCR_LIST_INT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [STORE_SUBSCR_DICT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [DELETE_SUBSCR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [CALL_INTRINSIC_1] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [RAISE_VARARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [INTERPRETER_EXIT] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [RETURN_VALUE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [GET_AITER] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [GET_ANEXT] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [GET_AWAITABLE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [SEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [YIELD_VALUE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [POP_EXCEPT] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [RERAISE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [PREP_RERAISE_STAR] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [END_ASYNC_FOR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [CLEANUP_THROW] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [LOAD_ASSERTION_ERROR] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [LOAD_BUILD_CLASS] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [STORE_NAME] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [DELETE_NAME] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNPACK_SEQUENCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNPACK_SEQUENCE_TWO_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [UNPACK_SEQUENCE_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNPACK_SEQUENCE_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNPACK_EX] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [STORE_ATTR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, - [DELETE_ATTR] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [STORE_GLOBAL] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [DELETE_GLOBAL] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_NAME] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_GLOBAL] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_GLOBAL_MODULE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_GLOBAL_BUILTIN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [DELETE_FAST] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [MAKE_CELL] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [DELETE_DEREF] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_CLASSDEREF] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_DEREF] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [STORE_DEREF] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [COPY_FREE_VARS] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BUILD_STRING] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BUILD_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BUILD_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LIST_EXTEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [SET_UPDATE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BUILD_SET] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BUILD_MAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [SETUP_ANNOTATIONS] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [BUILD_CONST_KEY_MAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [DICT_UPDATE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [DICT_MERGE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [MAP_ADD] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_INSTANCE_VALUE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_MODULE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_WITH_HINT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_SLOT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_PROPERTY] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [STORE_ATTR_INSTANCE_VALUE] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, - [STORE_ATTR_WITH_HINT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, - [STORE_ATTR_SLOT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, - [COMPARE_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [COMPARE_AND_BRANCH] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, - [COMPARE_AND_BRANCH_FLOAT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, - [COMPARE_AND_BRANCH_INT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, - [COMPARE_AND_BRANCH_STR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, - [IS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CONTAINS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CHECK_EG_MATCH] = { 2, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [CHECK_EXC_MATCH] = { 2, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [IMPORT_NAME] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [IMPORT_FROM] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [JUMP_FORWARD] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [JUMP_BACKWARD] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [POP_JUMP_IF_FALSE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [POP_JUMP_IF_TRUE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [POP_JUMP_IF_NOT_NONE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [POP_JUMP_IF_NONE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [JUMP_IF_FALSE_OR_POP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [JUMP_IF_TRUE_OR_POP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [JUMP_BACKWARD_NO_INTERRUPT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [GET_LEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [MATCH_CLASS] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [MATCH_MAPPING] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [MATCH_SEQUENCE] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [MATCH_KEYS] = { 2, 3, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [GET_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [GET_YIELD_FROM_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [FOR_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FOR_ITER_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FOR_ITER_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FOR_ITER_RANGE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FOR_ITER_GEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BEFORE_ASYNC_WITH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [BEFORE_WITH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [WITH_EXCEPT_START] = { 4, 5, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [PUSH_EXC_INFO] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [LOAD_ATTR_METHOD_WITH_VALUES] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [LOAD_ATTR_METHOD_NO_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [LOAD_ATTR_METHOD_LAZY_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [CALL_BOUND_METHOD_EXACT_ARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [KW_NAMES] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_PY_EXACT_ARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_PY_WITH_DEFAULTS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_TYPE_1] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_STR_1] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_TUPLE_1] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_BUILTIN_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_BUILTIN_O] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_BUILTIN_FAST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_LEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_ISINSTANCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_LIST_APPEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_METHOD_DESCRIPTOR_O] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_FUNCTION_EX] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [MAKE_FUNCTION] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [RETURN_GENERATOR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [BUILD_SLICE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FORMAT_VALUE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [COPY] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BINARY_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [SWAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [EXTENDED_ARG] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CACHE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [NOP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [RESUME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_CLOSURE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_FAST_CHECK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_CONST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_FAST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, + [LOAD_FAST__LOAD_CONST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, + [STORE_FAST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, + [STORE_FAST__STORE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, + [LOAD_CONST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, + [POP_TOP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [PUSH_NULL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [END_FOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNARY_NEGATIVE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [UNARY_NOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [UNARY_INVERT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [BINARY_OP_MULTIPLY_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_MULTIPLY_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_SUBTRACT_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_SUBTRACT_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_ADD_UNICODE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_INPLACE_ADD_UNICODE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [BINARY_OP_ADD_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_ADD_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [BINARY_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [STORE_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [BINARY_SUBSCR_LIST_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [BINARY_SUBSCR_TUPLE_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [BINARY_SUBSCR_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [BINARY_SUBSCR_GETITEM] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [LIST_APPEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [SET_ADD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [STORE_SUBSCR_LIST_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [STORE_SUBSCR_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [DELETE_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [CALL_INTRINSIC_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [RAISE_VARARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [INTERPRETER_EXIT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [RETURN_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [GET_AITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [GET_ANEXT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [GET_AWAITABLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [SEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [YIELD_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [POP_EXCEPT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [RERAISE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [PREP_RERAISE_STAR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [END_ASYNC_FOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [CLEANUP_THROW] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [LOAD_ASSERTION_ERROR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [LOAD_BUILD_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [STORE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DELETE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNPACK_SEQUENCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNPACK_SEQUENCE_TWO_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [UNPACK_SEQUENCE_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNPACK_SEQUENCE_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNPACK_EX] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [DELETE_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DELETE_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_GLOBAL_MODULE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_GLOBAL_BUILTIN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DELETE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MAKE_CELL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DELETE_DEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_CLASSDEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_DEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_DEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [COPY_FREE_VARS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BUILD_STRING] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BUILD_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BUILD_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LIST_EXTEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [SET_UPDATE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BUILD_SET] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BUILD_MAP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [SETUP_ANNOTATIONS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [BUILD_CONST_KEY_MAP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DICT_UPDATE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DICT_MERGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MAP_ADD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_INSTANCE_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_MODULE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_WITH_HINT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_SLOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_PROPERTY] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_ATTR_INSTANCE_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [STORE_ATTR_WITH_HINT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [STORE_ATTR_SLOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [COMPARE_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [COMPARE_AND_BRANCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, + [COMPARE_AND_BRANCH_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, + [COMPARE_AND_BRANCH_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, + [COMPARE_AND_BRANCH_STR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, + [IS_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CONTAINS_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CHECK_EG_MATCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [CHECK_EXC_MATCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [IMPORT_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [IMPORT_FROM] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [JUMP_FORWARD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [JUMP_BACKWARD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [POP_JUMP_IF_FALSE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [POP_JUMP_IF_TRUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [POP_JUMP_IF_NOT_NONE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [POP_JUMP_IF_NONE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [JUMP_IF_FALSE_OR_POP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [JUMP_IF_TRUE_OR_POP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [JUMP_BACKWARD_NO_INTERRUPT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [GET_LEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [MATCH_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MATCH_MAPPING] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [MATCH_SEQUENCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [MATCH_KEYS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [GET_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [GET_YIELD_FROM_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BEFORE_ASYNC_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [BEFORE_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [WITH_EXCEPT_START] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [PUSH_EXC_INFO] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [LOAD_ATTR_METHOD_WITH_VALUES] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [LOAD_ATTR_METHOD_NO_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [LOAD_ATTR_METHOD_LAZY_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [CALL_BOUND_METHOD_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [KW_NAMES] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_PY_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_PY_WITH_DEFAULTS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_TYPE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_STR_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_TUPLE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_BUILTIN_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_BUILTIN_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_BUILTIN_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_LEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_ISINSTANCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_LIST_APPEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_METHOD_DESCRIPTOR_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_FUNCTION_EX] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MAKE_FUNCTION] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [RETURN_GENERATOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [BUILD_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FORMAT_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [COPY] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BINARY_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [SWAP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [EXTENDED_ARG] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CACHE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, }; diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 429c9d34d60601..3e2ddaaf20063b 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -734,6 +734,60 @@ def stack_analysis( ] return stack, -lowest + def get_stack_effect_info( + self, thing: parser.InstDef | parser.Super | parser.Macro + ) -> tuple[Instruction, str, str]: + + def effect_str(effect: list[StackEffect]) -> str: + if getattr(thing, 'kind', None) == 'legacy': + return str(-1) + n_effect, sym_effect = list_effect_size(effect) + if sym_effect: + return f"{sym_effect} + {n_effect}" if n_effect else sym_effect + return str(n_effect) + + match thing: + case parser.InstDef(): + if thing.kind != "op": + instr = self.instrs[thing.name] + popped = effect_str(instr.input_effects) + pushed = effect_str(instr.output_effects) + case parser.Super(): + instr = self.super_instrs[thing.name] + popped = '+'.join(effect_str(comp.instr.input_effects) for comp in instr.parts) + pushed = '+'.join(effect_str(comp.instr.output_effects) for comp in instr.parts) + case parser.Macro(): + instr = self.macro_instrs[thing.name] + parts = [comp for comp in instr.parts if isinstance(comp, Component)] + popped = '+'.join(effect_str(comp.instr.input_effects) for comp in parts) + pushed = '+'.join(effect_str(comp.instr.output_effects) for comp in parts) + case _: + typing.assert_never(thing) + return instr, popped, pushed + + def write_stack_effect_functions(self) -> None: + popped_data = [] + pushed_data = [] + for thing in self.everything: + instr, popped, pushed = self.get_stack_effect_info(thing) + popped_data.append( (instr, popped) ) + pushed_data.append( (instr, pushed) ) + + def write_function(direction: str, data: list[tuple[Instruction, str]]) -> None: + self.out.emit("\nstatic int"); + self.out.emit(f"_PyOpcode_num_{direction}(int opcode, int oparg) {{") + self.out.emit(" switch(opcode) {"); + for instr, effect in data: + self.out.emit(f" case {instr.name}:") + self.out.emit(f" return {effect};") + self.out.emit(" default:") + self.out.emit(" Py_UNREACHABLE();") + self.out.emit(" }") + self.out.emit("}") + + write_function('popped', popped_data) + write_function('pushed', pushed_data) + def write_metadata(self) -> None: """Write instruction metadata to output file.""" @@ -762,13 +816,13 @@ def write_metadata(self) -> None: # Create formatter; the rest of the code uses this self.out = Formatter(f, 0) + self.write_stack_effect_functions() + # Write variable definition self.out.emit("enum Direction { DIR_NONE, DIR_READ, DIR_WRITE };") self.out.emit(f"enum InstructionFormat {{ {', '.join(format_enums)} }};") - self.out.emit("static const struct {") + self.out.emit("struct opcode_metadata {") with self.out.indent(): - self.out.emit("short n_popped;") - self.out.emit("short n_pushed;") self.out.emit("enum Direction dir_op1;") self.out.emit("enum Direction dir_op2;") self.out.emit("enum Direction dir_op3;") @@ -796,42 +850,30 @@ def write_metadata_for_inst(self, instr: Instruction) -> None: """Write metadata for a single instruction.""" dir_op1 = dir_op2 = dir_op3 = "DIR_NONE" if instr.kind == "legacy": - n_popped = n_pushed = -1 assert not instr.register else: - n_popped, sym_popped = list_effect_size(instr.input_effects) - n_pushed, sym_pushed = list_effect_size(instr.output_effects) - if sym_popped or sym_pushed: - # TODO: Record symbolic effects (how?) - n_popped = n_pushed = -1 if instr.register: directions: list[str] = [] directions.extend("DIR_READ" for _ in instr.input_effects) directions.extend("DIR_WRITE" for _ in instr.output_effects) directions.extend("DIR_NONE" for _ in range(3)) dir_op1, dir_op2, dir_op3 = directions[:3] - n_popped = n_pushed = 0 self.out.emit( - f' [{instr.name}] = {{ {n_popped}, {n_pushed}, {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{instr.instr_fmt} }},' + f' [{instr.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{instr.instr_fmt} }},' ) def write_metadata_for_super(self, sup: SuperInstruction) -> None: """Write metadata for a super-instruction.""" - n_popped = sum(len(comp.instr.input_effects) for comp in sup.parts) - n_pushed = sum(len(comp.instr.output_effects) for comp in sup.parts) dir_op1 = dir_op2 = dir_op3 = "DIR_NONE" self.out.emit( - f' [{sup.name}] = {{ {n_popped}, {n_pushed}, {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{sup.instr_fmt} }},' + f' [{sup.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{sup.instr_fmt} }},' ) def write_metadata_for_macro(self, mac: MacroInstruction) -> None: """Write metadata for a macro-instruction.""" - parts = [comp for comp in mac.parts if isinstance(comp, Component)] - n_popped = sum(len(comp.instr.input_effects) for comp in parts) - n_pushed = sum(len(comp.instr.output_effects) for comp in parts) dir_op1 = dir_op2 = dir_op3 = "DIR_NONE" self.out.emit( - f' [{mac.name}] = {{ {n_popped}, {n_pushed}, {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{mac.instr_fmt} }},' + f' [{mac.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{mac.instr_fmt} }},' ) def write_instructions(self) -> None: From 6162a0e305faf82534c011ddb2fb99a94ae84d29 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 25 Jan 2023 13:30:33 -0800 Subject: [PATCH 008/225] Fix incorrect versions in magic number comments (GH-101301) --- Lib/importlib/_bootstrap_external.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index cd44d0050ede82..e760fbb15759d4 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -423,14 +423,14 @@ def _write_atomic(path, data, mode=0o666): # Python 3.12a1 3507 (Set lineno of module's RESUME to 0) # Python 3.12a1 3508 (Add CLEANUP_THROW) # Python 3.12a1 3509 (Conditional jumps only jump forward) -# Python 3.12a1 3510 (FOR_ITER leaves iterator on the stack) -# Python 3.12a1 3511 (Add STOPITERATION_ERROR instruction) -# Python 3.12a1 3512 (Remove all unused consts from code objects) -# Python 3.12a1 3513 (Add CALL_INTRINSIC_1 instruction, removed STOPITERATION_ERROR, PRINT_EXPR, IMPORT_STAR) -# Python 3.12a1 3514 (Remove ASYNC_GEN_WRAP, LIST_TO_TUPLE, and UNARY_POSITIVE) -# Python 3.12a1 3515 (Embed jump mask in COMPARE_OP oparg) -# Python 3.12a1 3516 (Add COMPARE_AND_BRANCH instruction) -# Python 3.12a1 3517 (Change YIELD_VALUE oparg to exception block depth) +# Python 3.12a2 3510 (FOR_ITER leaves iterator on the stack) +# Python 3.12a2 3511 (Add STOPITERATION_ERROR instruction) +# Python 3.12a2 3512 (Remove all unused consts from code objects) +# Python 3.12a4 3513 (Add CALL_INTRINSIC_1 instruction, removed STOPITERATION_ERROR, PRINT_EXPR, IMPORT_STAR) +# Python 3.12a4 3514 (Remove ASYNC_GEN_WRAP, LIST_TO_TUPLE, and UNARY_POSITIVE) +# Python 3.12a5 3515 (Embed jump mask in COMPARE_OP oparg) +# Python 3.12a5 3516 (Add COMPARE_AND_BRANCH instruction) +# Python 3.12a5 3517 (Change YIELD_VALUE oparg to exception block depth) # Python 3.13 will start with 3550 From b400219df5f245ef2eb3c7ce0589b1f8020a1192 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Wed, 25 Jan 2023 22:29:56 +0000 Subject: [PATCH 009/225] gh-98831: rewrite RAISE_VARARGS in the instruction definition DSL (#101306) --- Python/bytecodes.c | 13 +++++-------- Python/generated_cases.c.h | 11 +++++------ Python/opcode_metadata.h | 8 ++++++-- Tools/cases_generator/generate_cases.py | 6 ++++-- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index d3e242b81e608d..e5769f61fc28d0 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -505,27 +505,24 @@ dummy_func( ERROR_IF(res == NULL, error); } - // This should remain a legacy instruction. - inst(RAISE_VARARGS) { + inst(RAISE_VARARGS, (args[oparg] -- )) { PyObject *cause = NULL, *exc = NULL; switch (oparg) { case 2: - cause = POP(); /* cause */ + cause = args[1]; /* fall through */ case 1: - exc = POP(); /* exc */ + exc = args[0]; /* fall through */ case 0: - if (do_raise(tstate, exc, cause)) { - goto exception_unwind; - } + ERROR_IF(do_raise(tstate, exc, cause), exception_unwind); break; default: _PyErr_SetString(tstate, PyExc_SystemError, "bad RAISE_VARARGS oparg"); break; } - goto error; + ERROR_IF(true, error); } inst(INTERPRETER_EXIT, (retval --)) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 7d3396ad6bdec3..287a1f1f042089 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -689,25 +689,24 @@ } TARGET(RAISE_VARARGS) { + PyObject **args = &PEEK(oparg); PyObject *cause = NULL, *exc = NULL; switch (oparg) { case 2: - cause = POP(); /* cause */ + cause = args[1]; /* fall through */ case 1: - exc = POP(); /* exc */ + exc = args[0]; /* fall through */ case 0: - if (do_raise(tstate, exc, cause)) { - goto exception_unwind; - } + if (do_raise(tstate, exc, cause)) { STACK_SHRINK(oparg); goto exception_unwind; } break; default: _PyErr_SetString(tstate, PyExc_SystemError, "bad RAISE_VARARGS oparg"); break; } - goto error; + if (true) { STACK_SHRINK(oparg); goto error; } } TARGET(INTERPRETER_EXIT) { diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 46fd9673e8fb64..cca86629e48d16 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -2,6 +2,7 @@ // from Python/bytecodes.c // Do not edit! +#ifndef NDEBUG static int _PyOpcode_num_popped(int opcode, int oparg) { switch(opcode) { @@ -86,7 +87,7 @@ _PyOpcode_num_popped(int opcode, int oparg) { case CALL_INTRINSIC_1: return 1; case RAISE_VARARGS: - return -1; + return oparg; case INTERPRETER_EXIT: return 1; case RETURN_VALUE: @@ -345,7 +346,9 @@ _PyOpcode_num_popped(int opcode, int oparg) { Py_UNREACHABLE(); } } +#endif +#ifndef NDEBUG static int _PyOpcode_num_pushed(int opcode, int oparg) { switch(opcode) { @@ -430,7 +433,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case CALL_INTRINSIC_1: return 1; case RAISE_VARARGS: - return -1; + return 0; case INTERPRETER_EXIT: return 0; case RETURN_VALUE: @@ -689,6 +692,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { Py_UNREACHABLE(); } } +#endif enum Direction { DIR_NONE, DIR_READ, DIR_WRITE }; enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 }; struct opcode_metadata { diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 3e2ddaaf20063b..b7942410c82fc3 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -774,7 +774,8 @@ def write_stack_effect_functions(self) -> None: pushed_data.append( (instr, pushed) ) def write_function(direction: str, data: list[tuple[Instruction, str]]) -> None: - self.out.emit("\nstatic int"); + self.out.emit("\n#ifndef NDEBUG"); + self.out.emit("static int"); self.out.emit(f"_PyOpcode_num_{direction}(int opcode, int oparg) {{") self.out.emit(" switch(opcode) {"); for instr, effect in data: @@ -784,6 +785,7 @@ def write_function(direction: str, data: list[tuple[Instruction, str]]) -> None: self.out.emit(" Py_UNREACHABLE();") self.out.emit(" }") self.out.emit("}") + self.out.emit("#endif"); write_function('popped', popped_data) write_function('pushed', pushed_data) @@ -1023,7 +1025,7 @@ def always_exits(lines: list[str]) -> bool: return False line = line[12:] return line.startswith( - ("goto ", "return ", "DISPATCH", "GO_TO_", "Py_UNREACHABLE()") + ("goto ", "return ", "DISPATCH", "GO_TO_", "Py_UNREACHABLE()", "ERROR_IF(true, ") ) From 73245d084e383b5bc3affedc9444e6b6c881c546 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Thu, 26 Jan 2023 10:50:33 +0400 Subject: [PATCH 010/225] gh-94518: Rename `group*` to `extra_group*` to avoid confusion (#101054) * Rename `group*` to `extra_group*` to avoid confusion * Rename `num_groups` into `extra_group_size` * Rename `groups_list` to `extra_groups_packed` --- ...3-01-15-09-11-30.gh-issue-94518.jvxtxm.rst | 3 + Modules/_posixsubprocess.c | 56 +++++++++---------- 2 files changed, 31 insertions(+), 28 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-01-15-09-11-30.gh-issue-94518.jvxtxm.rst diff --git a/Misc/NEWS.d/next/Library/2023-01-15-09-11-30.gh-issue-94518.jvxtxm.rst b/Misc/NEWS.d/next/Library/2023-01-15-09-11-30.gh-issue-94518.jvxtxm.rst new file mode 100644 index 00000000000000..77563090464dbc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-15-09-11-30.gh-issue-94518.jvxtxm.rst @@ -0,0 +1,3 @@ +Group-related variables of ``_posixsubprocess`` module are renamed to +stress that supplimentary group affinity is added to a fork, not +replace the inherited ones. Patch by Oleg Iarygin. diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index 516f11d3543d58..f3ff39215eab76 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -519,7 +519,7 @@ child_exec(char *const exec_array[], int close_fds, int restore_signals, int call_setsid, pid_t pgid_to_set, gid_t gid, - Py_ssize_t groups_size, const gid_t *groups, + Py_ssize_t extra_group_size, const gid_t *extra_groups, uid_t uid, int child_umask, const void *child_sigmask, PyObject *py_fds_to_keep, @@ -619,8 +619,8 @@ child_exec(char *const exec_array[], #endif #ifdef HAVE_SETGROUPS - if (groups_size > 0) - POSIX_CALL(setgroups(groups_size, groups)); + if (extra_group_size > 0) + POSIX_CALL(setgroups(extra_group_size, extra_groups)); #endif /* HAVE_SETGROUPS */ #ifdef HAVE_SETREGID @@ -725,7 +725,7 @@ do_fork_exec(char *const exec_array[], int close_fds, int restore_signals, int call_setsid, pid_t pgid_to_set, gid_t gid, - Py_ssize_t groups_size, const gid_t *groups, + Py_ssize_t extra_group_size, const gid_t *extra_groups, uid_t uid, int child_umask, const void *child_sigmask, PyObject *py_fds_to_keep, @@ -740,7 +740,7 @@ do_fork_exec(char *const exec_array[], /* These are checked by our caller; verify them in debug builds. */ assert(uid == (uid_t)-1); assert(gid == (gid_t)-1); - assert(groups_size < 0); + assert(extra_group_size < 0); assert(preexec_fn == Py_None); pid = vfork(); @@ -777,7 +777,7 @@ do_fork_exec(char *const exec_array[], p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, errpipe_read, errpipe_write, close_fds, restore_signals, call_setsid, pgid_to_set, - gid, groups_size, groups, + gid, extra_group_size, extra_groups, uid, child_umask, child_sigmask, py_fds_to_keep, preexec_fn, preexec_fn_args_tuple); _exit(255); @@ -793,20 +793,20 @@ subprocess_fork_exec(PyObject *module, PyObject *args) PyObject *env_list, *preexec_fn; PyObject *process_args, *converted_args = NULL, *fast_args = NULL; PyObject *preexec_fn_args_tuple = NULL; - PyObject *groups_list; + PyObject *extra_groups_packed; PyObject *uid_object, *gid_object; int p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite; int errpipe_read, errpipe_write, close_fds, restore_signals; int call_setsid; pid_t pgid_to_set = -1; - gid_t *groups = NULL; + gid_t *extra_groups = NULL; int child_umask; PyObject *cwd_obj, *cwd_obj2 = NULL; const char *cwd; pid_t pid = -1; int need_to_reenable_gc = 0; char *const *exec_array, *const *argv = NULL, *const *envp = NULL; - Py_ssize_t arg_num, num_groups = 0; + Py_ssize_t arg_num, extra_group_size = 0; int need_after_fork = 0; int saved_errno = 0; int allow_vfork; @@ -819,7 +819,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args) &p2cread, &p2cwrite, &c2pread, &c2pwrite, &errread, &errwrite, &errpipe_read, &errpipe_write, &restore_signals, &call_setsid, &pgid_to_set, - &gid_object, &groups_list, &uid_object, &child_umask, + &gid_object, &extra_groups_packed, &uid_object, &child_umask, &preexec_fn, &allow_vfork)) return NULL; @@ -895,41 +895,41 @@ subprocess_fork_exec(PyObject *module, PyObject *args) cwd = NULL; } - if (groups_list != Py_None) { + if (extra_groups_packed != Py_None) { #ifdef HAVE_SETGROUPS - if (!PyList_Check(groups_list)) { + if (!PyList_Check(extra_groups_packed)) { PyErr_SetString(PyExc_TypeError, "setgroups argument must be a list"); goto cleanup; } - num_groups = PySequence_Size(groups_list); + extra_group_size = PySequence_Size(extra_groups_packed); - if (num_groups < 0) + if (extra_group_size < 0) goto cleanup; - if (num_groups > MAX_GROUPS) { - PyErr_SetString(PyExc_ValueError, "too many groups"); + if (extra_group_size > MAX_GROUPS) { + PyErr_SetString(PyExc_ValueError, "too many extra_groups"); goto cleanup; } - /* Deliberately keep groups == NULL for num_groups == 0 */ - if (num_groups > 0) { - groups = PyMem_RawMalloc(num_groups * sizeof(gid_t)); - if (groups == NULL) { + /* Deliberately keep extra_groups == NULL for extra_group_size == 0 */ + if (extra_group_size > 0) { + extra_groups = PyMem_RawMalloc(extra_group_size * sizeof(gid_t)); + if (extra_groups == NULL) { PyErr_SetString(PyExc_MemoryError, "failed to allocate memory for group list"); goto cleanup; } } - for (Py_ssize_t i = 0; i < num_groups; i++) { + for (Py_ssize_t i = 0; i < extra_group_size; i++) { PyObject *elem; - elem = PySequence_GetItem(groups_list, i); + elem = PySequence_GetItem(extra_groups_packed, i); if (!elem) goto cleanup; if (!PyLong_Check(elem)) { PyErr_SetString(PyExc_TypeError, - "groups must be integers"); + "extra_groups must be integers"); Py_DECREF(elem); goto cleanup; } else { @@ -939,7 +939,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args) PyErr_SetString(PyExc_ValueError, "invalid group id"); goto cleanup; } - groups[i] = gid; + extra_groups[i] = gid; } Py_DECREF(elem); } @@ -991,7 +991,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args) /* Use vfork() only if it's safe. See the comment above child_exec(). */ sigset_t old_sigs; if (preexec_fn == Py_None && allow_vfork && - uid == (uid_t)-1 && gid == (gid_t)-1 && num_groups < 0) { + uid == (uid_t)-1 && gid == (gid_t)-1 && extra_group_size < 0) { /* Block all signals to ensure that no signal handlers are run in the * child process while it shares memory with us. Note that signals * used internally by C libraries won't be blocked by @@ -1014,7 +1014,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args) p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, errpipe_read, errpipe_write, close_fds, restore_signals, call_setsid, pgid_to_set, - gid, num_groups, groups, + gid, extra_group_size, extra_groups, uid, child_umask, old_sigmask, py_fds_to_keep, preexec_fn, preexec_fn_args_tuple); @@ -1054,7 +1054,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args) } Py_XDECREF(preexec_fn_args_tuple); - PyMem_RawFree(groups); + PyMem_RawFree(extra_groups); Py_XDECREF(cwd_obj2); if (envp) _Py_FreeCharPArray(envp); @@ -1079,7 +1079,7 @@ PyDoc_STRVAR(subprocess_fork_exec_doc, p2cread, p2cwrite, c2pread, c2pwrite,\n\ errread, errwrite, errpipe_read, errpipe_write,\n\ restore_signals, call_setsid, pgid_to_set,\n\ - gid, groups_list, uid,\n\ + gid, extra_groups, uid,\n\ preexec_fn)\n\ \n\ Forks a child process, closes parent file descriptors as appropriate in the\n\ From a2262789abccb68a61bb4047743fbcbd9a64b13c Mon Sep 17 00:00:00 2001 From: JosephSBoyle <48555120+JosephSBoyle@users.noreply.github.com> Date: Thu, 26 Jan 2023 07:01:11 +0000 Subject: [PATCH 011/225] gh-100522 Add a test for 'futures.as_completed' timing out with a non-zero timeout value (#100523) --- Lib/test/test_concurrent_futures.py | 40 ++++++++++++++++------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py index fe9fdc4f44d37b..b3520ae3994e03 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py @@ -711,7 +711,6 @@ def future_func(): class AsCompletedTests: - # TODO(brian@sweetapp.com): Should have a test with a non-zero timeout. def test_no_timeout(self): future1 = self.executor.submit(mul, 2, 21) future2 = self.executor.submit(mul, 7, 6) @@ -728,24 +727,29 @@ def test_no_timeout(self): future1, future2]), completed) - def test_zero_timeout(self): - future1 = self.executor.submit(time.sleep, 2) - completed_futures = set() - try: - for future in futures.as_completed( - [CANCELLED_AND_NOTIFIED_FUTURE, - EXCEPTION_FUTURE, - SUCCESSFUL_FUTURE, - future1], - timeout=0): - completed_futures.add(future) - except futures.TimeoutError: - pass + def test_future_times_out(self): + """Test ``futures.as_completed`` timing out before + completing it's final future.""" + already_completed = {CANCELLED_AND_NOTIFIED_FUTURE, + EXCEPTION_FUTURE, + SUCCESSFUL_FUTURE} - self.assertEqual(set([CANCELLED_AND_NOTIFIED_FUTURE, - EXCEPTION_FUTURE, - SUCCESSFUL_FUTURE]), - completed_futures) + for timeout in (0, 0.01): + with self.subTest(timeout): + + future = self.executor.submit(time.sleep, 0.1) + completed_futures = set() + try: + for f in futures.as_completed( + already_completed | {future}, + timeout + ): + completed_futures.add(f) + except futures.TimeoutError: + pass + + # Check that ``future`` wasn't completed. + self.assertEqual(completed_futures, already_completed) def test_duplicate_futures(self): # Issue 20367. Duplicate futures should not raise exceptions or give From f5ad63f79af3a5876f90b409d0c8402fa54e878a Mon Sep 17 00:00:00 2001 From: John Belmonte Date: Thu, 26 Jan 2023 16:25:43 +0900 Subject: [PATCH 012/225] datetime.rst: improve combine() docs (#101338) The explanation on handling of datetime as the date arg was confusingly mixed with an unrelated item, and lacked proper arg name formatting. --- Doc/library/datetime.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index eba8824f835113..ebb5f319efda8d 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -982,12 +982,11 @@ Other constructors, all class methods: are equal to the given :class:`.time` object's. If the *tzinfo* argument is provided, its value is used to set the :attr:`.tzinfo` attribute of the result, otherwise the :attr:`~.time.tzinfo` attribute of the *time* argument - is used. + is used. If the *date* argument is a :class:`.datetime` object, its time components + and :attr:`.tzinfo` attributes are ignored. For any :class:`.datetime` object *d*, - ``d == datetime.combine(d.date(), d.time(), d.tzinfo)``. If date is a - :class:`.datetime` object, its time components and :attr:`.tzinfo` attributes - are ignored. + ``d == datetime.combine(d.date(), d.time(), d.tzinfo)``. .. versionchanged:: 3.6 Added the *tzinfo* argument. From dfad678d7024ab86d265d84ed45999e031a03691 Mon Sep 17 00:00:00 2001 From: Yukihiro Nakadaira Date: Thu, 26 Jan 2023 17:28:34 +0900 Subject: [PATCH 013/225] gh-99952: [ctypes] fix refcount issues in from_param() result. (#100169) Fixes a reference counting issue with `ctypes.Structure` when a `from_param()` method call is used and the structure size is larger than a C pointer `sizeof(void*)`. This problem existed for a very long time, but became more apparent in 3.8+ by change likely due to garbage collection cleanup timing changes. --- Lib/test/test_ctypes/test_parameters.py | 52 +++++++++++++++++++ ...2-12-11-14-38-59.gh-issue-99952.IYGLzr.rst | 2 + Modules/_ctypes/_ctypes.c | 3 ++ Modules/_ctypes/_ctypes_test.c | 6 +++ 4 files changed, 63 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst diff --git a/Lib/test/test_ctypes/test_parameters.py b/Lib/test/test_ctypes/test_parameters.py index 22d290db1bcb7d..06cc95107b79fa 100644 --- a/Lib/test/test_ctypes/test_parameters.py +++ b/Lib/test/test_ctypes/test_parameters.py @@ -266,6 +266,58 @@ def test_parameter_repr(self): self.assertRegex(repr(c_wchar_p.from_param('hihi')), r"^$") self.assertRegex(repr(c_void_p.from_param(0x12)), r"^$") + @test.support.cpython_only + def test_from_param_result_refcount(self): + # Issue #99952 + import _ctypes_test + from ctypes import PyDLL, c_int, c_void_p, py_object, Structure + + class X(Structure): + """This struct size is <= sizeof(void*).""" + _fields_ = [("a", c_void_p)] + + def __del__(self): + trace.append(4) + + @classmethod + def from_param(cls, value): + trace.append(2) + return cls() + + PyList_Append = PyDLL(_ctypes_test.__file__)._testfunc_pylist_append + PyList_Append.restype = c_int + PyList_Append.argtypes = [py_object, py_object, X] + + trace = [] + trace.append(1) + PyList_Append(trace, 3, "dummy") + trace.append(5) + + self.assertEqual(trace, [1, 2, 3, 4, 5]) + + class Y(Structure): + """This struct size is > sizeof(void*).""" + _fields_ = [("a", c_void_p), ("b", c_void_p)] + + def __del__(self): + trace.append(4) + + @classmethod + def from_param(cls, value): + trace.append(2) + return cls() + + PyList_Append = PyDLL(_ctypes_test.__file__)._testfunc_pylist_append + PyList_Append.restype = c_int + PyList_Append.argtypes = [py_object, py_object, Y] + + trace = [] + trace.append(1) + PyList_Append(trace, 3, "dummy") + trace.append(5) + + self.assertEqual(trace, [1, 2, 3, 4, 5]) + ################################################################ if __name__ == '__main__': diff --git a/Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst b/Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst new file mode 100644 index 00000000000000..09ec961249534f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst @@ -0,0 +1,2 @@ +Fix a reference undercounting issue in :class:`ctypes.Structure` with ``from_param()`` +results larger than a C pointer. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 272cafb5a9a381..8690f2c1b07852 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -412,6 +412,7 @@ _ctypes_alloc_format_string_with_shape(int ndim, const Py_ssize_t *shape, typedef struct { PyObject_HEAD void *ptr; + PyObject *keep; // If set, a reference to the original CDataObject. } StructParamObject; @@ -419,6 +420,7 @@ static void StructParam_dealloc(PyObject *myself) { StructParamObject *self = (StructParamObject *)myself; + Py_XDECREF(self->keep); PyMem_Free(self->ptr); Py_TYPE(self)->tp_free(myself); } @@ -466,6 +468,7 @@ StructUnionType_paramfunc(CDataObject *self) StructParamObject *struct_param = (StructParamObject *)obj; struct_param->ptr = ptr; + struct_param->keep = Py_NewRef(self); } else { ptr = self->b_ptr; obj = Py_NewRef(self); diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c index e1f91b476a49fd..a8811d03cc91a2 100644 --- a/Modules/_ctypes/_ctypes_test.c +++ b/Modules/_ctypes/_ctypes_test.c @@ -1047,6 +1047,12 @@ EXPORT(long) _test_i38748_runCallback(_test_i38748_funcType callback, int a, int #endif +EXPORT(int) +_testfunc_pylist_append(PyObject *list, PyObject *item) +{ + return PyList_Append(list, item); +} + static struct PyModuleDef_Slot _ctypes_test_slots[] = { {0, NULL} }; From f2ac9510a5aa4f71d468f486140eae60f46833ad Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Thu, 26 Jan 2023 21:58:35 +0900 Subject: [PATCH 014/225] gh-85100: Migrate BPO link to the GitHub link for malloc warnings (gh-101343) --- Lib/test/support/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index e4e4de896dfff8..4a22ccdd4db403 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -582,7 +582,8 @@ def darwin_malloc_err_warning(test_name): msg = ' NOTICE ' detail = (f'{test_name} may generate "malloc can\'t allocate region"\n' 'warnings on macOS systems. This behavior is known. Do not\n' - 'report a bug unless tests are also failing. See bpo-40928.') + 'report a bug unless tests are also failing.\n' + 'See https://github.com/python/cpython/issues/85100') padding, _ = shutil.get_terminal_size() print(msg.center(padding, '-')) From 409f5337a3e466a5ef673797575cbd1745d27ca9 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Thu, 26 Jan 2023 18:16:27 +0400 Subject: [PATCH 015/225] gh-60580: Fix a wrong type of `ctypes.wintypes.BYTE` (#97579) Created from a patch file attached to an issue, by Anatoly Techtonik. --- Lib/ctypes/wintypes.py | 2 +- Lib/test/test_ctypes/test_wintypes.py | 19 +++++++++++++++++++ ...2-09-26-21-18-47.gh-issue-60580.0hBgde.rst | 3 +++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2022-09-26-21-18-47.gh-issue-60580.0hBgde.rst diff --git a/Lib/ctypes/wintypes.py b/Lib/ctypes/wintypes.py index c619d27596d40b..9c4e721438aad5 100644 --- a/Lib/ctypes/wintypes.py +++ b/Lib/ctypes/wintypes.py @@ -1,7 +1,7 @@ # The most useful windows datatypes import ctypes -BYTE = ctypes.c_byte +BYTE = ctypes.c_ubyte WORD = ctypes.c_ushort DWORD = ctypes.c_ulong diff --git a/Lib/test/test_ctypes/test_wintypes.py b/Lib/test/test_ctypes/test_wintypes.py index 243d5962ffa7f1..a01b9b1d0f3109 100644 --- a/Lib/test/test_ctypes/test_wintypes.py +++ b/Lib/test/test_ctypes/test_wintypes.py @@ -1,3 +1,6 @@ +# See +# for reference. + import unittest # also work on POSIX @@ -38,6 +41,22 @@ def test_variant_bool(self): vb.value = [] self.assertIs(vb.value, False) + def assertIsSigned(self, ctype): + self.assertLess(ctype(-1).value, 0) + + def assertIsUnsigned(self, ctype): + self.assertGreater(ctype(-1).value, 0) + + def test_signedness(self): + for ctype in (wintypes.BYTE, wintypes.WORD, wintypes.DWORD, + wintypes.BOOLEAN, wintypes.UINT, wintypes.ULONG): + with self.subTest(ctype=ctype): + self.assertIsUnsigned(ctype) + + for ctype in (wintypes.BOOL, wintypes.INT, wintypes.LONG): + with self.subTest(ctype=ctype): + self.assertIsSigned(ctype) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2022-09-26-21-18-47.gh-issue-60580.0hBgde.rst b/Misc/NEWS.d/next/Library/2022-09-26-21-18-47.gh-issue-60580.0hBgde.rst new file mode 100644 index 00000000000000..630e56cd2f7b87 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-09-26-21-18-47.gh-issue-60580.0hBgde.rst @@ -0,0 +1,3 @@ +:data:`ctypes.wintypes.BYTE` definition changed from +:data:`~ctypes.c_byte` to :data:`~ctypes.c_ubyte` to match Windows +SDK. Patch by Anatoly Techtonik and Oleg Iarygin. From 9f2c479eaf7d922746ef2f3c85b5c781757686b1 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 26 Jan 2023 09:15:05 -0800 Subject: [PATCH 016/225] gh-98831: Fix two bugs in case generator (#101349) Fix two bugs in case generator - UndefinedLocalError when generating metadata for an 'op' - Accidental newline inserted in test_generator.py --- Tools/cases_generator/generate_cases.py | 10 +++++++--- Tools/cases_generator/test_generator.py | 3 +-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index b7942410c82fc3..9d894d2ff57455 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -736,7 +736,7 @@ def stack_analysis( def get_stack_effect_info( self, thing: parser.InstDef | parser.Super | parser.Macro - ) -> tuple[Instruction, str, str]: + ) -> tuple[Instruction|None, str, str]: def effect_str(effect: list[StackEffect]) -> str: if getattr(thing, 'kind', None) == 'legacy': @@ -752,6 +752,9 @@ def effect_str(effect: list[StackEffect]) -> str: instr = self.instrs[thing.name] popped = effect_str(instr.input_effects) pushed = effect_str(instr.output_effects) + else: + instr = None + popped = pushed = "", "" case parser.Super(): instr = self.super_instrs[thing.name] popped = '+'.join(effect_str(comp.instr.input_effects) for comp in instr.parts) @@ -770,8 +773,9 @@ def write_stack_effect_functions(self) -> None: pushed_data = [] for thing in self.everything: instr, popped, pushed = self.get_stack_effect_info(thing) - popped_data.append( (instr, popped) ) - pushed_data.append( (instr, pushed) ) + if instr is not None: + popped_data.append( (instr, popped) ) + pushed_data.append( (instr, pushed) ) def write_function(direction: str, data: list[tuple[Instruction, str]]) -> None: self.out.emit("\n#ifndef NDEBUG"); diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index cf58e6aaf2b37c..bd1b974399abdd 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -358,8 +358,7 @@ def test_macro_instruction(): { PyObject *arg1 = _tmp_1; PyObject *interim; - uint16_t counter = re - ad_u16(&next_instr[0].cache); + uint16_t counter = read_u16(&next_instr[0].cache); interim = op1(arg1); _tmp_1 = interim; } From 8d18d1ffd52eb3917c4566b09596d596116a5532 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 26 Jan 2023 20:47:24 +0000 Subject: [PATCH 017/225] gh-99834: Update bundled copy of Tcl/Tk to 8.6.13.0 on Windows (GH-101307) --- .../Windows/2023-01-25-00-23-31.gh-issue-99834.WN41lc.rst | 1 + PC/layout/main.py | 2 +- PCbuild/_tkinter.vcxproj | 1 + PCbuild/get_externals.bat | 6 +++--- PCbuild/tcltk.props | 5 +++-- Tools/msi/tcltk/tcltk_files.wxs | 3 +++ 6 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-01-25-00-23-31.gh-issue-99834.WN41lc.rst diff --git a/Misc/NEWS.d/next/Windows/2023-01-25-00-23-31.gh-issue-99834.WN41lc.rst b/Misc/NEWS.d/next/Windows/2023-01-25-00-23-31.gh-issue-99834.WN41lc.rst new file mode 100644 index 00000000000000..d3894fa4ea3012 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-25-00-23-31.gh-issue-99834.WN41lc.rst @@ -0,0 +1 @@ +Updates bundled copy of Tcl/Tk to 8.6.13.0 diff --git a/PC/layout/main.py b/PC/layout/main.py index 17d27bba6640c5..c9246007d47d18 100644 --- a/PC/layout/main.py +++ b/PC/layout/main.py @@ -35,7 +35,7 @@ IDLE_DIRS_ONLY = FileNameSet("idlelib") -TCLTK_PYDS_ONLY = FileStemSet("tcl*", "tk*", "_tkinter") +TCLTK_PYDS_ONLY = FileStemSet("tcl*", "tk*", "_tkinter", "zlib1") TCLTK_DIRS_ONLY = FileNameSet("tkinter", "turtledemo") TCLTK_FILES_ONLY = FileNameSet("turtle.py") diff --git a/PCbuild/_tkinter.vcxproj b/PCbuild/_tkinter.vcxproj index af813b77c1d1c8..30cedcbb43de76 100644 --- a/PCbuild/_tkinter.vcxproj +++ b/PCbuild/_tkinter.vcxproj @@ -111,6 +111,7 @@ <_TclTkDLL Include="$(tcltkdir)\bin\$(tclDllName)" /> <_TclTkDLL Include="$(tcltkdir)\bin\$(tkDllName)" /> + <_TclTkDLL Include="$(tcltkdir)\bin\$(tclZlibDllName)" /> diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 7efdeb2d30a72c..0a41d131a3e887 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -55,8 +55,8 @@ set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.3 if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1s set libraries=%libraries% sqlite-3.39.4.0 -if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.12.1 -if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.12.1 +if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.13.0 +if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.13.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tix-8.4.3.6 set libraries=%libraries% xz-5.2.5 set libraries=%libraries% zlib-1.2.13 @@ -78,7 +78,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.3 if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1s -if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.12.1 +if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.13.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 for %%b in (%binaries%) do ( diff --git a/PCbuild/tcltk.props b/PCbuild/tcltk.props index 7fd43e8279e8e4..15c03e20fe2171 100644 --- a/PCbuild/tcltk.props +++ b/PCbuild/tcltk.props @@ -4,8 +4,8 @@ 8 6 - 12 - 1 + 13 + 0 $(TclMajorVersion) $(TclMinorVersion) $(TclPatchLevel) @@ -27,6 +27,7 @@ tclsh$(TclMajorVersion)$(TclMinorVersion)t$(TclDebugExt).exe tk$(TkMajorVersion)$(TkMinorVersion)t$(TclDebugExt).dll tk$(TkMajorVersion)$(TkMinorVersion)t$(TclDebugExt).lib + zlib1.dll tix$(TixMajorVersion)$(TixMinorVersion)$(TclDebugExt).dll $(tcltkDir)lib\tix$(TixMajorVersion).$(TixMinorVersion).$(TixPatchLevel)\$(tixDLLName) $(tcltkDir)lib\tcl$(TclMajorVersion)$(TclMinorVersion)t$(TclDebugExt).lib;$(tcltkDir)lib\tk$(TkMajorVersion)$(TkMinorVersion)t$(TclDebugExt).lib diff --git a/Tools/msi/tcltk/tcltk_files.wxs b/Tools/msi/tcltk/tcltk_files.wxs index 119451078096c4..5dad7c98d4f048 100644 --- a/Tools/msi/tcltk/tcltk_files.wxs +++ b/Tools/msi/tcltk/tcltk_files.wxs @@ -16,6 +16,9 @@ + + + From 37f15a5efab847b8aca47981ab596e9c36445bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D1=82=D0=B0=D0=BB=D0=B8=D0=B9=20=D0=94=D0=BC?= =?UTF-8?q?=D0=B8=D1=82=D1=80=D0=B8=D0=B5=D0=B2?= Date: Fri, 27 Jan 2023 02:04:11 +0300 Subject: [PATCH 018/225] Fix typos in pystate.c file (#101348) --- Python/pystate.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c index d31c1f166f222c..bf7688fd32134b 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -57,9 +57,9 @@ static void _PyThreadState_Delete(PyThreadState *tstate, int check_current); //------------------------------------------------- /* - The stored thread state is set by PyThraedState_Swap(). + The stored thread state is set by PyThreadState_Swap(). - For each of these functions, the GIL mus be held by the current thread. + For each of these functions, the GIL must be held by the current thread. */ static inline PyThreadState * @@ -232,7 +232,7 @@ unbind_tstate(PyThreadState *tstate) current_tss_clear(runtime); } - // We leave thread_id and native_thraed_id alone + // We leave thread_id and native_thread_id alone // since they can be useful for debugging. // Check the `_status` field to know if these values // are still valid. @@ -1140,7 +1140,7 @@ init_threadstate(PyThreadState *tstate, tstate->exc_info = &tstate->exc_state; // PyGILState_Release must not try to delete this thread state. - // This is cleared when PyGILState_Ensure() creates the thread sate. + // This is cleared when PyGILState_Ensure() creates the thread state. tstate->gilstate_counter = 1; tstate->cframe = &tstate->root_cframe; @@ -1220,7 +1220,7 @@ _PyThreadState_Prealloc(PyInterpreterState *interp) } // We keep this around for (accidental) stable ABI compatibility. -// Realisically, no extensions are using it. +// Realistically, no extensions are using it. void _PyThreadState_Init(PyThreadState *tstate) { From e5b08ddddf1099f04bf65e63017de840bd4b5980 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Fri, 27 Jan 2023 00:28:27 +0000 Subject: [PATCH 019/225] gh-101000: Add os.path.splitroot() (#101002) Co-authored-by: Eryk Sun Co-authored-by: Alex Waygood --- Doc/library/os.path.rst | 33 +++++ Doc/whatsnew/3.12.rst | 11 +- Lib/ntpath.py | 126 +++++++++------- Lib/pathlib.py | 24 +-- Lib/posixpath.py | 43 ++++-- Lib/test/test_ntpath.py | 138 ++++++++++++------ Lib/test/test_pathlib.py | 37 ----- Lib/test/test_posixpath.py | 29 ++++ ...-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst | 3 + 9 files changed, 279 insertions(+), 165 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst index 42bbe24830e6c1..786c2fd7f64fcc 100644 --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -488,6 +488,39 @@ the :mod:`glob` module.) Accepts a :term:`path-like object`. +.. function:: splitroot(path) + + Split the pathname *path* into a 3-item tuple ``(drive, root, tail)`` where + *drive* is a device name or mount point, *root* is a string of separators + after the drive, and *tail* is everything after the root. Any of these + items may be the empty string. In all cases, ``drive + root + tail`` will + be the same as *path*. + + On POSIX systems, *drive* is always empty. The *root* may be empty (if *path* is + relative), a single forward slash (if *path* is absolute), or two forward slashes + (implementation-defined per `IEEE Std 1003.1-2017; 4.13 Pathname Resolution + `_.) + For example:: + + >>> splitroot('/home/sam') + ('', '/', 'home/sam') + >>> splitroot('//home/sam') + ('', '//', 'home/sam') + >>> splitroot('///home/sam') + ('', '/', '//home/sam') + + On Windows, *drive* may be empty, a drive-letter name, a UNC share, or a device + name. The *root* may be empty, a forward slash, or a backward slash. For + example:: + + >>> splitroot('C:/Users/Sam') + ('C:', '/', 'Users/Sam') + >>> splitroot('//Server/Share/Users/Sam') + ('//Server/Share', '/', 'Users/Sam') + + .. versionadded:: 3.12 + + .. function:: splitext(path) Split the pathname *path* into a pair ``(root, ext)`` such that ``root + ext == diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 2f9ca1102d3d1b..a071159b800a34 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -288,13 +288,18 @@ os for a process with :func:`os.pidfd_open` in non-blocking mode. (Contributed by Kumar Aditya in :gh:`93312`.) -* Add :func:`os.path.isjunction` to check if a given path is a junction. - (Contributed by Charles Machalow in :gh:`99547`.) - * :class:`os.DirEntry` now includes an :meth:`os.DirEntry.is_junction` method to check if the entry is a junction. (Contributed by Charles Machalow in :gh:`99547`.) +os.path +------- + +* Add :func:`os.path.isjunction` to check if a given path is a junction. + (Contributed by Charles Machalow in :gh:`99547`.) + +* Add :func:`os.path.splitroot` to split a path into a triad + ``(drive, root, tail)``. (Contributed by Barney Gale in :gh:`101000`.) shutil ------ diff --git a/Lib/ntpath.py b/Lib/ntpath.py index cd7fb58a88de67..f9ee8e02a576b7 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -24,7 +24,7 @@ from genericpath import * -__all__ = ["normcase","isabs","join","splitdrive","split","splitext", +__all__ = ["normcase","isabs","join","splitdrive","splitroot","split","splitext", "basename","dirname","commonprefix","getsize","getmtime", "getatime","getctime", "islink","exists","lexists","isdir","isfile", "ismount", "expanduser","expandvars","normpath","abspath", @@ -117,19 +117,21 @@ def join(path, *paths): try: if not paths: path[:0] + sep #23780: Ensure compatible data type even if p is null. - result_drive, result_path = splitdrive(path) + result_drive, result_root, result_path = splitroot(path) for p in map(os.fspath, paths): - p_drive, p_path = splitdrive(p) - if p_path and p_path[0] in seps: + p_drive, p_root, p_path = splitroot(p) + if p_root: # Second path is absolute if p_drive or not result_drive: result_drive = p_drive + result_root = p_root result_path = p_path continue elif p_drive and p_drive != result_drive: if p_drive.lower() != result_drive.lower(): # Different drives => ignore the first path entirely result_drive = p_drive + result_root = p_root result_path = p_path continue # Same drive in different case @@ -139,10 +141,10 @@ def join(path, *paths): result_path = result_path + sep result_path = result_path + p_path ## add separator between UNC and non-absolute path - if (result_path and result_path[0] not in seps and + if (result_path and not result_root and result_drive and result_drive[-1:] != colon): return result_drive + sep + result_path - return result_drive + result_path + return result_drive + result_root + result_path except (TypeError, AttributeError, BytesWarning): genericpath._check_arg_types('join', path, *paths) raise @@ -169,35 +171,61 @@ def splitdrive(p): Paths cannot contain both a drive letter and a UNC path. + """ + drive, root, tail = splitroot(p) + return drive, root + tail + + +def splitroot(p): + """Split a pathname into drive, root and tail. The drive is defined + exactly as in splitdrive(). On Windows, the root may be a single path + separator or an empty string. The tail contains anything after the root. + For example: + + splitroot('//server/share/') == ('//server/share', '/', '') + splitroot('C:/Users/Barney') == ('C:', '/', 'Users/Barney') + splitroot('C:///spam///ham') == ('C:', '/', '//spam///ham') + splitroot('Windows/notepad') == ('', '', 'Windows/notepad') """ p = os.fspath(p) - if len(p) >= 2: - if isinstance(p, bytes): - sep = b'\\' - altsep = b'/' - colon = b':' - unc_prefix = b'\\\\?\\UNC\\' - else: - sep = '\\' - altsep = '/' - colon = ':' - unc_prefix = '\\\\?\\UNC\\' - normp = p.replace(altsep, sep) - if normp[0:2] == sep * 2: + if isinstance(p, bytes): + sep = b'\\' + altsep = b'/' + colon = b':' + unc_prefix = b'\\\\?\\UNC\\' + empty = b'' + else: + sep = '\\' + altsep = '/' + colon = ':' + unc_prefix = '\\\\?\\UNC\\' + empty = '' + normp = p.replace(altsep, sep) + if normp[:1] == sep: + if normp[1:2] == sep: # UNC drives, e.g. \\server\share or \\?\UNC\server\share # Device drives, e.g. \\.\device or \\?\device start = 8 if normp[:8].upper() == unc_prefix else 2 index = normp.find(sep, start) if index == -1: - return p, p[:0] + return p, empty, empty index2 = normp.find(sep, index + 1) if index2 == -1: - return p, p[:0] - return p[:index2], p[index2:] - if normp[1:2] == colon: - # Drive-letter drives, e.g. X: - return p[:2], p[2:] - return p[:0], p + return p, empty, empty + return p[:index2], p[index2:index2 + 1], p[index2 + 1:] + else: + # Relative path with root, e.g. \Windows + return empty, p[:1], p[1:] + elif normp[1:2] == colon: + if normp[2:3] == sep: + # Absolute drive-letter path, e.g. X:\Windows + return p[:2], p[2:3], p[3:] + else: + # Relative path with drive, e.g. X:Windows + return p[:2], empty, p[2:] + else: + # Relative path, e.g. Windows + return empty, empty, p # Split a path in head (everything up to the last '/') and tail (the @@ -212,15 +240,13 @@ def split(p): Either part may be empty.""" p = os.fspath(p) seps = _get_bothseps(p) - d, p = splitdrive(p) + d, r, p = splitroot(p) # set i to index beyond p's last slash i = len(p) while i and p[i-1] not in seps: i -= 1 head, tail = p[:i], p[i:] # now tail has no slashes - # remove trailing slashes from head, unless it's all slashes - head = head.rstrip(seps) or head - return d + head, tail + return d + r + head.rstrip(seps), tail # Split a path in root and extension. @@ -311,10 +337,10 @@ def ismount(path): path = os.fspath(path) seps = _get_bothseps(path) path = abspath(path) - root, rest = splitdrive(path) - if root and root[0] in seps: - return (not rest) or (rest in seps) - if rest and rest in seps: + drive, root, rest = splitroot(path) + if drive and drive[0] in seps: + return not rest + if root and not rest: return True if _getvolumepathname: @@ -525,13 +551,8 @@ def normpath(path): curdir = '.' pardir = '..' path = path.replace(altsep, sep) - prefix, path = splitdrive(path) - - # collapse initial backslashes - if path.startswith(sep): - prefix += sep - path = path.lstrip(sep) - + drive, root, path = splitroot(path) + prefix = drive + root comps = path.split(sep) i = 0 while i < len(comps): @@ -541,7 +562,7 @@ def normpath(path): if i > 0 and comps[i-1] != pardir: del comps[i-1:i+1] i -= 1 - elif i == 0 and prefix.endswith(sep): + elif i == 0 and root: del comps[i] else: i += 1 @@ -765,8 +786,8 @@ def relpath(path, start=None): try: start_abs = abspath(normpath(start)) path_abs = abspath(normpath(path)) - start_drive, start_rest = splitdrive(start_abs) - path_drive, path_rest = splitdrive(path_abs) + start_drive, _, start_rest = splitroot(start_abs) + path_drive, _, path_rest = splitroot(path_abs) if normcase(start_drive) != normcase(path_drive): raise ValueError("path is on mount %r, start on mount %r" % ( path_drive, start_drive)) @@ -816,21 +837,19 @@ def commonpath(paths): curdir = '.' try: - drivesplits = [splitdrive(p.replace(altsep, sep).lower()) for p in paths] - split_paths = [p.split(sep) for d, p in drivesplits] + drivesplits = [splitroot(p.replace(altsep, sep).lower()) for p in paths] + split_paths = [p.split(sep) for d, r, p in drivesplits] - try: - isabs, = set(p[:1] == sep for d, p in drivesplits) - except ValueError: - raise ValueError("Can't mix absolute and relative paths") from None + if len({r for d, r, p in drivesplits}) != 1: + raise ValueError("Can't mix absolute and relative paths") # Check that all drive letters or UNC paths match. The check is made only # now otherwise type errors for mixing strings and bytes would not be # caught. - if len(set(d for d, p in drivesplits)) != 1: + if len({d for d, r, p in drivesplits}) != 1: raise ValueError("Paths don't have the same drive") - drive, path = splitdrive(paths[0].replace(altsep, sep)) + drive, root, path = splitroot(paths[0].replace(altsep, sep)) common = path.split(sep) common = [c for c in common if c and c != curdir] @@ -844,8 +863,7 @@ def commonpath(paths): else: common = common[:len(s1)] - prefix = drive + sep if isabs else drive - return prefix + sep.join(common) + return drive + root + sep.join(common) except (TypeError, AttributeError): genericpath._check_arg_types('commonpath', *paths) raise diff --git a/Lib/pathlib.py b/Lib/pathlib.py index ae7a62f8a4cd65..17659bcd3e2d7f 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -271,19 +271,6 @@ def __reduce__(self): # when pickling related paths. return (self.__class__, tuple(self._parts)) - @classmethod - def _split_root(cls, part): - sep = cls._flavour.sep - rel = cls._flavour.splitdrive(part)[1].lstrip(sep) - anchor = part.removesuffix(rel) - if anchor: - anchor = cls._flavour.normpath(anchor) - drv, root = cls._flavour.splitdrive(anchor) - if drv.startswith(sep): - # UNC paths always have a root. - root = sep - return drv, root, rel - @classmethod def _parse_parts(cls, parts): if not parts: @@ -293,7 +280,10 @@ def _parse_parts(cls, parts): path = cls._flavour.join(*parts) if altsep: path = path.replace(altsep, sep) - drv, root, rel = cls._split_root(path) + drv, root, rel = cls._flavour.splitroot(path) + if drv.startswith(sep): + # pathlib assumes that UNC paths always have a root. + root = sep unfiltered_parsed = [drv + root] + rel.split(sep) parsed = [sys.intern(x) for x in unfiltered_parsed if x and x != '.'] return drv, root, parsed @@ -493,9 +483,9 @@ def with_name(self, name): """Return a new path with the file name changed.""" if not self.name: raise ValueError("%r has an empty name" % (self,)) - drv, root, parts = self._parse_parts((name,)) - if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep] - or drv or root or len(parts) != 1): + f = self._flavour + drv, root, tail = f.splitroot(name) + if drv or root or not tail or f.sep in tail or (f.altsep and f.altsep in tail): raise ValueError("Invalid name %r" % (name)) return self._from_parsed_parts(self._drv, self._root, self._parts[:-1] + [name]) diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 737f8a5c156d81..32b5d6e105dde9 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -28,7 +28,7 @@ import genericpath from genericpath import * -__all__ = ["normcase","isabs","join","splitdrive","split","splitext", +__all__ = ["normcase","isabs","join","splitdrive","splitroot","split","splitext", "basename","dirname","commonprefix","getsize","getmtime", "getatime","getctime","islink","exists","lexists","isdir","isfile", "ismount", "expanduser","expandvars","normpath","abspath", @@ -135,6 +135,35 @@ def splitdrive(p): return p[:0], p +def splitroot(p): + """Split a pathname into drive, root and tail. On Posix, drive is always + empty; the root may be empty, a single slash, or two slashes. The tail + contains anything after the root. For example: + + splitroot('foo/bar') == ('', '', 'foo/bar') + splitroot('/foo/bar') == ('', '/', 'foo/bar') + splitroot('//foo/bar') == ('', '//', 'foo/bar') + splitroot('///foo/bar') == ('', '/', '//foo/bar') + """ + p = os.fspath(p) + if isinstance(p, bytes): + sep = b'/' + empty = b'' + else: + sep = '/' + empty = '' + if p[:1] != sep: + # Relative path, e.g.: 'foo' + return empty, empty, p + elif p[1:2] != sep or p[2:3] == sep: + # Absolute path, e.g.: '/foo', '///foo', '////foo', etc. + return empty, sep, p[1:] + else: + # Precisely two leading slashes, e.g.: '//foo'. Implementation defined per POSIX, see + # https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 + return empty, p[:2], p[2:] + + # Return the tail (basename) part of a path, same as split(path)[1]. def basename(p): @@ -372,13 +401,7 @@ def normpath(path): dotdot = '..' if path == empty: return dot - initial_slashes = path.startswith(sep) - # POSIX allows one or two initial slashes, but treats three or more - # as single slash. - # (see https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13) - if (initial_slashes and - path.startswith(sep*2) and not path.startswith(sep*3)): - initial_slashes = 2 + _, initial_slashes, path = splitroot(path) comps = path.split(sep) new_comps = [] for comp in comps: @@ -390,9 +413,7 @@ def normpath(path): elif new_comps: new_comps.pop() comps = new_comps - path = sep.join(comps) - if initial_slashes: - path = sep*initial_slashes + path + path = initial_slashes + sep.join(comps) return path or dot else: diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index f56de0be772105..bce38a534a6a98 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -98,57 +98,106 @@ def test_splitext(self): tester('ntpath.splitext("c:a/b\\c.d")', ('c:a/b\\c', '.d')) def test_splitdrive(self): - tester('ntpath.splitdrive("c:\\foo\\bar")', - ('c:', '\\foo\\bar')) - tester('ntpath.splitdrive("c:/foo/bar")', - ('c:', '/foo/bar')) + tester("ntpath.splitdrive('')", ('', '')) + tester("ntpath.splitdrive('foo')", ('', 'foo')) + tester("ntpath.splitdrive('foo\\bar')", ('', 'foo\\bar')) + tester("ntpath.splitdrive('foo/bar')", ('', 'foo/bar')) + tester("ntpath.splitdrive('\\')", ('', '\\')) + tester("ntpath.splitdrive('/')", ('', '/')) + tester("ntpath.splitdrive('\\foo\\bar')", ('', '\\foo\\bar')) + tester("ntpath.splitdrive('/foo/bar')", ('', '/foo/bar')) + tester('ntpath.splitdrive("c:foo\\bar")', ('c:', 'foo\\bar')) + tester('ntpath.splitdrive("c:foo/bar")', ('c:', 'foo/bar')) + tester('ntpath.splitdrive("c:\\foo\\bar")', ('c:', '\\foo\\bar')) + tester('ntpath.splitdrive("c:/foo/bar")', ('c:', '/foo/bar')) + tester("ntpath.splitdrive('\\\\')", ('\\\\', '')) + tester("ntpath.splitdrive('//')", ('//', '')) tester('ntpath.splitdrive("\\\\conky\\mountpoint\\foo\\bar")', ('\\\\conky\\mountpoint', '\\foo\\bar')) tester('ntpath.splitdrive("//conky/mountpoint/foo/bar")', ('//conky/mountpoint', '/foo/bar')) - tester('ntpath.splitdrive("\\\\\\conky\\mountpoint\\foo\\bar")', - ('\\\\\\conky', '\\mountpoint\\foo\\bar')) - tester('ntpath.splitdrive("///conky/mountpoint/foo/bar")', - ('///conky', '/mountpoint/foo/bar')) - tester('ntpath.splitdrive("\\\\conky\\\\mountpoint\\foo\\bar")', - ('\\\\conky\\', '\\mountpoint\\foo\\bar')) - tester('ntpath.splitdrive("//conky//mountpoint/foo/bar")', - ('//conky/', '/mountpoint/foo/bar')) - # Issue #19911: UNC part containing U+0130 - self.assertEqual(ntpath.splitdrive('//conky/MOUNTPOİNT/foo/bar'), - ('//conky/MOUNTPOİNT', '/foo/bar')) - # gh-81790: support device namespace, including UNC drives. - tester('ntpath.splitdrive("//?/c:")', ("//?/c:", "")) - tester('ntpath.splitdrive("//?/c:/")', ("//?/c:", "/")) - tester('ntpath.splitdrive("//?/c:/dir")', ("//?/c:", "/dir")) - tester('ntpath.splitdrive("//?/UNC")', ("//?/UNC", "")) - tester('ntpath.splitdrive("//?/UNC/")', ("//?/UNC/", "")) - tester('ntpath.splitdrive("//?/UNC/server/")', ("//?/UNC/server/", "")) - tester('ntpath.splitdrive("//?/UNC/server/share")', ("//?/UNC/server/share", "")) - tester('ntpath.splitdrive("//?/UNC/server/share/dir")', ("//?/UNC/server/share", "/dir")) - tester('ntpath.splitdrive("//?/VOLUME{00000000-0000-0000-0000-000000000000}/spam")', - ('//?/VOLUME{00000000-0000-0000-0000-000000000000}', '/spam')) - tester('ntpath.splitdrive("//?/BootPartition/")', ("//?/BootPartition", "/")) - - tester('ntpath.splitdrive("\\\\?\\c:")', ("\\\\?\\c:", "")) - tester('ntpath.splitdrive("\\\\?\\c:\\")', ("\\\\?\\c:", "\\")) - tester('ntpath.splitdrive("\\\\?\\c:\\dir")', ("\\\\?\\c:", "\\dir")) - tester('ntpath.splitdrive("\\\\?\\UNC")', ("\\\\?\\UNC", "")) - tester('ntpath.splitdrive("\\\\?\\UNC\\")', ("\\\\?\\UNC\\", "")) - tester('ntpath.splitdrive("\\\\?\\UNC\\server\\")', ("\\\\?\\UNC\\server\\", "")) - tester('ntpath.splitdrive("\\\\?\\UNC\\server\\share")', ("\\\\?\\UNC\\server\\share", "")) tester('ntpath.splitdrive("\\\\?\\UNC\\server\\share\\dir")', ("\\\\?\\UNC\\server\\share", "\\dir")) - tester('ntpath.splitdrive("\\\\?\\VOLUME{00000000-0000-0000-0000-000000000000}\\spam")', - ('\\\\?\\VOLUME{00000000-0000-0000-0000-000000000000}', '\\spam')) - tester('ntpath.splitdrive("\\\\?\\BootPartition\\")', ("\\\\?\\BootPartition", "\\")) + tester('ntpath.splitdrive("//?/UNC/server/share/dir")', + ("//?/UNC/server/share", "/dir")) + + def test_splitroot(self): + tester("ntpath.splitroot('')", ('', '', '')) + tester("ntpath.splitroot('foo')", ('', '', 'foo')) + tester("ntpath.splitroot('foo\\bar')", ('', '', 'foo\\bar')) + tester("ntpath.splitroot('foo/bar')", ('', '', 'foo/bar')) + tester("ntpath.splitroot('\\')", ('', '\\', '')) + tester("ntpath.splitroot('/')", ('', '/', '')) + tester("ntpath.splitroot('\\foo\\bar')", ('', '\\', 'foo\\bar')) + tester("ntpath.splitroot('/foo/bar')", ('', '/', 'foo/bar')) + tester('ntpath.splitroot("c:foo\\bar")', ('c:', '', 'foo\\bar')) + tester('ntpath.splitroot("c:foo/bar")', ('c:', '', 'foo/bar')) + tester('ntpath.splitroot("c:\\foo\\bar")', ('c:', '\\', 'foo\\bar')) + tester('ntpath.splitroot("c:/foo/bar")', ('c:', '/', 'foo/bar')) + + # Redundant slashes are not included in the root. + tester("ntpath.splitroot('c:\\\\a')", ('c:', '\\', '\\a')) + tester("ntpath.splitroot('c:\\\\\\a/b')", ('c:', '\\', '\\\\a/b')) + + # Mixed path separators. + tester("ntpath.splitroot('c:/\\')", ('c:', '/', '\\')) + tester("ntpath.splitroot('c:\\/')", ('c:', '\\', '/')) + tester("ntpath.splitroot('/\\a/b\\/\\')", ('/\\a/b', '\\', '/\\')) + tester("ntpath.splitroot('\\/a\\b/\\/')", ('\\/a\\b', '/', '\\/')) + + # UNC paths. + tester("ntpath.splitroot('\\\\')", ('\\\\', '', '')) + tester("ntpath.splitroot('//')", ('//', '', '')) + tester('ntpath.splitroot("\\\\conky\\mountpoint\\foo\\bar")', + ('\\\\conky\\mountpoint', '\\', 'foo\\bar')) + tester('ntpath.splitroot("//conky/mountpoint/foo/bar")', + ('//conky/mountpoint', '/', 'foo/bar')) + tester('ntpath.splitroot("\\\\\\conky\\mountpoint\\foo\\bar")', + ('\\\\\\conky', '\\', 'mountpoint\\foo\\bar')) + tester('ntpath.splitroot("///conky/mountpoint/foo/bar")', + ('///conky', '/', 'mountpoint/foo/bar')) + tester('ntpath.splitroot("\\\\conky\\\\mountpoint\\foo\\bar")', + ('\\\\conky\\', '\\', 'mountpoint\\foo\\bar')) + tester('ntpath.splitroot("//conky//mountpoint/foo/bar")', + ('//conky/', '/', 'mountpoint/foo/bar')) + + # Issue #19911: UNC part containing U+0130 + self.assertEqual(ntpath.splitroot('//conky/MOUNTPOİNT/foo/bar'), + ('//conky/MOUNTPOİNT', '/', 'foo/bar')) + + # gh-81790: support device namespace, including UNC drives. + tester('ntpath.splitroot("//?/c:")', ("//?/c:", "", "")) + tester('ntpath.splitroot("//?/c:/")', ("//?/c:", "/", "")) + tester('ntpath.splitroot("//?/c:/dir")', ("//?/c:", "/", "dir")) + tester('ntpath.splitroot("//?/UNC")', ("//?/UNC", "", "")) + tester('ntpath.splitroot("//?/UNC/")', ("//?/UNC/", "", "")) + tester('ntpath.splitroot("//?/UNC/server/")', ("//?/UNC/server/", "", "")) + tester('ntpath.splitroot("//?/UNC/server/share")', ("//?/UNC/server/share", "", "")) + tester('ntpath.splitroot("//?/UNC/server/share/dir")', ("//?/UNC/server/share", "/", "dir")) + tester('ntpath.splitroot("//?/VOLUME{00000000-0000-0000-0000-000000000000}/spam")', + ('//?/VOLUME{00000000-0000-0000-0000-000000000000}', '/', 'spam')) + tester('ntpath.splitroot("//?/BootPartition/")', ("//?/BootPartition", "/", "")) + + tester('ntpath.splitroot("\\\\?\\c:")', ("\\\\?\\c:", "", "")) + tester('ntpath.splitroot("\\\\?\\c:\\")', ("\\\\?\\c:", "\\", "")) + tester('ntpath.splitroot("\\\\?\\c:\\dir")', ("\\\\?\\c:", "\\", "dir")) + tester('ntpath.splitroot("\\\\?\\UNC")', ("\\\\?\\UNC", "", "")) + tester('ntpath.splitroot("\\\\?\\UNC\\")', ("\\\\?\\UNC\\", "", "")) + tester('ntpath.splitroot("\\\\?\\UNC\\server\\")', ("\\\\?\\UNC\\server\\", "", "")) + tester('ntpath.splitroot("\\\\?\\UNC\\server\\share")', + ("\\\\?\\UNC\\server\\share", "", "")) + tester('ntpath.splitroot("\\\\?\\UNC\\server\\share\\dir")', + ("\\\\?\\UNC\\server\\share", "\\", "dir")) + tester('ntpath.splitroot("\\\\?\\VOLUME{00000000-0000-0000-0000-000000000000}\\spam")', + ('\\\\?\\VOLUME{00000000-0000-0000-0000-000000000000}', '\\', 'spam')) + tester('ntpath.splitroot("\\\\?\\BootPartition\\")', ("\\\\?\\BootPartition", "\\", "")) # gh-96290: support partial/invalid UNC drives - tester('ntpath.splitdrive("//")', ("//", "")) # empty server & missing share - tester('ntpath.splitdrive("///")', ("///", "")) # empty server & empty share - tester('ntpath.splitdrive("///y")', ("///y", "")) # empty server & non-empty share - tester('ntpath.splitdrive("//x")', ("//x", "")) # non-empty server & missing share - tester('ntpath.splitdrive("//x/")', ("//x/", "")) # non-empty server & empty share + tester('ntpath.splitroot("//")', ("//", "", "")) # empty server & missing share + tester('ntpath.splitroot("///")', ("///", "", "")) # empty server & empty share + tester('ntpath.splitroot("///y")', ("///y", "", "")) # empty server & non-empty share + tester('ntpath.splitroot("//x")', ("//x", "", "")) # non-empty server & missing share + tester('ntpath.splitroot("//x/")', ("//x/", "", "")) # non-empty server & empty share def test_split(self): tester('ntpath.split("c:\\foo\\bar")', ('c:\\foo', 'bar')) @@ -930,6 +979,9 @@ def test_path_splitext(self): def test_path_splitdrive(self): self._check_function(self.path.splitdrive) + def test_path_splitroot(self): + self._check_function(self.path.splitroot) + def test_path_basename(self): self._check_function(self.path.basename) diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 1fe242b7f6ab14..a596795b44f0fa 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -80,26 +80,6 @@ def test_parse_parts(self): check(['c:\\a'], ('', '', ['c:\\a'])) check(['\\a'], ('', '', ['\\a'])) - def test_splitroot(self): - f = self.cls._split_root - self.assertEqual(f(''), ('', '', '')) - self.assertEqual(f('a'), ('', '', 'a')) - self.assertEqual(f('a/b'), ('', '', 'a/b')) - self.assertEqual(f('a/b/'), ('', '', 'a/b/')) - self.assertEqual(f('/a'), ('', '/', 'a')) - self.assertEqual(f('/a/b'), ('', '/', 'a/b')) - self.assertEqual(f('/a/b/'), ('', '/', 'a/b/')) - # The root is collapsed when there are redundant slashes - # except when there are exactly two leading slashes, which - # is a special case in POSIX. - self.assertEqual(f('//a'), ('', '//', 'a')) - self.assertEqual(f('///a'), ('', '/', 'a')) - self.assertEqual(f('///a/b'), ('', '/', 'a/b')) - # Paths which look like NT paths aren't treated specially. - self.assertEqual(f('c:/a/b'), ('', '', 'c:/a/b')) - self.assertEqual(f('\\/a/b'), ('', '', '\\/a/b')) - self.assertEqual(f('\\a\\b'), ('', '', '\\a\\b')) - class NTFlavourTest(_BaseFlavourTest, unittest.TestCase): cls = pathlib.PureWindowsPath @@ -143,23 +123,6 @@ def test_parse_parts(self): check(['c:/a/b', 'c:x/y'], ('c:', '\\', ['c:\\', 'a', 'b', 'x', 'y'])) check(['c:/a/b', 'c:/x/y'], ('c:', '\\', ['c:\\', 'x', 'y'])) - def test_splitroot(self): - f = self.cls._split_root - self.assertEqual(f(''), ('', '', '')) - self.assertEqual(f('a'), ('', '', 'a')) - self.assertEqual(f('a\\b'), ('', '', 'a\\b')) - self.assertEqual(f('\\a'), ('', '\\', 'a')) - self.assertEqual(f('\\a\\b'), ('', '\\', 'a\\b')) - self.assertEqual(f('c:a\\b'), ('c:', '', 'a\\b')) - self.assertEqual(f('c:\\a\\b'), ('c:', '\\', 'a\\b')) - # Redundant slashes in the root are collapsed. - self.assertEqual(f('c:\\\\a'), ('c:', '\\', 'a')) - self.assertEqual(f('c:\\\\\\a/b'), ('c:', '\\', 'a/b')) - # Valid UNC paths. - self.assertEqual(f('\\\\a\\b'), ('\\\\a\\b', '\\', '')) - self.assertEqual(f('\\\\a\\b\\'), ('\\\\a\\b', '\\', '')) - self.assertEqual(f('\\\\a\\b\\c\\d'), ('\\\\a\\b', '\\', 'c\\d')) - # # Tests for the pure classes. diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index 6c1c0f5577b7ec..9be4640f970aef 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -115,6 +115,32 @@ def test_splitext(self): self.splitextTest("........", "........", "") self.splitextTest("", "", "") + def test_splitroot(self): + f = posixpath.splitroot + self.assertEqual(f(''), ('', '', '')) + self.assertEqual(f('a'), ('', '', 'a')) + self.assertEqual(f('a/b'), ('', '', 'a/b')) + self.assertEqual(f('a/b/'), ('', '', 'a/b/')) + self.assertEqual(f('/a'), ('', '/', 'a')) + self.assertEqual(f('/a/b'), ('', '/', 'a/b')) + self.assertEqual(f('/a/b/'), ('', '/', 'a/b/')) + # The root is collapsed when there are redundant slashes + # except when there are exactly two leading slashes, which + # is a special case in POSIX. + self.assertEqual(f('//a'), ('', '//', 'a')) + self.assertEqual(f('///a'), ('', '/', '//a')) + self.assertEqual(f('///a/b'), ('', '/', '//a/b')) + # Paths which look like NT paths aren't treated specially. + self.assertEqual(f('c:/a/b'), ('', '', 'c:/a/b')) + self.assertEqual(f('\\/a/b'), ('', '', '\\/a/b')) + self.assertEqual(f('\\a\\b'), ('', '', '\\a\\b')) + # Byte paths are supported + self.assertEqual(f(b''), (b'', b'', b'')) + self.assertEqual(f(b'a'), (b'', b'', b'a')) + self.assertEqual(f(b'/a'), (b'', b'/', b'a')) + self.assertEqual(f(b'//a'), (b'', b'//', b'a')) + self.assertEqual(f(b'///a'), (b'', b'/', b'//a')) + def test_isabs(self): self.assertIs(posixpath.isabs(""), False) self.assertIs(posixpath.isabs("/"), True) @@ -752,6 +778,9 @@ def test_path_splitext(self): def test_path_splitdrive(self): self.assertPathEqual(self.path.splitdrive) + def test_path_splitroot(self): + self.assertPathEqual(self.path.splitroot) + def test_path_basename(self): self.assertPathEqual(self.path.basename) diff --git a/Misc/NEWS.d/next/Library/2023-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst b/Misc/NEWS.d/next/Library/2023-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst new file mode 100644 index 00000000000000..2082361c41d697 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst @@ -0,0 +1,3 @@ +Add :func:`os.path.splitroot()`, which splits a path into a 3-item tuple +``(drive, root, tail)``. This new function is used by :mod:`pathlib` to +improve the performance of path construction by up to a third. From 7956e0c30001cc0940caa66fab4d72455c865b3a Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Fri, 27 Jan 2023 01:56:19 -0600 Subject: [PATCH 020/225] Speed-up and improve accuracy with Rump Algorithms (3.1) and (5.10) (GH-101366) --- Modules/mathmodule.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 69907ea04ec812..2da50bbd54b57f 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2851,17 +2851,17 @@ typedef struct{ double hi; double lo; } DoubleLength; static inline DoubleLength twosum(double a, double b) { - double s = a + b; - double ap = s - b; - double bp = s - a; - double da = a - ap; - double db = b - bp; - double t = da + db; - return (DoubleLength) {s, t}; + // Rump Algorithm 3.1 Error-free transformation of the sum + double x = a + b; + double z = x - a; + double y = (a - (x - z)) + (b - z); + return (DoubleLength) {x, y}; } static inline DoubleLength dl_split(double x) { + // Rump Algorithm 3.2 Error-free splitting of a floating point number + // Dekker (5.5) and (5.6). double t = x * 134217729.0; // Veltkamp constant = 2.0 ** 27 + 1 double hi = t - (t - x); double lo = x - hi; @@ -2871,7 +2871,7 @@ dl_split(double x) { static inline DoubleLength dl_mul(double x, double y) { - /* Dekker mul12(). Section (5.12) */ + // Dekker (5.12) and mul12() DoubleLength xx = dl_split(x); DoubleLength yy = dl_split(y); double p = xx.hi * yy.hi; @@ -2881,24 +2881,19 @@ dl_mul(double x, double y) return (DoubleLength) {z, zz}; } -typedef struct{ double hi; double lo; double tiny; } TripleLength; +typedef struct { double hi; double lo; double tiny; } TripleLength; static const TripleLength tl_zero = {0.0, 0.0, 0.0}; static inline TripleLength -tl_add(TripleLength total, double x) +tl_fma(TripleLength total, double x, double y) { - DoubleLength s = twosum(x, total.hi); - DoubleLength t = twosum(s.lo, total.lo); - return (TripleLength) {s.hi, t.hi, t.lo + total.tiny}; -} - -static inline TripleLength -tl_fma(TripleLength total, double p, double q) -{ - DoubleLength product = dl_mul(p, q); - total = tl_add(total, product.hi); - return tl_add(total, product.lo); + // Rump Algorithm 5.10 with K=3 and using SumKVert + DoubleLength pr = dl_mul(x, y); + DoubleLength sm = twosum(total.hi, pr.hi); + DoubleLength r1 = twosum(total.lo, pr.lo); + DoubleLength r2 = twosum(r1.hi, sm.lo); + return (TripleLength) {sm.hi, r2.hi, total.tiny + r1.lo + r2.lo}; } static inline double From 737d367b1f4bad76914173a64d6bbe19a984cd5f Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 27 Jan 2023 14:45:08 +0000 Subject: [PATCH 021/225] gh-77532: Minor tweaks to allow compiling with PlatformToolset=ClangCL on Windows (GH-101352) To use this, ensure that clang support was selected in Visual Studio Installer, then set the PlatformToolset environment variable to "ClangCL" and build as normal from the command line. It remains unsupported, but at least is possible now for experimentation. --- Include/cpython/pytime.h | 4 +++ Include/internal/pycore_tracemalloc.h | 6 ++-- ...3-01-26-19-02-11.gh-issue-77532.cXD8bg.rst | 1 + Modules/_decimal/libmpdec/mpdecimal.c | 6 ++++ PC/launcher2.c | 12 ++++--- PC/pyconfig.h | 34 ++++++++++--------- PCbuild/pyproject.props | 7 ++-- 7 files changed, 46 insertions(+), 24 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2023-01-26-19-02-11.gh-issue-77532.cXD8bg.rst diff --git a/Include/cpython/pytime.h b/Include/cpython/pytime.h index e64f3b13e75ca1..16d88d191e9e25 100644 --- a/Include/cpython/pytime.h +++ b/Include/cpython/pytime.h @@ -53,6 +53,10 @@ functions and constants extern "C" { #endif +#ifdef __clang__ +struct timeval; +#endif + /* _PyTime_t: Python timestamp with subsecond precision. It can be used to store a duration, and so indirectly a date (related to another date, like UNIX epoch). */ diff --git a/Include/internal/pycore_tracemalloc.h b/Include/internal/pycore_tracemalloc.h index 08d7d1096c78ce..d086adc61c319b 100644 --- a/Include/internal/pycore_tracemalloc.h +++ b/Include/internal/pycore_tracemalloc.h @@ -36,11 +36,13 @@ struct _PyTraceMalloc_Config { /* Pack the frame_t structure to reduce the memory footprint on 64-bit architectures: 12 bytes instead of 16. */ +#if defined(_MSC_VER) +#pragma pack(push, 4) +#endif + struct #ifdef __GNUC__ __attribute__((packed)) -#elif defined(_MSC_VER) -#pragma pack(push, 4) #endif tracemalloc_frame { /* filename cannot be NULL: "" is used if the Python frame diff --git a/Misc/NEWS.d/next/Build/2023-01-26-19-02-11.gh-issue-77532.cXD8bg.rst b/Misc/NEWS.d/next/Build/2023-01-26-19-02-11.gh-issue-77532.cXD8bg.rst new file mode 100644 index 00000000000000..5a746dca2e7d8d --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-01-26-19-02-11.gh-issue-77532.cXD8bg.rst @@ -0,0 +1 @@ +Minor fixes to allow building with ``PlatformToolset=ClangCL`` on Windows. diff --git a/Modules/_decimal/libmpdec/mpdecimal.c b/Modules/_decimal/libmpdec/mpdecimal.c index f1626df46ed46f..959934bda7a449 100644 --- a/Modules/_decimal/libmpdec/mpdecimal.c +++ b/Modules/_decimal/libmpdec/mpdecimal.c @@ -76,6 +76,12 @@ #endif #endif +/* ClangCL claims to support 128-bit int, but doesn't */ +#if defined(__SIZEOF_INT128__) && defined(__clang__) && defined(_MSC_VER) +#undef __SIZEOF_INT128__ +#endif + + #define MPD_NEWTONDIV_CUTOFF 1024L diff --git a/PC/launcher2.c b/PC/launcher2.c index 4c77ec0be43914..2052a2ffeb57b7 100644 --- a/PC/launcher2.c +++ b/PC/launcher2.c @@ -465,10 +465,14 @@ dumpSearchInfo(SearchInfo *search) return; } -#define DEBUGNAME(s) L"SearchInfo." ## s -#define DEBUG(s) debug(DEBUGNAME(#s) L": %s\n", (search->s) ? (search->s) : L"(null)") -#define DEBUG_2(s, sl) _debugStringAndLength((search->s), (search->sl), DEBUGNAME(#s)) -#define DEBUG_BOOL(s) debug(DEBUGNAME(#s) L": %s\n", (search->s) ? L"True" : L"False") +#ifdef __clang__ +#define DEBUGNAME(s) L # s +#else +#define DEBUGNAME(s) # s +#endif +#define DEBUG(s) debug(L"SearchInfo." DEBUGNAME(s) L": %s\n", (search->s) ? (search->s) : L"(null)") +#define DEBUG_2(s, sl) _debugStringAndLength((search->s), (search->sl), L"SearchInfo." DEBUGNAME(s)) +#define DEBUG_BOOL(s) debug(L"SearchInfo." DEBUGNAME(s) L": %s\n", (search->s) ? L"True" : L"False") DEBUG(originalCmdLine); DEBUG(restOfCmdLine); DEBUG(executablePath); diff --git a/PC/pyconfig.h b/PC/pyconfig.h index 1d8408b363a66a..f5166a1506c945 100644 --- a/PC/pyconfig.h +++ b/PC/pyconfig.h @@ -76,30 +76,26 @@ WIN32 is still required for the locale module. /* Compiler specific defines */ /* ------------------------------------------------------------------------*/ -/* Microsoft C defines _MSC_VER */ +/* Microsoft C defines _MSC_VER, as does clang-cl.exe */ #ifdef _MSC_VER /* We want COMPILER to expand to a string containing _MSC_VER's *value*. * This is horridly tricky, because the stringization operator only works * on macro arguments, and doesn't evaluate macros passed *as* arguments. - * Attempts simpler than the following appear doomed to produce "_MSC_VER" - * literally in the string. */ #define _Py_PASTE_VERSION(SUFFIX) \ ("[MSC v." _Py_STRINGIZE(_MSC_VER) " " SUFFIX "]") /* e.g., this produces, after compile-time string catenation, - * ("[MSC v.1200 32 bit (Intel)]") + * ("[MSC v.1900 64 bit (Intel)]") * * _Py_STRINGIZE(_MSC_VER) expands to - * _Py_STRINGIZE1((_MSC_VER)) expands to - * _Py_STRINGIZE2(_MSC_VER) but as this call is the result of token-pasting - * it's scanned again for macros and so further expands to (under MSVC 6) - * _Py_STRINGIZE2(1200) which then expands to - * "1200" + * _Py_STRINGIZE1(_MSC_VER) and this second macro call is scanned + * again for macros and so further expands to + * _Py_STRINGIZE1(1900) which then expands to + * "1900" */ -#define _Py_STRINGIZE(X) _Py_STRINGIZE1((X)) -#define _Py_STRINGIZE1(X) _Py_STRINGIZE2 ## X -#define _Py_STRINGIZE2(X) #X +#define _Py_STRINGIZE(X) _Py_STRINGIZE1(X) +#define _Py_STRINGIZE1(X) #X /* MSVC defines _WINxx to differentiate the windows platform types @@ -122,13 +118,16 @@ WIN32 is still required for the locale module. */ #ifdef MS_WIN64 #if defined(_M_X64) || defined(_M_AMD64) -#if defined(__INTEL_COMPILER) +#if defined(__clang__) +#define COMPILER ("[Clang " __clang_version__ "] 64 bit (AMD64) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") +#define PY_SUPPORT_TIER 0 +#elif defined(__INTEL_COMPILER) #define COMPILER ("[ICC v." _Py_STRINGIZE(__INTEL_COMPILER) " 64 bit (amd64) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") #define PY_SUPPORT_TIER 0 #else #define COMPILER _Py_PASTE_VERSION("64 bit (AMD64)") #define PY_SUPPORT_TIER 1 -#endif /* __INTEL_COMPILER */ +#endif /* __clang__ */ #define PYD_PLATFORM_TAG "win_amd64" #elif defined(_M_ARM64) #define COMPILER _Py_PASTE_VERSION("64 bit (ARM64)") @@ -181,13 +180,16 @@ typedef _W64 int Py_ssize_t; #if defined(MS_WIN32) && !defined(MS_WIN64) #if defined(_M_IX86) -#if defined(__INTEL_COMPILER) +#if defined(__clang__) +#define COMPILER ("[Clang " __clang_version__ "] 32 bit (Intel) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") +#define PY_SUPPORT_TIER 0 +#elif defined(__INTEL_COMPILER) #define COMPILER ("[ICC v." _Py_STRINGIZE(__INTEL_COMPILER) " 32 bit (Intel) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") #define PY_SUPPORT_TIER 0 #else #define COMPILER _Py_PASTE_VERSION("32 bit (Intel)") #define PY_SUPPORT_TIER 1 -#endif /* __INTEL_COMPILER */ +#endif /* __clang__ */ #define PYD_PLATFORM_TAG "win32" #elif defined(_M_ARM) #define COMPILER _Py_PASTE_VERSION("32 bit (ARM)") diff --git a/PCbuild/pyproject.props b/PCbuild/pyproject.props index e398b333572e9d..92c7849d3bcf75 100644 --- a/PCbuild/pyproject.props +++ b/PCbuild/pyproject.props @@ -25,7 +25,8 @@ <_DebugPreprocessorDefinition>NDEBUG; <_DebugPreprocessorDefinition Condition="$(Configuration) == 'Debug'">_DEBUG; <_PlatformPreprocessorDefinition>_WIN32; - <_PlatformPreprocessorDefinition Condition="$(Platform) == 'x64'">_WIN64;_M_X64; + <_PlatformPreprocessorDefinition Condition="$(Platform) == 'x64'">_WIN64; + <_PlatformPreprocessorDefinition Condition="$(Platform) == 'x64' and $(PlatformToolset) != 'ClangCL'">_M_X64;$(_PlatformPreprocessorDefinition) <_Py3NamePreprocessorDefinition>PY3_DLLNAME=L"$(Py3DllName)"; @@ -45,8 +46,10 @@ true true $(EnableControlFlowGuard) - /utf-8 %(AdditionalOptions) true + /utf-8 %(AdditionalOptions) + -Wno-deprecated-non-prototype -Wno-unused-label -Wno-pointer-sign -Wno-incompatible-pointer-types-discards-qualifiers -Wno-unused-function %(AdditionalOptions) + -flto %(AdditionalOptions) OnlyExplicitInline From d083df39fa61dc27b3c22a7bc06a71e95b718fa3 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Fri, 27 Jan 2023 14:50:09 +0000 Subject: [PATCH 022/225] [doc] Add some notices to logging configuration documentation. (GH-101373) --- Doc/library/logging.config.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst index 9b82c1f75e3f07..b4d0da1421dc3d 100644 --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -525,6 +525,11 @@ returned by the call:: my.package.customFormatterFactory(bar='baz', spam=99.9, answer=42) +.. warning:: The values for keys such as ``bar``, ``spam`` and ``answer`` in + the above example should not be configuration dictionaries or references such + as ``cfg://foo`` or ``ext://bar``, because they will not be processed by the + configuration machinery, but passed to the callable as-is. + The key ``'()'`` has been used as the special key because it is not a valid keyword parameter name, and so will not clash with the names of the keyword arguments used in the call. The ``'()'`` also serves as a @@ -553,6 +558,11 @@ following configuration:: the returned formatter will have attribute ``foo`` set to ``'bar'`` and attribute ``baz`` set to ``'bozz'``. +.. warning:: The values for attributes such as ``foo`` and ``baz`` in + the above example should not be configuration dictionaries or references such + as ``cfg://foo`` or ``ext://bar``, because they will not be processed by the + configuration machinery, but set as attribute values as-is. + .. _logging-config-dict-externalobj: From b5c4d6064cc2ae7042d2dc7b533d74f6c4176a38 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Fri, 27 Jan 2023 19:01:30 +0000 Subject: [PATCH 023/225] [doc] Add a section on logging handler configuration order. (GH-101380) --- Doc/library/logging.config.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst index b4d0da1421dc3d..2daf2422ebd5b4 100644 --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -564,6 +564,29 @@ attribute ``baz`` set to ``'bozz'``. configuration machinery, but set as attribute values as-is. +.. _handler-config-dict-order: + +Handler configuration order +""""""""""""""""""""""""""" + +Handlers are configured in alphabetical order of their keys, and a configured +handler replaces the configuration dictionary in (a working copy of) the +``handlers`` dictionary in the schema. If you use a construct such as +``cfg://handlers.foo``, then initially ``handlers['foo']`` points to the +configuration dictionary for the handler named ``foo``, and later (once that +handler has been configured) it points to the configured handler instance. +Thus, ``cfg://handlers.foo`` could resolve to either a dictionary or a handler +instance. In general, it is wise to name handlers in a way such that dependent +handlers are configured _after_ any handlers they depend on; that allows +something like ``cfg://handlers.foo`` to be used in configuring a handler that +depends on handler ``foo``. If that dependent handler were named ``bar``, +problems would result, because the configuration of ``bar`` would be attempted +before that of ``foo``, and ``foo`` would not yet have been configured. +However, if the dependent handler were named ``foobar``, it would be configured +after ``foo``, with the result that ``cfg://handlers.foo`` would resolve to +configured handler ``foo``, and not its configuration dictionary. + + .. _logging-config-dict-externalobj: Access to external objects From 8cef9c0f92720f6810be1c74e00f611acb4b8b1e Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sat, 28 Jan 2023 11:08:08 +0900 Subject: [PATCH 024/225] gh-101341: Remove unncessary enum._power_of_two function (gh-101342) --- Lib/enum.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Lib/enum.py b/Lib/enum.py index 4658393d756e07..adb61519abe942 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -1565,11 +1565,6 @@ def unique(enumeration): (enumeration, alias_details)) return enumeration -def _power_of_two(value): - if value < 1: - return False - return value == 2 ** _high_bit(value) - def _dataclass_repr(self): dcf = self.__dataclass_fields__ return ', '.join( From 052f53d65d9f65c7c3223a383857ad07a182c2d7 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 27 Jan 2023 18:35:14 -0800 Subject: [PATCH 025/225] gh-39615: Add warnings.warn() skip_file_prefixes support (#100840) `warnings.warn()` gains the ability to skip stack frames based on code filename prefix rather than only a numeric `stacklevel=` via a new `skip_file_prefixes=` keyword argument. --- Doc/library/warnings.rst | 40 +++++- .../pycore_global_objects_fini_generated.h | 1 + Include/internal/pycore_global_strings.h | 1 + .../internal/pycore_runtime_init_generated.h | 1 + .../internal/pycore_unicodeobject_generated.h | 2 + Lib/test/test_warnings/__init__.py | 39 +++++- Lib/test/test_warnings/data/package_helper.py | 10 ++ Lib/test/test_warnings/data/stacklevel.py | 8 +- Lib/warnings.py | 29 +++- ...3-01-08-00-12-44.gh-issue-39615.gn4PhB.rst | 3 + Python/_warnings.c | 127 ++++++++++++++---- Python/clinic/_warnings.c.h | 50 +++++-- 12 files changed, 263 insertions(+), 48 deletions(-) create mode 100644 Lib/test/test_warnings/data/package_helper.py create mode 100644 Misc/NEWS.d/next/Library/2023-01-08-00-12-44.gh-issue-39615.gn4PhB.rst diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst index 28579ce8df4a62..884de08eab1b16 100644 --- a/Doc/library/warnings.rst +++ b/Doc/library/warnings.rst @@ -396,7 +396,7 @@ Available Functions ------------------- -.. function:: warn(message, category=None, stacklevel=1, source=None) +.. function:: warn(message, category=None, stacklevel=1, source=None, \*, skip_file_prefixes=None) Issue a warning, or maybe ignore it or raise an exception. The *category* argument, if given, must be a :ref:`warning category class `; it @@ -407,12 +407,39 @@ Available Functions :ref:`warnings filter `. The *stacklevel* argument can be used by wrapper functions written in Python, like this:: - def deprecation(message): + def deprecated_api(message): warnings.warn(message, DeprecationWarning, stacklevel=2) - This makes the warning refer to :func:`deprecation`'s caller, rather than to the - source of :func:`deprecation` itself (since the latter would defeat the purpose - of the warning message). + This makes the warning refer to ``deprecated_api``'s caller, rather than to + the source of ``deprecated_api`` itself (since the latter would defeat the + purpose of the warning message). + + The *skip_file_prefixes* keyword argument can be used to indicate which + stack frames are ignored when counting stack levels. This can be useful when + you want the warning to always appear at call sites outside of a package + when a constant *stacklevel* does not fit all call paths or is otherwise + challenging to maintain. If supplied, it must be a tuple of strings. When + prefixes are supplied, stacklevel is implicitly overridden to be ``max(2, + stacklevel)``. To cause a warning to be attributed to the caller from + outside of the current package you might write:: + + # example/lower.py + _warn_skips = (os.path.dirname(__file__),) + + def one_way(r_luxury_yacht=None, t_wobbler_mangrove=None): + if r_luxury_yacht: + warnings.warn("Please migrate to t_wobbler_mangrove=.", + skip_file_prefixes=_warn_skips) + + # example/higher.py + from . import lower + + def another_way(**kw): + lower.one_way(**kw) + + This makes the warning refer to both the ``example.lower.one_way()`` and + ``package.higher.another_way()`` call sites only from calling code living + outside of ``example`` package. *source*, if supplied, is the destroyed object which emitted a :exc:`ResourceWarning`. @@ -420,6 +447,9 @@ Available Functions .. versionchanged:: 3.6 Added *source* parameter. + .. versionchanged:: 3.12 + Added *skip_file_prefixes*. + .. function:: warn_explicit(message, category, filename, lineno, module=None, registry=None, module_globals=None, source=None) diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index a5365d53150b4f..8c210111b5899f 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -1151,6 +1151,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(signed)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(size)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(sizehint)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(skip_file_prefixes)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(sleep)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(sock)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(sort)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 3d9e61e50eccaa..6b1c8424424d96 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -637,6 +637,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(signed) STRUCT_FOR_ID(size) STRUCT_FOR_ID(sizehint) + STRUCT_FOR_ID(skip_file_prefixes) STRUCT_FOR_ID(sleep) STRUCT_FOR_ID(sock) STRUCT_FOR_ID(sort) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 3534b94753e0d3..fcb613083ffe99 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -1143,6 +1143,7 @@ extern "C" { INIT_ID(signed), \ INIT_ID(size), \ INIT_ID(sizehint), \ + INIT_ID(skip_file_prefixes), \ INIT_ID(sleep), \ INIT_ID(sock), \ INIT_ID(sort), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 02435071bb2cff..301aee5210e799 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -1180,6 +1180,8 @@ _PyUnicode_InitStaticStrings(void) { PyUnicode_InternInPlace(&string); string = &_Py_ID(sizehint); PyUnicode_InternInPlace(&string); + string = &_Py_ID(skip_file_prefixes); + PyUnicode_InternInPlace(&string); string = &_Py_ID(sleep); PyUnicode_InternInPlace(&string); string = &_Py_ID(sock); diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index 9e473e923cad03..9e680c847dab7b 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -12,6 +12,7 @@ from test.support import warnings_helper from test.support.script_helper import assert_python_ok, assert_python_failure +from test.test_warnings.data import package_helper from test.test_warnings.data import stacklevel as warning_tests import warnings as original_warnings @@ -472,6 +473,42 @@ def test_stacklevel_import(self): self.assertEqual(len(w), 1) self.assertEqual(w[0].filename, __file__) + def test_skip_file_prefixes(self): + with warnings_state(self.module): + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + self.module.simplefilter('always') + + # Warning never attributed to the data/ package. + package_helper.inner_api( + "inner_api", stacklevel=2, + warnings_module=warning_tests.warnings) + self.assertEqual(w[-1].filename, __file__) + warning_tests.package("package api", stacklevel=2) + self.assertEqual(w[-1].filename, __file__) + self.assertEqual(w[-2].filename, w[-1].filename) + # Low stacklevels are overridden to 2 behavior. + warning_tests.package("package api 1", stacklevel=1) + self.assertEqual(w[-1].filename, __file__) + warning_tests.package("package api 0", stacklevel=0) + self.assertEqual(w[-1].filename, __file__) + warning_tests.package("package api -99", stacklevel=-99) + self.assertEqual(w[-1].filename, __file__) + + # The stacklevel still goes up out of the package. + warning_tests.package("prefix02", stacklevel=3) + self.assertIn("unittest", w[-1].filename) + + def test_skip_file_prefixes_type_errors(self): + with warnings_state(self.module): + warn = warning_tests.warnings.warn + with self.assertRaises(TypeError): + warn("msg", skip_file_prefixes=[]) + with self.assertRaises(TypeError): + warn("msg", skip_file_prefixes=(b"bytes",)) + with self.assertRaises(TypeError): + warn("msg", skip_file_prefixes="a sequence of strs") + def test_exec_filename(self): filename = "" codeobj = compile(("import warnings\n" @@ -895,7 +932,7 @@ def test_formatwarning(self): message = "msg" category = Warning file_name = os.path.splitext(warning_tests.__file__)[0] + '.py' - line_num = 3 + line_num = 5 file_line = linecache.getline(file_name, line_num).strip() format = "%s:%s: %s: %s\n %s\n" expect = format % (file_name, line_num, category.__name__, message, diff --git a/Lib/test/test_warnings/data/package_helper.py b/Lib/test/test_warnings/data/package_helper.py new file mode 100644 index 00000000000000..c22a4f6405c5b4 --- /dev/null +++ b/Lib/test/test_warnings/data/package_helper.py @@ -0,0 +1,10 @@ +# helper to the helper for testing skip_file_prefixes. + +import os + +package_path = os.path.dirname(__file__) + +def inner_api(message, *, stacklevel, warnings_module): + warnings_module.warn( + message, stacklevel=stacklevel, + skip_file_prefixes=(package_path,)) diff --git a/Lib/test/test_warnings/data/stacklevel.py b/Lib/test/test_warnings/data/stacklevel.py index d0519effdc8785..c6dd24733b3b74 100644 --- a/Lib/test/test_warnings/data/stacklevel.py +++ b/Lib/test/test_warnings/data/stacklevel.py @@ -1,9 +1,15 @@ -# Helper module for testing the skipmodules argument of warnings.warn() +# Helper module for testing stacklevel and skip_file_prefixes arguments +# of warnings.warn() import warnings +from test.test_warnings.data import package_helper def outer(message, stacklevel=1): inner(message, stacklevel) def inner(message, stacklevel=1): warnings.warn(message, stacklevel=stacklevel) + +def package(message, *, stacklevel): + package_helper.inner_api(message, stacklevel=stacklevel, + warnings_module=warnings) diff --git a/Lib/warnings.py b/Lib/warnings.py index 7d8c4400127f7f..98ae708ca3401d 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -269,22 +269,32 @@ def _getcategory(category): return cat +def _is_internal_filename(filename): + return 'importlib' in filename and '_bootstrap' in filename + + +def _is_filename_to_skip(filename, skip_file_prefixes): + return any(filename.startswith(prefix) for prefix in skip_file_prefixes) + + def _is_internal_frame(frame): """Signal whether the frame is an internal CPython implementation detail.""" - filename = frame.f_code.co_filename - return 'importlib' in filename and '_bootstrap' in filename + return _is_internal_filename(frame.f_code.co_filename) -def _next_external_frame(frame): - """Find the next frame that doesn't involve CPython internals.""" +def _next_external_frame(frame, skip_file_prefixes): + """Find the next frame that doesn't involve Python or user internals.""" frame = frame.f_back - while frame is not None and _is_internal_frame(frame): + while frame is not None and ( + _is_internal_filename(filename := frame.f_code.co_filename) or + _is_filename_to_skip(filename, skip_file_prefixes)): frame = frame.f_back return frame # Code typically replaced by _warnings -def warn(message, category=None, stacklevel=1, source=None): +def warn(message, category=None, stacklevel=1, source=None, + *, skip_file_prefixes=()): """Issue a warning, or maybe ignore it or raise an exception.""" # Check if message is already a Warning object if isinstance(message, Warning): @@ -295,6 +305,11 @@ def warn(message, category=None, stacklevel=1, source=None): if not (isinstance(category, type) and issubclass(category, Warning)): raise TypeError("category must be a Warning subclass, " "not '{:s}'".format(type(category).__name__)) + if not isinstance(skip_file_prefixes, tuple): + # The C version demands a tuple for implementation performance. + raise TypeError('skip_file_prefixes must be a tuple of strs.') + if skip_file_prefixes: + stacklevel = max(2, stacklevel) # Get context information try: if stacklevel <= 1 or _is_internal_frame(sys._getframe(1)): @@ -305,7 +320,7 @@ def warn(message, category=None, stacklevel=1, source=None): frame = sys._getframe(1) # Look for one frame less since the above line starts us off. for x in range(stacklevel-1): - frame = _next_external_frame(frame) + frame = _next_external_frame(frame, skip_file_prefixes) if frame is None: raise ValueError except ValueError: diff --git a/Misc/NEWS.d/next/Library/2023-01-08-00-12-44.gh-issue-39615.gn4PhB.rst b/Misc/NEWS.d/next/Library/2023-01-08-00-12-44.gh-issue-39615.gn4PhB.rst new file mode 100644 index 00000000000000..1d04cc2cd54b1e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-08-00-12-44.gh-issue-39615.gn4PhB.rst @@ -0,0 +1,3 @@ +:func:`warnings.warn` now has the ability to skip stack frames based on code +filename prefix rather than only a numeric ``stacklevel`` via the new +``skip_file_prefixes`` keyword argument. diff --git a/Python/_warnings.c b/Python/_warnings.c index 046c37eb49bac0..a8c1129356eb50 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -761,56 +761,99 @@ warn_explicit(PyThreadState *tstate, PyObject *category, PyObject *message, return result; /* Py_None or NULL. */ } -static int -is_internal_frame(PyFrameObject *frame) +static PyObject * +get_frame_filename(PyFrameObject *frame) { - if (frame == NULL) { - return 0; - } - PyCodeObject *code = PyFrame_GetCode(frame); PyObject *filename = code->co_filename; Py_DECREF(code); + return filename; +} - if (filename == NULL) { - return 0; - } +static bool +is_internal_filename(PyObject *filename) +{ if (!PyUnicode_Check(filename)) { - return 0; + return false; } int contains = PyUnicode_Contains(filename, &_Py_ID(importlib)); if (contains < 0) { - return 0; + return false; } else if (contains > 0) { contains = PyUnicode_Contains(filename, &_Py_ID(_bootstrap)); if (contains < 0) { - return 0; + return false; } else if (contains > 0) { - return 1; + return true; } } - return 0; + return false; +} + +static bool +is_filename_to_skip(PyObject *filename, PyTupleObject *skip_file_prefixes) +{ + if (skip_file_prefixes) { + if (!PyUnicode_Check(filename)) { + return false; + } + + Py_ssize_t prefixes = PyTuple_GET_SIZE(skip_file_prefixes); + for (Py_ssize_t idx = 0; idx < prefixes; ++idx) + { + PyObject *prefix = PyTuple_GET_ITEM(skip_file_prefixes, idx); + int found = PyUnicode_Tailmatch(filename, prefix, 0, -1, -1); + if (found == 1) { + return true; + } + if (found < 0) { + return false; + } + } + } + return false; +} + +static bool +is_internal_frame(PyFrameObject *frame) +{ + if (frame == NULL) { + return false; + } + + PyObject *filename = get_frame_filename(frame); + if (filename == NULL) { + return false; + } + + return is_internal_filename(filename); } static PyFrameObject * -next_external_frame(PyFrameObject *frame) +next_external_frame(PyFrameObject *frame, PyTupleObject *skip_file_prefixes) { + PyObject *frame_filename; do { PyFrameObject *back = PyFrame_GetBack(frame); Py_SETREF(frame, back); - } while (frame != NULL && is_internal_frame(frame)); + } while (frame != NULL && (frame_filename = get_frame_filename(frame)) && + (is_internal_filename(frame_filename) || + is_filename_to_skip(frame_filename, skip_file_prefixes))); return frame; } /* filename, module, and registry are new refs, globals is borrowed */ +/* skip_file_prefixes is either NULL or a tuple of strs. */ /* Returns 0 on error (no new refs), 1 on success */ static int -setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno, +setup_context(Py_ssize_t stack_level, + PyTupleObject *skip_file_prefixes, + PyObject **filename, int *lineno, PyObject **module, PyObject **registry) { PyObject *globals; @@ -820,6 +863,21 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno, if (tstate == NULL) { return 0; } + if (skip_file_prefixes) { + /* Type check our data structure up front. Later code that uses it + * isn't structured to report errors. */ + Py_ssize_t prefixes = PyTuple_GET_SIZE(skip_file_prefixes); + for (Py_ssize_t idx = 0; idx < prefixes; ++idx) + { + PyObject *prefix = PyTuple_GET_ITEM(skip_file_prefixes, idx); + if (!PyUnicode_Check(prefix)) { + PyErr_Format(PyExc_TypeError, + "Found non-str '%s' in skip_file_prefixes.", + Py_TYPE(prefix)->tp_name); + return 0; + } + } + } PyInterpreterState *interp = tstate->interp; PyFrameObject *f = PyThreadState_GetFrame(tstate); // Stack level comparisons to Python code is off by one as there is no @@ -832,7 +890,7 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno, } else { while (--stack_level > 0 && f != NULL) { - f = next_external_frame(f); + f = next_external_frame(f, skip_file_prefixes); } } @@ -925,7 +983,7 @@ get_category(PyObject *message, PyObject *category) static PyObject * do_warn(PyObject *message, PyObject *category, Py_ssize_t stack_level, - PyObject *source) + PyObject *source, PyTupleObject *skip_file_prefixes) { PyObject *filename, *module, *registry, *res; int lineno; @@ -935,7 +993,8 @@ do_warn(PyObject *message, PyObject *category, Py_ssize_t stack_level, return NULL; } - if (!setup_context(stack_level, &filename, &lineno, &module, ®istry)) + if (!setup_context(stack_level, skip_file_prefixes, + &filename, &lineno, &module, ®istry)) return NULL; res = warn_explicit(tstate, category, message, filename, lineno, module, registry, @@ -950,22 +1009,42 @@ do_warn(PyObject *message, PyObject *category, Py_ssize_t stack_level, warn as warnings_warn message: object + Text of the warning message. category: object = None + The Warning category subclass. Defaults to UserWarning. stacklevel: Py_ssize_t = 1 + How far up the call stack to make this warning appear. A value of 2 for + example attributes the warning to the caller of the code calling warn(). source: object = None + If supplied, the destroyed object which emitted a ResourceWarning + * + skip_file_prefixes: object(type='PyTupleObject *', subclass_of='&PyTuple_Type') = NULL + An optional tuple of module filename prefixes indicating frames to skip + during stacklevel computations for stack frame attribution. Issue a warning, or maybe ignore it or raise an exception. [clinic start generated code]*/ static PyObject * warnings_warn_impl(PyObject *module, PyObject *message, PyObject *category, - Py_ssize_t stacklevel, PyObject *source) -/*[clinic end generated code: output=31ed5ab7d8d760b2 input=bfdf5cf99f6c4edd]*/ + Py_ssize_t stacklevel, PyObject *source, + PyTupleObject *skip_file_prefixes) +/*[clinic end generated code: output=a68e0f6906c65f80 input=eb37c6a18bec4ea1]*/ { category = get_category(message, category); if (category == NULL) return NULL; - return do_warn(message, category, stacklevel, source); + if (skip_file_prefixes) { + if (PyTuple_GET_SIZE(skip_file_prefixes) > 0) { + if (stacklevel < 2) { + stacklevel = 2; + } + } else { + Py_DECREF((PyObject *)skip_file_prefixes); + skip_file_prefixes = NULL; + } + } + return do_warn(message, category, stacklevel, source, skip_file_prefixes); } static PyObject * @@ -1113,7 +1192,7 @@ warn_unicode(PyObject *category, PyObject *message, if (category == NULL) category = PyExc_RuntimeWarning; - res = do_warn(message, category, stack_level, source); + res = do_warn(message, category, stack_level, source, NULL); if (res == NULL) return -1; Py_DECREF(res); diff --git a/Python/clinic/_warnings.c.h b/Python/clinic/_warnings.c.h index 8838a42afc1c2a..432e554af85cf6 100644 --- a/Python/clinic/_warnings.c.h +++ b/Python/clinic/_warnings.c.h @@ -9,17 +9,32 @@ preserve PyDoc_STRVAR(warnings_warn__doc__, -"warn($module, /, message, category=None, stacklevel=1, source=None)\n" +"warn($module, /, message, category=None, stacklevel=1, source=None, *,\n" +" skip_file_prefixes=)\n" "--\n" "\n" -"Issue a warning, or maybe ignore it or raise an exception."); +"Issue a warning, or maybe ignore it or raise an exception.\n" +"\n" +" message\n" +" Text of the warning message.\n" +" category\n" +" The Warning category subclass. Defaults to UserWarning.\n" +" stacklevel\n" +" How far up the call stack to make this warning appear. A value of 2 for\n" +" example attributes the warning to the caller of the code calling warn().\n" +" source\n" +" If supplied, the destroyed object which emitted a ResourceWarning\n" +" skip_file_prefixes\n" +" An optional tuple of module filename prefixes indicating frames to skip\n" +" during stacklevel computations for stack frame attribution."); #define WARNINGS_WARN_METHODDEF \ {"warn", _PyCFunction_CAST(warnings_warn), METH_FASTCALL|METH_KEYWORDS, warnings_warn__doc__}, static PyObject * warnings_warn_impl(PyObject *module, PyObject *message, PyObject *category, - Py_ssize_t stacklevel, PyObject *source); + Py_ssize_t stacklevel, PyObject *source, + PyTupleObject *skip_file_prefixes); static PyObject * warnings_warn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -27,14 +42,14 @@ warnings_warn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - #define NUM_KEYWORDS 4 + #define NUM_KEYWORDS 5 static struct { PyGC_Head _this_is_not_used; PyObject_VAR_HEAD PyObject *ob_item[NUM_KEYWORDS]; } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(message), &_Py_ID(category), &_Py_ID(stacklevel), &_Py_ID(source), }, + .ob_item = { &_Py_ID(message), &_Py_ID(category), &_Py_ID(stacklevel), &_Py_ID(source), &_Py_ID(skip_file_prefixes), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -43,19 +58,20 @@ warnings_warn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"message", "category", "stacklevel", "source", NULL}; + static const char * const _keywords[] = {"message", "category", "stacklevel", "source", "skip_file_prefixes", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "warn", .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[4]; + PyObject *argsbuf[5]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *message; PyObject *category = Py_None; Py_ssize_t stacklevel = 1; PyObject *source = Py_None; + PyTupleObject *skip_file_prefixes = NULL; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 4, 0, argsbuf); if (!args) { @@ -88,9 +104,23 @@ warnings_warn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec goto skip_optional_pos; } } - source = args[3]; + if (args[3]) { + source = args[3]; + if (!--noptargs) { + goto skip_optional_pos; + } + } skip_optional_pos: - return_value = warnings_warn_impl(module, message, category, stacklevel, source); + if (!noptargs) { + goto skip_optional_kwonly; + } + if (!PyTuple_Check(args[4])) { + _PyArg_BadArgument("warn", "argument 'skip_file_prefixes'", "tuple", args[4]); + goto exit; + } + skip_file_prefixes = (PyTupleObject *)args[4]; +skip_optional_kwonly: + return_value = warnings_warn_impl(module, message, category, stacklevel, source, skip_file_prefixes); exit: return return_value; @@ -216,4 +246,4 @@ warnings_filters_mutated(PyObject *module, PyObject *Py_UNUSED(ignored)) { return warnings_filters_mutated_impl(module); } -/*[clinic end generated code: output=0d264d1ddfc37100 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=20429719d7223bdc input=a9049054013a1b77]*/ From db757f0e440d2b3fd3a04dd771907838e0c2241c Mon Sep 17 00:00:00 2001 From: Peter Jiping Xie Date: Sat, 28 Jan 2023 20:57:40 +1100 Subject: [PATCH 026/225] gh-101386: fix typos found by codespell (#101387) --- Doc/c-api/init_config.rst | 4 ++-- Doc/howto/logging-cookbook.rst | 4 ++-- Doc/library/sqlite3.rst | 2 +- Doc/library/struct.rst | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index c3346b0421ddef..161def0b4baf3a 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -839,7 +839,7 @@ PyConfig will produce an error. Configured by the :option:`-X int_max_str_digits <-X>` command line - flag or the :envvar:`PYTHONINTMAXSTRDIGITS` environment varable. + flag or the :envvar:`PYTHONINTMAXSTRDIGITS` environment variable. Default: ``-1`` in Python mode. 4300 (:data:`sys.int_info.default_max_str_digits`) in isolated mode. @@ -1582,7 +1582,7 @@ applied during the "Main" phase. It may allow to customize Python in Python to override or tune the :ref:`Path Configuration `, maybe install a custom :data:`sys.meta_path` importer or an import hook, etc. -It may become possible to calculatin the :ref:`Path Configuration +It may become possible to calculate the :ref:`Path Configuration ` in Python, after the Core phase and before the Main phase, which is one of the :pep:`432` motivation. diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 2ba5b5c1368143..26cf40274fd3e2 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -3628,7 +3628,7 @@ refer to the comments in the code snippet for more detailed information. Logging to syslog with RFC5424 support -------------------------------------- -Although :rfc:`5424` dates from 2009, most syslog servers are configured by detault to +Although :rfc:`5424` dates from 2009, most syslog servers are configured by default to use the older :rfc:`3164`, which hails from 2001. When ``logging`` was added to Python in 2003, it supported the earlier (and only existing) protocol at the time. Since RFC5424 came out, as there has not been widespread deployment of it in syslog @@ -3819,7 +3819,7 @@ then running the script results in WARNING:demo:division by zero As you can see, this output isn't ideal. That's because the underlying code -which writes to ``sys.stderr`` makes mutiple writes, each of which results in a +which writes to ``sys.stderr`` makes multiple writes, each of which results in a separate logged line (for example, the last three lines above). To get around this problem, you need to buffer things and only output log lines when newlines are seen. Let's use a slghtly better implementation of ``LoggerWriter``: diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 8796bdfc0b6f70..c0f0f133f4a286 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -2207,7 +2207,7 @@ This section shows recipes for common adapters and converters. assert convert_datetime(b"2019-05-18T15:17:08.123456") == dt # Using current time as fromtimestamp() returns local date/time. - # Droping microseconds as adapt_datetime_epoch truncates fractional second part. + # Dropping microseconds as adapt_datetime_epoch truncates fractional second part. now = datetime.datetime.now().replace(microsecond=0) current_timestamp = int(now.timestamp()) diff --git a/Doc/library/struct.rst b/Doc/library/struct.rst index 830ba69e294d80..69d95f27cb61d9 100644 --- a/Doc/library/struct.rst +++ b/Doc/library/struct.rst @@ -462,7 +462,7 @@ In such cases, the ``@`` format character should be used to specify native byte ordering and data sizes. Internal pad bytes are normally inserted automatically. It is possible that a zero-repeat format code will be needed at the end of a format string to round up to the correct -byte boundary for proper alignment of consective chunks of data. +byte boundary for proper alignment of consecutive chunks of data. Consider these two simple examples (on a 64-bit, little-endian machine):: From 84483aacc005e6a535355cb68342fa0801d956cc Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 28 Jan 2023 06:29:21 -0600 Subject: [PATCH 027/225] GH-100485: Add extended accuracy test. Switch to faster fma() based variant. GH-101383) --- Lib/test/test_math.py | 83 +++++++++++++++++++++++++++++++++++++++++++ Modules/mathmodule.c | 53 +++++++++------------------ 2 files changed, 100 insertions(+), 36 deletions(-) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index b8ac8f32055d33..fcdec93484e000 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1369,6 +1369,89 @@ def run(func, *args): args, ) + @requires_IEEE_754 + @unittest.skipIf(HAVE_DOUBLE_ROUNDING, + "sumprod() accuracy not guaranteed on machines with double rounding") + @support.cpython_only # Other implementations may choose a different algorithm + @support.requires_resource('cpu') + def test_sumprod_extended_precision_accuracy(self): + import operator + from fractions import Fraction + from itertools import starmap + from collections import namedtuple + from math import log2, exp2, fabs + from random import choices, uniform, shuffle + from statistics import median + + DotExample = namedtuple('DotExample', ('x', 'y', 'target_sumprod', 'condition')) + + def DotExact(x, y): + vec1 = map(Fraction, x) + vec2 = map(Fraction, y) + return sum(starmap(operator.mul, zip(vec1, vec2, strict=True))) + + def Condition(x, y): + return 2.0 * DotExact(map(abs, x), map(abs, y)) / abs(DotExact(x, y)) + + def linspace(lo, hi, n): + width = (hi - lo) / (n - 1) + return [lo + width * i for i in range(n)] + + def GenDot(n, c): + """ Algorithm 6.1 (GenDot) works as follows. The condition number (5.7) of + the dot product xT y is proportional to the degree of cancellation. In + order to achieve a prescribed cancellation, we generate the first half of + the vectors x and y randomly within a large exponent range. This range is + chosen according to the anticipated condition number. The second half of x + and y is then constructed choosing xi randomly with decreasing exponent, + and calculating yi such that some cancellation occurs. Finally, we permute + the vectors x, y randomly and calculate the achieved condition number. + """ + + assert n >= 6 + n2 = n // 2 + x = [0.0] * n + y = [0.0] * n + b = log2(c) + + # First half with exponents from 0 to |_b/2_| and random ints in between + e = choices(range(int(b/2)), k=n2) + e[0] = int(b / 2) + 1 + e[-1] = 0.0 + + x[:n2] = [uniform(-1.0, 1.0) * exp2(p) for p in e] + y[:n2] = [uniform(-1.0, 1.0) * exp2(p) for p in e] + + # Second half + e = list(map(round, linspace(b/2, 0.0 , n-n2))) + for i in range(n2, n): + x[i] = uniform(-1.0, 1.0) * exp2(e[i - n2]) + y[i] = (uniform(-1.0, 1.0) * exp2(e[i - n2]) - DotExact(x, y)) / x[i] + + # Shuffle + pairs = list(zip(x, y)) + shuffle(pairs) + x, y = zip(*pairs) + + return DotExample(x, y, DotExact(x, y), Condition(x, y)) + + def RelativeError(res, ex): + x, y, target_sumprod, condition = ex + n = DotExact(list(x) + [-res], list(y) + [1]) + return fabs(n / target_sumprod) + + def Trial(dotfunc, c, n): + ex = GenDot(10, c) + res = dotfunc(ex.x, ex.y) + return RelativeError(res, ex) + + times = 1000 # Number of trials + n = 20 # Length of vectors + c = 1e30 # Target condition number + + relative_err = median(Trial(math.sumprod, c, n) for i in range(times)) + self.assertLess(relative_err, 1e-16) + def testModf(self): self.assertRaises(TypeError, math.modf) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 2da50bbd54b57f..481e29915464f1 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2832,12 +2832,7 @@ long_add_would_overflow(long a, long b) } /* -Double and triple length extended precision floating point arithmetic -based on: - - A Floating-Point Technique for Extending the Available Precision - by T. J. Dekker - https://csclub.uwaterloo.ca/~pbarfuss/dekker1971.pdf +Double and triple length extended precision algorithms from: Accurate Sum and Dot Product by Takeshi Ogita, Siegfried M. Rump, and Shin’Ichi Oishi @@ -2848,36 +2843,22 @@ based on: typedef struct{ double hi; double lo; } DoubleLength; -static inline DoubleLength -twosum(double a, double b) +static DoubleLength +dl_sum(double a, double b) { - // Rump Algorithm 3.1 Error-free transformation of the sum + /* Algorithm 3.1 Error-free transformation of the sum */ double x = a + b; double z = x - a; double y = (a - (x - z)) + (b - z); return (DoubleLength) {x, y}; } -static inline DoubleLength -dl_split(double x) { - // Rump Algorithm 3.2 Error-free splitting of a floating point number - // Dekker (5.5) and (5.6). - double t = x * 134217729.0; // Veltkamp constant = 2.0 ** 27 + 1 - double hi = t - (t - x); - double lo = x - hi; - return (DoubleLength) {hi, lo}; -} - -static inline DoubleLength +static DoubleLength dl_mul(double x, double y) { - // Dekker (5.12) and mul12() - DoubleLength xx = dl_split(x); - DoubleLength yy = dl_split(y); - double p = xx.hi * yy.hi; - double q = xx.hi * yy.lo + xx.lo * yy.hi; - double z = p + q; - double zz = p - z + q + xx.lo * yy.lo; + /* Algorithm 3.5. Error-free transformation of a product */ + double z = x * y; + double zz = fma(x, y, -z); return (DoubleLength) {z, zz}; } @@ -2885,21 +2866,21 @@ typedef struct { double hi; double lo; double tiny; } TripleLength; static const TripleLength tl_zero = {0.0, 0.0, 0.0}; -static inline TripleLength -tl_fma(TripleLength total, double x, double y) +static TripleLength +tl_fma(double x, double y, TripleLength total) { - // Rump Algorithm 5.10 with K=3 and using SumKVert + /* Algorithm 5.10 with SumKVert for K=3 */ DoubleLength pr = dl_mul(x, y); - DoubleLength sm = twosum(total.hi, pr.hi); - DoubleLength r1 = twosum(total.lo, pr.lo); - DoubleLength r2 = twosum(r1.hi, sm.lo); + DoubleLength sm = dl_sum(total.hi, pr.hi); + DoubleLength r1 = dl_sum(total.lo, pr.lo); + DoubleLength r2 = dl_sum(r1.hi, sm.lo); return (TripleLength) {sm.hi, r2.hi, total.tiny + r1.lo + r2.lo}; } -static inline double +static double tl_to_d(TripleLength total) { - DoubleLength last = twosum(total.lo, total.hi); + DoubleLength last = dl_sum(total.lo, total.hi); return total.tiny + last.lo + last.hi; } @@ -3066,7 +3047,7 @@ math_sumprod_impl(PyObject *module, PyObject *p, PyObject *q) } else { goto finalize_flt_path; } - TripleLength new_flt_total = tl_fma(flt_total, flt_p, flt_q); + TripleLength new_flt_total = tl_fma(flt_p, flt_q, flt_total); if (isfinite(new_flt_total.hi)) { flt_total = new_flt_total; flt_total_in_use = true; From 666c0840dcac9941fa41ec619fef8d45cd849a0b Mon Sep 17 00:00:00 2001 From: socal-nerdtastic <37753609+socal-nerdtastic@users.noreply.github.com> Date: Sat, 28 Jan 2023 05:18:04 -0800 Subject: [PATCH 028/225] Fix trivial typo in shebang example (GH-101385) The example was showing the current version, but should be pinned to 3.7 to match the example command. --- Doc/using/windows.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index b2a0214026e48c..bd09666d5e01c1 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -797,7 +797,7 @@ is printed. Now try changing the first line to be: Re-executing the command should now print the latest Python 3.x information. As with the above command-line examples, you can specify a more explicit version qualifier. Assuming you have Python 3.7 installed, try changing -the first line to ``#! python3.7`` and you should find the |version| +the first line to ``#! python3.7`` and you should find the 3.7 version information printed. Note that unlike interactive use, a bare "python" will use the latest From 0ef92d979311ba82d4c41b22ef38e12e1b08b13d Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 29 Jan 2023 22:50:10 +0300 Subject: [PATCH 029/225] gh-89381: Fix invalid signatures of math/cmath.log (#101404) --- Doc/library/cmath.rst | 2 +- Doc/library/math.rst | 9 ++-- Lib/test/test_math.py | 1 + ...3-01-16-10-42-58.gh-issue-89381.lM2WL0.rst | 1 + Modules/clinic/cmathmodule.c.h | 9 ++-- Modules/clinic/mathmodule.c.h | 44 ++++++++----------- Modules/cmathmodule.c | 9 ++-- Modules/mathmodule.c | 14 +++--- 8 files changed, 42 insertions(+), 47 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-01-16-10-42-58.gh-issue-89381.lM2WL0.rst diff --git a/Doc/library/cmath.rst b/Doc/library/cmath.rst index 28cd96b0e12da9..c575b90e6461ed 100644 --- a/Doc/library/cmath.rst +++ b/Doc/library/cmath.rst @@ -89,7 +89,7 @@ Power and logarithmic functions logarithms. -.. function:: log(x[, base]) +.. function:: log(x, base=None) Returns the logarithm of *x* to the given *base*. If the *base* is not specified, returns the natural logarithm of *x*. There is one branch cut, from 0 diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 797f32408eac3d..9da22b6ad0562f 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -393,13 +393,12 @@ Power and logarithmic functions .. versionadded:: 3.2 -.. function:: log(x[, base]) +.. function:: log(x, base=None) - With one argument, return the natural logarithm of *x* (to base *e*). - - With two arguments, return the logarithm of *x* to the given *base*, - calculated as ``log(x)/log(base)``. + Return the logarithm of *x* to the given *base*. + If the *base* is not specified, returns the natural + logarithm (base *e*) of *x*. .. function:: log1p(x) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index fcdec93484e000..2c84e55ab45c57 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1146,6 +1146,7 @@ def testLog(self): self.ftest('log(1/e)', math.log(1/math.e), -1) self.ftest('log(1)', math.log(1), 0) self.ftest('log(e)', math.log(math.e), 1) + self.ftest('log(e, None)', math.log(math.e, None), 1) self.ftest('log(32,2)', math.log(32,2), 5) self.ftest('log(10**40, 10)', math.log(10**40, 10), 40) self.ftest('log(10**40, 10**20)', math.log(10**40, 10**20), 2) diff --git a/Misc/NEWS.d/next/Library/2023-01-16-10-42-58.gh-issue-89381.lM2WL0.rst b/Misc/NEWS.d/next/Library/2023-01-16-10-42-58.gh-issue-89381.lM2WL0.rst new file mode 100644 index 00000000000000..7bffe7d226e38a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-16-10-42-58.gh-issue-89381.lM2WL0.rst @@ -0,0 +1 @@ +:func:`~math.log` and :func:`~cmath.log` support default base=None values. diff --git a/Modules/clinic/cmathmodule.c.h b/Modules/clinic/cmathmodule.c.h index b1da9452c61db8..bc91c20f373bcd 100644 --- a/Modules/clinic/cmathmodule.c.h +++ b/Modules/clinic/cmathmodule.c.h @@ -639,12 +639,13 @@ cmath_tanh(PyObject *module, PyObject *arg) } PyDoc_STRVAR(cmath_log__doc__, -"log($module, z, base=, /)\n" +"log($module, z, base=None, /)\n" "--\n" "\n" "log(z[, base]) -> the logarithm of z to the given base.\n" "\n" -"If the base not specified, returns the natural logarithm (base e) of z."); +"If the base is not specified or is None, returns the\n" +"natural logarithm (base e) of z."); #define CMATH_LOG_METHODDEF \ {"log", _PyCFunction_CAST(cmath_log), METH_FASTCALL, cmath_log__doc__}, @@ -657,7 +658,7 @@ cmath_log(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; Py_complex x; - PyObject *y_obj = NULL; + PyObject *y_obj = Py_None; if (!_PyArg_CheckPositional("log", nargs, 1, 2)) { goto exit; @@ -982,4 +983,4 @@ cmath_isclose(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec exit: return return_value; } -/*[clinic end generated code: output=0146c656e67f5d5f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2630f8740909a8f7 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 1f9725883b9820..0d61fd1be38ddb 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -187,43 +187,37 @@ math_modf(PyObject *module, PyObject *arg) } PyDoc_STRVAR(math_log__doc__, -"log(x, [base=math.e])\n" +"log($module, x, base=None, /)\n" +"--\n" +"\n" "Return the logarithm of x to the given base.\n" "\n" -"If the base not specified, returns the natural logarithm (base e) of x."); +"If the base is not specified or is None, returns the natural\n" +"logarithm (base e) of x."); #define MATH_LOG_METHODDEF \ - {"log", (PyCFunction)math_log, METH_VARARGS, math_log__doc__}, + {"log", _PyCFunction_CAST(math_log), METH_FASTCALL, math_log__doc__}, static PyObject * -math_log_impl(PyObject *module, PyObject *x, int group_right_1, - PyObject *base); +math_log_impl(PyObject *module, PyObject *x, PyObject *base); static PyObject * -math_log(PyObject *module, PyObject *args) +math_log(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; PyObject *x; - int group_right_1 = 0; - PyObject *base = NULL; + PyObject *base = Py_None; - switch (PyTuple_GET_SIZE(args)) { - case 1: - if (!PyArg_ParseTuple(args, "O:log", &x)) { - goto exit; - } - break; - case 2: - if (!PyArg_ParseTuple(args, "OO:log", &x, &base)) { - goto exit; - } - group_right_1 = 1; - break; - default: - PyErr_SetString(PyExc_TypeError, "math.log requires 1 to 2 arguments"); - goto exit; + if (!_PyArg_CheckPositional("log", nargs, 1, 2)) { + goto exit; + } + x = args[0]; + if (nargs < 2) { + goto skip_optional; } - return_value = math_log_impl(module, x, group_right_1, base); + base = args[1]; +skip_optional: + return_value = math_log_impl(module, x, base); exit: return return_value; @@ -954,4 +948,4 @@ math_ulp(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=899211ec70e4506c input=a9049054013a1b77]*/ +/*[clinic end generated code: output=afec63ebb0da709a input=a9049054013a1b77]*/ diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 2038ac26e65857..62caba031eda27 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -952,23 +952,24 @@ cmath_tanh_impl(PyObject *module, Py_complex z) cmath.log z as x: Py_complex - base as y_obj: object = NULL + base as y_obj: object = None / log(z[, base]) -> the logarithm of z to the given base. -If the base not specified, returns the natural logarithm (base e) of z. +If the base is not specified or is None, returns the +natural logarithm (base e) of z. [clinic start generated code]*/ static PyObject * cmath_log_impl(PyObject *module, Py_complex x, PyObject *y_obj) -/*[clinic end generated code: output=4effdb7d258e0d94 input=230ed3a71ecd000a]*/ +/*[clinic end generated code: output=4effdb7d258e0d94 input=e7db51859ebf70bf]*/ { Py_complex y; errno = 0; x = c_log(x); - if (y_obj != NULL) { + if (y_obj != Py_None) { y = PyComplex_AsCComplex(y_obj); if (PyErr_Occurred()) { return NULL; diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 481e29915464f1..e6cdb3bae1ecff 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2366,26 +2366,24 @@ loghelper(PyObject* arg, double (*func)(double)) math.log x: object - [ - base: object(c_default="NULL") = math.e - ] + base: object = None / Return the logarithm of x to the given base. -If the base not specified, returns the natural logarithm (base e) of x. +If the base is not specified or is None, returns the natural +logarithm (base e) of x. [clinic start generated code]*/ static PyObject * -math_log_impl(PyObject *module, PyObject *x, int group_right_1, - PyObject *base) -/*[clinic end generated code: output=7b5a39e526b73fc9 input=0f62d5726cbfebbd]*/ +math_log_impl(PyObject *module, PyObject *x, PyObject *base) +/*[clinic end generated code: output=1dead263cbb1e854 input=ef032cc9837943e1]*/ { PyObject *num, *den; PyObject *ans; num = loghelper(x, m_log); - if (num == NULL || base == NULL) + if (num == NULL || base == Py_None) return num; den = loghelper(base, m_log); From c4170c36b0b54c10456f4b49245f570023a97f72 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Mon, 30 Jan 2023 00:41:27 +0000 Subject: [PATCH 030/225] gh-39615: fix warning on return type mismatch (#101407) --- Python/_warnings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/_warnings.c b/Python/_warnings.c index a8c1129356eb50..e78f21644f372b 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -806,7 +806,7 @@ is_filename_to_skip(PyObject *filename, PyTupleObject *skip_file_prefixes) for (Py_ssize_t idx = 0; idx < prefixes; ++idx) { PyObject *prefix = PyTuple_GET_ITEM(skip_file_prefixes, idx); - int found = PyUnicode_Tailmatch(filename, prefix, 0, -1, -1); + Py_ssize_t found = PyUnicode_Tailmatch(filename, prefix, 0, -1, -1); if (found == 1) { return true; } From f5a3d91b6c56ddff4644b5a5ac34d8c6d23d7c79 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sun, 29 Jan 2023 17:28:39 -0800 Subject: [PATCH 031/225] gh-98831: Support conditional effects; use for LOAD_ATTR (#101333) --- Python/bytecodes.c | 38 +++---- Python/generated_cases.c.h | 40 ++++---- Python/opcode_metadata.h | 8 +- Tools/cases_generator/generate_cases.py | 130 +++++++++++++++++------- Tools/cases_generator/parser.py | 49 +++++---- Tools/cases_generator/test_generator.py | 49 +++++++-- 6 files changed, 194 insertions(+), 120 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index e5769f61fc28d0..fb00b887732ed0 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -51,7 +51,7 @@ // Dummy variables for stack effects. static PyObject *value, *value1, *value2, *left, *right, *res, *sum, *prod, *sub; -static PyObject *container, *start, *stop, *v, *lhs, *rhs; +static PyObject *container, *start, *stop, *v, *lhs, *rhs, *res2; static PyObject *list, *tuple, *dict, *owner, *set, *str, *tup, *map, *keys; static PyObject *exit_func, *lasti, *val, *retval, *obj, *iter; static PyObject *aiter, *awaitable, *iterable, *w, *exc_value, *bc; @@ -1438,13 +1438,11 @@ dummy_func( PREDICT(JUMP_BACKWARD); } - // error: LOAD_ATTR has irregular stack effect - inst(LOAD_ATTR) { + inst(LOAD_ATTR, (unused/9, owner -- res2 if (oparg & 1), res)) { #if ENABLE_SPECIALIZATION _PyAttrCache *cache = (_PyAttrCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); - PyObject *owner = TOP(); PyObject *name = GETITEM(names, oparg>>1); next_instr--; _Py_Specialize_LoadAttr(owner, next_instr, name); @@ -1454,26 +1452,18 @@ dummy_func( DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ PyObject *name = GETITEM(names, oparg >> 1); - PyObject *owner = TOP(); if (oparg & 1) { - /* Designed to work in tandem with CALL. */ + /* Designed to work in tandem with CALL, pushes two values. */ PyObject* meth = NULL; - - int meth_found = _PyObject_GetMethod(owner, name, &meth); - - if (meth == NULL) { - /* Most likely attribute wasn't found. */ - goto error; - } - - if (meth_found) { + if (_PyObject_GetMethod(owner, name, &meth)) { /* We can bypass temporary bound method object. meth is unbound method and obj is self. meth | self | arg1 | ... | argN */ - SET_TOP(meth); - PUSH(owner); // self + assert(meth != NULL); // No errors on this branch + res2 = meth; + res = owner; // Transfer ownership } else { /* meth is not an unbound method (but a regular attr, or @@ -1483,20 +1473,18 @@ dummy_func( NULL | meth | arg1 | ... | argN */ - SET_TOP(NULL); Py_DECREF(owner); - PUSH(meth); + ERROR_IF(meth == NULL, error); + res2 = NULL; + res = meth; } } else { - PyObject *res = PyObject_GetAttr(owner, name); - if (res == NULL) { - goto error; - } + /* Classic, pushes one value. */ + res = PyObject_GetAttr(owner, name); Py_DECREF(owner); - SET_TOP(res); + ERROR_IF(res == NULL, error); } - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); } // error: LOAD_ATTR has irregular stack effect diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 287a1f1f042089..b5decf804ca652 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1745,11 +1745,13 @@ TARGET(LOAD_ATTR) { PREDICTED(LOAD_ATTR); + PyObject *owner = PEEK(1); + PyObject *res2 = NULL; + PyObject *res; #if ENABLE_SPECIALIZATION _PyAttrCache *cache = (_PyAttrCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); - PyObject *owner = TOP(); PyObject *name = GETITEM(names, oparg>>1); next_instr--; _Py_Specialize_LoadAttr(owner, next_instr, name); @@ -1759,26 +1761,18 @@ DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ PyObject *name = GETITEM(names, oparg >> 1); - PyObject *owner = TOP(); if (oparg & 1) { - /* Designed to work in tandem with CALL. */ + /* Designed to work in tandem with CALL, pushes two values. */ PyObject* meth = NULL; - - int meth_found = _PyObject_GetMethod(owner, name, &meth); - - if (meth == NULL) { - /* Most likely attribute wasn't found. */ - goto error; - } - - if (meth_found) { + if (_PyObject_GetMethod(owner, name, &meth)) { /* We can bypass temporary bound method object. meth is unbound method and obj is self. meth | self | arg1 | ... | argN */ - SET_TOP(meth); - PUSH(owner); // self + assert(meth != NULL); // No errors on this branch + res2 = meth; + res = owner; // Transfer ownership } else { /* meth is not an unbound method (but a regular attr, or @@ -1788,20 +1782,22 @@ NULL | meth | arg1 | ... | argN */ - SET_TOP(NULL); Py_DECREF(owner); - PUSH(meth); + if (meth == NULL) goto pop_1_error; + res2 = NULL; + res = meth; } } else { - PyObject *res = PyObject_GetAttr(owner, name); - if (res == NULL) { - goto error; - } + /* Classic, pushes one value. */ + res = PyObject_GetAttr(owner, name); Py_DECREF(owner); - SET_TOP(res); + if (res == NULL) goto pop_1_error; } - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); + STACK_GROW(((oparg & 1) ? 1 : 0)); + POKE(1, res); + if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } + JUMPBY(9); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index cca86629e48d16..e76ddda2f0292d 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -185,7 +185,7 @@ _PyOpcode_num_popped(int opcode, int oparg) { case MAP_ADD: return 2; case LOAD_ATTR: - return -1; + return 1; case LOAD_ATTR_INSTANCE_VALUE: return -1; case LOAD_ATTR_MODULE: @@ -531,7 +531,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case MAP_ADD: return 0; case LOAD_ATTR: - return -1; + return ((oparg & 1) ? 1 : 0) + 1; case LOAD_ATTR_INSTANCE_VALUE: return -1; case LOAD_ATTR_MODULE: @@ -694,7 +694,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { } #endif enum Direction { DIR_NONE, DIR_READ, DIR_WRITE }; -enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 }; +enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBC00000000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 }; struct opcode_metadata { enum Direction dir_op1; enum Direction dir_op2; @@ -791,7 +791,7 @@ struct opcode_metadata { [DICT_UPDATE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [DICT_MERGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [MAP_ADD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, [LOAD_ATTR_INSTANCE_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [LOAD_ATTR_MODULE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [LOAD_ATTR_WITH_HINT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 9d894d2ff57455..1d703a0a790ed5 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -26,7 +26,9 @@ ) BEGIN_MARKER = "// BEGIN BYTECODES //" END_MARKER = "// END BYTECODES //" -RE_PREDICTED = r"^\s*(?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);\s*(?://.*)?$" +RE_PREDICTED = ( + r"^\s*(?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);\s*(?://.*)?$" +) UNUSED = "unused" BITS_PER_CODE_UNIT = 16 @@ -59,7 +61,10 @@ def effect_size(effect: StackEffect) -> tuple[int, str]: At most one of these will be non-zero / non-empty. """ if effect.size: + assert not effect.cond, "Array effects cannot have a condition" return 0, effect.size + elif effect.cond: + return 0, f"{maybe_parenthesize(effect.cond)} ? 1 : 0" else: return 1, "" @@ -132,7 +137,12 @@ def block(self, head: str): yield self.emit("}") - def stack_adjust(self, diff: int, input_effects: list[StackEffect], output_effects: list[StackEffect]): + def stack_adjust( + self, + diff: int, + input_effects: list[StackEffect], + output_effects: list[StackEffect], + ): # TODO: Get rid of 'diff' parameter shrink, isym = list_effect_size(input_effects) grow, osym = list_effect_size(output_effects) @@ -150,10 +160,13 @@ def declare(self, dst: StackEffect, src: StackEffect | None): if dst.name == UNUSED: return typ = f"{dst.type}" if dst.type else "PyObject *" - init = "" if src: cast = self.cast(dst, src) init = f" = {cast}{src.name}" + elif dst.cond: + init = " = NULL" + else: + init = "" sepa = "" if typ.endswith("*") else " " self.emit(f"{typ}{sepa}{dst.name}{init};") @@ -162,7 +175,10 @@ def assign(self, dst: StackEffect, src: StackEffect): return cast = self.cast(dst, src) if m := re.match(r"^PEEK\((.*)\)$", dst.name): - self.emit(f"POKE({m.group(1)}, {cast}{src.name});") + stmt = f"POKE({m.group(1)}, {cast}{src.name});" + if src.cond: + stmt = f"if ({src.cond}) {{ {stmt} }}" + self.emit(stmt) elif m := re.match(r"^&PEEK\(.*\)$", dst.name): # NOTE: MOVE_ITEMS() does not actually exist. # The only supported output array forms are: @@ -234,7 +250,7 @@ def __init__(self, inst: parser.InstDef): if self.register: num_regs = len(self.input_effects) + len(self.output_effects) num_dummies = (num_regs // 2) * 2 + 1 - num_regs - fmt = "I" + "B"*num_regs + "X"*num_dummies + fmt = "I" + "B" * num_regs + "X" * num_dummies else: if variable_used(inst, "oparg"): fmt = "IB" @@ -276,9 +292,13 @@ def write(self, out: Formatter) -> None: # Write input stack effect variable declarations and initializations ieffects = list(reversed(self.input_effects)) for i, ieffect in enumerate(ieffects): - isize = string_effect_size(list_effect_size(ieffects[:i+1])) + isize = string_effect_size( + list_effect_size([ieff for ieff in ieffects[: i + 1]]) + ) if ieffect.size: src = StackEffect(f"&PEEK({isize})", "PyObject **") + elif ieffect.cond: + src = StackEffect(f"({ieffect.cond}) ? PEEK({isize}) : NULL", "") else: src = StackEffect(f"PEEK({isize})", "") out.declare(ieffect, src) @@ -304,14 +324,20 @@ def write(self, out: Formatter) -> None: if not self.register: # Write net stack growth/shrinkage - out.stack_adjust(0, self.input_effects, self.output_effects) + out.stack_adjust( + 0, + [ieff for ieff in self.input_effects], + [oeff for oeff in self.output_effects], + ) # Write output stack effect assignments oeffects = list(reversed(self.output_effects)) for i, oeffect in enumerate(oeffects): if oeffect.name in self.unmoved_names: continue - osize = string_effect_size(list_effect_size(oeffects[:i+1])) + osize = string_effect_size( + list_effect_size([oeff for oeff in oeffects[: i + 1]]) + ) if oeffect.size: dst = StackEffect(f"&PEEK({osize})", "PyObject **") else: @@ -438,6 +464,7 @@ class MacroInstruction(SuperOrMacroInstruction): parts: list[Component | parser.CacheEffect] +AnyInstruction = Instruction | SuperInstruction | MacroInstruction INSTR_FMT_PREFIX = "INSTR_FMT_" @@ -506,6 +533,7 @@ def parse(self) -> None: self.supers = {} self.macros = {} self.families = {} + thing: parser.InstDef | parser.Super | parser.Macro | parser.Family | None while thing := psr.definition(): match thing: case parser.InstDef(name=name): @@ -631,7 +659,9 @@ def analyze_super(self, super: parser.Super) -> SuperInstruction: parts.append(part) format += instr.instr_fmt final_sp = sp - return SuperInstruction(super.name, stack, initial_sp, final_sp, format, super, parts) + return SuperInstruction( + super.name, stack, initial_sp, final_sp, format, super, parts + ) def analyze_macro(self, macro: parser.Macro) -> MacroInstruction: components = self.check_macro_components(macro) @@ -657,7 +687,9 @@ def analyze_macro(self, macro: parser.Macro) -> MacroInstruction: case _: typing.assert_never(component) final_sp = sp - return MacroInstruction(macro.name, stack, initial_sp, final_sp, format, macro, parts) + return MacroInstruction( + macro.name, stack, initial_sp, final_sp, format, macro, parts + ) def analyze_instruction( self, instr: Instruction, stack: list[StackEffect], sp: int @@ -710,7 +742,9 @@ def stack_analysis( for thing in components: match thing: case Instruction() as instr: - if any(eff.size for eff in instr.input_effects + instr.output_effects): + if any( + eff.size for eff in instr.input_effects + instr.output_effects + ): # TODO: Eventually this will be needed, at least for macros. self.error( f"Instruction {instr.name!r} has variable-sized stack effect, " @@ -736,16 +770,16 @@ def stack_analysis( def get_stack_effect_info( self, thing: parser.InstDef | parser.Super | parser.Macro - ) -> tuple[Instruction|None, str, str]: - - def effect_str(effect: list[StackEffect]) -> str: - if getattr(thing, 'kind', None) == 'legacy': + ) -> tuple[AnyInstruction | None, str, str]: + def effect_str(effects: list[StackEffect]) -> str: + if getattr(thing, "kind", None) == "legacy": return str(-1) - n_effect, sym_effect = list_effect_size(effect) + n_effect, sym_effect = list_effect_size(effects) if sym_effect: return f"{sym_effect} + {n_effect}" if n_effect else sym_effect return str(n_effect) + instr: AnyInstruction | None match thing: case parser.InstDef(): if thing.kind != "op": @@ -754,34 +788,43 @@ def effect_str(effect: list[StackEffect]) -> str: pushed = effect_str(instr.output_effects) else: instr = None - popped = pushed = "", "" + popped = "" + pushed = "" case parser.Super(): instr = self.super_instrs[thing.name] - popped = '+'.join(effect_str(comp.instr.input_effects) for comp in instr.parts) - pushed = '+'.join(effect_str(comp.instr.output_effects) for comp in instr.parts) + popped = "+".join( + effect_str(comp.instr.input_effects) for comp in instr.parts + ) + pushed = "+".join( + effect_str(comp.instr.output_effects) for comp in instr.parts + ) case parser.Macro(): instr = self.macro_instrs[thing.name] parts = [comp for comp in instr.parts if isinstance(comp, Component)] - popped = '+'.join(effect_str(comp.instr.input_effects) for comp in parts) - pushed = '+'.join(effect_str(comp.instr.output_effects) for comp in parts) + popped = "+".join( + effect_str(comp.instr.input_effects) for comp in parts + ) + pushed = "+".join( + effect_str(comp.instr.output_effects) for comp in parts + ) case _: typing.assert_never(thing) return instr, popped, pushed def write_stack_effect_functions(self) -> None: - popped_data = [] - pushed_data = [] + popped_data: list[tuple[AnyInstruction, str]] = [] + pushed_data: list[tuple[AnyInstruction, str]] = [] for thing in self.everything: instr, popped, pushed = self.get_stack_effect_info(thing) if instr is not None: - popped_data.append( (instr, popped) ) - pushed_data.append( (instr, pushed) ) + popped_data.append((instr, popped)) + pushed_data.append((instr, pushed)) - def write_function(direction: str, data: list[tuple[Instruction, str]]) -> None: - self.out.emit("\n#ifndef NDEBUG"); - self.out.emit("static int"); + def write_function(direction: str, data: list[tuple[AnyInstruction, str]]) -> None: + self.out.emit("\n#ifndef NDEBUG") + self.out.emit("static int") self.out.emit(f"_PyOpcode_num_{direction}(int opcode, int oparg) {{") - self.out.emit(" switch(opcode) {"); + self.out.emit(" switch(opcode) {") for instr, effect in data: self.out.emit(f" case {instr.name}:") self.out.emit(f" return {effect};") @@ -789,10 +832,10 @@ def write_function(direction: str, data: list[tuple[Instruction, str]]) -> None: self.out.emit(" Py_UNREACHABLE();") self.out.emit(" }") self.out.emit("}") - self.out.emit("#endif"); + self.out.emit("#endif") - write_function('popped', popped_data) - write_function('pushed', pushed_data) + write_function("popped", popped_data) + write_function("pushed", pushed_data) def write_metadata(self) -> None: """Write instruction metadata to output file.""" @@ -865,21 +908,21 @@ def write_metadata_for_inst(self, instr: Instruction) -> None: directions.extend("DIR_NONE" for _ in range(3)) dir_op1, dir_op2, dir_op3 = directions[:3] self.out.emit( - f' [{instr.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{instr.instr_fmt} }},' + f" [{instr.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{instr.instr_fmt} }}," ) def write_metadata_for_super(self, sup: SuperInstruction) -> None: """Write metadata for a super-instruction.""" dir_op1 = dir_op2 = dir_op3 = "DIR_NONE" self.out.emit( - f' [{sup.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{sup.instr_fmt} }},' + f" [{sup.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{sup.instr_fmt} }}," ) def write_metadata_for_macro(self, mac: MacroInstruction) -> None: """Write metadata for a macro-instruction.""" dir_op1 = dir_op2 = dir_op3 = "DIR_NONE" self.out.emit( - f' [{mac.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{mac.instr_fmt} }},' + f" [{mac.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{mac.instr_fmt} }}," ) def write_instructions(self) -> None: @@ -1012,7 +1055,9 @@ def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]: # Separate PREDICT(...) macros from end predictions: list[str] = [] - while blocklines and (m := re.match(r"^\s*PREDICT\((\w+)\);\s*(?://.*)?$", blocklines[-1])): + while blocklines and ( + m := re.match(r"^\s*PREDICT\((\w+)\);\s*(?://.*)?$", blocklines[-1]) + ): predictions.insert(0, m.group(1)) blocklines.pop() @@ -1029,13 +1074,22 @@ def always_exits(lines: list[str]) -> bool: return False line = line[12:] return line.startswith( - ("goto ", "return ", "DISPATCH", "GO_TO_", "Py_UNREACHABLE()", "ERROR_IF(true, ") + ( + "goto ", + "return ", + "DISPATCH", + "GO_TO_", + "Py_UNREACHABLE()", + "ERROR_IF(true, ", + ) ) def variable_used(node: parser.Node, name: str) -> bool: """Determine whether a variable with a given name is used in a node.""" - return any(token.kind == "IDENTIFIER" and token.text == name for token in node.tokens) + return any( + token.kind == "IDENTIFIER" and token.text == name for token in node.tokens + ) def main(): diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py index c2cebe96ccd6be..ced66faee4931f 100644 --- a/Tools/cases_generator/parser.py +++ b/Tools/cases_generator/parser.py @@ -48,9 +48,6 @@ def to_text(self, dedent: int = 0) -> str: context = self.context if not context: return "" - tokens = context.owner.tokens - begin = context.begin - end = context.end return lx.to_text(self.tokens, dedent) @property @@ -74,13 +71,13 @@ class Block(Node): class StackEffect(Node): name: str type: str = "" # Optional `:type` + cond: str = "" # Optional `if (cond)` size: str = "" # Optional `[size]` - # Note: we can have type or size but not both - # TODO: condition (can be combined with type but not size) + # Note: size cannot be combined with type or cond @dataclass -class Dimension(Node): +class Expression(Node): size: str @@ -239,31 +236,39 @@ def cache_effect(self) -> CacheEffect | None: @contextual def stack_effect(self) -> StackEffect | None: - # IDENTIFIER - # | IDENTIFIER ':' IDENTIFIER - # | IDENTIFIER '[' dimension ']' - # TODO: Conditions + # IDENTIFIER [':' IDENTIFIER] ['if' '(' expression ')'] + # | IDENTIFIER '[' expression ']' if tkn := self.expect(lx.IDENTIFIER): + type_text = "" if self.expect(lx.COLON): - typ = self.require(lx.IDENTIFIER) - return StackEffect(tkn.text, typ.text) - elif self.expect(lx.LBRACKET): - if not (dim := self.dimension()): - raise self.make_syntax_error("Expected dimension") + type_text = self.require(lx.IDENTIFIER).text.strip() + cond_text = "" + if self.expect(lx.IF): + self.require(lx.LPAREN) + if not (cond := self.expression()): + raise self.make_syntax_error("Expected condition") + self.require(lx.RPAREN) + cond_text = cond.text.strip() + size_text = "" + if self.expect(lx.LBRACKET): + if type_text or cond_text: + raise self.make_syntax_error("Unexpected [") + if not (size := self.expression()): + raise self.make_syntax_error("Expected expression") self.require(lx.RBRACKET) - return StackEffect(tkn.text, "PyObject **", dim.text.strip()) - else: - return StackEffect(tkn.text) + type_text = "PyObject **" + size_text = size.text.strip() + return StackEffect(tkn.text, type_text, cond_text, size_text) @contextual - def dimension(self) -> Dimension | None: + def expression(self) -> Expression | None: tokens: list[lx.Token] = [] - while (tkn := self.peek()) and tkn.kind != lx.RBRACKET: + while (tkn := self.peek()) and tkn.kind not in (lx.RBRACKET, lx.RPAREN): tokens.append(tkn) self.next() if not tokens: return None - return Dimension(lx.to_text(tokens).strip()) + return Expression(lx.to_text(tokens).strip()) @contextual def super_def(self) -> Super | None: @@ -366,7 +371,7 @@ def members(self) -> list[str] | None: return None @contextual - def block(self) -> Block: + def block(self) -> Block | None: if self.c_blob(): return Block() diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index bd1b974399abdd..6e6c60782d73d3 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -9,19 +9,19 @@ def test_effect_sizes(): input_effects = [ - x := StackEffect("x", "", ""), - y := StackEffect("y", "", "oparg"), - z := StackEffect("z", "", "oparg*2"), + x := StackEffect("x", "", "", ""), + y := StackEffect("y", "", "", "oparg"), + z := StackEffect("z", "", "", "oparg*2"), ] output_effects = [ - a := StackEffect("a", "", ""), - b := StackEffect("b", "", "oparg*4"), - c := StackEffect("c", "", ""), + StackEffect("a", "", "", ""), + StackEffect("b", "", "", "oparg*4"), + StackEffect("c", "", "", ""), ] other_effects = [ - p := StackEffect("p", "", "oparg<<1"), - q := StackEffect("q", "", ""), - r := StackEffect("r", "", ""), + StackEffect("p", "", "", "oparg<<1"), + StackEffect("q", "", "", ""), + StackEffect("r", "", "", ""), ] assert generate_cases.effect_size(x) == (1, "") assert generate_cases.effect_size(y) == (0, "oparg") @@ -54,6 +54,12 @@ def run_cases_test(input: str, expected: str): while lines and lines[0].startswith("// "): lines.pop(0) actual = "".join(lines) + # if actual.rstrip() != expected.rstrip(): + # print("Actual:") + # print(actual) + # print("Expected:") + # print(expected) + # print("End") assert actual.rstrip() == expected.rstrip() def test_legacy(): @@ -475,3 +481,28 @@ def test_register(): } """ run_cases_test(input, output) + +def test_cond_effect(): + input = """ + inst(OP, (aa, input if (oparg & 1), cc -- xx, output if (oparg & 2), zz)) { + output = spam(oparg, input); + } + """ + output = """ + TARGET(OP) { + PyObject *cc = PEEK(1); + PyObject *input = (oparg & 1) ? PEEK(1 + ((oparg & 1) ? 1 : 0)) : NULL; + PyObject *aa = PEEK(2 + ((oparg & 1) ? 1 : 0)); + PyObject *xx; + PyObject *output = NULL; + PyObject *zz; + output = spam(oparg, input); + STACK_SHRINK(((oparg & 1) ? 1 : 0)); + STACK_GROW(((oparg & 2) ? 1 : 0)); + POKE(1, zz); + if (oparg & 2) { POKE(1 + ((oparg & 2) ? 1 : 0), output); } + POKE(2 + ((oparg & 2) ? 1 : 0), xx); + DISPATCH(); + } + """ + run_cases_test(input, output) From c1b1f51cd1632f0b77dacd43092fb44ed5e053a9 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 30 Jan 2023 10:03:04 +0000 Subject: [PATCH 032/225] GH-101291: Refactor the `PyLongObject` struct into object header and PyLongValue struct. (GH-101292) --- Include/cpython/longintrepr.h | 9 +- Include/internal/pycore_runtime_init.h | 8 +- ...-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst | 2 + Modules/_decimal/_decimal.c | 10 +- Objects/boolobject.c | 8 +- Objects/listobject.c | 4 +- Objects/longobject.c | 356 +++++++++--------- Python/bltinmodule.c | 4 +- Python/bytecodes.c | 14 +- Python/generated_cases.c.h | 14 +- Python/marshal.c | 10 +- Python/specialize.c | 2 +- Tools/gdb/libpython.py | 2 +- 13 files changed, 226 insertions(+), 217 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst diff --git a/Include/cpython/longintrepr.h b/Include/cpython/longintrepr.h index 6d52427508b5fe..810daa83165e71 100644 --- a/Include/cpython/longintrepr.h +++ b/Include/cpython/longintrepr.h @@ -79,9 +79,14 @@ typedef long stwodigits; /* signed variant of twodigits */ aware that ints abuse ob_size's sign bit. */ -struct _longobject { - PyObject_VAR_HEAD +typedef struct _PyLongValue { + Py_ssize_t ob_size; /* Number of items in variable part */ digit ob_digit[1]; +} _PyLongValue; + +struct _longobject { + PyObject_HEAD + _PyLongValue long_value; }; PyAPI_FUNC(PyLongObject *) _PyLong_New(Py_ssize_t); diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index f10dccc0158010..c6a27d076eae2d 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -149,9 +149,11 @@ extern "C" { #define _PyLong_DIGIT_INIT(val) \ { \ - _PyVarObject_IMMORTAL_INIT(&PyLong_Type, \ - ((val) == 0 ? 0 : ((val) > 0 ? 1 : -1))), \ - .ob_digit = { ((val) >= 0 ? (val) : -(val)) }, \ + .ob_base = _PyObject_IMMORTAL_INIT(&PyLong_Type), \ + .long_value = { \ + ((val) == 0 ? 0 : ((val) > 0 ? 1 : -1)), \ + { ((val) >= 0 ? (val) : -(val)) }, \ + } \ } #define _PyBytes_SIMPLE_INIT(CH, LEN) \ diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst new file mode 100644 index 00000000000000..b585ff5a817edf --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst @@ -0,0 +1,2 @@ +Refactor the ``PyLongObject`` struct into a normal Python object header and +a ``PyLongValue`` struct. diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index bc97615ffb4b72..5936fbaaf35eb0 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -2171,16 +2171,16 @@ dec_from_long(PyTypeObject *type, PyObject *v, } if (len == 1) { - _dec_settriple(dec, sign, *l->ob_digit, 0); + _dec_settriple(dec, sign, *l->long_value.ob_digit, 0); mpd_qfinalize(MPD(dec), ctx, status); return dec; } #if PYLONG_BITS_IN_DIGIT == 30 - mpd_qimport_u32(MPD(dec), l->ob_digit, len, sign, PyLong_BASE, + mpd_qimport_u32(MPD(dec), l->long_value.ob_digit, len, sign, PyLong_BASE, ctx, status); #elif PYLONG_BITS_IN_DIGIT == 15 - mpd_qimport_u16(MPD(dec), l->ob_digit, len, sign, PyLong_BASE, + mpd_qimport_u16(MPD(dec), l->long_value.ob_digit, len, sign, PyLong_BASE, ctx, status); #else #error "PYLONG_BITS_IN_DIGIT should be 15 or 30" @@ -3543,11 +3543,11 @@ dec_as_long(PyObject *dec, PyObject *context, int round) return NULL; } - memcpy(pylong->ob_digit, ob_digit, n * sizeof(digit)); + memcpy(pylong->long_value.ob_digit, ob_digit, n * sizeof(digit)); mpd_free(ob_digit); i = n; - while ((i > 0) && (pylong->ob_digit[i-1] == 0)) { + while ((i > 0) && (pylong->long_value.ob_digit[i-1] == 0)) { i--; } diff --git a/Objects/boolobject.c b/Objects/boolobject.c index 18666f88cbde10..55b4a4077ab783 100644 --- a/Objects/boolobject.c +++ b/Objects/boolobject.c @@ -195,11 +195,11 @@ PyTypeObject PyBool_Type = { /* The objects representing bool values False and True */ struct _longobject _Py_FalseStruct = { - PyVarObject_HEAD_INIT(&PyBool_Type, 0) - { 0 } + PyObject_HEAD_INIT(&PyBool_Type) + { 0, { 0 } } }; struct _longobject _Py_TrueStruct = { - PyVarObject_HEAD_INIT(&PyBool_Type, 1) - { 1 } + PyObject_HEAD_INIT(&PyBool_Type) + { 1, { 1 } } }; diff --git a/Objects/listobject.c b/Objects/listobject.c index 6629775604b67b..ca6b712311340a 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -2155,8 +2155,8 @@ unsafe_long_compare(PyObject *v, PyObject *w, MergeState *ms) vl = (PyLongObject*)v; wl = (PyLongObject*)w; - v0 = Py_SIZE(vl) == 0 ? 0 : (sdigit)vl->ob_digit[0]; - w0 = Py_SIZE(wl) == 0 ? 0 : (sdigit)wl->ob_digit[0]; + v0 = Py_SIZE(vl) == 0 ? 0 : (sdigit)vl->long_value.ob_digit[0]; + w0 = Py_SIZE(wl) == 0 ? 0 : (sdigit)wl->long_value.ob_digit[0]; if (Py_SIZE(vl) < 0) v0 = -v0; diff --git a/Objects/longobject.c b/Objects/longobject.c index 51ac86961c9954..65bf15648b07fb 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -30,7 +30,7 @@ static inline stwodigits medium_value(PyLongObject *x) { assert(IS_MEDIUM_VALUE(x)); - return ((stwodigits)Py_SIZE(x)) * x->ob_digit[0]; + return ((stwodigits)Py_SIZE(x)) * x->long_value.ob_digit[0]; } #define IS_SMALL_INT(ival) (-_PY_NSMALLNEGINTS <= (ival) && (ival) < _PY_NSMALLPOSINTS) @@ -129,7 +129,7 @@ long_normalize(PyLongObject *v) Py_ssize_t j = Py_ABS(Py_SIZE(v)); Py_ssize_t i = j; - while (i > 0 && v->ob_digit[i-1] == 0) + while (i > 0 && v->long_value.ob_digit[i-1] == 0) --i; if (i != j) { Py_SET_SIZE(v, (Py_SIZE(v) < 0) ? -(i) : i); @@ -141,7 +141,7 @@ long_normalize(PyLongObject *v) Return NULL and set exception if we run out of memory. */ #define MAX_LONG_DIGITS \ - ((PY_SSIZE_T_MAX - offsetof(PyLongObject, ob_digit))/sizeof(digit)) + ((PY_SSIZE_T_MAX - offsetof(PyLongObject, long_value.ob_digit))/sizeof(digit)) PyLongObject * _PyLong_New(Py_ssize_t size) @@ -160,7 +160,7 @@ _PyLong_New(Py_ssize_t size) sizeof(PyVarObject) instead of the offsetof, but this risks being incorrect in the presence of padding between the PyVarObject header and the digits. */ - result = PyObject_Malloc(offsetof(PyLongObject, ob_digit) + + result = PyObject_Malloc(offsetof(PyLongObject, long_value.ob_digit) + ndigits*sizeof(digit)); if (!result) { PyErr_NoMemory(); @@ -190,7 +190,7 @@ _PyLong_Copy(PyLongObject *src) if (result != NULL) { Py_SET_SIZE(result, Py_SIZE(src)); while (--i >= 0) { - result->ob_digit[i] = src->ob_digit[i]; + result->long_value.ob_digit[i] = src->long_value.ob_digit[i]; } } return (PyObject *)result; @@ -210,7 +210,7 @@ _PyLong_FromMedium(sdigit x) Py_ssize_t sign = x < 0 ? -1: 1; digit abs_x = x < 0 ? -x : x; _PyObject_InitVar((PyVarObject*)v, &PyLong_Type, sign); - v->ob_digit[0] = abs_x; + v->long_value.ob_digit[0] = abs_x; return (PyObject*)v; } @@ -241,7 +241,7 @@ _PyLong_FromLarge(stwodigits ival) } PyLongObject *v = _PyLong_New(ndigits); if (v != NULL) { - digit *p = v->ob_digit; + digit *p = v->long_value.ob_digit; Py_SET_SIZE(v, ndigits * sign); t = abs_ival; while (t) { @@ -285,7 +285,7 @@ _PyLong_AssignValue(PyObject **target, Py_ssize_t value) // Since the primary use-case is iterating over ranges, which // are typically positive, only do this optimization // for positive integers (for now). - ((PyLongObject *)old)->ob_digit[0] = + ((PyLongObject *)old)->long_value.ob_digit[0] = Py_SAFE_DOWNCAST(value, Py_ssize_t, digit); return 0; } @@ -346,7 +346,7 @@ PyLong_FromLong(long ival) /* Construct output value. */ v = _PyLong_New(ndigits); if (v != NULL) { - digit *p = v->ob_digit; + digit *p = v->long_value.ob_digit; Py_SET_SIZE(v, ival < 0 ? -ndigits : ndigits); t = abs_ival; while (t) { @@ -373,7 +373,7 @@ PyLong_FromLong(long ival) if (v == NULL) { \ return NULL; \ } \ - digit *p = v->ob_digit; \ + digit *p = v->long_value.ob_digit; \ while ((ival)) { \ *p++ = (digit)((ival) & PyLong_MASK); \ (ival) >>= PyLong_SHIFT; \ @@ -452,7 +452,7 @@ PyLong_FromDouble(double dval) frac = ldexp(frac, (expo-1) % PyLong_SHIFT + 1); for (i = ndig; --i >= 0; ) { digit bits = (digit)frac; - v->ob_digit[i] = bits; + v->long_value.ob_digit[i] = bits; frac = frac - (double)bits; frac = ldexp(frac, PyLong_SHIFT); } @@ -516,13 +516,13 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) switch (i) { case -1: - res = -(sdigit)v->ob_digit[0]; + res = -(sdigit)v->long_value.ob_digit[0]; break; case 0: res = 0; break; case 1: - res = v->ob_digit[0]; + res = v->long_value.ob_digit[0]; break; default: sign = 1; @@ -533,7 +533,7 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) } while (--i >= 0) { prev = x; - x = (x << PyLong_SHIFT) | v->ob_digit[i]; + x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; if ((x >> PyLong_SHIFT) != prev) { *overflow = sign; goto exit; @@ -617,9 +617,9 @@ PyLong_AsSsize_t(PyObject *vv) { v = (PyLongObject *)vv; i = Py_SIZE(v); switch (i) { - case -1: return -(sdigit)v->ob_digit[0]; + case -1: return -(sdigit)v->long_value.ob_digit[0]; case 0: return 0; - case 1: return v->ob_digit[0]; + case 1: return v->long_value.ob_digit[0]; } sign = 1; x = 0; @@ -629,7 +629,7 @@ PyLong_AsSsize_t(PyObject *vv) { } while (--i >= 0) { prev = x; - x = (x << PyLong_SHIFT) | v->ob_digit[i]; + x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; if ((x >> PyLong_SHIFT) != prev) goto overflow; } @@ -679,11 +679,11 @@ PyLong_AsUnsignedLong(PyObject *vv) } switch (i) { case 0: return 0; - case 1: return v->ob_digit[0]; + case 1: return v->long_value.ob_digit[0]; } while (--i >= 0) { prev = x; - x = (x << PyLong_SHIFT) | v->ob_digit[i]; + x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; if ((x >> PyLong_SHIFT) != prev) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert " @@ -723,11 +723,11 @@ PyLong_AsSize_t(PyObject *vv) } switch (i) { case 0: return 0; - case 1: return v->ob_digit[0]; + case 1: return v->long_value.ob_digit[0]; } while (--i >= 0) { prev = x; - x = (x << PyLong_SHIFT) | v->ob_digit[i]; + x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; if ((x >> PyLong_SHIFT) != prev) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C size_t"); @@ -756,7 +756,7 @@ _PyLong_AsUnsignedLongMask(PyObject *vv) i = Py_SIZE(v); switch (i) { case 0: return 0; - case 1: return v->ob_digit[0]; + case 1: return v->long_value.ob_digit[0]; } sign = 1; x = 0; @@ -765,7 +765,7 @@ _PyLong_AsUnsignedLongMask(PyObject *vv) i = -i; } while (--i >= 0) { - x = (x << PyLong_SHIFT) | v->ob_digit[i]; + x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; } return x * sign; } @@ -826,9 +826,9 @@ _PyLong_NumBits(PyObject *vv) assert(v != NULL); assert(PyLong_Check(v)); ndigits = Py_ABS(Py_SIZE(v)); - assert(ndigits == 0 || v->ob_digit[ndigits - 1] != 0); + assert(ndigits == 0 || v->long_value.ob_digit[ndigits - 1] != 0); if (ndigits > 0) { - digit msd = v->ob_digit[ndigits - 1]; + digit msd = v->long_value.ob_digit[ndigits - 1]; if ((size_t)(ndigits - 1) > SIZE_MAX / (size_t)PyLong_SHIFT) goto Overflow; result = (size_t)(ndigits - 1) * (size_t)PyLong_SHIFT; @@ -855,7 +855,7 @@ _PyLong_FromByteArray(const unsigned char* bytes, size_t n, size_t numsignificantbytes; /* number of bytes that matter */ Py_ssize_t ndigits; /* number of Python int digits */ PyLongObject* v; /* result */ - Py_ssize_t idigit = 0; /* next free index in v->ob_digit */ + Py_ssize_t idigit = 0; /* next free index in v->long_value.ob_digit */ if (n == 0) return PyLong_FromLong(0L); @@ -937,7 +937,7 @@ _PyLong_FromByteArray(const unsigned char* bytes, size_t n, if (accumbits >= PyLong_SHIFT) { /* There's enough to fill a Python digit. */ assert(idigit < ndigits); - v->ob_digit[idigit] = (digit)(accum & PyLong_MASK); + v->long_value.ob_digit[idigit] = (digit)(accum & PyLong_MASK); ++idigit; accum >>= PyLong_SHIFT; accumbits -= PyLong_SHIFT; @@ -947,7 +947,7 @@ _PyLong_FromByteArray(const unsigned char* bytes, size_t n, assert(accumbits < PyLong_SHIFT); if (accumbits) { assert(idigit < ndigits); - v->ob_digit[idigit] = (digit)accum; + v->long_value.ob_digit[idigit] = (digit)accum; ++idigit; } } @@ -961,7 +961,7 @@ _PyLong_AsByteArray(PyLongObject* v, unsigned char* bytes, size_t n, int little_endian, int is_signed) { - Py_ssize_t i; /* index into v->ob_digit */ + Py_ssize_t i; /* index into v->long_value.ob_digit */ Py_ssize_t ndigits; /* |v->ob_size| */ twodigits accum; /* sliding register */ unsigned int accumbits; /* # bits in accum */ @@ -1000,13 +1000,13 @@ _PyLong_AsByteArray(PyLongObject* v, It's crucial that every Python digit except for the MSD contribute exactly PyLong_SHIFT bits to the total, so first assert that the int is normalized. */ - assert(ndigits == 0 || v->ob_digit[ndigits - 1] != 0); + assert(ndigits == 0 || v->long_value.ob_digit[ndigits - 1] != 0); j = 0; accum = 0; accumbits = 0; carry = do_twos_comp ? 1 : 0; for (i = 0; i < ndigits; ++i) { - digit thisdigit = v->ob_digit[i]; + digit thisdigit = v->long_value.ob_digit[i]; if (do_twos_comp) { thisdigit = (thisdigit ^ PyLong_MASK) + carry; carry = thisdigit >> PyLong_SHIFT; @@ -1173,7 +1173,7 @@ PyLong_FromLongLong(long long ival) /* Construct output value. */ v = _PyLong_New(ndigits); if (v != NULL) { - digit *p = v->ob_digit; + digit *p = v->long_value.ob_digit; Py_SET_SIZE(v, ival < 0 ? -ndigits : ndigits); t = abs_ival; while (t) { @@ -1216,7 +1216,7 @@ PyLong_FromSsize_t(Py_ssize_t ival) } v = _PyLong_New(ndigits); if (v != NULL) { - digit *p = v->ob_digit; + digit *p = v->long_value.ob_digit; Py_SET_SIZE(v, negative ? -ndigits : ndigits); t = abs_ival; while (t) { @@ -1256,13 +1256,13 @@ PyLong_AsLongLong(PyObject *vv) res = 0; switch(Py_SIZE(v)) { case -1: - bytes = -(sdigit)v->ob_digit[0]; + bytes = -(sdigit)v->long_value.ob_digit[0]; break; case 0: bytes = 0; break; case 1: - bytes = v->ob_digit[0]; + bytes = v->long_value.ob_digit[0]; break; default: res = _PyLong_AsByteArray((PyLongObject *)v, (unsigned char *)&bytes, @@ -1301,7 +1301,7 @@ PyLong_AsUnsignedLongLong(PyObject *vv) v = (PyLongObject*)vv; switch(Py_SIZE(v)) { case 0: return 0; - case 1: return v->ob_digit[0]; + case 1: return v->long_value.ob_digit[0]; } res = _PyLong_AsByteArray((PyLongObject *)vv, (unsigned char *)&bytes, @@ -1332,7 +1332,7 @@ _PyLong_AsUnsignedLongLongMask(PyObject *vv) v = (PyLongObject *)vv; switch(Py_SIZE(v)) { case 0: return 0; - case 1: return v->ob_digit[0]; + case 1: return v->long_value.ob_digit[0]; } i = Py_SIZE(v); sign = 1; @@ -1342,7 +1342,7 @@ _PyLong_AsUnsignedLongLongMask(PyObject *vv) i = -i; } while (--i >= 0) { - x = (x << PyLong_SHIFT) | v->ob_digit[i]; + x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; } return x * sign; } @@ -1413,13 +1413,13 @@ PyLong_AsLongLongAndOverflow(PyObject *vv, int *overflow) switch (i) { case -1: - res = -(sdigit)v->ob_digit[0]; + res = -(sdigit)v->long_value.ob_digit[0]; break; case 0: res = 0; break; case 1: - res = v->ob_digit[0]; + res = v->long_value.ob_digit[0]; break; default: sign = 1; @@ -1430,7 +1430,7 @@ PyLong_AsLongLongAndOverflow(PyObject *vv, int *overflow) } while (--i >= 0) { prev = x; - x = (x << PyLong_SHIFT) + v->ob_digit[i]; + x = (x << PyLong_SHIFT) + v->long_value.ob_digit[i]; if ((x >> PyLong_SHIFT) != prev) { *overflow = sign; goto exit; @@ -1701,7 +1701,7 @@ divrem1(PyLongObject *a, digit n, digit *prem) z = _PyLong_New(size); if (z == NULL) return NULL; - *prem = inplace_divrem1(z->ob_digit, a->ob_digit, size, n); + *prem = inplace_divrem1(z->long_value.ob_digit, a->long_value.ob_digit, size, n); return long_normalize(z); } @@ -1730,7 +1730,7 @@ rem1(PyLongObject *a, digit n) assert(n > 0 && n <= PyLong_MASK); return (PyLongObject *)PyLong_FromLong( - (long)inplace_rem1(a->ob_digit, size, n) + (long)inplace_rem1(a->long_value.ob_digit, size, n) ); } @@ -1880,8 +1880,8 @@ long_to_decimal_string_internal(PyObject *aa, /* convert array of base _PyLong_BASE digits in pin to an array of base _PyLong_DECIMAL_BASE digits in pout, following Knuth (TAOCP, Volume 2 (3rd edn), section 4.4, Method 1b). */ - pin = a->ob_digit; - pout = scratch->ob_digit; + pin = a->long_value.ob_digit; + pout = scratch->long_value.ob_digit; size = 0; for (i = size_a; --i >= 0; ) { digit hi = pin[i]; @@ -2086,7 +2086,7 @@ long_format_binary(PyObject *aa, int base, int alternate, return -1; } size_a_in_bits = (size_a - 1) * PyLong_SHIFT + - bit_length_digit(a->ob_digit[size_a - 1]); + bit_length_digit(a->long_value.ob_digit[size_a - 1]); /* Allow 1 character for a '-' sign. */ sz = negative + (size_a_in_bits + (bits - 1)) / bits; } @@ -2123,7 +2123,7 @@ long_format_binary(PyObject *aa, int base, int alternate, int accumbits = 0; /* # of bits in accum */ \ Py_ssize_t i; \ for (i = 0; i < size_a; ++i) { \ - accum |= (twodigits)a->ob_digit[i] << accumbits; \ + accum |= (twodigits)a->long_value.ob_digit[i] << accumbits; \ accumbits += PyLong_SHIFT; \ assert(accumbits >= bits); \ do { \ @@ -2321,7 +2321,7 @@ long_from_binary_base(const char *start, const char *end, Py_ssize_t digits, int */ accum = 0; bits_in_accum = 0; - pdigit = z->ob_digit; + pdigit = z->long_value.ob_digit; p = end; while (--p >= start) { int k; @@ -2334,7 +2334,7 @@ long_from_binary_base(const char *start, const char *end, Py_ssize_t digits, int bits_in_accum += bits_per_char; if (bits_in_accum >= PyLong_SHIFT) { *pdigit++ = (digit)(accum & PyLong_MASK); - assert(pdigit - z->ob_digit <= n); + assert(pdigit - z->long_value.ob_digit <= n); accum >>= PyLong_SHIFT; bits_in_accum -= PyLong_SHIFT; assert(bits_in_accum < PyLong_SHIFT); @@ -2343,9 +2343,9 @@ long_from_binary_base(const char *start, const char *end, Py_ssize_t digits, int if (bits_in_accum) { assert(bits_in_accum <= PyLong_SHIFT); *pdigit++ = (digit)accum; - assert(pdigit - z->ob_digit <= n); + assert(pdigit - z->long_value.ob_digit <= n); } - while (pdigit - z->ob_digit < n) + while (pdigit - z->long_value.ob_digit < n) *pdigit++ = 0; *res = z; return 0; @@ -2512,7 +2512,7 @@ long_from_non_binary_base(const char *start, const char *end, Py_ssize_t digits, /* Create an int object that can contain the largest possible * integer with this base and length. Note that there's no - * need to initialize z->ob_digit -- no slot is read up before + * need to initialize z->long_value.ob_digit -- no slot is read up before * being stored into. */ double fsize_z = (double)digits * log_base_BASE[base] + 1.0; @@ -2571,7 +2571,7 @@ long_from_non_binary_base(const char *start, const char *end, Py_ssize_t digits, } /* Multiply z by convmult, and add c. */ - pz = z->ob_digit; + pz = z->long_value.ob_digit; pzstop = pz + Py_SIZE(z); for (; pz < pzstop; ++pz) { c += (twodigits)*pz * convmult; @@ -2595,11 +2595,11 @@ long_from_non_binary_base(const char *start, const char *end, Py_ssize_t digits, *res = NULL; return 0; } - memcpy(tmp->ob_digit, - z->ob_digit, + memcpy(tmp->long_value.ob_digit, + z->long_value.ob_digit, sizeof(digit) * size_z); Py_SETREF(z, tmp); - z->ob_digit[size_z] = (digit)c; + z->long_value.ob_digit[size_z] = (digit)c; ++size_z; } } @@ -2901,7 +2901,7 @@ long_divrem(PyLongObject *a, PyLongObject *b, } if (size_a < size_b || (size_a == size_b && - a->ob_digit[size_a-1] < b->ob_digit[size_b-1])) { + a->long_value.ob_digit[size_a-1] < b->long_value.ob_digit[size_b-1])) { /* |a| < |b|. */ *prem = (PyLongObject *)long_long((PyObject *)a); if (*prem == NULL) { @@ -2913,7 +2913,7 @@ long_divrem(PyLongObject *a, PyLongObject *b, } if (size_b == 1) { digit rem = 0; - z = divrem1(a, b->ob_digit[0], &rem); + z = divrem1(a, b->long_value.ob_digit[0], &rem); if (z == NULL) return -1; *prem = (PyLongObject *) PyLong_FromLong((long)rem); @@ -2965,13 +2965,13 @@ long_rem(PyLongObject *a, PyLongObject *b, PyLongObject **prem) } if (size_a < size_b || (size_a == size_b && - a->ob_digit[size_a-1] < b->ob_digit[size_b-1])) { + a->long_value.ob_digit[size_a-1] < b->long_value.ob_digit[size_b-1])) { /* |a| < |b|. */ *prem = (PyLongObject *)long_long((PyObject *)a); return -(*prem == NULL); } if (size_b == 1) { - *prem = rem1(a, b->ob_digit[0]); + *prem = rem1(a, b->long_value.ob_digit[0]); if (*prem == NULL) return -1; } @@ -3031,16 +3031,16 @@ x_divrem(PyLongObject *v1, PyLongObject *w1, PyLongObject **prem) /* normalize: shift w1 left so that its top digit is >= PyLong_BASE/2. shift v1 left by the same amount. Results go into w and v. */ - d = PyLong_SHIFT - bit_length_digit(w1->ob_digit[size_w-1]); - carry = v_lshift(w->ob_digit, w1->ob_digit, size_w, d); + d = PyLong_SHIFT - bit_length_digit(w1->long_value.ob_digit[size_w-1]); + carry = v_lshift(w->long_value.ob_digit, w1->long_value.ob_digit, size_w, d); assert(carry == 0); - carry = v_lshift(v->ob_digit, v1->ob_digit, size_v, d); - if (carry != 0 || v->ob_digit[size_v-1] >= w->ob_digit[size_w-1]) { - v->ob_digit[size_v] = carry; + carry = v_lshift(v->long_value.ob_digit, v1->long_value.ob_digit, size_v, d); + if (carry != 0 || v->long_value.ob_digit[size_v-1] >= w->long_value.ob_digit[size_w-1]) { + v->long_value.ob_digit[size_v] = carry; size_v++; } - /* Now v->ob_digit[size_v-1] < w->ob_digit[size_w-1], so quotient has + /* Now v->long_value.ob_digit[size_v-1] < w->long_value.ob_digit[size_w-1], so quotient has at most (and usually exactly) k = size_v - size_w digits. */ k = size_v - size_w; assert(k >= 0); @@ -3051,11 +3051,11 @@ x_divrem(PyLongObject *v1, PyLongObject *w1, PyLongObject **prem) *prem = NULL; return NULL; } - v0 = v->ob_digit; - w0 = w->ob_digit; + v0 = v->long_value.ob_digit; + w0 = w->long_value.ob_digit; wm1 = w0[size_w-1]; wm2 = w0[size_w-2]; - for (vk = v0+k, ak = a->ob_digit + k; vk-- > v0;) { + for (vk = v0+k, ak = a->long_value.ob_digit + k; vk-- > v0;) { /* inner loop: divide vk[0:size_w+1] by w0[0:size_w], giving single-digit quotient q, remainder in vk[0:size_w]. */ @@ -3160,7 +3160,7 @@ _PyLong_Frexp(PyLongObject *a, Py_ssize_t *e) *e = 0; return 0.0; } - a_bits = bit_length_digit(a->ob_digit[a_size-1]); + a_bits = bit_length_digit(a->long_value.ob_digit[a_size-1]); /* The following is an overflow-free version of the check "if ((a_size - 1) * PyLong_SHIFT + a_bits > PY_SSIZE_T_MAX) ..." */ if (a_size >= (PY_SSIZE_T_MAX - 1) / PyLong_SHIFT + 1 && @@ -3198,7 +3198,7 @@ _PyLong_Frexp(PyLongObject *a, Py_ssize_t *e) shift_digits = (DBL_MANT_DIG + 2 - a_bits) / PyLong_SHIFT; shift_bits = (DBL_MANT_DIG + 2 - a_bits) % PyLong_SHIFT; x_size = shift_digits; - rem = v_lshift(x_digits + x_size, a->ob_digit, a_size, + rem = v_lshift(x_digits + x_size, a->long_value.ob_digit, a_size, (int)shift_bits); x_size += a_size; x_digits[x_size++] = rem; @@ -3206,7 +3206,7 @@ _PyLong_Frexp(PyLongObject *a, Py_ssize_t *e) else { shift_digits = (a_bits - DBL_MANT_DIG - 2) / PyLong_SHIFT; shift_bits = (a_bits - DBL_MANT_DIG - 2) % PyLong_SHIFT; - rem = v_rshift(x_digits, a->ob_digit + shift_digits, + rem = v_rshift(x_digits, a->long_value.ob_digit + shift_digits, a_size - shift_digits, (int)shift_bits); x_size = a_size - shift_digits; /* For correct rounding below, we need the least significant @@ -3217,7 +3217,7 @@ _PyLong_Frexp(PyLongObject *a, Py_ssize_t *e) x_digits[0] |= 1; else while (shift_digits > 0) - if (a->ob_digit[--shift_digits]) { + if (a->long_value.ob_digit[--shift_digits]) { x_digits[0] |= 1; break; } @@ -3297,7 +3297,7 @@ long_compare(PyLongObject *a, PyLongObject *b) Py_ssize_t i = Py_ABS(Py_SIZE(a)); sdigit diff = 0; while (--i >= 0) { - diff = (sdigit) a->ob_digit[i] - (sdigit) b->ob_digit[i]; + diff = (sdigit) a->long_value.ob_digit[i] - (sdigit) b->long_value.ob_digit[i]; if (diff) { break; } @@ -3328,9 +3328,9 @@ long_hash(PyLongObject *v) i = Py_SIZE(v); switch(i) { - case -1: return v->ob_digit[0]==1 ? -2 : -(sdigit)v->ob_digit[0]; + case -1: return v->long_value.ob_digit[0]==1 ? -2 : -(sdigit)v->long_value.ob_digit[0]; case 0: return 0; - case 1: return v->ob_digit[0]; + case 1: return v->long_value.ob_digit[0]; } sign = 1; x = 0; @@ -3340,7 +3340,7 @@ long_hash(PyLongObject *v) } while (--i >= 0) { /* Here x is a quantity in the range [0, _PyHASH_MODULUS); we - want to compute x * 2**PyLong_SHIFT + v->ob_digit[i] modulo + want to compute x * 2**PyLong_SHIFT + v->long_value.ob_digit[i] modulo _PyHASH_MODULUS. The computation of x * 2**PyLong_SHIFT % _PyHASH_MODULUS @@ -3366,7 +3366,7 @@ long_hash(PyLongObject *v) _PyHASH_MODULUS. */ x = ((x << PyLong_SHIFT) & _PyHASH_MODULUS) | (x >> (_PyHASH_BITS - PyLong_SHIFT)); - x += v->ob_digit[i]; + x += v->long_value.ob_digit[i]; if (x >= _PyHASH_MODULUS) x -= _PyHASH_MODULUS; } @@ -3398,16 +3398,16 @@ x_add(PyLongObject *a, PyLongObject *b) if (z == NULL) return NULL; for (i = 0; i < size_b; ++i) { - carry += a->ob_digit[i] + b->ob_digit[i]; - z->ob_digit[i] = carry & PyLong_MASK; + carry += a->long_value.ob_digit[i] + b->long_value.ob_digit[i]; + z->long_value.ob_digit[i] = carry & PyLong_MASK; carry >>= PyLong_SHIFT; } for (; i < size_a; ++i) { - carry += a->ob_digit[i]; - z->ob_digit[i] = carry & PyLong_MASK; + carry += a->long_value.ob_digit[i]; + z->long_value.ob_digit[i] = carry & PyLong_MASK; carry >>= PyLong_SHIFT; } - z->ob_digit[i] = carry; + z->long_value.ob_digit[i] = carry; return long_normalize(z); } @@ -3433,11 +3433,11 @@ x_sub(PyLongObject *a, PyLongObject *b) else if (size_a == size_b) { /* Find highest digit where a and b differ: */ i = size_a; - while (--i >= 0 && a->ob_digit[i] == b->ob_digit[i]) + while (--i >= 0 && a->long_value.ob_digit[i] == b->long_value.ob_digit[i]) ; if (i < 0) return (PyLongObject *)PyLong_FromLong(0); - if (a->ob_digit[i] < b->ob_digit[i]) { + if (a->long_value.ob_digit[i] < b->long_value.ob_digit[i]) { sign = -1; { PyLongObject *temp = a; a = b; b = temp; } } @@ -3449,14 +3449,14 @@ x_sub(PyLongObject *a, PyLongObject *b) for (i = 0; i < size_b; ++i) { /* The following assumes unsigned arithmetic works module 2**N for some N>PyLong_SHIFT. */ - borrow = a->ob_digit[i] - b->ob_digit[i] - borrow; - z->ob_digit[i] = borrow & PyLong_MASK; + borrow = a->long_value.ob_digit[i] - b->long_value.ob_digit[i] - borrow; + z->long_value.ob_digit[i] = borrow & PyLong_MASK; borrow >>= PyLong_SHIFT; borrow &= 1; /* Keep only one sign bit */ } for (; i < size_a; ++i) { - borrow = a->ob_digit[i] - borrow; - z->ob_digit[i] = borrow & PyLong_MASK; + borrow = a->long_value.ob_digit[i] - borrow; + z->long_value.ob_digit[i] = borrow & PyLong_MASK; borrow >>= PyLong_SHIFT; borrow &= 1; /* Keep only one sign bit */ } @@ -3557,7 +3557,7 @@ x_mul(PyLongObject *a, PyLongObject *b) if (z == NULL) return NULL; - memset(z->ob_digit, 0, Py_SIZE(z) * sizeof(digit)); + memset(z->long_value.ob_digit, 0, Py_SIZE(z) * sizeof(digit)); if (a == b) { /* Efficient squaring per HAC, Algorithm 14.16: * http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf @@ -3565,12 +3565,12 @@ x_mul(PyLongObject *a, PyLongObject *b) * via exploiting that each entry in the multiplication * pyramid appears twice (except for the size_a squares). */ - digit *paend = a->ob_digit + size_a; + digit *paend = a->long_value.ob_digit + size_a; for (i = 0; i < size_a; ++i) { twodigits carry; - twodigits f = a->ob_digit[i]; - digit *pz = z->ob_digit + (i << 1); - digit *pa = a->ob_digit + i + 1; + twodigits f = a->long_value.ob_digit[i]; + digit *pz = z->long_value.ob_digit + (i << 1); + digit *pa = a->long_value.ob_digit + i + 1; SIGCHECK({ Py_DECREF(z); @@ -3619,10 +3619,10 @@ x_mul(PyLongObject *a, PyLongObject *b) else { /* a is not the same as b -- gradeschool int mult */ for (i = 0; i < size_a; ++i) { twodigits carry = 0; - twodigits f = a->ob_digit[i]; - digit *pz = z->ob_digit + i; - digit *pb = b->ob_digit; - digit *pbend = b->ob_digit + size_b; + twodigits f = a->long_value.ob_digit[i]; + digit *pz = z->long_value.ob_digit + i; + digit *pb = b->long_value.ob_digit; + digit *pbend = b->long_value.ob_digit + size_b; SIGCHECK({ Py_DECREF(z); @@ -3670,8 +3670,8 @@ kmul_split(PyLongObject *n, return -1; } - memcpy(lo->ob_digit, n->ob_digit, size_lo * sizeof(digit)); - memcpy(hi->ob_digit, n->ob_digit + size_lo, size_hi * sizeof(digit)); + memcpy(lo->long_value.ob_digit, n->long_value.ob_digit, size_lo * sizeof(digit)); + memcpy(hi->long_value.ob_digit, n->long_value.ob_digit + size_lo, size_hi * sizeof(digit)); *high = long_normalize(hi); *low = long_normalize(lo); @@ -3769,20 +3769,20 @@ k_mul(PyLongObject *a, PyLongObject *b) if (ret == NULL) goto fail; #ifdef Py_DEBUG /* Fill with trash, to catch reference to uninitialized digits. */ - memset(ret->ob_digit, 0xDF, Py_SIZE(ret) * sizeof(digit)); + memset(ret->long_value.ob_digit, 0xDF, Py_SIZE(ret) * sizeof(digit)); #endif /* 2. t1 <- ah*bh, and copy into high digits of result. */ if ((t1 = k_mul(ah, bh)) == NULL) goto fail; assert(Py_SIZE(t1) >= 0); assert(2*shift + Py_SIZE(t1) <= Py_SIZE(ret)); - memcpy(ret->ob_digit + 2*shift, t1->ob_digit, + memcpy(ret->long_value.ob_digit + 2*shift, t1->long_value.ob_digit, Py_SIZE(t1) * sizeof(digit)); /* Zero-out the digits higher than the ah*bh copy. */ i = Py_SIZE(ret) - 2*shift - Py_SIZE(t1); if (i) - memset(ret->ob_digit + 2*shift + Py_SIZE(t1), 0, + memset(ret->long_value.ob_digit + 2*shift + Py_SIZE(t1), 0, i * sizeof(digit)); /* 3. t2 <- al*bl, and copy into the low digits. */ @@ -3792,21 +3792,21 @@ k_mul(PyLongObject *a, PyLongObject *b) } assert(Py_SIZE(t2) >= 0); assert(Py_SIZE(t2) <= 2*shift); /* no overlap with high digits */ - memcpy(ret->ob_digit, t2->ob_digit, Py_SIZE(t2) * sizeof(digit)); + memcpy(ret->long_value.ob_digit, t2->long_value.ob_digit, Py_SIZE(t2) * sizeof(digit)); /* Zero out remaining digits. */ i = 2*shift - Py_SIZE(t2); /* number of uninitialized digits */ if (i) - memset(ret->ob_digit + Py_SIZE(t2), 0, i * sizeof(digit)); + memset(ret->long_value.ob_digit + Py_SIZE(t2), 0, i * sizeof(digit)); /* 4 & 5. Subtract ah*bh (t1) and al*bl (t2). We do al*bl first * because it's fresher in cache. */ i = Py_SIZE(ret) - shift; /* # digits after shift */ - (void)v_isub(ret->ob_digit + shift, i, t2->ob_digit, Py_SIZE(t2)); + (void)v_isub(ret->long_value.ob_digit + shift, i, t2->long_value.ob_digit, Py_SIZE(t2)); _Py_DECREF_INT(t2); - (void)v_isub(ret->ob_digit + shift, i, t1->ob_digit, Py_SIZE(t1)); + (void)v_isub(ret->long_value.ob_digit + shift, i, t1->long_value.ob_digit, Py_SIZE(t1)); _Py_DECREF_INT(t1); /* 6. t3 <- (ah+al)(bh+bl), and add into result. */ @@ -3835,7 +3835,7 @@ k_mul(PyLongObject *a, PyLongObject *b) /* Add t3. It's not obvious why we can't run out of room here. * See the (*) comment after this function. */ - (void)v_iadd(ret->ob_digit + shift, i, t3->ob_digit, Py_SIZE(t3)); + (void)v_iadd(ret->long_value.ob_digit + shift, i, t3->long_value.ob_digit, Py_SIZE(t3)); _Py_DECREF_INT(t3); return long_normalize(ret); @@ -3918,7 +3918,7 @@ k_lopsided_mul(PyLongObject *a, PyLongObject *b) ret = _PyLong_New(asize + bsize); if (ret == NULL) return NULL; - memset(ret->ob_digit, 0, Py_SIZE(ret) * sizeof(digit)); + memset(ret->long_value.ob_digit, 0, Py_SIZE(ret) * sizeof(digit)); /* Successive slices of b are copied into bslice. */ bslice = _PyLong_New(asize); @@ -3931,7 +3931,7 @@ k_lopsided_mul(PyLongObject *a, PyLongObject *b) const Py_ssize_t nbtouse = Py_MIN(bsize, asize); /* Multiply the next slice of b by a. */ - memcpy(bslice->ob_digit, b->ob_digit + nbdone, + memcpy(bslice->long_value.ob_digit, b->long_value.ob_digit + nbdone, nbtouse * sizeof(digit)); Py_SET_SIZE(bslice, nbtouse); product = k_mul(a, bslice); @@ -3939,8 +3939,8 @@ k_lopsided_mul(PyLongObject *a, PyLongObject *b) goto fail; /* Add into result. */ - (void)v_iadd(ret->ob_digit + nbdone, Py_SIZE(ret) - nbdone, - product->ob_digit, Py_SIZE(product)); + (void)v_iadd(ret->long_value.ob_digit + nbdone, Py_SIZE(ret) - nbdone, + product->long_value.ob_digit, Py_SIZE(product)); _Py_DECREF_INT(product); bsize -= nbtouse; @@ -3988,8 +3988,8 @@ long_mul(PyLongObject *a, PyLongObject *b) static PyObject * fast_mod(PyLongObject *a, PyLongObject *b) { - sdigit left = a->ob_digit[0]; - sdigit right = b->ob_digit[0]; + sdigit left = a->long_value.ob_digit[0]; + sdigit right = b->long_value.ob_digit[0]; sdigit mod; assert(Py_ABS(Py_SIZE(a)) == 1); @@ -4011,8 +4011,8 @@ fast_mod(PyLongObject *a, PyLongObject *b) static PyObject * fast_floor_div(PyLongObject *a, PyLongObject *b) { - sdigit left = a->ob_digit[0]; - sdigit right = b->ob_digit[0]; + sdigit left = a->long_value.ob_digit[0]; + sdigit right = b->long_value.ob_digit[0]; sdigit div; assert(Py_ABS(Py_SIZE(a)) == 1); @@ -4334,18 +4334,18 @@ long_true_divide(PyObject *v, PyObject *w) the x87 FPU set to 64-bit precision. */ a_is_small = a_size <= MANT_DIG_DIGITS || (a_size == MANT_DIG_DIGITS+1 && - a->ob_digit[MANT_DIG_DIGITS] >> MANT_DIG_BITS == 0); + a->long_value.ob_digit[MANT_DIG_DIGITS] >> MANT_DIG_BITS == 0); b_is_small = b_size <= MANT_DIG_DIGITS || (b_size == MANT_DIG_DIGITS+1 && - b->ob_digit[MANT_DIG_DIGITS] >> MANT_DIG_BITS == 0); + b->long_value.ob_digit[MANT_DIG_DIGITS] >> MANT_DIG_BITS == 0); if (a_is_small && b_is_small) { double da, db; - da = a->ob_digit[--a_size]; + da = a->long_value.ob_digit[--a_size]; while (a_size > 0) - da = da * PyLong_BASE + a->ob_digit[--a_size]; - db = b->ob_digit[--b_size]; + da = da * PyLong_BASE + a->long_value.ob_digit[--a_size]; + db = b->long_value.ob_digit[--b_size]; while (b_size > 0) - db = db * PyLong_BASE + b->ob_digit[--b_size]; + db = db * PyLong_BASE + b->long_value.ob_digit[--b_size]; result = da / db; goto success; } @@ -4359,8 +4359,8 @@ long_true_divide(PyObject *v, PyObject *w) /* Extreme underflow */ goto underflow_or_zero; /* Next line is now safe from overflowing a Py_ssize_t */ - diff = diff * PyLong_SHIFT + bit_length_digit(a->ob_digit[a_size - 1]) - - bit_length_digit(b->ob_digit[b_size - 1]); + diff = diff * PyLong_SHIFT + bit_length_digit(a->long_value.ob_digit[a_size - 1]) - + bit_length_digit(b->long_value.ob_digit[b_size - 1]); /* Now diff = a_bits - b_bits. */ if (diff > DBL_MAX_EXP) goto overflow; @@ -4389,10 +4389,10 @@ long_true_divide(PyObject *v, PyObject *w) if (x == NULL) goto error; for (i = 0; i < shift_digits; i++) - x->ob_digit[i] = 0; - rem = v_lshift(x->ob_digit + shift_digits, a->ob_digit, + x->long_value.ob_digit[i] = 0; + rem = v_lshift(x->long_value.ob_digit + shift_digits, a->long_value.ob_digit, a_size, -shift % PyLong_SHIFT); - x->ob_digit[a_size + shift_digits] = rem; + x->long_value.ob_digit[a_size + shift_digits] = rem; } else { Py_ssize_t shift_digits = shift / PyLong_SHIFT; @@ -4402,13 +4402,13 @@ long_true_divide(PyObject *v, PyObject *w) x = _PyLong_New(a_size - shift_digits); if (x == NULL) goto error; - rem = v_rshift(x->ob_digit, a->ob_digit + shift_digits, + rem = v_rshift(x->long_value.ob_digit, a->long_value.ob_digit + shift_digits, a_size - shift_digits, shift % PyLong_SHIFT); /* set inexact if any of the bits shifted out is nonzero */ if (rem) inexact = 1; while (!inexact && shift_digits > 0) - if (a->ob_digit[--shift_digits]) + if (a->long_value.ob_digit[--shift_digits]) inexact = 1; } long_normalize(x); @@ -4417,8 +4417,8 @@ long_true_divide(PyObject *v, PyObject *w) /* x //= b. If the remainder is nonzero, set inexact. We own the only reference to x, so it's safe to modify it in-place. */ if (b_size == 1) { - digit rem = inplace_divrem1(x->ob_digit, x->ob_digit, x_size, - b->ob_digit[0]); + digit rem = inplace_divrem1(x->long_value.ob_digit, x->long_value.ob_digit, x_size, + b->long_value.ob_digit[0]); long_normalize(x); if (rem) inexact = 1; @@ -4435,7 +4435,7 @@ long_true_divide(PyObject *v, PyObject *w) } x_size = Py_ABS(Py_SIZE(x)); assert(x_size > 0); /* result of division is never zero */ - x_bits = (x_size-1)*PyLong_SHIFT+bit_length_digit(x->ob_digit[x_size-1]); + x_bits = (x_size-1)*PyLong_SHIFT+bit_length_digit(x->long_value.ob_digit[x_size-1]); /* The number of extra bits that have to be rounded away. */ extra_bits = Py_MAX(x_bits, DBL_MIN_EXP - shift) - DBL_MANT_DIG; @@ -4443,15 +4443,15 @@ long_true_divide(PyObject *v, PyObject *w) /* Round by directly modifying the low digit of x. */ mask = (digit)1 << (extra_bits - 1); - low = x->ob_digit[0] | inexact; + low = x->long_value.ob_digit[0] | inexact; if ((low & mask) && (low & (3U*mask-1U))) low += mask; - x->ob_digit[0] = low & ~(2U*mask-1U); + x->long_value.ob_digit[0] = low & ~(2U*mask-1U); /* Convert x to a double dx; the conversion is exact. */ - dx = x->ob_digit[--x_size]; + dx = x->long_value.ob_digit[--x_size]; while (x_size > 0) - dx = dx * PyLong_BASE + x->ob_digit[--x_size]; + dx = dx * PyLong_BASE + x->long_value.ob_digit[--x_size]; Py_DECREF(x); /* Check whether ldexp result will overflow a double. */ @@ -4672,7 +4672,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x) /* if modulus == 1: return 0 */ - if ((Py_SIZE(c) == 1) && (c->ob_digit[0] == 1)) { + if ((Py_SIZE(c) == 1) && (c->long_value.ob_digit[0] == 1)) { z = (PyLongObject *)PyLong_FromLong(0L); goto Done; } @@ -4748,7 +4748,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x) } while(0) i = Py_SIZE(b); - digit bi = i ? b->ob_digit[i-1] : 0; + digit bi = i ? b->long_value.ob_digit[i-1] : 0; digit bit; if (i <= 1 && bi <= 3) { /* aim for minimal overhead */ @@ -4794,7 +4794,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x) if (--i < 0) { break; } - bi = b->ob_digit[i]; + bi = b->long_value.ob_digit[i]; bit = (digit)1 << (PyLong_SHIFT-1); } } @@ -4840,7 +4840,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x) } while(0) for (i = Py_SIZE(b) - 1; i >= 0; --i) { - const digit bi = b->ob_digit[i]; + const digit bi = b->long_value.ob_digit[i]; for (j = PyLong_SHIFT - 1; j >= 0; --j) { const int bit = (bi >> j) & 1; pending = (pending << 1) | bit; @@ -5011,7 +5011,7 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) } hishift = PyLong_SHIFT - remshift; - accum = a->ob_digit[wordshift]; + accum = a->long_value.ob_digit[wordshift]; if (a_negative) { /* For a positive integer a and nonnegative shift, we have: @@ -5028,19 +5028,19 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) digit sticky = 0; for (Py_ssize_t j = 0; j < wordshift; j++) { - sticky |= a->ob_digit[j]; + sticky |= a->long_value.ob_digit[j]; } accum += (PyLong_MASK >> hishift) + (digit)(sticky != 0); } accum >>= remshift; for (Py_ssize_t i = 0, j = wordshift + 1; j < size_a; i++, j++) { - accum += (twodigits)a->ob_digit[j] << hishift; - z->ob_digit[i] = (digit)(accum & PyLong_MASK); + accum += (twodigits)a->long_value.ob_digit[j] << hishift; + z->long_value.ob_digit[i] = (digit)(accum & PyLong_MASK); accum >>= PyLong_SHIFT; } assert(accum <= PyLong_MASK); - z->ob_digit[newsize - 1] = (digit)accum; + z->long_value.ob_digit[newsize - 1] = (digit)accum; z = maybe_small_long(long_normalize(z)); return (PyObject *)z; @@ -5108,15 +5108,15 @@ long_lshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) Py_SET_SIZE(z, -Py_SIZE(z)); } for (i = 0; i < wordshift; i++) - z->ob_digit[i] = 0; + z->long_value.ob_digit[i] = 0; accum = 0; for (j = 0; j < oldsize; i++, j++) { - accum |= (twodigits)a->ob_digit[j] << remshift; - z->ob_digit[i] = (digit)(accum & PyLong_MASK); + accum |= (twodigits)a->long_value.ob_digit[j] << remshift; + z->long_value.ob_digit[i] = (digit)(accum & PyLong_MASK); accum >>= PyLong_SHIFT; } if (remshift) - z->ob_digit[newsize-1] = (digit)accum; + z->long_value.ob_digit[newsize-1] = (digit)accum; else assert(!accum); z = long_normalize(z); @@ -5199,7 +5199,7 @@ long_bitwise(PyLongObject *a, z = _PyLong_New(size_a); if (z == NULL) return NULL; - v_complement(z->ob_digit, a->ob_digit, size_a); + v_complement(z->long_value.ob_digit, a->long_value.ob_digit, size_a); a = z; } else @@ -5215,7 +5215,7 @@ long_bitwise(PyLongObject *a, Py_DECREF(a); return NULL; } - v_complement(z->ob_digit, b->ob_digit, size_b); + v_complement(z->long_value.ob_digit, b->long_value.ob_digit, size_b); b = z; } else @@ -5265,15 +5265,15 @@ long_bitwise(PyLongObject *a, switch(op) { case '&': for (i = 0; i < size_b; ++i) - z->ob_digit[i] = a->ob_digit[i] & b->ob_digit[i]; + z->long_value.ob_digit[i] = a->long_value.ob_digit[i] & b->long_value.ob_digit[i]; break; case '|': for (i = 0; i < size_b; ++i) - z->ob_digit[i] = a->ob_digit[i] | b->ob_digit[i]; + z->long_value.ob_digit[i] = a->long_value.ob_digit[i] | b->long_value.ob_digit[i]; break; case '^': for (i = 0; i < size_b; ++i) - z->ob_digit[i] = a->ob_digit[i] ^ b->ob_digit[i]; + z->long_value.ob_digit[i] = a->long_value.ob_digit[i] ^ b->long_value.ob_digit[i]; break; default: Py_UNREACHABLE(); @@ -5282,16 +5282,16 @@ long_bitwise(PyLongObject *a, /* Copy any remaining digits of a, inverting if necessary. */ if (op == '^' && negb) for (; i < size_z; ++i) - z->ob_digit[i] = a->ob_digit[i] ^ PyLong_MASK; + z->long_value.ob_digit[i] = a->long_value.ob_digit[i] ^ PyLong_MASK; else if (i < size_z) - memcpy(&z->ob_digit[i], &a->ob_digit[i], + memcpy(&z->long_value.ob_digit[i], &a->long_value.ob_digit[i], (size_z-i)*sizeof(digit)); /* Complement result if negative. */ if (negz) { Py_SET_SIZE(z, -(Py_SIZE(z))); - z->ob_digit[size_z] = PyLong_MASK; - v_complement(z->ob_digit, z->ob_digit, size_z+1); + z->long_value.ob_digit[size_z] = PyLong_MASK; + v_complement(z->long_value.ob_digit, z->long_value.ob_digit, size_z+1); } Py_DECREF(a); @@ -5386,7 +5386,7 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg) alloc_b = Py_SIZE(b); /* reduce until a fits into 2 digits */ while ((size_a = Py_SIZE(a)) > 2) { - nbits = bit_length_digit(a->ob_digit[size_a-1]); + nbits = bit_length_digit(a->long_value.ob_digit[size_a-1]); /* extract top 2*PyLong_SHIFT bits of a into x, along with corresponding bits of b into y */ size_b = Py_SIZE(b); @@ -5403,13 +5403,13 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg) Py_XDECREF(d); return (PyObject *)r; } - x = (((twodigits)a->ob_digit[size_a-1] << (2*PyLong_SHIFT-nbits)) | - ((twodigits)a->ob_digit[size_a-2] << (PyLong_SHIFT-nbits)) | - (a->ob_digit[size_a-3] >> nbits)); + x = (((twodigits)a->long_value.ob_digit[size_a-1] << (2*PyLong_SHIFT-nbits)) | + ((twodigits)a->long_value.ob_digit[size_a-2] << (PyLong_SHIFT-nbits)) | + (a->long_value.ob_digit[size_a-3] >> nbits)); - y = ((size_b >= size_a - 2 ? b->ob_digit[size_a-3] >> nbits : 0) | - (size_b >= size_a - 1 ? (twodigits)b->ob_digit[size_a-2] << (PyLong_SHIFT-nbits) : 0) | - (size_b >= size_a ? (twodigits)b->ob_digit[size_a-1] << (2*PyLong_SHIFT-nbits) : 0)); + y = ((size_b >= size_a - 2 ? b->long_value.ob_digit[size_a-3] >> nbits : 0) | + (size_b >= size_a - 1 ? (twodigits)b->long_value.ob_digit[size_a-2] << (PyLong_SHIFT-nbits) : 0) | + (size_b >= size_a ? (twodigits)b->long_value.ob_digit[size_a-1] << (2*PyLong_SHIFT-nbits) : 0)); /* inner loop of Lehmer's algorithm; A, B, C, D never grow larger than PyLong_MASK during the algorithm. */ @@ -5471,14 +5471,14 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg) if (d == NULL) goto error; } - a_end = a->ob_digit + size_a; - b_end = b->ob_digit + size_b; + a_end = a->long_value.ob_digit + size_a; + b_end = b->long_value.ob_digit + size_b; /* compute new a and new b in parallel */ - a_digit = a->ob_digit; - b_digit = b->ob_digit; - c_digit = c->ob_digit; - d_digit = d->ob_digit; + a_digit = a->long_value.ob_digit; + b_digit = b->long_value.ob_digit; + c_digit = c->long_value.ob_digit; + d_digit = d->long_value.ob_digit; c_carry = 0; d_carry = 0; while (b_digit < b_end) { @@ -5651,7 +5651,7 @@ long_subtype_new(PyTypeObject *type, PyObject *x, PyObject *obase) assert(PyLong_Check(newobj)); Py_SET_SIZE(newobj, Py_SIZE(tmp)); for (i = 0; i < n; i++) { - newobj->ob_digit[i] = tmp->ob_digit[i]; + newobj->long_value.ob_digit[i] = tmp->long_value.ob_digit[i]; } Py_DECREF(tmp); return (PyObject *)newobj; @@ -5763,7 +5763,7 @@ _PyLong_DivmodNear(PyObject *a, PyObject *b) cmp = long_compare((PyLongObject *)twice_rem, (PyLongObject *)b); Py_DECREF(twice_rem); - quo_is_odd = Py_SIZE(quo) != 0 && ((quo->ob_digit[0] & 1) != 0); + quo_is_odd = Py_SIZE(quo) != 0 && ((quo->long_value.ob_digit[0] & 1) != 0); if ((Py_SIZE(b) < 0 ? cmp < 0 : cmp > 0) || (cmp == 0 && quo_is_odd)) { /* fix up quotient */ if (quo_is_neg) @@ -5884,7 +5884,7 @@ int___sizeof___impl(PyObject *self) { Py_ssize_t res; - res = offsetof(PyLongObject, ob_digit) + res = offsetof(PyLongObject, long_value.ob_digit) /* using Py_MAX(..., 1) because we always allocate space for at least one digit, even though the integer zero has a Py_SIZE of 0 */ + Py_MAX(Py_ABS(Py_SIZE(self)), 1)*sizeof(digit); @@ -5918,7 +5918,7 @@ int_bit_length_impl(PyObject *self) if (ndigits == 0) return PyLong_FromLong(0); - msd = ((PyLongObject *)self)->ob_digit[ndigits-1]; + msd = ((PyLongObject *)self)->long_value.ob_digit[ndigits-1]; msd_bits = bit_length_digit(msd); if (ndigits <= PY_SSIZE_T_MAX/PyLong_SHIFT) @@ -5991,7 +5991,7 @@ int_bit_count_impl(PyObject *self) Py_ssize_t. */ Py_ssize_t ndigits_fast = Py_MIN(ndigits, PY_SSIZE_T_MAX/PyLong_SHIFT); for (Py_ssize_t i = 0; i < ndigits_fast; i++) { - bit_count += popcount_digit(z->ob_digit[i]); + bit_count += popcount_digit(z->long_value.ob_digit[i]); } PyObject *result = PyLong_FromSsize_t(bit_count); @@ -6001,7 +6001,7 @@ int_bit_count_impl(PyObject *self) /* Use Python integers if bit_count would overflow. */ for (Py_ssize_t i = ndigits_fast; i < ndigits; i++) { - PyObject *x = PyLong_FromLong(popcount_digit(z->ob_digit[i])); + PyObject *x = PyLong_FromLong(popcount_digit(z->long_value.ob_digit[i])); if (x == NULL) { goto error; } @@ -6287,7 +6287,7 @@ static PyNumberMethods long_as_number = { PyTypeObject PyLong_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "int", /* tp_name */ - offsetof(PyLongObject, ob_digit), /* tp_basicsize */ + offsetof(PyLongObject, long_value.ob_digit), /* tp_basicsize */ sizeof(digit), /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index f97dd67269a270..53439ab16040c4 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2507,10 +2507,10 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) overflow = 0; /* Single digits are common, fast, and cannot overflow on unpacking. */ switch (Py_SIZE(item)) { - case -1: b = -(sdigit) ((PyLongObject*)item)->ob_digit[0]; break; + case -1: b = -(sdigit) ((PyLongObject*)item)->long_value.ob_digit[0]; break; // Note: the continue goes to the top of the "while" loop that iterates over the elements case 0: Py_DECREF(item); continue; - case 1: b = ((PyLongObject*)item)->ob_digit[0]; break; + case 1: b = ((PyLongObject*)item)->long_value.ob_digit[0]; break; default: b = PyLong_AsLongAndOverflow(item, &overflow); break; } if (overflow == 0 && diff --git a/Python/bytecodes.c b/Python/bytecodes.c index fb00b887732ed0..d1e59f7908b580 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -357,8 +357,8 @@ dummy_func( // Deopt unless 0 <= sub < PyList_Size(list) DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR); - assert(((PyLongObject *)_PyLong_GetZero())->ob_digit[0] == 0); - Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0]; + assert(((PyLongObject *)_PyLong_GetZero())->long_value.ob_digit[0] == 0); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR); STAT_INC(BINARY_SUBSCR, hit); res = PyList_GET_ITEM(list, index); @@ -375,8 +375,8 @@ dummy_func( // Deopt unless 0 <= sub < PyTuple_Size(list) DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR); - assert(((PyLongObject *)_PyLong_GetZero())->ob_digit[0] == 0); - Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0]; + assert(((PyLongObject *)_PyLong_GetZero())->long_value.ob_digit[0] == 0); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; DEOPT_IF(index >= PyTuple_GET_SIZE(tuple), BINARY_SUBSCR); STAT_INC(BINARY_SUBSCR, hit); res = PyTuple_GET_ITEM(tuple, index); @@ -469,7 +469,7 @@ dummy_func( // Ensure nonnegative, zero-or-one-digit ints. DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), STORE_SUBSCR); - Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0]; + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; // Ensure index < len(list) DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR); STAT_INC(STORE_SUBSCR, hit); @@ -1834,8 +1834,8 @@ dummy_func( DEOPT_IF((size_t)(Py_SIZE(right) + 1) > 2, COMPARE_AND_BRANCH); STAT_INC(COMPARE_AND_BRANCH, hit); assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1); - Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->ob_digit[0]; - Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->ob_digit[0]; + Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->long_value.ob_digit[0]; + Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->long_value.ob_digit[0]; // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg int sign_ish = COMPARISON_BIT(ileft, iright); _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index b5decf804ca652..3ee30ae8df9e3c 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -484,8 +484,8 @@ // Deopt unless 0 <= sub < PyList_Size(list) DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR); - assert(((PyLongObject *)_PyLong_GetZero())->ob_digit[0] == 0); - Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0]; + assert(((PyLongObject *)_PyLong_GetZero())->long_value.ob_digit[0] == 0); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR); STAT_INC(BINARY_SUBSCR, hit); res = PyList_GET_ITEM(list, index); @@ -509,8 +509,8 @@ // Deopt unless 0 <= sub < PyTuple_Size(list) DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR); - assert(((PyLongObject *)_PyLong_GetZero())->ob_digit[0] == 0); - Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0]; + assert(((PyLongObject *)_PyLong_GetZero())->long_value.ob_digit[0] == 0); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; DEOPT_IF(index >= PyTuple_GET_SIZE(tuple), BINARY_SUBSCR); STAT_INC(BINARY_SUBSCR, hit); res = PyTuple_GET_ITEM(tuple, index); @@ -634,7 +634,7 @@ // Ensure nonnegative, zero-or-one-digit ints. DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), STORE_SUBSCR); - Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0]; + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; // Ensure index < len(list) DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR); STAT_INC(STORE_SUBSCR, hit); @@ -2179,8 +2179,8 @@ DEOPT_IF((size_t)(Py_SIZE(right) + 1) > 2, COMPARE_AND_BRANCH); STAT_INC(COMPARE_AND_BRANCH, hit); assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1); - Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->ob_digit[0]; - Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->ob_digit[0]; + Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->long_value.ob_digit[0]; + Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->long_value.ob_digit[0]; // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg int sign_ish = COMPARISON_BIT(ileft, iright); _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); diff --git a/Python/marshal.c b/Python/marshal.c index 5f392d9e1ecfff..94e79d4392ae6d 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -240,7 +240,7 @@ w_PyLong(const PyLongObject *ob, char flag, WFILE *p) /* set l to number of base PyLong_MARSHAL_BASE digits */ n = Py_ABS(Py_SIZE(ob)); l = (n-1) * PyLong_MARSHAL_RATIO; - d = ob->ob_digit[n-1]; + d = ob->long_value.ob_digit[n-1]; assert(d != 0); /* a PyLong is always normalized */ do { d >>= PyLong_MARSHAL_SHIFT; @@ -254,14 +254,14 @@ w_PyLong(const PyLongObject *ob, char flag, WFILE *p) w_long((long)(Py_SIZE(ob) > 0 ? l : -l), p); for (i=0; i < n-1; i++) { - d = ob->ob_digit[i]; + d = ob->long_value.ob_digit[i]; for (j=0; j < PyLong_MARSHAL_RATIO; j++) { w_short(d & PyLong_MARSHAL_MASK, p); d >>= PyLong_MARSHAL_SHIFT; } assert (d == 0); } - d = ob->ob_digit[n-1]; + d = ob->long_value.ob_digit[n-1]; do { w_short(d & PyLong_MARSHAL_MASK, p); d >>= PyLong_MARSHAL_SHIFT; @@ -853,7 +853,7 @@ r_PyLong(RFILE *p) goto bad_digit; d += (digit)md << j*PyLong_MARSHAL_SHIFT; } - ob->ob_digit[i] = d; + ob->long_value.ob_digit[i] = d; } d = 0; @@ -880,7 +880,7 @@ r_PyLong(RFILE *p) } /* top digit should be nonzero, else the resulting PyLong won't be normalized */ - ob->ob_digit[size-1] = d; + ob->long_value.ob_digit[size-1] = d; return (PyObject *)ob; bad_digit: Py_DECREF(ob); diff --git a/Python/specialize.c b/Python/specialize.c index 84784b2d149e82..096687f5fdf023 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1411,7 +1411,7 @@ _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *ins if (container_type == &PyList_Type) { if (PyLong_CheckExact(sub)) { if ((Py_SIZE(sub) == 0 || Py_SIZE(sub) == 1) - && ((PyLongObject *)sub)->ob_digit[0] < (size_t)PyList_GET_SIZE(container)) + && ((PyLongObject *)sub)->long_value.ob_digit[0] < (size_t)PyList_GET_SIZE(container)) { _py_set_opcode(instr, STORE_SUBSCR_LIST_INT); goto success; diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 6453dff95df4a3..56d6970b29249c 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -901,7 +901,7 @@ def proxyval(self, visited): if ob_size == 0: return 0 - ob_digit = self.field('ob_digit') + ob_digit = self.field('long_value')['ob_digit'] if gdb.lookup_type('digit').sizeof == 2: SHIFT = 15 From ea232716d3de1675478db3a302629ba43194c967 Mon Sep 17 00:00:00 2001 From: Owain Davies <116417456+OTheDev@users.noreply.github.com> Date: Tue, 31 Jan 2023 00:56:33 +0700 Subject: [PATCH 033/225] gh-101422: (docs) TarFile default errorlevel argument is 1, not 0 (GH-101424) --- Doc/library/tarfile.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index 6e8baba04fb92d..741d40da152101 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -279,7 +279,7 @@ be finalized; only the internally used file object will be closed. See the .. versionadded:: 3.2 Added support for the context management protocol. -.. class:: TarFile(name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors='surrogateescape', pax_headers=None, debug=0, errorlevel=0) +.. class:: TarFile(name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors='surrogateescape', pax_headers=None, debug=0, errorlevel=1) All following arguments are optional and can be accessed as instance attributes as well. From e11fc032a75d067d2167a21037722a770b9dfb51 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 30 Jan 2023 12:07:48 -0700 Subject: [PATCH 034/225] gh-59956: Clarify Runtime State Status Expectations (gh-101308) A PyThreadState can be in one of many states in its lifecycle, represented by some status value. Those statuses haven't been particularly clear, so we're addressing that here. Specifically: * made the distinct lifecycle statuses clear on PyThreadState * identified expectations of how various lifecycle-related functions relate to status * noted the various places where those expectations don't match the actual behavior At some point we'll need to address the mismatches. (This change also includes some cleanup.) https://github.com/python/cpython/issues/59956 --- Include/cpython/pystate.h | 27 +- Include/internal/pycore_pystate.h | 17 +- Modules/_threadmodule.c | 2 +- Python/ceval_gil.c | 2 +- Python/pylifecycle.c | 43 ++- Python/pystate.c | 491 +++++++++++++++++++++--------- 6 files changed, 416 insertions(+), 166 deletions(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 3c1e70de3e4839..f5db52f76e8f4f 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -119,7 +119,30 @@ struct _ts { PyThreadState *next; PyInterpreterState *interp; - int _status; + struct { + /* Has been initialized to a safe state. + + In order to be effective, this must be set to 0 during or right + after allocation. */ + unsigned int initialized:1; + + /* Has been bound to an OS thread. */ + unsigned int bound:1; + /* Has been unbound from its OS thread. */ + unsigned int unbound:1; + /* Has been bound aa current for the GILState API. */ + unsigned int bound_gilstate:1; + /* Currently in use (maybe holds the GIL). */ + unsigned int active:1; + + /* various stages of finalization */ + unsigned int finalizing:1; + unsigned int cleared:1; + unsigned int finalized:1; + + /* padding to align to 4 bytes */ + unsigned int :24; + } _status; int py_recursion_remaining; int py_recursion_limit; @@ -245,6 +268,8 @@ struct _ts { // Alias for backward compatibility with Python 3.8 #define _PyInterpreterState_Get PyInterpreterState_Get +/* An alias for the internal _PyThreadState_New(), + kept for stable ABI compatibility. */ PyAPI_FUNC(PyThreadState *) _PyThreadState_Prealloc(PyInterpreterState *); /* Similar to PyThreadState_Get(), but don't issue a fatal error diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 0e46693c1f1283..7046ec8d9adaaf 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -120,13 +120,12 @@ static inline PyInterpreterState* _PyInterpreterState_GET(void) { // PyThreadState functions +PyAPI_FUNC(PyThreadState *) _PyThreadState_New(PyInterpreterState *interp); PyAPI_FUNC(void) _PyThreadState_Bind(PyThreadState *tstate); // We keep this around exclusively for stable ABI compatibility. PyAPI_FUNC(void) _PyThreadState_Init( PyThreadState *tstate); -PyAPI_FUNC(void) _PyThreadState_DeleteExcept( - _PyRuntimeState *runtime, - PyThreadState *tstate); +PyAPI_FUNC(void) _PyThreadState_DeleteExcept(PyThreadState *tstate); static inline void @@ -139,18 +138,6 @@ _PyThreadState_UpdateTracingState(PyThreadState *tstate) } -/* PyThreadState status */ - -#define PyThreadState_UNINITIALIZED 0 -/* Has been initialized to a safe state. - - In order to be effective, this must be set to 0 during or right - after allocation. */ -#define PyThreadState_INITIALIZED 1 -#define PyThreadState_BOUND 2 -#define PyThreadState_UNBOUND 3 - - /* Other */ PyAPI_FUNC(PyThreadState *) _PyThreadState_Swap( diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index bf4b6ec00e3e30..9c12c696757439 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1161,7 +1161,7 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) return PyErr_NoMemory(); } boot->interp = _PyInterpreterState_GET(); - boot->tstate = _PyThreadState_Prealloc(boot->interp); + boot->tstate = _PyThreadState_New(boot->interp); if (boot->tstate == NULL) { PyMem_Free(boot); if (!PyErr_Occurred()) { diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c index 73d412ba4d71c9..1bf223348d28fa 100644 --- a/Python/ceval_gil.c +++ b/Python/ceval_gil.c @@ -624,7 +624,7 @@ _PyEval_ReInitThreads(PyThreadState *tstate) } /* Destroy all threads except the current one */ - _PyThreadState_DeleteExcept(runtime, tstate); + _PyThreadState_DeleteExcept(tstate); return _PyStatus_OK(); } #endif diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 5ef2d3f6aa72b0..a8a8e7f3d84f21 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -696,10 +696,11 @@ pycore_create_interpreter(_PyRuntimeState *runtime, const _PyInterpreterConfig config = _PyInterpreterConfig_LEGACY_INIT; init_interp_settings(interp, &config); - PyThreadState *tstate = PyThreadState_New(interp); + PyThreadState *tstate = _PyThreadState_New(interp); if (tstate == NULL) { return _PyStatus_ERR("can't make first thread"); } + _PyThreadState_Bind(tstate); (void) PyThreadState_Swap(tstate); status = init_interp_create_gil(tstate); @@ -1821,6 +1822,11 @@ Py_FinalizeEx(void) /* Get current thread state and interpreter pointer */ PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime); + // XXX assert(_Py_IsMainInterpreter(tstate->interp)); + // XXX assert(_Py_IsMainThread()); + + // Block some operations. + tstate->interp->finalizing = 1; // Wrap up existing "threading"-module-created, non-daemon threads. wait_for_thread_shutdown(tstate); @@ -1867,7 +1873,23 @@ Py_FinalizeEx(void) _PyRuntimeState_SetFinalizing() has been called, no other Python thread can take the GIL at this point: if they try, they will exit immediately. */ - _PyThreadState_DeleteExcept(runtime, tstate); + _PyThreadState_DeleteExcept(tstate); + + /* At this point no Python code should be running at all. + The only thread state left should be the main thread of the main + interpreter (AKA tstate), in which this code is running right now. + There may be other OS threads running but none of them will have + thread states associated with them, nor will be able to create + new thread states. + + Thus tstate is the only possible thread state from here on out. + It may still be used during finalization to run Python code as + needed or provide runtime state (e.g. sys.modules) but that will + happen sparingly. Furthermore, the order of finalization aims + to not need a thread (or interpreter) state as soon as possible. + */ + // XXX Make sure we are preventing the creating of any new thread states + // (or interpreters). /* Flush sys.stdout and sys.stderr */ if (flush_std_files() < 0) { @@ -1958,6 +1980,20 @@ Py_FinalizeEx(void) } #endif /* Py_TRACE_REFS */ + /* At this point there's almost no other Python code that will run, + nor interpreter state needed. The only possibility is the + finalizers of the objects stored on tstate (and tstate->interp), + which are triggered via finalize_interp_clear(). + + For now we operate as though none of those finalizers actually + need an operational thread state or interpreter. In reality, + those finalizers may rely on some part of tstate or + tstate->interp, and/or may raise exceptions + or otherwise fail. + */ + // XXX Do this sooner during finalization. + // XXX Ensure finalizer errors are handled properly. + finalize_interp_clear(tstate); finalize_interp_delete(tstate->interp); @@ -2039,12 +2075,13 @@ new_interpreter(PyThreadState **tstate_p, const _PyInterpreterConfig *config) return _PyStatus_OK(); } - PyThreadState *tstate = PyThreadState_New(interp); + PyThreadState *tstate = _PyThreadState_New(interp); if (tstate == NULL) { PyInterpreterState_Delete(interp); *tstate_p = NULL; return _PyStatus_OK(); } + _PyThreadState_Bind(tstate); PyThreadState *save_tstate = PyThreadState_Swap(tstate); diff --git a/Python/pystate.c b/Python/pystate.c index bf7688fd32134b..8bb49d954a81b6 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -37,9 +37,6 @@ to avoid the expense of doing their own locking). extern "C" { #endif -/* Forward declarations */ -static void _PyThreadState_Delete(PyThreadState *tstate, int check_current); - /****************************************/ /* helpers for the current thread state */ @@ -81,69 +78,56 @@ current_fast_clear(_PyRuntimeState *runtime) _Py_atomic_store_relaxed(&runtime->tstate_current, (uintptr_t)NULL); } +#define tstate_verify_not_active(tstate) \ + if (tstate == current_fast_get((tstate)->interp->runtime)) { \ + _Py_FatalErrorFormat(__func__, "tstate %p is still current", tstate); \ + } + //------------------------------------------------ // the thread state bound to the current OS thread //------------------------------------------------ -/* - The stored thread state is set by bind_tstate() (AKA PyThreadState_Bind(). - - The GIL does no need to be held for these. - */ - -static int -current_tss_initialized(_PyRuntimeState *runtime) +static inline int +tstate_tss_initialized(Py_tss_t *key) { - return PyThread_tss_is_created(&runtime->autoTSSkey); + return PyThread_tss_is_created(key); } -static PyStatus -current_tss_init(_PyRuntimeState *runtime) +static inline int +tstate_tss_init(Py_tss_t *key) { - assert(!current_tss_initialized(runtime)); - if (PyThread_tss_create(&runtime->autoTSSkey) != 0) { - return _PyStatus_NO_MEMORY(); - } - return _PyStatus_OK(); + assert(!tstate_tss_initialized(key)); + return PyThread_tss_create(key); } -static void -current_tss_fini(_PyRuntimeState *runtime) +static inline void +tstate_tss_fini(Py_tss_t *key) { - assert(current_tss_initialized(runtime)); - PyThread_tss_delete(&runtime->autoTSSkey); + assert(tstate_tss_initialized(key)); + PyThread_tss_delete(key); } static inline PyThreadState * -current_tss_get(_PyRuntimeState *runtime) +tstate_tss_get(Py_tss_t *key) { - assert(current_tss_initialized(runtime)); - return (PyThreadState *)PyThread_tss_get(&runtime->autoTSSkey); + assert(tstate_tss_initialized(key)); + return (PyThreadState *)PyThread_tss_get(key); } static inline int -_current_tss_set(_PyRuntimeState *runtime, PyThreadState *tstate) +tstate_tss_set(Py_tss_t *key, PyThreadState *tstate) { assert(tstate != NULL); - assert(current_tss_initialized(runtime)); - return PyThread_tss_set(&runtime->autoTSSkey, (void *)tstate); -} -static inline void -current_tss_set(_PyRuntimeState *runtime, PyThreadState *tstate) -{ - if (_current_tss_set(runtime, tstate) != 0) { - Py_FatalError("failed to set current tstate (TSS)"); - } + assert(tstate_tss_initialized(key)); + return PyThread_tss_set(key, (void *)tstate); } -static inline void -current_tss_clear(_PyRuntimeState *runtime) +static inline int +tstate_tss_clear(Py_tss_t *key) { - assert(current_tss_initialized(runtime)); - if (PyThread_tss_set(&runtime->autoTSSkey, NULL) != 0) { - Py_FatalError("failed to clear current tstate (TSS)"); - } + assert(tstate_tss_initialized(key)); + return PyThread_tss_set(key, (void *)NULL); } #ifdef HAVE_FORK @@ -152,92 +136,178 @@ current_tss_clear(_PyRuntimeState *runtime) * don't reset TSS upon fork(), see issue #10517. */ static PyStatus -current_tss_reinit(_PyRuntimeState *runtime) +tstate_tss_reinit(Py_tss_t *key) { - if (!current_tss_initialized(runtime)) { + if (!tstate_tss_initialized(key)) { return _PyStatus_OK(); } - PyThreadState *tstate = current_tss_get(runtime); + PyThreadState *tstate = tstate_tss_get(key); - current_tss_fini(runtime); - PyStatus status = current_tss_init(runtime); - if (_PyStatus_EXCEPTION(status)) { - return status; + tstate_tss_fini(key); + if (tstate_tss_init(key) != 0) { + return _PyStatus_NO_MEMORY(); } /* If the thread had an associated auto thread state, reassociate it with * the new key. */ - if (tstate && _current_tss_set(runtime, tstate) != 0) { - return _PyStatus_ERR("failed to set autoTSSkey"); + if (tstate && tstate_tss_set(key, tstate) != 0) { + return _PyStatus_ERR("failed to re-set autoTSSkey"); } return _PyStatus_OK(); } #endif +/* + The stored thread state is set by bind_tstate() (AKA PyThreadState_Bind(). + + The GIL does no need to be held for these. + */ + +#define gilstate_tss_initialized(runtime) \ + tstate_tss_initialized(&(runtime)->autoTSSkey) +#define gilstate_tss_init(runtime) \ + tstate_tss_init(&(runtime)->autoTSSkey) +#define gilstate_tss_fini(runtime) \ + tstate_tss_fini(&(runtime)->autoTSSkey) +#define gilstate_tss_get(runtime) \ + tstate_tss_get(&(runtime)->autoTSSkey) +#define _gilstate_tss_set(runtime, tstate) \ + tstate_tss_set(&(runtime)->autoTSSkey, tstate) +#define _gilstate_tss_clear(runtime) \ + tstate_tss_clear(&(runtime)->autoTSSkey) +#define gilstate_tss_reinit(runtime) \ + tstate_tss_reinit(&(runtime)->autoTSSkey) + +static inline void +gilstate_tss_set(_PyRuntimeState *runtime, PyThreadState *tstate) +{ + assert(tstate != NULL && tstate->interp->runtime == runtime); + if (_gilstate_tss_set(runtime, tstate) != 0) { + Py_FatalError("failed to set current tstate (TSS)"); + } +} + +static inline void +gilstate_tss_clear(_PyRuntimeState *runtime) +{ + if (_gilstate_tss_clear(runtime) != 0) { + Py_FatalError("failed to clear current tstate (TSS)"); + } +} + + +static inline int tstate_is_alive(PyThreadState *tstate); + +static inline int +tstate_is_bound(PyThreadState *tstate) +{ + return tstate->_status.bound && !tstate->_status.unbound; +} + +static void bind_gilstate_tstate(PyThreadState *); +static void unbind_gilstate_tstate(PyThreadState *); + static void bind_tstate(PyThreadState *tstate) { assert(tstate != NULL); - assert(tstate->_status == PyThreadState_INITIALIZED); + assert(tstate_is_alive(tstate) && !tstate->_status.bound); + assert(!tstate->_status.unbound); // just in case + assert(!tstate->_status.bound_gilstate); + assert(tstate != gilstate_tss_get(tstate->interp->runtime)); + assert(!tstate->_status.active); assert(tstate->thread_id == 0); assert(tstate->native_thread_id == 0); - _PyRuntimeState *runtime = tstate->interp->runtime; - - /* Stick the thread state for this thread in thread specific storage. - The only situation where you can legitimately have more than one - thread state for an OS level thread is when there are multiple - interpreters. - - You shouldn't really be using the PyGILState_ APIs anyway (see issues - #10915 and #15751). - - The first thread state created for that given OS level thread will - "win", which seems reasonable behaviour. - */ - /* When a thread state is created for a thread by some mechanism - other than PyGILState_Ensure(), it's important that the GILState - machinery knows about it so it doesn't try to create another - thread state for the thread. - (This is a better fix for SF bug #1010677 than the first one attempted.) - */ - // XXX Skipping like this does not play nice with multiple interpreters. - if (current_tss_get(runtime) == NULL) { - current_tss_set(runtime, tstate); - } + // Currently we don't necessarily store the thread state + // in thread-local storage (e.g. per-interpreter). tstate->thread_id = PyThread_get_thread_ident(); #ifdef PY_HAVE_THREAD_NATIVE_ID tstate->native_thread_id = PyThread_get_thread_native_id(); #endif - tstate->_status = PyThreadState_BOUND; + tstate->_status.bound = 1; } static void unbind_tstate(PyThreadState *tstate) { assert(tstate != NULL); - assert(tstate->_status == PyThreadState_BOUND); + // XXX assert(tstate_is_alive(tstate)); + assert(tstate_is_bound(tstate)); + // XXX assert(!tstate->_status.active); assert(tstate->thread_id > 0); #ifdef PY_HAVE_THREAD_NATIVE_ID assert(tstate->native_thread_id > 0); #endif - _PyRuntimeState *runtime = tstate->interp->runtime; - - if (current_tss_initialized(runtime) && - tstate == current_tss_get(runtime)) - { - current_tss_clear(runtime); - } // We leave thread_id and native_thread_id alone // since they can be useful for debugging. // Check the `_status` field to know if these values // are still valid. - tstate->_status = PyThreadState_UNBOUND; + // We leave tstate->_status.bound set to 1 + // to indicate it was previously bound. + tstate->_status.unbound = 1; +} + + +/* Stick the thread state for this thread in thread specific storage. + + When a thread state is created for a thread by some mechanism + other than PyGILState_Ensure(), it's important that the GILState + machinery knows about it so it doesn't try to create another + thread state for the thread. + (This is a better fix for SF bug #1010677 than the first one attempted.) + + The only situation where you can legitimately have more than one + thread state for an OS level thread is when there are multiple + interpreters. + + The PyGILState_*() APIs don't work with multiple + interpreters (see bpo-10915 and bpo-15751), so this function + sets TSS only once. Thus, the first thread state created for that + given OS level thread will "win", which seems reasonable behaviour. +*/ + +static void +bind_gilstate_tstate(PyThreadState *tstate) +{ + assert(tstate != NULL); + assert(tstate_is_alive(tstate)); + assert(tstate_is_bound(tstate)); + // XXX assert(!tstate->_status.active); + assert(!tstate->_status.bound_gilstate); + + _PyRuntimeState *runtime = tstate->interp->runtime; + PyThreadState *tcur = gilstate_tss_get(runtime); + assert(tstate != tcur); + + if (tcur != NULL) { + // The original gilstate implementation only respects the + // first thread state set. + // XXX Skipping like this does not play nice with multiple interpreters. + return; + tcur->_status.bound_gilstate = 0; + } + gilstate_tss_set(runtime, tstate); + tstate->_status.bound_gilstate = 1; +} + +static void +unbind_gilstate_tstate(PyThreadState *tstate) +{ + assert(tstate != NULL); + // XXX assert(tstate_is_alive(tstate)); + assert(tstate_is_bound(tstate)); + // XXX assert(!tstate->_status.active); + assert(tstate->_status.bound_gilstate); + assert(tstate == gilstate_tss_get(tstate->interp->runtime)); + + gilstate_tss_clear(tstate->interp->runtime); + tstate->_status.bound_gilstate = 0; } @@ -261,7 +331,7 @@ holds_gil(PyThreadState *tstate) assert(tstate != NULL); _PyRuntimeState *runtime = tstate->interp->runtime; /* Must be the tstate for this thread */ - assert(tstate == current_tss_get(runtime)); + assert(tstate == gilstate_tss_get(runtime)); return tstate == current_fast_get(runtime); } @@ -394,10 +464,9 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime) memcpy(runtime, &initial, sizeof(*runtime)); } - PyStatus status = current_tss_init(runtime); - if (_PyStatus_EXCEPTION(status)) { + if (gilstate_tss_init(runtime) != 0) { _PyRuntimeState_Fini(runtime); - return status; + return _PyStatus_NO_MEMORY(); } if (PyThread_tss_create(&runtime->trashTSSkey) != 0) { @@ -414,8 +483,8 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime) void _PyRuntimeState_Fini(_PyRuntimeState *runtime) { - if (current_tss_initialized(runtime)) { - current_tss_fini(runtime); + if (gilstate_tss_initialized(runtime)) { + gilstate_tss_fini(runtime); } if (PyThread_tss_is_created(&runtime->trashTSSkey)) { @@ -475,7 +544,7 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime) } - PyStatus status = current_tss_reinit(runtime); + PyStatus status = gilstate_tss_reinit(runtime); if (_PyStatus_EXCEPTION(status)) { return status; } @@ -669,18 +738,38 @@ PyInterpreterState_New(void) static void interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) { + assert(interp != NULL); + assert(tstate != NULL); _PyRuntimeState *runtime = interp->runtime; + /* XXX Conditions we need to enforce: + + * the GIL must be held by the current thread + * tstate must be the "current" thread state (current_fast_get()) + * tstate->interp must be interp + * for the main interpreter, tstate must be the main thread + */ + // XXX Ideally, we would not rely on any thread state in this function + // (and we would drop the "tstate" argument). + if (_PySys_Audit(tstate, "cpython.PyInterpreterState_Clear", NULL) < 0) { _PyErr_Clear(tstate); } HEAD_LOCK(runtime); + // XXX Clear the current/main thread state last. for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) { PyThreadState_Clear(p); } 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 + or even the interpreter state. For now we trust that isn't + a problem. + */ + // XXX Make sure we properly deal with problematic finalizers. + Py_CLEAR(interp->audit_hooks); PyConfig_Clear(&interp->config); @@ -751,7 +840,6 @@ PyInterpreterState_Clear(PyInterpreterState *interp) // garbage. It can be different than the current Python thread state // of 'interp'. PyThreadState *current_tstate = current_fast_get(interp->runtime); - interpreter_clear(interp, current_tstate); } @@ -763,29 +851,25 @@ _PyInterpreterState_Clear(PyThreadState *tstate) } -static void -zapthreads(PyInterpreterState *interp, int check_current) -{ - PyThreadState *tstate; - /* No need to lock the mutex here because this should only happen - when the threads are all really dead (XXX famous last words). */ - while ((tstate = interp->threads.head) != NULL) { - _PyThreadState_Delete(tstate, check_current); - } -} - +static void zapthreads(PyInterpreterState *interp); void PyInterpreterState_Delete(PyInterpreterState *interp) { _PyRuntimeState *runtime = interp->runtime; struct pyinterpreters *interpreters = &runtime->interpreters; - zapthreads(interp, 0); - _PyEval_FiniState(&interp->ceval); + // XXX Clearing the "current" thread state should happen before + // we start finalizing the interpreter (or the current thread state). + PyThreadState *tcur = current_fast_get(runtime); + if (tcur != NULL && interp == tcur->interp) { + /* Unset current thread. After this, many C API calls become crashy. */ + _PyThreadState_Swap(runtime, NULL); + } + + zapthreads(interp); - /* Delete current thread. After this, many C API calls become crashy. */ - _PyThreadState_Swap(runtime, NULL); + _PyEval_FiniState(&interp->ceval); HEAD_LOCK(runtime); PyInterpreterState **p; @@ -843,8 +927,10 @@ _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime) continue; } + // XXX Won't this fail since PyInterpreterState_Clear() requires + // the "current" tstate to be set? PyInterpreterState_Clear(interp); // XXX must activate? - zapthreads(interp, 1); + zapthreads(interp); if (interp->id_mutex != NULL) { PyThread_free_lock(interp->id_mutex); } @@ -1062,6 +1148,16 @@ _PyInterpreterState_LookUpID(int64_t requested_id) /* the per-thread runtime state */ /********************************/ +static inline int +tstate_is_alive(PyThreadState *tstate) +{ + return (tstate->_status.initialized && + !tstate->_status.finalized && + !tstate->_status.cleared && + !tstate->_status.finalizing); +} + + //---------- // lifecycle //---------- @@ -1112,7 +1208,7 @@ init_threadstate(PyThreadState *tstate, PyInterpreterState *interp, uint64_t id, PyThreadState *next) { - if (tstate->_status != PyThreadState_UNINITIALIZED) { + if (tstate->_status.initialized) { Py_FatalError("thread state already initialized"); } @@ -1148,7 +1244,7 @@ init_threadstate(PyThreadState *tstate, tstate->datastack_top = NULL; tstate->datastack_limit = NULL; - tstate->_status = PyThreadState_INITIALIZED; + tstate->_status.initialized = 1; } static PyThreadState * @@ -1208,17 +1304,29 @@ PyThreadState_New(PyInterpreterState *interp) PyThreadState *tstate = new_threadstate(interp); if (tstate) { bind_tstate(tstate); + // This makes sure there's a gilstate tstate bound + // as soon as possible. + if (gilstate_tss_get(tstate->interp->runtime) == NULL) { + bind_gilstate_tstate(tstate); + } } return tstate; } // This must be followed by a call to _PyThreadState_Bind(); PyThreadState * -_PyThreadState_Prealloc(PyInterpreterState *interp) +_PyThreadState_New(PyInterpreterState *interp) { return new_threadstate(interp); } +// We keep this for stable ABI compabibility. +PyThreadState * +_PyThreadState_Prealloc(PyInterpreterState *interp) +{ + return _PyThreadState_New(interp); +} + // We keep this around for (accidental) stable ABI compatibility. // Realistically, no extensions are using it. void @@ -1230,6 +1338,17 @@ _PyThreadState_Init(PyThreadState *tstate) void PyThreadState_Clear(PyThreadState *tstate) { + assert(tstate->_status.initialized && !tstate->_status.cleared); + // XXX assert(!tstate->_status.bound || tstate->_status.unbound); + tstate->_status.finalizing = 1; // just in case + + /* XXX Conditions we need to enforce: + + * the GIL must be held by the current thread + * current_fast_get()->interp must match tstate->interp + * for the main interpreter, current_fast_get() must be the main thread + */ + int verbose = _PyInterpreterState_GetConfig(tstate->interp)->verbose; if (verbose && tstate->cframe->current_frame != NULL) { @@ -1244,6 +1363,17 @@ PyThreadState_Clear(PyThreadState *tstate) "PyThreadState_Clear: warning: thread still has a frame\n"); } + /* At this point tstate shouldn't be used any more, + neither to run Python code nor for other uses. + + This is tricky when current_fast_get() == tstate, in the same way + as noted in interpreter_clear() above. The below finalizers + can possibly run Python code or otherwise use the partially + cleared thread state. For now we trust that isn't a problem + in practice. + */ + // XXX Deal with the possibility of problematic finalizers. + /* Don't clear tstate->pyframe: it is a borrowed reference */ Py_CLEAR(tstate->dict); @@ -1274,6 +1404,11 @@ PyThreadState_Clear(PyThreadState *tstate) if (tstate->on_delete != NULL) { tstate->on_delete(tstate->on_delete_data); } + + tstate->_status.cleared = 1; + + // XXX Call _PyThreadStateSwap(runtime, NULL) here if "current". + // XXX Do it as early in the function as possible. } @@ -1281,7 +1416,8 @@ PyThreadState_Clear(PyThreadState *tstate) static void tstate_delete_common(PyThreadState *tstate) { - _Py_EnsureTstateNotNULL(tstate); + assert(tstate->_status.cleared && !tstate->_status.finalized); + PyInterpreterState *interp = tstate->interp; if (interp == NULL) { Py_FatalError("NULL interpreter"); @@ -1300,7 +1436,11 @@ tstate_delete_common(PyThreadState *tstate) } HEAD_UNLOCK(runtime); - // XXX Do this in PyThreadState_Swap() (and assert not-equal here)? + // XXX Unbind in PyThreadState_Clear(), or earlier + // (and assert not-equal here)? + if (tstate->_status.bound_gilstate) { + unbind_gilstate_tstate(tstate); + } unbind_tstate(tstate); // XXX Move to PyThreadState_Clear()? @@ -1311,25 +1451,32 @@ tstate_delete_common(PyThreadState *tstate) _PyObject_VirtualFree(chunk, chunk->size); chunk = prev; } + + tstate->_status.finalized = 1; } + static void -_PyThreadState_Delete(PyThreadState *tstate, int check_current) +zapthreads(PyInterpreterState *interp) { - if (check_current) { - if (tstate == current_fast_get(tstate->interp->runtime)) { - _Py_FatalErrorFormat(__func__, "tstate %p is still current", tstate); - } + PyThreadState *tstate; + /* No need to lock the mutex here because this should only happen + when the threads are all really dead (XXX famous last words). */ + while ((tstate = interp->threads.head) != NULL) { + tstate_verify_not_active(tstate); + tstate_delete_common(tstate); + free_threadstate(tstate); } - tstate_delete_common(tstate); - free_threadstate(tstate); } void PyThreadState_Delete(PyThreadState *tstate) { - _PyThreadState_Delete(tstate, 1); + _Py_EnsureTstateNotNULL(tstate); + tstate_verify_not_active(tstate); + tstate_delete_common(tstate); + free_threadstate(tstate); } @@ -1359,9 +1506,11 @@ PyThreadState_DeleteCurrent(void) * be kept in those other interpreters. */ void -_PyThreadState_DeleteExcept(_PyRuntimeState *runtime, PyThreadState *tstate) +_PyThreadState_DeleteExcept(PyThreadState *tstate) { + assert(tstate != NULL); PyInterpreterState *interp = tstate->interp; + _PyRuntimeState *runtime = interp->runtime; HEAD_LOCK(runtime); /* Remove all thread states, except tstate, from the linked list of @@ -1460,6 +1609,38 @@ PyThreadState_GetID(PyThreadState *tstate) } +static inline void +tstate_activate(PyThreadState *tstate) +{ + assert(tstate != NULL); + // XXX assert(tstate_is_alive(tstate)); + assert(tstate_is_bound(tstate)); + assert(!tstate->_status.active); + + assert(!tstate->_status.bound_gilstate || + tstate == gilstate_tss_get((tstate->interp->runtime))); + if (!tstate->_status.bound_gilstate) { + bind_gilstate_tstate(tstate); + } + + tstate->_status.active = 1; +} + +static inline void +tstate_deactivate(PyThreadState *tstate) +{ + assert(tstate != NULL); + // XXX assert(tstate_is_alive(tstate)); + assert(tstate_is_bound(tstate)); + assert(tstate->_status.active); + + tstate->_status.active = 0; + + // We do not unbind the gilstate tstate here. + // It will still be used in PyGILState_Ensure(). +} + + //---------- // other API //---------- @@ -1535,31 +1716,43 @@ PyThreadState_Get(void) PyThreadState * _PyThreadState_Swap(_PyRuntimeState *runtime, PyThreadState *newts) { +#if defined(Py_DEBUG) + /* This can be called from PyEval_RestoreThread(). Similar + to it, we need to ensure errno doesn't change. + */ + int err = errno; +#endif PyThreadState *oldts = current_fast_get(runtime); - if (newts == NULL) { - current_fast_clear(runtime); + current_fast_clear(runtime); + + if (oldts != NULL) { + // XXX assert(tstate_is_alive(oldts) && tstate_is_bound(oldts)); + tstate_deactivate(oldts); } - else { + + if (newts != NULL) { + // XXX assert(tstate_is_alive(newts)); + assert(tstate_is_bound(newts)); current_fast_set(runtime, newts); + tstate_activate(newts); } + /* It should not be possible for more than one thread state to be used for a thread. Check this the best we can in debug builds. */ // XXX The above isn't true when multiple interpreters are involved. #if defined(Py_DEBUG) - if (newts && current_tss_initialized(runtime)) { - /* This can be called from PyEval_RestoreThread(). Similar - to it, we need to ensure errno doesn't change. - */ - int err = errno; - PyThreadState *check = current_tss_get(runtime); - if (check && check->interp == newts->interp && check != newts) { - Py_FatalError("Invalid thread state for this thread"); + if (newts && gilstate_tss_initialized(runtime)) { + PyThreadState *check = gilstate_tss_get(runtime); + if (check != newts) { + if (check && check->interp == newts->interp) { + Py_FatalError("Invalid thread state for this thread"); + } } - errno = err; } + errno = err; #endif return oldts; } @@ -1575,6 +1768,11 @@ void _PyThreadState_Bind(PyThreadState *tstate) { bind_tstate(tstate); + // This makes sure there's a gilstate tstate bound + // as soon as possible. + if (gilstate_tss_get(tstate->interp->runtime) == NULL) { + bind_gilstate_tstate(tstate); + } } @@ -1863,7 +2061,7 @@ _PyGILState_Init(PyInterpreterState *interp) return _PyStatus_OK(); } _PyRuntimeState *runtime = interp->runtime; - assert(current_tss_get(runtime) == NULL); + assert(gilstate_tss_get(runtime) == NULL); assert(runtime->gilstate.autoInterpreterState == NULL); runtime->gilstate.autoInterpreterState = interp; return _PyStatus_OK(); @@ -1899,7 +2097,7 @@ _PyGILState_SetTstate(PyThreadState *tstate) _PyRuntimeState *runtime = tstate->interp->runtime; assert(runtime->gilstate.autoInterpreterState == tstate->interp); - assert(current_tss_get(runtime) == tstate); + assert(gilstate_tss_get(runtime) == tstate); assert(tstate->gilstate_counter == 1); #endif @@ -1918,10 +2116,10 @@ PyThreadState * PyGILState_GetThisThreadState(void) { _PyRuntimeState *runtime = &_PyRuntime; - if (!current_tss_initialized(runtime)) { + if (!gilstate_tss_initialized(runtime)) { return NULL; } - return current_tss_get(runtime); + return gilstate_tss_get(runtime); } int @@ -1932,7 +2130,7 @@ PyGILState_Check(void) return 1; } - if (!current_tss_initialized(runtime)) { + if (!gilstate_tss_initialized(runtime)) { return 1; } @@ -1941,7 +2139,7 @@ PyGILState_Check(void) return 0; } - return (tstate == current_tss_get(runtime)); + return (tstate == gilstate_tss_get(runtime)); } PyGILState_STATE @@ -1957,17 +2155,19 @@ PyGILState_Ensure(void) /* Ensure that _PyEval_InitThreads() and _PyGILState_Init() have been called by Py_Initialize() */ assert(_PyEval_ThreadsInitialized(runtime)); - assert(current_tss_initialized(runtime)); + assert(gilstate_tss_initialized(runtime)); assert(runtime->gilstate.autoInterpreterState != NULL); - PyThreadState *tcur = current_tss_get(runtime); + PyThreadState *tcur = gilstate_tss_get(runtime); int has_gil; if (tcur == NULL) { /* Create a new Python thread state for this thread */ - tcur = PyThreadState_New(runtime->gilstate.autoInterpreterState); + tcur = new_threadstate(runtime->gilstate.autoInterpreterState); if (tcur == NULL) { Py_FatalError("Couldn't create thread-state for new thread"); } + bind_tstate(tcur); + bind_gilstate_tstate(tcur); /* This is our thread state! We'll need to delete it in the matching call to PyGILState_Release(). */ @@ -1997,7 +2197,7 @@ void PyGILState_Release(PyGILState_STATE oldstate) { _PyRuntimeState *runtime = &_PyRuntime; - PyThreadState *tstate = current_tss_get(runtime); + PyThreadState *tstate = gilstate_tss_get(runtime); if (tstate == NULL) { Py_FatalError("auto-releasing thread-state, " "but no thread-state for this thread"); @@ -2023,6 +2223,7 @@ PyGILState_Release(PyGILState_STATE oldstate) if (tstate->gilstate_counter == 0) { /* can't have been locked when we created it */ assert(oldstate == PyGILState_UNLOCKED); + // XXX Unbind tstate here. PyThreadState_Clear(tstate); /* Delete the thread-state. Note this releases the GIL too! * It's vital that the GIL be held here, to avoid shutdown From 7a3752338a2bfea023fcb119c842750fe799262f Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 30 Jan 2023 11:23:57 -0800 Subject: [PATCH 035/225] GH-101369: Allow macros as family members (#101399) Also check for instructions straddling families (this includes macro parts). --- Tools/cases_generator/generate_cases.py | 75 +++++++++++++++++++------ Tools/cases_generator/test_generator.py | 54 ++++++++++++------ 2 files changed, 93 insertions(+), 36 deletions(-) diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 1d703a0a790ed5..f0c5f96733fe62 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -426,10 +426,12 @@ class Component: def write_body(self, out: Formatter, cache_adjust: int) -> None: with out.block(""): + input_names = {ieffect.name for _, ieffect in self.input_mapping} for var, ieffect in self.input_mapping: out.declare(ieffect, var) for _, oeffect in self.output_mapping: - out.declare(oeffect, None) + if oeffect.name not in input_names: + out.declare(oeffect, None) self.instr.write_body(out, dedent=-4, cache_adjust=cache_adjust) @@ -565,10 +567,10 @@ def analyze(self) -> None: Raises SystemExit if there is an error. """ self.find_predictions() - self.map_families() - self.check_families() self.analyze_register_instrs() self.analyze_supers_and_macros() + self.map_families() + self.check_families() def find_predictions(self) -> None: """Find the instructions that need PREDICTED() labels.""" @@ -587,11 +589,30 @@ def find_predictions(self) -> None: ) def map_families(self) -> None: - """Make instruction names back to their family, if they have one.""" + """Link instruction names back to their family, if they have one.""" for family in self.families.values(): for member in family.members: if member_instr := self.instrs.get(member): - member_instr.family = family + if member_instr.family not in (family, None): + self.error( + f"Instruction {member} is a member of multiple families " + f"({member_instr.family.name}, {family.name}).", + family + ) + else: + member_instr.family = family + elif member_macro := self.macro_instrs.get(member): + for part in member_macro.parts: + if isinstance(part, Component): + if part.instr.family not in (family, None): + self.error( + f"Component {part.instr.name} of macro {member} " + f"is a member of multiple families " + f"({part.instr.family.name}, {family.name}).", + family + ) + else: + part.instr.family = family else: self.error( f"Unknown instruction {member!r} referenced in family {family.name!r}", @@ -608,7 +629,7 @@ def check_families(self) -> None: for family in self.families.values(): if len(family.members) < 2: self.error(f"Family {family.name!r} has insufficient members", family) - members = [member for member in family.members if member in self.instrs] + members = [member for member in family.members if member in self.instrs or member in self.macro_instrs] if members != family.members: unknown = set(family.members) - set(members) self.error( @@ -616,24 +637,42 @@ def check_families(self) -> None: ) if len(members) < 2: continue - head = self.instrs[members[0]] - cache = head.cache_offset - input = len(head.input_effects) - output = len(head.output_effects) + expected_effects = self.effect_counts(members[0]) for member in members[1:]: - instr = self.instrs[member] - c = instr.cache_offset - i = len(instr.input_effects) - o = len(instr.output_effects) - if (c, i, o) != (cache, input, output): + member_effects = self.effect_counts(member) + if member_effects != expected_effects: self.error( f"Family {family.name!r} has inconsistent " - f"(cache, inputs, outputs) effects:\n" - f" {family.members[0]} = {(cache, input, output)}; " - f"{member} = {(c, i, o)}", + f"(cache, input, output) effects:\n" + f" {family.members[0]} = {expected_effects}; " + f"{member} = {member_effects}", family, ) + def effect_counts(self, name: str) -> tuple[int, int, int]: + if instr := self.instrs.get(name): + cache = instr.cache_offset + input = len(instr.input_effects) + output = len(instr.output_effects) + elif macro := self.macro_instrs.get(name): + cache, input, output = 0, 0, 0 + for part in macro.parts: + if isinstance(part, Component): + cache += part.instr.cache_offset + # A component may pop what the previous component pushed, + # so we offset the input/output counts by that. + delta_i = len(part.instr.input_effects) + delta_o = len(part.instr.output_effects) + offset = min(delta_i, output) + input += delta_i - offset + output += delta_o - offset + else: + assert isinstance(part, parser.CacheEffect), part + cache += part.size + else: + assert False, f"Unknown instruction {name!r}" + return cache, input, output + def analyze_register_instrs(self) -> None: for instr in self.instrs.values(): if instr.register: diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index 6e6c60782d73d3..49a99377fc04c8 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -339,21 +339,24 @@ def test_super_instruction(): def test_macro_instruction(): input = """ - inst(OP1, (counter/1, arg1 -- interim)) { - interim = op1(arg1); + inst(OP1, (counter/1, left, right -- left, right)) { + op1(left, right); } - op(OP2, (extra/2, arg2, interim -- res)) { - res = op2(arg2, interim); + op(OP2, (extra/2, arg2, left, right -- res)) { + res = op2(arg2, left, right); } macro(OP) = OP1 + cache/2 + OP2; + inst(OP3, (unused/5, arg2, left, right -- res)) { + res = op3(arg2, left, right); + } + family(op, INLINE_CACHE_ENTRIES_OP) = { OP, OP3 }; """ output = """ TARGET(OP1) { - PyObject *arg1 = PEEK(1); - PyObject *interim; + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); uint16_t counter = read_u16(&next_instr[0].cache); - interim = op1(arg1); - POKE(1, interim); + op1(left, right); JUMPBY(1); DISPATCH(); } @@ -361,24 +364,39 @@ def test_macro_instruction(): TARGET(OP) { PyObject *_tmp_1 = PEEK(1); PyObject *_tmp_2 = PEEK(2); + PyObject *_tmp_3 = PEEK(3); { - PyObject *arg1 = _tmp_1; - PyObject *interim; + PyObject *right = _tmp_1; + PyObject *left = _tmp_2; uint16_t counter = read_u16(&next_instr[0].cache); - interim = op1(arg1); - _tmp_1 = interim; + op1(left, right); + _tmp_2 = left; + _tmp_1 = right; } { - PyObject *interim = _tmp_1; - PyObject *arg2 = _tmp_2; + PyObject *right = _tmp_1; + PyObject *left = _tmp_2; + PyObject *arg2 = _tmp_3; PyObject *res; uint32_t extra = read_u32(&next_instr[3].cache); - res = op2(arg2, interim); - _tmp_2 = res; + res = op2(arg2, left, right); + _tmp_3 = res; } JUMPBY(5); - STACK_SHRINK(1); - POKE(1, _tmp_2); + STACK_SHRINK(2); + POKE(1, _tmp_3); + DISPATCH(); + } + + TARGET(OP3) { + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); + PyObject *arg2 = PEEK(3); + PyObject *res; + res = op3(arg2, left, right); + STACK_SHRINK(2); + POKE(1, res); + JUMPBY(5); DISPATCH(); } """ From 28db978d7f134edf6c86f21c42e15003511e7e9b Mon Sep 17 00:00:00 2001 From: Eclips4 <80244920+Eclips4@users.noreply.github.com> Date: Mon, 30 Jan 2023 22:49:06 +0300 Subject: [PATCH 036/225] gh-101229: Add tests for aliases of imported names (#101230) --- Lib/test/test_ast.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index c728d2b55e4277..98d9b603bbc1cb 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -408,6 +408,24 @@ def test_alias(self): self.assertEqual(alias.col_offset, 16) self.assertEqual(alias.end_col_offset, 17) + im = ast.parse("from bar import y as z").body[0] + alias = im.names[0] + self.assertEqual(alias.name, "y") + self.assertEqual(alias.asname, "z") + self.assertEqual(alias.lineno, 1) + self.assertEqual(alias.end_lineno, 1) + self.assertEqual(alias.col_offset, 16) + self.assertEqual(alias.end_col_offset, 22) + + im = ast.parse("import bar as foo").body[0] + alias = im.names[0] + self.assertEqual(alias.name, "bar") + self.assertEqual(alias.asname, "foo") + self.assertEqual(alias.lineno, 1) + self.assertEqual(alias.end_lineno, 1) + self.assertEqual(alias.col_offset, 7) + self.assertEqual(alias.end_col_offset, 17) + def test_base_classes(self): self.assertTrue(issubclass(ast.For, ast.stmt)) self.assertTrue(issubclass(ast.Name, ast.expr)) From e867c1b753424d8d3f9c9ba0b431d007fd958c80 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Tue, 31 Jan 2023 08:33:54 +0900 Subject: [PATCH 037/225] gh-101400: Fix incorrect lineno in exception message on continue/break which are not in a loop (#101413) --- Lib/test/test_syntax.py | 30 +++++++++++-------- ...-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst | 2 ++ Python/compile.c | 6 ++-- 3 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index cb284195d976ff..f23653558a9119 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -1957,9 +1957,6 @@ def error2(): """ self._check_error(source, "parameter and nonlocal", lineno=3) - def test_break_outside_loop(self): - self._check_error("break", "outside loop") - def test_yield_outside_function(self): self._check_error("if 0: yield", "outside function") self._check_error("if 0: yield\nelse: x=1", "outside function") @@ -1988,20 +1985,27 @@ def test_return_outside_function(self): "outside function") def test_break_outside_loop(self): - self._check_error("if 0: break", "outside loop") - self._check_error("if 0: break\nelse: x=1", "outside loop") - self._check_error("if 1: pass\nelse: break", "outside loop") - self._check_error("class C:\n if 0: break", "outside loop") + msg = "outside loop" + self._check_error("break", msg, lineno=1) + self._check_error("if 0: break", msg, lineno=1) + self._check_error("if 0: break\nelse: x=1", msg, lineno=1) + self._check_error("if 1: pass\nelse: break", msg, lineno=2) + self._check_error("class C:\n if 0: break", msg, lineno=2) self._check_error("class C:\n if 1: pass\n else: break", - "outside loop") + msg, lineno=3) + self._check_error("with object() as obj:\n break", + msg, lineno=2) def test_continue_outside_loop(self): - self._check_error("if 0: continue", "not properly in loop") - self._check_error("if 0: continue\nelse: x=1", "not properly in loop") - self._check_error("if 1: pass\nelse: continue", "not properly in loop") - self._check_error("class C:\n if 0: continue", "not properly in loop") + msg = "not properly in loop" + self._check_error("if 0: continue", msg, lineno=1) + self._check_error("if 0: continue\nelse: x=1", msg, lineno=1) + self._check_error("if 1: pass\nelse: continue", msg, lineno=2) + self._check_error("class C:\n if 0: continue", msg, lineno=2) self._check_error("class C:\n if 1: pass\n else: continue", - "not properly in loop") + msg, lineno=3) + self._check_error("with object() as obj:\n continue", + msg, lineno=2) def test_unexpected_indent(self): self._check_error("foo()\n bar()\n", "unexpected indent", diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst new file mode 100644 index 00000000000000..f3dd783c01e7c0 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst @@ -0,0 +1,2 @@ +Fix wrong lineno in exception message on :keyword:`continue` or +:keyword:`break` which are not in a loop. Patch by Dong-hee Na. diff --git a/Python/compile.c b/Python/compile.c index c31f08c0a1797b..70d05af58161f9 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -3246,11 +3246,12 @@ static int compiler_break(struct compiler *c, location loc) { struct fblockinfo *loop = NULL; + location origin_loc = loc; /* Emit instruction with line number */ ADDOP(c, loc, NOP); RETURN_IF_ERROR(compiler_unwind_fblock_stack(c, &loc, 0, &loop)); if (loop == NULL) { - return compiler_error(c, loc, "'break' outside loop"); + return compiler_error(c, origin_loc, "'break' outside loop"); } RETURN_IF_ERROR(compiler_unwind_fblock(c, &loc, loop, 0)); ADDOP_JUMP(c, loc, JUMP, loop->fb_exit); @@ -3261,11 +3262,12 @@ static int compiler_continue(struct compiler *c, location loc) { struct fblockinfo *loop = NULL; + location origin_loc = loc; /* Emit instruction with line number */ ADDOP(c, loc, NOP); RETURN_IF_ERROR(compiler_unwind_fblock_stack(c, &loc, 0, &loop)); if (loop == NULL) { - return compiler_error(c, loc, "'continue' not properly in loop"); + return compiler_error(c, origin_loc, "'continue' not properly in loop"); } ADDOP_JUMP(c, loc, JUMP, loop->fb_block); return SUCCESS; From af7b2db38497479238bba3f7edba1d8f8d685c4f Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 31 Jan 2023 00:06:19 +0000 Subject: [PATCH 038/225] gh-99955: use SUCCESS/ERROR return values in optimizer and assembler. Use RETURN_IF_ERROR where appropriate. Fix a couple of bugs. (#101412) --- Python/compile.c | 331 ++++++++++++++++++++--------------------------- 1 file changed, 142 insertions(+), 189 deletions(-) diff --git a/Python/compile.c b/Python/compile.c index 70d05af58161f9..a11bcc79a6dd10 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -176,13 +176,11 @@ static struct jump_target_label_ NO_LABEL = {-1}; #define NEW_JUMP_TARGET_LABEL(C, NAME) \ jump_target_label NAME = cfg_new_label(CFG_BUILDER(C)); \ if (!IS_LABEL(NAME)) { \ - return 0; \ + return ERROR; \ } #define USE_LABEL(C, LBL) \ - if (cfg_builder_use_label(CFG_BUILDER(C), LBL) < 0) { \ - return 0; \ - } + RETURN_IF_ERROR(cfg_builder_use_label(CFG_BUILDER(C), LBL)) struct instr { int i_opcode; @@ -619,8 +617,9 @@ _Py_Mangle(PyObject *privateobj, PyObject *ident) maxchar = PyUnicode_MAX_CHAR_VALUE(privateobj); result = PyUnicode_New(1 + nlen + plen, maxchar); - if (!result) - return 0; + if (!result) { + return NULL; + } /* ident = "_" + priv[ipriv:] + ident # i.e. 1+plen+nlen bytes */ PyUnicode_WRITE(PyUnicode_KIND(result), PyUnicode_DATA(result), 0, '_'); if (PyUnicode_CopyCharacters(result, 1, privateobj, ipriv, plen) < 0) { @@ -991,11 +990,11 @@ basicblock_append_instructions(basicblock *target, basicblock *source) for (int i = 0; i < source->b_iused; i++) { int n = basicblock_next_instr(target); if (n < 0) { - return -1; + return ERROR; } target->b_instr[n] = source->b_instr[i]; } - return 0; + return SUCCESS; } static basicblock * @@ -1029,7 +1028,7 @@ basicblock_next_instr(basicblock *b) DEFAULT_BLOCK_SIZE, sizeof(struct instr)); if (b->b_instr == NULL) { PyErr_NoMemory(); - return -1; + return ERROR; } b->b_ialloc = DEFAULT_BLOCK_SIZE; } @@ -1041,19 +1040,19 @@ basicblock_next_instr(basicblock *b) if (oldsize > (SIZE_MAX >> 1)) { PyErr_NoMemory(); - return -1; + return ERROR; } if (newsize == 0) { PyErr_NoMemory(); - return -1; + return ERROR; } b->b_ialloc <<= 1; tmp = (struct instr *)PyObject_Realloc( (void *)b->b_instr, newsize); if (tmp == NULL) { PyErr_NoMemory(); - return -1; + return ERROR; } b->b_instr = tmp; memset((char *)b->b_instr + oldsize, 0, newsize - oldsize); @@ -1371,21 +1370,19 @@ cfg_builder_maybe_start_new_block(cfg_builder *g) if (cfg_builder_current_block_is_terminated(g)) { basicblock *b = cfg_builder_new_block(g); if (b == NULL) { - return -1; + return ERROR; } b->b_label = g->g_current_label.id; g->g_current_label = NO_LABEL; cfg_builder_use_next_block(g, b); } - return 0; + return SUCCESS; } static int cfg_builder_addop(cfg_builder *g, int opcode, int oparg, location loc) { - if (cfg_builder_maybe_start_new_block(g) != 0) { - return -1; - } + RETURN_IF_ERROR(cfg_builder_maybe_start_new_block(g)); return basicblock_addop(g->g_curblock, opcode, oparg, loc); } @@ -1405,16 +1402,16 @@ dict_add_o(PyObject *dict, PyObject *o) v = PyDict_GetItemWithError(dict, o); if (!v) { if (PyErr_Occurred()) { - return -1; + return ERROR; } arg = PyDict_GET_SIZE(dict); v = PyLong_FromSsize_t(arg); if (!v) { - return -1; + return ERROR; } if (PyDict_SetItem(dict, o, v) < 0) { Py_DECREF(v); - return -1; + return ERROR; } Py_DECREF(v); } @@ -1536,7 +1533,7 @@ compiler_add_const(struct compiler *c, PyObject *o) { PyObject *key = merge_consts_recursive(c->c_const_cache, o); if (key == NULL) { - return -1; + return ERROR; } Py_ssize_t arg = dict_add_o(c->u->u_consts, key); @@ -1622,7 +1619,7 @@ cfg_builder_addop_j(cfg_builder *g, location loc, #define ADDOP_IN_SCOPE(C, LOC, OP) { \ if (cfg_builder_addop_noarg(CFG_BUILDER(C), (OP), (LOC)) < 0) { \ compiler_exit_scope(C); \ - return -1; \ + return ERROR; \ } \ } @@ -1697,8 +1694,7 @@ cfg_builder_addop_j(cfg_builder *g, location loc, asdl_ ## TYPE ## _seq *seq = (SEQ); /* avoid variable capture */ \ for (_i = 0; _i < asdl_seq_LEN(seq); _i++) { \ TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, _i); \ - if (compiler_visit_ ## TYPE((C), elt) < 0) \ - return ERROR; \ + RETURN_IF_ERROR(compiler_visit_ ## TYPE((C), elt)); \ } \ } @@ -2232,7 +2228,7 @@ get_ref_type(struct compiler *c, PyObject *name) name, c->u->u_name, c->u->u_ste->ste_id, c->u->u_ste->ste_symbols, c->u->u_varnames, c->u->u_names); - return -1; + return ERROR; } return scope; } @@ -2240,10 +2236,10 @@ get_ref_type(struct compiler *c, PyObject *name) static int compiler_lookup_arg(PyObject *dict, PyObject *name) { - PyObject *v; - v = PyDict_GetItemWithError(dict, name); - if (v == NULL) - return -1; + PyObject *v = PyDict_GetItemWithError(dict, name); + if (v == NULL) { + return ERROR; + } return PyLong_AS_LONG(v); } @@ -3022,7 +3018,7 @@ compiler_lambda(struct compiler *c, expr_ty e) location loc = LOC(e); funcflags = compiler_default_arguments(c, loc, args); if (funcflags == -1) { - return 0; + return ERROR; } _Py_DECLARE_STR(anon_lambda, ""); @@ -5083,9 +5079,9 @@ compiler_call_helper(struct compiler *c, location loc, if (n ==0 && nelts == 1 && ((expr_ty)asdl_seq_GET(args, 0))->kind == Starred_kind) { VISIT(c, expr, ((expr_ty)asdl_seq_GET(args, 0))->v.Starred.value); } - else if (starunpack_helper(c, loc, args, n, BUILD_LIST, - LIST_APPEND, LIST_EXTEND, 1) < 0) { - return ERROR; + else { + RETURN_IF_ERROR(starunpack_helper(c, loc, args, n, BUILD_LIST, + LIST_APPEND, LIST_EXTEND, 1)); } /* Then keyword arguments */ if (nkwelts) { @@ -7115,7 +7111,7 @@ stackdepth(basicblock *entryblock, int code_flags) } basicblock **stack = make_cfg_traversal_stack(entryblock); if (!stack) { - return -1; + return ERROR; } int maxdepth = 0; @@ -7138,7 +7134,7 @@ stackdepth(basicblock *entryblock, int code_flags) PyErr_Format(PyExc_SystemError, "compiler stack_effect(opcode=%d, arg=%i) failed", instr->i_opcode, instr->i_oparg); - return -1; + return ERROR; } int new_depth = depth + effect; assert(new_depth >= 0); /* invalid code or bug in stackdepth() */ @@ -7194,12 +7190,12 @@ assemble_init(struct assembler *a, int firstlineno) if (a->a_except_table == NULL) { goto error; } - return 0; + return SUCCESS; error: Py_XDECREF(a->a_bytecode); Py_XDECREF(a->a_linetable); Py_XDECREF(a->a_except_table); - return -1; + return ERROR; } static void @@ -7270,13 +7266,13 @@ static int label_exception_targets(basicblock *entryblock) { basicblock **todo_stack = make_cfg_traversal_stack(entryblock); if (todo_stack == NULL) { - return -1; + return ERROR; } ExceptStack *except_stack = make_except_stack(); if (except_stack == NULL) { PyMem_Free(todo_stack); PyErr_NoMemory(); - return -1; + return ERROR; } except_stack->depth = 0; todo_stack[0] = entryblock; @@ -7354,11 +7350,11 @@ label_exception_targets(basicblock *entryblock) { } #endif PyMem_Free(todo_stack); - return 0; + return SUCCESS; error: PyMem_Free(todo_stack); PyMem_Free(except_stack); - return -1; + return ERROR; } @@ -7377,14 +7373,14 @@ mark_except_handlers(basicblock *entryblock) { } } } - return 0; + return SUCCESS; } static int mark_warm(basicblock *entryblock) { basicblock **stack = make_cfg_traversal_stack(entryblock); if (stack == NULL) { - return -1; + return ERROR; } basicblock **sp = stack; @@ -7408,7 +7404,7 @@ mark_warm(basicblock *entryblock) { } } PyMem_Free(stack); - return 0; + return SUCCESS; } static int @@ -7417,12 +7413,12 @@ mark_cold(basicblock *entryblock) { assert(!b->b_cold && !b->b_warm); } if (mark_warm(entryblock) < 0) { - return -1; + return ERROR; } basicblock **stack = make_cfg_traversal_stack(entryblock); if (stack == NULL) { - return -1; + return ERROR; } basicblock **sp = stack; @@ -7457,7 +7453,7 @@ mark_cold(basicblock *entryblock) { } } PyMem_Free(stack); - return 0; + return SUCCESS; } static int @@ -7468,11 +7464,9 @@ push_cold_blocks_to_end(cfg_builder *g, int code_flags) { basicblock *entryblock = g->g_entryblock; if (entryblock->b_next == NULL) { /* single basicblock, no need to reorder */ - return 0; - } - if (mark_cold(entryblock) < 0) { - return -1; + return SUCCESS; } + RETURN_IF_ERROR(mark_cold(entryblock)); /* If we have a cold block with fallthrough to a warm block, add */ /* an explicit jump instead of fallthrough */ @@ -7480,7 +7474,7 @@ push_cold_blocks_to_end(cfg_builder *g, int code_flags) { if (b->b_cold && BB_HAS_FALLTHROUGH(b) && b->b_next && b->b_next->b_warm) { basicblock *explicit_jump = cfg_builder_new_block(g); if (explicit_jump == NULL) { - return -1; + return ERROR; } basicblock_addop(explicit_jump, JUMP, b->b_next->b_label, NO_LOCATION); explicit_jump->b_cold = 1; @@ -7534,11 +7528,9 @@ push_cold_blocks_to_end(cfg_builder *g, int code_flags) { b->b_next = cold_blocks; if (cold_blocks != NULL) { - if (remove_redundant_jumps(g) < 0) { - return -1; - } + RETURN_IF_ERROR(remove_redundant_jumps(g)); } - return 0; + return SUCCESS; } static void @@ -7596,9 +7588,7 @@ assemble_emit_exception_table_entry(struct assembler *a, int start, int end, bas { Py_ssize_t len = PyBytes_GET_SIZE(a->a_except_table); if (a->a_except_table_off + MAX_SIZE_OF_ENTRY >= len) { - if (_PyBytes_Resize(&a->a_except_table, len * 2) < 0) { - return -1; - } + RETURN_IF_ERROR(_PyBytes_Resize(&a->a_except_table, len * 2)); } int size = end-start; assert(end > start); @@ -7613,7 +7603,7 @@ assemble_emit_exception_table_entry(struct assembler *a, int start, int end, bas assemble_emit_exception_table_item(a, size, 0); assemble_emit_exception_table_item(a, target, 0); assemble_emit_exception_table_item(a, depth_lasti, 0); - return 0; + return SUCCESS; } static int @@ -7629,9 +7619,8 @@ assemble_exception_table(struct assembler *a, basicblock *entryblock) struct instr *instr = &b->b_instr[i]; if (instr->i_except != handler) { if (handler != NULL) { - if (assemble_emit_exception_table_entry(a, start, ioffset, handler) < 0) { - return -1; - } + RETURN_IF_ERROR( + assemble_emit_exception_table_entry(a, start, ioffset, handler)); } start = ioffset; handler = instr->i_except; @@ -7640,11 +7629,9 @@ assemble_exception_table(struct assembler *a, basicblock *entryblock) } } if (handler != NULL) { - if (assemble_emit_exception_table_entry(a, start, ioffset, handler) < 0) { - return -1; - } + RETURN_IF_ERROR(assemble_emit_exception_table_entry(a, start, ioffset, handler)); } - return 0; + return SUCCESS; } /* Code location emitting code. See locations.md for a description of the format. */ @@ -7746,13 +7733,11 @@ write_location_info_entry(struct assembler* a, location loc, int isize) Py_ssize_t len = PyBytes_GET_SIZE(a->a_linetable); if (a->a_location_off + THEORETICAL_MAX_ENTRY_SIZE >= len) { assert(len > THEORETICAL_MAX_ENTRY_SIZE); - if (_PyBytes_Resize(&a->a_linetable, len*2) < 0) { - return -1; - } + RETURN_IF_ERROR(_PyBytes_Resize(&a->a_linetable, len*2)); } if (loc.lineno < 0) { write_location_info_none(a, isize); - return 0; + return SUCCESS; } int line_delta = loc.lineno - a->a_lineno; int column = loc.col_offset; @@ -7763,35 +7748,33 @@ write_location_info_entry(struct assembler* a, location loc, int isize) if (loc.end_lineno == loc.lineno || loc.end_lineno == -1) { write_location_info_no_column(a, isize, line_delta); a->a_lineno = loc.lineno; - return 0; + return SUCCESS; } } else if (loc.end_lineno == loc.lineno) { if (line_delta == 0 && column < 80 && end_column - column < 16 && end_column >= column) { write_location_info_short_form(a, isize, column, end_column); - return 0; + return SUCCESS; } if (line_delta >= 0 && line_delta < 3 && column < 128 && end_column < 128) { write_location_info_oneline_form(a, isize, line_delta, column, end_column); a->a_lineno = loc.lineno; - return 0; + return SUCCESS; } } write_location_info_long_form(a, loc, isize); a->a_lineno = loc.lineno; - return 0; + return SUCCESS; } static int assemble_emit_location(struct assembler* a, location loc, int isize) { if (isize == 0) { - return 0; + return SUCCESS; } while (isize > 8) { - if (write_location_info_entry(a, loc, 8)) { - return -1; - } + RETURN_IF_ERROR(write_location_info_entry(a, loc, 8)); isize -= 8; } return write_location_info_entry(a, loc, isize); @@ -7811,34 +7794,32 @@ assemble_emit(struct assembler *a, struct instr *i) int size = instr_size(i); if (a->a_offset + size >= len / (int)sizeof(_Py_CODEUNIT)) { if (len > PY_SSIZE_T_MAX / 2) { - return -1; - } - if (_PyBytes_Resize(&a->a_bytecode, len * 2) < 0) { - return -1; + return ERROR; } + RETURN_IF_ERROR(_PyBytes_Resize(&a->a_bytecode, len * 2)); } code = (_Py_CODEUNIT *)PyBytes_AS_STRING(a->a_bytecode) + a->a_offset; a->a_offset += size; write_instr(code, i, size); - return 0; + return SUCCESS; } static int normalize_jumps_in_block(cfg_builder *g, basicblock *b) { struct instr *last = basicblock_last_instr(b); if (last == NULL || !is_jump(last)) { - return 0; + return SUCCESS; } assert(!IS_ASSEMBLER_OPCODE(last->i_opcode)); bool is_forward = last->i_target->b_visited == 0; switch(last->i_opcode) { case JUMP: last->i_opcode = is_forward ? JUMP_FORWARD : JUMP_BACKWARD; - return 0; + return SUCCESS; case JUMP_NO_INTERRUPT: last->i_opcode = is_forward ? JUMP_FORWARD : JUMP_BACKWARD_NO_INTERRUPT; - return 0; + return SUCCESS; } int reversed_opcode = 0; switch(last->i_opcode) { @@ -7868,10 +7849,10 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) { last->i_opcode == JUMP_IF_TRUE_OR_POP ? "JUMP_IF_TRUE_OR_POP" : "JUMP_IF_FALSE_OR_POP"); } - return 0; + return SUCCESS; } if (is_forward) { - return 0; + return SUCCESS; } /* transform 'conditional jump T' to @@ -7881,7 +7862,7 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) { basicblock *target = last->i_target; basicblock *backwards_jump = cfg_builder_new_block(g); if (backwards_jump == NULL) { - return -1; + return ERROR; } basicblock_addop(backwards_jump, JUMP, target->b_label, NO_LOCATION); backwards_jump->b_instr[0].i_target = target; @@ -7891,7 +7872,7 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) { backwards_jump->b_cold = b->b_cold; backwards_jump->b_next = b->b_next; b->b_next = backwards_jump; - return 0; + return SUCCESS; } static int @@ -7903,11 +7884,9 @@ normalize_jumps(cfg_builder *g) } for (basicblock *b = entryblock; b != NULL; b = b->b_next) { b->b_visited = 1; - if (normalize_jumps_in_block(g, b) < 0) { - return -1; - } + RETURN_IF_ERROR(normalize_jumps_in_block(g, b)); } - return 0; + return SUCCESS; } static void @@ -8047,7 +8026,7 @@ fast_scan_many_locals(basicblock *entryblock, int nlocals) Py_ssize_t *states = PyMem_Calloc(nlocals - 64, sizeof(Py_ssize_t)); if (states == NULL) { PyErr_NoMemory(); - return -1; + return ERROR; } Py_ssize_t blocknum = 0; // state[i - 64] == blocknum if local i is guaranteed to @@ -8083,7 +8062,7 @@ fast_scan_many_locals(basicblock *entryblock, int nlocals) } } PyMem_Free(states); - return 0; + return SUCCESS; } static int @@ -8092,20 +8071,20 @@ add_checks_for_loads_of_uninitialized_variables(basicblock *entryblock, { int nlocals = (int)PyDict_GET_SIZE(c->u->u_varnames); if (nlocals == 0) { - return 0; + return SUCCESS; } if (nlocals > 64) { // To avoid O(nlocals**2) compilation, locals beyond the first // 64 are only analyzed one basicblock at a time: initialization // info is not passed between basicblocks. if (fast_scan_many_locals(entryblock, nlocals) < 0) { - return -1; + return ERROR; } nlocals = 64; } basicblock **stack = make_cfg_traversal_stack(entryblock); if (stack == NULL) { - return -1; + return ERROR; } basicblock **sp = stack; @@ -8134,7 +8113,7 @@ add_checks_for_loads_of_uninitialized_variables(basicblock *entryblock, scan_block_for_locals(b, &sp); } PyMem_Free(stack); - return 0; + return SUCCESS; } static PyObject * @@ -8220,17 +8199,17 @@ merge_const_one(PyObject *const_cache, PyObject **obj) PyDict_CheckExact(const_cache); PyObject *key = _PyCode_ConstantKey(*obj); if (key == NULL) { - return -1; + return ERROR; } // t is borrowed reference PyObject *t = PyDict_SetDefault(const_cache, key, key); Py_DECREF(key); if (t == NULL) { - return -1; + return ERROR; } if (t == key) { // obj is new constant. - return 0; + return SUCCESS; } if (PyTuple_CheckExact(t)) { @@ -8239,7 +8218,7 @@ merge_const_one(PyObject *const_cache, PyObject **obj) } Py_SETREF(*obj, Py_NewRef(t)); - return 0; + return SUCCESS; } // This is in codeobject.c. @@ -8482,18 +8461,14 @@ insert_prefix_instructions(struct compiler *c, basicblock *entryblock, .i_loc = LOCATION(c->u->u_firstlineno, c->u->u_firstlineno, -1, -1), .i_target = NULL, }; - if (insert_instruction(entryblock, 0, &make_gen) < 0) { - return -1; - } + RETURN_IF_ERROR(insert_instruction(entryblock, 0, &make_gen)); struct instr pop_top = { .i_opcode = POP_TOP, .i_oparg = 0, .i_loc = NO_LOCATION, .i_target = NULL, }; - if (insert_instruction(entryblock, 1, &pop_top) < 0) { - return -1; - } + RETURN_IF_ERROR(insert_instruction(entryblock, 1, &pop_top)); } /* Set up cells for any variable that escapes, to be put in a closure. */ @@ -8506,7 +8481,7 @@ insert_prefix_instructions(struct compiler *c, basicblock *entryblock, int *sorted = PyMem_RawCalloc(nvars, sizeof(int)); if (sorted == NULL) { PyErr_NoMemory(); - return -1; + return ERROR; } for (int i = 0; i < ncellvars; i++) { sorted[fixed[i]] = i + 1; @@ -8523,9 +8498,7 @@ insert_prefix_instructions(struct compiler *c, basicblock *entryblock, .i_loc = NO_LOCATION, .i_target = NULL, }; - if (insert_instruction(entryblock, ncellsused, &make_cell) < 0) { - return -1; - } + RETURN_IF_ERROR(insert_instruction(entryblock, ncellsused, &make_cell)); ncellsused += 1; } PyMem_RawFree(sorted); @@ -8538,13 +8511,10 @@ insert_prefix_instructions(struct compiler *c, basicblock *entryblock, .i_loc = NO_LOCATION, .i_target = NULL, }; - if (insert_instruction(entryblock, 0, ©_frees) < 0) { - return -1; - } - + RETURN_IF_ERROR(insert_instruction(entryblock, 0, ©_frees)); } - return 0; + return SUCCESS; } /* Make sure that all returns have a line number, even if early passes @@ -8704,7 +8674,7 @@ remove_redundant_jumps(cfg_builder *g) { if (IS_UNCONDITIONAL_JUMP_OPCODE(last->i_opcode)) { if (last->i_target == NULL) { PyErr_SetString(PyExc_SystemError, "jump with NULL target"); - return -1; + return ERROR; } if (last->i_target == b->b_next) { assert(b->b_next->b_iused); @@ -8712,7 +8682,7 @@ remove_redundant_jumps(cfg_builder *g) { } } } - return 0; + return SUCCESS; } static int @@ -8729,7 +8699,7 @@ prepare_localsplus(struct compiler* c, int code_flags) int nlocalsplus = nlocals + ncellvars + nfreevars; int* cellfixedoffsets = build_cellfixedoffsets(c); if (cellfixedoffsets == NULL) { - return -1; + return ERROR; } cfg_builder* g = CFG_BUILDER(c); @@ -8737,14 +8707,14 @@ prepare_localsplus(struct compiler* c, int code_flags) // This must be called before fix_cell_offsets(). if (insert_prefix_instructions(c, g->g_entryblock, cellfixedoffsets, nfreevars, code_flags)) { PyMem_Free(cellfixedoffsets); - return -1; + return ERROR; } int numdropped = fix_cell_offsets(c, g->g_entryblock, cellfixedoffsets); PyMem_Free(cellfixedoffsets); // At this point we're done with it. cellfixedoffsets = NULL; if (numdropped < 0) { - return -1; + return ERROR; } nlocalsplus -= numdropped; return nlocalsplus; @@ -8825,7 +8795,7 @@ assemble(struct compiler *c, int addNone) if (add_checks_for_loads_of_uninitialized_variables(g->g_entryblock, c) < 0) { goto error; } - if (remove_unused_consts(g->g_entryblock, consts)) { + if (remove_unused_consts(g->g_entryblock, consts) < 0) { goto error; } @@ -8967,27 +8937,27 @@ fold_tuple_on_constants(PyObject *const_cache, for (int i = 0; i < n; i++) { if (!HAS_CONST(inst[i].i_opcode)) { - return 0; + return SUCCESS; } } /* Buildup new tuple of constants */ PyObject *newconst = PyTuple_New(n); if (newconst == NULL) { - return -1; + return ERROR; } for (int i = 0; i < n; i++) { int op = inst[i].i_opcode; int arg = inst[i].i_oparg; PyObject *constant = get_const_value(op, arg, consts); if (constant == NULL) { - return -1; + return ERROR; } PyTuple_SET_ITEM(newconst, i, constant); } if (merge_const_one(const_cache, &newconst) < 0) { Py_DECREF(newconst); - return -1; + return ERROR; } Py_ssize_t index; @@ -9000,11 +8970,11 @@ fold_tuple_on_constants(PyObject *const_cache, if ((size_t)index >= (size_t)INT_MAX - 1) { Py_DECREF(newconst); PyErr_SetString(PyExc_OverflowError, "too many constants"); - return -1; + return ERROR; } if (PyList_Append(consts, newconst)) { Py_DECREF(newconst); - return -1; + return ERROR; } } Py_DECREF(newconst); @@ -9012,7 +8982,7 @@ fold_tuple_on_constants(PyObject *const_cache, INSTR_SET_OP0(&inst[i], NOP); } INSTR_SET_OP1(&inst[n], LOAD_CONST, (int)index); - return 0; + return SUCCESS; } #define VISITED (-1) @@ -9045,13 +9015,13 @@ swaptimize(basicblock *block, int *ix) } // It's already optimal if there's only one SWAP: if (!more) { - return 0; + return SUCCESS; } // Create an array with elements {0, 1, 2, ..., depth - 1}: int *stack = PyMem_Malloc(depth * sizeof(int)); if (stack == NULL) { PyErr_NoMemory(); - return -1; + return ERROR; } for (int i = 0; i < depth; i++) { stack[i] = i; @@ -9112,7 +9082,7 @@ swaptimize(basicblock *block, int *ix) } PyMem_Free(stack); *ix += len - 1; - return 0; + return SUCCESS; } // This list is pretty small, since it's only okay to reorder opcodes that: @@ -9418,7 +9388,7 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) INSTR_SET_OP0(inst, NOP); break; } - if (swaptimize(bb, &i)) { + if (swaptimize(bb, &i) < 0) { goto error; } apply_static_swaps(bb, i); @@ -9436,9 +9406,9 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) assert (!HAS_CONST(inst->i_opcode)); } } - return 0; + return SUCCESS; error: - return -1; + return ERROR; } /* If this block ends with an unconditional jump to a small exit block, then @@ -9457,9 +9427,7 @@ inline_small_exit_blocks(basicblock *bb) { basicblock *target = last->i_target; if (basicblock_exits_scope(target) && target->b_iused <= MAX_COPY_SIZE) { INSTR_SET_OP0(last, NOP); - if (basicblock_append_instructions(bb, target) < 0) { - return -1; - } + RETURN_IF_ERROR(basicblock_append_instructions(bb, target)); return 1; } return 0; @@ -9528,19 +9496,19 @@ check_cfg(cfg_builder *g) { if (IS_TERMINATOR_OPCODE(opcode)) { if (i != b->b_iused - 1) { PyErr_SetString(PyExc_SystemError, "malformed control flow graph."); - return -1; + return ERROR; } } } } - return 0; + return SUCCESS; } static int mark_reachable(basicblock *entryblock) { basicblock **stack = make_cfg_traversal_stack(entryblock); if (stack == NULL) { - return -1; + return ERROR; } basicblock **sp = stack; entryblock->b_predecessors = 1; @@ -9569,7 +9537,7 @@ mark_reachable(basicblock *entryblock) { } } PyMem_Free(stack); - return 0; + return SUCCESS; } static void @@ -9658,7 +9626,7 @@ translate_jump_labels_to_targets(basicblock *entryblock) basicblock **label2block = (basicblock **)PyMem_Malloc(mapsize); if (!label2block) { PyErr_NoMemory(); - return -1; + return ERROR; } memset(label2block, 0, mapsize); for (basicblock *b = entryblock; b != NULL; b = b->b_next) { @@ -9680,7 +9648,7 @@ translate_jump_labels_to_targets(basicblock *entryblock) } } PyMem_Free(label2block); - return 0; + return SUCCESS; } /* Perform optimizations on a control flow graph. @@ -9695,31 +9663,22 @@ static int optimize_cfg(cfg_builder *g, PyObject *consts, PyObject *const_cache) { assert(PyDict_CheckExact(const_cache)); - if (check_cfg(g) < 0) { - return -1; - } + RETURN_IF_ERROR(check_cfg(g)); eliminate_empty_basic_blocks(g); for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { - if (inline_small_exit_blocks(b) < 0) { - return -1; - } + RETURN_IF_ERROR(inline_small_exit_blocks(b)); } assert(no_empty_basic_blocks(g)); for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { - if (optimize_basic_block(const_cache, b, consts)) { - return -1; - } + RETURN_IF_ERROR(optimize_basic_block(const_cache, b, consts)); remove_redundant_nops(b); assert(b->b_predecessors == 0); } for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { - if (inline_small_exit_blocks(b) < 0) { - return -1; - } - } - if (mark_reachable(g->g_entryblock)) { - return -1; + RETURN_IF_ERROR(inline_small_exit_blocks(b)); } + RETURN_IF_ERROR(mark_reachable(g->g_entryblock)); + /* Delete unreachable instructions */ for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { if (b->b_predecessors == 0) { @@ -9731,10 +9690,8 @@ optimize_cfg(cfg_builder *g, PyObject *consts, PyObject *const_cache) } eliminate_empty_basic_blocks(g); assert(no_redundant_nops(g)); - if (remove_redundant_jumps(g) < 0) { - return -1; - } - return 0; + RETURN_IF_ERROR(remove_redundant_jumps(g)); + return SUCCESS; } @@ -9744,12 +9701,12 @@ remove_unused_consts(basicblock *entryblock, PyObject *consts) assert(PyList_CheckExact(consts)); Py_ssize_t nconsts = PyList_GET_SIZE(consts); if (nconsts == 0) { - return 0; /* nothing to do */ + return SUCCESS; /* nothing to do */ } Py_ssize_t *index_map = NULL; Py_ssize_t *reverse_index_map = NULL; - int err = 1; + int err = ERROR; index_map = PyMem_Malloc(nconsts * sizeof(Py_ssize_t)); if (index_map == NULL) { @@ -9784,7 +9741,7 @@ remove_unused_consts(basicblock *entryblock, PyObject *consts) } if (n_used_consts == nconsts) { /* nothing to do */ - err = 0; + err = SUCCESS; goto end; } @@ -9832,7 +9789,7 @@ remove_unused_consts(basicblock *entryblock, PyObject *consts) } } - err = 0; + err = SUCCESS; end: PyMem_Free(index_map); PyMem_Free(reverse_index_map); @@ -9876,7 +9833,7 @@ duplicate_exits_without_lineno(cfg_builder *g) if (is_exit_without_lineno(target) && target->b_predecessors > 1) { basicblock *new_target = copy_basicblock(g, target); if (new_target == NULL) { - return -1; + return ERROR; } new_target->b_instr[0].i_loc = last->i_loc; last->i_target = new_target; @@ -9899,7 +9856,7 @@ duplicate_exits_without_lineno(cfg_builder *g) } } } - return 0; + return SUCCESS; } @@ -9928,54 +9885,50 @@ instructions_to_cfg(PyObject *instructions, cfg_builder *g) if (PyLong_Check(item)) { int lbl_id = PyLong_AsLong(item); if (PyErr_Occurred()) { - return -1; + return ERROR; } if (lbl_id <= 0 || lbl_id > instr_size) { /* expect label in a reasonable range */ PyErr_SetString(PyExc_ValueError, "label out of range"); - return -1; + return ERROR; } jump_target_label lbl = {lbl_id}; - if (cfg_builder_use_label(g, lbl) < 0) { - return -1; - } + RETURN_IF_ERROR(cfg_builder_use_label(g, lbl)); } else { if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 6) { PyErr_SetString(PyExc_ValueError, "expected a 6-tuple"); - return -1; + return ERROR; } int opcode = PyLong_AsLong(PyTuple_GET_ITEM(item, 0)); if (PyErr_Occurred()) { - return -1; + return ERROR; } int oparg = PyLong_AsLong(PyTuple_GET_ITEM(item, 1)); if (PyErr_Occurred()) { - return -1; + return ERROR; } location loc; loc.lineno = PyLong_AsLong(PyTuple_GET_ITEM(item, 2)); if (PyErr_Occurred()) { - return -1; + return ERROR; } loc.end_lineno = PyLong_AsLong(PyTuple_GET_ITEM(item, 3)); if (PyErr_Occurred()) { - return -1; + return ERROR; } loc.col_offset = PyLong_AsLong(PyTuple_GET_ITEM(item, 4)); if (PyErr_Occurred()) { - return -1; + return ERROR; } loc.end_col_offset = PyLong_AsLong(PyTuple_GET_ITEM(item, 5)); if (PyErr_Occurred()) { - return -1; - } - if (cfg_builder_addop(g, opcode, oparg, loc) < 0) { - return -1; + return ERROR; } + RETURN_IF_ERROR(cfg_builder_addop(g, opcode, oparg, loc)); } } - return 0; + return SUCCESS; } static PyObject * From 04ab767d28024b7a42b630a817227b02097aef30 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 30 Jan 2023 17:27:51 -0800 Subject: [PATCH 039/225] gh-98831: Clean up and add cache size static_assert to macro (#101442) --- Tools/cases_generator/generate_cases.py | 27 +++++++++++++++++++++---- Tools/cases_generator/test_generator.py | 1 + 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index f0c5f96733fe62..43685450cc0dfe 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -597,7 +597,7 @@ def map_families(self) -> None: self.error( f"Instruction {member} is a member of multiple families " f"({member_instr.family.name}, {family.name}).", - family + family, ) else: member_instr.family = family @@ -609,7 +609,7 @@ def map_families(self) -> None: f"Component {part.instr.name} of macro {member} " f"is a member of multiple families " f"({part.instr.family.name}, {family.name}).", - family + family, ) else: part.instr.family = family @@ -629,7 +629,11 @@ def check_families(self) -> None: for family in self.families.values(): if len(family.members) < 2: self.error(f"Family {family.name!r} has insufficient members", family) - members = [member for member in family.members if member in self.instrs or member in self.macro_instrs] + members = [ + member + for member in family.members + if member in self.instrs or member in self.macro_instrs + ] if members != family.members: unknown = set(family.members) - set(members) self.error( @@ -859,7 +863,9 @@ def write_stack_effect_functions(self) -> None: popped_data.append((instr, popped)) pushed_data.append((instr, pushed)) - def write_function(direction: str, data: list[tuple[AnyInstruction, str]]) -> None: + def write_function( + direction: str, data: list[tuple[AnyInstruction, str]] + ) -> None: self.out.emit("\n#ifndef NDEBUG") self.out.emit("static int") self.out.emit(f"_PyOpcode_num_{direction}(int opcode, int oparg) {{") @@ -1031,6 +1037,7 @@ def write_super(self, sup: SuperInstruction) -> None: def write_macro(self, mac: MacroInstruction) -> None: """Write code for a macro instruction.""" + last_instr: Instruction | None = None with self.wrap_super_or_macro(mac): cache_adjust = 0 for part in mac.parts: @@ -1038,12 +1045,24 @@ def write_macro(self, mac: MacroInstruction) -> None: case parser.CacheEffect(size=size): cache_adjust += size case Component() as comp: + last_instr = comp.instr comp.write_body(self.out, cache_adjust) cache_adjust += comp.instr.cache_offset if cache_adjust: self.out.emit(f"JUMPBY({cache_adjust});") + if ( + last_instr + and (family := last_instr.family) + and mac.name == family.members[0] + and (cache_size := family.size) + ): + self.out.emit( + f"static_assert({cache_size} == " + f'{cache_adjust}, "incorrect cache size");' + ) + @contextlib.contextmanager def wrap_super_or_macro(self, up: SuperOrMacroInstruction): """Shared boilerplate for super- and macro instructions.""" diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index 49a99377fc04c8..9df97d24ab6f43 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -383,6 +383,7 @@ def test_macro_instruction(): _tmp_3 = res; } JUMPBY(5); + static_assert(INLINE_CACHE_ENTRIES_OP == 5, "incorrect cache size"); STACK_SHRINK(2); POKE(1, _tmp_3); DISPATCH(); From ef09bf63d22b2efe5c0e9a2b9f25a9bec2ba1db0 Mon Sep 17 00:00:00 2001 From: Ben <22038077+bcla22@users.noreply.github.com> Date: Tue, 31 Jan 2023 00:36:40 -0500 Subject: [PATCH 040/225] Fixes typo in asyncio.TaskGroup context manager code example (#101449) --- Doc/library/asyncio-task.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index 631a5ddc1f6500..9a42b963167685 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -121,7 +121,7 @@ To actually run a coroutine, asyncio provides the following mechanisms: print(f"started at {time.strftime('%X')}") - # The wait is implicit when the context manager exits. + # The await is implicit when the context manager exits. print(f"finished at {time.strftime('%X')}") From 909a6746939ea1d09fab21f26b558cfd7e3e29a0 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 30 Jan 2023 21:39:30 -0800 Subject: [PATCH 041/225] gh-77607: Improve accuracy of os.path.join docs (#101406) This is a follow-up to #100811. One of the changes in that PR isn't accurate in that `os.path.join('', '')` will not end in a separator. This reverts that change to the previous wording that used "only", but explicitly calls out the case where the last part ends in a separator, which is what caused confusin in #77607 and motivated the change in #100811. --- Doc/library/os.path.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst index 786c2fd7f64fcc..96bcb48ad7d126 100644 --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -308,11 +308,11 @@ the :mod:`glob` module.) Join one or more path segments intelligently. The return value is the concatenation of *path* and all members of *\*paths*, with exactly one - directory separator following each non-empty part except the last. That is, - if the last part is empty, the result will end in a separator. If - a segment is an absolute path (which on Windows requires both a drive and a - root), then all previous segments are ignored and joining continues from the - absolute path segment. + directory separator following each non-empty part, except the last. That is, + the result will only end in a separator if the last part is either empty or + ends in a separator. If a segment is an absolute path (which on Windows + requires both a drive and a root), then all previous segments are ignored and + joining continues from the absolute path segment. On Windows, the drive is not reset when a rooted path segment (e.g., ``r'\foo'``) is encountered. If a segment is on a different drive or is an From 29a858b85f4c6479474b8d82c8995bde1166352b Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 31 Jan 2023 10:23:15 +0000 Subject: [PATCH 042/225] gh-98831: rewrite GET_LEN, GET_ITER, BEFORE_WITH and a few simple opcodes in the instruction definition DSL (#101443) --- Python/bytecodes.c | 57 ++++++++++++++------------------------ Python/generated_cases.c.h | 43 +++++++++++++++------------- Python/opcode_metadata.h | 28 +++++++++---------- 3 files changed, 59 insertions(+), 69 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index d1e59f7908b580..5b3bf4ec7ba796 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2053,8 +2053,7 @@ dummy_func( } } - // stack effect: ( -- ) - inst(JUMP_BACKWARD_NO_INTERRUPT) { + inst(JUMP_BACKWARD_NO_INTERRUPT, (--)) { /* This bytecode is used in the `yield from` or `await` loop. * If there is an interrupt, we want it handled in the innermost * generator or coroutine, so we deliberately do not check it here. @@ -2063,18 +2062,12 @@ dummy_func( JUMPBY(-oparg); } - // stack effect: ( -- __0) - inst(GET_LEN) { + inst(GET_LEN, (obj -- obj, len_o)) { // PUSH(len(TOS)) - Py_ssize_t len_i = PyObject_Length(TOP()); - if (len_i < 0) { - goto error; - } - PyObject *len_o = PyLong_FromSsize_t(len_i); - if (len_o == NULL) { - goto error; - } - PUSH(len_o); + Py_ssize_t len_i = PyObject_Length(obj); + ERROR_IF(len_i < 0, error); + len_o = PyLong_FromSsize_t(len_i); + ERROR_IF(len_o == NULL, error); } inst(MATCH_CLASS, (subject, type, names -- attrs)) { @@ -2110,15 +2103,11 @@ dummy_func( ERROR_IF(values_or_none == NULL, error); } - // stack effect: ( -- ) - inst(GET_ITER) { + inst(GET_ITER, (iterable -- iter)) { /* before: [obj]; after [getiter(obj)] */ - PyObject *iterable = TOP(); - PyObject *iter = PyObject_GetIter(iterable); - Py_DECREF(iterable); - SET_TOP(iter); - if (iter == NULL) - goto error; + iter = PyObject_GetIter(iterable); + DECREF_INPUTS(); + ERROR_IF(iter == NULL, error); } // stack effect: ( -- ) @@ -2313,10 +2302,10 @@ dummy_func( PREDICT(GET_AWAITABLE); } - // stack effect: ( -- __0) - inst(BEFORE_WITH) { - PyObject *mgr = TOP(); - PyObject *res; + inst(BEFORE_WITH, (mgr -- exit, res)) { + /* pop the context manager, push its __exit__ and the + * value returned from calling its __enter__ + */ PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__enter__)); if (enter == NULL) { if (!_PyErr_Occurred(tstate)) { @@ -2327,7 +2316,7 @@ dummy_func( } goto error; } - PyObject *exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__)); + exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__)); if (exit == NULL) { if (!_PyErr_Occurred(tstate)) { _PyErr_Format(tstate, PyExc_TypeError, @@ -2339,14 +2328,13 @@ dummy_func( Py_DECREF(enter); goto error; } - SET_TOP(exit); - Py_DECREF(mgr); + DECREF_INPUTS(); res = _PyObject_CallNoArgs(enter); Py_DECREF(enter); if (res == NULL) { - goto error; + Py_DECREF(exit); + ERROR_IF(true, error); } - PUSH(res); } inst(WITH_EXCEPT_START, (exit_func, lasti, unused, val -- exit_func, lasti, unused, val, res)) { @@ -2469,8 +2457,7 @@ dummy_func( GO_TO_INSTRUCTION(CALL_PY_EXACT_ARGS); } - // stack effect: ( -- ) - inst(KW_NAMES) { + inst(KW_NAMES, (--)) { assert(kwnames == NULL); assert(oparg < PyTuple_GET_SIZE(consts)); kwnames = GETITEM(consts, oparg); @@ -3252,8 +3239,7 @@ dummy_func( PEEK(oparg) = top; } - // stack effect: ( -- ) - inst(EXTENDED_ARG) { + inst(EXTENDED_ARG, (--)) { assert(oparg); assert(cframe.use_tracing == 0); opcode = _Py_OPCODE(*next_instr); @@ -3262,8 +3248,7 @@ dummy_func( DISPATCH_GOTO(); } - // stack effect: ( -- ) - inst(CACHE) { + inst(CACHE, (--)) { Py_UNREACHABLE(); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 3ee30ae8df9e3c..661fe27f327b33 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2460,16 +2460,15 @@ } TARGET(GET_LEN) { + PyObject *obj = PEEK(1); + PyObject *len_o; // PUSH(len(TOS)) - Py_ssize_t len_i = PyObject_Length(TOP()); - if (len_i < 0) { - goto error; - } - PyObject *len_o = PyLong_FromSsize_t(len_i); - if (len_o == NULL) { - goto error; - } - PUSH(len_o); + Py_ssize_t len_i = PyObject_Length(obj); + if (len_i < 0) goto error; + len_o = PyLong_FromSsize_t(len_i); + if (len_o == NULL) goto error; + STACK_GROW(1); + POKE(1, len_o); DISPATCH(); } @@ -2532,13 +2531,13 @@ } TARGET(GET_ITER) { + PyObject *iterable = PEEK(1); + PyObject *iter; /* before: [obj]; after [getiter(obj)] */ - PyObject *iterable = TOP(); - PyObject *iter = PyObject_GetIter(iterable); + iter = PyObject_GetIter(iterable); Py_DECREF(iterable); - SET_TOP(iter); - if (iter == NULL) - goto error; + if (iter == NULL) goto pop_1_error; + POKE(1, iter); DISPATCH(); } @@ -2736,8 +2735,12 @@ } TARGET(BEFORE_WITH) { - PyObject *mgr = TOP(); + PyObject *mgr = PEEK(1); + PyObject *exit; PyObject *res; + /* pop the context manager, push its __exit__ and the + * value returned from calling its __enter__ + */ PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__enter__)); if (enter == NULL) { if (!_PyErr_Occurred(tstate)) { @@ -2748,7 +2751,7 @@ } goto error; } - PyObject *exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__)); + exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__)); if (exit == NULL) { if (!_PyErr_Occurred(tstate)) { _PyErr_Format(tstate, PyExc_TypeError, @@ -2760,14 +2763,16 @@ Py_DECREF(enter); goto error; } - SET_TOP(exit); Py_DECREF(mgr); res = _PyObject_CallNoArgs(enter); Py_DECREF(enter); if (res == NULL) { - goto error; + Py_DECREF(exit); + if (true) goto pop_1_error; } - PUSH(res); + STACK_GROW(1); + POKE(1, res); + POKE(2, exit); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index e76ddda2f0292d..c40e40ff324d11 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -245,9 +245,9 @@ _PyOpcode_num_popped(int opcode, int oparg) { case JUMP_IF_TRUE_OR_POP: return -1; case JUMP_BACKWARD_NO_INTERRUPT: - return -1; + return 0; case GET_LEN: - return -1; + return 1; case MATCH_CLASS: return 3; case MATCH_MAPPING: @@ -257,7 +257,7 @@ _PyOpcode_num_popped(int opcode, int oparg) { case MATCH_KEYS: return 2; case GET_ITER: - return -1; + return 1; case GET_YIELD_FROM_ITER: return -1; case FOR_ITER: @@ -273,7 +273,7 @@ _PyOpcode_num_popped(int opcode, int oparg) { case BEFORE_ASYNC_WITH: return -1; case BEFORE_WITH: - return -1; + return 1; case WITH_EXCEPT_START: return 4; case PUSH_EXC_INFO: @@ -287,7 +287,7 @@ _PyOpcode_num_popped(int opcode, int oparg) { case CALL_BOUND_METHOD_EXACT_ARGS: return -1; case KW_NAMES: - return -1; + return 0; case CALL: return -1; case CALL_PY_EXACT_ARGS: @@ -339,9 +339,9 @@ _PyOpcode_num_popped(int opcode, int oparg) { case SWAP: return -1; case EXTENDED_ARG: - return -1; + return 0; case CACHE: - return -1; + return 0; default: Py_UNREACHABLE(); } @@ -591,9 +591,9 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case JUMP_IF_TRUE_OR_POP: return -1; case JUMP_BACKWARD_NO_INTERRUPT: - return -1; + return 0; case GET_LEN: - return -1; + return 2; case MATCH_CLASS: return 1; case MATCH_MAPPING: @@ -603,7 +603,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case MATCH_KEYS: return 3; case GET_ITER: - return -1; + return 1; case GET_YIELD_FROM_ITER: return -1; case FOR_ITER: @@ -619,7 +619,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case BEFORE_ASYNC_WITH: return -1; case BEFORE_WITH: - return -1; + return 2; case WITH_EXCEPT_START: return 5; case PUSH_EXC_INFO: @@ -633,7 +633,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case CALL_BOUND_METHOD_EXACT_ARGS: return -1; case KW_NAMES: - return -1; + return 0; case CALL: return -1; case CALL_PY_EXACT_ARGS: @@ -685,9 +685,9 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case SWAP: return -1; case EXTENDED_ARG: - return -1; + return 0; case CACHE: - return -1; + return 0; default: Py_UNREACHABLE(); } From 1a62ae84c687791bc1dfb54d1eb75e1c7277bb04 Mon Sep 17 00:00:00 2001 From: Christophe Nanteuil <35002064+christopheNan@users.noreply.github.com> Date: Tue, 31 Jan 2023 14:29:29 +0100 Subject: [PATCH 043/225] Add JOBS parameter to docs Makefile (#101395) --- Doc/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/Makefile b/Doc/Makefile index 3d484ac3ae7937..ebe7f3698000fb 100644 --- a/Doc/Makefile +++ b/Doc/Makefile @@ -9,6 +9,7 @@ VENVDIR = ./venv SPHINXBUILD = PATH=$(VENVDIR)/bin:$$PATH sphinx-build SPHINXLINT = PATH=$(VENVDIR)/bin:$$PATH sphinx-lint BLURB = PATH=$(VENVDIR)/bin:$$PATH blurb +JOBS = auto PAPER = SOURCES = DISTVERSION = $(shell $(PYTHON) tools/extensions/patchlevel.py) @@ -18,7 +19,7 @@ SPHINXERRORHANDLING = -W PAPEROPT_a4 = -D latex_elements.papersize=a4paper PAPEROPT_letter = -D latex_elements.papersize=letterpaper -ALLSPHINXOPTS = -b $(BUILDER) -d build/doctrees $(PAPEROPT_$(PAPER)) -j auto \ +ALLSPHINXOPTS = -b $(BUILDER) -d build/doctrees $(PAPEROPT_$(PAPER)) -j $(JOBS) \ $(SPHINXOPTS) $(SPHINXERRORHANDLING) . build/$(BUILDER) $(SOURCES) .PHONY: help From df0068ce4827471cc2962631ee64f6f38e818ec4 Mon Sep 17 00:00:00 2001 From: Raj <51259329+workingpayload@users.noreply.github.com> Date: Tue, 31 Jan 2023 20:46:17 +0530 Subject: [PATCH 044/225] gh-99276 - Updated Doc/faq/general.rst (#101396) Co-authored-by: Hugo van Kemenade --- Doc/faq/general.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/faq/general.rst b/Doc/faq/general.rst index 489bca76432d85..6256deb5797c89 100644 --- a/Doc/faq/general.rst +++ b/Doc/faq/general.rst @@ -248,8 +248,8 @@ Are there any published articles about Python that I can reference? It's probably best to cite your favorite book about Python. -The very first article about Python was written in 1991 and is now quite -outdated. +The `very first article `_ about Python was +written in 1991 and is now quite outdated. Guido van Rossum and Jelke de Boer, "Interactively Testing Remote Servers Using the Python Programming Language", CWI Quarterly, Volume 4, Issue 4 From 20c11f2e600e1c0bf42de4d6f2fb3ce5ccc2587c Mon Sep 17 00:00:00 2001 From: Peter Jiping Xie Date: Wed, 1 Feb 2023 02:30:38 +1100 Subject: [PATCH 045/225] gh-101440: fix json snippet error in logging-cookbook.rst (#101439) --- Doc/howto/logging-cookbook.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 26cf40274fd3e2..7661249ad522fa 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -307,7 +307,7 @@ Suppose you configure logging with the following JSON: "class": "logging.StreamHandler", "level": "INFO", "formatter": "simple", - "stream": "ext://sys.stdout", + "stream": "ext://sys.stdout" }, "stderr": { "class": "logging.StreamHandler", From f80db6cef075186f888a85258ccf2164bf148921 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 31 Jan 2023 19:19:11 +0100 Subject: [PATCH 046/225] gh-101469: Optimise get_io_state() by using _PyModule_GetState() (GH-101470) Automerge-Triggered-By: GH:erlend-aasland --- Modules/_io/_iomodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index af5950cf66c178..175fa97479d27d 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -10,6 +10,7 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" #include "_iomodule.h" +#include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_pystate.h" // _PyInterpreterState_GET() #ifdef HAVE_SYS_TYPES_H @@ -560,7 +561,7 @@ PyNumber_AsOff_t(PyObject *item, PyObject *err) static inline _PyIO_State* get_io_state(PyObject *module) { - void *state = PyModule_GetState(module); + void *state = _PyModule_GetState(module); assert(state != NULL); return (_PyIO_State *)state; } From 0062f538d937de55cf3b66b4a8d527b1fe9d5182 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 31 Jan 2023 18:47:50 +0000 Subject: [PATCH 047/225] gh-98831: rewrite BEFORE_ASYNC_WITH and END_ASYNC_FOR in the instruction definition DSL (#101458) --- Python/bytecodes.c | 35 +++++++++++++++-------------------- Python/generated_cases.c.h | 35 +++++++++++++++++++++-------------- Python/opcode_metadata.h | 8 ++++---- 3 files changed, 40 insertions(+), 38 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 5b3bf4ec7ba796..336088e08197de 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -770,18 +770,16 @@ dummy_func( ERROR_IF(val == NULL, error); } - // stack effect: (__0, __1 -- ) - inst(END_ASYNC_FOR) { - PyObject *val = POP(); - assert(val && PyExceptionInstance_Check(val)); - if (PyErr_GivenExceptionMatches(val, PyExc_StopAsyncIteration)) { - Py_DECREF(val); - Py_DECREF(POP()); + inst(END_ASYNC_FOR, (awaitable, exc -- )) { + assert(exc && PyExceptionInstance_Check(exc)); + if (PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration)) { + DECREF_INPUTS(); } else { - PyObject *exc = Py_NewRef(PyExceptionInstance_Class(val)); - PyObject *tb = PyException_GetTraceback(val); - _PyErr_Restore(tstate, exc, val, tb); + Py_INCREF(exc); + PyObject *typ = Py_NewRef(PyExceptionInstance_Class(exc)); + PyObject *tb = PyException_GetTraceback(exc); + _PyErr_Restore(tstate, typ, exc, tb); goto exception_unwind; } } @@ -2266,10 +2264,7 @@ dummy_func( DISPATCH_INLINED(gen_frame); } - // stack effect: ( -- __0) - inst(BEFORE_ASYNC_WITH) { - PyObject *mgr = TOP(); - PyObject *res; + inst(BEFORE_ASYNC_WITH, (mgr -- exit, res)) { PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__aenter__)); if (enter == NULL) { if (!_PyErr_Occurred(tstate)) { @@ -2280,7 +2275,7 @@ dummy_func( } goto error; } - PyObject *exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__)); + exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__)); if (exit == NULL) { if (!_PyErr_Occurred(tstate)) { _PyErr_Format(tstate, PyExc_TypeError, @@ -2292,13 +2287,13 @@ dummy_func( Py_DECREF(enter); goto error; } - SET_TOP(exit); - Py_DECREF(mgr); + DECREF_INPUTS(); res = _PyObject_CallNoArgs(enter); Py_DECREF(enter); - if (res == NULL) - goto error; - PUSH(res); + if (res == NULL) { + Py_DECREF(exit); + ERROR_IF(true, error); + } PREDICT(GET_AWAITABLE); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 661fe27f327b33..d70d64ecbdd5c1 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -981,18 +981,21 @@ } TARGET(END_ASYNC_FOR) { - PyObject *val = POP(); - assert(val && PyExceptionInstance_Check(val)); - if (PyErr_GivenExceptionMatches(val, PyExc_StopAsyncIteration)) { - Py_DECREF(val); - Py_DECREF(POP()); + PyObject *exc = PEEK(1); + PyObject *awaitable = PEEK(2); + assert(exc && PyExceptionInstance_Check(exc)); + if (PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration)) { + Py_DECREF(awaitable); + Py_DECREF(exc); } else { - PyObject *exc = Py_NewRef(PyExceptionInstance_Class(val)); - PyObject *tb = PyException_GetTraceback(val); - _PyErr_Restore(tstate, exc, val, tb); + Py_INCREF(exc); + PyObject *typ = Py_NewRef(PyExceptionInstance_Class(exc)); + PyObject *tb = PyException_GetTraceback(exc); + _PyErr_Restore(tstate, typ, exc, tb); goto exception_unwind; } + STACK_SHRINK(2); DISPATCH(); } @@ -2699,7 +2702,8 @@ } TARGET(BEFORE_ASYNC_WITH) { - PyObject *mgr = TOP(); + PyObject *mgr = PEEK(1); + PyObject *exit; PyObject *res; PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__aenter__)); if (enter == NULL) { @@ -2711,7 +2715,7 @@ } goto error; } - PyObject *exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__)); + exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__)); if (exit == NULL) { if (!_PyErr_Occurred(tstate)) { _PyErr_Format(tstate, PyExc_TypeError, @@ -2723,13 +2727,16 @@ Py_DECREF(enter); goto error; } - SET_TOP(exit); Py_DECREF(mgr); res = _PyObject_CallNoArgs(enter); Py_DECREF(enter); - if (res == NULL) - goto error; - PUSH(res); + if (res == NULL) { + Py_DECREF(exit); + if (true) goto pop_1_error; + } + STACK_GROW(1); + POKE(1, res); + POKE(2, exit); PREDICT(GET_AWAITABLE); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index c40e40ff324d11..171fed363d3be0 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -109,7 +109,7 @@ _PyOpcode_num_popped(int opcode, int oparg) { case PREP_RERAISE_STAR: return 2; case END_ASYNC_FOR: - return -1; + return 2; case CLEANUP_THROW: return -1; case LOAD_ASSERTION_ERROR: @@ -271,7 +271,7 @@ _PyOpcode_num_popped(int opcode, int oparg) { case FOR_ITER_GEN: return -1; case BEFORE_ASYNC_WITH: - return -1; + return 1; case BEFORE_WITH: return 1; case WITH_EXCEPT_START: @@ -455,7 +455,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case PREP_RERAISE_STAR: return 1; case END_ASYNC_FOR: - return -1; + return 0; case CLEANUP_THROW: return -1; case LOAD_ASSERTION_ERROR: @@ -617,7 +617,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case FOR_ITER_GEN: return -1; case BEFORE_ASYNC_WITH: - return -1; + return 2; case BEFORE_WITH: return 2; case WITH_EXCEPT_START: From 2753cf2ed6eb329bdc34b8f67228801182b82160 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 31 Jan 2023 21:42:03 +0100 Subject: [PATCH 048/225] gh-101409: Improve generated clinic code for self type checks (#101411) --- Modules/_io/clinic/bufferedio.c.h | 7 ++--- Modules/_sqlite/clinic/cursor.c.h | 7 ++--- Modules/_sqlite/clinic/row.c.h | 6 ++--- Modules/clinic/_collectionsmodule.c.h | 6 ++--- Modules/clinic/_queuemodule.c.h | 9 +++---- Modules/clinic/_ssl.c.h | 13 +++++---- Modules/clinic/itertoolsmodule.c.h | 38 +++++++++++++-------------- Modules/clinic/selectmodule.c.h | 9 +++---- Objects/clinic/classobject.c.h | 10 +++---- Objects/clinic/codeobject.c.h | 6 ++--- Objects/clinic/enumobject.c.h | 6 ++--- Objects/clinic/floatobject.c.h | 6 ++--- Objects/clinic/listobject.c.h | 7 ++--- Objects/clinic/tupleobject.c.h | 6 ++--- Tools/clinic/clinic.py | 22 +++++++++------- 15 files changed, 80 insertions(+), 78 deletions(-) diff --git a/Modules/_io/clinic/bufferedio.c.h b/Modules/_io/clinic/bufferedio.c.h index 8a8f86b2eea318..38ea756879c122 100644 --- a/Modules/_io/clinic/bufferedio.c.h +++ b/Modules/_io/clinic/bufferedio.c.h @@ -601,12 +601,13 @@ static int _io_BufferedRWPair___init__(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; + PyTypeObject *base_tp = &PyBufferedRWPair_Type; PyObject *reader; PyObject *writer; Py_ssize_t buffer_size = DEFAULT_BUFFER_SIZE; - if ((Py_IS_TYPE(self, &PyBufferedRWPair_Type) || - Py_TYPE(self)->tp_new == PyBufferedRWPair_Type.tp_new) && + if ((Py_IS_TYPE(self, base_tp) || + Py_TYPE(self)->tp_new == base_tp->tp_new) && !_PyArg_NoKeywords("BufferedRWPair", kwargs)) { goto exit; } @@ -713,4 +714,4 @@ _io_BufferedRandom___init__(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=ca87adcfff6a810b input=a9049054013a1b77]*/ +/*[clinic end generated code: output=953f1577e96e8d86 input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/clinic/cursor.c.h b/Modules/_sqlite/clinic/cursor.c.h index 36b8d0051a2915..43e912d1347963 100644 --- a/Modules/_sqlite/clinic/cursor.c.h +++ b/Modules/_sqlite/clinic/cursor.c.h @@ -16,10 +16,11 @@ static int pysqlite_cursor_init(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; + PyTypeObject *base_tp = clinic_state()->CursorType; pysqlite_Connection *connection; - if ((Py_IS_TYPE(self, clinic_state()->CursorType) || - Py_TYPE(self)->tp_new == clinic_state()->CursorType->tp_new) && + if ((Py_IS_TYPE(self, base_tp) || + Py_TYPE(self)->tp_new == base_tp->tp_new) && !_PyArg_NoKeywords("Cursor", kwargs)) { goto exit; } @@ -318,4 +319,4 @@ pysqlite_cursor_close(pysqlite_Cursor *self, PyObject *Py_UNUSED(ignored)) { return pysqlite_cursor_close_impl(self); } -/*[clinic end generated code: output=e53e75a32a9d92bd input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1f82e3c9791bb9a5 input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/clinic/row.c.h b/Modules/_sqlite/clinic/row.c.h index c543b398db3fcf..89a48fd52da226 100644 --- a/Modules/_sqlite/clinic/row.c.h +++ b/Modules/_sqlite/clinic/row.c.h @@ -16,11 +16,11 @@ static PyObject * pysqlite_row_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = clinic_state()->RowType; pysqlite_Cursor *cursor; PyObject *data; - if ((type == clinic_state()->RowType || - type->tp_init == clinic_state()->RowType->tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("Row", kwargs)) { goto exit; } @@ -60,4 +60,4 @@ pysqlite_row_keys(pysqlite_Row *self, PyObject *Py_UNUSED(ignored)) { return pysqlite_row_keys_impl(self); } -/*[clinic end generated code: output=87b91f234633702e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=157b31ac3f6af1ba input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_collectionsmodule.c.h b/Modules/clinic/_collectionsmodule.c.h index ad4da8856ac3c3..8ea0255b061070 100644 --- a/Modules/clinic/_collectionsmodule.c.h +++ b/Modules/clinic/_collectionsmodule.c.h @@ -46,11 +46,11 @@ static PyObject * tuplegetter_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &tuplegetter_type; Py_ssize_t index; PyObject *doc; - if ((type == &tuplegetter_type || - type->tp_init == tuplegetter_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("_tuplegetter", kwargs)) { goto exit; } @@ -75,4 +75,4 @@ tuplegetter_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=12168d58a11a4fb9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=91a0f221c7b1f96c input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_queuemodule.c.h b/Modules/clinic/_queuemodule.c.h index f86dac3c497d64..94fb59a5b17a49 100644 --- a/Modules/clinic/_queuemodule.c.h +++ b/Modules/clinic/_queuemodule.c.h @@ -21,14 +21,13 @@ static PyObject * simplequeue_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = simplequeue_get_state_by_type(type)->SimpleQueueType; - if ((type == simplequeue_get_state_by_type(type)->SimpleQueueType || - type->tp_init == simplequeue_get_state_by_type(type)->SimpleQueueType->tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoPositional("SimpleQueue", args)) { goto exit; } - if ((type == simplequeue_get_state_by_type(type)->SimpleQueueType || - type->tp_init == simplequeue_get_state_by_type(type)->SimpleQueueType->tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("SimpleQueue", kwargs)) { goto exit; } @@ -332,4 +331,4 @@ _queue_SimpleQueue_qsize(simplequeueobject *self, PyObject *Py_UNUSED(ignored)) exit: return return_value; } -/*[clinic end generated code: output=628e992d38f50aac input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9a72a8d1b5767f6a input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_ssl.c.h b/Modules/clinic/_ssl.c.h index 2d7c98c4f014a3..9f967ddc8e3061 100644 --- a/Modules/clinic/_ssl.c.h +++ b/Modules/clinic/_ssl.c.h @@ -435,10 +435,10 @@ static PyObject * _ssl__SSLContext(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = get_state_type(type)->PySSLContext_Type; int proto_version; - if ((type == get_state_type(type)->PySSLContext_Type || - type->tp_init == get_state_type(type)->PySSLContext_Type->tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("_SSLContext", kwargs)) { goto exit; } @@ -1028,14 +1028,13 @@ static PyObject * _ssl_MemoryBIO(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = get_state_type(type)->PySSLMemoryBIO_Type; - if ((type == get_state_type(type)->PySSLMemoryBIO_Type || - type->tp_init == get_state_type(type)->PySSLMemoryBIO_Type->tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoPositional("MemoryBIO", args)) { goto exit; } - if ((type == get_state_type(type)->PySSLMemoryBIO_Type || - type->tp_init == get_state_type(type)->PySSLMemoryBIO_Type->tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("MemoryBIO", kwargs)) { goto exit; } @@ -1543,4 +1542,4 @@ _ssl_enum_crls(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje #ifndef _SSL_ENUM_CRLS_METHODDEF #define _SSL_ENUM_CRLS_METHODDEF #endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */ -/*[clinic end generated code: output=a3d97a19163bb044 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=4d9b81fa81f520f0 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/itertoolsmodule.c.h b/Modules/clinic/itertoolsmodule.c.h index 287de524e91307..70299aceb7a02c 100644 --- a/Modules/clinic/itertoolsmodule.c.h +++ b/Modules/clinic/itertoolsmodule.c.h @@ -102,10 +102,10 @@ static PyObject * pairwise_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &pairwise_type; PyObject *iterable; - if ((type == &pairwise_type || - type->tp_init == pairwise_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("pairwise", kwargs)) { goto exit; } @@ -195,11 +195,11 @@ static PyObject * itertools__grouper(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &_grouper_type; PyObject *parent; PyObject *tgtkey; - if ((type == &_grouper_type || - type->tp_init == _grouper_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("_grouper", kwargs)) { goto exit; } @@ -232,12 +232,12 @@ static PyObject * itertools_teedataobject(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &teedataobject_type; PyObject *it; PyObject *values; PyObject *next; - if ((type == &teedataobject_type || - type->tp_init == teedataobject_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("teedataobject", kwargs)) { goto exit; } @@ -270,10 +270,10 @@ static PyObject * itertools__tee(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &tee_type; PyObject *iterable; - if ((type == &tee_type || - type->tp_init == tee_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("_tee", kwargs)) { goto exit; } @@ -345,10 +345,10 @@ static PyObject * itertools_cycle(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &cycle_type; PyObject *iterable; - if ((type == &cycle_type || - type->tp_init == cycle_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("cycle", kwargs)) { goto exit; } @@ -377,11 +377,11 @@ static PyObject * itertools_dropwhile(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &dropwhile_type; PyObject *func; PyObject *seq; - if ((type == &dropwhile_type || - type->tp_init == dropwhile_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("dropwhile", kwargs)) { goto exit; } @@ -409,11 +409,11 @@ static PyObject * itertools_takewhile(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &takewhile_type; PyObject *func; PyObject *seq; - if ((type == &takewhile_type || - type->tp_init == takewhile_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("takewhile", kwargs)) { goto exit; } @@ -441,11 +441,11 @@ static PyObject * itertools_starmap(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &starmap_type; PyObject *func; PyObject *seq; - if ((type == &starmap_type || - type->tp_init == starmap_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("starmap", kwargs)) { goto exit; } @@ -821,11 +821,11 @@ static PyObject * itertools_filterfalse(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &filterfalse_type; PyObject *func; PyObject *seq; - if ((type == &filterfalse_type || - type->tp_init == filterfalse_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("filterfalse", kwargs)) { goto exit; } @@ -913,4 +913,4 @@ itertools_count(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=0229ebd72962f130 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=47c8c8ccec8740d7 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/selectmodule.c.h b/Modules/clinic/selectmodule.c.h index fda9aaab475567..f44ca1d70a1e86 100644 --- a/Modules/clinic/selectmodule.c.h +++ b/Modules/clinic/selectmodule.c.h @@ -1061,14 +1061,13 @@ static PyObject * select_kqueue(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = _selectstate_by_type(type)->kqueue_queue_Type; - if ((type == _selectstate_by_type(type)->kqueue_queue_Type || - type->tp_init == _selectstate_by_type(type)->kqueue_queue_Type->tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoPositional("kqueue", args)) { goto exit; } - if ((type == _selectstate_by_type(type)->kqueue_queue_Type || - type->tp_init == _selectstate_by_type(type)->kqueue_queue_Type->tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("kqueue", kwargs)) { goto exit; } @@ -1310,4 +1309,4 @@ select_kqueue_control(kqueue_queue_Object *self, PyObject *const *args, Py_ssize #ifndef SELECT_KQUEUE_CONTROL_METHODDEF #define SELECT_KQUEUE_CONTROL_METHODDEF #endif /* !defined(SELECT_KQUEUE_CONTROL_METHODDEF) */ -/*[clinic end generated code: output=9556c7d6cd5192d1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=64516114287e894d input=a9049054013a1b77]*/ diff --git a/Objects/clinic/classobject.c.h b/Objects/clinic/classobject.c.h index 6c449829662af3..a7bac63052bc40 100644 --- a/Objects/clinic/classobject.c.h +++ b/Objects/clinic/classobject.c.h @@ -38,11 +38,11 @@ static PyObject * method_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyMethod_Type; PyObject *function; PyObject *instance; - if ((type == &PyMethod_Type || - type->tp_init == PyMethod_Type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("method", kwargs)) { goto exit; } @@ -70,10 +70,10 @@ static PyObject * instancemethod_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyInstanceMethod_Type; PyObject *function; - if ((type == &PyInstanceMethod_Type || - type->tp_init == PyInstanceMethod_Type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("instancemethod", kwargs)) { goto exit; } @@ -86,4 +86,4 @@ instancemethod_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=e3294c26a71d456d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2a5e7fa5947a86cb input=a9049054013a1b77]*/ diff --git a/Objects/clinic/codeobject.c.h b/Objects/clinic/codeobject.c.h index da33f4a6a20c1b..5ad4b1fed73417 100644 --- a/Objects/clinic/codeobject.c.h +++ b/Objects/clinic/codeobject.c.h @@ -30,6 +30,7 @@ static PyObject * code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyCode_Type; int argcount; int posonlyargcount; int kwonlyargcount; @@ -49,8 +50,7 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *freevars = NULL; PyObject *cellvars = NULL; - if ((type == &PyCode_Type || - type->tp_init == PyCode_Type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("code", kwargs)) { goto exit; } @@ -488,4 +488,4 @@ code__varname_from_oparg(PyCodeObject *self, PyObject *const *args, Py_ssize_t n exit: return return_value; } -/*[clinic end generated code: output=b6c98f17c60ace53 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f1fab6e71c785182 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/enumobject.c.h b/Objects/clinic/enumobject.c.h index 208a9e8be1a1a2..adf78efd0d66f4 100644 --- a/Objects/clinic/enumobject.c.h +++ b/Objects/clinic/enumobject.c.h @@ -91,10 +91,10 @@ static PyObject * reversed_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyReversed_Type; PyObject *seq; - if ((type == &PyReversed_Type || - type->tp_init == PyReversed_Type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("reversed", kwargs)) { goto exit; } @@ -107,4 +107,4 @@ reversed_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=683261097bfd794a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=aba0ddbeab1601e3 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/floatobject.c.h b/Objects/clinic/floatobject.c.h index 1a81e173231bfa..6bc25a0a409f97 100644 --- a/Objects/clinic/floatobject.c.h +++ b/Objects/clinic/floatobject.c.h @@ -212,10 +212,10 @@ static PyObject * float_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyFloat_Type; PyObject *x = NULL; - if ((type == &PyFloat_Type || - type->tp_init == PyFloat_Type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("float", kwargs)) { goto exit; } @@ -327,4 +327,4 @@ float___format__(PyObject *self, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=ac6374ac606a505e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=74bc91bb44014df9 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/listobject.c.h b/Objects/clinic/listobject.c.h index 94852e99617060..e3d6ffa9f76fdb 100644 --- a/Objects/clinic/listobject.c.h +++ b/Objects/clinic/listobject.c.h @@ -326,10 +326,11 @@ static int list___init__(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; + PyTypeObject *base_tp = &PyList_Type; PyObject *iterable = NULL; - if ((Py_IS_TYPE(self, &PyList_Type) || - Py_TYPE(self)->tp_new == PyList_Type.tp_new) && + if ((Py_IS_TYPE(self, base_tp) || + Py_TYPE(self)->tp_new == base_tp->tp_new) && !_PyArg_NoKeywords("list", kwargs)) { goto exit; } @@ -382,4 +383,4 @@ list___reversed__(PyListObject *self, PyObject *Py_UNUSED(ignored)) { return list___reversed___impl(self); } -/*[clinic end generated code: output=4e6f38b655394564 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2ca109d8acc775bc input=a9049054013a1b77]*/ diff --git a/Objects/clinic/tupleobject.c.h b/Objects/clinic/tupleobject.c.h index a4776e14fa0a9b..3de95759a13f21 100644 --- a/Objects/clinic/tupleobject.c.h +++ b/Objects/clinic/tupleobject.c.h @@ -81,10 +81,10 @@ static PyObject * tuple_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyTuple_Type; PyObject *iterable = NULL; - if ((type == &PyTuple_Type || - type->tp_init == PyTuple_Type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("tuple", kwargs)) { goto exit; } @@ -118,4 +118,4 @@ tuple___getnewargs__(PyTupleObject *self, PyObject *Py_UNUSED(ignored)) { return tuple___getnewargs___impl(self); } -/*[clinic end generated code: output=441d2b880e865f87 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=48a9e0834b300ac3 input=a9049054013a1b77]*/ diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index abff4d2583e170..b8b2b75c749152 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -1174,6 +1174,7 @@ def parser_body(prototype, *fields, declarations=''): raise ValueError("Slot methods cannot access their defining class.") if not parses_keywords: + declarations = '{base_type_ptr}' fields.insert(0, normalize_snippet(""" if ({self_type_check}!_PyArg_NoKeywords("{name}", kwargs)) {{ goto exit; @@ -1187,7 +1188,7 @@ def parser_body(prototype, *fields, declarations=''): """, indent=4)) parser_definition = parser_body(parser_prototype, *fields, - declarations=parser_body_declarations) + declarations=declarations) if flags in ('METH_NOARGS', 'METH_O', 'METH_VARARGS'): @@ -3839,21 +3840,22 @@ def set_template_dict(self, template_dict): cls = self.function.cls if ((kind in (METHOD_NEW, METHOD_INIT)) and cls and cls.typedef): - type_object = self.function.cls.type_object - prefix = (type_object[1:] + '.' if type_object[0] == '&' else - type_object + '->') if kind == METHOD_NEW: - type_check = ('({0} == {1} ||\n ' - ' {0}->tp_init == {2}tp_init)' - ).format(self.name, type_object, prefix) + type_check = ( + '({0} == base_tp || {0}->tp_init == base_tp->tp_init)' + ).format(self.name) else: - type_check = ('(Py_IS_TYPE({0}, {1}) ||\n ' - ' Py_TYPE({0})->tp_new == {2}tp_new)' - ).format(self.name, type_object, prefix) + type_check = ('(Py_IS_TYPE({0}, base_tp) ||\n ' + ' Py_TYPE({0})->tp_new == base_tp->tp_new)' + ).format(self.name) line = '{} &&\n '.format(type_check) template_dict['self_type_check'] = line + type_object = self.function.cls.type_object + type_ptr = f'PyTypeObject *base_tp = {type_object};' + template_dict['base_type_ptr'] = type_ptr + def add_c_return_converter(f, name=None): From 76efcb40930d1584e8706f015d0e5475fb16acb5 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Tue, 31 Jan 2023 13:28:32 -0800 Subject: [PATCH 049/225] GH-100288: Skip extra work when failing to specialize LOAD_ATTR (GH-101354) --- Python/specialize.c | 84 +++++++++++++-------------------------------- 1 file changed, 24 insertions(+), 60 deletions(-) diff --git a/Python/specialize.c b/Python/specialize.c index 096687f5fdf023..908ad6dceb57f3 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1039,14 +1039,6 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr, } } -typedef enum { - MANAGED_VALUES = 1, - MANAGED_DICT = 2, - OFFSET_DICT = 3, - NO_DICT = 4, - LAZY_DICT = 5, -} ObjectDictKind; - // Please collect stats carefully before and after modifying. A subtle change // can cause a significant drop in cache hits. A possible test is // python.exe -m test_typing test_re test_dis test_zlib. @@ -1058,71 +1050,45 @@ PyObject *descr, DescriptorClassification kind) PyTypeObject *owner_cls = Py_TYPE(owner); assert(kind == METHOD && descr != NULL); - ObjectDictKind dictkind; - PyDictKeysObject *keys; if (owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT) { PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - keys = ((PyHeapTypeObject *)owner_cls)->ht_cached_keys; - if (_PyDictOrValues_IsValues(dorv)) { - dictkind = MANAGED_VALUES; - } - else { - dictkind = MANAGED_DICT; - } - } - else { - Py_ssize_t dictoffset = owner_cls->tp_dictoffset; - if (dictoffset < 0 || dictoffset > INT16_MAX) { - SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_RANGE); - goto fail; - } - if (dictoffset == 0) { - dictkind = NO_DICT; - keys = NULL; - } - else { - PyObject *dict = *(PyObject **) ((char *)owner + dictoffset); - if (dict == NULL) { - // This object will have a dict if user access __dict__ - dictkind = LAZY_DICT; - keys = NULL; - } - else { - keys = ((PyDictObject *)dict)->ma_keys; - dictkind = OFFSET_DICT; - } + PyDictKeysObject *keys = ((PyHeapTypeObject *)owner_cls)->ht_cached_keys; + if (!_PyDictOrValues_IsValues(dorv)) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_HAS_MANAGED_DICT); + return 0; } - } - if (dictkind == MANAGED_VALUES || dictkind == OFFSET_DICT) { Py_ssize_t index = _PyDictKeys_StringLookup(keys, name); if (index != DKIX_EMPTY) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_SHADOWED); - goto fail; + return 0; } uint32_t keys_version = _PyDictKeys_GetVersionForCurrentState(keys); if (keys_version == 0) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_VERSIONS); - goto fail; + return 0; } write_u32(cache->keys_version, keys_version); + _py_set_opcode(instr, LOAD_ATTR_METHOD_WITH_VALUES); } - switch(dictkind) { - case NO_DICT: + else { + Py_ssize_t dictoffset = owner_cls->tp_dictoffset; + if (dictoffset < 0 || dictoffset > INT16_MAX) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_RANGE); + return 0; + } + if (dictoffset == 0) { _py_set_opcode(instr, LOAD_ATTR_METHOD_NO_DICT); - break; - case MANAGED_VALUES: - _py_set_opcode(instr, LOAD_ATTR_METHOD_WITH_VALUES); - break; - case MANAGED_DICT: - SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_HAS_MANAGED_DICT); - goto fail; - case OFFSET_DICT: - SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_NOT_MANAGED_DICT); - goto fail; - case LAZY_DICT: - assert(owner_cls->tp_dictoffset > 0 && owner_cls->tp_dictoffset <= INT16_MAX); + } + else { + PyObject *dict = *(PyObject **) ((char *)owner + dictoffset); + if (dict) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_NOT_MANAGED_DICT); + return 0; + } + assert(owner_cls->tp_dictoffset > 0); + assert(owner_cls->tp_dictoffset <= INT16_MAX); _py_set_opcode(instr, LOAD_ATTR_METHOD_LAZY_DICT); - break; + } } /* `descr` is borrowed. This is safe for methods (even inherited ones from * super classes!) as long as tp_version_tag is validated for two main reasons: @@ -1141,8 +1107,6 @@ PyObject *descr, DescriptorClassification kind) write_u32(cache->type_version, owner_cls->tp_version_tag); write_obj(cache->descr, descr); return 1; -fail: - return 0; } void From 75227fba1dd1683289d90ada7abba237bff55d14 Mon Sep 17 00:00:00 2001 From: John Belmonte Date: Wed, 1 Feb 2023 19:01:28 +0900 Subject: [PATCH 050/225] datetime.rst: fix combine() signature (#101490) The default `tzinfo` param of the `combine()` signature pseudocode was erroneously `self.tzinfo`. `self` has no meaning in the context of a classmethod, and the datetime class itself has no `tzinfo` attribute. The correct default pseudocode is `time.tzinfo`, reflecting that the default is the `tzinfo` attribute of the `time` parameter. --- Doc/library/datetime.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index ebb5f319efda8d..2f1ab7c3dd4b51 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -975,7 +975,7 @@ Other constructors, all class methods: microsecond of the result are all 0, and :attr:`.tzinfo` is ``None``. -.. classmethod:: datetime.combine(date, time, tzinfo=self.tzinfo) +.. classmethod:: datetime.combine(date, time, tzinfo=time.tzinfo) Return a new :class:`.datetime` object whose date components are equal to the given :class:`date` object's, and whose time components From cc407b9de645ab7c137df8ea2409a005369169a5 Mon Sep 17 00:00:00 2001 From: beavailable Date: Wed, 1 Feb 2023 19:03:59 +0800 Subject: [PATCH 051/225] gh-101317: Add `ssl_shutdown_timeout` parameter for `asyncio.StreamWriter.start_tls` (#101335) Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> --- Doc/library/asyncio-stream.rst | 10 +++++++++- Lib/asyncio/streams.py | 6 ++++-- .../2023-01-26-01-25-56.gh-issue-101317.vWaS1x.rst | 2 ++ 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-01-26-01-25-56.gh-issue-101317.vWaS1x.rst diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst index c1ae8abb9abcd5..533bdec8e03993 100644 --- a/Doc/library/asyncio-stream.rst +++ b/Doc/library/asyncio-stream.rst @@ -335,7 +335,7 @@ StreamWriter returns immediately. .. coroutinemethod:: start_tls(sslcontext, \*, server_hostname=None, \ - ssl_handshake_timeout=None) + ssl_handshake_timeout=None, ssl_shutdown_timeout=None) Upgrade an existing stream-based connection to TLS. @@ -350,8 +350,16 @@ StreamWriter handshake to complete before aborting the connection. ``60.0`` seconds if ``None`` (default). + * *ssl_shutdown_timeout* is the time in seconds to wait for the SSL shutdown + to complete before aborting the connection. ``30.0`` seconds if ``None`` + (default). + .. versionadded:: 3.11 + .. versionchanged:: 3.12 + Added the *ssl_shutdown_timeout* parameter. + + .. method:: is_closing() Return ``True`` if the stream is closed or in the process of diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py index 0f9098b4195633..7d13e961bd2de4 100644 --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -378,7 +378,8 @@ async def drain(self): async def start_tls(self, sslcontext, *, server_hostname=None, - ssl_handshake_timeout=None): + ssl_handshake_timeout=None, + ssl_shutdown_timeout=None): """Upgrade an existing stream-based connection to TLS.""" server_side = self._protocol._client_connected_cb is not None protocol = self._protocol @@ -386,7 +387,8 @@ async def start_tls(self, sslcontext, *, new_transport = await self._loop.start_tls( # type: ignore self._transport, protocol, sslcontext, server_side=server_side, server_hostname=server_hostname, - ssl_handshake_timeout=ssl_handshake_timeout) + ssl_handshake_timeout=ssl_handshake_timeout, + ssl_shutdown_timeout=ssl_shutdown_timeout) self._transport = new_transport protocol._replace_writer(self) diff --git a/Misc/NEWS.d/next/Library/2023-01-26-01-25-56.gh-issue-101317.vWaS1x.rst b/Misc/NEWS.d/next/Library/2023-01-26-01-25-56.gh-issue-101317.vWaS1x.rst new file mode 100644 index 00000000000000..f1ce0e0a527661 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-26-01-25-56.gh-issue-101317.vWaS1x.rst @@ -0,0 +1,2 @@ +Add *ssl_shutdown_timeout* parameter for :meth:`asyncio.StreamWriter.start_tls`. + From 2b3e02a705907d0db2ce5266f06ad88a6b6160db Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 1 Feb 2023 12:41:30 +0100 Subject: [PATCH 052/225] gh-101277: Isolate itertools, add group and _grouper types to module state (#101302) Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> --- Lib/test/test_itertools.py | 32 ++++ Modules/clinic/itertoolsmodule.c.h | 8 +- Modules/itertoolsmodule.c | 244 ++++++++++++++++------------- 3 files changed, 173 insertions(+), 111 deletions(-) diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index b447b6cbab9c22..7014bc97100cb4 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -1694,6 +1694,38 @@ def test_zip_longest_result_gc(self): gc.collect() self.assertTrue(gc.is_tracked(next(it))) + @support.cpython_only + def test_immutable_types(self): + from itertools import _grouper, _tee, _tee_dataobject + dataset = ( + accumulate, + batched, + chain, + combinations, + combinations_with_replacement, + compress, + count, + cycle, + dropwhile, + filterfalse, + groupby, + _grouper, + islice, + pairwise, + permutations, + product, + repeat, + starmap, + takewhile, + _tee, + _tee_dataobject, + zip_longest, + ) + for tp in dataset: + with self.subTest(tp=tp): + with self.assertRaisesRegex(TypeError, "immutable"): + tp.foobar = 1 + class TestExamples(unittest.TestCase): diff --git a/Modules/clinic/itertoolsmodule.c.h b/Modules/clinic/itertoolsmodule.c.h index 70299aceb7a02c..c492c33daea5a2 100644 --- a/Modules/clinic/itertoolsmodule.c.h +++ b/Modules/clinic/itertoolsmodule.c.h @@ -195,7 +195,7 @@ static PyObject * itertools__grouper(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - PyTypeObject *base_tp = &_grouper_type; + PyTypeObject *base_tp = clinic_state()->_grouper_type; PyObject *parent; PyObject *tgtkey; @@ -206,8 +206,8 @@ itertools__grouper(PyTypeObject *type, PyObject *args, PyObject *kwargs) if (!_PyArg_CheckPositional("_grouper", PyTuple_GET_SIZE(args), 2, 2)) { goto exit; } - if (!PyObject_TypeCheck(PyTuple_GET_ITEM(args, 0), &groupby_type)) { - _PyArg_BadArgument("_grouper", "argument 1", (&groupby_type)->tp_name, PyTuple_GET_ITEM(args, 0)); + if (!PyObject_TypeCheck(PyTuple_GET_ITEM(args, 0), clinic_state_by_cls()->groupby_type)) { + _PyArg_BadArgument("_grouper", "argument 1", (clinic_state_by_cls()->groupby_type)->tp_name, PyTuple_GET_ITEM(args, 0)); goto exit; } parent = PyTuple_GET_ITEM(args, 0); @@ -913,4 +913,4 @@ itertools_count(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=47c8c8ccec8740d7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c3069caac417e165 input=a9049054013a1b77]*/ diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index c1f1e7320db719..a2ee48228b5847 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -2,6 +2,7 @@ #include "Python.h" #include "pycore_call.h" // _PyObject_CallNoArgs() #include "pycore_long.h" // _PyLong_GetZero() +#include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_object.h" // _PyObject_GC_TRACK() #include "pycore_tuple.h" // _PyTuple_ITEMS() #include // offsetof() @@ -10,10 +11,42 @@ by Raymond D. Hettinger */ +typedef struct { + PyTypeObject *groupby_type; + PyTypeObject *_grouper_type; +} itertools_state; + +static inline itertools_state * +get_module_state(PyObject *mod) +{ + void *state = _PyModule_GetState(mod); + assert(state != NULL); + return (itertools_state *)state; +} + +static inline itertools_state * +get_module_state_by_cls(PyTypeObject *cls) +{ + void *state = PyType_GetModuleState(cls); + assert(state != NULL); + return (itertools_state *)state; +} + +static struct PyModuleDef itertoolsmodule; + +static inline itertools_state * +find_state_by_type(PyTypeObject *tp) +{ + PyObject *mod = PyType_GetModuleByDef(tp, &itertoolsmodule); + assert(mod != NULL); + return get_module_state(mod); +} +#define clinic_state() (find_state_by_type(type)) + /*[clinic input] module itertools -class itertools.groupby "groupbyobject *" "&groupby_type" -class itertools._grouper "_grouperobject *" "&_grouper_type" +class itertools.groupby "groupbyobject *" "clinic_state()->groupby_type" +class itertools._grouper "_grouperobject *" "clinic_state()->_grouper_type" class itertools.teedataobject "teedataobject *" "&teedataobject_type" class itertools._tee "teeobject *" "&tee_type" class itertools.batched "batchedobject *" "&batched_type" @@ -31,10 +64,8 @@ class itertools.filterfalse "filterfalseobject *" "&filterfalse_type" class itertools.count "countobject *" "&count_type" class itertools.pairwise "pairwiseobject *" "&pairwise_type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=1168b274011ce21b]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=424108522584b55b]*/ -static PyTypeObject groupby_type; -static PyTypeObject _grouper_type; static PyTypeObject teedataobject_type; static PyTypeObject tee_type; static PyTypeObject batched_type; @@ -51,7 +82,10 @@ static PyTypeObject filterfalse_type; static PyTypeObject count_type; static PyTypeObject pairwise_type; +#define clinic_state_by_cls() (get_module_state_by_cls(base_tp)) #include "clinic/itertoolsmodule.c.h" +#undef clinic_state_by_cls +#undef clinic_state /* batched object ************************************************************/ @@ -372,6 +406,7 @@ typedef struct { PyObject *currkey; PyObject *currvalue; const void *currgrouper; /* borrowed reference */ + itertools_state *state; } groupbyobject; static PyObject *_grouper_create(groupbyobject *, PyObject *); @@ -408,24 +443,28 @@ itertools_groupby_impl(PyTypeObject *type, PyObject *it, PyObject *keyfunc) Py_DECREF(gbo); return NULL; } + gbo->state = find_state_by_type(type); return (PyObject *)gbo; } static void groupby_dealloc(groupbyobject *gbo) { + PyTypeObject *tp = Py_TYPE(gbo); PyObject_GC_UnTrack(gbo); Py_XDECREF(gbo->it); Py_XDECREF(gbo->keyfunc); Py_XDECREF(gbo->tgtkey); Py_XDECREF(gbo->currkey); Py_XDECREF(gbo->currvalue); - Py_TYPE(gbo)->tp_free(gbo); + tp->tp_free(gbo); + Py_DECREF(tp); } static int groupby_traverse(groupbyobject *gbo, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(gbo)); Py_VISIT(gbo->it); Py_VISIT(gbo->keyfunc); Py_VISIT(gbo->tgtkey); @@ -546,50 +585,26 @@ static PyMethodDef groupby_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject groupby_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.groupby", /* tp_name */ - sizeof(groupbyobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)groupby_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - itertools_groupby__doc__, /* tp_doc */ - (traverseproc)groupby_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)groupby_next, /* tp_iternext */ - groupby_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_groupby, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot groupby_slots[] = { + {Py_tp_dealloc, groupby_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_groupby__doc__}, + {Py_tp_traverse, groupby_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, groupby_next}, + {Py_tp_methods, groupby_methods}, + {Py_tp_new, itertools_groupby}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, }; +static PyType_Spec groupby_spec = { + .name = "itertools.groupby", + .basicsize= sizeof(groupbyobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = groupby_slots, +}; /* _grouper object (internal) ************************************************/ @@ -603,7 +618,7 @@ typedef struct { @classmethod itertools._grouper.__new__ - parent: object(subclass_of='&groupby_type') + parent: object(subclass_of='clinic_state_by_cls()->groupby_type') tgtkey: object / [clinic start generated code]*/ @@ -611,7 +626,7 @@ itertools._grouper.__new__ static PyObject * itertools__grouper_impl(PyTypeObject *type, PyObject *parent, PyObject *tgtkey) -/*[clinic end generated code: output=462efb1cdebb5914 input=dc180d7771fc8c59]*/ +/*[clinic end generated code: output=462efb1cdebb5914 input=afe05eb477118f12]*/ { return _grouper_create((groupbyobject*) parent, tgtkey); } @@ -619,9 +634,8 @@ itertools__grouper_impl(PyTypeObject *type, PyObject *parent, static PyObject * _grouper_create(groupbyobject *parent, PyObject *tgtkey) { - _grouperobject *igo; - - igo = PyObject_GC_New(_grouperobject, &_grouper_type); + itertools_state *state = parent->state; + _grouperobject *igo = PyObject_GC_New(_grouperobject, state->_grouper_type); if (igo == NULL) return NULL; igo->parent = Py_NewRef(parent); @@ -635,15 +649,18 @@ _grouper_create(groupbyobject *parent, PyObject *tgtkey) static void _grouper_dealloc(_grouperobject *igo) { + PyTypeObject *tp = Py_TYPE(igo); PyObject_GC_UnTrack(igo); Py_DECREF(igo->parent); Py_DECREF(igo->tgtkey); PyObject_GC_Del(igo); + Py_DECREF(tp); } static int _grouper_traverse(_grouperobject *igo, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(igo)); Py_VISIT(igo->parent); Py_VISIT(igo->tgtkey); return 0; @@ -691,48 +708,24 @@ static PyMethodDef _grouper_methods[] = { {NULL, NULL} /* sentinel */ }; +static PyType_Slot _grouper_slots[] = { + {Py_tp_dealloc, _grouper_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_traverse, _grouper_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, _grouper_next}, + {Py_tp_methods, _grouper_methods}, + {Py_tp_new, itertools__grouper}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; -static PyTypeObject _grouper_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools._grouper", /* tp_name */ - sizeof(_grouperobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)_grouper_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - 0, /* tp_doc */ - (traverseproc)_grouper_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)_grouper_next, /* tp_iternext */ - _grouper_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools__grouper, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Spec _grouper_spec = { + .name = "itertools._grouper", + .basicsize = sizeof(_grouperobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = _grouper_slots, }; @@ -4979,8 +4972,47 @@ combinations_with_replacement(p, r)\n\ "); static int -itertoolsmodule_exec(PyObject *m) +itertoolsmodule_traverse(PyObject *mod, visitproc visit, void *arg) +{ + itertools_state *state = get_module_state(mod); + Py_VISIT(state->groupby_type); + Py_VISIT(state->_grouper_type); + return 0; +} + +static int +itertoolsmodule_clear(PyObject *mod) +{ + itertools_state *state = get_module_state(mod); + Py_CLEAR(state->groupby_type); + Py_CLEAR(state->_grouper_type); + return 0; +} + +static void +itertoolsmodule_free(void *mod) +{ + (void)itertoolsmodule_clear((PyObject *)mod); +} + +#define ADD_TYPE(module, type, spec) \ +do { \ + type = (PyTypeObject *)PyType_FromModuleAndSpec(module, spec, NULL); \ + if (type == NULL) { \ + return -1; \ + } \ + if (PyModule_AddType(module, type) < 0) { \ + return -1; \ + } \ +} while (0) + +static int +itertoolsmodule_exec(PyObject *mod) { + itertools_state *state = get_module_state(mod); + ADD_TYPE(mod, state->groupby_type, &groupby_spec); + ADD_TYPE(mod, state->_grouper_type, &_grouper_spec); + PyTypeObject *typelist[] = { &accumulate_type, &batched_type, @@ -5000,8 +5032,6 @@ itertoolsmodule_exec(PyObject *m) &permutations_type, &product_type, &repeat_type, - &groupby_type, - &_grouper_type, &tee_type, &teedataobject_type }; @@ -5009,7 +5039,7 @@ itertoolsmodule_exec(PyObject *m) Py_SET_TYPE(&teedataobject_type, &PyType_Type); for (size_t i = 0; i < Py_ARRAY_LENGTH(typelist); i++) { - if (PyModule_AddType(m, typelist[i]) < 0) { + if (PyModule_AddType(mod, typelist[i]) < 0) { return -1; } } @@ -5029,15 +5059,15 @@ static PyMethodDef module_methods[] = { static struct PyModuleDef itertoolsmodule = { - PyModuleDef_HEAD_INIT, - "itertools", - module_doc, - 0, - module_methods, - itertoolsmodule_slots, - NULL, - NULL, - NULL + .m_base = PyModuleDef_HEAD_INIT, + .m_name = "itertools", + .m_doc = module_doc, + .m_size = sizeof(itertools_state), + .m_methods = module_methods, + .m_slots = itertoolsmodule_slots, + .m_traverse = itertoolsmodule_traverse, + .m_clear = itertoolsmodule_clear, + .m_free = itertoolsmodule_free, }; PyMODINIT_FUNC From 62251c3da06eb4662502295697f39730565b1717 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Wed, 1 Feb 2023 12:49:59 +0000 Subject: [PATCH 053/225] gh-101454: fix documentation for END_ASYNC_FOR (#101455) --- Doc/library/dis.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 6a68ec4b14be31..1fe2d5d6227d61 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -616,10 +616,9 @@ not have to be) the original ``STACK[-2]``. .. opcode:: END_ASYNC_FOR Terminates an :keyword:`async for` loop. Handles an exception raised - when awaiting a next item. If ``STACK[-1]`` is :exc:`StopAsyncIteration` pop 3 - values from the stack and restore the exception state using the second - of them. Otherwise re-raise the exception using the value - from the stack. An exception handler block is removed from the block stack. + when awaiting a next item. The stack contains the async iterable in + ``STACK[-2]`` and the raised exception in ``STACK[-1]``. Both are popped. + If the exception is not :exc:`StopAsyncIteration`, it is re-raised. .. versionadded:: 3.8 From 95fb0e02582b5673eff49694eb0dce1d7df52301 Mon Sep 17 00:00:00 2001 From: Raj <51259329+workingpayload@users.noreply.github.com> Date: Wed, 1 Feb 2023 23:38:31 +0530 Subject: [PATCH 054/225] gh-101498 : Fix asyncio.Timeout example in docs (#101499) Doc/library/asyncio-task.rst#timeout --- Doc/library/asyncio-task.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index 9a42b963167685..39112580285cc0 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -666,7 +666,7 @@ Timeouts except TimeoutError: pass - if cm.expired: + if cm.expired(): print("Looks like we haven't finished on time.") Timeout context managers can be safely nested. From 7840ff3cdbdf64f517c9f981f57eff232e676104 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 1 Feb 2023 10:56:52 -0800 Subject: [PATCH 055/225] gh-98831: Modernize the LOAD_ATTR family (#101488) --- Python/bytecodes.c | 177 ++++++++++++--------------------- Python/generated_cases.c.h | 198 +++++++++++++++++++++---------------- Python/opcode_metadata.h | 60 +++++------ 3 files changed, 205 insertions(+), 230 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 336088e08197de..bb1deaf3fbeb4b 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -436,7 +436,7 @@ dummy_func( PREDICT(JUMP_BACKWARD); } - family(store_subscr) = { + family(store_subscr, INLINE_CACHE_ENTRIES_STORE_SUBSCR) = { STORE_SUBSCR, STORE_SUBSCR_DICT, STORE_SUBSCR_LIST_INT, @@ -950,7 +950,7 @@ dummy_func( Py_DECREF(seq); } - family(store_attr) = { + family(store_attr, INLINE_CACHE_ENTRIES_STORE_ATTR) = { STORE_ATTR, STORE_ATTR_INSTANCE_VALUE, STORE_ATTR_SLOT, @@ -1436,6 +1436,20 @@ dummy_func( PREDICT(JUMP_BACKWARD); } + family(load_attr, INLINE_CACHE_ENTRIES_LOAD_ATTR) = { + LOAD_ATTR, + LOAD_ATTR_INSTANCE_VALUE, + LOAD_ATTR_MODULE, + LOAD_ATTR_WITH_HINT, + LOAD_ATTR_SLOT, + LOAD_ATTR_CLASS, + LOAD_ATTR_PROPERTY, + LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN, + LOAD_ATTR_METHOD_WITH_VALUES, + LOAD_ATTR_METHOD_NO_DICT, + LOAD_ATTR_METHOD_LAZY_DICT, + }; + inst(LOAD_ATTR, (unused/9, owner -- res2 if (oparg & 1), res)) { #if ENABLE_SPECIALIZATION _PyAttrCache *cache = (_PyAttrCache *)next_instr; @@ -1485,64 +1499,43 @@ dummy_func( } } - // error: LOAD_ATTR has irregular stack effect - inst(LOAD_ATTR_INSTANCE_VALUE) { + inst(LOAD_ATTR_INSTANCE_VALUE, (unused/1, type_version/2, index/1, unused/5, owner -- res2 if (oparg & 1), res)) { assert(cframe.use_tracing == 0); - PyObject *owner = TOP(); - PyObject *res; PyTypeObject *tp = Py_TYPE(owner); - _PyAttrCache *cache = (_PyAttrCache *)next_instr; - uint32_t type_version = read_u32(cache->version); assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); assert(tp->tp_dictoffset < 0); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR); - res = _PyDictOrValues_GetValues(dorv)->values[cache->index]; + res = _PyDictOrValues_GetValues(dorv)->values[index]; DEOPT_IF(res == NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(res); - SET_TOP(NULL); - STACK_GROW((oparg & 1)); - SET_TOP(res); + res2 = NULL; Py_DECREF(owner); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); } - // error: LOAD_ATTR has irregular stack effect - inst(LOAD_ATTR_MODULE) { + inst(LOAD_ATTR_MODULE, (unused/1, type_version/2, index/1, unused/5, owner -- res2 if (oparg & 1), res)) { assert(cframe.use_tracing == 0); - PyObject *owner = TOP(); - PyObject *res; - _PyAttrCache *cache = (_PyAttrCache *)next_instr; DEOPT_IF(!PyModule_CheckExact(owner), LOAD_ATTR); PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; assert(dict != NULL); - DEOPT_IF(dict->ma_keys->dk_version != read_u32(cache->version), - LOAD_ATTR); + DEOPT_IF(dict->ma_keys->dk_version != type_version, LOAD_ATTR); assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); - assert(cache->index < dict->ma_keys->dk_nentries); - PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + cache->index; + assert(index < dict->ma_keys->dk_nentries); + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index; res = ep->me_value; DEOPT_IF(res == NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(res); - SET_TOP(NULL); - STACK_GROW((oparg & 1)); - SET_TOP(res); + res2 = NULL; Py_DECREF(owner); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); } - // error: LOAD_ATTR has irregular stack effect - inst(LOAD_ATTR_WITH_HINT) { + inst(LOAD_ATTR_WITH_HINT, (unused/1, type_version/2, index/1, unused/5, owner -- res2 if (oparg & 1), res)) { assert(cframe.use_tracing == 0); - PyObject *owner = TOP(); - PyObject *res; PyTypeObject *tp = Py_TYPE(owner); - _PyAttrCache *cache = (_PyAttrCache *)next_instr; - uint32_t type_version = read_u32(cache->version); assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); @@ -1552,7 +1545,7 @@ dummy_func( DEOPT_IF(dict == NULL, LOAD_ATTR); assert(PyDict_CheckExact((PyObject *)dict)); PyObject *name = GETITEM(names, oparg>>1); - uint16_t hint = cache->index; + uint16_t hint = index; DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR); if (DK_IS_UNICODE(dict->ma_keys)) { PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; @@ -1567,73 +1560,49 @@ dummy_func( DEOPT_IF(res == NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(res); - SET_TOP(NULL); - STACK_GROW((oparg & 1)); - SET_TOP(res); + res2 = NULL; Py_DECREF(owner); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); } - // error: LOAD_ATTR has irregular stack effect - inst(LOAD_ATTR_SLOT) { + inst(LOAD_ATTR_SLOT, (unused/1, type_version/2, index/1, unused/5, owner -- res2 if (oparg & 1), res)) { assert(cframe.use_tracing == 0); - PyObject *owner = TOP(); - PyObject *res; PyTypeObject *tp = Py_TYPE(owner); - _PyAttrCache *cache = (_PyAttrCache *)next_instr; - uint32_t type_version = read_u32(cache->version); assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); - char *addr = (char *)owner + cache->index; + char *addr = (char *)owner + index; res = *(PyObject **)addr; DEOPT_IF(res == NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(res); - SET_TOP(NULL); - STACK_GROW((oparg & 1)); - SET_TOP(res); + res2 = NULL; Py_DECREF(owner); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); } - // error: LOAD_ATTR has irregular stack effect - inst(LOAD_ATTR_CLASS) { + inst(LOAD_ATTR_CLASS, (unused/1, type_version/2, unused/2, descr/4, cls -- res2 if (oparg & 1), res)) { assert(cframe.use_tracing == 0); - _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; - PyObject *cls = TOP(); DEOPT_IF(!PyType_Check(cls), LOAD_ATTR); - uint32_t type_version = read_u32(cache->type_version); DEOPT_IF(((PyTypeObject *)cls)->tp_version_tag != type_version, LOAD_ATTR); assert(type_version != 0); STAT_INC(LOAD_ATTR, hit); - PyObject *res = read_obj(cache->descr); + res2 = NULL; + res = descr; assert(res != NULL); Py_INCREF(res); - SET_TOP(NULL); - STACK_GROW((oparg & 1)); - SET_TOP(res); Py_DECREF(cls); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); } - // error: LOAD_ATTR has irregular stack effect - inst(LOAD_ATTR_PROPERTY) { + inst(LOAD_ATTR_PROPERTY, (unused/1, type_version/2, func_version/2, fget/4, owner -- unused if (oparg & 1), unused)) { assert(cframe.use_tracing == 0); DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); - _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; - PyObject *owner = TOP(); PyTypeObject *cls = Py_TYPE(owner); - uint32_t type_version = read_u32(cache->type_version); DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR); assert(type_version != 0); - PyObject *fget = read_obj(cache->descr); assert(Py_IS_TYPE(fget, &PyFunction_Type)); PyFunctionObject *f = (PyFunctionObject *)fget; - uint32_t func_version = read_u32(cache->keys_version); assert(func_version != 0); DEOPT_IF(f->func_version != func_version, LOAD_ATTR); PyCodeObject *code = (PyCodeObject *)f->func_code; @@ -1642,6 +1611,7 @@ dummy_func( STAT_INC(LOAD_ATTR, hit); Py_INCREF(fget); _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 1); + // Manipulate stack directly because we exit with DISPATCH_INLINED(). SET_TOP(NULL); int shrink_stack = !(oparg & 1); STACK_SHRINK(shrink_stack); @@ -1650,20 +1620,14 @@ dummy_func( DISPATCH_INLINED(new_frame); } - // error: LOAD_ATTR has irregular stack effect - inst(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) { + inst(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN, (unused/1, type_version/2, func_version/2, getattribute/4, owner -- unused if (oparg & 1), unused)) { assert(cframe.use_tracing == 0); DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); - _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; - PyObject *owner = TOP(); PyTypeObject *cls = Py_TYPE(owner); - uint32_t type_version = read_u32(cache->type_version); DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR); assert(type_version != 0); - PyObject *getattribute = read_obj(cache->descr); assert(Py_IS_TYPE(getattribute, &PyFunction_Type)); PyFunctionObject *f = (PyFunctionObject *)getattribute; - uint32_t func_version = read_u32(cache->keys_version); assert(func_version != 0); DEOPT_IF(f->func_version != func_version, LOAD_ATTR); PyCodeObject *code = (PyCodeObject *)f->func_code; @@ -1674,6 +1638,7 @@ dummy_func( PyObject *name = GETITEM(names, oparg >> 1); Py_INCREF(f); _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 2); + // Manipulate stack directly because we exit with DISPATCH_INLINED(). SET_TOP(NULL); int shrink_stack = !(oparg & 1); STACK_SHRINK(shrink_stack); @@ -1768,6 +1733,7 @@ dummy_func( ERROR_IF(res == NULL, error); } + // No cache size here, since this is a family of super-instructions. family(compare_and_branch) = { COMPARE_AND_BRANCH, COMPARE_AND_BRANCH_FLOAT, @@ -2373,14 +2339,10 @@ dummy_func( } - // error: LOAD_ATTR has irregular stack effect - inst(LOAD_ATTR_METHOD_WITH_VALUES) { + inst(LOAD_ATTR_METHOD_WITH_VALUES, (unused/1, type_version/2, keys_version/2, descr/4, self -- res2 if (oparg & 1), res)) { /* Cached method object */ assert(cframe.use_tracing == 0); - PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); - _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; - uint32_t type_version = read_u32(cache->type_version); assert(type_version != 0); DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR); assert(self_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT); @@ -2388,41 +2350,31 @@ dummy_func( DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR); PyHeapTypeObject *self_heap_type = (PyHeapTypeObject *)self_cls; DEOPT_IF(self_heap_type->ht_cached_keys->dk_version != - read_u32(cache->keys_version), LOAD_ATTR); + keys_version, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); - PyObject *res = read_obj(cache->descr); - assert(res != NULL); - assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)); - SET_TOP(Py_NewRef(res)); - PUSH(self); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); + assert(descr != NULL); + res2 = Py_NewRef(descr); + assert(_PyType_HasFeature(Py_TYPE(res2), Py_TPFLAGS_METHOD_DESCRIPTOR)); + res = self; + assert(oparg & 1); } - // error: LOAD_ATTR has irregular stack effect - inst(LOAD_ATTR_METHOD_NO_DICT) { + inst(LOAD_ATTR_METHOD_NO_DICT, (unused/1, type_version/2, unused/2, descr/4, self -- res2 if (oparg & 1), res)) { assert(cframe.use_tracing == 0); - PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); - _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; - uint32_t type_version = read_u32(cache->type_version); DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR); assert(self_cls->tp_dictoffset == 0); STAT_INC(LOAD_ATTR, hit); - PyObject *res = read_obj(cache->descr); - assert(res != NULL); - assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)); - SET_TOP(Py_NewRef(res)); - PUSH(self); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + res2 = Py_NewRef(descr); + res = self; + assert(oparg & 1); } - // error: LOAD_ATTR has irregular stack effect - inst(LOAD_ATTR_METHOD_LAZY_DICT) { + inst(LOAD_ATTR_METHOD_LAZY_DICT, (unused/1, type_version/2, unused/2, descr/4, self -- res2 if (oparg & 1), res)) { assert(cframe.use_tracing == 0); - PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); - _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; - uint32_t type_version = read_u32(cache->type_version); DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR); Py_ssize_t dictoffset = self_cls->tp_dictoffset; assert(dictoffset > 0); @@ -2430,12 +2382,11 @@ dummy_func( /* This object has a __dict__, just not yet created */ DEOPT_IF(dict != NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); - PyObject *res = read_obj(cache->descr); - assert(res != NULL); - assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)); - SET_TOP(Py_NewRef(res)); - PUSH(self); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + res2 = Py_NewRef(descr); + res = self; + assert(oparg & 1); } // stack effect: (__0, __array[oparg] -- ) @@ -3265,7 +3216,7 @@ dummy_func( // Future families go below this point // -family(call) = { +family(call, INLINE_CACHE_ENTRIES_CALL) = { CALL, CALL_PY_EXACT_ARGS, CALL_PY_WITH_DEFAULTS, CALL_BOUND_METHOD_EXACT_ARGS, CALL_BUILTIN_CLASS, CALL_BUILTIN_FAST_WITH_KEYWORDS, CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, CALL_NO_KW_BUILTIN_FAST, @@ -3273,19 +3224,13 @@ family(call) = { CALL_NO_KW_LIST_APPEND, CALL_NO_KW_METHOD_DESCRIPTOR_FAST, CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, CALL_NO_KW_METHOD_DESCRIPTOR_O, CALL_NO_KW_STR_1, CALL_NO_KW_TUPLE_1, CALL_NO_KW_TYPE_1 }; -family(for_iter) = { +family(for_iter, INLINE_CACHE_ENTRIES_FOR_ITER) = { FOR_ITER, FOR_ITER_LIST, FOR_ITER_RANGE }; -family(load_attr) = { - LOAD_ATTR, LOAD_ATTR_CLASS, - LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN, LOAD_ATTR_INSTANCE_VALUE, LOAD_ATTR_MODULE, - LOAD_ATTR_PROPERTY, LOAD_ATTR_SLOT, LOAD_ATTR_WITH_HINT, - LOAD_ATTR_METHOD_LAZY_DICT, LOAD_ATTR_METHOD_NO_DICT, - LOAD_ATTR_METHOD_WITH_VALUES }; -family(load_global) = { +family(load_global, INLINE_CACHE_ENTRIES_LOAD_GLOBAL) = { LOAD_GLOBAL, LOAD_GLOBAL_BUILTIN, LOAD_GLOBAL_MODULE }; family(store_fast) = { STORE_FAST, STORE_FAST__LOAD_FAST, STORE_FAST__STORE_FAST }; -family(unpack_sequence) = { +family(unpack_sequence, INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE) = { UNPACK_SEQUENCE, UNPACK_SEQUENCE_LIST, UNPACK_SEQUENCE_TUPLE, UNPACK_SEQUENCE_TWO_TUPLE }; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index d70d64ecbdd5c1..e5c5c7e557a37c 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -596,6 +596,7 @@ TARGET(STORE_SUBSCR) { PREDICTED(STORE_SUBSCR); + static_assert(INLINE_CACHE_ENTRIES_STORE_SUBSCR == 1, "incorrect cache size"); PyObject *sub = PEEK(1); PyObject *container = PEEK(2); PyObject *v = PEEK(3); @@ -1180,6 +1181,7 @@ TARGET(STORE_ATTR) { PREDICTED(STORE_ATTR); + static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); PyObject *owner = PEEK(1); PyObject *v = PEEK(2); uint16_t counter = read_u16(&next_instr[0].cache); @@ -1748,6 +1750,7 @@ TARGET(LOAD_ATTR) { PREDICTED(LOAD_ATTR); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); PyObject *owner = PEEK(1); PyObject *res2 = NULL; PyObject *res; @@ -1805,62 +1808,67 @@ } TARGET(LOAD_ATTR_INSTANCE_VALUE) { - assert(cframe.use_tracing == 0); - PyObject *owner = TOP(); + PyObject *owner = PEEK(1); + PyObject *res2 = NULL; PyObject *res; + uint32_t type_version = read_u32(&next_instr[1].cache); + uint16_t index = read_u16(&next_instr[3].cache); + assert(cframe.use_tracing == 0); PyTypeObject *tp = Py_TYPE(owner); - _PyAttrCache *cache = (_PyAttrCache *)next_instr; - uint32_t type_version = read_u32(cache->version); assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); assert(tp->tp_dictoffset < 0); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR); - res = _PyDictOrValues_GetValues(dorv)->values[cache->index]; + res = _PyDictOrValues_GetValues(dorv)->values[index]; DEOPT_IF(res == NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(res); - SET_TOP(NULL); - STACK_GROW((oparg & 1)); - SET_TOP(res); + res2 = NULL; Py_DECREF(owner); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); + STACK_GROW(((oparg & 1) ? 1 : 0)); + POKE(1, res); + if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } + JUMPBY(9); DISPATCH(); } TARGET(LOAD_ATTR_MODULE) { - assert(cframe.use_tracing == 0); - PyObject *owner = TOP(); + PyObject *owner = PEEK(1); + PyObject *res2 = NULL; PyObject *res; - _PyAttrCache *cache = (_PyAttrCache *)next_instr; + uint32_t type_version = read_u32(&next_instr[1].cache); + uint16_t index = read_u16(&next_instr[3].cache); + assert(cframe.use_tracing == 0); DEOPT_IF(!PyModule_CheckExact(owner), LOAD_ATTR); PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; assert(dict != NULL); - DEOPT_IF(dict->ma_keys->dk_version != read_u32(cache->version), - LOAD_ATTR); + DEOPT_IF(dict->ma_keys->dk_version != type_version, LOAD_ATTR); assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); - assert(cache->index < dict->ma_keys->dk_nentries); - PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + cache->index; + assert(index < dict->ma_keys->dk_nentries); + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index; res = ep->me_value; DEOPT_IF(res == NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(res); - SET_TOP(NULL); - STACK_GROW((oparg & 1)); - SET_TOP(res); + res2 = NULL; Py_DECREF(owner); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); + STACK_GROW(((oparg & 1) ? 1 : 0)); + POKE(1, res); + if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } + JUMPBY(9); DISPATCH(); } TARGET(LOAD_ATTR_WITH_HINT) { - assert(cframe.use_tracing == 0); - PyObject *owner = TOP(); + PyObject *owner = PEEK(1); + PyObject *res2 = NULL; PyObject *res; + uint32_t type_version = read_u32(&next_instr[1].cache); + uint16_t index = read_u16(&next_instr[3].cache); + assert(cframe.use_tracing == 0); PyTypeObject *tp = Py_TYPE(owner); - _PyAttrCache *cache = (_PyAttrCache *)next_instr; - uint32_t type_version = read_u32(cache->version); assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); @@ -1870,7 +1878,7 @@ DEOPT_IF(dict == NULL, LOAD_ATTR); assert(PyDict_CheckExact((PyObject *)dict)); PyObject *name = GETITEM(names, oparg>>1); - uint16_t hint = cache->index; + uint16_t hint = index; DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR); if (DK_IS_UNICODE(dict->ma_keys)) { PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; @@ -1885,73 +1893,78 @@ DEOPT_IF(res == NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(res); - SET_TOP(NULL); - STACK_GROW((oparg & 1)); - SET_TOP(res); + res2 = NULL; Py_DECREF(owner); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); + STACK_GROW(((oparg & 1) ? 1 : 0)); + POKE(1, res); + if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } + JUMPBY(9); DISPATCH(); } TARGET(LOAD_ATTR_SLOT) { - assert(cframe.use_tracing == 0); - PyObject *owner = TOP(); + PyObject *owner = PEEK(1); + PyObject *res2 = NULL; PyObject *res; + uint32_t type_version = read_u32(&next_instr[1].cache); + uint16_t index = read_u16(&next_instr[3].cache); + assert(cframe.use_tracing == 0); PyTypeObject *tp = Py_TYPE(owner); - _PyAttrCache *cache = (_PyAttrCache *)next_instr; - uint32_t type_version = read_u32(cache->version); assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); - char *addr = (char *)owner + cache->index; + char *addr = (char *)owner + index; res = *(PyObject **)addr; DEOPT_IF(res == NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(res); - SET_TOP(NULL); - STACK_GROW((oparg & 1)); - SET_TOP(res); + res2 = NULL; Py_DECREF(owner); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); + STACK_GROW(((oparg & 1) ? 1 : 0)); + POKE(1, res); + if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } + JUMPBY(9); DISPATCH(); } TARGET(LOAD_ATTR_CLASS) { + PyObject *cls = PEEK(1); + PyObject *res2 = NULL; + PyObject *res; + uint32_t type_version = read_u32(&next_instr[1].cache); + PyObject *descr = read_obj(&next_instr[5].cache); assert(cframe.use_tracing == 0); - _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; - PyObject *cls = TOP(); DEOPT_IF(!PyType_Check(cls), LOAD_ATTR); - uint32_t type_version = read_u32(cache->type_version); DEOPT_IF(((PyTypeObject *)cls)->tp_version_tag != type_version, LOAD_ATTR); assert(type_version != 0); STAT_INC(LOAD_ATTR, hit); - PyObject *res = read_obj(cache->descr); + res2 = NULL; + res = descr; assert(res != NULL); Py_INCREF(res); - SET_TOP(NULL); - STACK_GROW((oparg & 1)); - SET_TOP(res); Py_DECREF(cls); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); + STACK_GROW(((oparg & 1) ? 1 : 0)); + POKE(1, res); + if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } + JUMPBY(9); DISPATCH(); } TARGET(LOAD_ATTR_PROPERTY) { + PyObject *owner = PEEK(1); + uint32_t type_version = read_u32(&next_instr[1].cache); + uint32_t func_version = read_u32(&next_instr[3].cache); + PyObject *fget = read_obj(&next_instr[5].cache); assert(cframe.use_tracing == 0); DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); - _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; - PyObject *owner = TOP(); PyTypeObject *cls = Py_TYPE(owner); - uint32_t type_version = read_u32(cache->type_version); DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR); assert(type_version != 0); - PyObject *fget = read_obj(cache->descr); assert(Py_IS_TYPE(fget, &PyFunction_Type)); PyFunctionObject *f = (PyFunctionObject *)fget; - uint32_t func_version = read_u32(cache->keys_version); assert(func_version != 0); DEOPT_IF(f->func_version != func_version, LOAD_ATTR); PyCodeObject *code = (PyCodeObject *)f->func_code; @@ -1960,6 +1973,7 @@ STAT_INC(LOAD_ATTR, hit); Py_INCREF(fget); _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 1); + // Manipulate stack directly because we exit with DISPATCH_INLINED(). SET_TOP(NULL); int shrink_stack = !(oparg & 1); STACK_SHRINK(shrink_stack); @@ -1969,18 +1983,17 @@ } TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) { + PyObject *owner = PEEK(1); + uint32_t type_version = read_u32(&next_instr[1].cache); + uint32_t func_version = read_u32(&next_instr[3].cache); + PyObject *getattribute = read_obj(&next_instr[5].cache); assert(cframe.use_tracing == 0); DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); - _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; - PyObject *owner = TOP(); PyTypeObject *cls = Py_TYPE(owner); - uint32_t type_version = read_u32(cache->type_version); DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR); assert(type_version != 0); - PyObject *getattribute = read_obj(cache->descr); assert(Py_IS_TYPE(getattribute, &PyFunction_Type)); PyFunctionObject *f = (PyFunctionObject *)getattribute; - uint32_t func_version = read_u32(cache->keys_version); assert(func_version != 0); DEOPT_IF(f->func_version != func_version, LOAD_ATTR); PyCodeObject *code = (PyCodeObject *)f->func_code; @@ -1991,6 +2004,7 @@ PyObject *name = GETITEM(names, oparg >> 1); Py_INCREF(f); _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 2); + // Manipulate stack directly because we exit with DISPATCH_INLINED(). SET_TOP(NULL); int shrink_stack = !(oparg & 1); STACK_SHRINK(shrink_stack); @@ -2831,12 +2845,15 @@ } TARGET(LOAD_ATTR_METHOD_WITH_VALUES) { + PyObject *self = PEEK(1); + PyObject *res2 = NULL; + PyObject *res; + uint32_t type_version = read_u32(&next_instr[1].cache); + uint32_t keys_version = read_u32(&next_instr[3].cache); + PyObject *descr = read_obj(&next_instr[5].cache); /* Cached method object */ assert(cframe.use_tracing == 0); - PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); - _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; - uint32_t type_version = read_u32(cache->type_version); assert(type_version != 0); DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR); assert(self_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT); @@ -2844,41 +2861,51 @@ DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR); PyHeapTypeObject *self_heap_type = (PyHeapTypeObject *)self_cls; DEOPT_IF(self_heap_type->ht_cached_keys->dk_version != - read_u32(cache->keys_version), LOAD_ATTR); + keys_version, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); - PyObject *res = read_obj(cache->descr); - assert(res != NULL); - assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)); - SET_TOP(Py_NewRef(res)); - PUSH(self); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); + assert(descr != NULL); + res2 = Py_NewRef(descr); + assert(_PyType_HasFeature(Py_TYPE(res2), Py_TPFLAGS_METHOD_DESCRIPTOR)); + res = self; + assert(oparg & 1); + STACK_GROW(((oparg & 1) ? 1 : 0)); + POKE(1, res); + if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } + JUMPBY(9); DISPATCH(); } TARGET(LOAD_ATTR_METHOD_NO_DICT) { + PyObject *self = PEEK(1); + PyObject *res2 = NULL; + PyObject *res; + uint32_t type_version = read_u32(&next_instr[1].cache); + PyObject *descr = read_obj(&next_instr[5].cache); assert(cframe.use_tracing == 0); - PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); - _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; - uint32_t type_version = read_u32(cache->type_version); DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR); assert(self_cls->tp_dictoffset == 0); STAT_INC(LOAD_ATTR, hit); - PyObject *res = read_obj(cache->descr); - assert(res != NULL); - assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)); - SET_TOP(Py_NewRef(res)); - PUSH(self); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + res2 = Py_NewRef(descr); + res = self; + assert(oparg & 1); + STACK_GROW(((oparg & 1) ? 1 : 0)); + POKE(1, res); + if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } + JUMPBY(9); DISPATCH(); } TARGET(LOAD_ATTR_METHOD_LAZY_DICT) { + PyObject *self = PEEK(1); + PyObject *res2 = NULL; + PyObject *res; + uint32_t type_version = read_u32(&next_instr[1].cache); + PyObject *descr = read_obj(&next_instr[5].cache); assert(cframe.use_tracing == 0); - PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); - _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; - uint32_t type_version = read_u32(cache->type_version); DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR); Py_ssize_t dictoffset = self_cls->tp_dictoffset; assert(dictoffset > 0); @@ -2886,12 +2913,15 @@ /* This object has a __dict__, just not yet created */ DEOPT_IF(dict != NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); - PyObject *res = read_obj(cache->descr); - assert(res != NULL); - assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)); - SET_TOP(Py_NewRef(res)); - PUSH(self); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + res2 = Py_NewRef(descr); + res = self; + assert(oparg & 1); + STACK_GROW(((oparg & 1) ? 1 : 0)); + POKE(1, res); + if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } + JUMPBY(9); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 171fed363d3be0..96e57be8cf5dea 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -187,19 +187,19 @@ _PyOpcode_num_popped(int opcode, int oparg) { case LOAD_ATTR: return 1; case LOAD_ATTR_INSTANCE_VALUE: - return -1; + return 1; case LOAD_ATTR_MODULE: - return -1; + return 1; case LOAD_ATTR_WITH_HINT: - return -1; + return 1; case LOAD_ATTR_SLOT: - return -1; + return 1; case LOAD_ATTR_CLASS: - return -1; + return 1; case LOAD_ATTR_PROPERTY: - return -1; + return 1; case LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN: - return -1; + return 1; case STORE_ATTR_INSTANCE_VALUE: return 2; case STORE_ATTR_WITH_HINT: @@ -279,11 +279,11 @@ _PyOpcode_num_popped(int opcode, int oparg) { case PUSH_EXC_INFO: return -1; case LOAD_ATTR_METHOD_WITH_VALUES: - return -1; + return 1; case LOAD_ATTR_METHOD_NO_DICT: - return -1; + return 1; case LOAD_ATTR_METHOD_LAZY_DICT: - return -1; + return 1; case CALL_BOUND_METHOD_EXACT_ARGS: return -1; case KW_NAMES: @@ -533,19 +533,19 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case LOAD_ATTR: return ((oparg & 1) ? 1 : 0) + 1; case LOAD_ATTR_INSTANCE_VALUE: - return -1; + return ((oparg & 1) ? 1 : 0) + 1; case LOAD_ATTR_MODULE: - return -1; + return ((oparg & 1) ? 1 : 0) + 1; case LOAD_ATTR_WITH_HINT: - return -1; + return ((oparg & 1) ? 1 : 0) + 1; case LOAD_ATTR_SLOT: - return -1; + return ((oparg & 1) ? 1 : 0) + 1; case LOAD_ATTR_CLASS: - return -1; + return ((oparg & 1) ? 1 : 0) + 1; case LOAD_ATTR_PROPERTY: - return -1; + return ((oparg & 1) ? 1 : 0) + 1; case LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN: - return -1; + return ((oparg & 1) ? 1 : 0) + 1; case STORE_ATTR_INSTANCE_VALUE: return 0; case STORE_ATTR_WITH_HINT: @@ -625,11 +625,11 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case PUSH_EXC_INFO: return -1; case LOAD_ATTR_METHOD_WITH_VALUES: - return -1; + return ((oparg & 1) ? 1 : 0) + 1; case LOAD_ATTR_METHOD_NO_DICT: - return -1; + return ((oparg & 1) ? 1 : 0) + 1; case LOAD_ATTR_METHOD_LAZY_DICT: - return -1; + return ((oparg & 1) ? 1 : 0) + 1; case CALL_BOUND_METHOD_EXACT_ARGS: return -1; case KW_NAMES: @@ -792,13 +792,13 @@ struct opcode_metadata { [DICT_MERGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [MAP_ADD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [LOAD_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, - [LOAD_ATTR_INSTANCE_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_MODULE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_WITH_HINT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_SLOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_PROPERTY] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_INSTANCE_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, + [LOAD_ATTR_MODULE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, + [LOAD_ATTR_WITH_HINT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, + [LOAD_ATTR_SLOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, + [LOAD_ATTR_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, + [LOAD_ATTR_PROPERTY] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, + [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, [STORE_ATTR_INSTANCE_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, [STORE_ATTR_WITH_HINT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, [STORE_ATTR_SLOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, @@ -838,9 +838,9 @@ struct opcode_metadata { [BEFORE_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [WITH_EXCEPT_START] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [PUSH_EXC_INFO] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [LOAD_ATTR_METHOD_WITH_VALUES] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [LOAD_ATTR_METHOD_NO_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [LOAD_ATTR_METHOD_LAZY_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [LOAD_ATTR_METHOD_WITH_VALUES] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, + [LOAD_ATTR_METHOD_NO_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, + [LOAD_ATTR_METHOD_LAZY_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, [CALL_BOUND_METHOD_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [KW_NAMES] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [CALL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, From b91b42d236c81bd7cbe402b322c82bfcd0d883a1 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Wed, 1 Feb 2023 19:38:06 +0000 Subject: [PATCH 056/225] gh-98831: rewrite PUSH_EXC_INFO and conditional jumps in the instruction definition DSL (#101481) --- Python/bytecodes.c | 71 ++++++++++--------------- Python/compile.c | 24 +++++---- Python/generated_cases.c.h | 68 +++++++++++++---------- Python/opcode_metadata.h | 32 +++++------ Tools/cases_generator/generate_cases.py | 2 +- 5 files changed, 98 insertions(+), 99 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index bb1deaf3fbeb4b..105bd95be0fdc3 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1898,9 +1898,7 @@ dummy_func( CHECK_EVAL_BREAKER(); } - // stack effect: (__0 -- ) - inst(POP_JUMP_IF_FALSE) { - PyObject *cond = POP(); + inst(POP_JUMP_IF_FALSE, (cond -- )) { if (Py_IsTrue(cond)) { _Py_DECREF_NO_DEALLOC(cond); } @@ -1911,19 +1909,16 @@ dummy_func( else { int err = PyObject_IsTrue(cond); Py_DECREF(cond); - if (err > 0) - ; - else if (err == 0) { + if (err == 0) { JUMPBY(oparg); } - else - goto error; + else { + ERROR_IF(err < 0, error); + } } } - // stack effect: (__0 -- ) - inst(POP_JUMP_IF_TRUE) { - PyObject *cond = POP(); + inst(POP_JUMP_IF_TRUE, (cond -- )) { if (Py_IsFalse(cond)) { _Py_DECREF_NO_DEALLOC(cond); } @@ -1937,25 +1932,23 @@ dummy_func( if (err > 0) { JUMPBY(oparg); } - else if (err == 0) - ; - else - goto error; + else { + ERROR_IF(err < 0, error); + } } } - // stack effect: (__0 -- ) - inst(POP_JUMP_IF_NOT_NONE) { - PyObject *value = POP(); + inst(POP_JUMP_IF_NOT_NONE, (value -- )) { if (!Py_IsNone(value)) { + Py_DECREF(value); JUMPBY(oparg); } - Py_DECREF(value); + else { + _Py_DECREF_NO_DEALLOC(value); + } } - // stack effect: (__0 -- ) - inst(POP_JUMP_IF_NONE) { - PyObject *value = POP(); + inst(POP_JUMP_IF_NONE, (value -- )) { if (Py_IsNone(value)) { _Py_DECREF_NO_DEALLOC(value); JUMPBY(oparg); @@ -1965,25 +1958,24 @@ dummy_func( } } - // error: JUMP_IF_FALSE_OR_POP stack effect depends on jump flag - inst(JUMP_IF_FALSE_OR_POP) { - PyObject *cond = TOP(); + inst(JUMP_IF_FALSE_OR_POP, (cond -- cond if (jump))) { + bool jump = false; int err; if (Py_IsTrue(cond)) { - STACK_SHRINK(1); _Py_DECREF_NO_DEALLOC(cond); } else if (Py_IsFalse(cond)) { JUMPBY(oparg); + jump = true; } else { err = PyObject_IsTrue(cond); if (err > 0) { - STACK_SHRINK(1); Py_DECREF(cond); } else if (err == 0) { JUMPBY(oparg); + jump = true; } else { goto error; @@ -1991,24 +1983,23 @@ dummy_func( } } - // error: JUMP_IF_TRUE_OR_POP stack effect depends on jump flag - inst(JUMP_IF_TRUE_OR_POP) { - PyObject *cond = TOP(); + inst(JUMP_IF_TRUE_OR_POP, (cond -- cond if (jump))) { + bool jump = false; int err; if (Py_IsFalse(cond)) { - STACK_SHRINK(1); _Py_DECREF_NO_DEALLOC(cond); } else if (Py_IsTrue(cond)) { JUMPBY(oparg); + jump = true; } else { err = PyObject_IsTrue(cond); if (err > 0) { JUMPBY(oparg); + jump = true; } else if (err == 0) { - STACK_SHRINK(1); Py_DECREF(cond); } else { @@ -2321,22 +2312,16 @@ dummy_func( ERROR_IF(res == NULL, error); } - // stack effect: ( -- __0) - inst(PUSH_EXC_INFO) { - PyObject *value = TOP(); - + inst(PUSH_EXC_INFO, (new_exc -- prev_exc, new_exc)) { _PyErr_StackItem *exc_info = tstate->exc_info; if (exc_info->exc_value != NULL) { - SET_TOP(exc_info->exc_value); + prev_exc = exc_info->exc_value; } else { - SET_TOP(Py_NewRef(Py_None)); + prev_exc = Py_NewRef(Py_None); } - - PUSH(Py_NewRef(value)); - assert(PyExceptionInstance_Check(value)); - exc_info->exc_value = value; - + assert(PyExceptionInstance_Check(new_exc)); + exc_info->exc_value = Py_NewRef(new_exc); } inst(LOAD_ATTR_METHOD_WITH_VALUES, (unused/1, type_version/2, keys_version/2, descr/4, self -- res2 if (oparg & 1), res)) { diff --git a/Python/compile.c b/Python/compile.c index a11bcc79a6dd10..d9ec68958972b5 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -8630,17 +8630,19 @@ opcode_metadata_is_sane(cfg_builder *g) { int opcode = instr->i_opcode; int oparg = instr->i_oparg; assert(opcode <= MAX_REAL_OPCODE); - int popped = _PyOpcode_num_popped(opcode, oparg); - int pushed = _PyOpcode_num_pushed(opcode, oparg); - assert((pushed < 0) == (popped < 0)); - if (pushed >= 0) { - assert(_PyOpcode_opcode_metadata[opcode].valid_entry); - int effect = stack_effect(opcode, instr->i_oparg, -1); - if (effect != pushed - popped) { - fprintf(stderr, - "op=%d: stack_effect (%d) != pushed (%d) - popped (%d)\n", - opcode, effect, pushed, popped); - result = false; + for (int jump = 0; jump <= 1; jump++) { + int popped = _PyOpcode_num_popped(opcode, oparg, jump ? true : false); + int pushed = _PyOpcode_num_pushed(opcode, oparg, jump ? true : false); + assert((pushed < 0) == (popped < 0)); + if (pushed >= 0) { + assert(_PyOpcode_opcode_metadata[opcode].valid_entry); + int effect = stack_effect(opcode, instr->i_oparg, jump); + if (effect != pushed - popped) { + fprintf(stderr, + "op=%d arg=%d jump=%d: stack_effect (%d) != pushed (%d) - popped (%d)\n", + opcode, oparg, jump, effect, pushed, popped); + result = false; + } } } } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index e5c5c7e557a37c..a02d8d79c60d37 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2348,7 +2348,7 @@ TARGET(POP_JUMP_IF_FALSE) { PREDICTED(POP_JUMP_IF_FALSE); - PyObject *cond = POP(); + PyObject *cond = PEEK(1); if (Py_IsTrue(cond)) { _Py_DECREF_NO_DEALLOC(cond); } @@ -2359,19 +2359,19 @@ else { int err = PyObject_IsTrue(cond); Py_DECREF(cond); - if (err > 0) - ; - else if (err == 0) { + if (err == 0) { JUMPBY(oparg); } - else - goto error; + else { + if (err < 0) goto pop_1_error; + } } + STACK_SHRINK(1); DISPATCH(); } TARGET(POP_JUMP_IF_TRUE) { - PyObject *cond = POP(); + PyObject *cond = PEEK(1); if (Py_IsFalse(cond)) { _Py_DECREF_NO_DEALLOC(cond); } @@ -2385,25 +2385,29 @@ if (err > 0) { JUMPBY(oparg); } - else if (err == 0) - ; - else - goto error; + else { + if (err < 0) goto pop_1_error; + } } + STACK_SHRINK(1); DISPATCH(); } TARGET(POP_JUMP_IF_NOT_NONE) { - PyObject *value = POP(); + PyObject *value = PEEK(1); if (!Py_IsNone(value)) { + Py_DECREF(value); JUMPBY(oparg); } - Py_DECREF(value); + else { + _Py_DECREF_NO_DEALLOC(value); + } + STACK_SHRINK(1); DISPATCH(); } TARGET(POP_JUMP_IF_NONE) { - PyObject *value = POP(); + PyObject *value = PEEK(1); if (Py_IsNone(value)) { _Py_DECREF_NO_DEALLOC(value); JUMPBY(oparg); @@ -2411,58 +2415,65 @@ else { Py_DECREF(value); } + STACK_SHRINK(1); DISPATCH(); } TARGET(JUMP_IF_FALSE_OR_POP) { - PyObject *cond = TOP(); + PyObject *cond = PEEK(1); + bool jump = false; int err; if (Py_IsTrue(cond)) { - STACK_SHRINK(1); _Py_DECREF_NO_DEALLOC(cond); } else if (Py_IsFalse(cond)) { JUMPBY(oparg); + jump = true; } else { err = PyObject_IsTrue(cond); if (err > 0) { - STACK_SHRINK(1); Py_DECREF(cond); } else if (err == 0) { JUMPBY(oparg); + jump = true; } else { goto error; } } + STACK_SHRINK(1); + STACK_GROW((jump ? 1 : 0)); DISPATCH(); } TARGET(JUMP_IF_TRUE_OR_POP) { - PyObject *cond = TOP(); + PyObject *cond = PEEK(1); + bool jump = false; int err; if (Py_IsFalse(cond)) { - STACK_SHRINK(1); _Py_DECREF_NO_DEALLOC(cond); } else if (Py_IsTrue(cond)) { JUMPBY(oparg); + jump = true; } else { err = PyObject_IsTrue(cond); if (err > 0) { JUMPBY(oparg); + jump = true; } else if (err == 0) { - STACK_SHRINK(1); Py_DECREF(cond); } else { goto error; } } + STACK_SHRINK(1); + STACK_GROW((jump ? 1 : 0)); DISPATCH(); } @@ -2828,19 +2839,20 @@ } TARGET(PUSH_EXC_INFO) { - PyObject *value = TOP(); - + PyObject *new_exc = PEEK(1); + PyObject *prev_exc; _PyErr_StackItem *exc_info = tstate->exc_info; if (exc_info->exc_value != NULL) { - SET_TOP(exc_info->exc_value); + prev_exc = exc_info->exc_value; } else { - SET_TOP(Py_NewRef(Py_None)); + prev_exc = Py_NewRef(Py_None); } - - PUSH(Py_NewRef(value)); - assert(PyExceptionInstance_Check(value)); - exc_info->exc_value = value; + assert(PyExceptionInstance_Check(new_exc)); + exc_info->exc_value = Py_NewRef(new_exc); + STACK_GROW(1); + POKE(1, new_exc); + POKE(2, prev_exc); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 96e57be8cf5dea..c9f9759e16b316 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -4,7 +4,7 @@ #ifndef NDEBUG static int -_PyOpcode_num_popped(int opcode, int oparg) { +_PyOpcode_num_popped(int opcode, int oparg, bool jump) { switch(opcode) { case NOP: return 0; @@ -233,17 +233,17 @@ _PyOpcode_num_popped(int opcode, int oparg) { case JUMP_BACKWARD: return 0; case POP_JUMP_IF_FALSE: - return -1; + return 1; case POP_JUMP_IF_TRUE: - return -1; + return 1; case POP_JUMP_IF_NOT_NONE: - return -1; + return 1; case POP_JUMP_IF_NONE: - return -1; + return 1; case JUMP_IF_FALSE_OR_POP: - return -1; + return 1; case JUMP_IF_TRUE_OR_POP: - return -1; + return 1; case JUMP_BACKWARD_NO_INTERRUPT: return 0; case GET_LEN: @@ -277,7 +277,7 @@ _PyOpcode_num_popped(int opcode, int oparg) { case WITH_EXCEPT_START: return 4; case PUSH_EXC_INFO: - return -1; + return 1; case LOAD_ATTR_METHOD_WITH_VALUES: return 1; case LOAD_ATTR_METHOD_NO_DICT: @@ -350,7 +350,7 @@ _PyOpcode_num_popped(int opcode, int oparg) { #ifndef NDEBUG static int -_PyOpcode_num_pushed(int opcode, int oparg) { +_PyOpcode_num_pushed(int opcode, int oparg, bool jump) { switch(opcode) { case NOP: return 0; @@ -579,17 +579,17 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case JUMP_BACKWARD: return 0; case POP_JUMP_IF_FALSE: - return -1; + return 0; case POP_JUMP_IF_TRUE: - return -1; + return 0; case POP_JUMP_IF_NOT_NONE: - return -1; + return 0; case POP_JUMP_IF_NONE: - return -1; + return 0; case JUMP_IF_FALSE_OR_POP: - return -1; + return (jump ? 1 : 0); case JUMP_IF_TRUE_OR_POP: - return -1; + return (jump ? 1 : 0); case JUMP_BACKWARD_NO_INTERRUPT: return 0; case GET_LEN: @@ -623,7 +623,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case WITH_EXCEPT_START: return 5; case PUSH_EXC_INFO: - return -1; + return 2; case LOAD_ATTR_METHOD_WITH_VALUES: return ((oparg & 1) ? 1 : 0) + 1; case LOAD_ATTR_METHOD_NO_DICT: diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 43685450cc0dfe..3925583b40e728 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -868,7 +868,7 @@ def write_function( ) -> None: self.out.emit("\n#ifndef NDEBUG") self.out.emit("static int") - self.out.emit(f"_PyOpcode_num_{direction}(int opcode, int oparg) {{") + self.out.emit(f"_PyOpcode_num_{direction}(int opcode, int oparg, bool jump) {{") self.out.emit(" switch(opcode) {") for instr, effect in data: self.out.emit(f" case {instr.name}:") From eda60916bc88f8af736790ffd52381e8bb83ae83 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 1 Feb 2023 21:06:56 +0000 Subject: [PATCH 057/225] gh-101467: Correct py.exe handling of prefix matches and cases when only one runtime is installed (GH-101468) --- Doc/using/windows.rst | 35 ++++++++-- Lib/test/test_launcher.py | 29 +++++++-- ...-01-31-16-50-07.gh-issue-101467.ye9t-L.rst | 3 + PC/launcher2.c | 65 ++++++++++++++++--- 4 files changed, 114 insertions(+), 18 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-01-31-16-50-07.gh-issue-101467.ye9t-L.rst diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index bd09666d5e01c1..1c4e41c0e0e239 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -743,22 +743,47 @@ command:: py -2 -You should find the latest version of Python 3.x starts. - If you see the following error, you do not have the launcher installed:: 'py' is not recognized as an internal or external command, operable program or batch file. -Per-user installations of Python do not add the launcher to :envvar:`PATH` -unless the option was selected on installation. - The command:: py --list displays the currently installed version(s) of Python. +The ``-x.y`` argument is the short form of the ``-V:Company/Tag`` argument, +which allows selecting a specific Python runtime, including those that may have +come from somewhere other than python.org. Any runtime registered by following +:pep:`514` will be discoverable. The ``--list`` command lists all available +runtimes using the ``-V:`` format. + +When using the ``-V:`` argument, specifying the Company will limit selection to +runtimes from that provider, while specifying only the Tag will select from all +providers. Note that omitting the slash implies a tag:: + + # Select any '3.*' tagged runtime + py -V:3 + + # Select any 'PythonCore' released runtime + py -V:PythonCore/ + + # Select PythonCore's latest Python 3 runtime + py -V:PythonCore/3 + +The short form of the argument (``-3``) only ever selects from core Python +releases, and not other distributions. However, the longer form (``-V:3``) will +select from any. + +The Company is matched on the full string, case-insenitive. The Tag is matched +oneither the full string, or a prefix, provided the next character is a dot or a +hyphen. This allows ``-V:3.1`` to match ``3.1-32``, but not ``3.10``. Tags are +sorted using numerical ordering (``3.10`` is newer than ``3.1``), but are +compared using text (``-V:3.01`` does not match ``3.1``). + + Virtual environments ^^^^^^^^^^^^^^^^^^^^ diff --git a/Lib/test/test_launcher.py b/Lib/test/test_launcher.py index 351a638c1dd340..2f35eaf08a2dc9 100644 --- a/Lib/test/test_launcher.py +++ b/Lib/test/test_launcher.py @@ -56,7 +56,17 @@ None: sys.prefix, } }, - } + }, + "PythonTestSuite1": { + "DisplayName": "Python Test Suite Single", + "3.100": { + "DisplayName": "Single Interpreter", + "InstallPath": { + None: sys.prefix, + "ExecutablePath": sys.executable, + } + } + }, } @@ -206,6 +216,7 @@ def run_py(self, args, env=None, allow_fail=False, expect_returncode=0, argv=Non **{k.upper(): v for k, v in os.environ.items() if k.upper() not in ignore}, "PYLAUNCHER_DEBUG": "1", "PYLAUNCHER_DRYRUN": "1", + "PYLAUNCHER_LIMIT_TO_COMPANY": "", **{k.upper(): v for k, v in (env or {}).items()}, } if not argv: @@ -388,23 +399,33 @@ def test_filter_to_tag(self): self.assertEqual(company, data["env.company"]) self.assertEqual("3.100", data["env.tag"]) - data = self.run_py([f"-V:3.100-3"]) + data = self.run_py([f"-V:3.100-32"]) self.assertEqual("X.Y-32.exe", data["LaunchCommand"]) self.assertEqual(company, data["env.company"]) self.assertEqual("3.100-32", data["env.tag"]) - data = self.run_py([f"-V:3.100-a"]) + data = self.run_py([f"-V:3.100-arm64"]) self.assertEqual("X.Y-arm64.exe -X fake_arg_for_test", data["LaunchCommand"]) self.assertEqual(company, data["env.company"]) self.assertEqual("3.100-arm64", data["env.tag"]) def test_filter_to_company_and_tag(self): company = "PythonTestSuite" - data = self.run_py([f"-V:{company}/3.1"]) + data = self.run_py([f"-V:{company}/3.1"], expect_returncode=103) + + data = self.run_py([f"-V:{company}/3.100"]) self.assertEqual("X.Y.exe", data["LaunchCommand"]) self.assertEqual(company, data["env.company"]) self.assertEqual("3.100", data["env.tag"]) + def test_filter_with_single_install(self): + company = "PythonTestSuite1" + data = self.run_py( + [f"-V:Nonexistent"], + env={"PYLAUNCHER_LIMIT_TO_COMPANY": company}, + expect_returncode=103, + ) + def test_search_major_3(self): try: data = self.run_py(["-3"], allow_fail=True) diff --git a/Misc/NEWS.d/next/Windows/2023-01-31-16-50-07.gh-issue-101467.ye9t-L.rst b/Misc/NEWS.d/next/Windows/2023-01-31-16-50-07.gh-issue-101467.ye9t-L.rst new file mode 100644 index 00000000000000..4d4da05afa2d16 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-31-16-50-07.gh-issue-101467.ye9t-L.rst @@ -0,0 +1,3 @@ +The ``py.exe`` launcher now correctly filters when only a single runtime is +installed. It also correctly handles prefix matches on tags so that ``-3.1`` +does not match ``3.11``, but would still match ``3.1-32``. diff --git a/PC/launcher2.c b/PC/launcher2.c index 2052a2ffeb57b7..beeb2ae46b83f0 100644 --- a/PC/launcher2.c +++ b/PC/launcher2.c @@ -295,6 +295,30 @@ _startsWithArgument(const wchar_t *x, int xLen, const wchar_t *y, int yLen) } +// Unlike regular startsWith, this function requires that the following +// character is either NULL (that is, the entire string matches) or is one of +// the characters in 'separators'. +bool +_startsWithSeparated(const wchar_t *x, int xLen, const wchar_t *y, int yLen, const wchar_t *separators) +{ + if (!x || !y) { + return false; + } + yLen = yLen < 0 ? (int)wcsnlen_s(y, MAXLEN) : yLen; + xLen = xLen < 0 ? (int)wcsnlen_s(x, MAXLEN) : xLen; + if (xLen < yLen) { + return false; + } + if (xLen == yLen) { + return 0 == _compare(x, xLen, y, yLen); + } + return separators && + 0 == _compare(x, yLen, y, yLen) && + wcschr(separators, x[yLen]) != NULL; +} + + + /******************************************************************************\ *** HELP TEXT *** \******************************************************************************/ @@ -409,6 +433,9 @@ typedef struct { bool listPaths; // if true, display help message before contiuning bool help; + // if set, limits search to registry keys with the specified Company + // This is intended for debugging and testing only + const wchar_t *limitToCompany; // dynamically allocated buffers to free later struct _SearchInfoBuffer *_buffer; } SearchInfo; @@ -489,6 +516,7 @@ dumpSearchInfo(SearchInfo *search) DEBUG_BOOL(list); DEBUG_BOOL(listPaths); DEBUG_BOOL(help); + DEBUG(limitToCompany); #undef DEBUG_BOOL #undef DEBUG_2 #undef DEBUG @@ -1606,6 +1634,10 @@ registrySearch(const SearchInfo *search, EnvironmentInfo **result, HKEY root, in } break; } + if (search->limitToCompany && 0 != _compare(search->limitToCompany, -1, buffer, cchBuffer)) { + debug(L"# Skipping %s due to PYLAUNCHER_LIMIT_TO_COMPANY\n", buffer); + continue; + } HKEY subkey; if (ERROR_SUCCESS == RegOpenKeyExW(root, buffer, 0, KEY_READ, &subkey)) { exitCode = _registrySearchTags(search, result, subkey, sortKey, buffer, fallbackArch); @@ -1884,6 +1916,11 @@ collectEnvironments(const SearchInfo *search, EnvironmentInfo **result) } } + if (search->limitToCompany) { + debug(L"# Skipping APPX search due to PYLAUNCHER_LIMIT_TO_COMPANY\n"); + return 0; + } + for (struct AppxSearchInfo *info = APPX_SEARCH; info->familyName; ++info) { exitCode = appxSearch(search, result, info->familyName, info->tag, info->sortKey); if (exitCode && exitCode != RC_NO_PYTHON) { @@ -2053,12 +2090,15 @@ _companyMatches(const SearchInfo *search, const EnvironmentInfo *env) bool -_tagMatches(const SearchInfo *search, const EnvironmentInfo *env) +_tagMatches(const SearchInfo *search, const EnvironmentInfo *env, int searchTagLength) { - if (!search->tag || !search->tagLength) { + if (searchTagLength < 0) { + searchTagLength = search->tagLength; + } + if (!search->tag || !searchTagLength) { return true; } - return _startsWith(env->tag, -1, search->tag, search->tagLength); + return _startsWithSeparated(env->tag, -1, search->tag, searchTagLength, L".-"); } @@ -2095,7 +2135,7 @@ _selectEnvironment(const SearchInfo *search, EnvironmentInfo *env, EnvironmentIn } if (!search->oldStyleTag) { - if (_companyMatches(search, env) && _tagMatches(search, env)) { + if (_companyMatches(search, env) && _tagMatches(search, env, -1)) { // Because of how our sort tree is set up, we will walk up the // "prev" side and implicitly select the "best" best. By // returning straight after a match, we skip the entire "next" @@ -2120,7 +2160,7 @@ _selectEnvironment(const SearchInfo *search, EnvironmentInfo *env, EnvironmentIn } } - if (_startsWith(env->tag, -1, search->tag, tagLength)) { + if (_tagMatches(search, env, tagLength)) { if (exclude32Bit && _is32Bit(env)) { debug(L"# Excluding %s/%s because it looks like 32bit\n", env->company, env->tag); } else if (only32Bit && !_is32Bit(env)) { @@ -2147,10 +2187,6 @@ selectEnvironment(const SearchInfo *search, EnvironmentInfo *root, EnvironmentIn *best = NULL; return RC_NO_PYTHON_AT_ALL; } - if (!root->next && !root->prev) { - *best = root; - return 0; - } EnvironmentInfo *result = NULL; int exitCode = _selectEnvironment(search, root, &result); @@ -2560,6 +2596,17 @@ process(int argc, wchar_t ** argv) debug(L"argv0: %s\nversion: %S\n", argv[0], PY_VERSION); } + DWORD len = GetEnvironmentVariableW(L"PYLAUNCHER_LIMIT_TO_COMPANY", NULL, 0); + if (len > 1) { + wchar_t *limitToCompany = allocSearchInfoBuffer(&search, len); + search.limitToCompany = limitToCompany; + if (0 == GetEnvironmentVariableW(L"PYLAUNCHER_LIMIT_TO_COMPANY", limitToCompany, len)) { + exitCode = RC_INTERNAL_ERROR; + winerror(0, L"Failed to read PYLAUNCHER_LIMIT_TO_COMPANY variable"); + goto abort; + } + } + search.originalCmdLine = GetCommandLineW(); exitCode = performSearch(&search, &envs); From ae9b38f4241a7d62ec4e9b775d4436d762b11fb3 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 1 Feb 2023 13:12:49 -0800 Subject: [PATCH 058/225] gh-98831: Modernize the LOAD_GLOBAL family (#101502) --- Python/bytecodes.c | 57 +++++++++++--------------------- Python/generated_cases.c.h | 66 +++++++++++++++++++++----------------- Python/opcode_metadata.h | 20 ++++++------ 3 files changed, 66 insertions(+), 77 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 105bd95be0fdc3..fb41c8387ccc90 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1068,8 +1068,13 @@ dummy_func( } } - // error: LOAD_GLOBAL has irregular stack effect - inst(LOAD_GLOBAL) { + family(load_global, INLINE_CACHE_ENTRIES_LOAD_GLOBAL) = { + LOAD_GLOBAL, + LOAD_GLOBAL_MODULE, + LOAD_GLOBAL_BUILTIN, + }; + + inst(LOAD_GLOBAL, (unused/1, unused/1, unused/2, unused/1 -- null if (oparg & 1), v)) { #if ENABLE_SPECIALIZATION _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { @@ -1082,10 +1087,7 @@ dummy_func( STAT_INC(LOAD_GLOBAL, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - int push_null = oparg & 1; - PEEK(0) = NULL; PyObject *name = GETITEM(names, oparg>>1); - PyObject *v; if (PyDict_CheckExact(GLOBALS()) && PyDict_CheckExact(BUILTINS())) { @@ -1099,7 +1101,7 @@ dummy_func( format_exc_check_arg(tstate, PyExc_NameError, NAME_ERROR_MSG, name); } - goto error; + ERROR_IF(true, error); } Py_INCREF(v); } @@ -1109,9 +1111,7 @@ dummy_func( /* namespace 1: globals */ v = PyObject_GetItem(GLOBALS(), name); if (v == NULL) { - if (!_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { - goto error; - } + ERROR_IF(!_PyErr_ExceptionMatches(tstate, PyExc_KeyError), error); _PyErr_Clear(tstate); /* namespace 2: builtins */ @@ -1122,58 +1122,42 @@ dummy_func( tstate, PyExc_NameError, NAME_ERROR_MSG, name); } - goto error; + ERROR_IF(true, error); } } } - /* Skip over inline cache */ - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_GLOBAL); - STACK_GROW(push_null); - PUSH(v); + null = NULL; } - // error: LOAD_GLOBAL has irregular stack effect - inst(LOAD_GLOBAL_MODULE) { + inst(LOAD_GLOBAL_MODULE, (unused/1, index/1, version/2, unused/1 -- null if (oparg & 1), res)) { assert(cframe.use_tracing == 0); DEOPT_IF(!PyDict_CheckExact(GLOBALS()), LOAD_GLOBAL); PyDictObject *dict = (PyDictObject *)GLOBALS(); - _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr; - uint32_t version = read_u32(cache->module_keys_version); DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL); assert(DK_IS_UNICODE(dict->ma_keys)); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys); - PyObject *res = entries[cache->index].me_value; + res = entries[index].me_value; DEOPT_IF(res == NULL, LOAD_GLOBAL); - int push_null = oparg & 1; - PEEK(0) = NULL; - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_GLOBAL); + Py_INCREF(res); STAT_INC(LOAD_GLOBAL, hit); - STACK_GROW(push_null+1); - SET_TOP(Py_NewRef(res)); + null = NULL; } - // error: LOAD_GLOBAL has irregular stack effect - inst(LOAD_GLOBAL_BUILTIN) { + inst(LOAD_GLOBAL_BUILTIN, (unused/1, index/1, mod_version/2, bltn_version/1 -- null if (oparg & 1), res)) { assert(cframe.use_tracing == 0); DEOPT_IF(!PyDict_CheckExact(GLOBALS()), LOAD_GLOBAL); DEOPT_IF(!PyDict_CheckExact(BUILTINS()), LOAD_GLOBAL); PyDictObject *mdict = (PyDictObject *)GLOBALS(); PyDictObject *bdict = (PyDictObject *)BUILTINS(); - _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr; - uint32_t mod_version = read_u32(cache->module_keys_version); - uint16_t bltn_version = cache->builtin_keys_version; DEOPT_IF(mdict->ma_keys->dk_version != mod_version, LOAD_GLOBAL); DEOPT_IF(bdict->ma_keys->dk_version != bltn_version, LOAD_GLOBAL); assert(DK_IS_UNICODE(bdict->ma_keys)); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(bdict->ma_keys); - PyObject *res = entries[cache->index].me_value; + res = entries[index].me_value; DEOPT_IF(res == NULL, LOAD_GLOBAL); - int push_null = oparg & 1; - PEEK(0) = NULL; - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_GLOBAL); + Py_INCREF(res); STAT_INC(LOAD_GLOBAL, hit); - STACK_GROW(push_null+1); - SET_TOP(Py_NewRef(res)); + null = NULL; } inst(DELETE_FAST, (--)) { @@ -3212,9 +3196,6 @@ family(call, INLINE_CACHE_ENTRIES_CALL) = { family(for_iter, INLINE_CACHE_ENTRIES_FOR_ITER) = { FOR_ITER, FOR_ITER_LIST, FOR_ITER_RANGE }; -family(load_global, INLINE_CACHE_ENTRIES_LOAD_GLOBAL) = { - LOAD_GLOBAL, LOAD_GLOBAL_BUILTIN, - LOAD_GLOBAL_MODULE }; family(store_fast) = { STORE_FAST, STORE_FAST__LOAD_FAST, STORE_FAST__STORE_FAST }; family(unpack_sequence, INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE) = { UNPACK_SEQUENCE, UNPACK_SEQUENCE_LIST, diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index a02d8d79c60d37..77f9b00c4f0abc 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1311,6 +1311,9 @@ TARGET(LOAD_GLOBAL) { PREDICTED(LOAD_GLOBAL); + static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 5, "incorrect cache size"); + PyObject *null = NULL; + PyObject *v; #if ENABLE_SPECIALIZATION _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { @@ -1323,10 +1326,7 @@ STAT_INC(LOAD_GLOBAL, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - int push_null = oparg & 1; - PEEK(0) = NULL; PyObject *name = GETITEM(names, oparg>>1); - PyObject *v; if (PyDict_CheckExact(GLOBALS()) && PyDict_CheckExact(BUILTINS())) { @@ -1340,7 +1340,7 @@ format_exc_check_arg(tstate, PyExc_NameError, NAME_ERROR_MSG, name); } - goto error; + if (true) goto error; } Py_INCREF(v); } @@ -1350,9 +1350,7 @@ /* namespace 1: globals */ v = PyObject_GetItem(GLOBALS(), name); if (v == NULL) { - if (!_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { - goto error; - } + if (!_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) goto error; _PyErr_Clear(tstate); /* namespace 2: builtins */ @@ -1363,58 +1361,68 @@ tstate, PyExc_NameError, NAME_ERROR_MSG, name); } - goto error; + if (true) goto error; } } } - /* Skip over inline cache */ - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_GLOBAL); - STACK_GROW(push_null); - PUSH(v); + null = NULL; + STACK_GROW(1); + STACK_GROW(((oparg & 1) ? 1 : 0)); + POKE(1, v); + if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), null); } + JUMPBY(5); DISPATCH(); } TARGET(LOAD_GLOBAL_MODULE) { + PyObject *null = NULL; + PyObject *res; + uint16_t index = read_u16(&next_instr[1].cache); + uint32_t version = read_u32(&next_instr[2].cache); assert(cframe.use_tracing == 0); DEOPT_IF(!PyDict_CheckExact(GLOBALS()), LOAD_GLOBAL); PyDictObject *dict = (PyDictObject *)GLOBALS(); - _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr; - uint32_t version = read_u32(cache->module_keys_version); DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL); assert(DK_IS_UNICODE(dict->ma_keys)); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys); - PyObject *res = entries[cache->index].me_value; + res = entries[index].me_value; DEOPT_IF(res == NULL, LOAD_GLOBAL); - int push_null = oparg & 1; - PEEK(0) = NULL; - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_GLOBAL); + Py_INCREF(res); STAT_INC(LOAD_GLOBAL, hit); - STACK_GROW(push_null+1); - SET_TOP(Py_NewRef(res)); + null = NULL; + STACK_GROW(1); + STACK_GROW(((oparg & 1) ? 1 : 0)); + POKE(1, res); + if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), null); } + JUMPBY(5); DISPATCH(); } TARGET(LOAD_GLOBAL_BUILTIN) { + PyObject *null = NULL; + PyObject *res; + uint16_t index = read_u16(&next_instr[1].cache); + uint32_t mod_version = read_u32(&next_instr[2].cache); + uint16_t bltn_version = read_u16(&next_instr[4].cache); assert(cframe.use_tracing == 0); DEOPT_IF(!PyDict_CheckExact(GLOBALS()), LOAD_GLOBAL); DEOPT_IF(!PyDict_CheckExact(BUILTINS()), LOAD_GLOBAL); PyDictObject *mdict = (PyDictObject *)GLOBALS(); PyDictObject *bdict = (PyDictObject *)BUILTINS(); - _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr; - uint32_t mod_version = read_u32(cache->module_keys_version); - uint16_t bltn_version = cache->builtin_keys_version; DEOPT_IF(mdict->ma_keys->dk_version != mod_version, LOAD_GLOBAL); DEOPT_IF(bdict->ma_keys->dk_version != bltn_version, LOAD_GLOBAL); assert(DK_IS_UNICODE(bdict->ma_keys)); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(bdict->ma_keys); - PyObject *res = entries[cache->index].me_value; + res = entries[index].me_value; DEOPT_IF(res == NULL, LOAD_GLOBAL); - int push_null = oparg & 1; - PEEK(0) = NULL; - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_GLOBAL); + Py_INCREF(res); STAT_INC(LOAD_GLOBAL, hit); - STACK_GROW(push_null+1); - SET_TOP(Py_NewRef(res)); + null = NULL; + STACK_GROW(1); + STACK_GROW(((oparg & 1) ? 1 : 0)); + POKE(1, res); + if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), null); } + JUMPBY(5); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index c9f9759e16b316..f0bdedeb667d44 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -141,11 +141,11 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case LOAD_NAME: return 0; case LOAD_GLOBAL: - return -1; + return 0; case LOAD_GLOBAL_MODULE: - return -1; + return 0; case LOAD_GLOBAL_BUILTIN: - return -1; + return 0; case DELETE_FAST: return 0; case MAKE_CELL: @@ -487,11 +487,11 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case LOAD_NAME: return 1; case LOAD_GLOBAL: - return -1; + return ((oparg & 1) ? 1 : 0) + 1; case LOAD_GLOBAL_MODULE: - return -1; + return ((oparg & 1) ? 1 : 0) + 1; case LOAD_GLOBAL_BUILTIN: - return -1; + return ((oparg & 1) ? 1 : 0) + 1; case DELETE_FAST: return 0; case MAKE_CELL: @@ -694,7 +694,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { } #endif enum Direction { DIR_NONE, DIR_READ, DIR_WRITE }; -enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBC00000000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 }; +enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBC0000, INSTR_FMT_IBC00000000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 }; struct opcode_metadata { enum Direction dir_op1; enum Direction dir_op2; @@ -769,9 +769,9 @@ struct opcode_metadata { [STORE_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [DELETE_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [LOAD_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_GLOBAL_MODULE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_GLOBAL_BUILTIN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0000 }, + [LOAD_GLOBAL_MODULE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0000 }, + [LOAD_GLOBAL_BUILTIN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0000 }, [DELETE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [MAKE_CELL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [DELETE_DEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, From ee21110086e277a0ac24f5f768f35847b4db3380 Mon Sep 17 00:00:00 2001 From: Marcos Pereira <3464445+marcospgp@users.noreply.github.com> Date: Wed, 1 Feb 2023 23:52:29 +0000 Subject: [PATCH 059/225] Docs: improve accuracy of sqlite3 `check_same_thread` parameter (#101351) Co-authored-by: Erlend E. Aasland Co-authored-by: C.A.M. Gerlach --- Doc/library/sqlite3.rst | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index c0f0f133f4a286..bbdc891c930cf4 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -302,10 +302,13 @@ Module functions :type isolation_level: str | None :param bool check_same_thread: - If ``True`` (default), only the creating thread may use the connection. - If ``False``, the connection may be shared across multiple threads; - if so, write operations should be serialized by the user to avoid data - corruption. + If ``True`` (default), :exc:`ProgrammingError` will be raised + if the database connection is used by a thread + other than the one that created it. + If ``False``, the connection may be accessed in multiple threads; + write operations may need to be serialized by the user + to avoid data corruption. + See :attr:`threadsafety` for more information. :param Connection factory: A custom subclass of :class:`Connection` to create the connection with, From 0675b8f032c69d265468b31d5cadac6a7ce4bd9c Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Thu, 2 Feb 2023 10:02:57 +0000 Subject: [PATCH 060/225] gh-98831: rewrite RERAISE and CLEANUP_THROW in the instruction definition DSL (#101511) --- Python/bytecodes.c | 28 +++++++++++----------------- Python/generated_cases.c.h | 32 +++++++++++++++++++------------- Python/opcode_metadata.h | 8 ++++---- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index fb41c8387ccc90..169a2647866b31 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -739,10 +739,10 @@ dummy_func( Py_XSETREF(exc_info->exc_value, exc_value); } - // stack effect: (__0 -- ) - inst(RERAISE) { + inst(RERAISE, (values[oparg], exc -- values[oparg])) { + assert(oparg >= 0 && oparg <= 2); if (oparg) { - PyObject *lasti = PEEK(oparg + 1); + PyObject *lasti = values[0]; if (PyLong_Check(lasti)) { frame->prev_instr = _PyCode_CODE(frame->f_code) + PyLong_AsLong(lasti); assert(!_PyErr_Occurred(tstate)); @@ -753,11 +753,11 @@ dummy_func( goto error; } } - PyObject *val = POP(); - assert(val && PyExceptionInstance_Check(val)); - PyObject *exc = Py_NewRef(PyExceptionInstance_Class(val)); - PyObject *tb = PyException_GetTraceback(val); - _PyErr_Restore(tstate, exc, val, tb); + assert(exc && PyExceptionInstance_Check(exc)); + Py_INCREF(exc); + PyObject *typ = Py_NewRef(PyExceptionInstance_Class(exc)); + PyObject *tb = PyException_GetTraceback(exc); + _PyErr_Restore(tstate, typ, exc, tb); goto exception_unwind; } @@ -784,18 +784,12 @@ dummy_func( } } - // stack effect: (__0, __1 -- ) - inst(CLEANUP_THROW) { + inst(CLEANUP_THROW, (sub_iter, last_sent_val, exc_value -- value)) { assert(throwflag); - PyObject *exc_value = TOP(); assert(exc_value && PyExceptionInstance_Check(exc_value)); if (PyErr_GivenExceptionMatches(exc_value, PyExc_StopIteration)) { - PyObject *value = ((PyStopIterationObject *)exc_value)->value; - Py_INCREF(value); - Py_DECREF(POP()); // The StopIteration. - Py_DECREF(POP()); // The last sent value. - Py_DECREF(POP()); // The delegated sub-iterator. - PUSH(value); + value = Py_NewRef(((PyStopIterationObject *)exc_value)->value); + DECREF_INPUTS(); } else { PyObject *exc_type = Py_NewRef(Py_TYPE(exc_value)); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 77f9b00c4f0abc..97263866fe9158 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -945,8 +945,11 @@ } TARGET(RERAISE) { + PyObject *exc = PEEK(1); + PyObject **values = &PEEK(1 + oparg); + assert(oparg >= 0 && oparg <= 2); if (oparg) { - PyObject *lasti = PEEK(oparg + 1); + PyObject *lasti = values[0]; if (PyLong_Check(lasti)) { frame->prev_instr = _PyCode_CODE(frame->f_code) + PyLong_AsLong(lasti); assert(!_PyErr_Occurred(tstate)); @@ -957,11 +960,11 @@ goto error; } } - PyObject *val = POP(); - assert(val && PyExceptionInstance_Check(val)); - PyObject *exc = Py_NewRef(PyExceptionInstance_Class(val)); - PyObject *tb = PyException_GetTraceback(val); - _PyErr_Restore(tstate, exc, val, tb); + assert(exc && PyExceptionInstance_Check(exc)); + Py_INCREF(exc); + PyObject *typ = Py_NewRef(PyExceptionInstance_Class(exc)); + PyObject *tb = PyException_GetTraceback(exc); + _PyErr_Restore(tstate, typ, exc, tb); goto exception_unwind; } @@ -1001,16 +1004,17 @@ } TARGET(CLEANUP_THROW) { + PyObject *exc_value = PEEK(1); + PyObject *last_sent_val = PEEK(2); + PyObject *sub_iter = PEEK(3); + PyObject *value; assert(throwflag); - PyObject *exc_value = TOP(); assert(exc_value && PyExceptionInstance_Check(exc_value)); if (PyErr_GivenExceptionMatches(exc_value, PyExc_StopIteration)) { - PyObject *value = ((PyStopIterationObject *)exc_value)->value; - Py_INCREF(value); - Py_DECREF(POP()); // The StopIteration. - Py_DECREF(POP()); // The last sent value. - Py_DECREF(POP()); // The delegated sub-iterator. - PUSH(value); + value = Py_NewRef(((PyStopIterationObject *)exc_value)->value); + Py_DECREF(sub_iter); + Py_DECREF(last_sent_val); + Py_DECREF(exc_value); } else { PyObject *exc_type = Py_NewRef(Py_TYPE(exc_value)); @@ -1018,6 +1022,8 @@ _PyErr_Restore(tstate, exc_type, Py_NewRef(exc_value), exc_traceback); goto exception_unwind; } + STACK_SHRINK(2); + POKE(1, value); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index f0bdedeb667d44..ca3dde363bfd85 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -105,13 +105,13 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case POP_EXCEPT: return 1; case RERAISE: - return -1; + return oparg + 1; case PREP_RERAISE_STAR: return 2; case END_ASYNC_FOR: return 2; case CLEANUP_THROW: - return -1; + return 3; case LOAD_ASSERTION_ERROR: return 0; case LOAD_BUILD_CLASS: @@ -451,13 +451,13 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case POP_EXCEPT: return 0; case RERAISE: - return -1; + return oparg; case PREP_RERAISE_STAR: return 1; case END_ASYNC_FOR: return 0; case CLEANUP_THROW: - return -1; + return 1; case LOAD_ASSERTION_ERROR: return 1; case LOAD_BUILD_CLASS: From 24cbc7a2a09bf22ff8122c1d50135e1c56622c95 Mon Sep 17 00:00:00 2001 From: Peter Gessler Date: Thu, 2 Feb 2023 14:12:57 -0600 Subject: [PATCH 061/225] docs: Fix enum reassign `str` documentation (GH-101507) --- Doc/library/enum.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index cfe4fbeb0698e7..13591a1bdc7347 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -922,6 +922,6 @@ Notes or you can reassign the appropriate :meth:`str`, etc., in your enum:: - >>> from enum import IntEnum + >>> from enum import Enum, IntEnum >>> class MyIntEnum(IntEnum): - ... __str__ = IntEnum.__str__ + ... __str__ = Enum.__str__ From ba4731d149185894c77d201bc5804da90ff45eee Mon Sep 17 00:00:00 2001 From: Ayappan Perumal Date: Fri, 3 Feb 2023 02:00:49 +0530 Subject: [PATCH 062/225] gh-96305: Fix AIX build by avoiding subprocess during bootstrap (#96429) * Fix AIX build by avoiding `subprocess` during bootstrap. --- Lib/_aix_support.py | 27 +++++++++++++++++-- ...2-08-30-10-16-31.gh-issue-96305.274i8B.rst | 2 ++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2022-08-30-10-16-31.gh-issue-96305.274i8B.rst diff --git a/Lib/_aix_support.py b/Lib/_aix_support.py index 18533e769b7514..dadc75c2bf4200 100644 --- a/Lib/_aix_support.py +++ b/Lib/_aix_support.py @@ -1,10 +1,28 @@ """Shared AIX support functions.""" -import subprocess import sys import sysconfig +# Taken from _osx_support _read_output function +def _read_cmd_output(commandstring, capture_stderr=False): + """Output from successful command execution or None""" + # Similar to os.popen(commandstring, "r").read(), + # but without actually using os.popen because that + # function is not usable during python bootstrap. + import os + import contextlib + fp = open("/tmp/_aix_support.%s"%( + os.getpid(),), "w+b") + + with contextlib.closing(fp) as fp: + if capture_stderr: + cmd = "%s >'%s' 2>&1" % (commandstring, fp.name) + else: + cmd = "%s 2>/dev/null >'%s'" % (commandstring, fp.name) + return fp.read() if not os.system(cmd) else None + + def _aix_tag(vrtl, bd): # type: (List[int], int) -> str # Infer the ABI bitwidth from maxsize (assuming 64 bit as the default) @@ -30,7 +48,12 @@ def _aix_bos_rte(): If no builddate is found give a value that will satisfy pep425 related queries """ # All AIX systems to have lslpp installed in this location - out = subprocess.check_output(["/usr/bin/lslpp", "-Lqc", "bos.rte"]) + # subprocess may not be available during python bootstrap + try: + import subprocess + out = subprocess.check_output(["/usr/bin/lslpp", "-Lqc", "bos.rte"]) + except ImportError: + out = _read_cmd_output("/usr/bin/lslpp -Lqc bos.rte") out = out.decode("utf-8") out = out.strip().split(":") # type: ignore _bd = int(out[-1]) if out[-1] != '' else 9988 diff --git a/Misc/NEWS.d/next/Build/2022-08-30-10-16-31.gh-issue-96305.274i8B.rst b/Misc/NEWS.d/next/Build/2022-08-30-10-16-31.gh-issue-96305.274i8B.rst new file mode 100644 index 00000000000000..64a48da658f21f --- /dev/null +++ b/Misc/NEWS.d/next/Build/2022-08-30-10-16-31.gh-issue-96305.274i8B.rst @@ -0,0 +1,2 @@ +``_aix_support`` now uses a simple code to get platform details rather than +the now non-existent ``_bootsubprocess`` during bootstrap. From 618b7a8260bb40290d6551f24885931077309590 Mon Sep 17 00:00:00 2001 From: Ayappan Perumal Date: Fri, 3 Feb 2023 02:02:33 +0530 Subject: [PATCH 063/225] gh-98705: Fix AIX build by undefining `__bool__` in C (#98768) --- Include/pyport.h | 6 ++++++ .../Build/2022-10-27-09-57-12.gh-issue-98705.H11XmR.rst | 2 ++ 2 files changed, 8 insertions(+) create mode 100644 Misc/NEWS.d/next/Build/2022-10-27-09-57-12.gh-issue-98705.H11XmR.rst diff --git a/Include/pyport.h b/Include/pyport.h index b1b2a74779691d..22085049a30487 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -726,4 +726,10 @@ extern char * _getpty(int *, int, mode_t, int); # endif #endif + +/* AIX has __bool__ redefined in it's system header file. */ +#if defined(_AIX) && defined(__bool__) +#undef __bool__ +#endif + #endif /* Py_PYPORT_H */ diff --git a/Misc/NEWS.d/next/Build/2022-10-27-09-57-12.gh-issue-98705.H11XmR.rst b/Misc/NEWS.d/next/Build/2022-10-27-09-57-12.gh-issue-98705.H11XmR.rst new file mode 100644 index 00000000000000..4519853cdbadd1 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2022-10-27-09-57-12.gh-issue-98705.H11XmR.rst @@ -0,0 +1,2 @@ +``__bool__`` is defined in AIX system header files which breaks the build in +AIX, so undefine it. From 0ca67e6313c11263ecaef7ce182308eeb5aa6814 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Thu, 2 Feb 2023 15:50:35 -0800 Subject: [PATCH 064/225] GH-84559: Deprecate fork being the multiprocessing default. (#100618) This starts the process. Users who don't specify their own start method and use the default on platforms where it is 'fork' will see a DeprecationWarning upon multiprocessing.Pool() construction or upon multiprocessing.Process.start() or concurrent.futures.ProcessPool use. See the related issue and documentation within this change for details. --- Doc/library/concurrent.futures.rst | 14 ++- Doc/library/multiprocessing.rst | 104 +++++++++++------- Doc/whatsnew/3.12.rst | 8 ++ Lib/compileall.py | 8 +- Lib/concurrent/futures/process.py | 23 +++- Lib/multiprocessing/context.py | 29 ++++- Lib/test/_test_multiprocessing.py | 21 ++-- Lib/test/_test_venv_multiprocessing.py | 1 + Lib/test/test_asyncio/test_events.py | 9 +- Lib/test/test_concurrent_futures.py | 19 ++++ Lib/test/test_fcntl.py | 8 +- Lib/test/test_logging.py | 5 +- Lib/test/test_multiprocessing_defaults.py | 82 ++++++++++++++ Lib/test/test_pickle.py | 2 + Lib/test/test_re.py | 3 +- ...3-01-01-01-19-33.gh-issue-84559.zEjsEJ.rst | 11 ++ 16 files changed, 284 insertions(+), 63 deletions(-) create mode 100644 Lib/test/test_multiprocessing_defaults.py create mode 100644 Misc/NEWS.d/next/Library/2023-01-01-01-19-33.gh-issue-84559.zEjsEJ.rst diff --git a/Doc/library/concurrent.futures.rst b/Doc/library/concurrent.futures.rst index 8106cc235e5a3c..10cffdaee0bb8c 100644 --- a/Doc/library/concurrent.futures.rst +++ b/Doc/library/concurrent.futures.rst @@ -250,9 +250,10 @@ to a :class:`ProcessPoolExecutor` will result in deadlock. then :exc:`ValueError` will be raised. If *max_workers* is ``None``, then the default chosen will be at most ``61``, even if more processors are available. - *mp_context* can be a multiprocessing context or None. It will be used to - launch the workers. If *mp_context* is ``None`` or not given, the default - multiprocessing context is used. + *mp_context* can be a :mod:`multiprocessing` context or ``None``. It will be + used to launch the workers. If *mp_context* is ``None`` or not given, the + default :mod:`multiprocessing` context is used. + See :ref:`multiprocessing-start-methods`. *initializer* is an optional callable that is called at the start of each worker process; *initargs* is a tuple of arguments passed to the @@ -284,6 +285,13 @@ to a :class:`ProcessPoolExecutor` will result in deadlock. The *max_tasks_per_child* argument was added to allow users to control the lifetime of workers in the pool. + .. versionchanged:: 3.12 + The implicit use of the :mod:`multiprocessing` *fork* start method as a + platform default (see :ref:`multiprocessing-start-methods`) now raises a + :exc:`DeprecationWarning`. The default will change in Python 3.14. + Code that requires *fork* should explicitly specify that when creating + their :class:`ProcessPoolExecutor` by passing a + ``mp_context=multiprocessing.get_context('fork')`` parameter. .. _processpoolexecutor-example: diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst index b5ceeb796f8f2f..c60b229ae2d07e 100644 --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -19,7 +19,7 @@ offers both local and remote concurrency, effectively side-stepping the :term:`Global Interpreter Lock ` by using subprocesses instead of threads. Due to this, the :mod:`multiprocessing` module allows the programmer to fully -leverage multiple processors on a given machine. It runs on both Unix and +leverage multiple processors on a given machine. It runs on both POSIX and Windows. The :mod:`multiprocessing` module also introduces APIs which do not have @@ -99,11 +99,11 @@ necessary, see :ref:`multiprocessing-programming`. +.. _multiprocessing-start-methods: + Contexts and start methods ~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. _multiprocessing-start-methods: - Depending on the platform, :mod:`multiprocessing` supports three ways to start a process. These *start methods* are @@ -115,7 +115,7 @@ to start a process. These *start methods* are will not be inherited. Starting a process using this method is rather slow compared to using *fork* or *forkserver*. - Available on Unix and Windows. The default on Windows and macOS. + Available on POSIX and Windows platforms. The default on Windows and macOS. *fork* The parent process uses :func:`os.fork` to fork the Python @@ -124,32 +124,39 @@ to start a process. These *start methods* are inherited by the child process. Note that safely forking a multithreaded process is problematic. - Available on Unix only. The default on Unix. + Available on POSIX systems. Currently the default on POSIX except macOS. *forkserver* When the program starts and selects the *forkserver* start method, - a server process is started. From then on, whenever a new process + a server process is spawned. From then on, whenever a new process is needed, the parent process connects to the server and requests - that it fork a new process. The fork server process is single - threaded so it is safe for it to use :func:`os.fork`. No - unnecessary resources are inherited. + that it fork a new process. The fork server process is single threaded + unless system libraries or preloaded imports spawn threads as a + side-effect so it is generally safe for it to use :func:`os.fork`. + No unnecessary resources are inherited. - Available on Unix platforms which support passing file descriptors - over Unix pipes. + Available on POSIX platforms which support passing file descriptors + over Unix pipes such as Linux. + +.. versionchanged:: 3.12 + Implicit use of the *fork* start method as the default now raises a + :exc:`DeprecationWarning`. Code that requires it should explicitly + specify *fork* via :func:`get_context` or :func:`set_start_method`. + The default will change away from *fork* in 3.14. .. versionchanged:: 3.8 On macOS, the *spawn* start method is now the default. The *fork* start method should be considered unsafe as it can lead to crashes of the - subprocess. See :issue:`33725`. + subprocess as macOS system libraries may start threads. See :issue:`33725`. .. versionchanged:: 3.4 - *spawn* added on all Unix platforms, and *forkserver* added for - some Unix platforms. + *spawn* added on all POSIX platforms, and *forkserver* added for + some POSIX platforms. Child processes no longer inherit all of the parents inheritable handles on Windows. -On Unix using the *spawn* or *forkserver* start methods will also +On POSIX using the *spawn* or *forkserver* start methods will also start a *resource tracker* process which tracks the unlinked named system resources (such as named semaphores or :class:`~multiprocessing.shared_memory.SharedMemory` objects) created @@ -211,10 +218,10 @@ library user. .. warning:: - The ``'spawn'`` and ``'forkserver'`` start methods cannot currently + The ``'spawn'`` and ``'forkserver'`` start methods generally cannot be used with "frozen" executables (i.e., binaries produced by - packages like **PyInstaller** and **cx_Freeze**) on Unix. - The ``'fork'`` start method does work. + packages like **PyInstaller** and **cx_Freeze**) on POSIX systems. + The ``'fork'`` start method may work if code does not use threads. Exchanging objects between processes @@ -629,14 +636,14 @@ The :mod:`multiprocessing` package mostly replicates the API of the calling :meth:`join()` is simpler. On Windows, this is an OS handle usable with the ``WaitForSingleObject`` - and ``WaitForMultipleObjects`` family of API calls. On Unix, this is + and ``WaitForMultipleObjects`` family of API calls. On POSIX, this is a file descriptor usable with primitives from the :mod:`select` module. .. versionadded:: 3.3 .. method:: terminate() - Terminate the process. On Unix this is done using the ``SIGTERM`` signal; + Terminate the process. On POSIX this is done using the ``SIGTERM`` signal; on Windows :c:func:`TerminateProcess` is used. Note that exit handlers and finally clauses, etc., will not be executed. @@ -653,7 +660,7 @@ The :mod:`multiprocessing` package mostly replicates the API of the .. method:: kill() - Same as :meth:`terminate()` but using the ``SIGKILL`` signal on Unix. + Same as :meth:`terminate()` but using the ``SIGKILL`` signal on POSIX. .. versionadded:: 3.7 @@ -676,16 +683,17 @@ The :mod:`multiprocessing` package mostly replicates the API of the .. doctest:: >>> import multiprocessing, time, signal - >>> p = multiprocessing.Process(target=time.sleep, args=(1000,)) + >>> mp_context = multiprocessing.get_context('spawn') + >>> p = mp_context.Process(target=time.sleep, args=(1000,)) >>> print(p, p.is_alive()) - False + <...Process ... initial> False >>> p.start() >>> print(p, p.is_alive()) - True + <...Process ... started> True >>> p.terminate() >>> time.sleep(0.1) >>> print(p, p.is_alive()) - False + <...Process ... stopped exitcode=-SIGTERM> False >>> p.exitcode == -signal.SIGTERM True @@ -815,7 +823,7 @@ For an example of the usage of queues for interprocess communication see Return the approximate size of the queue. Because of multithreading/multiprocessing semantics, this number is not reliable. - Note that this may raise :exc:`NotImplementedError` on Unix platforms like + Note that this may raise :exc:`NotImplementedError` on platforms like macOS where ``sem_getvalue()`` is not implemented. .. method:: empty() @@ -1034,9 +1042,8 @@ Miscellaneous Returns a list of the supported start methods, the first of which is the default. The possible start methods are ``'fork'``, - ``'spawn'`` and ``'forkserver'``. On Windows only ``'spawn'`` is - available. On Unix ``'fork'`` and ``'spawn'`` are always - supported, with ``'fork'`` being the default. + ``'spawn'`` and ``'forkserver'``. Not all platforms support all + methods. See :ref:`multiprocessing-start-methods`. .. versionadded:: 3.4 @@ -1048,7 +1055,7 @@ Miscellaneous If *method* is ``None`` then the default context is returned. Otherwise *method* should be ``'fork'``, ``'spawn'``, ``'forkserver'``. :exc:`ValueError` is raised if the specified - start method is not available. + start method is not available. See :ref:`multiprocessing-start-methods`. .. versionadded:: 3.4 @@ -1062,8 +1069,7 @@ Miscellaneous is true then ``None`` is returned. The return value can be ``'fork'``, ``'spawn'``, ``'forkserver'`` - or ``None``. ``'fork'`` is the default on Unix, while ``'spawn'`` is - the default on Windows and macOS. + or ``None``. See :ref:`multiprocessing-start-methods`. .. versionchanged:: 3.8 @@ -1084,11 +1090,26 @@ Miscellaneous before they can create child processes. .. versionchanged:: 3.4 - Now supported on Unix when the ``'spawn'`` start method is used. + Now supported on POSIX when the ``'spawn'`` start method is used. .. versionchanged:: 3.11 Accepts a :term:`path-like object`. +.. function:: set_forkserver_preload(module_names) + + Set a list of module names for the forkserver main process to attempt to + import so that their already imported state is inherited by forked + processes. Any :exc:`ImportError` when doing so is silently ignored. + This can be used as a performance enhancement to avoid repeated work + in every process. + + For this to work, it must be called before the forkserver process has been + launched (before creating a :class:`Pool` or starting a :class:`Process`). + + Only meaningful when using the ``'forkserver'`` start method. + + .. versionadded:: 3.4 + .. function:: set_start_method(method, force=False) Set the method which should be used to start child processes. @@ -1102,6 +1123,8 @@ Miscellaneous protected inside the ``if __name__ == '__main__'`` clause of the main module. + See :ref:`multiprocessing-start-methods`. + .. versionadded:: 3.4 .. note:: @@ -1906,7 +1929,8 @@ their parent process exits. The manager classes are defined in the .. doctest:: - >>> manager = multiprocessing.Manager() + >>> mp_context = multiprocessing.get_context('spawn') + >>> manager = mp_context.Manager() >>> Global = manager.Namespace() >>> Global.x = 10 >>> Global.y = 'hello' @@ -2018,8 +2042,8 @@ the proxy). In this way, a proxy can be used just like its referent can: .. doctest:: - >>> from multiprocessing import Manager - >>> manager = Manager() + >>> mp_context = multiprocessing.get_context('spawn') + >>> manager = mp_context.Manager() >>> l = manager.list([i*i for i in range(10)]) >>> print(l) [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] @@ -2520,7 +2544,7 @@ multiple connections at the same time. *timeout* is ``None`` then it will block for an unlimited period. A negative timeout is equivalent to a zero timeout. - For both Unix and Windows, an object can appear in *object_list* if + For both POSIX and Windows, an object can appear in *object_list* if it is * a readable :class:`~multiprocessing.connection.Connection` object; @@ -2531,7 +2555,7 @@ multiple connections at the same time. A connection or socket object is ready when there is data available to be read from it, or the other end has been closed. - **Unix**: ``wait(object_list, timeout)`` almost equivalent + **POSIX**: ``wait(object_list, timeout)`` almost equivalent ``select.select(object_list, [], [], timeout)``. The difference is that, if :func:`select.select` is interrupted by a signal, it can raise :exc:`OSError` with an error number of ``EINTR``, whereas @@ -2803,7 +2827,7 @@ Thread safety of proxies Joining zombie processes - On Unix when a process finishes but has not been joined it becomes a zombie. + On POSIX when a process finishes but has not been joined it becomes a zombie. There should never be very many because each time a new process starts (or :func:`~multiprocessing.active_children` is called) all completed processes which have not yet been joined will be joined. Also calling a finished @@ -2866,7 +2890,7 @@ Joining processes that use queues Explicitly pass resources to child processes - On Unix using the *fork* start method, a child process can make + On POSIX using the *fork* start method, a child process can make use of a shared resource created in a parent process using a global resource. However, it is better to pass the object as an argument to the constructor for the child process. diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index a071159b800a34..e675fada339a1e 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -440,6 +440,11 @@ Deprecated warning at compile time. This field will be removed in Python 3.14. (Contributed by Ramvikrams and Kumar Aditya in :gh:`101193`. PEP by Ken Jin.) +* Use of the implicit default ``'fork'`` start method for + :mod:`multiprocessing` and :class:`concurrent.futures.ProcessPoolExecutor` + now emits a :exc:`DeprecationWarning` on Linux and other non-macOS POSIX + systems. Avoid this by explicitly specifying a start method. + See :ref:`multiprocessing-start-methods`. Pending Removal in Python 3.13 ------------------------------ @@ -505,6 +510,9 @@ Pending Removal in Python 3.14 * Testing the truth value of an :class:`xml.etree.ElementTree.Element` is deprecated and will raise an exception in Python 3.14. +* The default :mod:`multiprocessing` start method will change to one of either + ``'forkserver'`` or ``'spawn'`` on all platforms for which ``'fork'`` remains + the default per :gh:`84559`. Pending Removal in Future Versions ---------------------------------- diff --git a/Lib/compileall.py b/Lib/compileall.py index a388931fb5a99d..d394156cedc4e7 100644 --- a/Lib/compileall.py +++ b/Lib/compileall.py @@ -97,9 +97,15 @@ def compile_dir(dir, maxlevels=None, ddir=None, force=False, files = _walk_dir(dir, quiet=quiet, maxlevels=maxlevels) success = True if workers != 1 and ProcessPoolExecutor is not None: + import multiprocessing + if multiprocessing.get_start_method() == 'fork': + mp_context = multiprocessing.get_context('forkserver') + else: + mp_context = None # If workers == 0, let ProcessPoolExecutor choose workers = workers or None - with ProcessPoolExecutor(max_workers=workers) as executor: + with ProcessPoolExecutor(max_workers=workers, + mp_context=mp_context) as executor: results = executor.map(partial(compile_file, ddir=ddir, force=force, rx=rx, quiet=quiet, diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py index 7e2f5fa30e8264..257dd02fbc6cce 100644 --- a/Lib/concurrent/futures/process.py +++ b/Lib/concurrent/futures/process.py @@ -57,6 +57,7 @@ import itertools import sys from traceback import format_exception +import warnings _threads_wakeups = weakref.WeakKeyDictionary() @@ -616,9 +617,9 @@ def __init__(self, max_workers=None, mp_context=None, max_workers: The maximum number of processes that can be used to execute the given calls. If None or not given then as many worker processes will be created as the machine has processors. - mp_context: A multiprocessing context to launch the workers. This - object should provide SimpleQueue, Queue and Process. Useful - to allow specific multiprocessing start methods. + mp_context: A multiprocessing context to launch the workers created + using the multiprocessing.get_context('start method') API. This + object should provide SimpleQueue, Queue and Process. initializer: A callable used to initialize worker processes. initargs: A tuple of arguments to pass to the initializer. max_tasks_per_child: The maximum number of tasks a worker process @@ -650,6 +651,22 @@ def __init__(self, max_workers=None, mp_context=None, mp_context = mp.get_context("spawn") else: mp_context = mp.get_context() + if (mp_context.get_start_method() == "fork" and + mp_context == mp.context._default_context._default_context): + warnings.warn( + "The default multiprocessing start method will change " + "away from 'fork' in Python >= 3.14, per GH-84559. " + "ProcessPoolExecutor uses multiprocessing. " + "If your application requires the 'fork' multiprocessing " + "start method, explicitly specify that by passing a " + "mp_context= parameter. " + "The safest start method is 'spawn'.", + category=mp.context.DefaultForkDeprecationWarning, + stacklevel=2, + ) + # Avoid the equivalent warning from multiprocessing itself via + # a non-default fork context. + mp_context = mp.get_context("fork") self._mp_context = mp_context # https://github.com/python/cpython/issues/90622 diff --git a/Lib/multiprocessing/context.py b/Lib/multiprocessing/context.py index b1960ea296fe20..010a920540e844 100644 --- a/Lib/multiprocessing/context.py +++ b/Lib/multiprocessing/context.py @@ -23,6 +23,9 @@ class TimeoutError(ProcessError): class AuthenticationError(ProcessError): pass +class DefaultForkDeprecationWarning(DeprecationWarning): + pass + # # Base type for contexts. Bound methods of an instance of this type are included in __all__ of __init__.py # @@ -258,6 +261,7 @@ def get_start_method(self, allow_none=False): return self._actual_context._name def get_all_start_methods(self): + """Returns a list of the supported start methods, default first.""" if sys.platform == 'win32': return ['spawn'] else: @@ -280,6 +284,23 @@ def _Popen(process_obj): from .popen_fork import Popen return Popen(process_obj) + _warn_package_prefixes = (os.path.dirname(__file__),) + + class _DeprecatedForkProcess(ForkProcess): + @classmethod + def _Popen(cls, process_obj): + import warnings + warnings.warn( + "The default multiprocessing start method will change " + "away from 'fork' in Python >= 3.14, per GH-84559. " + "Use multiprocessing.get_context(X) or .set_start_method(X) to " + "explicitly specify it when your application requires 'fork'. " + "The safest start method is 'spawn'.", + category=DefaultForkDeprecationWarning, + skip_file_prefixes=_warn_package_prefixes, + ) + return super()._Popen(process_obj) + class SpawnProcess(process.BaseProcess): _start_method = 'spawn' @staticmethod @@ -303,6 +324,9 @@ class ForkContext(BaseContext): _name = 'fork' Process = ForkProcess + class _DefaultForkContext(ForkContext): + Process = _DeprecatedForkProcess + class SpawnContext(BaseContext): _name = 'spawn' Process = SpawnProcess @@ -318,13 +342,16 @@ def _check_available(self): 'fork': ForkContext(), 'spawn': SpawnContext(), 'forkserver': ForkServerContext(), + # Remove None and _DefaultForkContext() when changing the default + # in 3.14 for https://github.com/python/cpython/issues/84559. + None: _DefaultForkContext(), } if sys.platform == 'darwin': # bpo-33725: running arbitrary code after fork() is no longer reliable # on macOS since macOS 10.14 (Mojave). Use spawn by default instead. _default_context = DefaultContext(_concrete_contexts['spawn']) else: - _default_context = DefaultContext(_concrete_contexts['fork']) + _default_context = DefaultContext(_concrete_contexts[None]) else: diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 2fa75eb4d11311..e4a60a4d674607 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -4098,9 +4098,10 @@ def test_shared_memory_SharedMemoryServer_ignores_sigint(self): def test_shared_memory_SharedMemoryManager_reuses_resource_tracker(self): # bpo-36867: test that a SharedMemoryManager uses the # same resource_tracker process as its parent. - cmd = '''if 1: + cmd = f'''if 1: from multiprocessing.managers import SharedMemoryManager - + from multiprocessing import set_start_method + set_start_method({multiprocessing.get_start_method()!r}) smm = SharedMemoryManager() smm.start() @@ -4967,11 +4968,13 @@ def run_in_grandchild(cls, conn): conn.send(tuple(sys.flags)) @classmethod - def run_in_child(cls): + def run_in_child(cls, start_method): import json - r, w = multiprocessing.Pipe(duplex=False) - p = multiprocessing.Process(target=cls.run_in_grandchild, args=(w,)) - p.start() + mp = multiprocessing.get_context(start_method) + r, w = mp.Pipe(duplex=False) + p = mp.Process(target=cls.run_in_grandchild, args=(w,)) + with warnings.catch_warnings(category=DeprecationWarning): + p.start() grandchild_flags = r.recv() p.join() r.close() @@ -4982,8 +4985,10 @@ def run_in_child(cls): def test_flags(self): import json # start child process using unusual flags - prog = ('from test._test_multiprocessing import TestFlags; ' + - 'TestFlags.run_in_child()') + prog = ( + 'from test._test_multiprocessing import TestFlags; ' + f'TestFlags.run_in_child({multiprocessing.get_start_method()!r})' + ) data = subprocess.check_output( [sys.executable, '-E', '-S', '-O', '-c', prog]) child_flags, grandchild_flags = json.loads(data.decode('ascii')) diff --git a/Lib/test/_test_venv_multiprocessing.py b/Lib/test/_test_venv_multiprocessing.py index af72e915ba52bb..044a0c6cd3f5ca 100644 --- a/Lib/test/_test_venv_multiprocessing.py +++ b/Lib/test/_test_venv_multiprocessing.py @@ -30,6 +30,7 @@ def test_func(): def main(): + multiprocessing.set_start_method('spawn') test_pool = multiprocessing.Process(target=test_func) test_pool.start() test_pool.join() diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 214544b89bc558..b9069056c3a436 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -4,6 +4,7 @@ import concurrent.futures import functools import io +import multiprocessing import os import platform import re @@ -2762,7 +2763,13 @@ def test_get_event_loop_new_process(self): support.skip_if_broken_multiprocessing_synchronize() async def main(): - pool = concurrent.futures.ProcessPoolExecutor() + if multiprocessing.get_start_method() == 'fork': + # Avoid 'fork' DeprecationWarning. + mp_context = multiprocessing.get_context('forkserver') + else: + mp_context = None + pool = concurrent.futures.ProcessPoolExecutor( + mp_context=mp_context) result = await self.loop.run_in_executor( pool, _test_get_event_loop_new_process__sub_proc) pool.shutdown() diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py index b3520ae3994e03..4493cd312528d6 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py @@ -18,6 +18,7 @@ import threading import time import unittest +import warnings import weakref from pickle import PicklingError @@ -571,6 +572,24 @@ def test_shutdown_no_wait(self): assert all([r == abs(v) for r, v in zip(res, range(-5, 5))]) +@unittest.skipIf(mp.get_all_start_methods()[0] != "fork", "non-fork default.") +class ProcessPoolExecutorDefaultForkWarning(unittest.TestCase): + def test_fork_default_warns(self): + with self.assertWarns(mp.context.DefaultForkDeprecationWarning): + with futures.ProcessPoolExecutor(2): + pass + + def test_explicit_fork_does_not_warn(self): + with warnings.catch_warnings(record=True) as ws: + warnings.simplefilter("ignore") + warnings.filterwarnings( + 'always', category=mp.context.DefaultForkDeprecationWarning) + ctx = mp.get_context("fork") # Non-default fork context. + with futures.ProcessPoolExecutor(2, mp_context=ctx): + pass + self.assertEqual(len(ws), 0, msg=[str(x) for x in ws]) + + create_executor_tests(ProcessPoolShutdownTest, executor_mixins=(ProcessPoolForkMixin, ProcessPoolForkserverMixin, diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py index fc8c39365f12b7..113c7802821dd4 100644 --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py @@ -1,11 +1,11 @@ """Test program for the fcntl C module. """ +import multiprocessing import platform import os import struct import sys import unittest -from multiprocessing import Process from test.support import verbose, cpython_only from test.support.import_helper import import_module from test.support.os_helper import TESTFN, unlink @@ -160,7 +160,8 @@ def test_lockf_exclusive(self): self.f = open(TESTFN, 'wb+') cmd = fcntl.LOCK_EX | fcntl.LOCK_NB fcntl.lockf(self.f, cmd) - p = Process(target=try_lockf_on_other_process_fail, args=(TESTFN, cmd)) + mp = multiprocessing.get_context('spawn') + p = mp.Process(target=try_lockf_on_other_process_fail, args=(TESTFN, cmd)) p.start() p.join() fcntl.lockf(self.f, fcntl.LOCK_UN) @@ -171,7 +172,8 @@ def test_lockf_share(self): self.f = open(TESTFN, 'wb+') cmd = fcntl.LOCK_SH | fcntl.LOCK_NB fcntl.lockf(self.f, cmd) - p = Process(target=try_lockf_on_other_process, args=(TESTFN, cmd)) + mp = multiprocessing.get_context('spawn') + p = mp.Process(target=try_lockf_on_other_process, args=(TESTFN, cmd)) p.start() p.join() fcntl.lockf(self.f, fcntl.LOCK_UN) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 072056d3722106..8a12d570f26f13 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -4759,8 +4759,9 @@ def test_multiprocessing(self): # In other processes, processName is correct when multiprocessing in imported, # but it is (incorrectly) defaulted to 'MainProcess' otherwise (bpo-38762). import multiprocessing - parent_conn, child_conn = multiprocessing.Pipe() - p = multiprocessing.Process( + mp = multiprocessing.get_context('spawn') + parent_conn, child_conn = mp.Pipe() + p = mp.Process( target=self._extract_logrecord_process_name, args=(2, LOG_MULTI_PROCESSING, child_conn,) ) diff --git a/Lib/test/test_multiprocessing_defaults.py b/Lib/test/test_multiprocessing_defaults.py new file mode 100644 index 00000000000000..1da4c065238384 --- /dev/null +++ b/Lib/test/test_multiprocessing_defaults.py @@ -0,0 +1,82 @@ +"""Test default behavior of multiprocessing.""" + +from inspect import currentframe, getframeinfo +import multiprocessing +from multiprocessing.context import DefaultForkDeprecationWarning +import sys +from test.support import threading_helper +import unittest +import warnings + + +def do_nothing(): + pass + + +# Process has the same API as Thread so this helper works. +join_process = threading_helper.join_thread + + +class DefaultWarningsTest(unittest.TestCase): + + @unittest.skipIf(sys.platform in ('win32', 'darwin'), + 'The default is not "fork" on Windows or macOS.') + def setUp(self): + self.assertEqual(multiprocessing.get_start_method(), 'fork') + self.assertIsInstance(multiprocessing.get_context(), + multiprocessing.context._DefaultForkContext) + + def test_default_fork_start_method_warning_process(self): + with warnings.catch_warnings(record=True) as ws: + warnings.simplefilter('ignore') + warnings.filterwarnings('always', category=DefaultForkDeprecationWarning) + process = multiprocessing.Process(target=do_nothing) + process.start() # warning should point here. + join_process(process) + self.assertIsInstance(ws[0].message, DefaultForkDeprecationWarning) + self.assertIn(__file__, ws[0].filename) + self.assertEqual(getframeinfo(currentframe()).lineno-4, ws[0].lineno) + self.assertIn("'fork'", str(ws[0].message)) + self.assertIn("get_context", str(ws[0].message)) + self.assertEqual(len(ws), 1, msg=[str(x) for x in ws]) + + def test_default_fork_start_method_warning_pool(self): + with warnings.catch_warnings(record=True) as ws: + warnings.simplefilter('ignore') + warnings.filterwarnings('always', category=DefaultForkDeprecationWarning) + pool = multiprocessing.Pool(1) # warning should point here. + pool.terminate() + pool.join() + self.assertIsInstance(ws[0].message, DefaultForkDeprecationWarning) + self.assertIn(__file__, ws[0].filename) + self.assertEqual(getframeinfo(currentframe()).lineno-5, ws[0].lineno) + self.assertIn("'fork'", str(ws[0].message)) + self.assertIn("get_context", str(ws[0].message)) + self.assertEqual(len(ws), 1, msg=[str(x) for x in ws]) + + def test_default_fork_start_method_warning_manager(self): + with warnings.catch_warnings(record=True) as ws: + warnings.simplefilter('ignore') + warnings.filterwarnings('always', category=DefaultForkDeprecationWarning) + manager = multiprocessing.Manager() # warning should point here. + manager.shutdown() + self.assertIsInstance(ws[0].message, DefaultForkDeprecationWarning) + self.assertIn(__file__, ws[0].filename) + self.assertEqual(getframeinfo(currentframe()).lineno-4, ws[0].lineno) + self.assertIn("'fork'", str(ws[0].message)) + self.assertIn("get_context", str(ws[0].message)) + self.assertEqual(len(ws), 1, msg=[str(x) for x in ws]) + + def test_no_mp_warning_when_using_explicit_fork_context(self): + with warnings.catch_warnings(record=True) as ws: + warnings.simplefilter('ignore') + warnings.filterwarnings('always', category=DefaultForkDeprecationWarning) + fork_mp = multiprocessing.get_context('fork') + pool = fork_mp.Pool(1) + pool.terminate() + pool.join() + self.assertEqual(len(ws), 0, msg=[str(x) for x in ws]) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_pickle.py b/Lib/test/test_pickle.py index 44fdca7a6b1688..80e7a4d23a4ba8 100644 --- a/Lib/test/test_pickle.py +++ b/Lib/test/test_pickle.py @@ -533,6 +533,8 @@ def test_exceptions(self): def test_multiprocessing_exceptions(self): module = import_helper.import_module('multiprocessing.context') for name, exc in get_exceptions(module): + if issubclass(exc, Warning): + continue with self.subTest(name): self.assertEqual(reverse_mapping('multiprocessing.context', name), ('multiprocessing', name)) diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 11628a236ade9a..eacb1a7c82a54d 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -2431,7 +2431,8 @@ def test_regression_gh94675(self): input_js = '''a(function() { /////////////////////////////////////////////////////////////////// });''' - p = multiprocessing.Process(target=pattern.sub, args=('', input_js)) + mp = multiprocessing.get_context('spawn') + p = mp.Process(target=pattern.sub, args=('', input_js)) p.start() p.join(SHORT_TIMEOUT) try: diff --git a/Misc/NEWS.d/next/Library/2023-01-01-01-19-33.gh-issue-84559.zEjsEJ.rst b/Misc/NEWS.d/next/Library/2023-01-01-01-19-33.gh-issue-84559.zEjsEJ.rst new file mode 100644 index 00000000000000..3793e0f1fddb20 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-01-01-19-33.gh-issue-84559.zEjsEJ.rst @@ -0,0 +1,11 @@ +The :mod:`multiprocessing` module and +:class:`concurrent.futures.ProcessPoolExecutor` will emit a +:exc:`DeprecationWarning` on Linux and other non-macOS POSIX systems when +the default multiprocessing start method of ``'fork'`` is used implicitly +rather than being explicitly specified through a +:func:`multiprocessing.get_context` context. + +This is in preparation for default start method to change in Python 3.14 to +a default that is safe for multithreaded applications. + +Windows and macOS are unaffected as their default start method is ``spawn``. From 1b6045668d233269f667c4658c7240256f37f111 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Thu, 2 Feb 2023 18:03:27 -0600 Subject: [PATCH 065/225] gh-100925: Move array methods under class in array doc (#101485) * Move array methods under class in array doc * Fix a few internal references related to the touched lines --- Doc/library/array.rst | 179 +++++++++++++++++++++--------------------- 1 file changed, 89 insertions(+), 90 deletions(-) diff --git a/Doc/library/array.rst b/Doc/library/array.rst index 95f1eaf401b052..75c49e0f6d1ebe 100644 --- a/Doc/library/array.rst +++ b/Doc/library/array.rst @@ -60,7 +60,7 @@ Notes: The actual representation of values is determined by the machine architecture (strictly speaking, by the C implementation). The actual size can be accessed -through the :attr:`itemsize` attribute. +through the :attr:`array.itemsize` attribute. The module defines the following item: @@ -85,161 +85,160 @@ The module defines the following type: to add initial items to the array. Otherwise, the iterable initializer is passed to the :meth:`extend` method. - .. audit-event:: array.__new__ typecode,initializer array.array + Array objects support the ordinary sequence operations of indexing, slicing, + concatenation, and multiplication. When using slice assignment, the assigned + value must be an array object with the same type code; in all other cases, + :exc:`TypeError` is raised. Array objects also implement the buffer interface, + and may be used wherever :term:`bytes-like objects ` are supported. + .. audit-event:: array.__new__ typecode,initializer array.array -Array objects support the ordinary sequence operations of indexing, slicing, -concatenation, and multiplication. When using slice assignment, the assigned -value must be an array object with the same type code; in all other cases, -:exc:`TypeError` is raised. Array objects also implement the buffer interface, -and may be used wherever :term:`bytes-like objects ` are supported. -The following data items and methods are also supported: + .. attribute:: typecode -.. attribute:: array.typecode + The typecode character used to create the array. - The typecode character used to create the array. + .. attribute:: itemsize -.. attribute:: array.itemsize + The length in bytes of one array item in the internal representation. - The length in bytes of one array item in the internal representation. + .. method:: append(x) -.. method:: array.append(x) + Append a new item with value *x* to the end of the array. - Append a new item with value *x* to the end of the array. + .. method:: buffer_info() -.. method:: array.buffer_info() + Return a tuple ``(address, length)`` giving the current memory address and the + length in elements of the buffer used to hold array's contents. The size of the + memory buffer in bytes can be computed as ``array.buffer_info()[1] * + array.itemsize``. This is occasionally useful when working with low-level (and + inherently unsafe) I/O interfaces that require memory addresses, such as certain + :c:func:`!ioctl` operations. The returned numbers are valid as long as the array + exists and no length-changing operations are applied to it. - Return a tuple ``(address, length)`` giving the current memory address and the - length in elements of the buffer used to hold array's contents. The size of the - memory buffer in bytes can be computed as ``array.buffer_info()[1] * - array.itemsize``. This is occasionally useful when working with low-level (and - inherently unsafe) I/O interfaces that require memory addresses, such as certain - :c:func:`ioctl` operations. The returned numbers are valid as long as the array - exists and no length-changing operations are applied to it. + .. note:: - .. note:: + When using array objects from code written in C or C++ (the only way to + effectively make use of this information), it makes more sense to use the buffer + interface supported by array objects. This method is maintained for backward + compatibility and should be avoided in new code. The buffer interface is + documented in :ref:`bufferobjects`. - When using array objects from code written in C or C++ (the only way to - effectively make use of this information), it makes more sense to use the buffer - interface supported by array objects. This method is maintained for backward - compatibility and should be avoided in new code. The buffer interface is - documented in :ref:`bufferobjects`. + .. method:: byteswap() -.. method:: array.byteswap() + "Byteswap" all items of the array. This is only supported for values which are + 1, 2, 4, or 8 bytes in size; for other types of values, :exc:`RuntimeError` is + raised. It is useful when reading data from a file written on a machine with a + different byte order. - "Byteswap" all items of the array. This is only supported for values which are - 1, 2, 4, or 8 bytes in size; for other types of values, :exc:`RuntimeError` is - raised. It is useful when reading data from a file written on a machine with a - different byte order. + .. method:: count(x) -.. method:: array.count(x) + Return the number of occurrences of *x* in the array. - Return the number of occurrences of *x* in the array. + .. method:: extend(iterable) -.. method:: array.extend(iterable) + Append items from *iterable* to the end of the array. If *iterable* is another + array, it must have *exactly* the same type code; if not, :exc:`TypeError` will + be raised. If *iterable* is not an array, it must be iterable and its elements + must be the right type to be appended to the array. - Append items from *iterable* to the end of the array. If *iterable* is another - array, it must have *exactly* the same type code; if not, :exc:`TypeError` will - be raised. If *iterable* is not an array, it must be iterable and its elements - must be the right type to be appended to the array. + .. method:: frombytes(s) -.. method:: array.frombytes(s) + Appends items from the string, interpreting the string as an array of machine + values (as if it had been read from a file using the :meth:`fromfile` method). - Appends items from the string, interpreting the string as an array of machine - values (as if it had been read from a file using the :meth:`fromfile` method). + .. versionadded:: 3.2 + :meth:`!fromstring` is renamed to :meth:`frombytes` for clarity. - .. versionadded:: 3.2 - :meth:`fromstring` is renamed to :meth:`frombytes` for clarity. + .. method:: fromfile(f, n) -.. method:: array.fromfile(f, n) + Read *n* items (as machine values) from the :term:`file object` *f* and append + them to the end of the array. If less than *n* items are available, + :exc:`EOFError` is raised, but the items that were available are still + inserted into the array. - Read *n* items (as machine values) from the :term:`file object` *f* and append - them to the end of the array. If less than *n* items are available, - :exc:`EOFError` is raised, but the items that were available are still - inserted into the array. + .. method:: fromlist(list) -.. method:: array.fromlist(list) + Append items from the list. This is equivalent to ``for x in list: + a.append(x)`` except that if there is a type error, the array is unchanged. - Append items from the list. This is equivalent to ``for x in list: - a.append(x)`` except that if there is a type error, the array is unchanged. + .. method:: fromunicode(s) -.. method:: array.fromunicode(s) + Extends this array with data from the given unicode string. The array must + be a type ``'u'`` array; otherwise a :exc:`ValueError` is raised. Use + ``array.frombytes(unicodestring.encode(enc))`` to append Unicode data to an + array of some other type. - Extends this array with data from the given unicode string. The array must - be a type ``'u'`` array; otherwise a :exc:`ValueError` is raised. Use - ``array.frombytes(unicodestring.encode(enc))`` to append Unicode data to an - array of some other type. + .. method:: index(x[, start[, stop]]) -.. method:: array.index(x[, start[, stop]]) + Return the smallest *i* such that *i* is the index of the first occurrence of + *x* in the array. The optional arguments *start* and *stop* can be + specified to search for *x* within a subsection of the array. Raise + :exc:`ValueError` if *x* is not found. - Return the smallest *i* such that *i* is the index of the first occurrence of - *x* in the array. The optional arguments *start* and *stop* can be - specified to search for *x* within a subsection of the array. Raise - :exc:`ValueError` if *x* is not found. + .. versionchanged:: 3.10 + Added optional *start* and *stop* parameters. - .. versionchanged:: 3.10 - Added optional *start* and *stop* parameters. -.. method:: array.insert(i, x) + .. method:: insert(i, x) - Insert a new item with value *x* in the array before position *i*. Negative - values are treated as being relative to the end of the array. + Insert a new item with value *x* in the array before position *i*. Negative + values are treated as being relative to the end of the array. -.. method:: array.pop([i]) + .. method:: pop([i]) - Removes the item with the index *i* from the array and returns it. The optional - argument defaults to ``-1``, so that by default the last item is removed and - returned. + Removes the item with the index *i* from the array and returns it. The optional + argument defaults to ``-1``, so that by default the last item is removed and + returned. -.. method:: array.remove(x) + .. method:: remove(x) - Remove the first occurrence of *x* from the array. + Remove the first occurrence of *x* from the array. -.. method:: array.reverse() + .. method:: reverse() - Reverse the order of the items in the array. + Reverse the order of the items in the array. -.. method:: array.tobytes() + .. method:: tobytes() - Convert the array to an array of machine values and return the bytes - representation (the same sequence of bytes that would be written to a file by - the :meth:`tofile` method.) + Convert the array to an array of machine values and return the bytes + representation (the same sequence of bytes that would be written to a file by + the :meth:`tofile` method.) - .. versionadded:: 3.2 - :meth:`tostring` is renamed to :meth:`tobytes` for clarity. + .. versionadded:: 3.2 + :meth:`!tostring` is renamed to :meth:`tobytes` for clarity. -.. method:: array.tofile(f) + .. method:: tofile(f) - Write all items (as machine values) to the :term:`file object` *f*. + Write all items (as machine values) to the :term:`file object` *f*. -.. method:: array.tolist() + .. method:: tolist() - Convert the array to an ordinary list with the same items. + Convert the array to an ordinary list with the same items. -.. method:: array.tounicode() + .. method:: tounicode() - Convert the array to a unicode string. The array must be a type ``'u'`` array; - otherwise a :exc:`ValueError` is raised. Use ``array.tobytes().decode(enc)`` to - obtain a unicode string from an array of some other type. + Convert the array to a unicode string. The array must be a type ``'u'`` array; + otherwise a :exc:`ValueError` is raised. Use ``array.tobytes().decode(enc)`` to + obtain a unicode string from an array of some other type. When an array object is printed or converted to a string, it is represented as From 5dcae3f0c3e9072251217e814a9438670e5f1e40 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Thu, 2 Feb 2023 17:14:23 -0800 Subject: [PATCH 066/225] gh-84559: skip the test when no multiprocessing (wasm, etc) (#101530) skip test when no _multiprocessing (wasm, etc) --- Lib/test/test_multiprocessing_defaults.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_multiprocessing_defaults.py b/Lib/test/test_multiprocessing_defaults.py index 1da4c065238384..7ea872fef20dd9 100644 --- a/Lib/test/test_multiprocessing_defaults.py +++ b/Lib/test/test_multiprocessing_defaults.py @@ -4,10 +4,13 @@ import multiprocessing from multiprocessing.context import DefaultForkDeprecationWarning import sys -from test.support import threading_helper +from test.support import import_helper, threading_helper import unittest import warnings +# Skip tests if _multiprocessing wasn't built. +import_helper.import_module('_multiprocessing') + def do_nothing(): pass From 5c39daf50b7f388f9b24bb2d6ef415955440bebf Mon Sep 17 00:00:00 2001 From: Viet Than Date: Fri, 3 Feb 2023 02:18:39 -0500 Subject: [PATCH 067/225] gh-100920: Update documentation for `asyncio.StreamWriter.wait_closed` (#101514) --- Doc/library/asyncio-stream.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst index 533bdec8e03993..3b3c68ab6ef625 100644 --- a/Doc/library/asyncio-stream.rst +++ b/Doc/library/asyncio-stream.rst @@ -295,7 +295,8 @@ StreamWriter The method closes the stream and the underlying socket. - The method should be used along with the ``wait_closed()`` method:: + The method should be used, though not mandatory, + along with the ``wait_closed()`` method:: stream.close() await stream.wait_closed() @@ -372,7 +373,8 @@ StreamWriter Wait until the stream is closed. Should be called after :meth:`close` to wait until the underlying - connection is closed. + connection is closed, ensuring that all data has been flushed + before e.g. exiting the program. .. versionadded:: 3.7 @@ -402,6 +404,7 @@ TCP echo client using the :func:`asyncio.open_connection` function:: print('Close the connection') writer.close() + await writer.wait_closed() asyncio.run(tcp_echo_client('Hello World!')) @@ -434,6 +437,7 @@ TCP echo server using the :func:`asyncio.start_server` function:: print("Close the connection") writer.close() + await writer.wait_closed() async def main(): server = await asyncio.start_server( @@ -490,6 +494,7 @@ Simple example querying HTTP headers of the URL passed on the command line:: # Ignore the body, close the socket writer.close() + await writer.wait_closed() url = sys.argv[1] asyncio.run(print_http_headers(url)) @@ -535,6 +540,7 @@ Coroutine waiting until a socket receives data using the # Got data, we are done: close the socket print("Received:", data.decode()) writer.close() + await writer.wait_closed() # Close the second socket wsock.close() From 45d014e03ba7ba4c9c912120ec36b2aca02061f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Magimel?= Date: Fri, 3 Feb 2023 08:23:11 +0100 Subject: [PATCH 068/225] docs: replace PyPI description with link (#101506) --- Doc/tutorial/venv.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/tutorial/venv.rst b/Doc/tutorial/venv.rst index 1fdb370b33d5af..05f0e6bbcc1b04 100644 --- a/Doc/tutorial/venv.rst +++ b/Doc/tutorial/venv.rst @@ -98,8 +98,8 @@ Managing Packages with pip ========================== You can install, upgrade, and remove packages using a program called -:program:`pip`. By default ``pip`` will install packages from the Python -Package Index, . You can browse the Python +:program:`pip`. By default ``pip`` will install packages from the `Python +Package Index `_. You can browse the Python Package Index by going to it in your web browser. ``pip`` has a number of subcommands: "install", "uninstall", From a52cc9853fed39b5cc90b7ffb6a64249307a990b Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 3 Feb 2023 10:54:27 +0100 Subject: [PATCH 069/225] gh-101277: Port more `itertools` static types to heap types (#101303) Add dropwhile, takewhile, starmap, combinations*, and permutations types to module state. --- Modules/clinic/itertoolsmodule.c.h | 10 +- Modules/itertoolsmodule.c | 520 +++++++++++------------------ 2 files changed, 202 insertions(+), 328 deletions(-) diff --git a/Modules/clinic/itertoolsmodule.c.h b/Modules/clinic/itertoolsmodule.c.h index c492c33daea5a2..be44246cc9705a 100644 --- a/Modules/clinic/itertoolsmodule.c.h +++ b/Modules/clinic/itertoolsmodule.c.h @@ -345,7 +345,7 @@ static PyObject * itertools_cycle(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - PyTypeObject *base_tp = &cycle_type; + PyTypeObject *base_tp = clinic_state()->cycle_type; PyObject *iterable; if ((type == base_tp || type->tp_init == base_tp->tp_init) && @@ -377,7 +377,7 @@ static PyObject * itertools_dropwhile(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - PyTypeObject *base_tp = &dropwhile_type; + PyTypeObject *base_tp = clinic_state()->dropwhile_type; PyObject *func; PyObject *seq; @@ -409,7 +409,7 @@ static PyObject * itertools_takewhile(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - PyTypeObject *base_tp = &takewhile_type; + PyTypeObject *base_tp = clinic_state()->takewhile_type; PyObject *func; PyObject *seq; @@ -441,7 +441,7 @@ static PyObject * itertools_starmap(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - PyTypeObject *base_tp = &starmap_type; + PyTypeObject *base_tp = clinic_state()->starmap_type; PyObject *func; PyObject *seq; @@ -913,4 +913,4 @@ itertools_count(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=c3069caac417e165 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b86fcd99bd32145e input=a9049054013a1b77]*/ diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index a2ee48228b5847..c9baa47e2c0edd 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -12,8 +12,15 @@ */ typedef struct { + PyTypeObject *combinations_type; + PyTypeObject *cwr_type; + PyTypeObject *cycle_type; + PyTypeObject *dropwhile_type; PyTypeObject *groupby_type; PyTypeObject *_grouper_type; + PyTypeObject *permutations_type; + PyTypeObject *starmap_type; + PyTypeObject *takewhile_type; } itertools_state; static inline itertools_state * @@ -50,32 +57,25 @@ class itertools._grouper "_grouperobject *" "clinic_state()->_grouper_type" class itertools.teedataobject "teedataobject *" "&teedataobject_type" class itertools._tee "teeobject *" "&tee_type" class itertools.batched "batchedobject *" "&batched_type" -class itertools.cycle "cycleobject *" "&cycle_type" -class itertools.dropwhile "dropwhileobject *" "&dropwhile_type" -class itertools.takewhile "takewhileobject *" "&takewhile_type" -class itertools.starmap "starmapobject *" "&starmap_type" +class itertools.cycle "cycleobject *" "clinic_state()->cycle_type" +class itertools.dropwhile "dropwhileobject *" "clinic_state()->dropwhile_type" +class itertools.takewhile "takewhileobject *" "clinic_state()->takewhile_type" +class itertools.starmap "starmapobject *" "clinic_state()->starmap_type" class itertools.chain "chainobject *" "&chain_type" -class itertools.combinations "combinationsobject *" "&combinations_type" -class itertools.combinations_with_replacement "cwr_object *" "&cwr_type" -class itertools.permutations "permutationsobject *" "&permutations_type" +class itertools.combinations "combinationsobject *" "clinic_state()->combinations_type" +class itertools.combinations_with_replacement "cwr_object *" "clinic_state()->cwr_type" +class itertools.permutations "permutationsobject *" "clinic_state()->permutations_type" class itertools.accumulate "accumulateobject *" "&accumulate_type" class itertools.compress "compressobject *" "&compress_type" class itertools.filterfalse "filterfalseobject *" "&filterfalse_type" class itertools.count "countobject *" "&count_type" class itertools.pairwise "pairwiseobject *" "&pairwise_type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=424108522584b55b]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=1790ac655869a651]*/ static PyTypeObject teedataobject_type; static PyTypeObject tee_type; static PyTypeObject batched_type; -static PyTypeObject cycle_type; -static PyTypeObject dropwhile_type; -static PyTypeObject takewhile_type; -static PyTypeObject starmap_type; -static PyTypeObject combinations_type; -static PyTypeObject cwr_type; -static PyTypeObject permutations_type; static PyTypeObject accumulate_type; static PyTypeObject compress_type; static PyTypeObject filterfalse_type; @@ -1286,15 +1286,18 @@ itertools_cycle_impl(PyTypeObject *type, PyObject *iterable) static void cycle_dealloc(cycleobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->it); Py_XDECREF(lz->saved); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int cycle_traverse(cycleobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->it); Py_VISIT(lz->saved); return 0; @@ -1381,48 +1384,25 @@ static PyMethodDef cycle_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject cycle_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.cycle", /* tp_name */ - sizeof(cycleobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)cycle_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - itertools_cycle__doc__, /* tp_doc */ - (traverseproc)cycle_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)cycle_next, /* tp_iternext */ - cycle_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_cycle, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot cycle_slots[] = { + {Py_tp_dealloc, cycle_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_cycle__doc__}, + {Py_tp_traverse, cycle_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, cycle_next}, + {Py_tp_methods, cycle_methods}, + {Py_tp_new, itertools_cycle}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec cycle_spec = { + .name = "itertools.cycle", + .basicsize = sizeof(cycleobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = cycle_slots, }; @@ -1474,15 +1454,18 @@ itertools_dropwhile_impl(PyTypeObject *type, PyObject *func, PyObject *seq) static void dropwhile_dealloc(dropwhileobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->func); Py_XDECREF(lz->it); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int dropwhile_traverse(dropwhileobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->it); Py_VISIT(lz->func); return 0; @@ -1545,48 +1528,25 @@ static PyMethodDef dropwhile_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject dropwhile_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.dropwhile", /* tp_name */ - sizeof(dropwhileobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)dropwhile_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - itertools_dropwhile__doc__, /* tp_doc */ - (traverseproc)dropwhile_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)dropwhile_next, /* tp_iternext */ - dropwhile_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_dropwhile, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot dropwhile_slots[] = { + {Py_tp_dealloc, dropwhile_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_dropwhile__doc__}, + {Py_tp_traverse, dropwhile_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, dropwhile_next}, + {Py_tp_methods, dropwhile_methods}, + {Py_tp_new, itertools_dropwhile}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec dropwhile_spec = { + .name = "itertools.dropwhile", + .basicsize = sizeof(dropwhileobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = dropwhile_slots, }; @@ -1636,15 +1596,18 @@ itertools_takewhile_impl(PyTypeObject *type, PyObject *func, PyObject *seq) static void takewhile_dealloc(takewhileobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->func); Py_XDECREF(lz->it); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int takewhile_traverse(takewhileobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->it); Py_VISIT(lz->func); return 0; @@ -1704,48 +1667,25 @@ static PyMethodDef takewhile_reduce_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject takewhile_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.takewhile", /* tp_name */ - sizeof(takewhileobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)takewhile_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - itertools_takewhile__doc__, /* tp_doc */ - (traverseproc)takewhile_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)takewhile_next, /* tp_iternext */ - takewhile_reduce_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_takewhile, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot takewhile_slots[] = { + {Py_tp_dealloc, takewhile_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_takewhile__doc__}, + {Py_tp_traverse, takewhile_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, takewhile_next}, + {Py_tp_methods, takewhile_reduce_methods}, + {Py_tp_new, itertools_takewhile}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec takewhile_spec = { + .name = "itertools.takewhile", + .basicsize = sizeof(takewhileobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = takewhile_slots, }; @@ -2052,15 +1992,18 @@ itertools_starmap_impl(PyTypeObject *type, PyObject *func, PyObject *seq) static void starmap_dealloc(starmapobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->func); Py_XDECREF(lz->it); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int starmap_traverse(starmapobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->it); Py_VISIT(lz->func); return 0; @@ -2101,48 +2044,25 @@ static PyMethodDef starmap_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject starmap_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.starmap", /* tp_name */ - sizeof(starmapobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)starmap_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - itertools_starmap__doc__, /* tp_doc */ - (traverseproc)starmap_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)starmap_next, /* tp_iternext */ - starmap_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_starmap, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot starmap_slots[] = { + {Py_tp_dealloc, starmap_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_starmap__doc__}, + {Py_tp_traverse, starmap_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, starmap_next}, + {Py_tp_methods, starmap_methods}, + {Py_tp_new, itertools_starmap}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec starmap_spec = { + .name = "itertools.starmap", + .basicsize = sizeof(starmapobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = starmap_slots, }; @@ -2799,12 +2719,14 @@ itertools_combinations_impl(PyTypeObject *type, PyObject *iterable, static void combinations_dealloc(combinationsobject *co) { + PyTypeObject *tp = Py_TYPE(co); PyObject_GC_UnTrack(co); Py_XDECREF(co->pool); Py_XDECREF(co->result); if (co->indices != NULL) PyMem_Free(co->indices); - Py_TYPE(co)->tp_free(co); + tp->tp_free(co); + Py_DECREF(tp); } static PyObject * @@ -2818,6 +2740,7 @@ combinations_sizeof(combinationsobject *co, void *unused) static int combinations_traverse(combinationsobject *co, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(co)); Py_VISIT(co->pool); Py_VISIT(co->result); return 0; @@ -2988,48 +2911,25 @@ static PyMethodDef combinations_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject combinations_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.combinations", /* tp_name */ - sizeof(combinationsobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)combinations_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - itertools_combinations__doc__, /* tp_doc */ - (traverseproc)combinations_traverse,/* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)combinations_next, /* tp_iternext */ - combinations_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_combinations, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot combinations_slots[] = { + {Py_tp_dealloc, combinations_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_combinations__doc__}, + {Py_tp_traverse, combinations_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, combinations_next}, + {Py_tp_methods, combinations_methods}, + {Py_tp_new, itertools_combinations}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec combinations_spec = { + .name = "itertools.combinations", + .basicsize = sizeof(combinationsobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = combinations_slots, }; @@ -3133,12 +3033,14 @@ itertools_combinations_with_replacement_impl(PyTypeObject *type, static void cwr_dealloc(cwrobject *co) { + PyTypeObject *tp = Py_TYPE(co); PyObject_GC_UnTrack(co); Py_XDECREF(co->pool); Py_XDECREF(co->result); if (co->indices != NULL) PyMem_Free(co->indices); - Py_TYPE(co)->tp_free(co); + tp->tp_free(co); + Py_DECREF(tp); } static PyObject * @@ -3152,6 +3054,7 @@ cwr_sizeof(cwrobject *co, void *unused) static int cwr_traverse(cwrobject *co, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(co)); Py_VISIT(co->pool); Py_VISIT(co->result); return 0; @@ -3312,48 +3215,25 @@ static PyMethodDef cwr_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject cwr_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.combinations_with_replacement", /* tp_name */ - sizeof(cwrobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)cwr_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - itertools_combinations_with_replacement__doc__, /* tp_doc */ - (traverseproc)cwr_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)cwr_next, /* tp_iternext */ - cwr_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_combinations_with_replacement, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot cwr_slots[] = { + {Py_tp_dealloc, cwr_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_combinations_with_replacement__doc__}, + {Py_tp_traverse, cwr_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, cwr_next}, + {Py_tp_methods, cwr_methods}, + {Py_tp_new, itertools_combinations_with_replacement}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec cwr_spec = { + .name = "itertools.combinations_with_replacement", + .basicsize = sizeof(cwrobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = cwr_slots, }; @@ -3476,12 +3356,14 @@ itertools_permutations_impl(PyTypeObject *type, PyObject *iterable, static void permutations_dealloc(permutationsobject *po) { + PyTypeObject *tp = Py_TYPE(po); PyObject_GC_UnTrack(po); Py_XDECREF(po->pool); Py_XDECREF(po->result); PyMem_Free(po->indices); PyMem_Free(po->cycles); - Py_TYPE(po)->tp_free(po); + tp->tp_free(po); + Py_DECREF(tp); } static PyObject * @@ -3496,6 +3378,7 @@ permutations_sizeof(permutationsobject *po, void *unused) static int permutations_traverse(permutationsobject *po, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(po)); Py_VISIT(po->pool); Py_VISIT(po->result); return 0; @@ -3701,48 +3584,25 @@ static PyMethodDef permuations_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject permutations_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.permutations", /* tp_name */ - sizeof(permutationsobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)permutations_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - itertools_permutations__doc__, /* tp_doc */ - (traverseproc)permutations_traverse,/* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)permutations_next, /* tp_iternext */ - permuations_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_permutations, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot permutations_slots[] = { + {Py_tp_dealloc, permutations_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_permutations__doc__}, + {Py_tp_traverse, permutations_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, permutations_next}, + {Py_tp_methods, permuations_methods}, + {Py_tp_new, itertools_permutations}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec permutations_spec = { + .name = "itertools.permutations", + .basicsize = sizeof(permutationsobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = permutations_slots, }; @@ -4975,8 +4835,15 @@ static int itertoolsmodule_traverse(PyObject *mod, visitproc visit, void *arg) { itertools_state *state = get_module_state(mod); + Py_VISIT(state->combinations_type); + Py_VISIT(state->cwr_type); + Py_VISIT(state->cycle_type); + Py_VISIT(state->dropwhile_type); Py_VISIT(state->groupby_type); Py_VISIT(state->_grouper_type); + Py_VISIT(state->permutations_type); + Py_VISIT(state->starmap_type); + Py_VISIT(state->takewhile_type); return 0; } @@ -4984,8 +4851,15 @@ static int itertoolsmodule_clear(PyObject *mod) { itertools_state *state = get_module_state(mod); + Py_CLEAR(state->combinations_type); + Py_CLEAR(state->cwr_type); + Py_CLEAR(state->cycle_type); + Py_CLEAR(state->dropwhile_type); Py_CLEAR(state->groupby_type); Py_CLEAR(state->_grouper_type); + Py_CLEAR(state->permutations_type); + Py_CLEAR(state->starmap_type); + Py_CLEAR(state->takewhile_type); return 0; } @@ -5010,26 +4884,26 @@ static int itertoolsmodule_exec(PyObject *mod) { itertools_state *state = get_module_state(mod); + ADD_TYPE(mod, state->combinations_type, &combinations_spec); + ADD_TYPE(mod, state->cwr_type, &cwr_spec); + ADD_TYPE(mod, state->cycle_type, &cycle_spec); + ADD_TYPE(mod, state->dropwhile_type, &dropwhile_spec); ADD_TYPE(mod, state->groupby_type, &groupby_spec); ADD_TYPE(mod, state->_grouper_type, &_grouper_spec); + ADD_TYPE(mod, state->permutations_type, &permutations_spec); + ADD_TYPE(mod, state->starmap_type, &starmap_spec); + ADD_TYPE(mod, state->takewhile_type, &takewhile_spec); PyTypeObject *typelist[] = { &accumulate_type, &batched_type, - &combinations_type, - &cwr_type, - &cycle_type, - &dropwhile_type, - &takewhile_type, &islice_type, - &starmap_type, &chain_type, &compress_type, &filterfalse_type, &count_type, &ziplongest_type, &pairwise_type, - &permutations_type, &product_type, &repeat_type, &tee_type, From 04e06e20ee61f3c0d1d7a827b2feb4ed41bb198d Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Fri, 3 Feb 2023 11:30:21 +0000 Subject: [PATCH 070/225] gh-98831: rewrite SEND, GET_YIELD_FROM_ITER, RETURN_GENERATOR in the instruction definition DSL (#101516) --- Python/bytecodes.c | 35 ++++++++++++++--------------------- Python/generated_cases.c.h | 35 ++++++++++++++++++++--------------- Python/opcode_metadata.h | 12 ++++++------ 3 files changed, 40 insertions(+), 42 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 169a2647866b31..74c53ad1579f6c 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -663,14 +663,10 @@ dummy_func( PREDICT(LOAD_CONST); } - // error: SEND stack effect depends on jump flag - inst(SEND) { + inst(SEND, (receiver, v -- receiver if (!jump), retval)) { assert(frame != &entry_frame); - assert(STACK_LEVEL() >= 2); - PyObject *v = POP(); - PyObject *receiver = TOP(); + bool jump = false; PySendResult gen_status; - PyObject *retval; if (tstate->c_tracefunc == NULL) { gen_status = PyIter_Send(receiver, v, &retval); } else { @@ -695,21 +691,20 @@ dummy_func( gen_status = PYGEN_NEXT; } } - Py_DECREF(v); if (gen_status == PYGEN_ERROR) { assert(retval == NULL); goto error; } + Py_DECREF(v); if (gen_status == PYGEN_RETURN) { assert(retval != NULL); Py_DECREF(receiver); - SET_TOP(retval); JUMPBY(oparg); + jump = true; } else { assert(gen_status == PYGEN_NEXT); assert(retval != NULL); - PUSH(retval); } } @@ -2043,31 +2038,30 @@ dummy_func( ERROR_IF(iter == NULL, error); } - // stack effect: ( -- ) - inst(GET_YIELD_FROM_ITER) { + inst(GET_YIELD_FROM_ITER, (iterable -- iter)) { /* before: [obj]; after [getiter(obj)] */ - PyObject *iterable = TOP(); - PyObject *iter; if (PyCoro_CheckExact(iterable)) { /* `iterable` is a coroutine */ if (!(frame->f_code->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) { /* and it is used in a 'yield from' expression of a regular generator. */ - Py_DECREF(iterable); - SET_TOP(NULL); _PyErr_SetString(tstate, PyExc_TypeError, "cannot 'yield from' a coroutine object " "in a non-coroutine generator"); goto error; } + iter = iterable; + } + else if (PyGen_CheckExact(iterable)) { + iter = iterable; } - else if (!PyGen_CheckExact(iterable)) { + else { /* `iterable` is not a generator. */ iter = PyObject_GetIter(iterable); - Py_DECREF(iterable); - SET_TOP(iter); - if (iter == NULL) + if (iter == NULL) { goto error; + } + Py_DECREF(iterable); } PREDICT(LOAD_CONST); } @@ -3010,8 +3004,7 @@ dummy_func( PUSH((PyObject *)func); } - // stack effect: ( -- ) - inst(RETURN_GENERATOR) { + inst(RETURN_GENERATOR, (--)) { assert(PyFunction_Check(frame->f_funcobj)); PyFunctionObject *func = (PyFunctionObject *)frame->f_funcobj; PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 97263866fe9158..6f90d9ca4a59ef 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -865,12 +865,12 @@ } TARGET(SEND) { + PyObject *v = PEEK(1); + PyObject *receiver = PEEK(2); + PyObject *retval; assert(frame != &entry_frame); - assert(STACK_LEVEL() >= 2); - PyObject *v = POP(); - PyObject *receiver = TOP(); + bool jump = false; PySendResult gen_status; - PyObject *retval; if (tstate->c_tracefunc == NULL) { gen_status = PyIter_Send(receiver, v, &retval); } else { @@ -895,22 +895,24 @@ gen_status = PYGEN_NEXT; } } - Py_DECREF(v); if (gen_status == PYGEN_ERROR) { assert(retval == NULL); goto error; } + Py_DECREF(v); if (gen_status == PYGEN_RETURN) { assert(retval != NULL); Py_DECREF(receiver); - SET_TOP(retval); JUMPBY(oparg); + jump = true; } else { assert(gen_status == PYGEN_NEXT); assert(retval != NULL); - PUSH(retval); } + STACK_SHRINK(1); + STACK_GROW(((!jump) ? 1 : 0)); + POKE(1, retval); DISPATCH(); } @@ -2584,30 +2586,33 @@ } TARGET(GET_YIELD_FROM_ITER) { - /* before: [obj]; after [getiter(obj)] */ - PyObject *iterable = TOP(); + PyObject *iterable = PEEK(1); PyObject *iter; + /* before: [obj]; after [getiter(obj)] */ if (PyCoro_CheckExact(iterable)) { /* `iterable` is a coroutine */ if (!(frame->f_code->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) { /* and it is used in a 'yield from' expression of a regular generator. */ - Py_DECREF(iterable); - SET_TOP(NULL); _PyErr_SetString(tstate, PyExc_TypeError, "cannot 'yield from' a coroutine object " "in a non-coroutine generator"); goto error; } + iter = iterable; } - else if (!PyGen_CheckExact(iterable)) { + else if (PyGen_CheckExact(iterable)) { + iter = iterable; + } + else { /* `iterable` is not a generator. */ iter = PyObject_GetIter(iterable); - Py_DECREF(iterable); - SET_TOP(iter); - if (iter == NULL) + if (iter == NULL) { goto error; + } + Py_DECREF(iterable); } + POKE(1, iter); PREDICT(LOAD_CONST); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index ca3dde363bfd85..256f81a89fcd36 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -99,7 +99,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case GET_AWAITABLE: return 1; case SEND: - return -1; + return 2; case YIELD_VALUE: return 1; case POP_EXCEPT: @@ -259,7 +259,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case GET_ITER: return 1; case GET_YIELD_FROM_ITER: - return -1; + return 1; case FOR_ITER: return -1; case FOR_ITER_LIST: @@ -327,7 +327,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case MAKE_FUNCTION: return -1; case RETURN_GENERATOR: - return -1; + return 0; case BUILD_SLICE: return -1; case FORMAT_VALUE: @@ -445,7 +445,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case GET_AWAITABLE: return 1; case SEND: - return -1; + return ((!jump) ? 1 : 0) + 1; case YIELD_VALUE: return 1; case POP_EXCEPT: @@ -605,7 +605,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case GET_ITER: return 1; case GET_YIELD_FROM_ITER: - return -1; + return 1; case FOR_ITER: return -1; case FOR_ITER_LIST: @@ -673,7 +673,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case MAKE_FUNCTION: return -1; case RETURN_GENERATOR: - return -1; + return 0; case BUILD_SLICE: return -1; case FORMAT_VALUE: From 433fb3ef08c71b97a0d08e522df56e0afaf3747a Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Fri, 3 Feb 2023 14:40:45 +0000 Subject: [PATCH 071/225] gh-98831: rewrite MAKE_FUNCTION and BUILD_SLICE in the instruction definition DSL (#101529) --- Python/bytecodes.c | 47 +++++++++++++++------------------- Python/generated_cases.c.h | 52 +++++++++++++++++++++----------------- Python/opcode_metadata.h | 8 +++--- 3 files changed, 53 insertions(+), 54 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 74c53ad1579f6c..8993567ac82206 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2972,36 +2972,39 @@ dummy_func( CHECK_EVAL_BREAKER(); } - // error: MAKE_FUNCTION has irregular stack effect - inst(MAKE_FUNCTION) { - PyObject *codeobj = POP(); - PyFunctionObject *func = (PyFunctionObject *) + inst(MAKE_FUNCTION, (defaults if (oparg & 0x01), + kwdefaults if (oparg & 0x02), + annotations if (oparg & 0x04), + closure if (oparg & 0x08), + codeobj -- func)) { + + PyFunctionObject *func_obj = (PyFunctionObject *) PyFunction_New(codeobj, GLOBALS()); Py_DECREF(codeobj); - if (func == NULL) { + if (func_obj == NULL) { goto error; } if (oparg & 0x08) { - assert(PyTuple_CheckExact(TOP())); - func->func_closure = POP(); + assert(PyTuple_CheckExact(closure)); + func_obj->func_closure = closure; } if (oparg & 0x04) { - assert(PyTuple_CheckExact(TOP())); - func->func_annotations = POP(); + assert(PyTuple_CheckExact(annotations)); + func_obj->func_annotations = annotations; } if (oparg & 0x02) { - assert(PyDict_CheckExact(TOP())); - func->func_kwdefaults = POP(); + assert(PyDict_CheckExact(kwdefaults)); + func_obj->func_kwdefaults = kwdefaults; } if (oparg & 0x01) { - assert(PyTuple_CheckExact(TOP())); - func->func_defaults = POP(); + assert(PyTuple_CheckExact(defaults)); + func_obj->func_defaults = defaults; } - func->func_version = ((PyCodeObject *)codeobj)->co_version; - PUSH((PyObject *)func); + func_obj->func_version = ((PyCodeObject *)codeobj)->co_version; + func = (PyObject *)func_obj; } inst(RETURN_GENERATOR, (--)) { @@ -3027,22 +3030,12 @@ dummy_func( goto resume_frame; } - // error: BUILD_SLICE has irregular stack effect - inst(BUILD_SLICE) { - PyObject *start, *stop, *step, *slice; - if (oparg == 3) - step = POP(); - else - step = NULL; - stop = POP(); - start = TOP(); + inst(BUILD_SLICE, (start, stop, step if (oparg == 3) -- slice)) { slice = PySlice_New(start, stop, step); Py_DECREF(start); Py_DECREF(stop); Py_XDECREF(step); - SET_TOP(slice); - if (slice == NULL) - goto error; + ERROR_IF(slice == NULL, error); } // error: FORMAT_VALUE has irregular stack effect diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 6f90d9ca4a59ef..e524bfcb99d470 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3584,34 +3584,42 @@ } TARGET(MAKE_FUNCTION) { - PyObject *codeobj = POP(); - PyFunctionObject *func = (PyFunctionObject *) + PyObject *codeobj = PEEK(1); + PyObject *closure = (oparg & 0x08) ? PEEK(1 + ((oparg & 0x08) ? 1 : 0)) : NULL; + PyObject *annotations = (oparg & 0x04) ? PEEK(1 + ((oparg & 0x08) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0)) : NULL; + PyObject *kwdefaults = (oparg & 0x02) ? PEEK(1 + ((oparg & 0x08) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0)) : NULL; + PyObject *defaults = (oparg & 0x01) ? PEEK(1 + ((oparg & 0x08) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x01) ? 1 : 0)) : NULL; + PyObject *func; + + PyFunctionObject *func_obj = (PyFunctionObject *) PyFunction_New(codeobj, GLOBALS()); Py_DECREF(codeobj); - if (func == NULL) { + if (func_obj == NULL) { goto error; } if (oparg & 0x08) { - assert(PyTuple_CheckExact(TOP())); - func->func_closure = POP(); + assert(PyTuple_CheckExact(closure)); + func_obj->func_closure = closure; } if (oparg & 0x04) { - assert(PyTuple_CheckExact(TOP())); - func->func_annotations = POP(); + assert(PyTuple_CheckExact(annotations)); + func_obj->func_annotations = annotations; } if (oparg & 0x02) { - assert(PyDict_CheckExact(TOP())); - func->func_kwdefaults = POP(); + assert(PyDict_CheckExact(kwdefaults)); + func_obj->func_kwdefaults = kwdefaults; } if (oparg & 0x01) { - assert(PyTuple_CheckExact(TOP())); - func->func_defaults = POP(); + assert(PyTuple_CheckExact(defaults)); + func_obj->func_defaults = defaults; } - func->func_version = ((PyCodeObject *)codeobj)->co_version; - PUSH((PyObject *)func); + func_obj->func_version = ((PyCodeObject *)codeobj)->co_version; + func = (PyObject *)func_obj; + STACK_SHRINK(((oparg & 0x01) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x08) ? 1 : 0)); + POKE(1, func); DISPATCH(); } @@ -3639,20 +3647,18 @@ } TARGET(BUILD_SLICE) { - PyObject *start, *stop, *step, *slice; - if (oparg == 3) - step = POP(); - else - step = NULL; - stop = POP(); - start = TOP(); + PyObject *step = (oparg == 3) ? PEEK(((oparg == 3) ? 1 : 0)) : NULL; + PyObject *stop = PEEK(1 + ((oparg == 3) ? 1 : 0)); + PyObject *start = PEEK(2 + ((oparg == 3) ? 1 : 0)); + PyObject *slice; slice = PySlice_New(start, stop, step); Py_DECREF(start); Py_DECREF(stop); Py_XDECREF(step); - SET_TOP(slice); - if (slice == NULL) - goto error; + if (slice == NULL) { STACK_SHRINK(((oparg == 3) ? 1 : 0)); goto pop_2_error; } + STACK_SHRINK(((oparg == 3) ? 1 : 0)); + STACK_SHRINK(1); + POKE(1, slice); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 256f81a89fcd36..857526c35aa5b6 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -325,11 +325,11 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case CALL_FUNCTION_EX: return -1; case MAKE_FUNCTION: - return -1; + return ((oparg & 0x01) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x08) ? 1 : 0) + 1; case RETURN_GENERATOR: return 0; case BUILD_SLICE: - return -1; + return ((oparg == 3) ? 1 : 0) + 2; case FORMAT_VALUE: return -1; case COPY: @@ -671,11 +671,11 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case CALL_FUNCTION_EX: return -1; case MAKE_FUNCTION: - return -1; + return 1; case RETURN_GENERATOR: return 0; case BUILD_SLICE: - return -1; + return 1; case FORMAT_VALUE: return -1; case COPY: From f6c53b80a16f63825479c5ca0f8a5e2829c3f505 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 3 Feb 2023 18:08:34 +0000 Subject: [PATCH 072/225] gh-101522: Allow overriding Windows dependencies versions and paths using MSBuild properties (GH-101523) --- ...-02-02-23-43-46.gh-issue-101522.lnUDta.rst | 2 + PCbuild/python.props | 37 ++++++++++++------- PCbuild/tcltk.props | 35 ++++++++++-------- 3 files changed, 44 insertions(+), 30 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2023-02-02-23-43-46.gh-issue-101522.lnUDta.rst diff --git a/Misc/NEWS.d/next/Build/2023-02-02-23-43-46.gh-issue-101522.lnUDta.rst b/Misc/NEWS.d/next/Build/2023-02-02-23-43-46.gh-issue-101522.lnUDta.rst new file mode 100644 index 00000000000000..2e7f9029e9ee54 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-02-02-23-43-46.gh-issue-101522.lnUDta.rst @@ -0,0 +1,2 @@ +Allow overriding Windows dependencies versions and paths using MSBuild +properties. diff --git a/PCbuild/python.props b/PCbuild/python.props index 971c1490d9f120..57360e57baba66 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -56,23 +56,32 @@ ..\\.. ..\\..\\.. + - - $(EXTERNALS_DIR) + + + $(EXTERNALS_DIR) $([System.IO.Path]::GetFullPath(`$(PySourcePath)externals`)) $(ExternalsDir)\ - $(ExternalsDir)sqlite-3.39.4.0\ - $(ExternalsDir)bzip2-1.0.8\ - $(ExternalsDir)xz-5.2.5\ - $(ExternalsDir)libffi-3.4.3\ - $(ExternalsDir)libffi-3.4.3\$(ArchName)\ - $(libffiOutDir)include - $(ExternalsDir)openssl-1.1.1s\ - $(ExternalsDir)openssl-bin-1.1.1s\$(ArchName)\ - $(opensslOutDir)include - $(ExternalsDir)\nasm-2.11.06\ - $(ExternalsDir)\zlib-1.2.13\ - + + + + + + $(ExternalsDir)sqlite-3.39.4.0\ + $(ExternalsDir)bzip2-1.0.8\ + $(ExternalsDir)xz-5.2.5\ + $(ExternalsDir)libffi-3.4.3\ + $(libffiDir)$(ArchName)\ + $(libffiOutDir)include + $(ExternalsDir)openssl-1.1.1s\ + $(ExternalsDir)openssl-bin-1.1.1s\$(ArchName)\ + $(opensslOutDir)include + $(ExternalsDir)\nasm-2.11.06\ + $(ExternalsDir)\zlib-1.2.13\ + + + _d diff --git a/PCbuild/tcltk.props b/PCbuild/tcltk.props index 15c03e20fe2171..9d5189b3b8e93d 100644 --- a/PCbuild/tcltk.props +++ b/PCbuild/tcltk.props @@ -2,22 +2,25 @@ - 8 - 6 - 13 - 0 - $(TclMajorVersion) - $(TclMinorVersion) - $(TclPatchLevel) - $(TclRevision) - 8 - 4 - 3 - 6 - $(ExternalsDir)tcl-core-$(TclMajorVersion).$(TclMinorVersion).$(TclPatchLevel).$(TclRevision)\ - $(ExternalsDir)tk-$(TkMajorVersion).$(TkMinorVersion).$(TkPatchLevel).$(TkRevision)\ - $(ExternalsDir)tix-$(TixMajorVersion).$(TixMinorVersion).$(TixPatchLevel).$(TixRevision)\ - $(ExternalsDir)tcltk-$(TclMajorVersion).$(TclMinorVersion).$(TclPatchLevel).$(TclRevision)\$(ArchName)\ + 8.6.13.0 + $(TclVersion) + 8.4.3.6 + $([System.Version]::Parse($(TclVersion)).Major) + $([System.Version]::Parse($(TclVersion)).Minor) + $([System.Version]::Parse($(TclVersion)).Build) + $([System.Version]::Parse($(TclVersion)).Revision) + $([System.Version]::Parse($(TkVersion)).Major) + $([System.Version]::Parse($(TkVersion)).Minor) + $([System.Version]::Parse($(TkVersion)).Build) + $([System.Version]::Parse($(TkVersion)).Revision) + $([System.Version]::Parse($(TixVersion)).Major) + $([System.Version]::Parse($(TixVersion)).Minor) + $([System.Version]::Parse($(TixVersion)).Build) + $([System.Version]::Parse($(TixVersion)).Revision) + $(ExternalsDir)tcl-core-$(TclVersion)\ + $(ExternalsDir)tk-$(TkVersion)\ + $(ExternalsDir)tix-$(TixVersion)\ + $(ExternalsDir)tcltk-$(TclVersion)\$(ArchName)\ $(tcltkDir)\bin\tclsh$(TclMajorVersion)$(TclMinorVersion)t.exe $(tcltkDir)\..\win32\bin\tclsh$(TclMajorVersion)$(TclMinorVersion)t.exe From d4c410f0f922683f38c9d435923939d037fbd8c2 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 3 Feb 2023 15:20:46 -0800 Subject: [PATCH 073/225] gh-84559: Remove the new multiprocessing warning, too disruptive. (#101551) This reverts the core of #100618 while leaving relevant documentation improvements and minor refactorings in place. --- Doc/library/concurrent.futures.rst | 16 ++-- Doc/library/multiprocessing.rst | 11 +-- Doc/whatsnew/3.12.rst | 16 ++-- Lib/concurrent/futures/process.py | 17 ---- Lib/multiprocessing/context.py | 28 +----- Lib/test/_test_multiprocessing.py | 5 +- Lib/test/test_concurrent_futures.py | 19 ----- Lib/test/test_logging.py | 5 +- Lib/test/test_multiprocessing_defaults.py | 85 ------------------- Lib/test/test_re.py | 3 +- ...3-01-01-01-19-33.gh-issue-84559.zEjsEJ.rst | 11 --- 11 files changed, 27 insertions(+), 189 deletions(-) delete mode 100644 Lib/test/test_multiprocessing_defaults.py delete mode 100644 Misc/NEWS.d/next/Library/2023-01-01-01-19-33.gh-issue-84559.zEjsEJ.rst diff --git a/Doc/library/concurrent.futures.rst b/Doc/library/concurrent.futures.rst index 10cffdaee0bb8c..c543c849585b7f 100644 --- a/Doc/library/concurrent.futures.rst +++ b/Doc/library/concurrent.futures.rst @@ -281,18 +281,18 @@ to a :class:`ProcessPoolExecutor` will result in deadlock. Added the *initializer* and *initargs* arguments. + .. note:: + The default :mod:`multiprocessing` start method + (see :ref:`multiprocessing-start-methods`) will change away from + *fork* in Python 3.14. Code that requires *fork* be used for their + :class:`ProcessPoolExecutor` should explicitly specify that by + passing a ``mp_context=multiprocessing.get_context("fork")`` + parameter. + .. versionchanged:: 3.11 The *max_tasks_per_child* argument was added to allow users to control the lifetime of workers in the pool. - .. versionchanged:: 3.12 - The implicit use of the :mod:`multiprocessing` *fork* start method as a - platform default (see :ref:`multiprocessing-start-methods`) now raises a - :exc:`DeprecationWarning`. The default will change in Python 3.14. - Code that requires *fork* should explicitly specify that when creating - their :class:`ProcessPoolExecutor` by passing a - ``mp_context=multiprocessing.get_context('fork')`` parameter. - .. _processpoolexecutor-example: ProcessPoolExecutor Example diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst index c60b229ae2d07e..0ec47bb956a99e 100644 --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -126,6 +126,11 @@ to start a process. These *start methods* are Available on POSIX systems. Currently the default on POSIX except macOS. + .. note:: + The default start method will change away from *fork* in Python 3.14. + Code that requires *fork* should explicitly specify that via + :func:`get_context` or :func:`set_start_method`. + *forkserver* When the program starts and selects the *forkserver* start method, a server process is spawned. From then on, whenever a new process @@ -138,11 +143,6 @@ to start a process. These *start methods* are Available on POSIX platforms which support passing file descriptors over Unix pipes such as Linux. -.. versionchanged:: 3.12 - Implicit use of the *fork* start method as the default now raises a - :exc:`DeprecationWarning`. Code that requires it should explicitly - specify *fork* via :func:`get_context` or :func:`set_start_method`. - The default will change away from *fork* in 3.14. .. versionchanged:: 3.8 @@ -1107,6 +1107,7 @@ Miscellaneous launched (before creating a :class:`Pool` or starting a :class:`Process`). Only meaningful when using the ``'forkserver'`` start method. + See :ref:`multiprocessing-start-methods`. .. versionadded:: 3.4 diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index e675fada339a1e..0c5a70b64574ef 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -440,12 +440,6 @@ Deprecated warning at compile time. This field will be removed in Python 3.14. (Contributed by Ramvikrams and Kumar Aditya in :gh:`101193`. PEP by Ken Jin.) -* Use of the implicit default ``'fork'`` start method for - :mod:`multiprocessing` and :class:`concurrent.futures.ProcessPoolExecutor` - now emits a :exc:`DeprecationWarning` on Linux and other non-macOS POSIX - systems. Avoid this by explicitly specifying a start method. - See :ref:`multiprocessing-start-methods`. - Pending Removal in Python 3.13 ------------------------------ @@ -510,9 +504,13 @@ Pending Removal in Python 3.14 * Testing the truth value of an :class:`xml.etree.ElementTree.Element` is deprecated and will raise an exception in Python 3.14. -* The default :mod:`multiprocessing` start method will change to one of either - ``'forkserver'`` or ``'spawn'`` on all platforms for which ``'fork'`` remains - the default per :gh:`84559`. +* The default :mod:`multiprocessing` start method will change to a safer one on + Linux, BSDs, and other non-macOS POSIX platforms where ``'fork'`` is currently + the default (:gh:`84559`). Adding a runtime warning about this was deemed too + disruptive as the majority of code is not expected to care. Use the + :func:`~multiprocessing.get_context` or + :func:`~multiprocessing.set_start_method` APIs to explicitly specify when + your code *requires* ``'fork'``. See :ref:`multiprocessing-start-methods`. Pending Removal in Future Versions ---------------------------------- diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py index 257dd02fbc6cce..bee162430a6f8e 100644 --- a/Lib/concurrent/futures/process.py +++ b/Lib/concurrent/futures/process.py @@ -57,7 +57,6 @@ import itertools import sys from traceback import format_exception -import warnings _threads_wakeups = weakref.WeakKeyDictionary() @@ -651,22 +650,6 @@ def __init__(self, max_workers=None, mp_context=None, mp_context = mp.get_context("spawn") else: mp_context = mp.get_context() - if (mp_context.get_start_method() == "fork" and - mp_context == mp.context._default_context._default_context): - warnings.warn( - "The default multiprocessing start method will change " - "away from 'fork' in Python >= 3.14, per GH-84559. " - "ProcessPoolExecutor uses multiprocessing. " - "If your application requires the 'fork' multiprocessing " - "start method, explicitly specify that by passing a " - "mp_context= parameter. " - "The safest start method is 'spawn'.", - category=mp.context.DefaultForkDeprecationWarning, - stacklevel=2, - ) - # Avoid the equivalent warning from multiprocessing itself via - # a non-default fork context. - mp_context = mp.get_context("fork") self._mp_context = mp_context # https://github.com/python/cpython/issues/90622 diff --git a/Lib/multiprocessing/context.py b/Lib/multiprocessing/context.py index 010a920540e844..de8a264829dff3 100644 --- a/Lib/multiprocessing/context.py +++ b/Lib/multiprocessing/context.py @@ -23,9 +23,6 @@ class TimeoutError(ProcessError): class AuthenticationError(ProcessError): pass -class DefaultForkDeprecationWarning(DeprecationWarning): - pass - # # Base type for contexts. Bound methods of an instance of this type are included in __all__ of __init__.py # @@ -284,23 +281,6 @@ def _Popen(process_obj): from .popen_fork import Popen return Popen(process_obj) - _warn_package_prefixes = (os.path.dirname(__file__),) - - class _DeprecatedForkProcess(ForkProcess): - @classmethod - def _Popen(cls, process_obj): - import warnings - warnings.warn( - "The default multiprocessing start method will change " - "away from 'fork' in Python >= 3.14, per GH-84559. " - "Use multiprocessing.get_context(X) or .set_start_method(X) to " - "explicitly specify it when your application requires 'fork'. " - "The safest start method is 'spawn'.", - category=DefaultForkDeprecationWarning, - skip_file_prefixes=_warn_package_prefixes, - ) - return super()._Popen(process_obj) - class SpawnProcess(process.BaseProcess): _start_method = 'spawn' @staticmethod @@ -324,9 +304,6 @@ class ForkContext(BaseContext): _name = 'fork' Process = ForkProcess - class _DefaultForkContext(ForkContext): - Process = _DeprecatedForkProcess - class SpawnContext(BaseContext): _name = 'spawn' Process = SpawnProcess @@ -342,16 +319,13 @@ def _check_available(self): 'fork': ForkContext(), 'spawn': SpawnContext(), 'forkserver': ForkServerContext(), - # Remove None and _DefaultForkContext() when changing the default - # in 3.14 for https://github.com/python/cpython/issues/84559. - None: _DefaultForkContext(), } if sys.platform == 'darwin': # bpo-33725: running arbitrary code after fork() is no longer reliable # on macOS since macOS 10.14 (Mojave). Use spawn by default instead. _default_context = DefaultContext(_concrete_contexts['spawn']) else: - _default_context = DefaultContext(_concrete_contexts[None]) + _default_context = DefaultContext(_concrete_contexts['fork']) else: diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index e4a60a4d674607..9a2db24b4bd597 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -4098,10 +4098,9 @@ def test_shared_memory_SharedMemoryServer_ignores_sigint(self): def test_shared_memory_SharedMemoryManager_reuses_resource_tracker(self): # bpo-36867: test that a SharedMemoryManager uses the # same resource_tracker process as its parent. - cmd = f'''if 1: + cmd = '''if 1: from multiprocessing.managers import SharedMemoryManager - from multiprocessing import set_start_method - set_start_method({multiprocessing.get_start_method()!r}) + smm = SharedMemoryManager() smm.start() diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py index 4493cd312528d6..b3520ae3994e03 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py @@ -18,7 +18,6 @@ import threading import time import unittest -import warnings import weakref from pickle import PicklingError @@ -572,24 +571,6 @@ def test_shutdown_no_wait(self): assert all([r == abs(v) for r, v in zip(res, range(-5, 5))]) -@unittest.skipIf(mp.get_all_start_methods()[0] != "fork", "non-fork default.") -class ProcessPoolExecutorDefaultForkWarning(unittest.TestCase): - def test_fork_default_warns(self): - with self.assertWarns(mp.context.DefaultForkDeprecationWarning): - with futures.ProcessPoolExecutor(2): - pass - - def test_explicit_fork_does_not_warn(self): - with warnings.catch_warnings(record=True) as ws: - warnings.simplefilter("ignore") - warnings.filterwarnings( - 'always', category=mp.context.DefaultForkDeprecationWarning) - ctx = mp.get_context("fork") # Non-default fork context. - with futures.ProcessPoolExecutor(2, mp_context=ctx): - pass - self.assertEqual(len(ws), 0, msg=[str(x) for x in ws]) - - create_executor_tests(ProcessPoolShutdownTest, executor_mixins=(ProcessPoolForkMixin, ProcessPoolForkserverMixin, diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 8a12d570f26f13..072056d3722106 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -4759,9 +4759,8 @@ def test_multiprocessing(self): # In other processes, processName is correct when multiprocessing in imported, # but it is (incorrectly) defaulted to 'MainProcess' otherwise (bpo-38762). import multiprocessing - mp = multiprocessing.get_context('spawn') - parent_conn, child_conn = mp.Pipe() - p = mp.Process( + parent_conn, child_conn = multiprocessing.Pipe() + p = multiprocessing.Process( target=self._extract_logrecord_process_name, args=(2, LOG_MULTI_PROCESSING, child_conn,) ) diff --git a/Lib/test/test_multiprocessing_defaults.py b/Lib/test/test_multiprocessing_defaults.py deleted file mode 100644 index 7ea872fef20dd9..00000000000000 --- a/Lib/test/test_multiprocessing_defaults.py +++ /dev/null @@ -1,85 +0,0 @@ -"""Test default behavior of multiprocessing.""" - -from inspect import currentframe, getframeinfo -import multiprocessing -from multiprocessing.context import DefaultForkDeprecationWarning -import sys -from test.support import import_helper, threading_helper -import unittest -import warnings - -# Skip tests if _multiprocessing wasn't built. -import_helper.import_module('_multiprocessing') - - -def do_nothing(): - pass - - -# Process has the same API as Thread so this helper works. -join_process = threading_helper.join_thread - - -class DefaultWarningsTest(unittest.TestCase): - - @unittest.skipIf(sys.platform in ('win32', 'darwin'), - 'The default is not "fork" on Windows or macOS.') - def setUp(self): - self.assertEqual(multiprocessing.get_start_method(), 'fork') - self.assertIsInstance(multiprocessing.get_context(), - multiprocessing.context._DefaultForkContext) - - def test_default_fork_start_method_warning_process(self): - with warnings.catch_warnings(record=True) as ws: - warnings.simplefilter('ignore') - warnings.filterwarnings('always', category=DefaultForkDeprecationWarning) - process = multiprocessing.Process(target=do_nothing) - process.start() # warning should point here. - join_process(process) - self.assertIsInstance(ws[0].message, DefaultForkDeprecationWarning) - self.assertIn(__file__, ws[0].filename) - self.assertEqual(getframeinfo(currentframe()).lineno-4, ws[0].lineno) - self.assertIn("'fork'", str(ws[0].message)) - self.assertIn("get_context", str(ws[0].message)) - self.assertEqual(len(ws), 1, msg=[str(x) for x in ws]) - - def test_default_fork_start_method_warning_pool(self): - with warnings.catch_warnings(record=True) as ws: - warnings.simplefilter('ignore') - warnings.filterwarnings('always', category=DefaultForkDeprecationWarning) - pool = multiprocessing.Pool(1) # warning should point here. - pool.terminate() - pool.join() - self.assertIsInstance(ws[0].message, DefaultForkDeprecationWarning) - self.assertIn(__file__, ws[0].filename) - self.assertEqual(getframeinfo(currentframe()).lineno-5, ws[0].lineno) - self.assertIn("'fork'", str(ws[0].message)) - self.assertIn("get_context", str(ws[0].message)) - self.assertEqual(len(ws), 1, msg=[str(x) for x in ws]) - - def test_default_fork_start_method_warning_manager(self): - with warnings.catch_warnings(record=True) as ws: - warnings.simplefilter('ignore') - warnings.filterwarnings('always', category=DefaultForkDeprecationWarning) - manager = multiprocessing.Manager() # warning should point here. - manager.shutdown() - self.assertIsInstance(ws[0].message, DefaultForkDeprecationWarning) - self.assertIn(__file__, ws[0].filename) - self.assertEqual(getframeinfo(currentframe()).lineno-4, ws[0].lineno) - self.assertIn("'fork'", str(ws[0].message)) - self.assertIn("get_context", str(ws[0].message)) - self.assertEqual(len(ws), 1, msg=[str(x) for x in ws]) - - def test_no_mp_warning_when_using_explicit_fork_context(self): - with warnings.catch_warnings(record=True) as ws: - warnings.simplefilter('ignore') - warnings.filterwarnings('always', category=DefaultForkDeprecationWarning) - fork_mp = multiprocessing.get_context('fork') - pool = fork_mp.Pool(1) - pool.terminate() - pool.join() - self.assertEqual(len(ws), 0, msg=[str(x) for x in ws]) - - -if __name__ == '__main__': - unittest.main() diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index eacb1a7c82a54d..11628a236ade9a 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -2431,8 +2431,7 @@ def test_regression_gh94675(self): input_js = '''a(function() { /////////////////////////////////////////////////////////////////// });''' - mp = multiprocessing.get_context('spawn') - p = mp.Process(target=pattern.sub, args=('', input_js)) + p = multiprocessing.Process(target=pattern.sub, args=('', input_js)) p.start() p.join(SHORT_TIMEOUT) try: diff --git a/Misc/NEWS.d/next/Library/2023-01-01-01-19-33.gh-issue-84559.zEjsEJ.rst b/Misc/NEWS.d/next/Library/2023-01-01-01-19-33.gh-issue-84559.zEjsEJ.rst deleted file mode 100644 index 3793e0f1fddb20..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-01-01-19-33.gh-issue-84559.zEjsEJ.rst +++ /dev/null @@ -1,11 +0,0 @@ -The :mod:`multiprocessing` module and -:class:`concurrent.futures.ProcessPoolExecutor` will emit a -:exc:`DeprecationWarning` on Linux and other non-macOS POSIX systems when -the default multiprocessing start method of ``'fork'`` is used implicitly -rather than being explicitly specified through a -:func:`multiprocessing.get_context` context. - -This is in preparation for default start method to change in Python 3.14 to -a default that is safe for multithreaded applications. - -Windows and macOS are unaffected as their default start method is ``spawn``. From c67b00534abfeca83016a00818cf1fd949613d6b Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 3 Feb 2023 18:14:43 -0700 Subject: [PATCH 074/225] gh-101524: Split Up the _xxsubinterpreters Module (gh-101526) This is step 1 in potentially dropping all the "channel"-related code. Channels have already been removed from PEP 554. https://github.com/python/cpython/issues/101524 --- Lib/test/support/interpreters.py | 23 +- Lib/test/test__xxinterpchannels.py | 1541 ++++++++++++ Lib/test/test__xxsubinterpreters.py | 1529 +----------- Lib/test/test_interpreters.py | 5 +- Modules/Setup | 1 + Modules/Setup.stdlib.in | 1 + Modules/_testcapimodule.c | 54 + Modules/_xxinterpchannelsmodule.c | 2325 +++++++++++++++++++ Modules/_xxsubinterpretersmodule.c | 2291 +----------------- PC/config.c | 2 + PCbuild/pythoncore.vcxproj | 1 + PCbuild/pythoncore.vcxproj.filters | 3 + Tools/build/generate_stdlib_module_names.py | 1 + configure | 27 + configure.ac | 2 + 15 files changed, 4058 insertions(+), 3748 deletions(-) create mode 100644 Lib/test/test__xxinterpchannels.py create mode 100644 Modules/_xxinterpchannelsmodule.c diff --git a/Lib/test/support/interpreters.py b/Lib/test/support/interpreters.py index 2935708f9df1a5..eeff3abe0324e5 100644 --- a/Lib/test/support/interpreters.py +++ b/Lib/test/support/interpreters.py @@ -2,11 +2,12 @@ import time import _xxsubinterpreters as _interpreters +import _xxinterpchannels as _channels # aliases: -from _xxsubinterpreters import ( +from _xxsubinterpreters import is_shareable +from _xxinterpchannels import ( ChannelError, ChannelNotFoundError, ChannelEmptyError, - is_shareable, ) @@ -102,7 +103,7 @@ def create_channel(): The channel may be used to pass data safely between interpreters. """ - cid = _interpreters.channel_create() + cid = _channels.create() recv, send = RecvChannel(cid), SendChannel(cid) return recv, send @@ -110,14 +111,14 @@ def create_channel(): def list_all_channels(): """Return a list of (recv, send) for all open channels.""" return [(RecvChannel(cid), SendChannel(cid)) - for cid in _interpreters.channel_list_all()] + for cid in _channels.list_all()] class _ChannelEnd: """The base class for RecvChannel and SendChannel.""" def __init__(self, id): - if not isinstance(id, (int, _interpreters.ChannelID)): + if not isinstance(id, (int, _channels.ChannelID)): raise TypeError(f'id must be an int, got {id!r}') self._id = id @@ -152,10 +153,10 @@ def recv(self, *, _sentinel=object(), _delay=10 / 1000): # 10 milliseconds This blocks until an object has been sent, if none have been sent already. """ - obj = _interpreters.channel_recv(self._id, _sentinel) + obj = _channels.recv(self._id, _sentinel) while obj is _sentinel: time.sleep(_delay) - obj = _interpreters.channel_recv(self._id, _sentinel) + obj = _channels.recv(self._id, _sentinel) return obj def recv_nowait(self, default=_NOT_SET): @@ -166,9 +167,9 @@ def recv_nowait(self, default=_NOT_SET): is the same as recv(). """ if default is _NOT_SET: - return _interpreters.channel_recv(self._id) + return _channels.recv(self._id) else: - return _interpreters.channel_recv(self._id, default) + return _channels.recv(self._id, default) class SendChannel(_ChannelEnd): @@ -179,7 +180,7 @@ def send(self, obj): This blocks until the object is received. """ - _interpreters.channel_send(self._id, obj) + _channels.send(self._id, obj) # XXX We are missing a low-level channel_send_wait(). # See bpo-32604 and gh-19829. # Until that shows up we fake it: @@ -194,4 +195,4 @@ def send_nowait(self, obj): # XXX Note that at the moment channel_send() only ever returns # None. This should be fixed when channel_send_wait() is added. # See bpo-32604 and gh-19829. - return _interpreters.channel_send(self._id, obj) + return _channels.send(self._id, obj) diff --git a/Lib/test/test__xxinterpchannels.py b/Lib/test/test__xxinterpchannels.py new file mode 100644 index 00000000000000..03bb5c80b8dac9 --- /dev/null +++ b/Lib/test/test__xxinterpchannels.py @@ -0,0 +1,1541 @@ +from collections import namedtuple +import contextlib +import os +import sys +from textwrap import dedent +import threading +import time +import unittest + +from test.support import import_helper + +from test.test__xxsubinterpreters import ( + interpreters, + _run_output, + clean_up_interpreters, +) + + +channels = import_helper.import_module('_xxinterpchannels') + + +################################## +# helpers + +#@contextmanager +#def run_threaded(id, source, **shared): +# def run(): +# run_interp(id, source, **shared) +# t = threading.Thread(target=run) +# t.start() +# yield +# t.join() + + +def run_interp(id, source, **shared): + _run_interp(id, source, shared) + + +def _run_interp(id, source, shared, _mainns={}): + source = dedent(source) + main = interpreters.get_main() + if main == id: + if interpreters.get_current() != main: + raise RuntimeError + # XXX Run a func? + exec(source, _mainns) + else: + interpreters.run_string(id, source, shared) + + +class Interpreter(namedtuple('Interpreter', 'name id')): + + @classmethod + def from_raw(cls, raw): + if isinstance(raw, cls): + return raw + elif isinstance(raw, str): + return cls(raw) + else: + raise NotImplementedError + + def __new__(cls, name=None, id=None): + main = interpreters.get_main() + if id == main: + if not name: + name = 'main' + elif name != 'main': + raise ValueError( + 'name mismatch (expected "main", got "{}")'.format(name)) + id = main + elif id is not None: + if not name: + name = 'interp' + elif name == 'main': + raise ValueError('name mismatch (unexpected "main")') + if not isinstance(id, interpreters.InterpreterID): + id = interpreters.InterpreterID(id) + elif not name or name == 'main': + name = 'main' + id = main + else: + id = interpreters.create() + self = super().__new__(cls, name, id) + return self + + +# XXX expect_channel_closed() is unnecessary once we improve exc propagation. + +@contextlib.contextmanager +def expect_channel_closed(): + try: + yield + except channels.ChannelClosedError: + pass + else: + assert False, 'channel not closed' + + +class ChannelAction(namedtuple('ChannelAction', 'action end interp')): + + def __new__(cls, action, end=None, interp=None): + if not end: + end = 'both' + if not interp: + interp = 'main' + self = super().__new__(cls, action, end, interp) + return self + + def __init__(self, *args, **kwargs): + if self.action == 'use': + if self.end not in ('same', 'opposite', 'send', 'recv'): + raise ValueError(self.end) + elif self.action in ('close', 'force-close'): + if self.end not in ('both', 'same', 'opposite', 'send', 'recv'): + raise ValueError(self.end) + else: + raise ValueError(self.action) + if self.interp not in ('main', 'same', 'other', 'extra'): + raise ValueError(self.interp) + + def resolve_end(self, end): + if self.end == 'same': + return end + elif self.end == 'opposite': + return 'recv' if end == 'send' else 'send' + else: + return self.end + + def resolve_interp(self, interp, other, extra): + if self.interp == 'same': + return interp + elif self.interp == 'other': + if other is None: + raise RuntimeError + return other + elif self.interp == 'extra': + if extra is None: + raise RuntimeError + return extra + elif self.interp == 'main': + if interp.name == 'main': + return interp + elif other and other.name == 'main': + return other + else: + raise RuntimeError + # Per __init__(), there aren't any others. + + +class ChannelState(namedtuple('ChannelState', 'pending closed')): + + def __new__(cls, pending=0, *, closed=False): + self = super().__new__(cls, pending, closed) + return self + + def incr(self): + return type(self)(self.pending + 1, closed=self.closed) + + def decr(self): + return type(self)(self.pending - 1, closed=self.closed) + + def close(self, *, force=True): + if self.closed: + if not force or self.pending == 0: + return self + return type(self)(0 if force else self.pending, closed=True) + + +def run_action(cid, action, end, state, *, hideclosed=True): + if state.closed: + if action == 'use' and end == 'recv' and state.pending: + expectfail = False + else: + expectfail = True + else: + expectfail = False + + try: + result = _run_action(cid, action, end, state) + except channels.ChannelClosedError: + if not hideclosed and not expectfail: + raise + result = state.close() + else: + if expectfail: + raise ... # XXX + return result + + +def _run_action(cid, action, end, state): + if action == 'use': + if end == 'send': + channels.send(cid, b'spam') + return state.incr() + elif end == 'recv': + if not state.pending: + try: + channels.recv(cid) + except channels.ChannelEmptyError: + return state + else: + raise Exception('expected ChannelEmptyError') + else: + channels.recv(cid) + return state.decr() + else: + raise ValueError(end) + elif action == 'close': + kwargs = {} + if end in ('recv', 'send'): + kwargs[end] = True + channels.close(cid, **kwargs) + return state.close() + elif action == 'force-close': + kwargs = { + 'force': True, + } + if end in ('recv', 'send'): + kwargs[end] = True + channels.close(cid, **kwargs) + return state.close(force=True) + else: + raise ValueError(action) + + +def clean_up_channels(): + for cid in channels.list_all(): + try: + channels.destroy(cid) + except channels.ChannelNotFoundError: + pass # already destroyed + + +class TestBase(unittest.TestCase): + + def tearDown(self): + clean_up_channels() + clean_up_interpreters() + + +################################## +# channel tests + +class ChannelIDTests(TestBase): + + def test_default_kwargs(self): + cid = channels._channel_id(10, force=True) + + self.assertEqual(int(cid), 10) + self.assertEqual(cid.end, 'both') + + def test_with_kwargs(self): + cid = channels._channel_id(10, send=True, force=True) + self.assertEqual(cid.end, 'send') + + cid = channels._channel_id(10, send=True, recv=False, force=True) + self.assertEqual(cid.end, 'send') + + cid = channels._channel_id(10, recv=True, force=True) + self.assertEqual(cid.end, 'recv') + + cid = channels._channel_id(10, recv=True, send=False, force=True) + self.assertEqual(cid.end, 'recv') + + cid = channels._channel_id(10, send=True, recv=True, force=True) + self.assertEqual(cid.end, 'both') + + def test_coerce_id(self): + class Int(str): + def __index__(self): + return 10 + + cid = channels._channel_id(Int(), force=True) + self.assertEqual(int(cid), 10) + + def test_bad_id(self): + self.assertRaises(TypeError, channels._channel_id, object()) + self.assertRaises(TypeError, channels._channel_id, 10.0) + self.assertRaises(TypeError, channels._channel_id, '10') + self.assertRaises(TypeError, channels._channel_id, b'10') + self.assertRaises(ValueError, channels._channel_id, -1) + self.assertRaises(OverflowError, channels._channel_id, 2**64) + + def test_bad_kwargs(self): + with self.assertRaises(ValueError): + channels._channel_id(10, send=False, recv=False) + + def test_does_not_exist(self): + cid = channels.create() + with self.assertRaises(channels.ChannelNotFoundError): + channels._channel_id(int(cid) + 1) # unforced + + def test_str(self): + cid = channels._channel_id(10, force=True) + self.assertEqual(str(cid), '10') + + def test_repr(self): + cid = channels._channel_id(10, force=True) + self.assertEqual(repr(cid), 'ChannelID(10)') + + cid = channels._channel_id(10, send=True, force=True) + self.assertEqual(repr(cid), 'ChannelID(10, send=True)') + + cid = channels._channel_id(10, recv=True, force=True) + self.assertEqual(repr(cid), 'ChannelID(10, recv=True)') + + cid = channels._channel_id(10, send=True, recv=True, force=True) + self.assertEqual(repr(cid), 'ChannelID(10)') + + def test_equality(self): + cid1 = channels.create() + cid2 = channels._channel_id(int(cid1)) + cid3 = channels.create() + + self.assertTrue(cid1 == cid1) + self.assertTrue(cid1 == cid2) + self.assertTrue(cid1 == int(cid1)) + self.assertTrue(int(cid1) == cid1) + self.assertTrue(cid1 == float(int(cid1))) + self.assertTrue(float(int(cid1)) == cid1) + self.assertFalse(cid1 == float(int(cid1)) + 0.1) + self.assertFalse(cid1 == str(int(cid1))) + self.assertFalse(cid1 == 2**1000) + self.assertFalse(cid1 == float('inf')) + self.assertFalse(cid1 == 'spam') + self.assertFalse(cid1 == cid3) + + self.assertFalse(cid1 != cid1) + self.assertFalse(cid1 != cid2) + self.assertTrue(cid1 != cid3) + + def test_shareable(self): + chan = channels.create() + + obj = channels.create() + channels.send(chan, obj) + got = channels.recv(chan) + + self.assertEqual(got, obj) + self.assertIs(type(got), type(obj)) + # XXX Check the following in the channel tests? + #self.assertIsNot(got, obj) + + +class ChannelTests(TestBase): + + def test_create_cid(self): + cid = channels.create() + self.assertIsInstance(cid, channels.ChannelID) + + def test_sequential_ids(self): + before = channels.list_all() + id1 = channels.create() + id2 = channels.create() + id3 = channels.create() + after = channels.list_all() + + self.assertEqual(id2, int(id1) + 1) + self.assertEqual(id3, int(id2) + 1) + self.assertEqual(set(after) - set(before), {id1, id2, id3}) + + def test_ids_global(self): + id1 = interpreters.create() + out = _run_output(id1, dedent(""" + import _xxinterpchannels as _channels + cid = _channels.create() + print(cid) + """)) + cid1 = int(out.strip()) + + id2 = interpreters.create() + out = _run_output(id2, dedent(""" + import _xxinterpchannels as _channels + cid = _channels.create() + print(cid) + """)) + cid2 = int(out.strip()) + + self.assertEqual(cid2, int(cid1) + 1) + + def test_channel_list_interpreters_none(self): + """Test listing interpreters for a channel with no associations.""" + # Test for channel with no associated interpreters. + cid = channels.create() + send_interps = channels.list_interpreters(cid, send=True) + recv_interps = channels.list_interpreters(cid, send=False) + self.assertEqual(send_interps, []) + self.assertEqual(recv_interps, []) + + def test_channel_list_interpreters_basic(self): + """Test basic listing channel interpreters.""" + interp0 = interpreters.get_main() + cid = channels.create() + channels.send(cid, "send") + # Test for a channel that has one end associated to an interpreter. + send_interps = channels.list_interpreters(cid, send=True) + recv_interps = channels.list_interpreters(cid, send=False) + self.assertEqual(send_interps, [interp0]) + self.assertEqual(recv_interps, []) + + interp1 = interpreters.create() + _run_output(interp1, dedent(f""" + import _xxinterpchannels as _channels + obj = _channels.recv({cid}) + """)) + # Test for channel that has both ends associated to an interpreter. + send_interps = channels.list_interpreters(cid, send=True) + recv_interps = channels.list_interpreters(cid, send=False) + self.assertEqual(send_interps, [interp0]) + self.assertEqual(recv_interps, [interp1]) + + def test_channel_list_interpreters_multiple(self): + """Test listing interpreters for a channel with many associations.""" + interp0 = interpreters.get_main() + interp1 = interpreters.create() + interp2 = interpreters.create() + interp3 = interpreters.create() + cid = channels.create() + + channels.send(cid, "send") + _run_output(interp1, dedent(f""" + import _xxinterpchannels as _channels + _channels.send({cid}, "send") + """)) + _run_output(interp2, dedent(f""" + import _xxinterpchannels as _channels + obj = _channels.recv({cid}) + """)) + _run_output(interp3, dedent(f""" + import _xxinterpchannels as _channels + obj = _channels.recv({cid}) + """)) + send_interps = channels.list_interpreters(cid, send=True) + recv_interps = channels.list_interpreters(cid, send=False) + self.assertEqual(set(send_interps), {interp0, interp1}) + self.assertEqual(set(recv_interps), {interp2, interp3}) + + def test_channel_list_interpreters_destroyed(self): + """Test listing channel interpreters with a destroyed interpreter.""" + interp0 = interpreters.get_main() + interp1 = interpreters.create() + cid = channels.create() + channels.send(cid, "send") + _run_output(interp1, dedent(f""" + import _xxinterpchannels as _channels + obj = _channels.recv({cid}) + """)) + # Should be one interpreter associated with each end. + send_interps = channels.list_interpreters(cid, send=True) + recv_interps = channels.list_interpreters(cid, send=False) + self.assertEqual(send_interps, [interp0]) + self.assertEqual(recv_interps, [interp1]) + + interpreters.destroy(interp1) + # Destroyed interpreter should not be listed. + send_interps = channels.list_interpreters(cid, send=True) + recv_interps = channels.list_interpreters(cid, send=False) + self.assertEqual(send_interps, [interp0]) + self.assertEqual(recv_interps, []) + + def test_channel_list_interpreters_released(self): + """Test listing channel interpreters with a released channel.""" + # Set up one channel with main interpreter on the send end and two + # subinterpreters on the receive end. + interp0 = interpreters.get_main() + interp1 = interpreters.create() + interp2 = interpreters.create() + cid = channels.create() + channels.send(cid, "data") + _run_output(interp1, dedent(f""" + import _xxinterpchannels as _channels + obj = _channels.recv({cid}) + """)) + channels.send(cid, "data") + _run_output(interp2, dedent(f""" + import _xxinterpchannels as _channels + obj = _channels.recv({cid}) + """)) + # Check the setup. + send_interps = channels.list_interpreters(cid, send=True) + recv_interps = channels.list_interpreters(cid, send=False) + self.assertEqual(len(send_interps), 1) + self.assertEqual(len(recv_interps), 2) + + # Release the main interpreter from the send end. + channels.release(cid, send=True) + # Send end should have no associated interpreters. + send_interps = channels.list_interpreters(cid, send=True) + recv_interps = channels.list_interpreters(cid, send=False) + self.assertEqual(len(send_interps), 0) + self.assertEqual(len(recv_interps), 2) + + # Release one of the subinterpreters from the receive end. + _run_output(interp2, dedent(f""" + import _xxinterpchannels as _channels + _channels.release({cid}) + """)) + # Receive end should have the released interpreter removed. + send_interps = channels.list_interpreters(cid, send=True) + recv_interps = channels.list_interpreters(cid, send=False) + self.assertEqual(len(send_interps), 0) + self.assertEqual(recv_interps, [interp1]) + + def test_channel_list_interpreters_closed(self): + """Test listing channel interpreters with a closed channel.""" + interp0 = interpreters.get_main() + interp1 = interpreters.create() + cid = channels.create() + # Put something in the channel so that it's not empty. + channels.send(cid, "send") + + # Check initial state. + send_interps = channels.list_interpreters(cid, send=True) + recv_interps = channels.list_interpreters(cid, send=False) + self.assertEqual(len(send_interps), 1) + self.assertEqual(len(recv_interps), 0) + + # Force close the channel. + channels.close(cid, force=True) + # Both ends should raise an error. + with self.assertRaises(channels.ChannelClosedError): + channels.list_interpreters(cid, send=True) + with self.assertRaises(channels.ChannelClosedError): + channels.list_interpreters(cid, send=False) + + def test_channel_list_interpreters_closed_send_end(self): + """Test listing channel interpreters with a channel's send end closed.""" + interp0 = interpreters.get_main() + interp1 = interpreters.create() + cid = channels.create() + # Put something in the channel so that it's not empty. + channels.send(cid, "send") + + # Check initial state. + send_interps = channels.list_interpreters(cid, send=True) + recv_interps = channels.list_interpreters(cid, send=False) + self.assertEqual(len(send_interps), 1) + self.assertEqual(len(recv_interps), 0) + + # Close the send end of the channel. + channels.close(cid, send=True) + # Send end should raise an error. + with self.assertRaises(channels.ChannelClosedError): + channels.list_interpreters(cid, send=True) + # Receive end should not be closed (since channel is not empty). + recv_interps = channels.list_interpreters(cid, send=False) + self.assertEqual(len(recv_interps), 0) + + # Close the receive end of the channel from a subinterpreter. + _run_output(interp1, dedent(f""" + import _xxinterpchannels as _channels + _channels.close({cid}, force=True) + """)) + # Both ends should raise an error. + with self.assertRaises(channels.ChannelClosedError): + channels.list_interpreters(cid, send=True) + with self.assertRaises(channels.ChannelClosedError): + channels.list_interpreters(cid, send=False) + + #################### + + def test_send_recv_main(self): + cid = channels.create() + orig = b'spam' + channels.send(cid, orig) + obj = channels.recv(cid) + + self.assertEqual(obj, orig) + self.assertIsNot(obj, orig) + + def test_send_recv_same_interpreter(self): + id1 = interpreters.create() + out = _run_output(id1, dedent(""" + import _xxinterpchannels as _channels + cid = _channels.create() + orig = b'spam' + _channels.send(cid, orig) + obj = _channels.recv(cid) + assert obj is not orig + assert obj == orig + """)) + + def test_send_recv_different_interpreters(self): + cid = channels.create() + id1 = interpreters.create() + out = _run_output(id1, dedent(f""" + import _xxinterpchannels as _channels + _channels.send({cid}, b'spam') + """)) + obj = channels.recv(cid) + + self.assertEqual(obj, b'spam') + + def test_send_recv_different_threads(self): + cid = channels.create() + + def f(): + while True: + try: + obj = channels.recv(cid) + break + except channels.ChannelEmptyError: + time.sleep(0.1) + channels.send(cid, obj) + t = threading.Thread(target=f) + t.start() + + channels.send(cid, b'spam') + t.join() + obj = channels.recv(cid) + + self.assertEqual(obj, b'spam') + + def test_send_recv_different_interpreters_and_threads(self): + cid = channels.create() + id1 = interpreters.create() + out = None + + def f(): + nonlocal out + out = _run_output(id1, dedent(f""" + import time + import _xxinterpchannels as _channels + while True: + try: + obj = _channels.recv({cid}) + break + except _channels.ChannelEmptyError: + time.sleep(0.1) + assert(obj == b'spam') + _channels.send({cid}, b'eggs') + """)) + t = threading.Thread(target=f) + t.start() + + channels.send(cid, b'spam') + t.join() + obj = channels.recv(cid) + + self.assertEqual(obj, b'eggs') + + def test_send_not_found(self): + with self.assertRaises(channels.ChannelNotFoundError): + channels.send(10, b'spam') + + def test_recv_not_found(self): + with self.assertRaises(channels.ChannelNotFoundError): + channels.recv(10) + + def test_recv_empty(self): + cid = channels.create() + with self.assertRaises(channels.ChannelEmptyError): + channels.recv(cid) + + def test_recv_default(self): + default = object() + cid = channels.create() + obj1 = channels.recv(cid, default) + channels.send(cid, None) + channels.send(cid, 1) + channels.send(cid, b'spam') + channels.send(cid, b'eggs') + obj2 = channels.recv(cid, default) + obj3 = channels.recv(cid, default) + obj4 = channels.recv(cid) + obj5 = channels.recv(cid, default) + obj6 = channels.recv(cid, default) + + self.assertIs(obj1, default) + self.assertIs(obj2, None) + self.assertEqual(obj3, 1) + self.assertEqual(obj4, b'spam') + self.assertEqual(obj5, b'eggs') + self.assertIs(obj6, default) + + def test_recv_sending_interp_destroyed(self): + cid = channels.create() + interp = interpreters.create() + interpreters.run_string(interp, dedent(f""" + import _xxinterpchannels as _channels + _channels.send({cid}, b'spam') + """)) + interpreters.destroy(interp) + + with self.assertRaisesRegex(RuntimeError, + 'unrecognized interpreter ID'): + channels.recv(cid) + + def test_allowed_types(self): + cid = channels.create() + objects = [ + None, + 'spam', + b'spam', + 42, + ] + for obj in objects: + with self.subTest(obj): + channels.send(cid, obj) + got = channels.recv(cid) + + self.assertEqual(got, obj) + self.assertIs(type(got), type(obj)) + # XXX Check the following? + #self.assertIsNot(got, obj) + # XXX What about between interpreters? + + def test_run_string_arg_unresolved(self): + cid = channels.create() + interp = interpreters.create() + + out = _run_output(interp, dedent(""" + import _xxinterpchannels as _channels + print(cid.end) + _channels.send(cid, b'spam') + """), + dict(cid=cid.send)) + obj = channels.recv(cid) + + self.assertEqual(obj, b'spam') + self.assertEqual(out.strip(), 'send') + + # XXX For now there is no high-level channel into which the + # sent channel ID can be converted... + # Note: this test caused crashes on some buildbots (bpo-33615). + @unittest.skip('disabled until high-level channels exist') + def test_run_string_arg_resolved(self): + cid = channels.create() + cid = channels._channel_id(cid, _resolve=True) + interp = interpreters.create() + + out = _run_output(interp, dedent(""" + import _xxinterpchannels as _channels + print(chan.id.end) + _channels.send(chan.id, b'spam') + """), + dict(chan=cid.send)) + obj = channels.recv(cid) + + self.assertEqual(obj, b'spam') + self.assertEqual(out.strip(), 'send') + + # close + + def test_close_single_user(self): + cid = channels.create() + channels.send(cid, b'spam') + channels.recv(cid) + channels.close(cid) + + with self.assertRaises(channels.ChannelClosedError): + channels.send(cid, b'eggs') + with self.assertRaises(channels.ChannelClosedError): + channels.recv(cid) + + def test_close_multiple_users(self): + cid = channels.create() + id1 = interpreters.create() + id2 = interpreters.create() + interpreters.run_string(id1, dedent(f""" + import _xxinterpchannels as _channels + _channels.send({cid}, b'spam') + """)) + interpreters.run_string(id2, dedent(f""" + import _xxinterpchannels as _channels + _channels.recv({cid}) + """)) + channels.close(cid) + with self.assertRaises(interpreters.RunFailedError) as cm: + interpreters.run_string(id1, dedent(f""" + _channels.send({cid}, b'spam') + """)) + self.assertIn('ChannelClosedError', str(cm.exception)) + with self.assertRaises(interpreters.RunFailedError) as cm: + interpreters.run_string(id2, dedent(f""" + _channels.send({cid}, b'spam') + """)) + self.assertIn('ChannelClosedError', str(cm.exception)) + + def test_close_multiple_times(self): + cid = channels.create() + channels.send(cid, b'spam') + channels.recv(cid) + channels.close(cid) + + with self.assertRaises(channels.ChannelClosedError): + channels.close(cid) + + def test_close_empty(self): + tests = [ + (False, False), + (True, False), + (False, True), + (True, True), + ] + for send, recv in tests: + with self.subTest((send, recv)): + cid = channels.create() + channels.send(cid, b'spam') + channels.recv(cid) + channels.close(cid, send=send, recv=recv) + + with self.assertRaises(channels.ChannelClosedError): + channels.send(cid, b'eggs') + with self.assertRaises(channels.ChannelClosedError): + channels.recv(cid) + + def test_close_defaults_with_unused_items(self): + cid = channels.create() + channels.send(cid, b'spam') + channels.send(cid, b'ham') + + with self.assertRaises(channels.ChannelNotEmptyError): + channels.close(cid) + channels.recv(cid) + channels.send(cid, b'eggs') + + def test_close_recv_with_unused_items_unforced(self): + cid = channels.create() + channels.send(cid, b'spam') + channels.send(cid, b'ham') + + with self.assertRaises(channels.ChannelNotEmptyError): + channels.close(cid, recv=True) + channels.recv(cid) + channels.send(cid, b'eggs') + channels.recv(cid) + channels.recv(cid) + channels.close(cid, recv=True) + + def test_close_send_with_unused_items_unforced(self): + cid = channels.create() + channels.send(cid, b'spam') + channels.send(cid, b'ham') + channels.close(cid, send=True) + + with self.assertRaises(channels.ChannelClosedError): + channels.send(cid, b'eggs') + channels.recv(cid) + channels.recv(cid) + with self.assertRaises(channels.ChannelClosedError): + channels.recv(cid) + + def test_close_both_with_unused_items_unforced(self): + cid = channels.create() + channels.send(cid, b'spam') + channels.send(cid, b'ham') + + with self.assertRaises(channels.ChannelNotEmptyError): + channels.close(cid, recv=True, send=True) + channels.recv(cid) + channels.send(cid, b'eggs') + channels.recv(cid) + channels.recv(cid) + channels.close(cid, recv=True) + + def test_close_recv_with_unused_items_forced(self): + cid = channels.create() + channels.send(cid, b'spam') + channels.send(cid, b'ham') + channels.close(cid, recv=True, force=True) + + with self.assertRaises(channels.ChannelClosedError): + channels.send(cid, b'eggs') + with self.assertRaises(channels.ChannelClosedError): + channels.recv(cid) + + def test_close_send_with_unused_items_forced(self): + cid = channels.create() + channels.send(cid, b'spam') + channels.send(cid, b'ham') + channels.close(cid, send=True, force=True) + + with self.assertRaises(channels.ChannelClosedError): + channels.send(cid, b'eggs') + with self.assertRaises(channels.ChannelClosedError): + channels.recv(cid) + + def test_close_both_with_unused_items_forced(self): + cid = channels.create() + channels.send(cid, b'spam') + channels.send(cid, b'ham') + channels.close(cid, send=True, recv=True, force=True) + + with self.assertRaises(channels.ChannelClosedError): + channels.send(cid, b'eggs') + with self.assertRaises(channels.ChannelClosedError): + channels.recv(cid) + + def test_close_never_used(self): + cid = channels.create() + channels.close(cid) + + with self.assertRaises(channels.ChannelClosedError): + channels.send(cid, b'spam') + with self.assertRaises(channels.ChannelClosedError): + channels.recv(cid) + + def test_close_by_unassociated_interp(self): + cid = channels.create() + channels.send(cid, b'spam') + interp = interpreters.create() + interpreters.run_string(interp, dedent(f""" + import _xxinterpchannels as _channels + _channels.close({cid}, force=True) + """)) + with self.assertRaises(channels.ChannelClosedError): + channels.recv(cid) + with self.assertRaises(channels.ChannelClosedError): + channels.close(cid) + + def test_close_used_multiple_times_by_single_user(self): + cid = channels.create() + channels.send(cid, b'spam') + channels.send(cid, b'spam') + channels.send(cid, b'spam') + channels.recv(cid) + channels.close(cid, force=True) + + with self.assertRaises(channels.ChannelClosedError): + channels.send(cid, b'eggs') + with self.assertRaises(channels.ChannelClosedError): + channels.recv(cid) + + def test_channel_list_interpreters_invalid_channel(self): + cid = channels.create() + # Test for invalid channel ID. + with self.assertRaises(channels.ChannelNotFoundError): + channels.list_interpreters(1000, send=True) + + channels.close(cid) + # Test for a channel that has been closed. + with self.assertRaises(channels.ChannelClosedError): + channels.list_interpreters(cid, send=True) + + def test_channel_list_interpreters_invalid_args(self): + # Tests for invalid arguments passed to the API. + cid = channels.create() + with self.assertRaises(TypeError): + channels.list_interpreters(cid) + + +class ChannelReleaseTests(TestBase): + + # XXX Add more test coverage a la the tests for close(). + + """ + - main / interp / other + - run in: current thread / new thread / other thread / different threads + - end / opposite + - force / no force + - used / not used (associated / not associated) + - empty / emptied / never emptied / partly emptied + - closed / not closed + - released / not released + - creator (interp) / other + - associated interpreter not running + - associated interpreter destroyed + """ + + """ + use + pre-release + release + after + check + """ + + """ + release in: main, interp1 + creator: same, other (incl. interp2) + + use: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all + pre-release: None,send,recv,both in None,same,other(incl. interp2),same+other(incl. interp2),all + pre-release forced: None,send,recv,both in None,same,other(incl. interp2),same+other(incl. interp2),all + + release: same + release forced: same + + use after: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all + release after: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all + check released: send/recv for same/other(incl. interp2) + check closed: send/recv for same/other(incl. interp2) + """ + + def test_single_user(self): + cid = channels.create() + channels.send(cid, b'spam') + channels.recv(cid) + channels.release(cid, send=True, recv=True) + + with self.assertRaises(channels.ChannelClosedError): + channels.send(cid, b'eggs') + with self.assertRaises(channels.ChannelClosedError): + channels.recv(cid) + + def test_multiple_users(self): + cid = channels.create() + id1 = interpreters.create() + id2 = interpreters.create() + interpreters.run_string(id1, dedent(f""" + import _xxinterpchannels as _channels + _channels.send({cid}, b'spam') + """)) + out = _run_output(id2, dedent(f""" + import _xxinterpchannels as _channels + obj = _channels.recv({cid}) + _channels.release({cid}) + print(repr(obj)) + """)) + interpreters.run_string(id1, dedent(f""" + _channels.release({cid}) + """)) + + self.assertEqual(out.strip(), "b'spam'") + + def test_no_kwargs(self): + cid = channels.create() + channels.send(cid, b'spam') + channels.recv(cid) + channels.release(cid) + + with self.assertRaises(channels.ChannelClosedError): + channels.send(cid, b'eggs') + with self.assertRaises(channels.ChannelClosedError): + channels.recv(cid) + + def test_multiple_times(self): + cid = channels.create() + channels.send(cid, b'spam') + channels.recv(cid) + channels.release(cid, send=True, recv=True) + + with self.assertRaises(channels.ChannelClosedError): + channels.release(cid, send=True, recv=True) + + def test_with_unused_items(self): + cid = channels.create() + channels.send(cid, b'spam') + channels.send(cid, b'ham') + channels.release(cid, send=True, recv=True) + + with self.assertRaises(channels.ChannelClosedError): + channels.recv(cid) + + def test_never_used(self): + cid = channels.create() + channels.release(cid) + + with self.assertRaises(channels.ChannelClosedError): + channels.send(cid, b'spam') + with self.assertRaises(channels.ChannelClosedError): + channels.recv(cid) + + def test_by_unassociated_interp(self): + cid = channels.create() + channels.send(cid, b'spam') + interp = interpreters.create() + interpreters.run_string(interp, dedent(f""" + import _xxinterpchannels as _channels + _channels.release({cid}) + """)) + obj = channels.recv(cid) + channels.release(cid) + + with self.assertRaises(channels.ChannelClosedError): + channels.send(cid, b'eggs') + self.assertEqual(obj, b'spam') + + def test_close_if_unassociated(self): + # XXX Something's not right with this test... + cid = channels.create() + interp = interpreters.create() + interpreters.run_string(interp, dedent(f""" + import _xxinterpchannels as _channels + obj = _channels.send({cid}, b'spam') + _channels.release({cid}) + """)) + + with self.assertRaises(channels.ChannelClosedError): + channels.recv(cid) + + def test_partially(self): + # XXX Is partial close too weird/confusing? + cid = channels.create() + channels.send(cid, None) + channels.recv(cid) + channels.send(cid, b'spam') + channels.release(cid, send=True) + obj = channels.recv(cid) + + self.assertEqual(obj, b'spam') + + def test_used_multiple_times_by_single_user(self): + cid = channels.create() + channels.send(cid, b'spam') + channels.send(cid, b'spam') + channels.send(cid, b'spam') + channels.recv(cid) + channels.release(cid, send=True, recv=True) + + with self.assertRaises(channels.ChannelClosedError): + channels.send(cid, b'eggs') + with self.assertRaises(channels.ChannelClosedError): + channels.recv(cid) + + +class ChannelCloseFixture(namedtuple('ChannelCloseFixture', + 'end interp other extra creator')): + + # Set this to True to avoid creating interpreters, e.g. when + # scanning through test permutations without running them. + QUICK = False + + def __new__(cls, end, interp, other, extra, creator): + assert end in ('send', 'recv') + if cls.QUICK: + known = {} + else: + interp = Interpreter.from_raw(interp) + other = Interpreter.from_raw(other) + extra = Interpreter.from_raw(extra) + known = { + interp.name: interp, + other.name: other, + extra.name: extra, + } + if not creator: + creator = 'same' + self = super().__new__(cls, end, interp, other, extra, creator) + self._prepped = set() + self._state = ChannelState() + self._known = known + return self + + @property + def state(self): + return self._state + + @property + def cid(self): + try: + return self._cid + except AttributeError: + creator = self._get_interpreter(self.creator) + self._cid = self._new_channel(creator) + return self._cid + + def get_interpreter(self, interp): + interp = self._get_interpreter(interp) + self._prep_interpreter(interp) + return interp + + def expect_closed_error(self, end=None): + if end is None: + end = self.end + if end == 'recv' and self.state.closed == 'send': + return False + return bool(self.state.closed) + + def prep_interpreter(self, interp): + self._prep_interpreter(interp) + + def record_action(self, action, result): + self._state = result + + def clean_up(self): + clean_up_interpreters() + clean_up_channels() + + # internal methods + + def _new_channel(self, creator): + if creator.name == 'main': + return channels.create() + else: + ch = channels.create() + run_interp(creator.id, f""" + import _xxsubinterpreters + cid = _xxsubchannels.create() + # We purposefully send back an int to avoid tying the + # channel to the other interpreter. + _xxsubchannels.send({ch}, int(cid)) + del _xxsubinterpreters + """) + self._cid = channels.recv(ch) + return self._cid + + def _get_interpreter(self, interp): + if interp in ('same', 'interp'): + return self.interp + elif interp == 'other': + return self.other + elif interp == 'extra': + return self.extra + else: + name = interp + try: + interp = self._known[name] + except KeyError: + interp = self._known[name] = Interpreter(name) + return interp + + def _prep_interpreter(self, interp): + if interp.id in self._prepped: + return + self._prepped.add(interp.id) + if interp.name == 'main': + return + run_interp(interp.id, f""" + import _xxinterpchannels as channels + import test.test__xxinterpchannels as helpers + ChannelState = helpers.ChannelState + try: + cid + except NameError: + cid = channels._channel_id({self.cid}) + """) + + +@unittest.skip('these tests take several hours to run') +class ExhaustiveChannelTests(TestBase): + + """ + - main / interp / other + - run in: current thread / new thread / other thread / different threads + - end / opposite + - force / no force + - used / not used (associated / not associated) + - empty / emptied / never emptied / partly emptied + - closed / not closed + - released / not released + - creator (interp) / other + - associated interpreter not running + - associated interpreter destroyed + + - close after unbound + """ + + """ + use + pre-close + close + after + check + """ + + """ + close in: main, interp1 + creator: same, other, extra + + use: None,send,recv,send/recv in None,same,other,same+other,all + pre-close: None,send,recv in None,same,other,same+other,all + pre-close forced: None,send,recv in None,same,other,same+other,all + + close: same + close forced: same + + use after: None,send,recv,send/recv in None,same,other,extra,same+other,all + close after: None,send,recv,send/recv in None,same,other,extra,same+other,all + check closed: send/recv for same/other(incl. interp2) + """ + + def iter_action_sets(self): + # - used / not used (associated / not associated) + # - empty / emptied / never emptied / partly emptied + # - closed / not closed + # - released / not released + + # never used + yield [] + + # only pre-closed (and possible used after) + for closeactions in self._iter_close_action_sets('same', 'other'): + yield closeactions + for postactions in self._iter_post_close_action_sets(): + yield closeactions + postactions + for closeactions in self._iter_close_action_sets('other', 'extra'): + yield closeactions + for postactions in self._iter_post_close_action_sets(): + yield closeactions + postactions + + # used + for useactions in self._iter_use_action_sets('same', 'other'): + yield useactions + for closeactions in self._iter_close_action_sets('same', 'other'): + actions = useactions + closeactions + yield actions + for postactions in self._iter_post_close_action_sets(): + yield actions + postactions + for closeactions in self._iter_close_action_sets('other', 'extra'): + actions = useactions + closeactions + yield actions + for postactions in self._iter_post_close_action_sets(): + yield actions + postactions + for useactions in self._iter_use_action_sets('other', 'extra'): + yield useactions + for closeactions in self._iter_close_action_sets('same', 'other'): + actions = useactions + closeactions + yield actions + for postactions in self._iter_post_close_action_sets(): + yield actions + postactions + for closeactions in self._iter_close_action_sets('other', 'extra'): + actions = useactions + closeactions + yield actions + for postactions in self._iter_post_close_action_sets(): + yield actions + postactions + + def _iter_use_action_sets(self, interp1, interp2): + interps = (interp1, interp2) + + # only recv end used + yield [ + ChannelAction('use', 'recv', interp1), + ] + yield [ + ChannelAction('use', 'recv', interp2), + ] + yield [ + ChannelAction('use', 'recv', interp1), + ChannelAction('use', 'recv', interp2), + ] + + # never emptied + yield [ + ChannelAction('use', 'send', interp1), + ] + yield [ + ChannelAction('use', 'send', interp2), + ] + yield [ + ChannelAction('use', 'send', interp1), + ChannelAction('use', 'send', interp2), + ] + + # partially emptied + for interp1 in interps: + for interp2 in interps: + for interp3 in interps: + yield [ + ChannelAction('use', 'send', interp1), + ChannelAction('use', 'send', interp2), + ChannelAction('use', 'recv', interp3), + ] + + # fully emptied + for interp1 in interps: + for interp2 in interps: + for interp3 in interps: + for interp4 in interps: + yield [ + ChannelAction('use', 'send', interp1), + ChannelAction('use', 'send', interp2), + ChannelAction('use', 'recv', interp3), + ChannelAction('use', 'recv', interp4), + ] + + def _iter_close_action_sets(self, interp1, interp2): + ends = ('recv', 'send') + interps = (interp1, interp2) + for force in (True, False): + op = 'force-close' if force else 'close' + for interp in interps: + for end in ends: + yield [ + ChannelAction(op, end, interp), + ] + for recvop in ('close', 'force-close'): + for sendop in ('close', 'force-close'): + for recv in interps: + for send in interps: + yield [ + ChannelAction(recvop, 'recv', recv), + ChannelAction(sendop, 'send', send), + ] + + def _iter_post_close_action_sets(self): + for interp in ('same', 'extra', 'other'): + yield [ + ChannelAction('use', 'recv', interp), + ] + yield [ + ChannelAction('use', 'send', interp), + ] + + def run_actions(self, fix, actions): + for action in actions: + self.run_action(fix, action) + + def run_action(self, fix, action, *, hideclosed=True): + end = action.resolve_end(fix.end) + interp = action.resolve_interp(fix.interp, fix.other, fix.extra) + fix.prep_interpreter(interp) + if interp.name == 'main': + result = run_action( + fix.cid, + action.action, + end, + fix.state, + hideclosed=hideclosed, + ) + fix.record_action(action, result) + else: + _cid = channels.create() + run_interp(interp.id, f""" + result = helpers.run_action( + {fix.cid}, + {repr(action.action)}, + {repr(end)}, + {repr(fix.state)}, + hideclosed={hideclosed}, + ) + channels.send({_cid}, result.pending.to_bytes(1, 'little')) + channels.send({_cid}, b'X' if result.closed else b'') + """) + result = ChannelState( + pending=int.from_bytes(channels.recv(_cid), 'little'), + closed=bool(channels.recv(_cid)), + ) + fix.record_action(action, result) + + def iter_fixtures(self): + # XXX threads? + interpreters = [ + ('main', 'interp', 'extra'), + ('interp', 'main', 'extra'), + ('interp1', 'interp2', 'extra'), + ('interp1', 'interp2', 'main'), + ] + for interp, other, extra in interpreters: + for creator in ('same', 'other', 'creator'): + for end in ('send', 'recv'): + yield ChannelCloseFixture(end, interp, other, extra, creator) + + def _close(self, fix, *, force): + op = 'force-close' if force else 'close' + close = ChannelAction(op, fix.end, 'same') + if not fix.expect_closed_error(): + self.run_action(fix, close, hideclosed=False) + else: + with self.assertRaises(channels.ChannelClosedError): + self.run_action(fix, close, hideclosed=False) + + def _assert_closed_in_interp(self, fix, interp=None): + if interp is None or interp.name == 'main': + with self.assertRaises(channels.ChannelClosedError): + channels.recv(fix.cid) + with self.assertRaises(channels.ChannelClosedError): + channels.send(fix.cid, b'spam') + with self.assertRaises(channels.ChannelClosedError): + channels.close(fix.cid) + with self.assertRaises(channels.ChannelClosedError): + channels.close(fix.cid, force=True) + else: + run_interp(interp.id, f""" + with helpers.expect_channel_closed(): + channels.recv(cid) + """) + run_interp(interp.id, f""" + with helpers.expect_channel_closed(): + channels.send(cid, b'spam') + """) + run_interp(interp.id, f""" + with helpers.expect_channel_closed(): + channels.close(cid) + """) + run_interp(interp.id, f""" + with helpers.expect_channel_closed(): + channels.close(cid, force=True) + """) + + def _assert_closed(self, fix): + self.assertTrue(fix.state.closed) + + for _ in range(fix.state.pending): + channels.recv(fix.cid) + self._assert_closed_in_interp(fix) + + for interp in ('same', 'other'): + interp = fix.get_interpreter(interp) + if interp.name == 'main': + continue + self._assert_closed_in_interp(fix, interp) + + interp = fix.get_interpreter('fresh') + self._assert_closed_in_interp(fix, interp) + + def _iter_close_tests(self, verbose=False): + i = 0 + for actions in self.iter_action_sets(): + print() + for fix in self.iter_fixtures(): + i += 1 + if i > 1000: + return + if verbose: + if (i - 1) % 6 == 0: + print() + print(i, fix, '({} actions)'.format(len(actions))) + else: + if (i - 1) % 6 == 0: + print(' ', end='') + print('.', end=''); sys.stdout.flush() + yield i, fix, actions + if verbose: + print('---') + print() + + # This is useful for scanning through the possible tests. + def _skim_close_tests(self): + ChannelCloseFixture.QUICK = True + for i, fix, actions in self._iter_close_tests(): + pass + + def test_close(self): + for i, fix, actions in self._iter_close_tests(): + with self.subTest('{} {} {}'.format(i, fix, actions)): + fix.prep_interpreter(fix.interp) + self.run_actions(fix, actions) + + self._close(fix, force=False) + + self._assert_closed(fix) + # XXX Things slow down if we have too many interpreters. + fix.clean_up() + + def test_force_close(self): + for i, fix, actions in self._iter_close_tests(): + with self.subTest('{} {} {}'.format(i, fix, actions)): + fix.prep_interpreter(fix.interp) + self.run_actions(fix, actions) + + self._close(fix, force=True) + + self._assert_closed(fix) + # XXX Things slow down if we have too many interpreters. + fix.clean_up() + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index 18900bb9f7162c..687fcf3b770522 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -9,6 +9,7 @@ import time import unittest +import _testcapi from test import support from test.support import import_helper from test.support import script_helper @@ -73,207 +74,6 @@ def run(): t.join() -#@contextmanager -#def run_threaded(id, source, **shared): -# def run(): -# run_interp(id, source, **shared) -# t = threading.Thread(target=run) -# t.start() -# yield -# t.join() - - -def run_interp(id, source, **shared): - _run_interp(id, source, shared) - - -def _run_interp(id, source, shared, _mainns={}): - source = dedent(source) - main = interpreters.get_main() - if main == id: - if interpreters.get_current() != main: - raise RuntimeError - # XXX Run a func? - exec(source, _mainns) - else: - interpreters.run_string(id, source, shared) - - -class Interpreter(namedtuple('Interpreter', 'name id')): - - @classmethod - def from_raw(cls, raw): - if isinstance(raw, cls): - return raw - elif isinstance(raw, str): - return cls(raw) - else: - raise NotImplementedError - - def __new__(cls, name=None, id=None): - main = interpreters.get_main() - if id == main: - if not name: - name = 'main' - elif name != 'main': - raise ValueError( - 'name mismatch (expected "main", got "{}")'.format(name)) - id = main - elif id is not None: - if not name: - name = 'interp' - elif name == 'main': - raise ValueError('name mismatch (unexpected "main")') - if not isinstance(id, interpreters.InterpreterID): - id = interpreters.InterpreterID(id) - elif not name or name == 'main': - name = 'main' - id = main - else: - id = interpreters.create() - self = super().__new__(cls, name, id) - return self - - -# XXX expect_channel_closed() is unnecessary once we improve exc propagation. - -@contextlib.contextmanager -def expect_channel_closed(): - try: - yield - except interpreters.ChannelClosedError: - pass - else: - assert False, 'channel not closed' - - -class ChannelAction(namedtuple('ChannelAction', 'action end interp')): - - def __new__(cls, action, end=None, interp=None): - if not end: - end = 'both' - if not interp: - interp = 'main' - self = super().__new__(cls, action, end, interp) - return self - - def __init__(self, *args, **kwargs): - if self.action == 'use': - if self.end not in ('same', 'opposite', 'send', 'recv'): - raise ValueError(self.end) - elif self.action in ('close', 'force-close'): - if self.end not in ('both', 'same', 'opposite', 'send', 'recv'): - raise ValueError(self.end) - else: - raise ValueError(self.action) - if self.interp not in ('main', 'same', 'other', 'extra'): - raise ValueError(self.interp) - - def resolve_end(self, end): - if self.end == 'same': - return end - elif self.end == 'opposite': - return 'recv' if end == 'send' else 'send' - else: - return self.end - - def resolve_interp(self, interp, other, extra): - if self.interp == 'same': - return interp - elif self.interp == 'other': - if other is None: - raise RuntimeError - return other - elif self.interp == 'extra': - if extra is None: - raise RuntimeError - return extra - elif self.interp == 'main': - if interp.name == 'main': - return interp - elif other and other.name == 'main': - return other - else: - raise RuntimeError - # Per __init__(), there aren't any others. - - -class ChannelState(namedtuple('ChannelState', 'pending closed')): - - def __new__(cls, pending=0, *, closed=False): - self = super().__new__(cls, pending, closed) - return self - - def incr(self): - return type(self)(self.pending + 1, closed=self.closed) - - def decr(self): - return type(self)(self.pending - 1, closed=self.closed) - - def close(self, *, force=True): - if self.closed: - if not force or self.pending == 0: - return self - return type(self)(0 if force else self.pending, closed=True) - - -def run_action(cid, action, end, state, *, hideclosed=True): - if state.closed: - if action == 'use' and end == 'recv' and state.pending: - expectfail = False - else: - expectfail = True - else: - expectfail = False - - try: - result = _run_action(cid, action, end, state) - except interpreters.ChannelClosedError: - if not hideclosed and not expectfail: - raise - result = state.close() - else: - if expectfail: - raise ... # XXX - return result - - -def _run_action(cid, action, end, state): - if action == 'use': - if end == 'send': - interpreters.channel_send(cid, b'spam') - return state.incr() - elif end == 'recv': - if not state.pending: - try: - interpreters.channel_recv(cid) - except interpreters.ChannelEmptyError: - return state - else: - raise Exception('expected ChannelEmptyError') - else: - interpreters.channel_recv(cid) - return state.decr() - else: - raise ValueError(end) - elif action == 'close': - kwargs = {} - if end in ('recv', 'send'): - kwargs[end] = True - interpreters.channel_close(cid, **kwargs) - return state.close() - elif action == 'force-close': - kwargs = { - 'force': True, - } - if end in ('recv', 'send'): - kwargs[end] = True - interpreters.channel_close(cid, **kwargs) - return state.close(force=True) - else: - raise ValueError(action) - - def clean_up_interpreters(): for id in interpreters.list_all(): if id == 0: # main @@ -284,18 +84,9 @@ def clean_up_interpreters(): pass # already destroyed -def clean_up_channels(): - for cid in interpreters.channel_list_all(): - try: - interpreters.channel_destroy(cid) - except interpreters.ChannelNotFoundError: - pass # already destroyed - - class TestBase(unittest.TestCase): def tearDown(self): - clean_up_channels() clean_up_interpreters() @@ -354,30 +145,20 @@ class SubBytes(bytes): class ShareableTypeTests(unittest.TestCase): - def setUp(self): - super().setUp() - self.cid = interpreters.channel_create() - - def tearDown(self): - interpreters.channel_destroy(self.cid) - super().tearDown() - def _assert_values(self, values): for obj in values: with self.subTest(obj): - interpreters.channel_send(self.cid, obj) - got = interpreters.channel_recv(self.cid) + xid = _testcapi.get_crossinterp_data(obj) + got = _testcapi.restore_crossinterp_data(xid) self.assertEqual(got, obj) self.assertIs(type(got), type(obj)) - # XXX Check the following in the channel tests? - #self.assertIsNot(got, obj) def test_singletons(self): for obj in [None]: with self.subTest(obj): - interpreters.channel_send(self.cid, obj) - got = interpreters.channel_recv(self.cid) + xid = _testcapi.get_crossinterp_data(obj) + got = _testcapi.restore_crossinterp_data(xid) # XXX What about between interpreters? self.assertIs(got, obj) @@ -408,7 +189,7 @@ def test_non_shareable_int(self): for i in ints: with self.subTest(i): with self.assertRaises(OverflowError): - interpreters.channel_send(self.cid, i) + _testcapi.get_crossinterp_data(i) class ModuleTests(TestBase): @@ -555,7 +336,7 @@ def test_bad_id(self): self.assertRaises(OverflowError, interpreters.InterpreterID, 2**64) def test_does_not_exist(self): - id = interpreters.channel_create() + id = interpreters.create() with self.assertRaises(RuntimeError): interpreters.InterpreterID(int(id) + 1) # unforced @@ -864,6 +645,22 @@ def f(): self.assertEqual(out, 'it worked!') + def test_shareable_types(self): + interp = interpreters.create() + objects = [ + None, + 'spam', + b'spam', + 42, + ] + for obj in objects: + with self.subTest(obj): + interpreters.run_string( + interp, + f'assert(obj == {obj!r})', + shared=dict(obj=obj), + ) + def test_os_exec(self): expected = 'spam spam spam spam spam' subinterp = interpreters.create() @@ -1130,1285 +927,5 @@ def f(): self.assertEqual(retcode, 0) -################################## -# channel tests - -class ChannelIDTests(TestBase): - - def test_default_kwargs(self): - cid = interpreters._channel_id(10, force=True) - - self.assertEqual(int(cid), 10) - self.assertEqual(cid.end, 'both') - - def test_with_kwargs(self): - cid = interpreters._channel_id(10, send=True, force=True) - self.assertEqual(cid.end, 'send') - - cid = interpreters._channel_id(10, send=True, recv=False, force=True) - self.assertEqual(cid.end, 'send') - - cid = interpreters._channel_id(10, recv=True, force=True) - self.assertEqual(cid.end, 'recv') - - cid = interpreters._channel_id(10, recv=True, send=False, force=True) - self.assertEqual(cid.end, 'recv') - - cid = interpreters._channel_id(10, send=True, recv=True, force=True) - self.assertEqual(cid.end, 'both') - - def test_coerce_id(self): - class Int(str): - def __index__(self): - return 10 - - cid = interpreters._channel_id(Int(), force=True) - self.assertEqual(int(cid), 10) - - def test_bad_id(self): - self.assertRaises(TypeError, interpreters._channel_id, object()) - self.assertRaises(TypeError, interpreters._channel_id, 10.0) - self.assertRaises(TypeError, interpreters._channel_id, '10') - self.assertRaises(TypeError, interpreters._channel_id, b'10') - self.assertRaises(ValueError, interpreters._channel_id, -1) - self.assertRaises(OverflowError, interpreters._channel_id, 2**64) - - def test_bad_kwargs(self): - with self.assertRaises(ValueError): - interpreters._channel_id(10, send=False, recv=False) - - def test_does_not_exist(self): - cid = interpreters.channel_create() - with self.assertRaises(interpreters.ChannelNotFoundError): - interpreters._channel_id(int(cid) + 1) # unforced - - def test_str(self): - cid = interpreters._channel_id(10, force=True) - self.assertEqual(str(cid), '10') - - def test_repr(self): - cid = interpreters._channel_id(10, force=True) - self.assertEqual(repr(cid), 'ChannelID(10)') - - cid = interpreters._channel_id(10, send=True, force=True) - self.assertEqual(repr(cid), 'ChannelID(10, send=True)') - - cid = interpreters._channel_id(10, recv=True, force=True) - self.assertEqual(repr(cid), 'ChannelID(10, recv=True)') - - cid = interpreters._channel_id(10, send=True, recv=True, force=True) - self.assertEqual(repr(cid), 'ChannelID(10)') - - def test_equality(self): - cid1 = interpreters.channel_create() - cid2 = interpreters._channel_id(int(cid1)) - cid3 = interpreters.channel_create() - - self.assertTrue(cid1 == cid1) - self.assertTrue(cid1 == cid2) - self.assertTrue(cid1 == int(cid1)) - self.assertTrue(int(cid1) == cid1) - self.assertTrue(cid1 == float(int(cid1))) - self.assertTrue(float(int(cid1)) == cid1) - self.assertFalse(cid1 == float(int(cid1)) + 0.1) - self.assertFalse(cid1 == str(int(cid1))) - self.assertFalse(cid1 == 2**1000) - self.assertFalse(cid1 == float('inf')) - self.assertFalse(cid1 == 'spam') - self.assertFalse(cid1 == cid3) - - self.assertFalse(cid1 != cid1) - self.assertFalse(cid1 != cid2) - self.assertTrue(cid1 != cid3) - - def test_shareable(self): - chan = interpreters.channel_create() - - obj = interpreters.channel_create() - interpreters.channel_send(chan, obj) - got = interpreters.channel_recv(chan) - - self.assertEqual(got, obj) - self.assertIs(type(got), type(obj)) - # XXX Check the following in the channel tests? - #self.assertIsNot(got, obj) - - -class ChannelTests(TestBase): - - def test_create_cid(self): - cid = interpreters.channel_create() - self.assertIsInstance(cid, interpreters.ChannelID) - - def test_sequential_ids(self): - before = interpreters.channel_list_all() - id1 = interpreters.channel_create() - id2 = interpreters.channel_create() - id3 = interpreters.channel_create() - after = interpreters.channel_list_all() - - self.assertEqual(id2, int(id1) + 1) - self.assertEqual(id3, int(id2) + 1) - self.assertEqual(set(after) - set(before), {id1, id2, id3}) - - def test_ids_global(self): - id1 = interpreters.create() - out = _run_output(id1, dedent(""" - import _xxsubinterpreters as _interpreters - cid = _interpreters.channel_create() - print(cid) - """)) - cid1 = int(out.strip()) - - id2 = interpreters.create() - out = _run_output(id2, dedent(""" - import _xxsubinterpreters as _interpreters - cid = _interpreters.channel_create() - print(cid) - """)) - cid2 = int(out.strip()) - - self.assertEqual(cid2, int(cid1) + 1) - - def test_channel_list_interpreters_none(self): - """Test listing interpreters for a channel with no associations.""" - # Test for channel with no associated interpreters. - cid = interpreters.channel_create() - send_interps = interpreters.channel_list_interpreters(cid, send=True) - recv_interps = interpreters.channel_list_interpreters(cid, send=False) - self.assertEqual(send_interps, []) - self.assertEqual(recv_interps, []) - - def test_channel_list_interpreters_basic(self): - """Test basic listing channel interpreters.""" - interp0 = interpreters.get_main() - cid = interpreters.channel_create() - interpreters.channel_send(cid, "send") - # Test for a channel that has one end associated to an interpreter. - send_interps = interpreters.channel_list_interpreters(cid, send=True) - recv_interps = interpreters.channel_list_interpreters(cid, send=False) - self.assertEqual(send_interps, [interp0]) - self.assertEqual(recv_interps, []) - - interp1 = interpreters.create() - _run_output(interp1, dedent(f""" - import _xxsubinterpreters as _interpreters - obj = _interpreters.channel_recv({cid}) - """)) - # Test for channel that has both ends associated to an interpreter. - send_interps = interpreters.channel_list_interpreters(cid, send=True) - recv_interps = interpreters.channel_list_interpreters(cid, send=False) - self.assertEqual(send_interps, [interp0]) - self.assertEqual(recv_interps, [interp1]) - - def test_channel_list_interpreters_multiple(self): - """Test listing interpreters for a channel with many associations.""" - interp0 = interpreters.get_main() - interp1 = interpreters.create() - interp2 = interpreters.create() - interp3 = interpreters.create() - cid = interpreters.channel_create() - - interpreters.channel_send(cid, "send") - _run_output(interp1, dedent(f""" - import _xxsubinterpreters as _interpreters - _interpreters.channel_send({cid}, "send") - """)) - _run_output(interp2, dedent(f""" - import _xxsubinterpreters as _interpreters - obj = _interpreters.channel_recv({cid}) - """)) - _run_output(interp3, dedent(f""" - import _xxsubinterpreters as _interpreters - obj = _interpreters.channel_recv({cid}) - """)) - send_interps = interpreters.channel_list_interpreters(cid, send=True) - recv_interps = interpreters.channel_list_interpreters(cid, send=False) - self.assertEqual(set(send_interps), {interp0, interp1}) - self.assertEqual(set(recv_interps), {interp2, interp3}) - - def test_channel_list_interpreters_destroyed(self): - """Test listing channel interpreters with a destroyed interpreter.""" - interp0 = interpreters.get_main() - interp1 = interpreters.create() - cid = interpreters.channel_create() - interpreters.channel_send(cid, "send") - _run_output(interp1, dedent(f""" - import _xxsubinterpreters as _interpreters - obj = _interpreters.channel_recv({cid}) - """)) - # Should be one interpreter associated with each end. - send_interps = interpreters.channel_list_interpreters(cid, send=True) - recv_interps = interpreters.channel_list_interpreters(cid, send=False) - self.assertEqual(send_interps, [interp0]) - self.assertEqual(recv_interps, [interp1]) - - interpreters.destroy(interp1) - # Destroyed interpreter should not be listed. - send_interps = interpreters.channel_list_interpreters(cid, send=True) - recv_interps = interpreters.channel_list_interpreters(cid, send=False) - self.assertEqual(send_interps, [interp0]) - self.assertEqual(recv_interps, []) - - def test_channel_list_interpreters_released(self): - """Test listing channel interpreters with a released channel.""" - # Set up one channel with main interpreter on the send end and two - # subinterpreters on the receive end. - interp0 = interpreters.get_main() - interp1 = interpreters.create() - interp2 = interpreters.create() - cid = interpreters.channel_create() - interpreters.channel_send(cid, "data") - _run_output(interp1, dedent(f""" - import _xxsubinterpreters as _interpreters - obj = _interpreters.channel_recv({cid}) - """)) - interpreters.channel_send(cid, "data") - _run_output(interp2, dedent(f""" - import _xxsubinterpreters as _interpreters - obj = _interpreters.channel_recv({cid}) - """)) - # Check the setup. - send_interps = interpreters.channel_list_interpreters(cid, send=True) - recv_interps = interpreters.channel_list_interpreters(cid, send=False) - self.assertEqual(len(send_interps), 1) - self.assertEqual(len(recv_interps), 2) - - # Release the main interpreter from the send end. - interpreters.channel_release(cid, send=True) - # Send end should have no associated interpreters. - send_interps = interpreters.channel_list_interpreters(cid, send=True) - recv_interps = interpreters.channel_list_interpreters(cid, send=False) - self.assertEqual(len(send_interps), 0) - self.assertEqual(len(recv_interps), 2) - - # Release one of the subinterpreters from the receive end. - _run_output(interp2, dedent(f""" - import _xxsubinterpreters as _interpreters - _interpreters.channel_release({cid}) - """)) - # Receive end should have the released interpreter removed. - send_interps = interpreters.channel_list_interpreters(cid, send=True) - recv_interps = interpreters.channel_list_interpreters(cid, send=False) - self.assertEqual(len(send_interps), 0) - self.assertEqual(recv_interps, [interp1]) - - def test_channel_list_interpreters_closed(self): - """Test listing channel interpreters with a closed channel.""" - interp0 = interpreters.get_main() - interp1 = interpreters.create() - cid = interpreters.channel_create() - # Put something in the channel so that it's not empty. - interpreters.channel_send(cid, "send") - - # Check initial state. - send_interps = interpreters.channel_list_interpreters(cid, send=True) - recv_interps = interpreters.channel_list_interpreters(cid, send=False) - self.assertEqual(len(send_interps), 1) - self.assertEqual(len(recv_interps), 0) - - # Force close the channel. - interpreters.channel_close(cid, force=True) - # Both ends should raise an error. - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_list_interpreters(cid, send=True) - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_list_interpreters(cid, send=False) - - def test_channel_list_interpreters_closed_send_end(self): - """Test listing channel interpreters with a channel's send end closed.""" - interp0 = interpreters.get_main() - interp1 = interpreters.create() - cid = interpreters.channel_create() - # Put something in the channel so that it's not empty. - interpreters.channel_send(cid, "send") - - # Check initial state. - send_interps = interpreters.channel_list_interpreters(cid, send=True) - recv_interps = interpreters.channel_list_interpreters(cid, send=False) - self.assertEqual(len(send_interps), 1) - self.assertEqual(len(recv_interps), 0) - - # Close the send end of the channel. - interpreters.channel_close(cid, send=True) - # Send end should raise an error. - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_list_interpreters(cid, send=True) - # Receive end should not be closed (since channel is not empty). - recv_interps = interpreters.channel_list_interpreters(cid, send=False) - self.assertEqual(len(recv_interps), 0) - - # Close the receive end of the channel from a subinterpreter. - _run_output(interp1, dedent(f""" - import _xxsubinterpreters as _interpreters - _interpreters.channel_close({cid}, force=True) - """)) - # Both ends should raise an error. - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_list_interpreters(cid, send=True) - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_list_interpreters(cid, send=False) - - #################### - - def test_send_recv_main(self): - cid = interpreters.channel_create() - orig = b'spam' - interpreters.channel_send(cid, orig) - obj = interpreters.channel_recv(cid) - - self.assertEqual(obj, orig) - self.assertIsNot(obj, orig) - - def test_send_recv_same_interpreter(self): - id1 = interpreters.create() - out = _run_output(id1, dedent(""" - import _xxsubinterpreters as _interpreters - cid = _interpreters.channel_create() - orig = b'spam' - _interpreters.channel_send(cid, orig) - obj = _interpreters.channel_recv(cid) - assert obj is not orig - assert obj == orig - """)) - - def test_send_recv_different_interpreters(self): - cid = interpreters.channel_create() - id1 = interpreters.create() - out = _run_output(id1, dedent(f""" - import _xxsubinterpreters as _interpreters - _interpreters.channel_send({cid}, b'spam') - """)) - obj = interpreters.channel_recv(cid) - - self.assertEqual(obj, b'spam') - - def test_send_recv_different_threads(self): - cid = interpreters.channel_create() - - def f(): - while True: - try: - obj = interpreters.channel_recv(cid) - break - except interpreters.ChannelEmptyError: - time.sleep(0.1) - interpreters.channel_send(cid, obj) - t = threading.Thread(target=f) - t.start() - - interpreters.channel_send(cid, b'spam') - t.join() - obj = interpreters.channel_recv(cid) - - self.assertEqual(obj, b'spam') - - def test_send_recv_different_interpreters_and_threads(self): - cid = interpreters.channel_create() - id1 = interpreters.create() - out = None - - def f(): - nonlocal out - out = _run_output(id1, dedent(f""" - import time - import _xxsubinterpreters as _interpreters - while True: - try: - obj = _interpreters.channel_recv({cid}) - break - except _interpreters.ChannelEmptyError: - time.sleep(0.1) - assert(obj == b'spam') - _interpreters.channel_send({cid}, b'eggs') - """)) - t = threading.Thread(target=f) - t.start() - - interpreters.channel_send(cid, b'spam') - t.join() - obj = interpreters.channel_recv(cid) - - self.assertEqual(obj, b'eggs') - - def test_send_not_found(self): - with self.assertRaises(interpreters.ChannelNotFoundError): - interpreters.channel_send(10, b'spam') - - def test_recv_not_found(self): - with self.assertRaises(interpreters.ChannelNotFoundError): - interpreters.channel_recv(10) - - def test_recv_empty(self): - cid = interpreters.channel_create() - with self.assertRaises(interpreters.ChannelEmptyError): - interpreters.channel_recv(cid) - - def test_recv_default(self): - default = object() - cid = interpreters.channel_create() - obj1 = interpreters.channel_recv(cid, default) - interpreters.channel_send(cid, None) - interpreters.channel_send(cid, 1) - interpreters.channel_send(cid, b'spam') - interpreters.channel_send(cid, b'eggs') - obj2 = interpreters.channel_recv(cid, default) - obj3 = interpreters.channel_recv(cid, default) - obj4 = interpreters.channel_recv(cid) - obj5 = interpreters.channel_recv(cid, default) - obj6 = interpreters.channel_recv(cid, default) - - self.assertIs(obj1, default) - self.assertIs(obj2, None) - self.assertEqual(obj3, 1) - self.assertEqual(obj4, b'spam') - self.assertEqual(obj5, b'eggs') - self.assertIs(obj6, default) - - def test_recv_sending_interp_destroyed(self): - cid = interpreters.channel_create() - interp = interpreters.create() - interpreters.run_string(interp, dedent(f""" - import _xxsubinterpreters as _interpreters - _interpreters.channel_send({cid}, b'spam') - """)) - interpreters.destroy(interp) - - with self.assertRaisesRegex(RuntimeError, - 'unrecognized interpreter ID'): - interpreters.channel_recv(cid) - - def test_run_string_arg_unresolved(self): - cid = interpreters.channel_create() - interp = interpreters.create() - - out = _run_output(interp, dedent(""" - import _xxsubinterpreters as _interpreters - print(cid.end) - _interpreters.channel_send(cid, b'spam') - """), - dict(cid=cid.send)) - obj = interpreters.channel_recv(cid) - - self.assertEqual(obj, b'spam') - self.assertEqual(out.strip(), 'send') - - # XXX For now there is no high-level channel into which the - # sent channel ID can be converted... - # Note: this test caused crashes on some buildbots (bpo-33615). - @unittest.skip('disabled until high-level channels exist') - def test_run_string_arg_resolved(self): - cid = interpreters.channel_create() - cid = interpreters._channel_id(cid, _resolve=True) - interp = interpreters.create() - - out = _run_output(interp, dedent(""" - import _xxsubinterpreters as _interpreters - print(chan.id.end) - _interpreters.channel_send(chan.id, b'spam') - """), - dict(chan=cid.send)) - obj = interpreters.channel_recv(cid) - - self.assertEqual(obj, b'spam') - self.assertEqual(out.strip(), 'send') - - # close - - def test_close_single_user(self): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interpreters.channel_recv(cid) - interpreters.channel_close(cid) - - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_send(cid, b'eggs') - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_recv(cid) - - def test_close_multiple_users(self): - cid = interpreters.channel_create() - id1 = interpreters.create() - id2 = interpreters.create() - interpreters.run_string(id1, dedent(f""" - import _xxsubinterpreters as _interpreters - _interpreters.channel_send({cid}, b'spam') - """)) - interpreters.run_string(id2, dedent(f""" - import _xxsubinterpreters as _interpreters - _interpreters.channel_recv({cid}) - """)) - interpreters.channel_close(cid) - with self.assertRaises(interpreters.RunFailedError) as cm: - interpreters.run_string(id1, dedent(f""" - _interpreters.channel_send({cid}, b'spam') - """)) - self.assertIn('ChannelClosedError', str(cm.exception)) - with self.assertRaises(interpreters.RunFailedError) as cm: - interpreters.run_string(id2, dedent(f""" - _interpreters.channel_send({cid}, b'spam') - """)) - self.assertIn('ChannelClosedError', str(cm.exception)) - - def test_close_multiple_times(self): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interpreters.channel_recv(cid) - interpreters.channel_close(cid) - - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_close(cid) - - def test_close_empty(self): - tests = [ - (False, False), - (True, False), - (False, True), - (True, True), - ] - for send, recv in tests: - with self.subTest((send, recv)): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interpreters.channel_recv(cid) - interpreters.channel_close(cid, send=send, recv=recv) - - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_send(cid, b'eggs') - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_recv(cid) - - def test_close_defaults_with_unused_items(self): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interpreters.channel_send(cid, b'ham') - - with self.assertRaises(interpreters.ChannelNotEmptyError): - interpreters.channel_close(cid) - interpreters.channel_recv(cid) - interpreters.channel_send(cid, b'eggs') - - def test_close_recv_with_unused_items_unforced(self): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interpreters.channel_send(cid, b'ham') - - with self.assertRaises(interpreters.ChannelNotEmptyError): - interpreters.channel_close(cid, recv=True) - interpreters.channel_recv(cid) - interpreters.channel_send(cid, b'eggs') - interpreters.channel_recv(cid) - interpreters.channel_recv(cid) - interpreters.channel_close(cid, recv=True) - - def test_close_send_with_unused_items_unforced(self): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interpreters.channel_send(cid, b'ham') - interpreters.channel_close(cid, send=True) - - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_send(cid, b'eggs') - interpreters.channel_recv(cid) - interpreters.channel_recv(cid) - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_recv(cid) - - def test_close_both_with_unused_items_unforced(self): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interpreters.channel_send(cid, b'ham') - - with self.assertRaises(interpreters.ChannelNotEmptyError): - interpreters.channel_close(cid, recv=True, send=True) - interpreters.channel_recv(cid) - interpreters.channel_send(cid, b'eggs') - interpreters.channel_recv(cid) - interpreters.channel_recv(cid) - interpreters.channel_close(cid, recv=True) - - def test_close_recv_with_unused_items_forced(self): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interpreters.channel_send(cid, b'ham') - interpreters.channel_close(cid, recv=True, force=True) - - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_send(cid, b'eggs') - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_recv(cid) - - def test_close_send_with_unused_items_forced(self): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interpreters.channel_send(cid, b'ham') - interpreters.channel_close(cid, send=True, force=True) - - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_send(cid, b'eggs') - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_recv(cid) - - def test_close_both_with_unused_items_forced(self): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interpreters.channel_send(cid, b'ham') - interpreters.channel_close(cid, send=True, recv=True, force=True) - - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_send(cid, b'eggs') - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_recv(cid) - - def test_close_never_used(self): - cid = interpreters.channel_create() - interpreters.channel_close(cid) - - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_send(cid, b'spam') - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_recv(cid) - - def test_close_by_unassociated_interp(self): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interp = interpreters.create() - interpreters.run_string(interp, dedent(f""" - import _xxsubinterpreters as _interpreters - _interpreters.channel_close({cid}, force=True) - """)) - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_recv(cid) - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_close(cid) - - def test_close_used_multiple_times_by_single_user(self): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interpreters.channel_send(cid, b'spam') - interpreters.channel_send(cid, b'spam') - interpreters.channel_recv(cid) - interpreters.channel_close(cid, force=True) - - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_send(cid, b'eggs') - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_recv(cid) - - def test_channel_list_interpreters_invalid_channel(self): - cid = interpreters.channel_create() - # Test for invalid channel ID. - with self.assertRaises(interpreters.ChannelNotFoundError): - interpreters.channel_list_interpreters(1000, send=True) - - interpreters.channel_close(cid) - # Test for a channel that has been closed. - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_list_interpreters(cid, send=True) - - def test_channel_list_interpreters_invalid_args(self): - # Tests for invalid arguments passed to the API. - cid = interpreters.channel_create() - with self.assertRaises(TypeError): - interpreters.channel_list_interpreters(cid) - - -class ChannelReleaseTests(TestBase): - - # XXX Add more test coverage a la the tests for close(). - - """ - - main / interp / other - - run in: current thread / new thread / other thread / different threads - - end / opposite - - force / no force - - used / not used (associated / not associated) - - empty / emptied / never emptied / partly emptied - - closed / not closed - - released / not released - - creator (interp) / other - - associated interpreter not running - - associated interpreter destroyed - """ - - """ - use - pre-release - release - after - check - """ - - """ - release in: main, interp1 - creator: same, other (incl. interp2) - - use: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all - pre-release: None,send,recv,both in None,same,other(incl. interp2),same+other(incl. interp2),all - pre-release forced: None,send,recv,both in None,same,other(incl. interp2),same+other(incl. interp2),all - - release: same - release forced: same - - use after: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all - release after: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all - check released: send/recv for same/other(incl. interp2) - check closed: send/recv for same/other(incl. interp2) - """ - - def test_single_user(self): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interpreters.channel_recv(cid) - interpreters.channel_release(cid, send=True, recv=True) - - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_send(cid, b'eggs') - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_recv(cid) - - def test_multiple_users(self): - cid = interpreters.channel_create() - id1 = interpreters.create() - id2 = interpreters.create() - interpreters.run_string(id1, dedent(f""" - import _xxsubinterpreters as _interpreters - _interpreters.channel_send({cid}, b'spam') - """)) - out = _run_output(id2, dedent(f""" - import _xxsubinterpreters as _interpreters - obj = _interpreters.channel_recv({cid}) - _interpreters.channel_release({cid}) - print(repr(obj)) - """)) - interpreters.run_string(id1, dedent(f""" - _interpreters.channel_release({cid}) - """)) - - self.assertEqual(out.strip(), "b'spam'") - - def test_no_kwargs(self): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interpreters.channel_recv(cid) - interpreters.channel_release(cid) - - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_send(cid, b'eggs') - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_recv(cid) - - def test_multiple_times(self): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interpreters.channel_recv(cid) - interpreters.channel_release(cid, send=True, recv=True) - - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_release(cid, send=True, recv=True) - - def test_with_unused_items(self): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interpreters.channel_send(cid, b'ham') - interpreters.channel_release(cid, send=True, recv=True) - - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_recv(cid) - - def test_never_used(self): - cid = interpreters.channel_create() - interpreters.channel_release(cid) - - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_send(cid, b'spam') - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_recv(cid) - - def test_by_unassociated_interp(self): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interp = interpreters.create() - interpreters.run_string(interp, dedent(f""" - import _xxsubinterpreters as _interpreters - _interpreters.channel_release({cid}) - """)) - obj = interpreters.channel_recv(cid) - interpreters.channel_release(cid) - - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_send(cid, b'eggs') - self.assertEqual(obj, b'spam') - - def test_close_if_unassociated(self): - # XXX Something's not right with this test... - cid = interpreters.channel_create() - interp = interpreters.create() - interpreters.run_string(interp, dedent(f""" - import _xxsubinterpreters as _interpreters - obj = _interpreters.channel_send({cid}, b'spam') - _interpreters.channel_release({cid}) - """)) - - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_recv(cid) - - def test_partially(self): - # XXX Is partial close too weird/confusing? - cid = interpreters.channel_create() - interpreters.channel_send(cid, None) - interpreters.channel_recv(cid) - interpreters.channel_send(cid, b'spam') - interpreters.channel_release(cid, send=True) - obj = interpreters.channel_recv(cid) - - self.assertEqual(obj, b'spam') - - def test_used_multiple_times_by_single_user(self): - cid = interpreters.channel_create() - interpreters.channel_send(cid, b'spam') - interpreters.channel_send(cid, b'spam') - interpreters.channel_send(cid, b'spam') - interpreters.channel_recv(cid) - interpreters.channel_release(cid, send=True, recv=True) - - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_send(cid, b'eggs') - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_recv(cid) - - -class ChannelCloseFixture(namedtuple('ChannelCloseFixture', - 'end interp other extra creator')): - - # Set this to True to avoid creating interpreters, e.g. when - # scanning through test permutations without running them. - QUICK = False - - def __new__(cls, end, interp, other, extra, creator): - assert end in ('send', 'recv') - if cls.QUICK: - known = {} - else: - interp = Interpreter.from_raw(interp) - other = Interpreter.from_raw(other) - extra = Interpreter.from_raw(extra) - known = { - interp.name: interp, - other.name: other, - extra.name: extra, - } - if not creator: - creator = 'same' - self = super().__new__(cls, end, interp, other, extra, creator) - self._prepped = set() - self._state = ChannelState() - self._known = known - return self - - @property - def state(self): - return self._state - - @property - def cid(self): - try: - return self._cid - except AttributeError: - creator = self._get_interpreter(self.creator) - self._cid = self._new_channel(creator) - return self._cid - - def get_interpreter(self, interp): - interp = self._get_interpreter(interp) - self._prep_interpreter(interp) - return interp - - def expect_closed_error(self, end=None): - if end is None: - end = self.end - if end == 'recv' and self.state.closed == 'send': - return False - return bool(self.state.closed) - - def prep_interpreter(self, interp): - self._prep_interpreter(interp) - - def record_action(self, action, result): - self._state = result - - def clean_up(self): - clean_up_interpreters() - clean_up_channels() - - # internal methods - - def _new_channel(self, creator): - if creator.name == 'main': - return interpreters.channel_create() - else: - ch = interpreters.channel_create() - run_interp(creator.id, f""" - import _xxsubinterpreters - cid = _xxsubinterpreters.channel_create() - # We purposefully send back an int to avoid tying the - # channel to the other interpreter. - _xxsubinterpreters.channel_send({ch}, int(cid)) - del _xxsubinterpreters - """) - self._cid = interpreters.channel_recv(ch) - return self._cid - - def _get_interpreter(self, interp): - if interp in ('same', 'interp'): - return self.interp - elif interp == 'other': - return self.other - elif interp == 'extra': - return self.extra - else: - name = interp - try: - interp = self._known[name] - except KeyError: - interp = self._known[name] = Interpreter(name) - return interp - - def _prep_interpreter(self, interp): - if interp.id in self._prepped: - return - self._prepped.add(interp.id) - if interp.name == 'main': - return - run_interp(interp.id, f""" - import _xxsubinterpreters as interpreters - import test.test__xxsubinterpreters as helpers - ChannelState = helpers.ChannelState - try: - cid - except NameError: - cid = interpreters._channel_id({self.cid}) - """) - - -@unittest.skip('these tests take several hours to run') -class ExhaustiveChannelTests(TestBase): - - """ - - main / interp / other - - run in: current thread / new thread / other thread / different threads - - end / opposite - - force / no force - - used / not used (associated / not associated) - - empty / emptied / never emptied / partly emptied - - closed / not closed - - released / not released - - creator (interp) / other - - associated interpreter not running - - associated interpreter destroyed - - - close after unbound - """ - - """ - use - pre-close - close - after - check - """ - - """ - close in: main, interp1 - creator: same, other, extra - - use: None,send,recv,send/recv in None,same,other,same+other,all - pre-close: None,send,recv in None,same,other,same+other,all - pre-close forced: None,send,recv in None,same,other,same+other,all - - close: same - close forced: same - - use after: None,send,recv,send/recv in None,same,other,extra,same+other,all - close after: None,send,recv,send/recv in None,same,other,extra,same+other,all - check closed: send/recv for same/other(incl. interp2) - """ - - def iter_action_sets(self): - # - used / not used (associated / not associated) - # - empty / emptied / never emptied / partly emptied - # - closed / not closed - # - released / not released - - # never used - yield [] - - # only pre-closed (and possible used after) - for closeactions in self._iter_close_action_sets('same', 'other'): - yield closeactions - for postactions in self._iter_post_close_action_sets(): - yield closeactions + postactions - for closeactions in self._iter_close_action_sets('other', 'extra'): - yield closeactions - for postactions in self._iter_post_close_action_sets(): - yield closeactions + postactions - - # used - for useactions in self._iter_use_action_sets('same', 'other'): - yield useactions - for closeactions in self._iter_close_action_sets('same', 'other'): - actions = useactions + closeactions - yield actions - for postactions in self._iter_post_close_action_sets(): - yield actions + postactions - for closeactions in self._iter_close_action_sets('other', 'extra'): - actions = useactions + closeactions - yield actions - for postactions in self._iter_post_close_action_sets(): - yield actions + postactions - for useactions in self._iter_use_action_sets('other', 'extra'): - yield useactions - for closeactions in self._iter_close_action_sets('same', 'other'): - actions = useactions + closeactions - yield actions - for postactions in self._iter_post_close_action_sets(): - yield actions + postactions - for closeactions in self._iter_close_action_sets('other', 'extra'): - actions = useactions + closeactions - yield actions - for postactions in self._iter_post_close_action_sets(): - yield actions + postactions - - def _iter_use_action_sets(self, interp1, interp2): - interps = (interp1, interp2) - - # only recv end used - yield [ - ChannelAction('use', 'recv', interp1), - ] - yield [ - ChannelAction('use', 'recv', interp2), - ] - yield [ - ChannelAction('use', 'recv', interp1), - ChannelAction('use', 'recv', interp2), - ] - - # never emptied - yield [ - ChannelAction('use', 'send', interp1), - ] - yield [ - ChannelAction('use', 'send', interp2), - ] - yield [ - ChannelAction('use', 'send', interp1), - ChannelAction('use', 'send', interp2), - ] - - # partially emptied - for interp1 in interps: - for interp2 in interps: - for interp3 in interps: - yield [ - ChannelAction('use', 'send', interp1), - ChannelAction('use', 'send', interp2), - ChannelAction('use', 'recv', interp3), - ] - - # fully emptied - for interp1 in interps: - for interp2 in interps: - for interp3 in interps: - for interp4 in interps: - yield [ - ChannelAction('use', 'send', interp1), - ChannelAction('use', 'send', interp2), - ChannelAction('use', 'recv', interp3), - ChannelAction('use', 'recv', interp4), - ] - - def _iter_close_action_sets(self, interp1, interp2): - ends = ('recv', 'send') - interps = (interp1, interp2) - for force in (True, False): - op = 'force-close' if force else 'close' - for interp in interps: - for end in ends: - yield [ - ChannelAction(op, end, interp), - ] - for recvop in ('close', 'force-close'): - for sendop in ('close', 'force-close'): - for recv in interps: - for send in interps: - yield [ - ChannelAction(recvop, 'recv', recv), - ChannelAction(sendop, 'send', send), - ] - - def _iter_post_close_action_sets(self): - for interp in ('same', 'extra', 'other'): - yield [ - ChannelAction('use', 'recv', interp), - ] - yield [ - ChannelAction('use', 'send', interp), - ] - - def run_actions(self, fix, actions): - for action in actions: - self.run_action(fix, action) - - def run_action(self, fix, action, *, hideclosed=True): - end = action.resolve_end(fix.end) - interp = action.resolve_interp(fix.interp, fix.other, fix.extra) - fix.prep_interpreter(interp) - if interp.name == 'main': - result = run_action( - fix.cid, - action.action, - end, - fix.state, - hideclosed=hideclosed, - ) - fix.record_action(action, result) - else: - _cid = interpreters.channel_create() - run_interp(interp.id, f""" - result = helpers.run_action( - {fix.cid}, - {repr(action.action)}, - {repr(end)}, - {repr(fix.state)}, - hideclosed={hideclosed}, - ) - interpreters.channel_send({_cid}, result.pending.to_bytes(1, 'little')) - interpreters.channel_send({_cid}, b'X' if result.closed else b'') - """) - result = ChannelState( - pending=int.from_bytes(interpreters.channel_recv(_cid), 'little'), - closed=bool(interpreters.channel_recv(_cid)), - ) - fix.record_action(action, result) - - def iter_fixtures(self): - # XXX threads? - interpreters = [ - ('main', 'interp', 'extra'), - ('interp', 'main', 'extra'), - ('interp1', 'interp2', 'extra'), - ('interp1', 'interp2', 'main'), - ] - for interp, other, extra in interpreters: - for creator in ('same', 'other', 'creator'): - for end in ('send', 'recv'): - yield ChannelCloseFixture(end, interp, other, extra, creator) - - def _close(self, fix, *, force): - op = 'force-close' if force else 'close' - close = ChannelAction(op, fix.end, 'same') - if not fix.expect_closed_error(): - self.run_action(fix, close, hideclosed=False) - else: - with self.assertRaises(interpreters.ChannelClosedError): - self.run_action(fix, close, hideclosed=False) - - def _assert_closed_in_interp(self, fix, interp=None): - if interp is None or interp.name == 'main': - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_recv(fix.cid) - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_send(fix.cid, b'spam') - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_close(fix.cid) - with self.assertRaises(interpreters.ChannelClosedError): - interpreters.channel_close(fix.cid, force=True) - else: - run_interp(interp.id, f""" - with helpers.expect_channel_closed(): - interpreters.channel_recv(cid) - """) - run_interp(interp.id, f""" - with helpers.expect_channel_closed(): - interpreters.channel_send(cid, b'spam') - """) - run_interp(interp.id, f""" - with helpers.expect_channel_closed(): - interpreters.channel_close(cid) - """) - run_interp(interp.id, f""" - with helpers.expect_channel_closed(): - interpreters.channel_close(cid, force=True) - """) - - def _assert_closed(self, fix): - self.assertTrue(fix.state.closed) - - for _ in range(fix.state.pending): - interpreters.channel_recv(fix.cid) - self._assert_closed_in_interp(fix) - - for interp in ('same', 'other'): - interp = fix.get_interpreter(interp) - if interp.name == 'main': - continue - self._assert_closed_in_interp(fix, interp) - - interp = fix.get_interpreter('fresh') - self._assert_closed_in_interp(fix, interp) - - def _iter_close_tests(self, verbose=False): - i = 0 - for actions in self.iter_action_sets(): - print() - for fix in self.iter_fixtures(): - i += 1 - if i > 1000: - return - if verbose: - if (i - 1) % 6 == 0: - print() - print(i, fix, '({} actions)'.format(len(actions))) - else: - if (i - 1) % 6 == 0: - print(' ', end='') - print('.', end=''); sys.stdout.flush() - yield i, fix, actions - if verbose: - print('---') - print() - - # This is useful for scanning through the possible tests. - def _skim_close_tests(self): - ChannelCloseFixture.QUICK = True - for i, fix, actions in self._iter_close_tests(): - pass - - def test_close(self): - for i, fix, actions in self._iter_close_tests(): - with self.subTest('{} {} {}'.format(i, fix, actions)): - fix.prep_interpreter(fix.interp) - self.run_actions(fix, actions) - - self._close(fix, force=False) - - self._assert_closed(fix) - # XXX Things slow down if we have too many interpreters. - fix.clean_up() - - def test_force_close(self): - for i, fix, actions in self._iter_close_tests(): - with self.subTest('{} {} {}'.format(i, fix, actions)): - fix.prep_interpreter(fix.interp) - self.run_actions(fix, actions) - - self._close(fix, force=True) - - self._assert_closed(fix) - # XXX Things slow down if we have too many interpreters. - fix.clean_up() - - if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_interpreters.py b/Lib/test/test_interpreters.py index b969ddf33d81bc..d1bebe47158322 100644 --- a/Lib/test/test_interpreters.py +++ b/Lib/test/test_interpreters.py @@ -8,6 +8,7 @@ from test import support from test.support import import_helper _interpreters = import_helper.import_module('_xxsubinterpreters') +_channels = import_helper.import_module('_xxinterpchannels') from test.support import interpreters @@ -533,7 +534,7 @@ class TestRecvChannelAttrs(TestBase): def test_id_type(self): rch, _ = interpreters.create_channel() - self.assertIsInstance(rch.id, _interpreters.ChannelID) + self.assertIsInstance(rch.id, _channels.ChannelID) def test_custom_id(self): rch = interpreters.RecvChannel(1) @@ -558,7 +559,7 @@ class TestSendChannelAttrs(TestBase): def test_id_type(self): _, sch = interpreters.create_channel() - self.assertIsInstance(sch.id, _interpreters.ChannelID) + self.assertIsInstance(sch.id, _channels.ChannelID) def test_custom_id(self): sch = interpreters.SendChannel(1) diff --git a/Modules/Setup b/Modules/Setup index e3a82975b5ff5e..428be0a1bf8fa1 100644 --- a/Modules/Setup +++ b/Modules/Setup @@ -280,6 +280,7 @@ PYTHONPATH=$(COREPYTHONPATH) # Testing #_xxsubinterpreters _xxsubinterpretersmodule.c +#_xxinterpchannels _xxinterpchannelsmodule.c #_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c #_testbuffer _testbuffer.c #_testinternalcapi _testinternalcapi.c diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index d64752e8ca9609..3fd591c70d493f 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -43,6 +43,7 @@ @MODULE__STRUCT_TRUE@_struct _struct.c @MODULE__TYPING_TRUE@_typing _typingmodule.c @MODULE__XXSUBINTERPRETERS_TRUE@_xxsubinterpreters _xxsubinterpretersmodule.c +@MODULE__XXINTERPCHANNELS_TRUE@_xxinterpchannels _xxinterpchannelsmodule.c @MODULE__ZONEINFO_TRUE@_zoneinfo _zoneinfo.c # needs libm diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 46c025bf53404a..f0d6e404f54a2f 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1639,6 +1639,58 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs) return PyLong_FromLong(r); } +static void +_xid_capsule_destructor(PyObject *capsule) +{ + _PyCrossInterpreterData *data = \ + (_PyCrossInterpreterData *)PyCapsule_GetPointer(capsule, NULL); + if (data != NULL) { + assert(_PyCrossInterpreterData_Release(data) == 0); + PyMem_Free(data); + } +} + +static PyObject * +get_crossinterp_data(PyObject *self, PyObject *args) +{ + PyObject *obj = NULL; + if (!PyArg_ParseTuple(args, "O:get_crossinterp_data", &obj)) { + return NULL; + } + + _PyCrossInterpreterData *data = PyMem_NEW(_PyCrossInterpreterData, 1); + if (data == NULL) { + PyErr_NoMemory(); + return NULL; + } + if (_PyObject_GetCrossInterpreterData(obj, data) != 0) { + PyMem_Free(data); + return NULL; + } + PyObject *capsule = PyCapsule_New(data, NULL, _xid_capsule_destructor); + if (capsule == NULL) { + assert(_PyCrossInterpreterData_Release(data) == 0); + PyMem_Free(data); + } + return capsule; +} + +static PyObject * +restore_crossinterp_data(PyObject *self, PyObject *args) +{ + PyObject *capsule = NULL; + if (!PyArg_ParseTuple(args, "O:restore_crossinterp_data", &capsule)) { + return NULL; + } + + _PyCrossInterpreterData *data = \ + (_PyCrossInterpreterData *)PyCapsule_GetPointer(capsule, NULL); + if (data == NULL) { + return NULL; + } + return _PyCrossInterpreterData_NewObject(data); +} + static void slot_tp_del(PyObject *self) { @@ -3306,6 +3358,8 @@ static PyMethodDef TestMethods[] = { {"run_in_subinterp_with_config", _PyCFunction_CAST(run_in_subinterp_with_config), METH_VARARGS | METH_KEYWORDS}, + {"get_crossinterp_data", get_crossinterp_data, METH_VARARGS}, + {"restore_crossinterp_data", restore_crossinterp_data, METH_VARARGS}, {"with_tp_del", with_tp_del, METH_VARARGS}, {"create_cfunction", create_cfunction, METH_NOARGS}, {"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_VARARGS, diff --git a/Modules/_xxinterpchannelsmodule.c b/Modules/_xxinterpchannelsmodule.c new file mode 100644 index 00000000000000..8601a189e87526 --- /dev/null +++ b/Modules/_xxinterpchannelsmodule.c @@ -0,0 +1,2325 @@ + +/* interpreters module */ +/* low-level access to interpreter primitives */ +#ifndef Py_BUILD_CORE_BUILTIN +# define Py_BUILD_CORE_MODULE 1 +#endif + +#include "Python.h" +#include "pycore_pystate.h" // _PyThreadState_GET() +#include "pycore_interpreteridobject.h" + + +#define MODULE_NAME "_xxinterpchannels" + + +static PyInterpreterState * +_get_current_interp(void) +{ + // PyInterpreterState_Get() aborts if lookup fails, so don't need + // to check the result for NULL. + return PyInterpreterState_Get(); +} + +static PyObject * +_get_current_module(void) +{ + PyObject *name = PyUnicode_FromString(MODULE_NAME); + if (name == NULL) { + return NULL; + } + PyObject *mod = PyImport_GetModule(name); + Py_DECREF(name); + if (mod == NULL) { + return NULL; + } + assert(mod != Py_None); + return mod; +} + +static PyObject * +get_module_from_owned_type(PyTypeObject *cls) +{ + assert(cls != NULL); + return _get_current_module(); + // XXX Use the more efficient API now that we use heap types: + //return PyType_GetModule(cls); +} + +static struct PyModuleDef moduledef; + +static PyObject * +get_module_from_type(PyTypeObject *cls) +{ + assert(cls != NULL); + return _get_current_module(); + // XXX Use the more efficient API now that we use heap types: + //return PyType_GetModuleByDef(cls, &moduledef); +} + +static PyObject * +add_new_exception(PyObject *mod, const char *name, PyObject *base) +{ + assert(!PyObject_HasAttrString(mod, name)); + PyObject *exctype = PyErr_NewException(name, base, NULL); + if (exctype == NULL) { + return NULL; + } + int res = PyModule_AddType(mod, (PyTypeObject *)exctype); + if (res < 0) { + Py_DECREF(exctype); + return NULL; + } + return exctype; +} + +#define ADD_NEW_EXCEPTION(MOD, NAME, BASE) \ + add_new_exception(MOD, MODULE_NAME "." Py_STRINGIFY(NAME), BASE) + +static PyTypeObject * +add_new_type(PyObject *mod, PyType_Spec *spec, crossinterpdatafunc shared) +{ + PyTypeObject *cls = (PyTypeObject *)PyType_FromMetaclass( + NULL, mod, spec, NULL); + if (cls == NULL) { + return NULL; + } + if (PyModule_AddType(mod, cls) < 0) { + Py_DECREF(cls); + return NULL; + } + if (shared != NULL) { + if (_PyCrossInterpreterData_RegisterClass(cls, shared)) { + Py_DECREF(cls); + return NULL; + } + } + return cls; +} + +static int +_release_xid_data(_PyCrossInterpreterData *data, int ignoreexc) +{ + PyObject *exctype, *excval, *exctb; + if (ignoreexc) { + PyErr_Fetch(&exctype, &excval, &exctb); + } + int res = _PyCrossInterpreterData_Release(data); + if (res < 0) { + // XXX Fix this! + /* The owning interpreter is already destroyed. + * Ideally, this shouldn't ever happen. When an interpreter is + * about to be destroyed, we should clear out all of its objects + * from every channel associated with that interpreter. + * For now we hack around that to resolve refleaks, by decref'ing + * the released object here, even if its the wrong interpreter. + * The owning interpreter has already been destroyed + * so we should be okay, especially since the currently + * shareable types are all very basic, with no GC. + * That said, it becomes much messier once interpreters + * no longer share a GIL, so this needs to be fixed before then. */ + _PyCrossInterpreterData_Clear(NULL, data); + if (ignoreexc) { + // XXX Emit a warning? + PyErr_Clear(); + } + } + if (ignoreexc) { + PyErr_Restore(exctype, excval, exctb); + } + return res; +} + + +/* module state *************************************************************/ + +typedef struct { + /* heap types */ + PyTypeObject *ChannelIDType; + + /* exceptions */ + PyObject *ChannelError; + PyObject *ChannelNotFoundError; + PyObject *ChannelClosedError; + PyObject *ChannelEmptyError; + PyObject *ChannelNotEmptyError; +} module_state; + +static inline module_state * +get_module_state(PyObject *mod) +{ + assert(mod != NULL); + module_state *state = PyModule_GetState(mod); + assert(state != NULL); + return state; +} + +static int +traverse_module_state(module_state *state, visitproc visit, void *arg) +{ + /* heap types */ + Py_VISIT(state->ChannelIDType); + + /* exceptions */ + Py_VISIT(state->ChannelError); + Py_VISIT(state->ChannelNotFoundError); + Py_VISIT(state->ChannelClosedError); + Py_VISIT(state->ChannelEmptyError); + Py_VISIT(state->ChannelNotEmptyError); + + return 0; +} + +static int +clear_module_state(module_state *state) +{ + /* heap types */ + (void)_PyCrossInterpreterData_UnregisterClass(state->ChannelIDType); + Py_CLEAR(state->ChannelIDType); + + /* exceptions */ + Py_CLEAR(state->ChannelError); + Py_CLEAR(state->ChannelNotFoundError); + Py_CLEAR(state->ChannelClosedError); + Py_CLEAR(state->ChannelEmptyError); + Py_CLEAR(state->ChannelNotEmptyError); + + return 0; +} + + +/* channel-specific code ****************************************************/ + +#define CHANNEL_SEND 1 +#define CHANNEL_BOTH 0 +#define CHANNEL_RECV -1 + +/* channel errors */ + +#define ERR_CHANNEL_NOT_FOUND -2 +#define ERR_CHANNEL_CLOSED -3 +#define ERR_CHANNEL_INTERP_CLOSED -4 +#define ERR_CHANNEL_EMPTY -5 +#define ERR_CHANNEL_NOT_EMPTY -6 +#define ERR_CHANNEL_MUTEX_INIT -7 +#define ERR_CHANNELS_MUTEX_INIT -8 +#define ERR_NO_NEXT_CHANNEL_ID -9 + +static int +exceptions_init(PyObject *mod) +{ + module_state *state = get_module_state(mod); + if (state == NULL) { + return -1; + } + +#define ADD(NAME, BASE) \ + do { \ + assert(state->NAME == NULL); \ + state->NAME = ADD_NEW_EXCEPTION(mod, NAME, BASE); \ + if (state->NAME == NULL) { \ + return -1; \ + } \ + } while (0) + + // A channel-related operation failed. + ADD(ChannelError, PyExc_RuntimeError); + // An operation tried to use a channel that doesn't exist. + ADD(ChannelNotFoundError, state->ChannelError); + // An operation tried to use a closed channel. + ADD(ChannelClosedError, state->ChannelError); + // An operation tried to pop from an empty channel. + ADD(ChannelEmptyError, state->ChannelError); + // An operation tried to close a non-empty channel. + ADD(ChannelNotEmptyError, state->ChannelError); +#undef ADD + + return 0; +} + +static int +handle_channel_error(int err, PyObject *mod, int64_t cid) +{ + if (err == 0) { + assert(!PyErr_Occurred()); + return 0; + } + assert(err < 0); + module_state *state = get_module_state(mod); + assert(state != NULL); + if (err == ERR_CHANNEL_NOT_FOUND) { + PyErr_Format(state->ChannelNotFoundError, + "channel %" PRId64 " not found", cid); + } + else if (err == ERR_CHANNEL_CLOSED) { + PyErr_Format(state->ChannelClosedError, + "channel %" PRId64 " is closed", cid); + } + else if (err == ERR_CHANNEL_INTERP_CLOSED) { + PyErr_Format(state->ChannelClosedError, + "channel %" PRId64 " is already closed", cid); + } + else if (err == ERR_CHANNEL_EMPTY) { + PyErr_Format(state->ChannelEmptyError, + "channel %" PRId64 " is empty", cid); + } + else if (err == ERR_CHANNEL_NOT_EMPTY) { + PyErr_Format(state->ChannelNotEmptyError, + "channel %" PRId64 " may not be closed " + "if not empty (try force=True)", + cid); + } + else if (err == ERR_CHANNEL_MUTEX_INIT) { + PyErr_SetString(state->ChannelError, + "can't initialize mutex for new channel"); + } + else if (err == ERR_CHANNELS_MUTEX_INIT) { + PyErr_SetString(state->ChannelError, + "can't initialize mutex for channel management"); + } + else if (err == ERR_NO_NEXT_CHANNEL_ID) { + PyErr_SetString(state->ChannelError, + "failed to get a channel ID"); + } + else { + assert(PyErr_Occurred()); + } + return 1; +} + +/* the channel queue */ + +struct _channelitem; + +typedef struct _channelitem { + _PyCrossInterpreterData *data; + struct _channelitem *next; +} _channelitem; + +static _channelitem * +_channelitem_new(void) +{ + _channelitem *item = PyMem_NEW(_channelitem, 1); + if (item == NULL) { + PyErr_NoMemory(); + return NULL; + } + item->data = NULL; + item->next = NULL; + return item; +} + +static void +_channelitem_clear(_channelitem *item) +{ + if (item->data != NULL) { + (void)_release_xid_data(item->data, 1); + PyMem_Free(item->data); + item->data = NULL; + } + item->next = NULL; +} + +static void +_channelitem_free(_channelitem *item) +{ + _channelitem_clear(item); + PyMem_Free(item); +} + +static void +_channelitem_free_all(_channelitem *item) +{ + while (item != NULL) { + _channelitem *last = item; + item = item->next; + _channelitem_free(last); + } +} + +static _PyCrossInterpreterData * +_channelitem_popped(_channelitem *item) +{ + _PyCrossInterpreterData *data = item->data; + item->data = NULL; + _channelitem_free(item); + return data; +} + +typedef struct _channelqueue { + int64_t count; + _channelitem *first; + _channelitem *last; +} _channelqueue; + +static _channelqueue * +_channelqueue_new(void) +{ + _channelqueue *queue = PyMem_NEW(_channelqueue, 1); + if (queue == NULL) { + PyErr_NoMemory(); + return NULL; + } + queue->count = 0; + queue->first = NULL; + queue->last = NULL; + return queue; +} + +static void +_channelqueue_clear(_channelqueue *queue) +{ + _channelitem_free_all(queue->first); + queue->count = 0; + queue->first = NULL; + queue->last = NULL; +} + +static void +_channelqueue_free(_channelqueue *queue) +{ + _channelqueue_clear(queue); + PyMem_Free(queue); +} + +static int +_channelqueue_put(_channelqueue *queue, _PyCrossInterpreterData *data) +{ + _channelitem *item = _channelitem_new(); + if (item == NULL) { + return -1; + } + item->data = data; + + queue->count += 1; + if (queue->first == NULL) { + queue->first = item; + } + else { + queue->last->next = item; + } + queue->last = item; + return 0; +} + +static _PyCrossInterpreterData * +_channelqueue_get(_channelqueue *queue) +{ + _channelitem *item = queue->first; + if (item == NULL) { + return NULL; + } + queue->first = item->next; + if (queue->last == item) { + queue->last = NULL; + } + queue->count -= 1; + + return _channelitem_popped(item); +} + +/* channel-interpreter associations */ + +struct _channelend; + +typedef struct _channelend { + struct _channelend *next; + int64_t interp; + int open; +} _channelend; + +static _channelend * +_channelend_new(int64_t interp) +{ + _channelend *end = PyMem_NEW(_channelend, 1); + if (end == NULL) { + PyErr_NoMemory(); + return NULL; + } + end->next = NULL; + end->interp = interp; + end->open = 1; + return end; +} + +static void +_channelend_free(_channelend *end) +{ + PyMem_Free(end); +} + +static void +_channelend_free_all(_channelend *end) +{ + while (end != NULL) { + _channelend *last = end; + end = end->next; + _channelend_free(last); + } +} + +static _channelend * +_channelend_find(_channelend *first, int64_t interp, _channelend **pprev) +{ + _channelend *prev = NULL; + _channelend *end = first; + while (end != NULL) { + if (end->interp == interp) { + break; + } + prev = end; + end = end->next; + } + if (pprev != NULL) { + *pprev = prev; + } + return end; +} + +typedef struct _channelassociations { + // Note that the list entries are never removed for interpreter + // for which the channel is closed. This should not be a problem in + // practice. Also, a channel isn't automatically closed when an + // interpreter is destroyed. + int64_t numsendopen; + int64_t numrecvopen; + _channelend *send; + _channelend *recv; +} _channelends; + +static _channelends * +_channelends_new(void) +{ + _channelends *ends = PyMem_NEW(_channelends, 1); + if (ends== NULL) { + return NULL; + } + ends->numsendopen = 0; + ends->numrecvopen = 0; + ends->send = NULL; + ends->recv = NULL; + return ends; +} + +static void +_channelends_clear(_channelends *ends) +{ + _channelend_free_all(ends->send); + ends->send = NULL; + ends->numsendopen = 0; + + _channelend_free_all(ends->recv); + ends->recv = NULL; + ends->numrecvopen = 0; +} + +static void +_channelends_free(_channelends *ends) +{ + _channelends_clear(ends); + PyMem_Free(ends); +} + +static _channelend * +_channelends_add(_channelends *ends, _channelend *prev, int64_t interp, + int send) +{ + _channelend *end = _channelend_new(interp); + if (end == NULL) { + return NULL; + } + + if (prev == NULL) { + if (send) { + ends->send = end; + } + else { + ends->recv = end; + } + } + else { + prev->next = end; + } + if (send) { + ends->numsendopen += 1; + } + else { + ends->numrecvopen += 1; + } + return end; +} + +static int +_channelends_associate(_channelends *ends, int64_t interp, int send) +{ + _channelend *prev; + _channelend *end = _channelend_find(send ? ends->send : ends->recv, + interp, &prev); + if (end != NULL) { + if (!end->open) { + return ERR_CHANNEL_CLOSED; + } + // already associated + return 0; + } + if (_channelends_add(ends, prev, interp, send) == NULL) { + return -1; + } + return 0; +} + +static int +_channelends_is_open(_channelends *ends) +{ + if (ends->numsendopen != 0 || ends->numrecvopen != 0) { + return 1; + } + if (ends->send == NULL && ends->recv == NULL) { + return 1; + } + return 0; +} + +static void +_channelends_close_end(_channelends *ends, _channelend *end, int send) +{ + end->open = 0; + if (send) { + ends->numsendopen -= 1; + } + else { + ends->numrecvopen -= 1; + } +} + +static int +_channelends_close_interpreter(_channelends *ends, int64_t interp, int which) +{ + _channelend *prev; + _channelend *end; + if (which >= 0) { // send/both + end = _channelend_find(ends->send, interp, &prev); + if (end == NULL) { + // never associated so add it + end = _channelends_add(ends, prev, interp, 1); + if (end == NULL) { + return -1; + } + } + _channelends_close_end(ends, end, 1); + } + if (which <= 0) { // recv/both + end = _channelend_find(ends->recv, interp, &prev); + if (end == NULL) { + // never associated so add it + end = _channelends_add(ends, prev, interp, 0); + if (end == NULL) { + return -1; + } + } + _channelends_close_end(ends, end, 0); + } + return 0; +} + +static void +_channelends_close_all(_channelends *ends, int which, int force) +{ + // XXX Handle the ends. + // XXX Handle force is True. + + // Ensure all the "send"-associated interpreters are closed. + _channelend *end; + for (end = ends->send; end != NULL; end = end->next) { + _channelends_close_end(ends, end, 1); + } + + // Ensure all the "recv"-associated interpreters are closed. + for (end = ends->recv; end != NULL; end = end->next) { + _channelends_close_end(ends, end, 0); + } +} + +/* channels */ + +struct _channel; +struct _channel_closing; +static void _channel_clear_closing(struct _channel *); +static void _channel_finish_closing(struct _channel *); + +typedef struct _channel { + PyThread_type_lock mutex; + _channelqueue *queue; + _channelends *ends; + int open; + struct _channel_closing *closing; +} _PyChannelState; + +static _PyChannelState * +_channel_new(PyThread_type_lock mutex) +{ + _PyChannelState *chan = PyMem_NEW(_PyChannelState, 1); + if (chan == NULL) { + return NULL; + } + chan->mutex = mutex; + chan->queue = _channelqueue_new(); + if (chan->queue == NULL) { + PyMem_Free(chan); + return NULL; + } + chan->ends = _channelends_new(); + if (chan->ends == NULL) { + _channelqueue_free(chan->queue); + PyMem_Free(chan); + return NULL; + } + chan->open = 1; + chan->closing = NULL; + return chan; +} + +static void +_channel_free(_PyChannelState *chan) +{ + _channel_clear_closing(chan); + PyThread_acquire_lock(chan->mutex, WAIT_LOCK); + _channelqueue_free(chan->queue); + _channelends_free(chan->ends); + PyThread_release_lock(chan->mutex); + + PyThread_free_lock(chan->mutex); + PyMem_Free(chan); +} + +static int +_channel_add(_PyChannelState *chan, int64_t interp, + _PyCrossInterpreterData *data) +{ + int res = -1; + PyThread_acquire_lock(chan->mutex, WAIT_LOCK); + + if (!chan->open) { + res = ERR_CHANNEL_CLOSED; + goto done; + } + if (_channelends_associate(chan->ends, interp, 1) != 0) { + res = ERR_CHANNEL_INTERP_CLOSED; + goto done; + } + + if (_channelqueue_put(chan->queue, data) != 0) { + goto done; + } + + res = 0; +done: + PyThread_release_lock(chan->mutex); + return res; +} + +static int +_channel_next(_PyChannelState *chan, int64_t interp, + _PyCrossInterpreterData **res) +{ + int err = 0; + PyThread_acquire_lock(chan->mutex, WAIT_LOCK); + + if (!chan->open) { + err = ERR_CHANNEL_CLOSED; + goto done; + } + if (_channelends_associate(chan->ends, interp, 0) != 0) { + err = ERR_CHANNEL_INTERP_CLOSED; + goto done; + } + + _PyCrossInterpreterData *data = _channelqueue_get(chan->queue); + if (data == NULL && !PyErr_Occurred() && chan->closing != NULL) { + chan->open = 0; + } + *res = data; + +done: + PyThread_release_lock(chan->mutex); + if (chan->queue->count == 0) { + _channel_finish_closing(chan); + } + return err; +} + +static int +_channel_close_interpreter(_PyChannelState *chan, int64_t interp, int end) +{ + PyThread_acquire_lock(chan->mutex, WAIT_LOCK); + + int res = -1; + if (!chan->open) { + res = ERR_CHANNEL_CLOSED; + goto done; + } + + if (_channelends_close_interpreter(chan->ends, interp, end) != 0) { + goto done; + } + chan->open = _channelends_is_open(chan->ends); + + res = 0; +done: + PyThread_release_lock(chan->mutex); + return res; +} + +static int +_channel_close_all(_PyChannelState *chan, int end, int force) +{ + int res = -1; + PyThread_acquire_lock(chan->mutex, WAIT_LOCK); + + if (!chan->open) { + res = ERR_CHANNEL_CLOSED; + goto done; + } + + if (!force && chan->queue->count > 0) { + res = ERR_CHANNEL_NOT_EMPTY; + goto done; + } + + chan->open = 0; + + // We *could* also just leave these in place, since we've marked + // the channel as closed already. + _channelends_close_all(chan->ends, end, force); + + res = 0; +done: + PyThread_release_lock(chan->mutex); + return res; +} + +/* the set of channels */ + +struct _channelref; + +typedef struct _channelref { + int64_t id; + _PyChannelState *chan; + struct _channelref *next; + Py_ssize_t objcount; +} _channelref; + +static _channelref * +_channelref_new(int64_t id, _PyChannelState *chan) +{ + _channelref *ref = PyMem_NEW(_channelref, 1); + if (ref == NULL) { + return NULL; + } + ref->id = id; + ref->chan = chan; + ref->next = NULL; + ref->objcount = 0; + return ref; +} + +//static void +//_channelref_clear(_channelref *ref) +//{ +// ref->id = -1; +// ref->chan = NULL; +// ref->next = NULL; +// ref->objcount = 0; +//} + +static void +_channelref_free(_channelref *ref) +{ + if (ref->chan != NULL) { + _channel_clear_closing(ref->chan); + } + //_channelref_clear(ref); + PyMem_Free(ref); +} + +static _channelref * +_channelref_find(_channelref *first, int64_t id, _channelref **pprev) +{ + _channelref *prev = NULL; + _channelref *ref = first; + while (ref != NULL) { + if (ref->id == id) { + break; + } + prev = ref; + ref = ref->next; + } + if (pprev != NULL) { + *pprev = prev; + } + return ref; +} + +typedef struct _channels { + PyThread_type_lock mutex; + _channelref *head; + int64_t numopen; + int64_t next_id; +} _channels; + +static void +_channels_init(_channels *channels, PyThread_type_lock mutex) +{ + channels->mutex = mutex; + channels->head = NULL; + channels->numopen = 0; + channels->next_id = 0; +} + +static void +_channels_fini(_channels *channels) +{ + assert(channels->numopen == 0); + assert(channels->head == NULL); + if (channels->mutex != NULL) { + PyThread_free_lock(channels->mutex); + channels->mutex = NULL; + } +} + +static int64_t +_channels_next_id(_channels *channels) // needs lock +{ + int64_t id = channels->next_id; + if (id < 0) { + /* overflow */ + return -1; + } + channels->next_id += 1; + return id; +} + +static int +_channels_lookup(_channels *channels, int64_t id, PyThread_type_lock *pmutex, + _PyChannelState **res) +{ + int err = -1; + _PyChannelState *chan = NULL; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + if (pmutex != NULL) { + *pmutex = NULL; + } + + _channelref *ref = _channelref_find(channels->head, id, NULL); + if (ref == NULL) { + err = ERR_CHANNEL_NOT_FOUND; + goto done; + } + if (ref->chan == NULL || !ref->chan->open) { + err = ERR_CHANNEL_CLOSED; + goto done; + } + + if (pmutex != NULL) { + // The mutex will be closed by the caller. + *pmutex = channels->mutex; + } + + chan = ref->chan; + err = 0; + +done: + if (pmutex == NULL || *pmutex == NULL) { + PyThread_release_lock(channels->mutex); + } + *res = chan; + return err; +} + +static int64_t +_channels_add(_channels *channels, _PyChannelState *chan) +{ + int64_t cid = -1; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + + // Create a new ref. + int64_t id = _channels_next_id(channels); + if (id < 0) { + cid = ERR_NO_NEXT_CHANNEL_ID; + goto done; + } + _channelref *ref = _channelref_new(id, chan); + if (ref == NULL) { + goto done; + } + + // Add it to the list. + // We assume that the channel is a new one (not already in the list). + ref->next = channels->head; + channels->head = ref; + channels->numopen += 1; + + cid = id; +done: + PyThread_release_lock(channels->mutex); + return cid; +} + +/* forward */ +static int _channel_set_closing(struct _channelref *, PyThread_type_lock); + +static int +_channels_close(_channels *channels, int64_t cid, _PyChannelState **pchan, + int end, int force) +{ + int res = -1; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + if (pchan != NULL) { + *pchan = NULL; + } + + _channelref *ref = _channelref_find(channels->head, cid, NULL); + if (ref == NULL) { + res = ERR_CHANNEL_NOT_FOUND; + goto done; + } + + if (ref->chan == NULL) { + res = ERR_CHANNEL_CLOSED; + goto done; + } + else if (!force && end == CHANNEL_SEND && ref->chan->closing != NULL) { + res = ERR_CHANNEL_CLOSED; + goto done; + } + else { + int err = _channel_close_all(ref->chan, end, force); + if (err != 0) { + if (end == CHANNEL_SEND && err == ERR_CHANNEL_NOT_EMPTY) { + if (ref->chan->closing != NULL) { + res = ERR_CHANNEL_CLOSED; + goto done; + } + // Mark the channel as closing and return. The channel + // will be cleaned up in _channel_next(). + PyErr_Clear(); + int err = _channel_set_closing(ref, channels->mutex); + if (err != 0) { + res = err; + goto done; + } + if (pchan != NULL) { + *pchan = ref->chan; + } + res = 0; + } + else { + res = err; + } + goto done; + } + if (pchan != NULL) { + *pchan = ref->chan; + } + else { + _channel_free(ref->chan); + } + ref->chan = NULL; + } + + res = 0; +done: + PyThread_release_lock(channels->mutex); + return res; +} + +static void +_channels_remove_ref(_channels *channels, _channelref *ref, _channelref *prev, + _PyChannelState **pchan) +{ + if (ref == channels->head) { + channels->head = ref->next; + } + else { + prev->next = ref->next; + } + channels->numopen -= 1; + + if (pchan != NULL) { + *pchan = ref->chan; + } + _channelref_free(ref); +} + +static int +_channels_remove(_channels *channels, int64_t id, _PyChannelState **pchan) +{ + int res = -1; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + + if (pchan != NULL) { + *pchan = NULL; + } + + _channelref *prev = NULL; + _channelref *ref = _channelref_find(channels->head, id, &prev); + if (ref == NULL) { + res = ERR_CHANNEL_NOT_FOUND; + goto done; + } + + _channels_remove_ref(channels, ref, prev, pchan); + + res = 0; +done: + PyThread_release_lock(channels->mutex); + return res; +} + +static int +_channels_add_id_object(_channels *channels, int64_t id) +{ + int res = -1; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + + _channelref *ref = _channelref_find(channels->head, id, NULL); + if (ref == NULL) { + res = ERR_CHANNEL_NOT_FOUND; + goto done; + } + ref->objcount += 1; + + res = 0; +done: + PyThread_release_lock(channels->mutex); + return res; +} + +static void +_channels_drop_id_object(_channels *channels, int64_t id) +{ + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + + _channelref *prev = NULL; + _channelref *ref = _channelref_find(channels->head, id, &prev); + if (ref == NULL) { + // Already destroyed. + goto done; + } + ref->objcount -= 1; + + // Destroy if no longer used. + if (ref->objcount == 0) { + _PyChannelState *chan = NULL; + _channels_remove_ref(channels, ref, prev, &chan); + if (chan != NULL) { + _channel_free(chan); + } + } + +done: + PyThread_release_lock(channels->mutex); +} + +static int64_t * +_channels_list_all(_channels *channels, int64_t *count) +{ + int64_t *cids = NULL; + PyThread_acquire_lock(channels->mutex, WAIT_LOCK); + int64_t *ids = PyMem_NEW(int64_t, (Py_ssize_t)(channels->numopen)); + if (ids == NULL) { + goto done; + } + _channelref *ref = channels->head; + for (int64_t i=0; ref != NULL; ref = ref->next, i++) { + ids[i] = ref->id; + } + *count = channels->numopen; + + cids = ids; +done: + PyThread_release_lock(channels->mutex); + return cids; +} + +/* support for closing non-empty channels */ + +struct _channel_closing { + struct _channelref *ref; +}; + +static int +_channel_set_closing(struct _channelref *ref, PyThread_type_lock mutex) { + struct _channel *chan = ref->chan; + if (chan == NULL) { + // already closed + return 0; + } + int res = -1; + PyThread_acquire_lock(chan->mutex, WAIT_LOCK); + if (chan->closing != NULL) { + res = ERR_CHANNEL_CLOSED; + goto done; + } + chan->closing = PyMem_NEW(struct _channel_closing, 1); + if (chan->closing == NULL) { + goto done; + } + chan->closing->ref = ref; + + res = 0; +done: + PyThread_release_lock(chan->mutex); + return res; +} + +static void +_channel_clear_closing(struct _channel *chan) { + PyThread_acquire_lock(chan->mutex, WAIT_LOCK); + if (chan->closing != NULL) { + PyMem_Free(chan->closing); + chan->closing = NULL; + } + PyThread_release_lock(chan->mutex); +} + +static void +_channel_finish_closing(struct _channel *chan) { + struct _channel_closing *closing = chan->closing; + if (closing == NULL) { + return; + } + _channelref *ref = closing->ref; + _channel_clear_closing(chan); + // Do the things that would have been done in _channels_close(). + ref->chan = NULL; + _channel_free(chan); +} + +/* "high"-level channel-related functions */ + +static int64_t +_channel_create(_channels *channels) +{ + PyThread_type_lock mutex = PyThread_allocate_lock(); + if (mutex == NULL) { + return ERR_CHANNEL_MUTEX_INIT; + } + _PyChannelState *chan = _channel_new(mutex); + if (chan == NULL) { + PyThread_free_lock(mutex); + return -1; + } + int64_t id = _channels_add(channels, chan); + if (id < 0) { + _channel_free(chan); + } + return id; +} + +static int +_channel_destroy(_channels *channels, int64_t id) +{ + _PyChannelState *chan = NULL; + int err = _channels_remove(channels, id, &chan); + if (err != 0) { + return err; + } + if (chan != NULL) { + _channel_free(chan); + } + return 0; +} + +static int +_channel_send(_channels *channels, int64_t id, PyObject *obj) +{ + PyInterpreterState *interp = _get_current_interp(); + if (interp == NULL) { + return -1; + } + + // Look up the channel. + PyThread_type_lock mutex = NULL; + _PyChannelState *chan = NULL; + int err = _channels_lookup(channels, id, &mutex, &chan); + if (err != 0) { + return err; + } + assert(chan != NULL); + // Past this point we are responsible for releasing the mutex. + + if (chan->closing != NULL) { + PyThread_release_lock(mutex); + return ERR_CHANNEL_CLOSED; + } + + // Convert the object to cross-interpreter data. + _PyCrossInterpreterData *data = PyMem_NEW(_PyCrossInterpreterData, 1); + if (data == NULL) { + PyThread_release_lock(mutex); + return -1; + } + if (_PyObject_GetCrossInterpreterData(obj, data) != 0) { + PyThread_release_lock(mutex); + PyMem_Free(data); + return -1; + } + + // Add the data to the channel. + int res = _channel_add(chan, PyInterpreterState_GetID(interp), data); + PyThread_release_lock(mutex); + if (res != 0) { + // We may chain an exception here: + (void)_release_xid_data(data, 0); + PyMem_Free(data); + return res; + } + + return 0; +} + +static int +_channel_recv(_channels *channels, int64_t id, PyObject **res) +{ + int err; + *res = NULL; + + PyInterpreterState *interp = _get_current_interp(); + if (interp == NULL) { + // XXX Is this always an error? + if (PyErr_Occurred()) { + return -1; + } + return 0; + } + + // Look up the channel. + PyThread_type_lock mutex = NULL; + _PyChannelState *chan = NULL; + err = _channels_lookup(channels, id, &mutex, &chan); + if (err != 0) { + return err; + } + assert(chan != NULL); + // Past this point we are responsible for releasing the mutex. + + // Pop off the next item from the channel. + _PyCrossInterpreterData *data = NULL; + err = _channel_next(chan, PyInterpreterState_GetID(interp), &data); + PyThread_release_lock(mutex); + if (err != 0) { + return err; + } + else if (data == NULL) { + assert(!PyErr_Occurred()); + return 0; + } + + // Convert the data back to an object. + PyObject *obj = _PyCrossInterpreterData_NewObject(data); + if (obj == NULL) { + assert(PyErr_Occurred()); + (void)_release_xid_data(data, 1); + PyMem_Free(data); + return -1; + } + int release_res = _release_xid_data(data, 0); + PyMem_Free(data); + if (release_res < 0) { + // The source interpreter has been destroyed already. + assert(PyErr_Occurred()); + Py_DECREF(obj); + return -1; + } + + *res = obj; + return 0; +} + +static int +_channel_drop(_channels *channels, int64_t id, int send, int recv) +{ + PyInterpreterState *interp = _get_current_interp(); + if (interp == NULL) { + return -1; + } + + // Look up the channel. + PyThread_type_lock mutex = NULL; + _PyChannelState *chan = NULL; + int err = _channels_lookup(channels, id, &mutex, &chan); + if (err != 0) { + return err; + } + // Past this point we are responsible for releasing the mutex. + + // Close one or both of the two ends. + int res = _channel_close_interpreter(chan, PyInterpreterState_GetID(interp), send-recv); + PyThread_release_lock(mutex); + return res; +} + +static int +_channel_close(_channels *channels, int64_t id, int end, int force) +{ + return _channels_close(channels, id, NULL, end, force); +} + +static int +_channel_is_associated(_channels *channels, int64_t cid, int64_t interp, + int send) +{ + _PyChannelState *chan = NULL; + int err = _channels_lookup(channels, cid, NULL, &chan); + if (err != 0) { + return err; + } + else if (send && chan->closing != NULL) { + return ERR_CHANNEL_CLOSED; + } + + _channelend *end = _channelend_find(send ? chan->ends->send : chan->ends->recv, + interp, NULL); + + return (end != NULL && end->open); +} + +/* ChannelID class */ + +typedef struct channelid { + PyObject_HEAD + int64_t id; + int end; + int resolve; + _channels *channels; +} channelid; + +struct channel_id_converter_data { + PyObject *module; + int64_t cid; +}; + +static int +channel_id_converter(PyObject *arg, void *ptr) +{ + int64_t cid; + struct channel_id_converter_data *data = ptr; + module_state *state = get_module_state(data->module); + assert(state != NULL); + if (PyObject_TypeCheck(arg, state->ChannelIDType)) { + cid = ((channelid *)arg)->id; + } + else if (PyIndex_Check(arg)) { + cid = PyLong_AsLongLong(arg); + if (cid == -1 && PyErr_Occurred()) { + return 0; + } + if (cid < 0) { + PyErr_Format(PyExc_ValueError, + "channel ID must be a non-negative int, got %R", arg); + return 0; + } + } + else { + PyErr_Format(PyExc_TypeError, + "channel ID must be an int, got %.100s", + Py_TYPE(arg)->tp_name); + return 0; + } + data->cid = cid; + return 1; +} + +static int +newchannelid(PyTypeObject *cls, int64_t cid, int end, _channels *channels, + int force, int resolve, channelid **res) +{ + *res = NULL; + + channelid *self = PyObject_New(channelid, cls); + if (self == NULL) { + return -1; + } + self->id = cid; + self->end = end; + self->resolve = resolve; + self->channels = channels; + + int err = _channels_add_id_object(channels, cid); + if (err != 0) { + if (force && err == ERR_CHANNEL_NOT_FOUND) { + assert(!PyErr_Occurred()); + } + else { + Py_DECREF((PyObject *)self); + return err; + } + } + + *res = self; + return 0; +} + +static _channels * _global_channels(void); + +static PyObject * +_channelid_new(PyObject *mod, PyTypeObject *cls, + PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"id", "send", "recv", "force", "_resolve", NULL}; + int64_t cid; + struct channel_id_converter_data cid_data = { + .module = mod, + }; + int send = -1; + int recv = -1; + int force = 0; + int resolve = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "O&|$pppp:ChannelID.__new__", kwlist, + channel_id_converter, &cid_data, + &send, &recv, &force, &resolve)) { + return NULL; + } + cid = cid_data.cid; + + // Handle "send" and "recv". + if (send == 0 && recv == 0) { + PyErr_SetString(PyExc_ValueError, + "'send' and 'recv' cannot both be False"); + return NULL; + } + + int end = 0; + if (send == 1) { + if (recv == 0 || recv == -1) { + end = CHANNEL_SEND; + } + } + else if (recv == 1) { + end = CHANNEL_RECV; + } + + PyObject *id = NULL; + int err = newchannelid(cls, cid, end, _global_channels(), + force, resolve, + (channelid **)&id); + if (handle_channel_error(err, mod, cid)) { + assert(id == NULL); + return NULL; + } + assert(id != NULL); + return id; +} + +static void +channelid_dealloc(PyObject *self) +{ + int64_t cid = ((channelid *)self)->id; + _channels *channels = ((channelid *)self)->channels; + + PyTypeObject *tp = Py_TYPE(self); + tp->tp_free(self); + /* "Instances of heap-allocated types hold a reference to their type." + * See: https://docs.python.org/3.11/howto/isolating-extensions.html#garbage-collection-protocol + * See: https://docs.python.org/3.11/c-api/typeobj.html#c.PyTypeObject.tp_traverse + */ + // XXX Why don't we implement Py_TPFLAGS_HAVE_GC, e.g. Py_tp_traverse, + // like we do for _abc._abc_data? + Py_DECREF(tp); + + _channels_drop_id_object(channels, cid); +} + +static PyObject * +channelid_repr(PyObject *self) +{ + PyTypeObject *type = Py_TYPE(self); + const char *name = _PyType_Name(type); + + channelid *cid = (channelid *)self; + const char *fmt; + if (cid->end == CHANNEL_SEND) { + fmt = "%s(%" PRId64 ", send=True)"; + } + else if (cid->end == CHANNEL_RECV) { + fmt = "%s(%" PRId64 ", recv=True)"; + } + else { + fmt = "%s(%" PRId64 ")"; + } + return PyUnicode_FromFormat(fmt, name, cid->id); +} + +static PyObject * +channelid_str(PyObject *self) +{ + channelid *cid = (channelid *)self; + return PyUnicode_FromFormat("%" PRId64 "", cid->id); +} + +static PyObject * +channelid_int(PyObject *self) +{ + channelid *cid = (channelid *)self; + return PyLong_FromLongLong(cid->id); +} + +static Py_hash_t +channelid_hash(PyObject *self) +{ + channelid *cid = (channelid *)self; + PyObject *id = PyLong_FromLongLong(cid->id); + if (id == NULL) { + return -1; + } + Py_hash_t hash = PyObject_Hash(id); + Py_DECREF(id); + return hash; +} + +static PyObject * +channelid_richcompare(PyObject *self, PyObject *other, int op) +{ + PyObject *res = NULL; + if (op != Py_EQ && op != Py_NE) { + Py_RETURN_NOTIMPLEMENTED; + } + + PyObject *mod = get_module_from_type(Py_TYPE(self)); + if (mod == NULL) { + return NULL; + } + module_state *state = get_module_state(mod); + if (state == NULL) { + goto done; + } + + if (!PyObject_TypeCheck(self, state->ChannelIDType)) { + res = Py_NewRef(Py_NotImplemented); + goto done; + } + + channelid *cid = (channelid *)self; + int equal; + if (PyObject_TypeCheck(other, state->ChannelIDType)) { + channelid *othercid = (channelid *)other; + equal = (cid->end == othercid->end) && (cid->id == othercid->id); + } + else if (PyLong_Check(other)) { + /* Fast path */ + int overflow; + long long othercid = PyLong_AsLongLongAndOverflow(other, &overflow); + if (othercid == -1 && PyErr_Occurred()) { + goto done; + } + equal = !overflow && (othercid >= 0) && (cid->id == othercid); + } + else if (PyNumber_Check(other)) { + PyObject *pyid = PyLong_FromLongLong(cid->id); + if (pyid == NULL) { + goto done; + } + res = PyObject_RichCompare(pyid, other, op); + Py_DECREF(pyid); + goto done; + } + else { + res = Py_NewRef(Py_NotImplemented); + goto done; + } + + if ((op == Py_EQ && equal) || (op == Py_NE && !equal)) { + res = Py_NewRef(Py_True); + } + else { + res = Py_NewRef(Py_False); + } + +done: + Py_DECREF(mod); + return res; +} + +static PyObject * +_channel_from_cid(PyObject *cid, int end) +{ + PyObject *highlevel = PyImport_ImportModule("interpreters"); + if (highlevel == NULL) { + PyErr_Clear(); + highlevel = PyImport_ImportModule("test.support.interpreters"); + if (highlevel == NULL) { + return NULL; + } + } + const char *clsname = (end == CHANNEL_RECV) ? "RecvChannel" : + "SendChannel"; + PyObject *cls = PyObject_GetAttrString(highlevel, clsname); + Py_DECREF(highlevel); + if (cls == NULL) { + return NULL; + } + PyObject *chan = PyObject_CallFunctionObjArgs(cls, cid, NULL); + Py_DECREF(cls); + if (chan == NULL) { + return NULL; + } + return chan; +} + +struct _channelid_xid { + int64_t id; + int end; + int resolve; +}; + +static PyObject * +_channelid_from_xid(_PyCrossInterpreterData *data) +{ + struct _channelid_xid *xid = (struct _channelid_xid *)data->data; + + // It might not be imported yet, so we can't use _get_current_module(). + PyObject *mod = PyImport_ImportModule(MODULE_NAME); + if (mod == NULL) { + return NULL; + } + assert(mod != Py_None); + module_state *state = get_module_state(mod); + if (state == NULL) { + return NULL; + } + + // Note that we do not preserve the "resolve" flag. + PyObject *cid = NULL; + int err = newchannelid(state->ChannelIDType, xid->id, xid->end, + _global_channels(), 0, 0, + (channelid **)&cid); + if (err != 0) { + assert(cid == NULL); + (void)handle_channel_error(err, mod, xid->id); + goto done; + } + assert(cid != NULL); + if (xid->end == 0) { + goto done; + } + if (!xid->resolve) { + goto done; + } + + /* Try returning a high-level channel end but fall back to the ID. */ + PyObject *chan = _channel_from_cid(cid, xid->end); + if (chan == NULL) { + PyErr_Clear(); + goto done; + } + Py_DECREF(cid); + cid = chan; + +done: + Py_DECREF(mod); + return cid; +} + +static int +_channelid_shared(PyThreadState *tstate, PyObject *obj, + _PyCrossInterpreterData *data) +{ + if (_PyCrossInterpreterData_InitWithSize( + data, tstate->interp, sizeof(struct _channelid_xid), obj, + _channelid_from_xid + ) < 0) + { + return -1; + } + struct _channelid_xid *xid = (struct _channelid_xid *)data->data; + xid->id = ((channelid *)obj)->id; + xid->end = ((channelid *)obj)->end; + xid->resolve = ((channelid *)obj)->resolve; + return 0; +} + +static PyObject * +channelid_end(PyObject *self, void *end) +{ + int force = 1; + channelid *cid = (channelid *)self; + if (end != NULL) { + PyObject *id = NULL; + int err = newchannelid(Py_TYPE(self), cid->id, *(int *)end, + cid->channels, force, cid->resolve, + (channelid **)&id); + if (err != 0) { + assert(id == NULL); + PyObject *mod = get_module_from_type(Py_TYPE(self)); + if (mod == NULL) { + return NULL; + } + (void)handle_channel_error(err, mod, cid->id); + Py_DECREF(mod); + return NULL; + } + assert(id != NULL); + return id; + } + + if (cid->end == CHANNEL_SEND) { + return PyUnicode_InternFromString("send"); + } + if (cid->end == CHANNEL_RECV) { + return PyUnicode_InternFromString("recv"); + } + return PyUnicode_InternFromString("both"); +} + +static int _channelid_end_send = CHANNEL_SEND; +static int _channelid_end_recv = CHANNEL_RECV; + +static PyGetSetDef channelid_getsets[] = { + {"end", (getter)channelid_end, NULL, + PyDoc_STR("'send', 'recv', or 'both'")}, + {"send", (getter)channelid_end, NULL, + PyDoc_STR("the 'send' end of the channel"), &_channelid_end_send}, + {"recv", (getter)channelid_end, NULL, + PyDoc_STR("the 'recv' end of the channel"), &_channelid_end_recv}, + {NULL} +}; + +PyDoc_STRVAR(channelid_doc, +"A channel ID identifies a channel and may be used as an int."); + +static PyType_Slot ChannelIDType_slots[] = { + {Py_tp_dealloc, (destructor)channelid_dealloc}, + {Py_tp_doc, (void *)channelid_doc}, + {Py_tp_repr, (reprfunc)channelid_repr}, + {Py_tp_str, (reprfunc)channelid_str}, + {Py_tp_hash, channelid_hash}, + {Py_tp_richcompare, channelid_richcompare}, + {Py_tp_getset, channelid_getsets}, + // number slots + {Py_nb_int, (unaryfunc)channelid_int}, + {Py_nb_index, (unaryfunc)channelid_int}, + {0, NULL}, +}; + +static PyType_Spec ChannelIDType_spec = { + .name = "_xxsubinterpreters.ChannelID", + .basicsize = sizeof(channelid), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE), + .slots = ChannelIDType_slots, +}; + + +/* module level code ********************************************************/ + +/* globals is the process-global state for the module. It holds all + the data that we need to share between interpreters, so it cannot + hold PyObject values. */ +static struct globals { + int module_count; + _channels channels; +} _globals = {0}; + +static int +_globals_init(void) +{ + // XXX This isn't thread-safe. + _globals.module_count++; + if (_globals.module_count > 1) { + // Already initialized. + return 0; + } + + assert(_globals.channels.mutex == NULL); + PyThread_type_lock mutex = PyThread_allocate_lock(); + if (mutex == NULL) { + return ERR_CHANNELS_MUTEX_INIT; + } + _channels_init(&_globals.channels, mutex); + return 0; +} + +static void +_globals_fini(void) +{ + // XXX This isn't thread-safe. + _globals.module_count--; + if (_globals.module_count > 0) { + return; + } + + _channels_fini(&_globals.channels); +} + +static _channels * +_global_channels(void) { + return &_globals.channels; +} + + +static PyObject * +channel_create(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + int64_t cid = _channel_create(&_globals.channels); + if (cid < 0) { + (void)handle_channel_error(-1, self, cid); + return NULL; + } + module_state *state = get_module_state(self); + if (state == NULL) { + return NULL; + } + PyObject *id = NULL; + int err = newchannelid(state->ChannelIDType, cid, 0, + &_globals.channels, 0, 0, + (channelid **)&id); + if (handle_channel_error(err, self, cid)) { + assert(id == NULL); + err = _channel_destroy(&_globals.channels, cid); + if (handle_channel_error(err, self, cid)) { + // XXX issue a warning? + } + return NULL; + } + assert(id != NULL); + assert(((channelid *)id)->channels != NULL); + return id; +} + +PyDoc_STRVAR(channel_create_doc, +"channel_create() -> cid\n\ +\n\ +Create a new cross-interpreter channel and return a unique generated ID."); + +static PyObject * +channel_destroy(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cid", NULL}; + int64_t cid; + struct channel_id_converter_data cid_data = { + .module = self, + }; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:channel_destroy", kwlist, + channel_id_converter, &cid_data)) { + return NULL; + } + cid = cid_data.cid; + + int err = _channel_destroy(&_globals.channels, cid); + if (handle_channel_error(err, self, cid)) { + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(channel_destroy_doc, +"channel_destroy(cid)\n\ +\n\ +Close and finalize the channel. Afterward attempts to use the channel\n\ +will behave as though it never existed."); + +static PyObject * +channel_list_all(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + int64_t count = 0; + int64_t *cids = _channels_list_all(&_globals.channels, &count); + if (cids == NULL) { + if (count == 0) { + return PyList_New(0); + } + return NULL; + } + PyObject *ids = PyList_New((Py_ssize_t)count); + if (ids == NULL) { + goto finally; + } + module_state *state = get_module_state(self); + if (state == NULL) { + Py_DECREF(ids); + ids = NULL; + goto finally; + } + int64_t *cur = cids; + for (int64_t i=0; i < count; cur++, i++) { + PyObject *id = NULL; + int err = newchannelid(state->ChannelIDType, *cur, 0, + &_globals.channels, 0, 0, + (channelid **)&id); + if (handle_channel_error(err, self, *cur)) { + assert(id == NULL); + Py_SETREF(ids, NULL); + break; + } + assert(id != NULL); + PyList_SET_ITEM(ids, (Py_ssize_t)i, id); + } + +finally: + PyMem_Free(cids); + return ids; +} + +PyDoc_STRVAR(channel_list_all_doc, +"channel_list_all() -> [cid]\n\ +\n\ +Return the list of all IDs for active channels."); + +static PyObject * +channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cid", "send", NULL}; + int64_t cid; /* Channel ID */ + struct channel_id_converter_data cid_data = { + .module = self, + }; + int send = 0; /* Send or receive end? */ + int64_t id; + PyObject *ids, *id_obj; + PyInterpreterState *interp; + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "O&$p:channel_list_interpreters", + kwlist, channel_id_converter, &cid_data, &send)) { + return NULL; + } + cid = cid_data.cid; + + ids = PyList_New(0); + if (ids == NULL) { + goto except; + } + + interp = PyInterpreterState_Head(); + while (interp != NULL) { + id = PyInterpreterState_GetID(interp); + assert(id >= 0); + int res = _channel_is_associated(&_globals.channels, cid, id, send); + if (res < 0) { + (void)handle_channel_error(res, self, cid); + goto except; + } + if (res) { + id_obj = _PyInterpreterState_GetIDObject(interp); + if (id_obj == NULL) { + goto except; + } + res = PyList_Insert(ids, 0, id_obj); + Py_DECREF(id_obj); + if (res < 0) { + goto except; + } + } + interp = PyInterpreterState_Next(interp); + } + + goto finally; + +except: + Py_CLEAR(ids); + +finally: + return ids; +} + +PyDoc_STRVAR(channel_list_interpreters_doc, +"channel_list_interpreters(cid, *, send) -> [id]\n\ +\n\ +Return the list of all interpreter IDs associated with an end of the channel.\n\ +\n\ +The 'send' argument should be a boolean indicating whether to use the send or\n\ +receive end."); + + +static PyObject * +channel_send(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cid", "obj", NULL}; + int64_t cid; + struct channel_id_converter_data cid_data = { + .module = self, + }; + PyObject *obj; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O:channel_send", kwlist, + channel_id_converter, &cid_data, &obj)) { + return NULL; + } + cid = cid_data.cid; + + int err = _channel_send(&_globals.channels, cid, obj); + if (handle_channel_error(err, self, cid)) { + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(channel_send_doc, +"channel_send(cid, obj)\n\ +\n\ +Add the object's data to the channel's queue."); + +static PyObject * +channel_recv(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cid", "default", NULL}; + int64_t cid; + struct channel_id_converter_data cid_data = { + .module = self, + }; + PyObject *dflt = NULL; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O:channel_recv", kwlist, + channel_id_converter, &cid_data, &dflt)) { + return NULL; + } + cid = cid_data.cid; + + PyObject *obj = NULL; + int err = _channel_recv(&_globals.channels, cid, &obj); + if (handle_channel_error(err, self, cid)) { + return NULL; + } + Py_XINCREF(dflt); + if (obj == NULL) { + // Use the default. + if (dflt == NULL) { + (void)handle_channel_error(ERR_CHANNEL_EMPTY, self, cid); + return NULL; + } + obj = Py_NewRef(dflt); + } + Py_XDECREF(dflt); + return obj; +} + +PyDoc_STRVAR(channel_recv_doc, +"channel_recv(cid, [default]) -> obj\n\ +\n\ +Return a new object from the data at the front of the channel's queue.\n\ +\n\ +If there is nothing to receive then raise ChannelEmptyError, unless\n\ +a default value is provided. In that case return it."); + +static PyObject * +channel_close(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cid", "send", "recv", "force", NULL}; + int64_t cid; + struct channel_id_converter_data cid_data = { + .module = self, + }; + int send = 0; + int recv = 0; + int force = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "O&|$ppp:channel_close", kwlist, + channel_id_converter, &cid_data, + &send, &recv, &force)) { + return NULL; + } + cid = cid_data.cid; + + int err = _channel_close(&_globals.channels, cid, send-recv, force); + if (handle_channel_error(err, self, cid)) { + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(channel_close_doc, +"channel_close(cid, *, send=None, recv=None, force=False)\n\ +\n\ +Close the channel for all interpreters.\n\ +\n\ +If the channel is empty then the keyword args are ignored and both\n\ +ends are immediately closed. Otherwise, if 'force' is True then\n\ +all queued items are released and both ends are immediately\n\ +closed.\n\ +\n\ +If the channel is not empty *and* 'force' is False then following\n\ +happens:\n\ +\n\ + * recv is True (regardless of send):\n\ + - raise ChannelNotEmptyError\n\ + * recv is None and send is None:\n\ + - raise ChannelNotEmptyError\n\ + * send is True and recv is not True:\n\ + - fully close the 'send' end\n\ + - close the 'recv' end to interpreters not already receiving\n\ + - fully close it once empty\n\ +\n\ +Closing an already closed channel results in a ChannelClosedError.\n\ +\n\ +Once the channel's ID has no more ref counts in any interpreter\n\ +the channel will be destroyed."); + +static PyObject * +channel_release(PyObject *self, PyObject *args, PyObject *kwds) +{ + // Note that only the current interpreter is affected. + static char *kwlist[] = {"cid", "send", "recv", "force", NULL}; + int64_t cid; + struct channel_id_converter_data cid_data = { + .module = self, + }; + int send = 0; + int recv = 0; + int force = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "O&|$ppp:channel_release", kwlist, + channel_id_converter, &cid_data, + &send, &recv, &force)) { + return NULL; + } + cid = cid_data.cid; + if (send == 0 && recv == 0) { + send = 1; + recv = 1; + } + + // XXX Handle force is True. + // XXX Fix implicit release. + + int err = _channel_drop(&_globals.channels, cid, send, recv); + if (handle_channel_error(err, self, cid)) { + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(channel_release_doc, +"channel_release(cid, *, send=None, recv=None, force=True)\n\ +\n\ +Close the channel for the current interpreter. 'send' and 'recv'\n\ +(bool) may be used to indicate the ends to close. By default both\n\ +ends are closed. Closing an already closed end is a noop."); + +static PyObject * +channel__channel_id(PyObject *self, PyObject *args, PyObject *kwds) +{ + module_state *state = get_module_state(self); + if (state == NULL) { + return NULL; + } + PyTypeObject *cls = state->ChannelIDType; + PyObject *mod = get_module_from_owned_type(cls); + if (mod == NULL) { + return NULL; + } + PyObject *cid = _channelid_new(mod, cls, args, kwds); + Py_DECREF(mod); + return cid; +} + +static PyMethodDef module_functions[] = { + {"create", channel_create, + METH_NOARGS, channel_create_doc}, + {"destroy", _PyCFunction_CAST(channel_destroy), + METH_VARARGS | METH_KEYWORDS, channel_destroy_doc}, + {"list_all", channel_list_all, + METH_NOARGS, channel_list_all_doc}, + {"list_interpreters", _PyCFunction_CAST(channel_list_interpreters), + METH_VARARGS | METH_KEYWORDS, channel_list_interpreters_doc}, + {"send", _PyCFunction_CAST(channel_send), + METH_VARARGS | METH_KEYWORDS, channel_send_doc}, + {"recv", _PyCFunction_CAST(channel_recv), + METH_VARARGS | METH_KEYWORDS, channel_recv_doc}, + {"close", _PyCFunction_CAST(channel_close), + METH_VARARGS | METH_KEYWORDS, channel_close_doc}, + {"release", _PyCFunction_CAST(channel_release), + METH_VARARGS | METH_KEYWORDS, channel_release_doc}, + {"_channel_id", _PyCFunction_CAST(channel__channel_id), + METH_VARARGS | METH_KEYWORDS, NULL}, + + {NULL, NULL} /* sentinel */ +}; + + +/* initialization function */ + +PyDoc_STRVAR(module_doc, +"This module provides primitive operations to manage Python interpreters.\n\ +The 'interpreters' module provides a more convenient interface."); + +static int +module_exec(PyObject *mod) +{ + if (_globals_init() != 0) { + return -1; + } + + /* Add exception types */ + if (exceptions_init(mod) != 0) { + goto error; + } + + /* Add other types */ + module_state *state = get_module_state(mod); + if (state == NULL) { + goto error; + } + + // ChannelID + state->ChannelIDType = add_new_type( + mod, &ChannelIDType_spec, _channelid_shared); + if (state->ChannelIDType == NULL) { + goto error; + } + + return 0; + +error: + (void)_PyCrossInterpreterData_UnregisterClass(state->ChannelIDType); + _globals_fini(); + return -1; +} + +static struct PyModuleDef_Slot module_slots[] = { + {Py_mod_exec, module_exec}, + {0, NULL}, +}; + +static int +module_traverse(PyObject *mod, visitproc visit, void *arg) +{ + module_state *state = get_module_state(mod); + assert(state != NULL); + traverse_module_state(state, visit, arg); + return 0; +} + +static int +module_clear(PyObject *mod) +{ + module_state *state = get_module_state(mod); + assert(state != NULL); + clear_module_state(state); + return 0; +} + +static void +module_free(void *mod) +{ + module_state *state = get_module_state(mod); + assert(state != NULL); + clear_module_state(state); + _globals_fini(); +} + +static struct PyModuleDef moduledef = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = MODULE_NAME, + .m_doc = module_doc, + .m_size = sizeof(module_state), + .m_methods = module_functions, + .m_slots = module_slots, + .m_traverse = module_traverse, + .m_clear = module_clear, + .m_free = (freefunc)module_free, +}; + +PyMODINIT_FUNC +PyInit__xxinterpchannels(void) +{ + return PyModuleDef_Init(&moduledef); +} diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 0892fa3a9595e8..461c505c092c70 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -39,43 +39,6 @@ _get_current_interp(void) return PyInterpreterState_Get(); } -static PyObject * -_get_current_module(void) -{ - // We ensured it was imported in _run_script(). - PyObject *name = PyUnicode_FromString(MODULE_NAME); - if (name == NULL) { - return NULL; - } - PyObject *mod = PyImport_GetModule(name); - Py_DECREF(name); - if (mod == NULL) { - return NULL; - } - assert(mod != Py_None); - return mod; -} - -static PyObject * -get_module_from_owned_type(PyTypeObject *cls) -{ - assert(cls != NULL); - return _get_current_module(); - // XXX Use the more efficient API now that we use heap types: - //return PyType_GetModule(cls); -} - -static struct PyModuleDef moduledef; - -static PyObject * -get_module_from_type(PyTypeObject *cls) -{ - assert(cls != NULL); - return _get_current_module(); - // XXX Use the more efficient API now that we use heap types: - //return PyType_GetModuleByDef(cls, &moduledef); -} - static PyObject * add_new_exception(PyObject *mod, const char *name, PyObject *base) { @@ -95,27 +58,6 @@ add_new_exception(PyObject *mod, const char *name, PyObject *base) #define ADD_NEW_EXCEPTION(MOD, NAME, BASE) \ add_new_exception(MOD, MODULE_NAME "." Py_STRINGIFY(NAME), BASE) -static PyTypeObject * -add_new_type(PyObject *mod, PyType_Spec *spec, crossinterpdatafunc shared) -{ - PyTypeObject *cls = (PyTypeObject *)PyType_FromMetaclass( - NULL, mod, spec, NULL); - if (cls == NULL) { - return NULL; - } - if (PyModule_AddType(mod, cls) < 0) { - Py_DECREF(cls); - return NULL; - } - if (shared != NULL) { - if (_PyCrossInterpreterData_RegisterClass(cls, shared)) { - Py_DECREF(cls); - return NULL; - } - } - return cls; -} - static int _release_xid_data(_PyCrossInterpreterData *data, int ignoreexc) { @@ -127,9 +69,7 @@ _release_xid_data(_PyCrossInterpreterData *data, int ignoreexc) if (res < 0) { // XXX Fix this! /* The owning interpreter is already destroyed. - * Ideally, this shouldn't ever happen. When an interpreter is - * about to be destroyed, we should clear out all of its objects - * from every channel associated with that interpreter. + * Ideally, this shouldn't ever happen. (It's highly unlikely.) * For now we hack around that to resolve refleaks, by decref'ing * the released object here, even if its the wrong interpreter. * The owning interpreter has already been destroyed @@ -153,17 +93,8 @@ _release_xid_data(_PyCrossInterpreterData *data, int ignoreexc) /* module state *************************************************************/ typedef struct { - PyTypeObject *ChannelIDType; - - /* interpreter exceptions */ + /* exceptions */ PyObject *RunFailedError; - - /* channel exceptions */ - PyObject *ChannelError; - PyObject *ChannelNotFoundError; - PyObject *ChannelClosedError; - PyObject *ChannelEmptyError; - PyObject *ChannelNotEmptyError; } module_state; static inline module_state * @@ -178,37 +109,18 @@ get_module_state(PyObject *mod) static int traverse_module_state(module_state *state, visitproc visit, void *arg) { - /* heap types */ - Py_VISIT(state->ChannelIDType); - - /* interpreter exceptions */ + /* exceptions */ Py_VISIT(state->RunFailedError); - /* channel exceptions */ - Py_VISIT(state->ChannelError); - Py_VISIT(state->ChannelNotFoundError); - Py_VISIT(state->ChannelClosedError); - Py_VISIT(state->ChannelEmptyError); - Py_VISIT(state->ChannelNotEmptyError); return 0; } static int clear_module_state(module_state *state) { - /* heap types */ - (void)_PyCrossInterpreterData_UnregisterClass(state->ChannelIDType); - Py_CLEAR(state->ChannelIDType); - - /* interpreter exceptions */ + /* exceptions */ Py_CLEAR(state->RunFailedError); - /* channel exceptions */ - Py_CLEAR(state->ChannelError); - Py_CLEAR(state->ChannelNotFoundError); - Py_CLEAR(state->ChannelClosedError); - Py_CLEAR(state->ChannelEmptyError); - Py_CLEAR(state->ChannelNotEmptyError); return 0; } @@ -298,10 +210,8 @@ _sharedns_free(_sharedns *shared) } static _sharedns * -_get_shared_ns(PyObject *shareable, PyTypeObject *channelidtype, - int *needs_import) +_get_shared_ns(PyObject *shareable) { - *needs_import = 0; if (shareable == NULL || shareable == Py_None) { return NULL; } @@ -323,9 +233,6 @@ _get_shared_ns(PyObject *shareable, PyTypeObject *channelidtype, if (_sharednsitem_init(&shared->items[i], key, value) != 0) { break; } - if (Py_TYPE(value) == channelidtype) { - *needs_import = 1; - } } if (PyErr_Occurred()) { _sharedns_free(shared); @@ -394,1701 +301,79 @@ _sharedexception_bind(PyObject *exctype, PyObject *exc, PyObject *tb) _sharedexception *err = _sharedexception_new(); if (err == NULL) { - goto finally; - } - - PyObject *name = PyUnicode_FromFormat("%S", exctype); - if (name == NULL) { - failure = "unable to format exception type name"; - goto finally; - } - err->name = _copy_raw_string(name); - Py_DECREF(name); - if (err->name == NULL) { - if (PyErr_ExceptionMatches(PyExc_MemoryError)) { - failure = "out of memory copying exception type name"; - } else { - failure = "unable to encode and copy exception type name"; - } - goto finally; - } - - if (exc != NULL) { - PyObject *msg = PyUnicode_FromFormat("%S", exc); - if (msg == NULL) { - failure = "unable to format exception message"; - goto finally; - } - err->msg = _copy_raw_string(msg); - Py_DECREF(msg); - if (err->msg == NULL) { - if (PyErr_ExceptionMatches(PyExc_MemoryError)) { - failure = "out of memory copying exception message"; - } else { - failure = "unable to encode and copy exception message"; - } - goto finally; - } - } - -finally: - if (failure != NULL) { - PyErr_Clear(); - if (err->name != NULL) { - PyMem_Free(err->name); - err->name = NULL; - } - err->msg = failure; - } - return err; -} - -static void -_sharedexception_apply(_sharedexception *exc, PyObject *wrapperclass) -{ - if (exc->name != NULL) { - if (exc->msg != NULL) { - PyErr_Format(wrapperclass, "%s: %s", exc->name, exc->msg); - } - else { - PyErr_SetString(wrapperclass, exc->name); - } - } - else if (exc->msg != NULL) { - PyErr_SetString(wrapperclass, exc->msg); - } - else { - PyErr_SetNone(wrapperclass); - } -} - - -/* channel-specific code ****************************************************/ - -#define CHANNEL_SEND 1 -#define CHANNEL_BOTH 0 -#define CHANNEL_RECV -1 - -/* channel errors */ - -#define ERR_CHANNEL_NOT_FOUND -2 -#define ERR_CHANNEL_CLOSED -3 -#define ERR_CHANNEL_INTERP_CLOSED -4 -#define ERR_CHANNEL_EMPTY -5 -#define ERR_CHANNEL_NOT_EMPTY -6 -#define ERR_CHANNEL_MUTEX_INIT -7 -#define ERR_CHANNELS_MUTEX_INIT -8 -#define ERR_NO_NEXT_CHANNEL_ID -9 - -static int -channel_exceptions_init(PyObject *mod) -{ - module_state *state = get_module_state(mod); - if (state == NULL) { - return -1; - } - -#define ADD(NAME, BASE) \ - do { \ - assert(state->NAME == NULL); \ - state->NAME = ADD_NEW_EXCEPTION(mod, NAME, BASE); \ - if (state->NAME == NULL) { \ - return -1; \ - } \ - } while (0) - - // A channel-related operation failed. - ADD(ChannelError, PyExc_RuntimeError); - // An operation tried to use a channel that doesn't exist. - ADD(ChannelNotFoundError, state->ChannelError); - // An operation tried to use a closed channel. - ADD(ChannelClosedError, state->ChannelError); - // An operation tried to pop from an empty channel. - ADD(ChannelEmptyError, state->ChannelError); - // An operation tried to close a non-empty channel. - ADD(ChannelNotEmptyError, state->ChannelError); -#undef ADD - - return 0; -} - -static int -handle_channel_error(int err, PyObject *mod, int64_t cid) -{ - if (err == 0) { - assert(!PyErr_Occurred()); - return 0; - } - assert(err < 0); - module_state *state = get_module_state(mod); - assert(state != NULL); - if (err == ERR_CHANNEL_NOT_FOUND) { - PyErr_Format(state->ChannelNotFoundError, - "channel %" PRId64 " not found", cid); - } - else if (err == ERR_CHANNEL_CLOSED) { - PyErr_Format(state->ChannelClosedError, - "channel %" PRId64 " is closed", cid); - } - else if (err == ERR_CHANNEL_INTERP_CLOSED) { - PyErr_Format(state->ChannelClosedError, - "channel %" PRId64 " is already closed", cid); - } - else if (err == ERR_CHANNEL_EMPTY) { - PyErr_Format(state->ChannelEmptyError, - "channel %" PRId64 " is empty", cid); - } - else if (err == ERR_CHANNEL_NOT_EMPTY) { - PyErr_Format(state->ChannelNotEmptyError, - "channel %" PRId64 " may not be closed " - "if not empty (try force=True)", - cid); - } - else if (err == ERR_CHANNEL_MUTEX_INIT) { - PyErr_SetString(state->ChannelError, - "can't initialize mutex for new channel"); - } - else if (err == ERR_CHANNELS_MUTEX_INIT) { - PyErr_SetString(state->ChannelError, - "can't initialize mutex for channel management"); - } - else if (err == ERR_NO_NEXT_CHANNEL_ID) { - PyErr_SetString(state->ChannelError, - "failed to get a channel ID"); - } - else { - assert(PyErr_Occurred()); - } - return 1; -} - -/* the channel queue */ - -struct _channelitem; - -typedef struct _channelitem { - _PyCrossInterpreterData *data; - struct _channelitem *next; -} _channelitem; - -static _channelitem * -_channelitem_new(void) -{ - _channelitem *item = PyMem_NEW(_channelitem, 1); - if (item == NULL) { - PyErr_NoMemory(); - return NULL; - } - item->data = NULL; - item->next = NULL; - return item; -} - -static void -_channelitem_clear(_channelitem *item) -{ - if (item->data != NULL) { - (void)_release_xid_data(item->data, 1); - PyMem_Free(item->data); - item->data = NULL; - } - item->next = NULL; -} - -static void -_channelitem_free(_channelitem *item) -{ - _channelitem_clear(item); - PyMem_Free(item); -} - -static void -_channelitem_free_all(_channelitem *item) -{ - while (item != NULL) { - _channelitem *last = item; - item = item->next; - _channelitem_free(last); - } -} - -static _PyCrossInterpreterData * -_channelitem_popped(_channelitem *item) -{ - _PyCrossInterpreterData *data = item->data; - item->data = NULL; - _channelitem_free(item); - return data; -} - -typedef struct _channelqueue { - int64_t count; - _channelitem *first; - _channelitem *last; -} _channelqueue; - -static _channelqueue * -_channelqueue_new(void) -{ - _channelqueue *queue = PyMem_NEW(_channelqueue, 1); - if (queue == NULL) { - PyErr_NoMemory(); - return NULL; - } - queue->count = 0; - queue->first = NULL; - queue->last = NULL; - return queue; -} - -static void -_channelqueue_clear(_channelqueue *queue) -{ - _channelitem_free_all(queue->first); - queue->count = 0; - queue->first = NULL; - queue->last = NULL; -} - -static void -_channelqueue_free(_channelqueue *queue) -{ - _channelqueue_clear(queue); - PyMem_Free(queue); -} - -static int -_channelqueue_put(_channelqueue *queue, _PyCrossInterpreterData *data) -{ - _channelitem *item = _channelitem_new(); - if (item == NULL) { - return -1; - } - item->data = data; - - queue->count += 1; - if (queue->first == NULL) { - queue->first = item; - } - else { - queue->last->next = item; - } - queue->last = item; - return 0; -} - -static _PyCrossInterpreterData * -_channelqueue_get(_channelqueue *queue) -{ - _channelitem *item = queue->first; - if (item == NULL) { - return NULL; - } - queue->first = item->next; - if (queue->last == item) { - queue->last = NULL; - } - queue->count -= 1; - - return _channelitem_popped(item); -} - -/* channel-interpreter associations */ - -struct _channelend; - -typedef struct _channelend { - struct _channelend *next; - int64_t interp; - int open; -} _channelend; - -static _channelend * -_channelend_new(int64_t interp) -{ - _channelend *end = PyMem_NEW(_channelend, 1); - if (end == NULL) { - PyErr_NoMemory(); - return NULL; - } - end->next = NULL; - end->interp = interp; - end->open = 1; - return end; -} - -static void -_channelend_free(_channelend *end) -{ - PyMem_Free(end); -} - -static void -_channelend_free_all(_channelend *end) -{ - while (end != NULL) { - _channelend *last = end; - end = end->next; - _channelend_free(last); - } -} - -static _channelend * -_channelend_find(_channelend *first, int64_t interp, _channelend **pprev) -{ - _channelend *prev = NULL; - _channelend *end = first; - while (end != NULL) { - if (end->interp == interp) { - break; - } - prev = end; - end = end->next; - } - if (pprev != NULL) { - *pprev = prev; - } - return end; -} - -typedef struct _channelassociations { - // Note that the list entries are never removed for interpreter - // for which the channel is closed. This should not be a problem in - // practice. Also, a channel isn't automatically closed when an - // interpreter is destroyed. - int64_t numsendopen; - int64_t numrecvopen; - _channelend *send; - _channelend *recv; -} _channelends; - -static _channelends * -_channelends_new(void) -{ - _channelends *ends = PyMem_NEW(_channelends, 1); - if (ends== NULL) { - return NULL; - } - ends->numsendopen = 0; - ends->numrecvopen = 0; - ends->send = NULL; - ends->recv = NULL; - return ends; -} - -static void -_channelends_clear(_channelends *ends) -{ - _channelend_free_all(ends->send); - ends->send = NULL; - ends->numsendopen = 0; - - _channelend_free_all(ends->recv); - ends->recv = NULL; - ends->numrecvopen = 0; -} - -static void -_channelends_free(_channelends *ends) -{ - _channelends_clear(ends); - PyMem_Free(ends); -} - -static _channelend * -_channelends_add(_channelends *ends, _channelend *prev, int64_t interp, - int send) -{ - _channelend *end = _channelend_new(interp); - if (end == NULL) { - return NULL; - } - - if (prev == NULL) { - if (send) { - ends->send = end; - } - else { - ends->recv = end; - } - } - else { - prev->next = end; - } - if (send) { - ends->numsendopen += 1; - } - else { - ends->numrecvopen += 1; - } - return end; -} - -static int -_channelends_associate(_channelends *ends, int64_t interp, int send) -{ - _channelend *prev; - _channelend *end = _channelend_find(send ? ends->send : ends->recv, - interp, &prev); - if (end != NULL) { - if (!end->open) { - return ERR_CHANNEL_CLOSED; - } - // already associated - return 0; - } - if (_channelends_add(ends, prev, interp, send) == NULL) { - return -1; - } - return 0; -} - -static int -_channelends_is_open(_channelends *ends) -{ - if (ends->numsendopen != 0 || ends->numrecvopen != 0) { - return 1; - } - if (ends->send == NULL && ends->recv == NULL) { - return 1; - } - return 0; -} - -static void -_channelends_close_end(_channelends *ends, _channelend *end, int send) -{ - end->open = 0; - if (send) { - ends->numsendopen -= 1; - } - else { - ends->numrecvopen -= 1; - } -} - -static int -_channelends_close_interpreter(_channelends *ends, int64_t interp, int which) -{ - _channelend *prev; - _channelend *end; - if (which >= 0) { // send/both - end = _channelend_find(ends->send, interp, &prev); - if (end == NULL) { - // never associated so add it - end = _channelends_add(ends, prev, interp, 1); - if (end == NULL) { - return -1; - } - } - _channelends_close_end(ends, end, 1); - } - if (which <= 0) { // recv/both - end = _channelend_find(ends->recv, interp, &prev); - if (end == NULL) { - // never associated so add it - end = _channelends_add(ends, prev, interp, 0); - if (end == NULL) { - return -1; - } - } - _channelends_close_end(ends, end, 0); - } - return 0; -} - -static void -_channelends_close_all(_channelends *ends, int which, int force) -{ - // XXX Handle the ends. - // XXX Handle force is True. - - // Ensure all the "send"-associated interpreters are closed. - _channelend *end; - for (end = ends->send; end != NULL; end = end->next) { - _channelends_close_end(ends, end, 1); - } - - // Ensure all the "recv"-associated interpreters are closed. - for (end = ends->recv; end != NULL; end = end->next) { - _channelends_close_end(ends, end, 0); - } -} - -/* channels */ - -struct _channel; -struct _channel_closing; -static void _channel_clear_closing(struct _channel *); -static void _channel_finish_closing(struct _channel *); - -typedef struct _channel { - PyThread_type_lock mutex; - _channelqueue *queue; - _channelends *ends; - int open; - struct _channel_closing *closing; -} _PyChannelState; - -static _PyChannelState * -_channel_new(PyThread_type_lock mutex) -{ - _PyChannelState *chan = PyMem_NEW(_PyChannelState, 1); - if (chan == NULL) { - return NULL; - } - chan->mutex = mutex; - chan->queue = _channelqueue_new(); - if (chan->queue == NULL) { - PyMem_Free(chan); - return NULL; - } - chan->ends = _channelends_new(); - if (chan->ends == NULL) { - _channelqueue_free(chan->queue); - PyMem_Free(chan); - return NULL; - } - chan->open = 1; - chan->closing = NULL; - return chan; -} - -static void -_channel_free(_PyChannelState *chan) -{ - _channel_clear_closing(chan); - PyThread_acquire_lock(chan->mutex, WAIT_LOCK); - _channelqueue_free(chan->queue); - _channelends_free(chan->ends); - PyThread_release_lock(chan->mutex); - - PyThread_free_lock(chan->mutex); - PyMem_Free(chan); -} - -static int -_channel_add(_PyChannelState *chan, int64_t interp, - _PyCrossInterpreterData *data) -{ - int res = -1; - PyThread_acquire_lock(chan->mutex, WAIT_LOCK); - - if (!chan->open) { - res = ERR_CHANNEL_CLOSED; - goto done; - } - if (_channelends_associate(chan->ends, interp, 1) != 0) { - res = ERR_CHANNEL_INTERP_CLOSED; - goto done; - } - - if (_channelqueue_put(chan->queue, data) != 0) { - goto done; - } - - res = 0; -done: - PyThread_release_lock(chan->mutex); - return res; -} - -static int -_channel_next(_PyChannelState *chan, int64_t interp, - _PyCrossInterpreterData **res) -{ - int err = 0; - PyThread_acquire_lock(chan->mutex, WAIT_LOCK); - - if (!chan->open) { - err = ERR_CHANNEL_CLOSED; - goto done; - } - if (_channelends_associate(chan->ends, interp, 0) != 0) { - err = ERR_CHANNEL_INTERP_CLOSED; - goto done; - } - - _PyCrossInterpreterData *data = _channelqueue_get(chan->queue); - if (data == NULL && !PyErr_Occurred() && chan->closing != NULL) { - chan->open = 0; - } - *res = data; - -done: - PyThread_release_lock(chan->mutex); - if (chan->queue->count == 0) { - _channel_finish_closing(chan); - } - return err; -} - -static int -_channel_close_interpreter(_PyChannelState *chan, int64_t interp, int end) -{ - PyThread_acquire_lock(chan->mutex, WAIT_LOCK); - - int res = -1; - if (!chan->open) { - res = ERR_CHANNEL_CLOSED; - goto done; - } - - if (_channelends_close_interpreter(chan->ends, interp, end) != 0) { - goto done; - } - chan->open = _channelends_is_open(chan->ends); - - res = 0; -done: - PyThread_release_lock(chan->mutex); - return res; -} - -static int -_channel_close_all(_PyChannelState *chan, int end, int force) -{ - int res = -1; - PyThread_acquire_lock(chan->mutex, WAIT_LOCK); - - if (!chan->open) { - res = ERR_CHANNEL_CLOSED; - goto done; - } - - if (!force && chan->queue->count > 0) { - res = ERR_CHANNEL_NOT_EMPTY; - goto done; - } - - chan->open = 0; - - // We *could* also just leave these in place, since we've marked - // the channel as closed already. - _channelends_close_all(chan->ends, end, force); - - res = 0; -done: - PyThread_release_lock(chan->mutex); - return res; -} - -/* the set of channels */ - -struct _channelref; - -typedef struct _channelref { - int64_t id; - _PyChannelState *chan; - struct _channelref *next; - Py_ssize_t objcount; -} _channelref; - -static _channelref * -_channelref_new(int64_t id, _PyChannelState *chan) -{ - _channelref *ref = PyMem_NEW(_channelref, 1); - if (ref == NULL) { - return NULL; - } - ref->id = id; - ref->chan = chan; - ref->next = NULL; - ref->objcount = 0; - return ref; -} - -//static void -//_channelref_clear(_channelref *ref) -//{ -// ref->id = -1; -// ref->chan = NULL; -// ref->next = NULL; -// ref->objcount = 0; -//} - -static void -_channelref_free(_channelref *ref) -{ - if (ref->chan != NULL) { - _channel_clear_closing(ref->chan); - } - //_channelref_clear(ref); - PyMem_Free(ref); -} - -static _channelref * -_channelref_find(_channelref *first, int64_t id, _channelref **pprev) -{ - _channelref *prev = NULL; - _channelref *ref = first; - while (ref != NULL) { - if (ref->id == id) { - break; - } - prev = ref; - ref = ref->next; - } - if (pprev != NULL) { - *pprev = prev; - } - return ref; -} - -typedef struct _channels { - PyThread_type_lock mutex; - _channelref *head; - int64_t numopen; - int64_t next_id; -} _channels; - -static void -_channels_init(_channels *channels, PyThread_type_lock mutex) -{ - channels->mutex = mutex; - channels->head = NULL; - channels->numopen = 0; - channels->next_id = 0; -} - -static void -_channels_fini(_channels *channels) -{ - assert(channels->numopen == 0); - assert(channels->head == NULL); - if (channels->mutex != NULL) { - PyThread_free_lock(channels->mutex); - channels->mutex = NULL; - } -} - -static int64_t -_channels_next_id(_channels *channels) // needs lock -{ - int64_t id = channels->next_id; - if (id < 0) { - /* overflow */ - return -1; - } - channels->next_id += 1; - return id; -} - -static int -_channels_lookup(_channels *channels, int64_t id, PyThread_type_lock *pmutex, - _PyChannelState **res) -{ - int err = -1; - _PyChannelState *chan = NULL; - PyThread_acquire_lock(channels->mutex, WAIT_LOCK); - if (pmutex != NULL) { - *pmutex = NULL; - } - - _channelref *ref = _channelref_find(channels->head, id, NULL); - if (ref == NULL) { - err = ERR_CHANNEL_NOT_FOUND; - goto done; - } - if (ref->chan == NULL || !ref->chan->open) { - err = ERR_CHANNEL_CLOSED; - goto done; - } - - if (pmutex != NULL) { - // The mutex will be closed by the caller. - *pmutex = channels->mutex; - } - - chan = ref->chan; - err = 0; - -done: - if (pmutex == NULL || *pmutex == NULL) { - PyThread_release_lock(channels->mutex); - } - *res = chan; - return err; -} - -static int64_t -_channels_add(_channels *channels, _PyChannelState *chan) -{ - int64_t cid = -1; - PyThread_acquire_lock(channels->mutex, WAIT_LOCK); - - // Create a new ref. - int64_t id = _channels_next_id(channels); - if (id < 0) { - cid = ERR_NO_NEXT_CHANNEL_ID; - goto done; - } - _channelref *ref = _channelref_new(id, chan); - if (ref == NULL) { - goto done; - } - - // Add it to the list. - // We assume that the channel is a new one (not already in the list). - ref->next = channels->head; - channels->head = ref; - channels->numopen += 1; - - cid = id; -done: - PyThread_release_lock(channels->mutex); - return cid; -} - -/* forward */ -static int _channel_set_closing(struct _channelref *, PyThread_type_lock); - -static int -_channels_close(_channels *channels, int64_t cid, _PyChannelState **pchan, - int end, int force) -{ - int res = -1; - PyThread_acquire_lock(channels->mutex, WAIT_LOCK); - if (pchan != NULL) { - *pchan = NULL; - } - - _channelref *ref = _channelref_find(channels->head, cid, NULL); - if (ref == NULL) { - res = ERR_CHANNEL_NOT_FOUND; - goto done; - } - - if (ref->chan == NULL) { - res = ERR_CHANNEL_CLOSED; - goto done; - } - else if (!force && end == CHANNEL_SEND && ref->chan->closing != NULL) { - res = ERR_CHANNEL_CLOSED; - goto done; - } - else { - int err = _channel_close_all(ref->chan, end, force); - if (err != 0) { - if (end == CHANNEL_SEND && err == ERR_CHANNEL_NOT_EMPTY) { - if (ref->chan->closing != NULL) { - res = ERR_CHANNEL_CLOSED; - goto done; - } - // Mark the channel as closing and return. The channel - // will be cleaned up in _channel_next(). - PyErr_Clear(); - int err = _channel_set_closing(ref, channels->mutex); - if (err != 0) { - res = err; - goto done; - } - if (pchan != NULL) { - *pchan = ref->chan; - } - res = 0; - } - else { - res = err; - } - goto done; - } - if (pchan != NULL) { - *pchan = ref->chan; - } - else { - _channel_free(ref->chan); - } - ref->chan = NULL; - } - - res = 0; -done: - PyThread_release_lock(channels->mutex); - return res; -} - -static void -_channels_remove_ref(_channels *channels, _channelref *ref, _channelref *prev, - _PyChannelState **pchan) -{ - if (ref == channels->head) { - channels->head = ref->next; - } - else { - prev->next = ref->next; - } - channels->numopen -= 1; - - if (pchan != NULL) { - *pchan = ref->chan; - } - _channelref_free(ref); -} - -static int -_channels_remove(_channels *channels, int64_t id, _PyChannelState **pchan) -{ - int res = -1; - PyThread_acquire_lock(channels->mutex, WAIT_LOCK); - - if (pchan != NULL) { - *pchan = NULL; - } - - _channelref *prev = NULL; - _channelref *ref = _channelref_find(channels->head, id, &prev); - if (ref == NULL) { - res = ERR_CHANNEL_NOT_FOUND; - goto done; - } - - _channels_remove_ref(channels, ref, prev, pchan); - - res = 0; -done: - PyThread_release_lock(channels->mutex); - return res; -} - -static int -_channels_add_id_object(_channels *channels, int64_t id) -{ - int res = -1; - PyThread_acquire_lock(channels->mutex, WAIT_LOCK); - - _channelref *ref = _channelref_find(channels->head, id, NULL); - if (ref == NULL) { - res = ERR_CHANNEL_NOT_FOUND; - goto done; - } - ref->objcount += 1; - - res = 0; -done: - PyThread_release_lock(channels->mutex); - return res; -} - -static void -_channels_drop_id_object(_channels *channels, int64_t id) -{ - PyThread_acquire_lock(channels->mutex, WAIT_LOCK); - - _channelref *prev = NULL; - _channelref *ref = _channelref_find(channels->head, id, &prev); - if (ref == NULL) { - // Already destroyed. - goto done; - } - ref->objcount -= 1; - - // Destroy if no longer used. - if (ref->objcount == 0) { - _PyChannelState *chan = NULL; - _channels_remove_ref(channels, ref, prev, &chan); - if (chan != NULL) { - _channel_free(chan); - } - } - -done: - PyThread_release_lock(channels->mutex); -} - -static int64_t * -_channels_list_all(_channels *channels, int64_t *count) -{ - int64_t *cids = NULL; - PyThread_acquire_lock(channels->mutex, WAIT_LOCK); - int64_t *ids = PyMem_NEW(int64_t, (Py_ssize_t)(channels->numopen)); - if (ids == NULL) { - goto done; - } - _channelref *ref = channels->head; - for (int64_t i=0; ref != NULL; ref = ref->next, i++) { - ids[i] = ref->id; - } - *count = channels->numopen; - - cids = ids; -done: - PyThread_release_lock(channels->mutex); - return cids; -} - -/* support for closing non-empty channels */ - -struct _channel_closing { - struct _channelref *ref; -}; - -static int -_channel_set_closing(struct _channelref *ref, PyThread_type_lock mutex) { - struct _channel *chan = ref->chan; - if (chan == NULL) { - // already closed - return 0; - } - int res = -1; - PyThread_acquire_lock(chan->mutex, WAIT_LOCK); - if (chan->closing != NULL) { - res = ERR_CHANNEL_CLOSED; - goto done; - } - chan->closing = PyMem_NEW(struct _channel_closing, 1); - if (chan->closing == NULL) { - goto done; - } - chan->closing->ref = ref; - - res = 0; -done: - PyThread_release_lock(chan->mutex); - return res; -} - -static void -_channel_clear_closing(struct _channel *chan) { - PyThread_acquire_lock(chan->mutex, WAIT_LOCK); - if (chan->closing != NULL) { - PyMem_Free(chan->closing); - chan->closing = NULL; - } - PyThread_release_lock(chan->mutex); -} - -static void -_channel_finish_closing(struct _channel *chan) { - struct _channel_closing *closing = chan->closing; - if (closing == NULL) { - return; - } - _channelref *ref = closing->ref; - _channel_clear_closing(chan); - // Do the things that would have been done in _channels_close(). - ref->chan = NULL; - _channel_free(chan); -} - -/* "high"-level channel-related functions */ - -static int64_t -_channel_create(_channels *channels) -{ - PyThread_type_lock mutex = PyThread_allocate_lock(); - if (mutex == NULL) { - return ERR_CHANNEL_MUTEX_INIT; - } - _PyChannelState *chan = _channel_new(mutex); - if (chan == NULL) { - PyThread_free_lock(mutex); - return -1; - } - int64_t id = _channels_add(channels, chan); - if (id < 0) { - _channel_free(chan); - } - return id; -} - -static int -_channel_destroy(_channels *channels, int64_t id) -{ - _PyChannelState *chan = NULL; - int err = _channels_remove(channels, id, &chan); - if (err != 0) { - return err; - } - if (chan != NULL) { - _channel_free(chan); - } - return 0; -} - -static int -_channel_send(_channels *channels, int64_t id, PyObject *obj) -{ - PyInterpreterState *interp = _get_current_interp(); - if (interp == NULL) { - return -1; - } - - // Look up the channel. - PyThread_type_lock mutex = NULL; - _PyChannelState *chan = NULL; - int err = _channels_lookup(channels, id, &mutex, &chan); - if (err != 0) { - return err; - } - assert(chan != NULL); - // Past this point we are responsible for releasing the mutex. - - if (chan->closing != NULL) { - PyThread_release_lock(mutex); - return ERR_CHANNEL_CLOSED; - } - - // Convert the object to cross-interpreter data. - _PyCrossInterpreterData *data = PyMem_NEW(_PyCrossInterpreterData, 1); - if (data == NULL) { - PyThread_release_lock(mutex); - return -1; - } - if (_PyObject_GetCrossInterpreterData(obj, data) != 0) { - PyThread_release_lock(mutex); - PyMem_Free(data); - return -1; - } - - // Add the data to the channel. - int res = _channel_add(chan, PyInterpreterState_GetID(interp), data); - PyThread_release_lock(mutex); - if (res != 0) { - // We may chain an exception here: - (void)_release_xid_data(data, 0); - PyMem_Free(data); - return res; - } - - return 0; -} - -static int -_channel_recv(_channels *channels, int64_t id, PyObject **res) -{ - int err; - *res = NULL; - - PyInterpreterState *interp = _get_current_interp(); - if (interp == NULL) { - // XXX Is this always an error? - if (PyErr_Occurred()) { - return -1; - } - return 0; - } - - // Look up the channel. - PyThread_type_lock mutex = NULL; - _PyChannelState *chan = NULL; - err = _channels_lookup(channels, id, &mutex, &chan); - if (err != 0) { - return err; - } - assert(chan != NULL); - // Past this point we are responsible for releasing the mutex. - - // Pop off the next item from the channel. - _PyCrossInterpreterData *data = NULL; - err = _channel_next(chan, PyInterpreterState_GetID(interp), &data); - PyThread_release_lock(mutex); - if (err != 0) { - return err; - } - else if (data == NULL) { - assert(!PyErr_Occurred()); - return 0; - } - - // Convert the data back to an object. - PyObject *obj = _PyCrossInterpreterData_NewObject(data); - if (obj == NULL) { - assert(PyErr_Occurred()); - (void)_release_xid_data(data, 1); - PyMem_Free(data); - return -1; - } - int release_res = _release_xid_data(data, 0); - PyMem_Free(data); - if (release_res < 0) { - // The source interpreter has been destroyed already. - assert(PyErr_Occurred()); - Py_DECREF(obj); - return -1; - } - - *res = obj; - return 0; -} - -static int -_channel_drop(_channels *channels, int64_t id, int send, int recv) -{ - PyInterpreterState *interp = _get_current_interp(); - if (interp == NULL) { - return -1; - } - - // Look up the channel. - PyThread_type_lock mutex = NULL; - _PyChannelState *chan = NULL; - int err = _channels_lookup(channels, id, &mutex, &chan); - if (err != 0) { - return err; - } - // Past this point we are responsible for releasing the mutex. - - // Close one or both of the two ends. - int res = _channel_close_interpreter(chan, PyInterpreterState_GetID(interp), send-recv); - PyThread_release_lock(mutex); - return res; -} - -static int -_channel_close(_channels *channels, int64_t id, int end, int force) -{ - return _channels_close(channels, id, NULL, end, force); -} - -static int -_channel_is_associated(_channels *channels, int64_t cid, int64_t interp, - int send) -{ - _PyChannelState *chan = NULL; - int err = _channels_lookup(channels, cid, NULL, &chan); - if (err != 0) { - return err; - } - else if (send && chan->closing != NULL) { - return ERR_CHANNEL_CLOSED; - } - - _channelend *end = _channelend_find(send ? chan->ends->send : chan->ends->recv, - interp, NULL); - - return (end != NULL && end->open); -} - -/* ChannelID class */ - -typedef struct channelid { - PyObject_HEAD - int64_t id; - int end; - int resolve; - _channels *channels; -} channelid; - -struct channel_id_converter_data { - PyObject *module; - int64_t cid; -}; - -static int -channel_id_converter(PyObject *arg, void *ptr) -{ - int64_t cid; - struct channel_id_converter_data *data = ptr; - module_state *state = get_module_state(data->module); - assert(state != NULL); - if (PyObject_TypeCheck(arg, state->ChannelIDType)) { - cid = ((channelid *)arg)->id; - } - else if (PyIndex_Check(arg)) { - cid = PyLong_AsLongLong(arg); - if (cid == -1 && PyErr_Occurred()) { - return 0; - } - if (cid < 0) { - PyErr_Format(PyExc_ValueError, - "channel ID must be a non-negative int, got %R", arg); - return 0; - } - } - else { - PyErr_Format(PyExc_TypeError, - "channel ID must be an int, got %.100s", - Py_TYPE(arg)->tp_name); - return 0; - } - data->cid = cid; - return 1; -} - -static int -newchannelid(PyTypeObject *cls, int64_t cid, int end, _channels *channels, - int force, int resolve, channelid **res) -{ - *res = NULL; - - channelid *self = PyObject_New(channelid, cls); - if (self == NULL) { - return -1; - } - self->id = cid; - self->end = end; - self->resolve = resolve; - self->channels = channels; - - int err = _channels_add_id_object(channels, cid); - if (err != 0) { - if (force && err == ERR_CHANNEL_NOT_FOUND) { - assert(!PyErr_Occurred()); - } - else { - Py_DECREF((PyObject *)self); - return err; - } - } - - *res = self; - return 0; -} - -static _channels * _global_channels(void); - -static PyObject * -_channelid_new(PyObject *mod, PyTypeObject *cls, - PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"id", "send", "recv", "force", "_resolve", NULL}; - int64_t cid; - struct channel_id_converter_data cid_data = { - .module = mod, - }; - int send = -1; - int recv = -1; - int force = 0; - int resolve = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O&|$pppp:ChannelID.__new__", kwlist, - channel_id_converter, &cid_data, - &send, &recv, &force, &resolve)) { - return NULL; - } - cid = cid_data.cid; - - // Handle "send" and "recv". - if (send == 0 && recv == 0) { - PyErr_SetString(PyExc_ValueError, - "'send' and 'recv' cannot both be False"); - return NULL; - } - - int end = 0; - if (send == 1) { - if (recv == 0 || recv == -1) { - end = CHANNEL_SEND; - } - } - else if (recv == 1) { - end = CHANNEL_RECV; - } - - PyObject *id = NULL; - int err = newchannelid(cls, cid, end, _global_channels(), - force, resolve, - (channelid **)&id); - if (handle_channel_error(err, mod, cid)) { - assert(id == NULL); - return NULL; - } - assert(id != NULL); - return id; -} - -static void -channelid_dealloc(PyObject *self) -{ - int64_t cid = ((channelid *)self)->id; - _channels *channels = ((channelid *)self)->channels; - - PyTypeObject *tp = Py_TYPE(self); - tp->tp_free(self); - /* "Instances of heap-allocated types hold a reference to their type." - * See: https://docs.python.org/3.11/howto/isolating-extensions.html#garbage-collection-protocol - * See: https://docs.python.org/3.11/c-api/typeobj.html#c.PyTypeObject.tp_traverse - */ - // XXX Why don't we implement Py_TPFLAGS_HAVE_GC, e.g. Py_tp_traverse, - // like we do for _abc._abc_data? - Py_DECREF(tp); - - _channels_drop_id_object(channels, cid); -} - -static PyObject * -channelid_repr(PyObject *self) -{ - PyTypeObject *type = Py_TYPE(self); - const char *name = _PyType_Name(type); - - channelid *cid = (channelid *)self; - const char *fmt; - if (cid->end == CHANNEL_SEND) { - fmt = "%s(%" PRId64 ", send=True)"; - } - else if (cid->end == CHANNEL_RECV) { - fmt = "%s(%" PRId64 ", recv=True)"; - } - else { - fmt = "%s(%" PRId64 ")"; - } - return PyUnicode_FromFormat(fmt, name, cid->id); -} - -static PyObject * -channelid_str(PyObject *self) -{ - channelid *cid = (channelid *)self; - return PyUnicode_FromFormat("%" PRId64 "", cid->id); -} - -static PyObject * -channelid_int(PyObject *self) -{ - channelid *cid = (channelid *)self; - return PyLong_FromLongLong(cid->id); -} - -static Py_hash_t -channelid_hash(PyObject *self) -{ - channelid *cid = (channelid *)self; - PyObject *id = PyLong_FromLongLong(cid->id); - if (id == NULL) { - return -1; - } - Py_hash_t hash = PyObject_Hash(id); - Py_DECREF(id); - return hash; -} - -static PyObject * -channelid_richcompare(PyObject *self, PyObject *other, int op) -{ - PyObject *res = NULL; - if (op != Py_EQ && op != Py_NE) { - Py_RETURN_NOTIMPLEMENTED; - } - - PyObject *mod = get_module_from_type(Py_TYPE(self)); - if (mod == NULL) { - return NULL; - } - module_state *state = get_module_state(mod); - if (state == NULL) { - goto done; - } - - if (!PyObject_TypeCheck(self, state->ChannelIDType)) { - res = Py_NewRef(Py_NotImplemented); - goto done; - } - - channelid *cid = (channelid *)self; - int equal; - if (PyObject_TypeCheck(other, state->ChannelIDType)) { - channelid *othercid = (channelid *)other; - equal = (cid->end == othercid->end) && (cid->id == othercid->id); - } - else if (PyLong_Check(other)) { - /* Fast path */ - int overflow; - long long othercid = PyLong_AsLongLongAndOverflow(other, &overflow); - if (othercid == -1 && PyErr_Occurred()) { - goto done; - } - equal = !overflow && (othercid >= 0) && (cid->id == othercid); - } - else if (PyNumber_Check(other)) { - PyObject *pyid = PyLong_FromLongLong(cid->id); - if (pyid == NULL) { - goto done; - } - res = PyObject_RichCompare(pyid, other, op); - Py_DECREF(pyid); - goto done; - } - else { - res = Py_NewRef(Py_NotImplemented); - goto done; - } - - if ((op == Py_EQ && equal) || (op == Py_NE && !equal)) { - res = Py_NewRef(Py_True); - } - else { - res = Py_NewRef(Py_False); - } - -done: - Py_DECREF(mod); - return res; -} - -static PyObject * -_channel_from_cid(PyObject *cid, int end) -{ - PyObject *highlevel = PyImport_ImportModule("interpreters"); - if (highlevel == NULL) { - PyErr_Clear(); - highlevel = PyImport_ImportModule("test.support.interpreters"); - if (highlevel == NULL) { - return NULL; - } - } - const char *clsname = (end == CHANNEL_RECV) ? "RecvChannel" : - "SendChannel"; - PyObject *cls = PyObject_GetAttrString(highlevel, clsname); - Py_DECREF(highlevel); - if (cls == NULL) { - return NULL; - } - PyObject *chan = PyObject_CallFunctionObjArgs(cls, cid, NULL); - Py_DECREF(cls); - if (chan == NULL) { - return NULL; - } - return chan; -} - -struct _channelid_xid { - int64_t id; - int end; - int resolve; -}; - -static PyObject * -_channelid_from_xid(_PyCrossInterpreterData *data) -{ - struct _channelid_xid *xid = (struct _channelid_xid *)data->data; - - PyObject *mod = _get_current_module(); - if (mod == NULL) { - return NULL; - } - module_state *state = get_module_state(mod); - if (state == NULL) { - return NULL; + goto finally; } - // Note that we do not preserve the "resolve" flag. - PyObject *cid = NULL; - int err = newchannelid(state->ChannelIDType, xid->id, xid->end, - _global_channels(), 0, 0, - (channelid **)&cid); - if (err != 0) { - assert(cid == NULL); - (void)handle_channel_error(err, mod, xid->id); - goto done; - } - assert(cid != NULL); - if (xid->end == 0) { - goto done; + PyObject *name = PyUnicode_FromFormat("%S", exctype); + if (name == NULL) { + failure = "unable to format exception type name"; + goto finally; } - if (!xid->resolve) { - goto done; + err->name = _copy_raw_string(name); + Py_DECREF(name); + if (err->name == NULL) { + if (PyErr_ExceptionMatches(PyExc_MemoryError)) { + failure = "out of memory copying exception type name"; + } else { + failure = "unable to encode and copy exception type name"; + } + goto finally; } - /* Try returning a high-level channel end but fall back to the ID. */ - PyObject *chan = _channel_from_cid(cid, xid->end); - if (chan == NULL) { - PyErr_Clear(); - goto done; + if (exc != NULL) { + PyObject *msg = PyUnicode_FromFormat("%S", exc); + if (msg == NULL) { + failure = "unable to format exception message"; + goto finally; + } + err->msg = _copy_raw_string(msg); + Py_DECREF(msg); + if (err->msg == NULL) { + if (PyErr_ExceptionMatches(PyExc_MemoryError)) { + failure = "out of memory copying exception message"; + } else { + failure = "unable to encode and copy exception message"; + } + goto finally; + } } - Py_DECREF(cid); - cid = chan; -done: - Py_DECREF(mod); - return cid; -} - -static int -_channelid_shared(PyThreadState *tstate, PyObject *obj, - _PyCrossInterpreterData *data) -{ - if (_PyCrossInterpreterData_InitWithSize( - data, tstate->interp, sizeof(struct _channelid_xid), obj, - _channelid_from_xid - ) < 0) - { - return -1; +finally: + if (failure != NULL) { + PyErr_Clear(); + if (err->name != NULL) { + PyMem_Free(err->name); + err->name = NULL; + } + err->msg = failure; } - struct _channelid_xid *xid = (struct _channelid_xid *)data->data; - xid->id = ((channelid *)obj)->id; - xid->end = ((channelid *)obj)->end; - xid->resolve = ((channelid *)obj)->resolve; - return 0; + return err; } -static PyObject * -channelid_end(PyObject *self, void *end) +static void +_sharedexception_apply(_sharedexception *exc, PyObject *wrapperclass) { - int force = 1; - channelid *cid = (channelid *)self; - if (end != NULL) { - PyObject *id = NULL; - int err = newchannelid(Py_TYPE(self), cid->id, *(int *)end, - cid->channels, force, cid->resolve, - (channelid **)&id); - if (err != 0) { - assert(id == NULL); - PyObject *mod = get_module_from_type(Py_TYPE(self)); - if (mod == NULL) { - return NULL; - } - (void)handle_channel_error(err, mod, cid->id); - Py_DECREF(mod); - return NULL; + if (exc->name != NULL) { + if (exc->msg != NULL) { + PyErr_Format(wrapperclass, "%s: %s", exc->name, exc->msg); + } + else { + PyErr_SetString(wrapperclass, exc->name); } - assert(id != NULL); - return id; } - - if (cid->end == CHANNEL_SEND) { - return PyUnicode_InternFromString("send"); + else if (exc->msg != NULL) { + PyErr_SetString(wrapperclass, exc->msg); } - if (cid->end == CHANNEL_RECV) { - return PyUnicode_InternFromString("recv"); + else { + PyErr_SetNone(wrapperclass); } - return PyUnicode_InternFromString("both"); } -static int _channelid_end_send = CHANNEL_SEND; -static int _channelid_end_recv = CHANNEL_RECV; - -static PyGetSetDef channelid_getsets[] = { - {"end", (getter)channelid_end, NULL, - PyDoc_STR("'send', 'recv', or 'both'")}, - {"send", (getter)channelid_end, NULL, - PyDoc_STR("the 'send' end of the channel"), &_channelid_end_send}, - {"recv", (getter)channelid_end, NULL, - PyDoc_STR("the 'recv' end of the channel"), &_channelid_end_recv}, - {NULL} -}; - -PyDoc_STRVAR(channelid_doc, -"A channel ID identifies a channel and may be used as an int."); - -static PyType_Slot ChannelIDType_slots[] = { - {Py_tp_dealloc, (destructor)channelid_dealloc}, - {Py_tp_doc, (void *)channelid_doc}, - {Py_tp_repr, (reprfunc)channelid_repr}, - {Py_tp_str, (reprfunc)channelid_str}, - {Py_tp_hash, channelid_hash}, - {Py_tp_richcompare, channelid_richcompare}, - {Py_tp_getset, channelid_getsets}, - // number slots - {Py_nb_int, (unaryfunc)channelid_int}, - {Py_nb_index, (unaryfunc)channelid_int}, - {0, NULL}, -}; - -static PyType_Spec ChannelIDType_spec = { - .name = "_xxsubinterpreters.ChannelID", - .basicsize = sizeof(channelid), - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | - Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE), - .slots = ChannelIDType_slots, -}; - /* interpreter-specific code ************************************************/ static int -interp_exceptions_init(PyObject *mod) +exceptions_init(PyObject *mod) { module_state *state = get_module_state(mod); if (state == NULL) { @@ -2145,24 +430,12 @@ _ensure_not_running(PyInterpreterState *interp) static int _run_script(PyInterpreterState *interp, const char *codestr, - _sharedns *shared, int needs_import, - _sharedexception **exc) + _sharedns *shared, _sharedexception **exc) { PyObject *exctype = NULL; PyObject *excval = NULL; PyObject *tb = NULL; - if (needs_import) { - // It might not have been imported yet in the current interpreter. - // However, it will (almost) always have been imported already - // in the main interpreter. - PyObject *mod = PyImport_ImportModule(MODULE_NAME); - if (mod == NULL) { - goto error; - } - Py_DECREF(mod); - } - PyObject *main_mod = _PyInterpreterState_GetMainModule(interp); if (main_mod == NULL) { goto error; @@ -2223,9 +496,7 @@ _run_script_in_interpreter(PyObject *mod, PyInterpreterState *interp, } module_state *state = get_module_state(mod); - int needs_import = 0; - _sharedns *shared = _get_shared_ns(shareables, state->ChannelIDType, - &needs_import); + _sharedns *shared = _get_shared_ns(shareables); if (shared == NULL && PyErr_Occurred()) { return -1; } @@ -2241,7 +512,7 @@ _run_script_in_interpreter(PyObject *mod, PyInterpreterState *interp, // Run the script. _sharedexception *exc = NULL; - int result = _run_script(interp, codestr, shared, needs_import, &exc); + int result = _run_script(interp, codestr, shared, &exc); // Switch back. if (save_tstate != NULL) { @@ -2269,50 +540,6 @@ _run_script_in_interpreter(PyObject *mod, PyInterpreterState *interp, /* module level code ********************************************************/ -/* globals is the process-global state for the module. It holds all - the data that we need to share between interpreters, so it cannot - hold PyObject values. */ -static struct globals { - int module_count; - _channels channels; -} _globals = {0}; - -static int -_globals_init(void) -{ - // XXX This isn't thread-safe. - _globals.module_count++; - if (_globals.module_count > 1) { - // Already initialized. - return 0; - } - - assert(_globals.channels.mutex == NULL); - PyThread_type_lock mutex = PyThread_allocate_lock(); - if (mutex == NULL) { - return ERR_CHANNELS_MUTEX_INIT; - } - _channels_init(&_globals.channels, mutex); - return 0; -} - -static void -_globals_fini(void) -{ - // XXX This isn't thread-safe. - _globals.module_count--; - if (_globals.module_count > 0) { - return; - } - - _channels_fini(&_globals.channels); -} - -static _channels * -_global_channels(void) { - return &_globals.channels; -} - static PyObject * interp_create(PyObject *self, PyObject *args, PyObject *kwds) { @@ -2578,358 +805,6 @@ PyDoc_STRVAR(is_running_doc, \n\ Return whether or not the identified interpreter is running."); -static PyObject * -channel_create(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - int64_t cid = _channel_create(&_globals.channels); - if (cid < 0) { - (void)handle_channel_error(-1, self, cid); - return NULL; - } - module_state *state = get_module_state(self); - if (state == NULL) { - return NULL; - } - PyObject *id = NULL; - int err = newchannelid(state->ChannelIDType, cid, 0, - &_globals.channels, 0, 0, - (channelid **)&id); - if (handle_channel_error(err, self, cid)) { - assert(id == NULL); - err = _channel_destroy(&_globals.channels, cid); - if (handle_channel_error(err, self, cid)) { - // XXX issue a warning? - } - return NULL; - } - assert(id != NULL); - assert(((channelid *)id)->channels != NULL); - return id; -} - -PyDoc_STRVAR(channel_create_doc, -"channel_create() -> cid\n\ -\n\ -Create a new cross-interpreter channel and return a unique generated ID."); - -static PyObject * -channel_destroy(PyObject *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"cid", NULL}; - int64_t cid; - struct channel_id_converter_data cid_data = { - .module = self, - }; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:channel_destroy", kwlist, - channel_id_converter, &cid_data)) { - return NULL; - } - cid = cid_data.cid; - - int err = _channel_destroy(&_globals.channels, cid); - if (handle_channel_error(err, self, cid)) { - return NULL; - } - Py_RETURN_NONE; -} - -PyDoc_STRVAR(channel_destroy_doc, -"channel_destroy(cid)\n\ -\n\ -Close and finalize the channel. Afterward attempts to use the channel\n\ -will behave as though it never existed."); - -static PyObject * -channel_list_all(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - int64_t count = 0; - int64_t *cids = _channels_list_all(&_globals.channels, &count); - if (cids == NULL) { - if (count == 0) { - return PyList_New(0); - } - return NULL; - } - PyObject *ids = PyList_New((Py_ssize_t)count); - if (ids == NULL) { - goto finally; - } - module_state *state = get_module_state(self); - if (state == NULL) { - Py_DECREF(ids); - ids = NULL; - goto finally; - } - int64_t *cur = cids; - for (int64_t i=0; i < count; cur++, i++) { - PyObject *id = NULL; - int err = newchannelid(state->ChannelIDType, *cur, 0, - &_globals.channels, 0, 0, - (channelid **)&id); - if (handle_channel_error(err, self, *cur)) { - assert(id == NULL); - Py_SETREF(ids, NULL); - break; - } - assert(id != NULL); - PyList_SET_ITEM(ids, (Py_ssize_t)i, id); - } - -finally: - PyMem_Free(cids); - return ids; -} - -PyDoc_STRVAR(channel_list_all_doc, -"channel_list_all() -> [cid]\n\ -\n\ -Return the list of all IDs for active channels."); - -static PyObject * -channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"cid", "send", NULL}; - int64_t cid; /* Channel ID */ - struct channel_id_converter_data cid_data = { - .module = self, - }; - int send = 0; /* Send or receive end? */ - int64_t id; - PyObject *ids, *id_obj; - PyInterpreterState *interp; - - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "O&$p:channel_list_interpreters", - kwlist, channel_id_converter, &cid_data, &send)) { - return NULL; - } - cid = cid_data.cid; - - ids = PyList_New(0); - if (ids == NULL) { - goto except; - } - - interp = PyInterpreterState_Head(); - while (interp != NULL) { - id = PyInterpreterState_GetID(interp); - assert(id >= 0); - int res = _channel_is_associated(&_globals.channels, cid, id, send); - if (res < 0) { - (void)handle_channel_error(res, self, cid); - goto except; - } - if (res) { - id_obj = _PyInterpreterState_GetIDObject(interp); - if (id_obj == NULL) { - goto except; - } - res = PyList_Insert(ids, 0, id_obj); - Py_DECREF(id_obj); - if (res < 0) { - goto except; - } - } - interp = PyInterpreterState_Next(interp); - } - - goto finally; - -except: - Py_CLEAR(ids); - -finally: - return ids; -} - -PyDoc_STRVAR(channel_list_interpreters_doc, -"channel_list_interpreters(cid, *, send) -> [id]\n\ -\n\ -Return the list of all interpreter IDs associated with an end of the channel.\n\ -\n\ -The 'send' argument should be a boolean indicating whether to use the send or\n\ -receive end."); - - -static PyObject * -channel_send(PyObject *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"cid", "obj", NULL}; - int64_t cid; - struct channel_id_converter_data cid_data = { - .module = self, - }; - PyObject *obj; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O:channel_send", kwlist, - channel_id_converter, &cid_data, &obj)) { - return NULL; - } - cid = cid_data.cid; - - int err = _channel_send(&_globals.channels, cid, obj); - if (handle_channel_error(err, self, cid)) { - return NULL; - } - Py_RETURN_NONE; -} - -PyDoc_STRVAR(channel_send_doc, -"channel_send(cid, obj)\n\ -\n\ -Add the object's data to the channel's queue."); - -static PyObject * -channel_recv(PyObject *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"cid", "default", NULL}; - int64_t cid; - struct channel_id_converter_data cid_data = { - .module = self, - }; - PyObject *dflt = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O:channel_recv", kwlist, - channel_id_converter, &cid_data, &dflt)) { - return NULL; - } - cid = cid_data.cid; - - PyObject *obj = NULL; - int err = _channel_recv(&_globals.channels, cid, &obj); - if (handle_channel_error(err, self, cid)) { - return NULL; - } - Py_XINCREF(dflt); - if (obj == NULL) { - // Use the default. - if (dflt == NULL) { - (void)handle_channel_error(ERR_CHANNEL_EMPTY, self, cid); - return NULL; - } - obj = Py_NewRef(dflt); - } - Py_XDECREF(dflt); - return obj; -} - -PyDoc_STRVAR(channel_recv_doc, -"channel_recv(cid, [default]) -> obj\n\ -\n\ -Return a new object from the data at the front of the channel's queue.\n\ -\n\ -If there is nothing to receive then raise ChannelEmptyError, unless\n\ -a default value is provided. In that case return it."); - -static PyObject * -channel_close(PyObject *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"cid", "send", "recv", "force", NULL}; - int64_t cid; - struct channel_id_converter_data cid_data = { - .module = self, - }; - int send = 0; - int recv = 0; - int force = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O&|$ppp:channel_close", kwlist, - channel_id_converter, &cid_data, - &send, &recv, &force)) { - return NULL; - } - cid = cid_data.cid; - - int err = _channel_close(&_globals.channels, cid, send-recv, force); - if (handle_channel_error(err, self, cid)) { - return NULL; - } - Py_RETURN_NONE; -} - -PyDoc_STRVAR(channel_close_doc, -"channel_close(cid, *, send=None, recv=None, force=False)\n\ -\n\ -Close the channel for all interpreters.\n\ -\n\ -If the channel is empty then the keyword args are ignored and both\n\ -ends are immediately closed. Otherwise, if 'force' is True then\n\ -all queued items are released and both ends are immediately\n\ -closed.\n\ -\n\ -If the channel is not empty *and* 'force' is False then following\n\ -happens:\n\ -\n\ - * recv is True (regardless of send):\n\ - - raise ChannelNotEmptyError\n\ - * recv is None and send is None:\n\ - - raise ChannelNotEmptyError\n\ - * send is True and recv is not True:\n\ - - fully close the 'send' end\n\ - - close the 'recv' end to interpreters not already receiving\n\ - - fully close it once empty\n\ -\n\ -Closing an already closed channel results in a ChannelClosedError.\n\ -\n\ -Once the channel's ID has no more ref counts in any interpreter\n\ -the channel will be destroyed."); - -static PyObject * -channel_release(PyObject *self, PyObject *args, PyObject *kwds) -{ - // Note that only the current interpreter is affected. - static char *kwlist[] = {"cid", "send", "recv", "force", NULL}; - int64_t cid; - struct channel_id_converter_data cid_data = { - .module = self, - }; - int send = 0; - int recv = 0; - int force = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O&|$ppp:channel_release", kwlist, - channel_id_converter, &cid_data, - &send, &recv, &force)) { - return NULL; - } - cid = cid_data.cid; - if (send == 0 && recv == 0) { - send = 1; - recv = 1; - } - - // XXX Handle force is True. - // XXX Fix implicit release. - - int err = _channel_drop(&_globals.channels, cid, send, recv); - if (handle_channel_error(err, self, cid)) { - return NULL; - } - Py_RETURN_NONE; -} - -PyDoc_STRVAR(channel_release_doc, -"channel_release(cid, *, send=None, recv=None, force=True)\n\ -\n\ -Close the channel for the current interpreter. 'send' and 'recv'\n\ -(bool) may be used to indicate the ends to close. By default both\n\ -ends are closed. Closing an already closed end is a noop."); - -static PyObject * -channel__channel_id(PyObject *self, PyObject *args, PyObject *kwds) -{ - module_state *state = get_module_state(self); - if (state == NULL) { - return NULL; - } - PyTypeObject *cls = state->ChannelIDType; - PyObject *mod = get_module_from_owned_type(cls); - if (mod == NULL) { - return NULL; - } - PyObject *cid = _channelid_new(mod, cls, args, kwds); - Py_DECREF(mod); - return cid; -} - static PyMethodDef module_functions[] = { {"create", _PyCFunction_CAST(interp_create), METH_VARARGS | METH_KEYWORDS, create_doc}, @@ -2941,6 +816,7 @@ static PyMethodDef module_functions[] = { METH_NOARGS, get_current_doc}, {"get_main", interp_get_main, METH_NOARGS, get_main_doc}, + {"is_running", _PyCFunction_CAST(interp_is_running), METH_VARARGS | METH_KEYWORDS, is_running_doc}, {"run_string", _PyCFunction_CAST(interp_run_string), @@ -2949,25 +825,6 @@ static PyMethodDef module_functions[] = { {"is_shareable", _PyCFunction_CAST(object_is_shareable), METH_VARARGS | METH_KEYWORDS, is_shareable_doc}, - {"channel_create", channel_create, - METH_NOARGS, channel_create_doc}, - {"channel_destroy", _PyCFunction_CAST(channel_destroy), - METH_VARARGS | METH_KEYWORDS, channel_destroy_doc}, - {"channel_list_all", channel_list_all, - METH_NOARGS, channel_list_all_doc}, - {"channel_list_interpreters", _PyCFunction_CAST(channel_list_interpreters), - METH_VARARGS | METH_KEYWORDS, channel_list_interpreters_doc}, - {"channel_send", _PyCFunction_CAST(channel_send), - METH_VARARGS | METH_KEYWORDS, channel_send_doc}, - {"channel_recv", _PyCFunction_CAST(channel_recv), - METH_VARARGS | METH_KEYWORDS, channel_recv_doc}, - {"channel_close", _PyCFunction_CAST(channel_close), - METH_VARARGS | METH_KEYWORDS, channel_close_doc}, - {"channel_release", _PyCFunction_CAST(channel_release), - METH_VARARGS | METH_KEYWORDS, channel_release_doc}, - {"_channel_id", _PyCFunction_CAST(channel__channel_id), - METH_VARARGS | METH_KEYWORDS, NULL}, - {NULL, NULL} /* sentinel */ }; @@ -2981,29 +838,8 @@ The 'interpreters' module provides a more convenient interface."); static int module_exec(PyObject *mod) { - if (_globals_init() != 0) { - return -1; - } - - module_state *state = get_module_state(mod); - if (state == NULL) { - goto error; - } - /* Add exception types */ - if (interp_exceptions_init(mod) != 0) { - goto error; - } - if (channel_exceptions_init(mod) != 0) { - goto error; - } - - /* Add other types */ - - // ChannelID - state->ChannelIDType = add_new_type( - mod, &ChannelIDType_spec, _channelid_shared); - if (state->ChannelIDType == NULL) { + if (exceptions_init(mod) != 0) { goto error; } @@ -3015,8 +851,6 @@ module_exec(PyObject *mod) return 0; error: - (void)_PyCrossInterpreterData_UnregisterClass(state->ChannelIDType); - _globals_fini(); return -1; } @@ -3049,7 +883,6 @@ module_free(void *mod) module_state *state = get_module_state(mod); assert(state != NULL); clear_module_state(state); - _globals_fini(); } static struct PyModuleDef moduledef = { diff --git a/PC/config.c b/PC/config.c index 9d900c78e40d00..cdb5db23c4ae49 100644 --- a/PC/config.c +++ b/PC/config.c @@ -37,6 +37,7 @@ extern PyObject* PyInit__weakref(void); /* XXX: These two should really be extracted to standalone extensions. */ extern PyObject* PyInit_xxsubtype(void); extern PyObject* PyInit__xxsubinterpreters(void); +extern PyObject* PyInit__xxinterpchannels(void); extern PyObject* PyInit__random(void); extern PyObject* PyInit_itertools(void); extern PyObject* PyInit__collections(void); @@ -134,6 +135,7 @@ struct _inittab _PyImport_Inittab[] = { {"xxsubtype", PyInit_xxsubtype}, {"_xxsubinterpreters", PyInit__xxsubinterpreters}, + {"_xxinterpchannels", PyInit__xxinterpchannels}, #ifdef _Py_HAVE_ZLIB {"zlib", PyInit_zlib}, #endif diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index d3bd5b378e0dd0..397d22abe23503 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -418,6 +418,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index c1b531fd818a97..bcbedcc3235b3e 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -1322,6 +1322,9 @@ Modules + + Modules + Parser diff --git a/Tools/build/generate_stdlib_module_names.py b/Tools/build/generate_stdlib_module_names.py index c8a23f41fdd475..d15e5e2d5450d7 100644 --- a/Tools/build/generate_stdlib_module_names.py +++ b/Tools/build/generate_stdlib_module_names.py @@ -36,6 +36,7 @@ '_testmultiphase', '_testsinglephase', '_xxsubinterpreters', + '_xxinterpchannels', '_xxtestfuzz', 'idlelib.idle_test', 'test', diff --git a/configure b/configure index 8b707cda62129f..aef8103085bc20 100755 --- a/configure +++ b/configure @@ -752,6 +752,8 @@ MODULE__MULTIPROCESSING_FALSE MODULE__MULTIPROCESSING_TRUE MODULE__ZONEINFO_FALSE MODULE__ZONEINFO_TRUE +MODULE__XXINTERPCHANNELS_FALSE +MODULE__XXINTERPCHANNELS_TRUE MODULE__XXSUBINTERPRETERS_FALSE MODULE__XXSUBINTERPRETERS_TRUE MODULE__TYPING_FALSE @@ -25615,6 +25617,7 @@ case $ac_sys_system in #( py_cv_module__scproxy=n/a py_cv_module__tkinter=n/a py_cv_module__xxsubinterpreters=n/a + py_cv_module__xxinterpchannels=n/a py_cv_module_grp=n/a py_cv_module_nis=n/a py_cv_module_ossaudiodev=n/a @@ -26057,6 +26060,26 @@ fi +fi + + + if test "$py_cv_module__xxinterpchannels" != "n/a"; then : + py_cv_module__xxinterpchannels=yes +fi + if test "$py_cv_module__xxinterpchannels" = yes; then + MODULE__XXINTERPCHANNELS_TRUE= + MODULE__XXINTERPCHANNELS_FALSE='#' +else + MODULE__XXINTERPCHANNELS_TRUE='#' + MODULE__XXINTERPCHANNELS_FALSE= +fi + + as_fn_append MODULE_BLOCK "MODULE__XXINTERPCHANNELS_STATE=$py_cv_module__xxinterpchannels$as_nl" + if test "x$py_cv_module__xxinterpchannels" = xyes; then : + + + + fi @@ -28236,6 +28259,10 @@ if test -z "${MODULE__XXSUBINTERPRETERS_TRUE}" && test -z "${MODULE__XXSUBINTERP as_fn_error $? "conditional \"MODULE__XXSUBINTERPRETERS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${MODULE__XXINTERPCHANNELS_TRUE}" && test -z "${MODULE__XXINTERPCHANNELS_FALSE}"; then + as_fn_error $? "conditional \"MODULE__XXINTERPCHANNELS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${MODULE__ZONEINFO_TRUE}" && test -z "${MODULE__ZONEINFO_FALSE}"; then as_fn_error $? "conditional \"MODULE__ZONEINFO\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/configure.ac b/configure.ac index 5eee4586680dbb..010bca855f0059 100644 --- a/configure.ac +++ b/configure.ac @@ -7017,6 +7017,7 @@ AS_CASE([$ac_sys_system], [_scproxy], [_tkinter], [_xxsubinterpreters], + [_xxinterpchannels], [grp], [nis], [ossaudiodev], @@ -7135,6 +7136,7 @@ PY_STDLIB_MOD_SIMPLE([select]) PY_STDLIB_MOD_SIMPLE([_struct]) PY_STDLIB_MOD_SIMPLE([_typing]) PY_STDLIB_MOD_SIMPLE([_xxsubinterpreters]) +PY_STDLIB_MOD_SIMPLE([_xxinterpchannels]) PY_STDLIB_MOD_SIMPLE([_zoneinfo]) dnl multiprocessing modules From f11a3d1ebe0c78f8c159c63a37022b9b96f720dd Mon Sep 17 00:00:00 2001 From: Max Bachmann Date: Sat, 4 Feb 2023 04:33:28 +0100 Subject: [PATCH 075/225] Add missing `versionadded` directive for `PyCode_Addr2Location` (#101347) --- Doc/c-api/code.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst index a6eb86f1a0b514..ae75d68901d7ba 100644 --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -77,6 +77,8 @@ bound into a function. Returns ``1`` if the function succeeds and 0 otherwise. + .. versionadded:: 3.11 + .. c:function:: PyObject* PyCode_GetCode(PyCodeObject *co) Equivalent to the Python code ``getattr(co, 'co_code')``. From cef9de62b8bf5e2d11d5a074012dfa81dc4ea935 Mon Sep 17 00:00:00 2001 From: Furkan Onder Date: Sat, 4 Feb 2023 03:49:29 +0000 Subject: [PATCH 076/225] GH-56426: Add cross-reference to the documentation for faulthandler, traceback, and pdb. (#101157) Co-authored-by: C.A.M. Gerlach --- Doc/library/faulthandler.rst | 10 +++++++++- Doc/library/pdb.rst | 9 +++++++++ Doc/library/traceback.rst | 9 ++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Doc/library/faulthandler.rst b/Doc/library/faulthandler.rst index 07a7489941442b..f64dfeb5e081c7 100644 --- a/Doc/library/faulthandler.rst +++ b/Doc/library/faulthandler.rst @@ -43,6 +43,13 @@ Python is deadlocked. The :ref:`Python Development Mode ` calls :func:`faulthandler.enable` at Python startup. +.. seealso:: + + Module :mod:`pdb` + Interactive source code debugger for Python programs. + + Module :mod:`traceback` + Standard interface to extract, format and print stack traces of Python programs. Dumping the traceback --------------------- @@ -52,6 +59,8 @@ Dumping the traceback Dump the tracebacks of all threads into *file*. If *all_threads* is ``False``, dump only the current thread. + .. seealso:: :func:`traceback.print_tb`, which can be used to print a traceback object. + .. versionchanged:: 3.5 Added support for passing file descriptor to this function. @@ -178,4 +187,3 @@ handler: File "/home/python/cpython/Lib/ctypes/__init__.py", line 486 in string_at File "", line 1 in Segmentation fault - diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst index 383c3adcf289d5..4ae12a5d03a78d 100644 --- a/Doc/library/pdb.rst +++ b/Doc/library/pdb.rst @@ -27,6 +27,15 @@ The debugger is extensible -- it is actually defined as the class :class:`Pdb`. This is currently undocumented but easily understood by reading the source. The extension interface uses the modules :mod:`bdb` and :mod:`cmd`. +.. seealso:: + + Module :mod:`faulthandler` + Used to dump Python tracebacks explicitly, on a fault, after a timeout, + or on a user signal. + + Module :mod:`traceback` + Standard interface to extract, format and print stack traces of Python programs. + The debugger's prompt is ``(Pdb)``. Typical usage to run a program under control of the debugger is:: diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst index f8c1eabadacf9f..69818baf184d7c 100644 --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -20,8 +20,15 @@ The module uses traceback objects --- this is the object type that is stored in the :data:`sys.last_traceback` variable and returned as the third item from :func:`sys.exc_info`. -The module defines the following functions: +.. seealso:: + + Module :mod:`faulthandler` + Used to dump Python tracebacks explicitly, on a fault, after a timeout, or on a user signal. + Module :mod:`pdb` + Interactive source code debugger for Python programs. + +The module defines the following functions: .. function:: print_tb(tb, limit=None, file=None) From 144aaa74bbd77aee822ee92344744dbb05aa2f30 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sat, 4 Feb 2023 16:55:31 +0900 Subject: [PATCH 077/225] =?UTF-8?q?gh-101282:=20Update=20BOLT=20--split-fu?= =?UTF-8?q?nctions=20flag=20not=20to=20use=20deprecated=20u=E2=80=A6=20(gh?= =?UTF-8?q?-101557)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gh-101282: Update BOLT --split-functions flag not to use deprecated usage --- Makefile.pre.in | 2 +- .../next/Build/2023-02-04-06-59-07.gh-issue-101282.7sQz5l.rst | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Build/2023-02-04-06-59-07.gh-issue-101282.7sQz5l.rst diff --git a/Makefile.pre.in b/Makefile.pre.in index 618bb7b5f7307d..f2f9371e8ac04d 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -647,7 +647,7 @@ bolt-opt: @PREBOLT_RULE@ @LLVM_BOLT@ ./$(BUILDPYTHON) -instrument -instrumentation-file-append-pid -instrumentation-file=$(abspath $(BUILDPYTHON).bolt) -o $(BUILDPYTHON).bolt_inst ./$(BUILDPYTHON).bolt_inst $(PROFILE_TASK) || true @MERGE_FDATA@ $(BUILDPYTHON).*.fdata > $(BUILDPYTHON).fdata - @LLVM_BOLT@ ./$(BUILDPYTHON) -o $(BUILDPYTHON).bolt -data=$(BUILDPYTHON).fdata -update-debug-sections -reorder-blocks=ext-tsp -reorder-functions=hfsort+ -split-functions=3 -icf=1 -inline-all -split-eh -reorder-functions-use-hot-size -peepholes=all -jump-tables=aggressive -inline-ap -indirect-call-promotion=all -dyno-stats -use-gnu-stack -frame-opt=hot + @LLVM_BOLT@ ./$(BUILDPYTHON) -o $(BUILDPYTHON).bolt -data=$(BUILDPYTHON).fdata -update-debug-sections -reorder-blocks=ext-tsp -reorder-functions=hfsort+ -split-functions -icf=1 -inline-all -split-eh -reorder-functions-use-hot-size -peepholes=all -jump-tables=aggressive -inline-ap -indirect-call-promotion=all -dyno-stats -use-gnu-stack -frame-opt=hot rm -f *.fdata rm -f $(BUILDPYTHON).bolt_inst mv $(BUILDPYTHON).bolt $(BUILDPYTHON) diff --git a/Misc/NEWS.d/next/Build/2023-02-04-06-59-07.gh-issue-101282.7sQz5l.rst b/Misc/NEWS.d/next/Build/2023-02-04-06-59-07.gh-issue-101282.7sQz5l.rst new file mode 100644 index 00000000000000..49d48564667349 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-02-04-06-59-07.gh-issue-101282.7sQz5l.rst @@ -0,0 +1,2 @@ +Update BOLT configration not to use depreacted usage of ``--split +functions``. Patch by Dong-hee Na. From a89e6713c4de99d4be5a1304b134e57a24ab10ac Mon Sep 17 00:00:00 2001 From: Ruben Vorderman Date: Sat, 4 Feb 2023 21:07:30 +0100 Subject: [PATCH 078/225] gh-101322: Ensure test_zlib.ZlibDecompressorTest runs, fix errors in ZlibDecompressor (#101323) * Ensure test_zlib.ZlibDecompressorTest actually runs, fix errors in ZlibDecompressor. --- Lib/test/test_zlib.py | 9 +++++++-- .../2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst | 2 ++ Modules/zlibmodule.c | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index ae54f6c46891c6..3dac70eb12852c 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -944,13 +944,18 @@ def choose_lines(source, number, seed=None, generator=random): """ -class ZlibDecompressorTest(): +class ZlibDecompressorTest(unittest.TestCase): # Test adopted from test_bz2.py TEXT = HAMLET_SCENE DATA = zlib.compress(HAMLET_SCENE) BAD_DATA = b"Not a valid deflate block" + BIG_TEXT = DATA * ((128 * 1024 // len(DATA)) + 1) + BIG_DATA = zlib.compress(BIG_TEXT) + def test_Constructor(self): - self.assertRaises(TypeError, zlib._ZlibDecompressor, 42) + self.assertRaises(TypeError, zlib._ZlibDecompressor, "ASDA") + self.assertRaises(TypeError, zlib._ZlibDecompressor, -15, "notbytes") + self.assertRaises(TypeError, zlib._ZlibDecompressor, -15, b"bytes", 5) def testDecompress(self): zlibd = zlib._ZlibDecompressor() diff --git a/Misc/NEWS.d/next/Library/2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst b/Misc/NEWS.d/next/Library/2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst new file mode 100644 index 00000000000000..f8419e130dbcd7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst @@ -0,0 +1,2 @@ +Fix a bug where errors where not thrown by zlib._ZlibDecompressor if +encountered during decompressing. diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c index 1cdfd01320288b..e2f7dbaca87a9f 100644 --- a/Modules/zlibmodule.c +++ b/Modules/zlibmodule.c @@ -1519,6 +1519,7 @@ decompress_buf(ZlibDecompressor *self, Py_ssize_t max_length) } } else if (err != Z_OK && err != Z_BUF_ERROR) { zlib_error(state, self->zst, err, "while decompressing data"); + goto error; } self->avail_in_real += self->zst.avail_in; From 5a2b984568f72f0d7ff7c7b4ee8ce31af9fd1b7e Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 4 Feb 2023 17:54:44 -0600 Subject: [PATCH 079/225] GH-100485: Create an alternative code path when an accurate fma() implementation is not available (#101567) --- Lib/test/test_math.py | 5 +++++ Modules/mathmodule.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 2c84e55ab45c57..8d849045b2d11f 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1450,6 +1450,11 @@ def Trial(dotfunc, c, n): n = 20 # Length of vectors c = 1e30 # Target condition number + # If the following test fails, it means that the C math library + # implementation of fma() is not compliant with the C99 standard + # and is inaccurate. To solve this problem, make a new build + # with the symbol UNRELIABLE_FMA defined. That will enable a + # slower but accurate code path that avoids the fma() call. relative_err = median(Trial(math.sumprod, c, n) for i in range(times)) self.assertLess(relative_err, 1e-16) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index e6cdb3bae1ecff..654336d6d9f4bc 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2851,6 +2851,8 @@ dl_sum(double a, double b) return (DoubleLength) {x, y}; } +#ifndef UNRELIABLE_FMA + static DoubleLength dl_mul(double x, double y) { @@ -2860,6 +2862,47 @@ dl_mul(double x, double y) return (DoubleLength) {z, zz}; } +#else + +/* + The default implementation of dl_mul() depends on the C math library + having an accurate fma() function as required by § 7.12.13.1 of the + C99 standard. + + The UNRELIABLE_FMA option is provided as a slower but accurate + alternative for builds where the fma() function is found wanting. + The speed penalty may be modest (17% slower on an Apple M1 Max), + so don't hesitate to enable this build option. + + The algorithms are from the T. J. Dekker paper: + A Floating-Point Technique for Extending the Available Precision + https://csclub.uwaterloo.ca/~pbarfuss/dekker1971.pdf +*/ + +static DoubleLength +dl_split(double x) { + // Dekker (5.5) and (5.6). + double t = x * 134217729.0; // Veltkamp constant = 2.0 ** 27 + 1 + double hi = t - (t - x); + double lo = x - hi; + return (DoubleLength) {hi, lo}; +} + +static DoubleLength +dl_mul(double x, double y) +{ + // Dekker (5.12) and mul12() + DoubleLength xx = dl_split(x); + DoubleLength yy = dl_split(y); + double p = xx.hi * yy.hi; + double q = xx.hi * yy.lo + xx.lo * yy.hi; + double z = p + q; + double zz = p - z + q + xx.lo * yy.lo; + return (DoubleLength) {z, zz}; +} + +#endif + typedef struct { double hi; double lo; double tiny; } TripleLength; static const TripleLength tl_zero = {0.0, 0.0, 0.0}; From ddd619cffa457776a22f224b7111bd39de289d66 Mon Sep 17 00:00:00 2001 From: Alexander Belopolsky Date: Sun, 5 Feb 2023 11:14:15 +0400 Subject: [PATCH 080/225] Fix detection of presence of time.tzset (gh-101539) (#101540) Resolves gh-101539 Related to gh-31898 --- Lib/test/datetimetester.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 6a1df174a1b972..570f803918c1ef 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -6173,7 +6173,7 @@ def test_gaps(self): self.assertEqual(ldt.fold, 0) @unittest.skipUnless( - hasattr(time, "tzset"), "time module has no attribute tzset" + hasattr(_time, "tzset"), "time module has no attribute tzset" ) def test_system_transitions(self): if ('Riyadh8' in self.zonename or From 6e4a521c2ab84c082ad71e540b045699f0dbbc11 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Sun, 5 Feb 2023 01:45:07 -0800 Subject: [PATCH 081/225] Add missing preposition in argparse docs (#101548) --- Doc/library/argparse.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index 475cac70291e9a..dbaa5d0d9b995b 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -31,7 +31,7 @@ Core Functionality The :mod:`argparse` module's support for command-line interfaces is built around an instance of :class:`argparse.ArgumentParser`. It is a container for -argument specifications and has options that apply the parser as whole:: +argument specifications and has options that apply to the parser as whole:: parser = argparse.ArgumentParser( prog = 'ProgramName', From 9b60ee976a6b66fe96c2d39051612999c26561e5 Mon Sep 17 00:00:00 2001 From: busywhitespace Date: Sun, 5 Feb 2023 10:55:36 +0100 Subject: [PATCH 082/225] gh-101221: Add options in the documentation of timeit command (#101222) --- Doc/library/timeit.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/timeit.rst b/Doc/library/timeit.rst index 5437704cec337b..32ab565aba0c08 100644 --- a/Doc/library/timeit.rst +++ b/Doc/library/timeit.rst @@ -206,7 +206,7 @@ Command-Line Interface When called as a program from the command line, the following form is used:: - python -m timeit [-n N] [-r N] [-u U] [-s S] [-h] [statement ...] + python -m timeit [-n N] [-r N] [-u U] [-s S] [-p] [-v] [-h] [statement ...] Where the following options are understood: From 39017e04b55d4c110787551dc9a0cb753f27d700 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 5 Feb 2023 10:02:53 +0000 Subject: [PATCH 083/225] gh-101266: Fix __sizeof__ for subclasses of int (#101394) Fix the behaviour of the `__sizeof__` method (and hence the results returned by `sys.getsizeof`) for subclasses of `int`. Previously, `int` subclasses gave identical results to the `int` base class, ignoring the presence of the instance dictionary. * Issue: gh-101266 --- Lib/test/test_long.py | 39 +++++++++++++++++++ ...-01-28-13-11-52.gh-issue-101266.AxV3OF.rst | 1 + Objects/boolobject.c | 6 ++- Objects/longobject.c | 11 ++---- 4 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index 569ab15820e302..d299c34cec076d 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -1601,5 +1601,44 @@ def test_square(self): self.assertEqual(n**2, (1 << (2 * bitlen)) - (1 << (bitlen + 1)) + 1) + def test___sizeof__(self): + self.assertEqual(int.__itemsize__, sys.int_info.sizeof_digit) + + # Pairs (test_value, number of allocated digits) + test_values = [ + # We always allocate space for at least one digit, even for + # a value of zero; sys.getsizeof should reflect that. + (0, 1), + (1, 1), + (-1, 1), + (BASE-1, 1), + (1-BASE, 1), + (BASE, 2), + (-BASE, 2), + (BASE*BASE - 1, 2), + (BASE*BASE, 3), + ] + + for value, ndigits in test_values: + with self.subTest(value): + self.assertEqual( + value.__sizeof__(), + int.__basicsize__ + int.__itemsize__ * ndigits + ) + + # Same test for a subclass of int. + class MyInt(int): + pass + + self.assertEqual(MyInt.__itemsize__, sys.int_info.sizeof_digit) + + for value, ndigits in test_values: + with self.subTest(value): + self.assertEqual( + MyInt(value).__sizeof__(), + MyInt.__basicsize__ + MyInt.__itemsize__ * ndigits + ) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst new file mode 100644 index 00000000000000..51999bacb8de07 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst @@ -0,0 +1 @@ +Fix :func:`sys.getsizeof` reporting for :class:`int` subclasses. diff --git a/Objects/boolobject.c b/Objects/boolobject.c index 55b4a4077ab783..a035f463323823 100644 --- a/Objects/boolobject.c +++ b/Objects/boolobject.c @@ -4,6 +4,8 @@ #include "pycore_object.h" // _Py_FatalRefcountError() #include "pycore_runtime.h" // _Py_ID() +#include + /* We define bool_repr to return "False" or "True" */ static PyObject * @@ -153,8 +155,8 @@ bool_dealloc(PyObject* Py_UNUSED(ignore)) PyTypeObject PyBool_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "bool", - sizeof(struct _longobject), - 0, + offsetof(struct _longobject, long_value.ob_digit), /* tp_basicsize */ + sizeof(digit), /* tp_itemsize */ bool_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ diff --git a/Objects/longobject.c b/Objects/longobject.c index 65bf15648b07fb..8293f133bed213 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5882,13 +5882,10 @@ static Py_ssize_t int___sizeof___impl(PyObject *self) /*[clinic end generated code: output=3303f008eaa6a0a5 input=9b51620c76fc4507]*/ { - Py_ssize_t res; - - res = offsetof(PyLongObject, long_value.ob_digit) - /* using Py_MAX(..., 1) because we always allocate space for at least - one digit, even though the integer zero has a Py_SIZE of 0 */ - + Py_MAX(Py_ABS(Py_SIZE(self)), 1)*sizeof(digit); - return res; + /* using Py_MAX(..., 1) because we always allocate space for at least + one digit, even though the integer zero has a Py_SIZE of 0 */ + Py_ssize_t ndigits = Py_MAX(Py_ABS(Py_SIZE(self)), 1); + return Py_TYPE(self)->tp_basicsize + Py_TYPE(self)->tp_itemsize * ndigits; } /*[clinic input] From 19ac43629e6fd73f2dbf9fd5a0b97d791c28acc7 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Sun, 5 Feb 2023 11:30:44 +0000 Subject: [PATCH 084/225] gh-101570: Update bundled pip version to 23.0 (#101571) Update bundled pip version to 23.0 This is the current latest version of `pip`. --------- Co-authored-by: Pradyun Gedam Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> --- Lib/ensurepip/__init__.py | 2 +- ...none-any.whl => pip-23.0-py3-none-any.whl} | Bin 2051534 -> 2056044 bytes ...-02-04-21-01-49.gh-issue-101570.lbtUsD.rst | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) rename Lib/ensurepip/_bundled/{pip-22.3.1-py3-none-any.whl => pip-23.0-py3-none-any.whl} (73%) create mode 100644 Misc/NEWS.d/next/Library/2023-02-04-21-01-49.gh-issue-101570.lbtUsD.rst diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index 1a2f57c07ba341..052b7bca28ddd3 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -11,7 +11,7 @@ __all__ = ["version", "bootstrap"] _PACKAGE_NAMES = ('setuptools', 'pip') _SETUPTOOLS_VERSION = "65.5.0" -_PIP_VERSION = "22.3.1" +_PIP_VERSION = "23.0" _PROJECTS = [ ("setuptools", _SETUPTOOLS_VERSION, "py3"), ("pip", _PIP_VERSION, "py3"), diff --git a/Lib/ensurepip/_bundled/pip-22.3.1-py3-none-any.whl b/Lib/ensurepip/_bundled/pip-23.0-py3-none-any.whl similarity index 73% rename from Lib/ensurepip/_bundled/pip-22.3.1-py3-none-any.whl rename to Lib/ensurepip/_bundled/pip-23.0-py3-none-any.whl index c5b7753e757df23620ad5f039050e7dcd49fffb5..bb9aebf474cfe981dbf984bd9e36e23560111288 100644 GIT binary patch delta 520370 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 |%G3#nRz51y3_SSkD3X+Odirs*jJgznSlu-HY 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
    O0OJxAC|60&& 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 delta 515738 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

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

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

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

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

    s4$e@L4CjxI$3t(skZ-Ezo}tFOiyMGO_{z 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�UFpG;B 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 diff --git a/Misc/NEWS.d/next/Library/2023-02-04-21-01-49.gh-issue-101570.lbtUsD.rst b/Misc/NEWS.d/next/Library/2023-02-04-21-01-49.gh-issue-101570.lbtUsD.rst new file mode 100644 index 00000000000000..599edab6c07117 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-04-21-01-49.gh-issue-101570.lbtUsD.rst @@ -0,0 +1 @@ +Upgrade pip wheel bundled with ensurepip (pip 23.0) From 0672a6c23b2b72666e10d9c61fc025e66aad9c2b Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 5 Feb 2023 16:36:33 +0000 Subject: [PATCH 085/225] Revert "gh-89381: Fix invalid signatures of math/cmath.log (#101404)" (#101580) This reverts commit 0ef92d979311ba82d4c41b22ef38e12e1b08b13d. --- Doc/library/cmath.rst | 2 +- Doc/library/math.rst | 9 ++-- Lib/test/test_math.py | 1 - ...3-01-16-10-42-58.gh-issue-89381.lM2WL0.rst | 1 - Modules/clinic/cmathmodule.c.h | 9 ++-- Modules/clinic/mathmodule.c.h | 44 +++++++++++-------- Modules/cmathmodule.c | 9 ++-- Modules/mathmodule.c | 14 +++--- 8 files changed, 47 insertions(+), 42 deletions(-) delete mode 100644 Misc/NEWS.d/next/Library/2023-01-16-10-42-58.gh-issue-89381.lM2WL0.rst diff --git a/Doc/library/cmath.rst b/Doc/library/cmath.rst index c575b90e6461ed..28cd96b0e12da9 100644 --- a/Doc/library/cmath.rst +++ b/Doc/library/cmath.rst @@ -89,7 +89,7 @@ Power and logarithmic functions logarithms. -.. function:: log(x, base=None) +.. function:: log(x[, base]) Returns the logarithm of *x* to the given *base*. If the *base* is not specified, returns the natural logarithm of *x*. There is one branch cut, from 0 diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 9da22b6ad0562f..797f32408eac3d 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -393,12 +393,13 @@ Power and logarithmic functions .. versionadded:: 3.2 -.. function:: log(x, base=None) +.. function:: log(x[, base]) - Return the logarithm of *x* to the given *base*. + With one argument, return the natural logarithm of *x* (to base *e*). + + With two arguments, return the logarithm of *x* to the given *base*, + calculated as ``log(x)/log(base)``. - If the *base* is not specified, returns the natural - logarithm (base *e*) of *x*. .. function:: log1p(x) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 8d849045b2d11f..433161c2dd4145 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1146,7 +1146,6 @@ def testLog(self): self.ftest('log(1/e)', math.log(1/math.e), -1) self.ftest('log(1)', math.log(1), 0) self.ftest('log(e)', math.log(math.e), 1) - self.ftest('log(e, None)', math.log(math.e, None), 1) self.ftest('log(32,2)', math.log(32,2), 5) self.ftest('log(10**40, 10)', math.log(10**40, 10), 40) self.ftest('log(10**40, 10**20)', math.log(10**40, 10**20), 2) diff --git a/Misc/NEWS.d/next/Library/2023-01-16-10-42-58.gh-issue-89381.lM2WL0.rst b/Misc/NEWS.d/next/Library/2023-01-16-10-42-58.gh-issue-89381.lM2WL0.rst deleted file mode 100644 index 7bffe7d226e38a..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-16-10-42-58.gh-issue-89381.lM2WL0.rst +++ /dev/null @@ -1 +0,0 @@ -:func:`~math.log` and :func:`~cmath.log` support default base=None values. diff --git a/Modules/clinic/cmathmodule.c.h b/Modules/clinic/cmathmodule.c.h index bc91c20f373bcd..b1da9452c61db8 100644 --- a/Modules/clinic/cmathmodule.c.h +++ b/Modules/clinic/cmathmodule.c.h @@ -639,13 +639,12 @@ cmath_tanh(PyObject *module, PyObject *arg) } PyDoc_STRVAR(cmath_log__doc__, -"log($module, z, base=None, /)\n" +"log($module, z, base=, /)\n" "--\n" "\n" "log(z[, base]) -> the logarithm of z to the given base.\n" "\n" -"If the base is not specified or is None, returns the\n" -"natural logarithm (base e) of z."); +"If the base not specified, returns the natural logarithm (base e) of z."); #define CMATH_LOG_METHODDEF \ {"log", _PyCFunction_CAST(cmath_log), METH_FASTCALL, cmath_log__doc__}, @@ -658,7 +657,7 @@ cmath_log(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; Py_complex x; - PyObject *y_obj = Py_None; + PyObject *y_obj = NULL; if (!_PyArg_CheckPositional("log", nargs, 1, 2)) { goto exit; @@ -983,4 +982,4 @@ cmath_isclose(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec exit: return return_value; } -/*[clinic end generated code: output=2630f8740909a8f7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=0146c656e67f5d5f input=a9049054013a1b77]*/ diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 0d61fd1be38ddb..1f9725883b9820 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -187,37 +187,43 @@ math_modf(PyObject *module, PyObject *arg) } PyDoc_STRVAR(math_log__doc__, -"log($module, x, base=None, /)\n" -"--\n" -"\n" +"log(x, [base=math.e])\n" "Return the logarithm of x to the given base.\n" "\n" -"If the base is not specified or is None, returns the natural\n" -"logarithm (base e) of x."); +"If the base not specified, returns the natural logarithm (base e) of x."); #define MATH_LOG_METHODDEF \ - {"log", _PyCFunction_CAST(math_log), METH_FASTCALL, math_log__doc__}, + {"log", (PyCFunction)math_log, METH_VARARGS, math_log__doc__}, static PyObject * -math_log_impl(PyObject *module, PyObject *x, PyObject *base); +math_log_impl(PyObject *module, PyObject *x, int group_right_1, + PyObject *base); static PyObject * -math_log(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +math_log(PyObject *module, PyObject *args) { PyObject *return_value = NULL; PyObject *x; - PyObject *base = Py_None; + int group_right_1 = 0; + PyObject *base = NULL; - if (!_PyArg_CheckPositional("log", nargs, 1, 2)) { - goto exit; - } - x = args[0]; - if (nargs < 2) { - goto skip_optional; + switch (PyTuple_GET_SIZE(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O:log", &x)) { + goto exit; + } + break; + case 2: + if (!PyArg_ParseTuple(args, "OO:log", &x, &base)) { + goto exit; + } + group_right_1 = 1; + break; + default: + PyErr_SetString(PyExc_TypeError, "math.log requires 1 to 2 arguments"); + goto exit; } - base = args[1]; -skip_optional: - return_value = math_log_impl(module, x, base); + return_value = math_log_impl(module, x, group_right_1, base); exit: return return_value; @@ -948,4 +954,4 @@ math_ulp(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=afec63ebb0da709a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=899211ec70e4506c input=a9049054013a1b77]*/ diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 62caba031eda27..2038ac26e65857 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -952,24 +952,23 @@ cmath_tanh_impl(PyObject *module, Py_complex z) cmath.log z as x: Py_complex - base as y_obj: object = None + base as y_obj: object = NULL / log(z[, base]) -> the logarithm of z to the given base. -If the base is not specified or is None, returns the -natural logarithm (base e) of z. +If the base not specified, returns the natural logarithm (base e) of z. [clinic start generated code]*/ static PyObject * cmath_log_impl(PyObject *module, Py_complex x, PyObject *y_obj) -/*[clinic end generated code: output=4effdb7d258e0d94 input=e7db51859ebf70bf]*/ +/*[clinic end generated code: output=4effdb7d258e0d94 input=230ed3a71ecd000a]*/ { Py_complex y; errno = 0; x = c_log(x); - if (y_obj != Py_None) { + if (y_obj != NULL) { y = PyComplex_AsCComplex(y_obj); if (PyErr_Occurred()) { return NULL; diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 654336d6d9f4bc..992957e675a7a3 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2366,24 +2366,26 @@ loghelper(PyObject* arg, double (*func)(double)) math.log x: object - base: object = None + [ + base: object(c_default="NULL") = math.e + ] / Return the logarithm of x to the given base. -If the base is not specified or is None, returns the natural -logarithm (base e) of x. +If the base not specified, returns the natural logarithm (base e) of x. [clinic start generated code]*/ static PyObject * -math_log_impl(PyObject *module, PyObject *x, PyObject *base) -/*[clinic end generated code: output=1dead263cbb1e854 input=ef032cc9837943e1]*/ +math_log_impl(PyObject *module, PyObject *x, int group_right_1, + PyObject *base) +/*[clinic end generated code: output=7b5a39e526b73fc9 input=0f62d5726cbfebbd]*/ { PyObject *num, *den; PyObject *ans; num = loghelper(x, m_log); - if (num == NULL || base == Py_None) + if (num == NULL || base == NULL) return num; den = loghelper(base, m_log); From 90d85a9b4136aa1feb02f88aab614a3c29f20ed3 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 5 Feb 2023 17:10:53 +0000 Subject: [PATCH 086/225] gh-76961: Fix the PEP3118 format string for ctypes.Structure (#5561) The summary of this diff is that it: * adds a `_ctypes_alloc_format_padding` function to append strings like `37x` to a format string to indicate 37 padding bytes * removes the branches that amount to "give up on producing a valid format string if the struct is packed" * combines the resulting adjacent `if (isStruct) {`s now that neither is `if (isStruct && !isPacked) {` * invokes `_ctypes_alloc_format_padding` to add padding between structure fields, and after the last structure field. The computation used for the total size is unchanged from ctypes already used. This patch does not affect any existing aligment computation; all it does is use subtraction to deduce the amount of paddnig introduced by the existing code. --- Without this fix, it would never include padding bytes - an assumption that was only valid in the case when `_pack_` was set - and this case was explicitly not implemented. This should allow conversion from ctypes structs to numpy structs Fixes https://github.com/numpy/numpy/issues/10528 --- Doc/library/ctypes.rst | 1 + Lib/test/test_ctypes/test_pep3118.py | 23 +++- .../2018-02-05-21-54-46.bpo-32780.Dtiz8z.rst | 3 + Modules/_ctypes/stgdict.c | 117 +++++++++++++----- 4 files changed, 111 insertions(+), 33 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2018-02-05-21-54-46.bpo-32780.Dtiz8z.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 4de5c820f2c6ac..0642bbe9f99d0d 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -2510,6 +2510,7 @@ fields, or any other data types containing pointer type fields. An optional small integer that allows overriding the alignment of structure fields in the instance. :attr:`_pack_` must already be defined when :attr:`_fields_` is assigned, otherwise it will have no effect. + Setting this attribute to 0 is the same as not setting it at all. .. attribute:: _anonymous_ diff --git a/Lib/test/test_ctypes/test_pep3118.py b/Lib/test/test_ctypes/test_pep3118.py index efffc80a66fcb8..74fdf29fc9ad05 100644 --- a/Lib/test/test_ctypes/test_pep3118.py +++ b/Lib/test/test_ctypes/test_pep3118.py @@ -86,6 +86,20 @@ class PackedPoint(Structure): _pack_ = 2 _fields_ = [("x", c_long), ("y", c_long)] +class PointMidPad(Structure): + _fields_ = [("x", c_byte), ("y", c_uint64)] + +class PackedPointMidPad(Structure): + _pack_ = 2 + _fields_ = [("x", c_byte), ("y", c_uint64)] + +class PointEndPad(Structure): + _fields_ = [("x", c_uint64), ("y", c_byte)] + +class PackedPointEndPad(Structure): + _pack_ = 2 + _fields_ = [("x", c_uint64), ("y", c_byte)] + class Point2(Structure): pass Point2._fields_ = [("x", c_long), ("y", c_long)] @@ -185,10 +199,13 @@ class Complete(Structure): ## structures and unions - (Point, "T{ 0); + + if (padding == 1) { + /* Use x instead of 1x, for brevity */ + return _ctypes_alloc_format_string(prefix, "x"); + } + + int ret = PyOS_snprintf(buf, sizeof(buf), "%zdx", padding); + assert(0 <= ret && ret < sizeof(buf)); + return _ctypes_alloc_format_string(prefix, buf); +} + /* Retrieve the (optional) _pack_ attribute from a type, the _fields_ attribute, and create an StgDictObject. Used for Structure and Union subclasses. @@ -346,11 +369,10 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct { StgDictObject *stgdict, *basedict; Py_ssize_t len, offset, size, align, i; - Py_ssize_t union_size, total_align; + Py_ssize_t union_size, total_align, aligned_size; Py_ssize_t field_size = 0; int bitofs; PyObject *tmp; - int isPacked; int pack; Py_ssize_t ffi_ofs; int big_endian; @@ -374,7 +396,6 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct return -1; } if (tmp) { - isPacked = 1; pack = _PyLong_AsInt(tmp); Py_DECREF(tmp); if (pack < 0) { @@ -389,7 +410,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct } } else { - isPacked = 0; + /* Setting `_pack_ = 0` amounts to using the default alignment */ pack = 0; } @@ -470,12 +491,10 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct } assert(stgdict->format == NULL); - if (isStruct && !isPacked) { + if (isStruct) { stgdict->format = _ctypes_alloc_format_string(NULL, "T{"); } else { - /* PEP3118 doesn't support union, or packed structures (well, - only standard packing, but we don't support the pep for - that). Use 'B' for bytes. */ + /* PEP3118 doesn't support union. Use 'B' for bytes. */ stgdict->format = _ctypes_alloc_format_string(NULL, "B"); } if (stgdict->format == NULL) @@ -543,12 +562,14 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct } else bitsize = 0; - if (isStruct && !isPacked) { + if (isStruct) { const char *fieldfmt = dict->format ? dict->format : "B"; const char *fieldname = PyUnicode_AsUTF8(name); char *ptr; Py_ssize_t len; char *buf; + Py_ssize_t last_size = size; + Py_ssize_t padding; if (fieldname == NULL) { @@ -556,11 +577,38 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct return -1; } + /* construct the field now, as `prop->offset` is `offset` with + corrected alignment */ + prop = PyCField_FromDesc(desc, i, + &field_size, bitsize, &bitofs, + &size, &offset, &align, + pack, big_endian); + if (prop == NULL) { + Py_DECREF(pair); + return -1; + } + + /* number of bytes between the end of the last field and the start + of this one */ + padding = ((CFieldObject *)prop)->offset - last_size; + + if (padding > 0) { + ptr = stgdict->format; + stgdict->format = _ctypes_alloc_format_padding(ptr, padding); + PyMem_Free(ptr); + if (stgdict->format == NULL) { + Py_DECREF(pair); + Py_DECREF(prop); + return -1; + } + } + len = strlen(fieldname) + strlen(fieldfmt); buf = PyMem_Malloc(len + 2 + 1); if (buf == NULL) { Py_DECREF(pair); + Py_DECREF(prop); PyErr_NoMemory(); return -1; } @@ -578,15 +626,9 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct if (stgdict->format == NULL) { Py_DECREF(pair); + Py_DECREF(prop); return -1; } - } - - if (isStruct) { - prop = PyCField_FromDesc(desc, i, - &field_size, bitsize, &bitofs, - &size, &offset, &align, - pack, big_endian); } else /* union */ { size = 0; offset = 0; @@ -595,14 +637,14 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct &field_size, bitsize, &bitofs, &size, &offset, &align, pack, big_endian); + if (prop == NULL) { + Py_DECREF(pair); + return -1; + } union_size = max(size, union_size); } total_align = max(align, total_align); - if (!prop) { - Py_DECREF(pair); - return -1; - } if (-1 == PyObject_SetAttr(type, name, prop)) { Py_DECREF(prop); Py_DECREF(pair); @@ -612,26 +654,41 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct Py_DECREF(prop); } - if (isStruct && !isPacked) { - char *ptr = stgdict->format; + if (!isStruct) { + size = union_size; + } + + /* Adjust the size according to the alignment requirements */ + aligned_size = ((size + total_align - 1) / total_align) * total_align; + + if (isStruct) { + char *ptr; + Py_ssize_t padding; + + /* Pad up to the full size of the struct */ + padding = aligned_size - size; + if (padding > 0) { + ptr = stgdict->format; + stgdict->format = _ctypes_alloc_format_padding(ptr, padding); + PyMem_Free(ptr); + if (stgdict->format == NULL) { + return -1; + } + } + + ptr = stgdict->format; stgdict->format = _ctypes_alloc_format_string(stgdict->format, "}"); PyMem_Free(ptr); if (stgdict->format == NULL) return -1; } - if (!isStruct) - size = union_size; - - /* Adjust the size according to the alignment requirements */ - size = ((size + total_align - 1) / total_align) * total_align; - stgdict->ffi_type_pointer.alignment = Py_SAFE_DOWNCAST(total_align, Py_ssize_t, unsigned short); - stgdict->ffi_type_pointer.size = size; + stgdict->ffi_type_pointer.size = aligned_size; - stgdict->size = size; + stgdict->size = aligned_size; stgdict->align = total_align; stgdict->length = len; /* ADD ffi_ofs? */ From f7e9fbacb250ad9df95d0024161b40dfdc9cc39e Mon Sep 17 00:00:00 2001 From: mrh1997 Date: Sun, 5 Feb 2023 18:36:57 +0100 Subject: [PATCH 087/225] bpo-33591: Add support for path like objects to `ctypes.CDLL` (#7032) Co-authored-by: Oleg Iarygin --- Doc/library/ctypes.rst | 16 ++++++++++++++++ Lib/ctypes/__init__.py | 2 ++ Lib/test/test_ctypes/test_loading.py | 18 ++++++++++++++---- Misc/ACKS | 1 + ...18-05-21-17-18-00.gh-issue-77772.Fhg84L.rst | 3 +++ 5 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 0642bbe9f99d0d..8fd681286b812d 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1380,6 +1380,10 @@ way is to instantiate one of the following classes: DLLs and determine which one is not found using Windows debugging and tracing tools. + .. versionchanged:: 3.12 + + The *name* parameter can now be a :term:`path-like object`. + .. seealso:: `Microsoft DUMPBIN tool `_ @@ -1398,6 +1402,10 @@ way is to instantiate one of the following classes: .. versionchanged:: 3.3 :exc:`WindowsError` used to be raised. + .. versionchanged:: 3.12 + + The *name* parameter can now be a :term:`path-like object`. + .. class:: WinDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=None) @@ -1405,6 +1413,10 @@ way is to instantiate one of the following classes: functions in these libraries use the ``stdcall`` calling convention, and are assumed to return :c:expr:`int` by default. + .. versionchanged:: 3.12 + + The *name* parameter can now be a :term:`path-like object`. + The Python :term:`global interpreter lock` is released before calling any function exported by these libraries, and reacquired afterwards. @@ -1418,6 +1430,10 @@ function exported by these libraries, and reacquired afterwards. Thus, this is only useful to call Python C api functions directly. + .. versionchanged:: 3.12 + + The *name* parameter can now be a :term:`path-like object`. + All these classes can be instantiated by calling them with at least one argument, the pathname of the shared library. If you have an existing handle to an already loaded shared library, it can be passed as the ``handle`` named diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 2e9d4c5e7238e9..95353bab26cc71 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -344,6 +344,8 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=None): + if name: + name = _os.fspath(name) self._name = name flags = self._func_flags_ if use_errno: diff --git a/Lib/test/test_ctypes/test_loading.py b/Lib/test/test_ctypes/test_loading.py index 15e365ed267d9b..f2434926a51714 100644 --- a/Lib/test/test_ctypes/test_loading.py +++ b/Lib/test/test_ctypes/test_loading.py @@ -28,10 +28,20 @@ class LoaderTest(unittest.TestCase): unknowndll = "xxrandomnamexx" def test_load(self): - if libc_name is None: - self.skipTest('could not find libc') - CDLL(libc_name) - CDLL(os.path.basename(libc_name)) + if libc_name is not None: + test_lib = libc_name + else: + if os.name == "nt": + import _ctypes_test + test_lib = _ctypes_test.__file__ + else: + self.skipTest('could not find library to load') + CDLL(test_lib) + CDLL(os.path.basename(test_lib)) + class CTypesTestPathLikeCls: + def __fspath__(self): + return test_lib + CDLL(CTypesTestPathLikeCls()) self.assertRaises(OSError, CDLL, self.unknowndll) def test_load_version(self): diff --git a/Misc/ACKS b/Misc/ACKS index 74abcebe21ea60..d27d60f5b3605e 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -754,6 +754,7 @@ Tim Hochberg Benjamin Hodgson Joerg-Cyril Hoehle Douwe Hoekstra +Robert Hoelzl Gregor Hoffleit Chris Hoffman Tim Hoffmann diff --git a/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst b/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst new file mode 100644 index 00000000000000..3a7c6d45297ba4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst @@ -0,0 +1,3 @@ +:class:`ctypes.CDLL`, :class:`ctypes.OleDLL`, :class:`ctypes.WinDLL`, +and :class:`ctypes.PyDLL` now accept :term:`path-like objects +` as their ``name`` argument. Patch by Robert Hoelzl. From ffcb8220d7a8c8ca169b467d9e4a752874f68af2 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 5 Feb 2023 09:44:57 -0800 Subject: [PATCH 088/225] gh-101334: Don't force USTAR format in test_tarfile. (GH-101572) That causes the test to fail when run using a high UID as that ancient format cannot represent it. The current default (PAX) and the old default (GNU) both support high UIDs. --- Lib/test/test_tarfile.py | 5 +++++ .../Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst | 1 + 2 files changed, 6 insertions(+) create mode 100644 Misc/NEWS.d/next/Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 213932069201b9..f15a800976681c 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -225,6 +225,11 @@ def test_add_dir_getmember(self): self.add_dir_and_getmember('bar') self.add_dir_and_getmember('a'*101) + @unittest.skipIf( + (hasattr(os, 'getuid') and os.getuid() > 0o777_7777) or + (hasattr(os, 'getgid') and os.getgid() > 0o777_7777), + "uid or gid too high for USTAR format." + ) def add_dir_and_getmember(self, name): with os_helper.temp_cwd(): with tarfile.open(tmpname, 'w') as tar: diff --git a/Misc/NEWS.d/next/Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst b/Misc/NEWS.d/next/Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst new file mode 100644 index 00000000000000..2a95fd9ae53c86 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst @@ -0,0 +1 @@ +``test_tarfile`` has been updated to pass when run as a high UID. From d3e2dd6e71bd8e5482973891926d5df19be687eb Mon Sep 17 00:00:00 2001 From: Matty G Date: Sun, 5 Feb 2023 18:55:37 -0800 Subject: [PATCH 089/225] Trivial Change: Remove unhelpful doc in `datetime.timedelta` (#100164) --- Lib/datetime.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/datetime.py b/Lib/datetime.py index 68746de1cabf85..637144637485bc 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -587,9 +587,12 @@ class timedelta: returning a timedelta, and addition or subtraction of a datetime and a timedelta giving a datetime. - Representation: (days, seconds, microseconds). Why? Because I - felt like it. + Representation: (days, seconds, microseconds). """ + # The representation of (days, seconds, microseconds) was chosen + # arbitrarily; the exact rationale originally specified in the docstring + # was "Because I felt like it." + __slots__ = '_days', '_seconds', '_microseconds', '_hashcode' def __new__(cls, days=0, seconds=0, microseconds=0, From ef7c2bfcf1fd618438f981ace64499a99ae9fae0 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Sun, 5 Feb 2023 19:29:06 -0800 Subject: [PATCH 090/225] gh-101541: [Enum] create flag psuedo-member without calling original __new__ (GH-101590) --- Lib/enum.py | 5 +-- Lib/test/test_enum.py | 40 +++++++++++++++++++ ...-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst | 1 + 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst diff --git a/Lib/enum.py b/Lib/enum.py index adb61519abe942..d14e91a9b017d1 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -1429,12 +1429,11 @@ def _missing_(cls, value): % (cls.__name__, value, unknown, bin(unknown)) ) # normal Flag? - __new__ = getattr(cls, '__new_member__', None) - if cls._member_type_ is object and not __new__: + if cls._member_type_ is object: # construct a singleton enum pseudo-member pseudo_member = object.__new__(cls) else: - pseudo_member = (__new__ or cls._member_type_.__new__)(cls, value) + pseudo_member = cls._member_type_.__new__(cls, value) if not hasattr(pseudo_member, '_value_'): pseudo_member._value_ = value if member_value: diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 1e653e94f6b57a..0a2e0c14d268af 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -2855,6 +2855,46 @@ class NTEnum(Enum): [TTuple(id=0, a=0, blist=[]), TTuple(id=1, a=2, blist=[4]), TTuple(id=2, a=4, blist=[0, 1, 2])], ) + def test_flag_with_custom_new(self): + class FlagFromChar(IntFlag): + def __new__(cls, c): + value = 1 << c + self = int.__new__(cls, value) + self._value_ = value + return self + # + a = ord('a') + # + self.assertEqual(FlagFromChar.a, 158456325028528675187087900672) + self.assertEqual(FlagFromChar.a|1, 158456325028528675187087900673) + # + # + class FlagFromChar(Flag): + def __new__(cls, c): + value = 1 << c + self = object.__new__(cls) + self._value_ = value + return self + # + a = ord('a') + z = 1 + # + self.assertEqual(FlagFromChar.a.value, 158456325028528675187087900672) + self.assertEqual((FlagFromChar.a|FlagFromChar.z).value, 158456325028528675187087900674) + # + # + class FlagFromChar(int, Flag, boundary=KEEP): + def __new__(cls, c): + value = 1 << c + self = int.__new__(cls, value) + self._value_ = value + return self + # + a = ord('a') + # + self.assertEqual(FlagFromChar.a, 158456325028528675187087900672) + self.assertEqual(FlagFromChar.a|1, 158456325028528675187087900673) + class TestOrder(unittest.TestCase): "test usage of the `_order_` attribute" diff --git a/Misc/NEWS.d/next/Library/2023-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst b/Misc/NEWS.d/next/Library/2023-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst new file mode 100644 index 00000000000000..0f149e80dc54a2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst @@ -0,0 +1 @@ +[Enum] - fix psuedo-flag creation From 9ef7e75434587fc8f167d73eee5dd9bdca62714b Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Mon, 6 Feb 2023 13:58:00 +0900 Subject: [PATCH 091/225] =?UTF-8?q?gh-101372:=20Fix=20unicodedata.is=5Fnor?= =?UTF-8?q?malized=20to=20properly=20handle=20the=20UCD=203=E2=80=A6=20(gh?= =?UTF-8?q?-101388)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst | 2 ++ Modules/unicodedata.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst new file mode 100644 index 00000000000000..65a207e3f7e436 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst @@ -0,0 +1,2 @@ +Fix :func:`~unicodedata.is_normalized` to properly handle the UCD 3.2.0 +cases. Patch by Dong-hee Na. diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c index 59fccd4b834dd3..c108f14871f946 100644 --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -800,7 +800,7 @@ is_normalized_quickcheck(PyObject *self, PyObject *input, bool nfc, bool k, { /* UCD 3.2.0 is requested, quickchecks must be disabled. */ if (UCD_Check(self)) { - return NO; + return MAYBE; } if (PyUnicode_IS_ASCII(input)) { From 46416b9004b687856eaa73e5d48520cd768bbf82 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 6 Feb 2023 12:25:31 +0000 Subject: [PATCH 092/225] gh-76961: Fix buildbot failures in test_pep3118 (#101587) This PR fixes the buildbot failures introduced by the merge of #5561, by restricting the relevant tests to something that should work on both 32-bit and 64-bit platforms. It also silences some compiler warnings introduced in that PR. --- Lib/test/test_ctypes/test_pep3118.py | 20 ++++++++++---------- Modules/_ctypes/stgdict.c | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Lib/test/test_ctypes/test_pep3118.py b/Lib/test/test_ctypes/test_pep3118.py index 74fdf29fc9ad05..c8a70e3e335693 100644 --- a/Lib/test/test_ctypes/test_pep3118.py +++ b/Lib/test/test_ctypes/test_pep3118.py @@ -87,14 +87,14 @@ class PackedPoint(Structure): _fields_ = [("x", c_long), ("y", c_long)] class PointMidPad(Structure): - _fields_ = [("x", c_byte), ("y", c_uint64)] + _fields_ = [("x", c_byte), ("y", c_uint)] class PackedPointMidPad(Structure): _pack_ = 2 _fields_ = [("x", c_byte), ("y", c_uint64)] class PointEndPad(Structure): - _fields_ = [("x", c_uint64), ("y", c_byte)] + _fields_ = [("x", c_uint), ("y", c_byte)] class PackedPointEndPad(Structure): _pack_ = 2 @@ -199,14 +199,14 @@ class Complete(Structure): ## structures and unions - (Point2, "T{ Date: Mon, 6 Feb 2023 15:55:32 +0000 Subject: [PATCH 093/225] gh-101543: Ensure Windows registry path is only used when stdlib can't be found (GH-101544) --- ...-02-03-17-53-06.gh-issue-101543.cORAT4.rst | 2 ++ Modules/getpath.py | 31 +++++++------------ 2 files changed, 14 insertions(+), 19 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-02-03-17-53-06.gh-issue-101543.cORAT4.rst diff --git a/Misc/NEWS.d/next/Windows/2023-02-03-17-53-06.gh-issue-101543.cORAT4.rst b/Misc/NEWS.d/next/Windows/2023-02-03-17-53-06.gh-issue-101543.cORAT4.rst new file mode 100644 index 00000000000000..d4e2c6f23013b6 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-02-03-17-53-06.gh-issue-101543.cORAT4.rst @@ -0,0 +1,2 @@ +Ensure the install path in the registry is only used when the standard +library hasn't been located in any other way. diff --git a/Modules/getpath.py b/Modules/getpath.py index 6a0883878778a5..9913fcba497d30 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -582,7 +582,7 @@ def search_up(prefix, *landmarks, test=isfile): # Detect prefix by searching from our executable location for the stdlib_dir if STDLIB_SUBDIR and STDLIB_LANDMARKS and executable_dir and not prefix: prefix = search_up(executable_dir, *STDLIB_LANDMARKS) - if prefix: + if prefix and not stdlib_dir: stdlib_dir = joinpath(prefix, STDLIB_SUBDIR) if PREFIX and not prefix: @@ -631,20 +631,6 @@ def search_up(prefix, *landmarks, test=isfile): warn('Consider setting $PYTHONHOME to [:]') -# If we haven't set [plat]stdlib_dir already, set them now -if not stdlib_dir: - if prefix: - stdlib_dir = joinpath(prefix, STDLIB_SUBDIR) - else: - stdlib_dir = '' - -if not platstdlib_dir: - if exec_prefix: - platstdlib_dir = joinpath(exec_prefix, PLATSTDLIB_LANDMARK) - else: - platstdlib_dir = '' - - # For a venv, update the main prefix/exec_prefix but leave the base ones unchanged # XXX: We currently do not update prefix here, but it happens in site.py #if venv_prefix: @@ -706,8 +692,9 @@ def search_up(prefix, *landmarks, test=isfile): pythonpath.extend(v.split(DELIM)) i += 1 # Paths from the core key get appended last, but only - # when home was not set and we aren't in a build dir - if not home_was_set and not venv_prefix and not build_prefix: + # when home was not set and we haven't found our stdlib + # some other way. + if not home and not stdlib_dir: v = winreg.QueryValue(key, None) if isinstance(v, str): pythonpath.extend(v.split(DELIM)) @@ -722,6 +709,11 @@ def search_up(prefix, *landmarks, test=isfile): pythonpath.append(joinpath(prefix, p)) # Then add stdlib_dir and platstdlib_dir + if not stdlib_dir and prefix: + stdlib_dir = joinpath(prefix, STDLIB_SUBDIR) + if not platstdlib_dir and exec_prefix: + platstdlib_dir = joinpath(exec_prefix, PLATSTDLIB_LANDMARK) + if os_name == 'nt': # QUIRK: Windows generates paths differently if platstdlib_dir: @@ -792,5 +784,6 @@ def search_up(prefix, *landmarks, test=isfile): config['base_exec_prefix'] = base_exec_prefix or exec_prefix config['platlibdir'] = platlibdir -config['stdlib_dir'] = stdlib_dir -config['platstdlib_dir'] = platstdlib_dir +# test_embed expects empty strings, not None +config['stdlib_dir'] = stdlib_dir or '' +config['platstdlib_dir'] = platstdlib_dir or '' From b96b344f251954bb64aeb13c3e0c460350565c2a Mon Sep 17 00:00:00 2001 From: Eclips4 <80244920+Eclips4@users.noreply.github.com> Date: Mon, 6 Feb 2023 22:28:24 +0300 Subject: [PATCH 094/225] gh-101562: typing: add tests for inheritance with NotRequired & Required in parent fields (#101563) --- Lib/test/test_typing.py | 33 +++++++++++++++++++++++++++++++++ Misc/ACKS | 1 + 2 files changed, 34 insertions(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 5aa49bb0e2456d..7a460d94469fe7 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4892,6 +4892,18 @@ class NontotalMovie(TypedDict, total=False): title: Required[str] year: int +class ParentNontotalMovie(TypedDict, total=False): + title: Required[str] + +class ChildTotalMovie(ParentNontotalMovie): + year: NotRequired[int] + +class ParentDeeplyAnnotatedMovie(TypedDict): + title: Annotated[Annotated[Required[str], "foobar"], "another level"] + +class ChildDeeplyAnnotatedMovie(ParentDeeplyAnnotatedMovie): + year: NotRequired[Annotated[int, 2000]] + class AnnotatedMovie(TypedDict): title: Annotated[Required[str], "foobar"] year: NotRequired[Annotated[int, 2000]] @@ -5221,6 +5233,17 @@ def test_get_type_hints_typeddict(self): 'a': Annotated[Required[int], "a", "b", "c"] }) + self.assertEqual(get_type_hints(ChildTotalMovie), {"title": str, "year": int}) + self.assertEqual(get_type_hints(ChildTotalMovie, include_extras=True), { + "title": Required[str], "year": NotRequired[int] + }) + + self.assertEqual(get_type_hints(ChildDeeplyAnnotatedMovie), {"title": str, "year": int}) + self.assertEqual(get_type_hints(ChildDeeplyAnnotatedMovie, include_extras=True), { + "title": Annotated[Required[str], "foobar", "another level"], + "year": NotRequired[Annotated[int, 2000]] + }) + def test_get_type_hints_collections_abc_callable(self): # https://github.com/python/cpython/issues/91621 P = ParamSpec('P') @@ -6381,6 +6404,16 @@ def test_required_notrequired_keys(self): self.assertEqual(WeirdlyQuotedMovie.__optional_keys__, frozenset({"year"})) + self.assertEqual(ChildTotalMovie.__required_keys__, + frozenset({"title"})) + self.assertEqual(ChildTotalMovie.__optional_keys__, + frozenset({"year"})) + + self.assertEqual(ChildDeeplyAnnotatedMovie.__required_keys__, + frozenset({"title"})) + self.assertEqual(ChildDeeplyAnnotatedMovie.__optional_keys__, + frozenset({"year"})) + def test_multiple_inheritance(self): class One(TypedDict): one: int diff --git a/Misc/ACKS b/Misc/ACKS index d27d60f5b3605e..e12cbea0ebd6ed 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1414,6 +1414,7 @@ Jean-François Piéronne Oleg Plakhotnyuk Anatoliy Platonov Marcel Plch +Kirill Podoprigora Remi Pointel Jon Poler Ariel Poliak From 262003fd3297f7f4ee09cebd1abb225066412ce7 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Tue, 7 Feb 2023 00:05:41 +0300 Subject: [PATCH 095/225] =?UTF-8?q?gh-101609:=20Fix=20"=E2=80=98state?= =?UTF-8?q?=E2=80=99=20may=20be=20used=20uninitialized"=20warning=20in=20`?= =?UTF-8?q?=5Fxxinterpchannelsmodule`=20(GH-101610)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I went with the easiest solution: just removing the offending line. See the issue description with my reasoning. https://github.com/python/cpython/issues/101609 --- Modules/_xxinterpchannelsmodule.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Modules/_xxinterpchannelsmodule.c b/Modules/_xxinterpchannelsmodule.c index 8601a189e87526..60538c31874864 100644 --- a/Modules/_xxinterpchannelsmodule.c +++ b/Modules/_xxinterpchannelsmodule.c @@ -174,7 +174,9 @@ static int clear_module_state(module_state *state) { /* heap types */ - (void)_PyCrossInterpreterData_UnregisterClass(state->ChannelIDType); + if (state->ChannelIDType != NULL) { + (void)_PyCrossInterpreterData_UnregisterClass(state->ChannelIDType); + } Py_CLEAR(state->ChannelIDType); /* exceptions */ @@ -2269,7 +2271,6 @@ module_exec(PyObject *mod) return 0; error: - (void)_PyCrossInterpreterData_UnregisterClass(state->ChannelIDType); _globals_fini(); return -1; } From 132b3f8302c021ac31e9c1797a127d57faa1afee Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 6 Feb 2023 14:39:25 -0700 Subject: [PATCH 096/225] gh-59956: Partial Fix for GILState API Compatibility with Subinterpreters (gh-101431) The GILState API (PEP 311) implementation from 2003 made the assumption that only one thread state would ever be used for any given OS thread, explicitly disregarding the case of subinterpreters. However, PyThreadState_Swap() still facilitated switching between subinterpreters, meaning the "current" thread state (holding the GIL), and the GILState thread state could end up out of sync, causing problems (including crashes). This change addresses the issue by keeping the two in sync in PyThreadState_Swap(). I verified the fix against gh-99040. Note that the other GILState-subinterpreter incompatibility (with autoInterpreterState) is not resolved here. https://github.com/python/cpython/issues/59956 --- ...3-01-30-11-56-09.gh-issue-59956.7xqnC_.rst | 3 +++ Python/pystate.c | 25 +++---------------- 2 files changed, 7 insertions(+), 21 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-30-11-56-09.gh-issue-59956.7xqnC_.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-30-11-56-09.gh-issue-59956.7xqnC_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-30-11-56-09.gh-issue-59956.7xqnC_.rst new file mode 100644 index 00000000000000..b3c1896b9493e1 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-30-11-56-09.gh-issue-59956.7xqnC_.rst @@ -0,0 +1,3 @@ +The GILState API is now partially compatible with subinterpreters. +Previously, ``PyThreadState_GET()`` and ``PyGILState_GetThisThreadState()`` +would get out of sync, causing inconsistent behavior and crashes. diff --git a/Python/pystate.c b/Python/pystate.c index 8bb49d954a81b6..ed8c2e212a5539 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -266,10 +266,10 @@ unbind_tstate(PyThreadState *tstate) thread state for an OS level thread is when there are multiple interpreters. - The PyGILState_*() APIs don't work with multiple - interpreters (see bpo-10915 and bpo-15751), so this function - sets TSS only once. Thus, the first thread state created for that - given OS level thread will "win", which seems reasonable behaviour. + Before 3.12, the PyGILState_*() APIs didn't work with multiple + interpreters (see bpo-10915 and bpo-15751), so this function used + to set TSS only once. Thus, the first thread state created for that + given OS level thread would "win", which seemed reasonable behaviour. */ static void @@ -286,10 +286,6 @@ bind_gilstate_tstate(PyThreadState *tstate) assert(tstate != tcur); if (tcur != NULL) { - // The original gilstate implementation only respects the - // first thread state set. - // XXX Skipping like this does not play nice with multiple interpreters. - return; tcur->_status.bound_gilstate = 0; } gilstate_tss_set(runtime, tstate); @@ -1738,20 +1734,7 @@ _PyThreadState_Swap(_PyRuntimeState *runtime, PyThreadState *newts) tstate_activate(newts); } - /* It should not be possible for more than one thread state - to be used for a thread. Check this the best we can in debug - builds. - */ - // XXX The above isn't true when multiple interpreters are involved. #if defined(Py_DEBUG) - if (newts && gilstate_tss_initialized(runtime)) { - PyThreadState *check = gilstate_tss_get(runtime); - if (check != newts) { - if (check && check->interp == newts->interp) { - Py_FatalError("Invalid thread state for this thread"); - } - } - } errno = err; #endif return oldts; From 949c58f945b93af5b7bb70c6448e940da669065d Mon Sep 17 00:00:00 2001 From: Mariatta Wijaya Date: Mon, 6 Feb 2023 13:59:45 -0800 Subject: [PATCH 097/225] GH-101616: Mention the Docs Discourse forum in the "reporting docs issues" (GH-101617) Fixes https://github.com/python/cpython/issues/101616 --- Doc/bugs.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/bugs.rst b/Doc/bugs.rst index 69d7c27410d56a..4f30ef19ee4d8a 100644 --- a/Doc/bugs.rst +++ b/Doc/bugs.rst @@ -19,6 +19,9 @@ If you find a bug in this documentation or would like to propose an improvement, please submit a bug report on the :ref:`tracker `. If you have a suggestion on how to fix it, include that as well. +You can also open a discussion item on our +`Documentation Discourse forum `_. + If you're short on time, you can also email documentation bug reports to docs@python.org (behavioral bugs can be sent to python-list@python.org). 'docs@' is a mailing list run by volunteers; your request will be noticed, From 38752760c91c87dd67af16d2cee611a22e647567 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Mon, 6 Feb 2023 22:45:18 +0000 Subject: [PATCH 098/225] gh-98831: rewrite COPY and SWAP in the instruction definition DSL (#101620) --- Python/bytecodes.c | 17 ++++++----------- Python/generated_cases.c.h | 18 +++++++++++------- Python/opcode_metadata.h | 8 ++++---- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 8993567ac82206..0fc0b3b8280f8b 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -3098,11 +3098,9 @@ dummy_func( PUSH(result); } - // stack effect: ( -- __0) - inst(COPY) { - assert(oparg != 0); - PyObject *peek = PEEK(oparg); - PUSH(Py_NewRef(peek)); + inst(COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) { + assert(oparg > 0); + top = Py_NewRef(bottom); } inst(BINARY_OP, (unused/1, lhs, rhs -- res)) { @@ -3126,12 +3124,9 @@ dummy_func( ERROR_IF(res == NULL, error); } - // stack effect: ( -- ) - inst(SWAP) { - assert(oparg != 0); - PyObject *top = TOP(); - SET_TOP(PEEK(oparg)); - PEEK(oparg) = top; + inst(SWAP, (bottom, unused[oparg-2], top -- + top, unused[oparg-2], bottom)) { + assert(oparg >= 2); } inst(EXTENDED_ARG, (--)) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index e524bfcb99d470..f0f314a143c2c0 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3723,9 +3723,12 @@ } TARGET(COPY) { - assert(oparg != 0); - PyObject *peek = PEEK(oparg); - PUSH(Py_NewRef(peek)); + PyObject *bottom = PEEK(1 + (oparg-1)); + PyObject *top; + assert(oparg > 0); + top = Py_NewRef(bottom); + STACK_GROW(1); + POKE(1, top); DISPATCH(); } @@ -3760,10 +3763,11 @@ } TARGET(SWAP) { - assert(oparg != 0); - PyObject *top = TOP(); - SET_TOP(PEEK(oparg)); - PEEK(oparg) = top; + PyObject *top = PEEK(1); + PyObject *bottom = PEEK(2 + (oparg-2)); + assert(oparg >= 2); + POKE(1, bottom); + POKE(2 + (oparg-2), top); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 857526c35aa5b6..948d17519e2709 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -333,11 +333,11 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case FORMAT_VALUE: return -1; case COPY: - return -1; + return (oparg-1) + 1; case BINARY_OP: return 2; case SWAP: - return -1; + return (oparg-2) + 2; case EXTENDED_ARG: return 0; case CACHE: @@ -679,11 +679,11 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case FORMAT_VALUE: return -1; case COPY: - return -1; + return (oparg-1) + 2; case BINARY_OP: return 1; case SWAP: - return -1; + return (oparg-2) + 2; case EXTENDED_ARG: return 0; case CACHE: From 914f8fd9f7fc5e48b54d938a68c932cc618ef3a6 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 6 Feb 2023 15:53:31 -0700 Subject: [PATCH 099/225] gh-59956: Add a Test to Verify GILState Matches the "Current" Thread State (gh-101625) This test should have been in gh-101431. https://github.com/python/cpython/issues/59956 --- Lib/test/test_capi/test_misc.py | 3 +++ Modules/_testcapimodule.c | 37 +++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index dace37c362e569..03e22d7a2d382d 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -1413,6 +1413,9 @@ def callback(): ret = assert_python_ok('-X', 'tracemalloc', '-c', code) self.assertIn(b'callback called', ret.out) + def test_gilstate_matches_current(self): + _testcapi.test_current_tstate_matches() + class Test_testcapi(unittest.TestCase): locals().update((name, getattr(_testcapi, name)) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index f0d6e404f54a2f..5e47f4975a2d54 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1534,6 +1534,42 @@ crash_no_current_thread(PyObject *self, PyObject *Py_UNUSED(ignored)) return NULL; } +/* Test that the GILState thread and the "current" thread match. */ +static PyObject * +test_current_tstate_matches(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyThreadState *orig_tstate = PyThreadState_Get(); + + if (orig_tstate != PyGILState_GetThisThreadState()) { + PyErr_SetString(PyExc_RuntimeError, + "current thread state doesn't match GILState"); + return NULL; + } + + const char *err = NULL; + PyThreadState_Swap(NULL); + PyThreadState *substate = Py_NewInterpreter(); + + if (substate != PyThreadState_Get()) { + err = "subinterpreter thread state not current"; + goto finally; + } + if (substate != PyGILState_GetThisThreadState()) { + err = "subinterpreter thread state doesn't match GILState"; + goto finally; + } + +finally: + Py_EndInterpreter(substate); + PyThreadState_Swap(orig_tstate); + + if (err != NULL) { + PyErr_SetString(PyExc_RuntimeError, err); + return NULL; + } + Py_RETURN_NONE; +} + /* To run some code in a sub-interpreter. */ static PyObject * run_in_subinterp(PyObject *self, PyObject *args) @@ -3354,6 +3390,7 @@ static PyMethodDef TestMethods[] = { {"make_memoryview_from_NULL_pointer", make_memoryview_from_NULL_pointer, METH_NOARGS}, {"crash_no_current_thread", crash_no_current_thread, METH_NOARGS}, + {"test_current_tstate_matches", test_current_tstate_matches, METH_NOARGS}, {"run_in_subinterp", run_in_subinterp, METH_VARARGS}, {"run_in_subinterp_with_config", _PyCFunction_CAST(run_in_subinterp_with_config), From 1fcc0efdaa84b3602c236391633b70ff36df149b Mon Sep 17 00:00:00 2001 From: Jonathan Protzenko Date: Mon, 6 Feb 2023 18:11:01 -0800 Subject: [PATCH 100/225] gh-99108: Replace SHA2-224 & 256 with verified code from HACL* (#99109) replacing hashlib primitives (for the non-OpenSSL case) with verified implementations from HACL*. This is the first PR in the series, and focuses specifically on SHA2-256 and SHA2-224. This PR imports Hacl_Streaming_SHA2 into the Python tree. This is the HACL* implementation of SHA2, which combines a core implementation of SHA2 along with a layer of buffer management that allows updating the digest with any number of bytes. This supersedes the previous implementation in the tree. @franziskuskiefer was kind enough to benchmark the changes: in addition to being verified (thus providing significant safety and security improvements), this implementation also provides a sizeable performance boost! ``` --------------------------------------------------------------- Benchmark Time CPU Iterations --------------------------------------------------------------- Sha2_256_Streaming 3163 ns 3160 ns 219353 // this PR LibTomCrypt_Sha2_256 5057 ns 5056 ns 136234 // library used by Python currently ``` The changes in this PR are as follows: - import the subset of HACL* that covers SHA2-256/224 into `Modules/_hacl` - rewire sha256module.c to use the HACL* implementation Co-authored-by: Gregory P. Smith [Google LLC] Co-authored-by: Erlend E. Aasland --- Lib/test/test_hashlib.py | 10 + Makefile.pre.in | 2 +- ...2-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst | 4 + Modules/Setup.stdlib.in | 2 +- Modules/_hacl/Hacl_Streaming_SHA2.c | 682 ++++++++++++++++++ Modules/_hacl/Hacl_Streaming_SHA2.h | 136 ++++ Modules/_hacl/README.md | 29 + .../include/krml/FStar_UInt_8_16_32_64.h | 109 +++ Modules/_hacl/include/krml/internal/target.h | 218 ++++++ .../_hacl/include/krml/lowstar_endianness.h | 230 ++++++ .../_hacl/include/python_hacl_namespaces.h | 28 + Modules/_hacl/internal/Hacl_SHA2_Generic.h | 135 ++++ Modules/_hacl/refresh.sh | 132 ++++ Modules/sha256module.c | 398 ++-------- PCbuild/pythoncore.vcxproj | 3 +- PCbuild/pythoncore.vcxproj.filters | 3 + configure | 3 +- configure.ac | 5 +- 18 files changed, 1779 insertions(+), 350 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2022-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst create mode 100644 Modules/_hacl/Hacl_Streaming_SHA2.c create mode 100644 Modules/_hacl/Hacl_Streaming_SHA2.h create mode 100644 Modules/_hacl/README.md create mode 100644 Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h create mode 100644 Modules/_hacl/include/krml/internal/target.h create mode 100644 Modules/_hacl/include/krml/lowstar_endianness.h create mode 100644 Modules/_hacl/include/python_hacl_namespaces.h create mode 100644 Modules/_hacl/internal/Hacl_SHA2_Generic.h create mode 100755 Modules/_hacl/refresh.sh diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index 450dc4933f47f7..9c92b4e9c280dc 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -22,6 +22,7 @@ from test.support import _4G, bigmemtest from test.support.import_helper import import_fresh_module from test.support import os_helper +from test.support import requires_resource from test.support import threading_helper from test.support import warnings_helper from http.client import HTTPException @@ -354,6 +355,15 @@ def test_large_update(self): self.assertEqual(m1.digest(*args), m4_copy.digest(*args)) self.assertEqual(m4.digest(*args), m4_digest) + @requires_resource('cpu') + def test_sha256_update_over_4gb(self): + zero_1mb = b"\0" * 1024 * 1024 + h = hashlib.sha256() + for i in range(0, 4096): + h.update(zero_1mb) + h.update(b"hello world") + self.assertEqual(h.hexdigest(), "a5364f7a52ebe2e25f1838a4ca715a893b6fd7a23f2a0d9e9762120da8b1bf53") + def check(self, name, data, hexdigest, shake=False, **kwargs): length = len(hexdigest)//2 hexdigest = hexdigest.lower() diff --git a/Makefile.pre.in b/Makefile.pre.in index f2f9371e8ac04d..3641c4eeebeee3 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2612,7 +2612,7 @@ MODULE__HASHLIB_DEPS=$(srcdir)/Modules/hashlib.h MODULE__IO_DEPS=$(srcdir)/Modules/_io/_iomodule.h MODULE__MD5_DEPS=$(srcdir)/Modules/hashlib.h MODULE__SHA1_DEPS=$(srcdir)/Modules/hashlib.h -MODULE__SHA256_DEPS=$(srcdir)/Modules/hashlib.h +MODULE__SHA256_DEPS=$(srcdir)/Modules/hashlib.h $(srcdir)/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h $(srcdir)/Modules/_hacl/include/krml/lowstar_endianness.h $(srcdir)/Modules/_hacl/include/krml/internal/target.h $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.h MODULE__SHA3_DEPS=$(srcdir)/Modules/_sha3/sha3.c $(srcdir)/Modules/_sha3/sha3.h $(srcdir)/Modules/hashlib.h MODULE__SHA512_DEPS=$(srcdir)/Modules/hashlib.h MODULE__SOCKET_DEPS=$(srcdir)/Modules/socketmodule.h $(srcdir)/Modules/addrinfo.h $(srcdir)/Modules/getaddrinfo.c $(srcdir)/Modules/getnameinfo.c diff --git a/Misc/NEWS.d/next/Security/2022-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst b/Misc/NEWS.d/next/Security/2022-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst new file mode 100644 index 00000000000000..64acc09c482ecb --- /dev/null +++ b/Misc/NEWS.d/next/Security/2022-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst @@ -0,0 +1,4 @@ +Replace the builtin :mod:`hashlib` implementations of SHA2-224 and SHA2-256 +originally from LibTomCrypt with formally verified, side-channel resistant +code from the `HACL* `_ project. The +builtins remain a fallback only used when OpenSSL does not provide them. diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 3fd591c70d493f..f72783810f9415 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -79,7 +79,7 @@ # hashing builtins, can be disabled with --without-builtin-hashlib-hashes @MODULE__MD5_TRUE@_md5 md5module.c @MODULE__SHA1_TRUE@_sha1 sha1module.c -@MODULE__SHA256_TRUE@_sha256 sha256module.c +@MODULE__SHA256_TRUE@_sha256 sha256module.c _hacl/Hacl_Streaming_SHA2.c @MODULE__SHA512_TRUE@_sha512 sha512module.c @MODULE__SHA3_TRUE@_sha3 _sha3/sha3module.c @MODULE__BLAKE2_TRUE@_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c diff --git a/Modules/_hacl/Hacl_Streaming_SHA2.c b/Modules/_hacl/Hacl_Streaming_SHA2.c new file mode 100644 index 00000000000000..84566571792a3c --- /dev/null +++ b/Modules/_hacl/Hacl_Streaming_SHA2.c @@ -0,0 +1,682 @@ +/* MIT License + * + * Copyright (c) 2016-2022 INRIA, CMU and Microsoft Corporation + * Copyright (c) 2022-2023 HACL* Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#include "Hacl_Streaming_SHA2.h" + +#include "internal/Hacl_SHA2_Generic.h" + + +static inline void sha256_init(uint32_t *hash) +{ + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + uint32_t *os = hash; + uint32_t x = Hacl_Impl_SHA2_Generic_h256[i]; + os[i] = x;); +} + +static inline void sha256_update0(uint8_t *b, uint32_t *hash) +{ + uint32_t hash_old[8U] = { 0U }; + uint32_t ws[16U] = { 0U }; + memcpy(hash_old, hash, (uint32_t)8U * sizeof (uint32_t)); + uint8_t *b10 = b; + uint32_t u = load32_be(b10); + ws[0U] = u; + uint32_t u0 = load32_be(b10 + (uint32_t)4U); + ws[1U] = u0; + uint32_t u1 = load32_be(b10 + (uint32_t)8U); + ws[2U] = u1; + uint32_t u2 = load32_be(b10 + (uint32_t)12U); + ws[3U] = u2; + uint32_t u3 = load32_be(b10 + (uint32_t)16U); + ws[4U] = u3; + uint32_t u4 = load32_be(b10 + (uint32_t)20U); + ws[5U] = u4; + uint32_t u5 = load32_be(b10 + (uint32_t)24U); + ws[6U] = u5; + uint32_t u6 = load32_be(b10 + (uint32_t)28U); + ws[7U] = u6; + uint32_t u7 = load32_be(b10 + (uint32_t)32U); + ws[8U] = u7; + uint32_t u8 = load32_be(b10 + (uint32_t)36U); + ws[9U] = u8; + uint32_t u9 = load32_be(b10 + (uint32_t)40U); + ws[10U] = u9; + uint32_t u10 = load32_be(b10 + (uint32_t)44U); + ws[11U] = u10; + uint32_t u11 = load32_be(b10 + (uint32_t)48U); + ws[12U] = u11; + uint32_t u12 = load32_be(b10 + (uint32_t)52U); + ws[13U] = u12; + uint32_t u13 = load32_be(b10 + (uint32_t)56U); + ws[14U] = u13; + uint32_t u14 = load32_be(b10 + (uint32_t)60U); + ws[15U] = u14; + KRML_MAYBE_FOR4(i0, + (uint32_t)0U, + (uint32_t)4U, + (uint32_t)1U, + KRML_MAYBE_FOR16(i, + (uint32_t)0U, + (uint32_t)16U, + (uint32_t)1U, + uint32_t k_t = Hacl_Impl_SHA2_Generic_k224_256[(uint32_t)16U * i0 + i]; + uint32_t ws_t = ws[i]; + uint32_t a0 = hash[0U]; + uint32_t b0 = hash[1U]; + uint32_t c0 = hash[2U]; + uint32_t d0 = hash[3U]; + uint32_t e0 = hash[4U]; + uint32_t f0 = hash[5U]; + uint32_t g0 = hash[6U]; + uint32_t h02 = hash[7U]; + uint32_t k_e_t = k_t; + uint32_t + t1 = + h02 + + + ((e0 << (uint32_t)26U | e0 >> (uint32_t)6U) + ^ + ((e0 << (uint32_t)21U | e0 >> (uint32_t)11U) + ^ (e0 << (uint32_t)7U | e0 >> (uint32_t)25U))) + + ((e0 & f0) ^ (~e0 & g0)) + + k_e_t + + ws_t; + uint32_t + t2 = + ((a0 << (uint32_t)30U | a0 >> (uint32_t)2U) + ^ + ((a0 << (uint32_t)19U | a0 >> (uint32_t)13U) + ^ (a0 << (uint32_t)10U | a0 >> (uint32_t)22U))) + + ((a0 & b0) ^ ((a0 & c0) ^ (b0 & c0))); + uint32_t a1 = t1 + t2; + uint32_t b1 = a0; + uint32_t c1 = b0; + uint32_t d1 = c0; + uint32_t e1 = d0 + t1; + uint32_t f1 = e0; + uint32_t g1 = f0; + uint32_t h12 = g0; + hash[0U] = a1; + hash[1U] = b1; + hash[2U] = c1; + hash[3U] = d1; + hash[4U] = e1; + hash[5U] = f1; + hash[6U] = g1; + hash[7U] = h12;); + if (i0 < (uint32_t)3U) + { + KRML_MAYBE_FOR16(i, + (uint32_t)0U, + (uint32_t)16U, + (uint32_t)1U, + uint32_t t16 = ws[i]; + uint32_t t15 = ws[(i + (uint32_t)1U) % (uint32_t)16U]; + uint32_t t7 = ws[(i + (uint32_t)9U) % (uint32_t)16U]; + uint32_t t2 = ws[(i + (uint32_t)14U) % (uint32_t)16U]; + uint32_t + s1 = + (t2 << (uint32_t)15U | t2 >> (uint32_t)17U) + ^ ((t2 << (uint32_t)13U | t2 >> (uint32_t)19U) ^ t2 >> (uint32_t)10U); + uint32_t + s0 = + (t15 << (uint32_t)25U | t15 >> (uint32_t)7U) + ^ ((t15 << (uint32_t)14U | t15 >> (uint32_t)18U) ^ t15 >> (uint32_t)3U); + ws[i] = s1 + t7 + s0 + t16;); + }); + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + uint32_t *os = hash; + uint32_t x = hash[i] + hash_old[i]; + os[i] = x;); +} + +static inline void sha256_update_nblocks(uint32_t len, uint8_t *b, uint32_t *st) +{ + uint32_t blocks = len / (uint32_t)64U; + for (uint32_t i = (uint32_t)0U; i < blocks; i++) + { + uint8_t *b0 = b; + uint8_t *mb = b0 + i * (uint32_t)64U; + sha256_update0(mb, st); + } +} + +static inline void +sha256_update_last(uint64_t totlen, uint32_t len, uint8_t *b, uint32_t *hash) +{ + uint32_t blocks; + if (len + (uint32_t)8U + (uint32_t)1U <= (uint32_t)64U) + { + blocks = (uint32_t)1U; + } + else + { + blocks = (uint32_t)2U; + } + uint32_t fin = blocks * (uint32_t)64U; + uint8_t last[128U] = { 0U }; + uint8_t totlen_buf[8U] = { 0U }; + uint64_t total_len_bits = totlen << (uint32_t)3U; + store64_be(totlen_buf, total_len_bits); + uint8_t *b0 = b; + memcpy(last, b0, len * sizeof (uint8_t)); + last[len] = (uint8_t)0x80U; + memcpy(last + fin - (uint32_t)8U, totlen_buf, (uint32_t)8U * sizeof (uint8_t)); + uint8_t *last00 = last; + uint8_t *last10 = last + (uint32_t)64U; + uint8_t *l0 = last00; + uint8_t *l1 = last10; + uint8_t *lb0 = l0; + uint8_t *lb1 = l1; + uint8_t *last0 = lb0; + uint8_t *last1 = lb1; + sha256_update0(last0, hash); + if (blocks > (uint32_t)1U) + { + sha256_update0(last1, hash); + return; + } +} + +static inline void sha256_finish(uint32_t *st, uint8_t *h) +{ + uint8_t hbuf[32U] = { 0U }; + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + store32_be(hbuf + i * (uint32_t)4U, st[i]);); + memcpy(h, hbuf, (uint32_t)32U * sizeof (uint8_t)); +} + +static inline void sha224_init(uint32_t *hash) +{ + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + uint32_t *os = hash; + uint32_t x = Hacl_Impl_SHA2_Generic_h224[i]; + os[i] = x;); +} + +static inline void sha224_update_nblocks(uint32_t len, uint8_t *b, uint32_t *st) +{ + sha256_update_nblocks(len, b, st); +} + +static void sha224_update_last(uint64_t totlen, uint32_t len, uint8_t *b, uint32_t *st) +{ + sha256_update_last(totlen, len, b, st); +} + +static inline void sha224_finish(uint32_t *st, uint8_t *h) +{ + uint8_t hbuf[32U] = { 0U }; + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + store32_be(hbuf + i * (uint32_t)4U, st[i]);); + memcpy(h, hbuf, (uint32_t)28U * sizeof (uint8_t)); +} + +/** +Allocate initial state for the SHA2_256 hash. The state is to be freed by +calling `free_256`. +*/ +Hacl_Streaming_SHA2_state_sha2_224 *Hacl_Streaming_SHA2_create_in_256(void) +{ + uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); + uint32_t *block_state = (uint32_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint32_t)); + Hacl_Streaming_SHA2_state_sha2_224 + s = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + Hacl_Streaming_SHA2_state_sha2_224 + *p = + (Hacl_Streaming_SHA2_state_sha2_224 *)KRML_HOST_MALLOC(sizeof ( + Hacl_Streaming_SHA2_state_sha2_224 + )); + p[0U] = s; + sha256_init(block_state); + return p; +} + +/** +Copies the state passed as argument into a newly allocated state (deep copy). +The state is to be freed by calling `free_256`. Cloning the state this way is +useful, for instance, if your control-flow diverges and you need to feed +more (different) data into the hash in each branch. +*/ +Hacl_Streaming_SHA2_state_sha2_224 +*Hacl_Streaming_SHA2_copy_256(Hacl_Streaming_SHA2_state_sha2_224 *s0) +{ + Hacl_Streaming_SHA2_state_sha2_224 scrut = *s0; + uint32_t *block_state0 = scrut.block_state; + uint8_t *buf0 = scrut.buf; + uint64_t total_len0 = scrut.total_len; + uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); + memcpy(buf, buf0, (uint32_t)64U * sizeof (uint8_t)); + uint32_t *block_state = (uint32_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint32_t)); + memcpy(block_state, block_state0, (uint32_t)8U * sizeof (uint32_t)); + Hacl_Streaming_SHA2_state_sha2_224 + s = { .block_state = block_state, .buf = buf, .total_len = total_len0 }; + Hacl_Streaming_SHA2_state_sha2_224 + *p = + (Hacl_Streaming_SHA2_state_sha2_224 *)KRML_HOST_MALLOC(sizeof ( + Hacl_Streaming_SHA2_state_sha2_224 + )); + p[0U] = s; + return p; +} + +/** +Reset an existing state to the initial hash state with empty data. +*/ +void Hacl_Streaming_SHA2_init_256(Hacl_Streaming_SHA2_state_sha2_224 *s) +{ + Hacl_Streaming_SHA2_state_sha2_224 scrut = *s; + uint8_t *buf = scrut.buf; + uint32_t *block_state = scrut.block_state; + sha256_init(block_state); + Hacl_Streaming_SHA2_state_sha2_224 + tmp = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + s[0U] = tmp; +} + +static inline uint32_t +update_224_256(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *data, uint32_t len) +{ + Hacl_Streaming_SHA2_state_sha2_224 s = *p; + uint64_t total_len = s.total_len; + if ((uint64_t)len > (uint64_t)2305843009213693951U - total_len) + { + return (uint32_t)1U; + } + uint32_t sz; + if (total_len % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len > (uint64_t)0U) + { + sz = (uint32_t)64U; + } + else + { + sz = (uint32_t)(total_len % (uint64_t)(uint32_t)64U); + } + if (len <= (uint32_t)64U - sz) + { + Hacl_Streaming_SHA2_state_sha2_224 s1 = *p; + uint32_t *block_state1 = s1.block_state; + uint8_t *buf = s1.buf; + uint64_t total_len1 = s1.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)64U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)64U); + } + uint8_t *buf2 = buf + sz1; + memcpy(buf2, data, len * sizeof (uint8_t)); + uint64_t total_len2 = total_len1 + (uint64_t)len; + *p + = + ( + (Hacl_Streaming_SHA2_state_sha2_224){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len2 + } + ); + } + else if (sz == (uint32_t)0U) + { + Hacl_Streaming_SHA2_state_sha2_224 s1 = *p; + uint32_t *block_state1 = s1.block_state; + uint8_t *buf = s1.buf; + uint64_t total_len1 = s1.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)64U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)64U); + } + if (!(sz1 == (uint32_t)0U)) + { + sha256_update_nblocks((uint32_t)64U, buf, block_state1); + } + uint32_t ite; + if ((uint64_t)len % (uint64_t)(uint32_t)64U == (uint64_t)0U && (uint64_t)len > (uint64_t)0U) + { + ite = (uint32_t)64U; + } + else + { + ite = (uint32_t)((uint64_t)len % (uint64_t)(uint32_t)64U); + } + uint32_t n_blocks = (len - ite) / (uint32_t)64U; + uint32_t data1_len = n_blocks * (uint32_t)64U; + uint32_t data2_len = len - data1_len; + uint8_t *data1 = data; + uint8_t *data2 = data + data1_len; + sha256_update_nblocks(data1_len, data1, block_state1); + uint8_t *dst = buf; + memcpy(dst, data2, data2_len * sizeof (uint8_t)); + *p + = + ( + (Hacl_Streaming_SHA2_state_sha2_224){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len1 + (uint64_t)len + } + ); + } + else + { + uint32_t diff = (uint32_t)64U - sz; + uint8_t *data1 = data; + uint8_t *data2 = data + diff; + Hacl_Streaming_SHA2_state_sha2_224 s1 = *p; + uint32_t *block_state10 = s1.block_state; + uint8_t *buf0 = s1.buf; + uint64_t total_len10 = s1.total_len; + uint32_t sz10; + if (total_len10 % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len10 > (uint64_t)0U) + { + sz10 = (uint32_t)64U; + } + else + { + sz10 = (uint32_t)(total_len10 % (uint64_t)(uint32_t)64U); + } + uint8_t *buf2 = buf0 + sz10; + memcpy(buf2, data1, diff * sizeof (uint8_t)); + uint64_t total_len2 = total_len10 + (uint64_t)diff; + *p + = + ( + (Hacl_Streaming_SHA2_state_sha2_224){ + .block_state = block_state10, + .buf = buf0, + .total_len = total_len2 + } + ); + Hacl_Streaming_SHA2_state_sha2_224 s10 = *p; + uint32_t *block_state1 = s10.block_state; + uint8_t *buf = s10.buf; + uint64_t total_len1 = s10.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)64U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)64U); + } + if (!(sz1 == (uint32_t)0U)) + { + sha256_update_nblocks((uint32_t)64U, buf, block_state1); + } + uint32_t ite; + if + ( + (uint64_t)(len - diff) + % (uint64_t)(uint32_t)64U + == (uint64_t)0U + && (uint64_t)(len - diff) > (uint64_t)0U + ) + { + ite = (uint32_t)64U; + } + else + { + ite = (uint32_t)((uint64_t)(len - diff) % (uint64_t)(uint32_t)64U); + } + uint32_t n_blocks = (len - diff - ite) / (uint32_t)64U; + uint32_t data1_len = n_blocks * (uint32_t)64U; + uint32_t data2_len = len - diff - data1_len; + uint8_t *data11 = data2; + uint8_t *data21 = data2 + data1_len; + sha256_update_nblocks(data1_len, data11, block_state1); + uint8_t *dst = buf; + memcpy(dst, data21, data2_len * sizeof (uint8_t)); + *p + = + ( + (Hacl_Streaming_SHA2_state_sha2_224){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len1 + (uint64_t)(len - diff) + } + ); + } + return (uint32_t)0U; +} + +/** +Feed an arbitrary amount of data into the hash. This function returns 0 for +success, or 1 if the combined length of all of the data passed to `update_256` +(since the last call to `init_256`) exceeds 2^61-1 bytes. + +This function is identical to the update function for SHA2_224. +*/ +uint32_t +Hacl_Streaming_SHA2_update_256( + Hacl_Streaming_SHA2_state_sha2_224 *p, + uint8_t *input, + uint32_t input_len +) +{ + return update_224_256(p, input, input_len); +} + +/** +Write the resulting hash into `dst`, an array of 32 bytes. The state remains +valid after a call to `finish_256`, meaning the user may feed more data into +the hash via `update_256`. (The finish_256 function operates on an internal copy of +the state and therefore does not invalidate the client-held state `p`.) +*/ +void Hacl_Streaming_SHA2_finish_256(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *dst) +{ + Hacl_Streaming_SHA2_state_sha2_224 scrut = *p; + uint32_t *block_state = scrut.block_state; + uint8_t *buf_ = scrut.buf; + uint64_t total_len = scrut.total_len; + uint32_t r; + if (total_len % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len > (uint64_t)0U) + { + r = (uint32_t)64U; + } + else + { + r = (uint32_t)(total_len % (uint64_t)(uint32_t)64U); + } + uint8_t *buf_1 = buf_; + uint32_t tmp_block_state[8U] = { 0U }; + memcpy(tmp_block_state, block_state, (uint32_t)8U * sizeof (uint32_t)); + uint32_t ite; + if (r % (uint32_t)64U == (uint32_t)0U && r > (uint32_t)0U) + { + ite = (uint32_t)64U; + } + else + { + ite = r % (uint32_t)64U; + } + uint8_t *buf_last = buf_1 + r - ite; + uint8_t *buf_multi = buf_1; + sha256_update_nblocks((uint32_t)0U, buf_multi, tmp_block_state); + uint64_t prev_len_last = total_len - (uint64_t)r; + sha256_update_last(prev_len_last + (uint64_t)r, r, buf_last, tmp_block_state); + sha256_finish(tmp_block_state, dst); +} + +/** +Free a state allocated with `create_in_256`. + +This function is identical to the free function for SHA2_224. +*/ +void Hacl_Streaming_SHA2_free_256(Hacl_Streaming_SHA2_state_sha2_224 *s) +{ + Hacl_Streaming_SHA2_state_sha2_224 scrut = *s; + uint8_t *buf = scrut.buf; + uint32_t *block_state = scrut.block_state; + KRML_HOST_FREE(block_state); + KRML_HOST_FREE(buf); + KRML_HOST_FREE(s); +} + +/** +Hash `input`, of len `input_len`, into `dst`, an array of 32 bytes. +*/ +void Hacl_Streaming_SHA2_sha256(uint8_t *input, uint32_t input_len, uint8_t *dst) +{ + uint8_t *ib = input; + uint8_t *rb = dst; + uint32_t st[8U] = { 0U }; + sha256_init(st); + uint32_t rem = input_len % (uint32_t)64U; + uint64_t len_ = (uint64_t)input_len; + sha256_update_nblocks(input_len, ib, st); + uint32_t rem1 = input_len % (uint32_t)64U; + uint8_t *b0 = ib; + uint8_t *lb = b0 + input_len - rem1; + sha256_update_last(len_, rem, lb, st); + sha256_finish(st, rb); +} + +Hacl_Streaming_SHA2_state_sha2_224 *Hacl_Streaming_SHA2_create_in_224(void) +{ + uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); + uint32_t *block_state = (uint32_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint32_t)); + Hacl_Streaming_SHA2_state_sha2_224 + s = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + Hacl_Streaming_SHA2_state_sha2_224 + *p = + (Hacl_Streaming_SHA2_state_sha2_224 *)KRML_HOST_MALLOC(sizeof ( + Hacl_Streaming_SHA2_state_sha2_224 + )); + p[0U] = s; + sha224_init(block_state); + return p; +} + +void Hacl_Streaming_SHA2_init_224(Hacl_Streaming_SHA2_state_sha2_224 *s) +{ + Hacl_Streaming_SHA2_state_sha2_224 scrut = *s; + uint8_t *buf = scrut.buf; + uint32_t *block_state = scrut.block_state; + sha224_init(block_state); + Hacl_Streaming_SHA2_state_sha2_224 + tmp = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + s[0U] = tmp; +} + +uint32_t +Hacl_Streaming_SHA2_update_224( + Hacl_Streaming_SHA2_state_sha2_224 *p, + uint8_t *input, + uint32_t input_len +) +{ + return update_224_256(p, input, input_len); +} + +/** +Write the resulting hash into `dst`, an array of 28 bytes. The state remains +valid after a call to `finish_224`, meaning the user may feed more data into +the hash via `update_224`. +*/ +void Hacl_Streaming_SHA2_finish_224(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *dst) +{ + Hacl_Streaming_SHA2_state_sha2_224 scrut = *p; + uint32_t *block_state = scrut.block_state; + uint8_t *buf_ = scrut.buf; + uint64_t total_len = scrut.total_len; + uint32_t r; + if (total_len % (uint64_t)(uint32_t)64U == (uint64_t)0U && total_len > (uint64_t)0U) + { + r = (uint32_t)64U; + } + else + { + r = (uint32_t)(total_len % (uint64_t)(uint32_t)64U); + } + uint8_t *buf_1 = buf_; + uint32_t tmp_block_state[8U] = { 0U }; + memcpy(tmp_block_state, block_state, (uint32_t)8U * sizeof (uint32_t)); + uint32_t ite; + if (r % (uint32_t)64U == (uint32_t)0U && r > (uint32_t)0U) + { + ite = (uint32_t)64U; + } + else + { + ite = r % (uint32_t)64U; + } + uint8_t *buf_last = buf_1 + r - ite; + uint8_t *buf_multi = buf_1; + sha224_update_nblocks((uint32_t)0U, buf_multi, tmp_block_state); + uint64_t prev_len_last = total_len - (uint64_t)r; + sha224_update_last(prev_len_last + (uint64_t)r, r, buf_last, tmp_block_state); + sha224_finish(tmp_block_state, dst); +} + +void Hacl_Streaming_SHA2_free_224(Hacl_Streaming_SHA2_state_sha2_224 *p) +{ + Hacl_Streaming_SHA2_free_256(p); +} + +/** +Hash `input`, of len `input_len`, into `dst`, an array of 28 bytes. +*/ +void Hacl_Streaming_SHA2_sha224(uint8_t *input, uint32_t input_len, uint8_t *dst) +{ + uint8_t *ib = input; + uint8_t *rb = dst; + uint32_t st[8U] = { 0U }; + sha224_init(st); + uint32_t rem = input_len % (uint32_t)64U; + uint64_t len_ = (uint64_t)input_len; + sha224_update_nblocks(input_len, ib, st); + uint32_t rem1 = input_len % (uint32_t)64U; + uint8_t *b0 = ib; + uint8_t *lb = b0 + input_len - rem1; + sha224_update_last(len_, rem, lb, st); + sha224_finish(st, rb); +} + diff --git a/Modules/_hacl/Hacl_Streaming_SHA2.h b/Modules/_hacl/Hacl_Streaming_SHA2.h new file mode 100644 index 00000000000000..c83a835afe70fd --- /dev/null +++ b/Modules/_hacl/Hacl_Streaming_SHA2.h @@ -0,0 +1,136 @@ +/* MIT License + * + * Copyright (c) 2016-2022 INRIA, CMU and Microsoft Corporation + * Copyright (c) 2022-2023 HACL* Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef __Hacl_Streaming_SHA2_H +#define __Hacl_Streaming_SHA2_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include "python_hacl_namespaces.h" +#include "krml/FStar_UInt_8_16_32_64.h" +#include "krml/lowstar_endianness.h" +#include "krml/internal/target.h" + + + + +typedef struct Hacl_Streaming_SHA2_state_sha2_224_s +{ + uint32_t *block_state; + uint8_t *buf; + uint64_t total_len; +} +Hacl_Streaming_SHA2_state_sha2_224; + +typedef Hacl_Streaming_SHA2_state_sha2_224 Hacl_Streaming_SHA2_state_sha2_256; + +/** +Allocate initial state for the SHA2_256 hash. The state is to be freed by +calling `free_256`. +*/ +Hacl_Streaming_SHA2_state_sha2_224 *Hacl_Streaming_SHA2_create_in_256(void); + +/** +Copies the state passed as argument into a newly allocated state (deep copy). +The state is to be freed by calling `free_256`. Cloning the state this way is +useful, for instance, if your control-flow diverges and you need to feed +more (different) data into the hash in each branch. +*/ +Hacl_Streaming_SHA2_state_sha2_224 +*Hacl_Streaming_SHA2_copy_256(Hacl_Streaming_SHA2_state_sha2_224 *s0); + +/** +Reset an existing state to the initial hash state with empty data. +*/ +void Hacl_Streaming_SHA2_init_256(Hacl_Streaming_SHA2_state_sha2_224 *s); + +/** +Feed an arbitrary amount of data into the hash. This function returns 0 for +success, or 1 if the combined length of all of the data passed to `update_256` +(since the last call to `init_256`) exceeds 2^61-1 bytes. + +This function is identical to the update function for SHA2_224. +*/ +uint32_t +Hacl_Streaming_SHA2_update_256( + Hacl_Streaming_SHA2_state_sha2_224 *p, + uint8_t *input, + uint32_t input_len +); + +/** +Write the resulting hash into `dst`, an array of 32 bytes. The state remains +valid after a call to `finish_256`, meaning the user may feed more data into +the hash via `update_256`. (The finish_256 function operates on an internal copy of +the state and therefore does not invalidate the client-held state `p`.) +*/ +void Hacl_Streaming_SHA2_finish_256(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *dst); + +/** +Free a state allocated with `create_in_256`. + +This function is identical to the free function for SHA2_224. +*/ +void Hacl_Streaming_SHA2_free_256(Hacl_Streaming_SHA2_state_sha2_224 *s); + +/** +Hash `input`, of len `input_len`, into `dst`, an array of 32 bytes. +*/ +void Hacl_Streaming_SHA2_sha256(uint8_t *input, uint32_t input_len, uint8_t *dst); + +Hacl_Streaming_SHA2_state_sha2_224 *Hacl_Streaming_SHA2_create_in_224(void); + +void Hacl_Streaming_SHA2_init_224(Hacl_Streaming_SHA2_state_sha2_224 *s); + +uint32_t +Hacl_Streaming_SHA2_update_224( + Hacl_Streaming_SHA2_state_sha2_224 *p, + uint8_t *input, + uint32_t input_len +); + +/** +Write the resulting hash into `dst`, an array of 28 bytes. The state remains +valid after a call to `finish_224`, meaning the user may feed more data into +the hash via `update_224`. +*/ +void Hacl_Streaming_SHA2_finish_224(Hacl_Streaming_SHA2_state_sha2_224 *p, uint8_t *dst); + +void Hacl_Streaming_SHA2_free_224(Hacl_Streaming_SHA2_state_sha2_224 *p); + +/** +Hash `input`, of len `input_len`, into `dst`, an array of 28 bytes. +*/ +void Hacl_Streaming_SHA2_sha224(uint8_t *input, uint32_t input_len, uint8_t *dst); + +#if defined(__cplusplus) +} +#endif + +#define __Hacl_Streaming_SHA2_H_DEFINED +#endif diff --git a/Modules/_hacl/README.md b/Modules/_hacl/README.md new file mode 100644 index 00000000000000..e6a156a54b3cee --- /dev/null +++ b/Modules/_hacl/README.md @@ -0,0 +1,29 @@ +# Algorithm implementations used by the `hashlib` module. + +This code comes from the +[HACL\*](https://github.com/hacl-star/hacl-star/) project. + +HACL\* is a cryptographic library that has been formally verified for memory +safety, functional correctness, and secret independence. + +## Updating HACL* + +Use the `refresh.sh` script in this directory to pull in a new upstream code +version. The upstream git hash used for the most recent code pull is recorded +in the script. Modify the script as needed to bring in more if changes are +needed based on upstream code refactoring. + +Never manually edit HACL\* files. Always add transformation shell code to the +`refresh.sh` script to perform any necessary edits. If there are serious code +changes needed, work with the upstream repository. + +## Local files + +1. `./include/python_hacl_namespaces.h` +1. `./README.md` +1. `./refresh.sh` + +## ACKS + +* Jonathan Protzenko aka [@msprotz on Github](https://github.com/msprotz) +contributed our HACL\* based builtin code. diff --git a/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h b/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h new file mode 100644 index 00000000000000..3e2e4b32b22f96 --- /dev/null +++ b/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h @@ -0,0 +1,109 @@ +/* + Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. +*/ + + +#ifndef __FStar_UInt_8_16_32_64_H +#define __FStar_UInt_8_16_32_64_H + + + + +#include +#include + +#include "krml/lowstar_endianness.h" +#include "krml/FStar_UInt_8_16_32_64.h" +#include "krml/internal/target.h" +static inline uint64_t FStar_UInt64_eq_mask(uint64_t a, uint64_t b) +{ + uint64_t x = a ^ b; + uint64_t minus_x = ~x + (uint64_t)1U; + uint64_t x_or_minus_x = x | minus_x; + uint64_t xnx = x_or_minus_x >> (uint32_t)63U; + return xnx - (uint64_t)1U; +} + +static inline uint64_t FStar_UInt64_gte_mask(uint64_t a, uint64_t b) +{ + uint64_t x = a; + uint64_t y = b; + uint64_t x_xor_y = x ^ y; + uint64_t x_sub_y = x - y; + uint64_t x_sub_y_xor_y = x_sub_y ^ y; + uint64_t q = x_xor_y | x_sub_y_xor_y; + uint64_t x_xor_q = x ^ q; + uint64_t x_xor_q_ = x_xor_q >> (uint32_t)63U; + return x_xor_q_ - (uint64_t)1U; +} + +static inline uint32_t FStar_UInt32_eq_mask(uint32_t a, uint32_t b) +{ + uint32_t x = a ^ b; + uint32_t minus_x = ~x + (uint32_t)1U; + uint32_t x_or_minus_x = x | minus_x; + uint32_t xnx = x_or_minus_x >> (uint32_t)31U; + return xnx - (uint32_t)1U; +} + +static inline uint32_t FStar_UInt32_gte_mask(uint32_t a, uint32_t b) +{ + uint32_t x = a; + uint32_t y = b; + uint32_t x_xor_y = x ^ y; + uint32_t x_sub_y = x - y; + uint32_t x_sub_y_xor_y = x_sub_y ^ y; + uint32_t q = x_xor_y | x_sub_y_xor_y; + uint32_t x_xor_q = x ^ q; + uint32_t x_xor_q_ = x_xor_q >> (uint32_t)31U; + return x_xor_q_ - (uint32_t)1U; +} + +static inline uint16_t FStar_UInt16_eq_mask(uint16_t a, uint16_t b) +{ + uint16_t x = a ^ b; + uint16_t minus_x = ~x + (uint16_t)1U; + uint16_t x_or_minus_x = x | minus_x; + uint16_t xnx = x_or_minus_x >> (uint32_t)15U; + return xnx - (uint16_t)1U; +} + +static inline uint16_t FStar_UInt16_gte_mask(uint16_t a, uint16_t b) +{ + uint16_t x = a; + uint16_t y = b; + uint16_t x_xor_y = x ^ y; + uint16_t x_sub_y = x - y; + uint16_t x_sub_y_xor_y = x_sub_y ^ y; + uint16_t q = x_xor_y | x_sub_y_xor_y; + uint16_t x_xor_q = x ^ q; + uint16_t x_xor_q_ = x_xor_q >> (uint32_t)15U; + return x_xor_q_ - (uint16_t)1U; +} + +static inline uint8_t FStar_UInt8_eq_mask(uint8_t a, uint8_t b) +{ + uint8_t x = a ^ b; + uint8_t minus_x = ~x + (uint8_t)1U; + uint8_t x_or_minus_x = x | minus_x; + uint8_t xnx = x_or_minus_x >> (uint32_t)7U; + return xnx - (uint8_t)1U; +} + +static inline uint8_t FStar_UInt8_gte_mask(uint8_t a, uint8_t b) +{ + uint8_t x = a; + uint8_t y = b; + uint8_t x_xor_y = x ^ y; + uint8_t x_sub_y = x - y; + uint8_t x_sub_y_xor_y = x_sub_y ^ y; + uint8_t q = x_xor_y | x_sub_y_xor_y; + uint8_t x_xor_q = x ^ q; + uint8_t x_xor_q_ = x_xor_q >> (uint32_t)7U; + return x_xor_q_ - (uint8_t)1U; +} + + +#define __FStar_UInt_8_16_32_64_H_DEFINED +#endif diff --git a/Modules/_hacl/include/krml/internal/target.h b/Modules/_hacl/include/krml/internal/target.h new file mode 100644 index 00000000000000..9ef59859a554b5 --- /dev/null +++ b/Modules/_hacl/include/krml/internal/target.h @@ -0,0 +1,218 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +#ifndef __KRML_TARGET_H +#define __KRML_TARGET_H + +#include +#include +#include +#include +#include +#include +#include + +/* Since KaRaMeL emits the inline keyword unconditionally, we follow the + * guidelines at https://gcc.gnu.org/onlinedocs/gcc/Inline.html and make this + * __inline__ to ensure the code compiles with -std=c90 and earlier. */ +#ifdef __GNUC__ +# define inline __inline__ +#endif + +#ifndef KRML_HOST_MALLOC +# define KRML_HOST_MALLOC malloc +#endif + +#ifndef KRML_HOST_CALLOC +# define KRML_HOST_CALLOC calloc +#endif + +#ifndef KRML_HOST_FREE +# define KRML_HOST_FREE free +#endif + +/* Macros for prettier unrolling of loops */ +#define KRML_LOOP1(i, n, x) { \ + x \ + i += n; \ +} + +#define KRML_LOOP2(i, n, x) \ + KRML_LOOP1(i, n, x) \ + KRML_LOOP1(i, n, x) + +#define KRML_LOOP3(i, n, x) \ + KRML_LOOP2(i, n, x) \ + KRML_LOOP1(i, n, x) + +#define KRML_LOOP4(i, n, x) \ + KRML_LOOP2(i, n, x) \ + KRML_LOOP2(i, n, x) + +#define KRML_LOOP5(i, n, x) \ + KRML_LOOP4(i, n, x) \ + KRML_LOOP1(i, n, x) + +#define KRML_LOOP6(i, n, x) \ + KRML_LOOP4(i, n, x) \ + KRML_LOOP2(i, n, x) + +#define KRML_LOOP7(i, n, x) \ + KRML_LOOP4(i, n, x) \ + KRML_LOOP3(i, n, x) + +#define KRML_LOOP8(i, n, x) \ + KRML_LOOP4(i, n, x) \ + KRML_LOOP4(i, n, x) + +#define KRML_LOOP9(i, n, x) \ + KRML_LOOP8(i, n, x) \ + KRML_LOOP1(i, n, x) + +#define KRML_LOOP10(i, n, x) \ + KRML_LOOP8(i, n, x) \ + KRML_LOOP2(i, n, x) + +#define KRML_LOOP11(i, n, x) \ + KRML_LOOP8(i, n, x) \ + KRML_LOOP3(i, n, x) + +#define KRML_LOOP12(i, n, x) \ + KRML_LOOP8(i, n, x) \ + KRML_LOOP4(i, n, x) + +#define KRML_LOOP13(i, n, x) \ + KRML_LOOP8(i, n, x) \ + KRML_LOOP5(i, n, x) + +#define KRML_LOOP14(i, n, x) \ + KRML_LOOP8(i, n, x) \ + KRML_LOOP6(i, n, x) + +#define KRML_LOOP15(i, n, x) \ + KRML_LOOP8(i, n, x) \ + KRML_LOOP7(i, n, x) + +#define KRML_LOOP16(i, n, x) \ + KRML_LOOP8(i, n, x) \ + KRML_LOOP8(i, n, x) + +#define KRML_UNROLL_FOR(i, z, n, k, x) do { \ + uint32_t i = z; \ + KRML_LOOP##n(i, k, x) \ +} while (0) + +#define KRML_ACTUAL_FOR(i, z, n, k, x) \ + do { \ + for (uint32_t i = z; i < n; i += k) { \ + x \ + } \ + } while (0) + +#ifndef KRML_UNROLL_MAX +#define KRML_UNROLL_MAX 16 +#endif + +/* 1 is the number of loop iterations, i.e. (n - z)/k as evaluated by krml */ +#if 0 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR0(i, z, n, k, x) +#else +#define KRML_MAYBE_FOR0(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 1 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR1(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 1, k, x) +#else +#define KRML_MAYBE_FOR1(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 2 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR2(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 2, k, x) +#else +#define KRML_MAYBE_FOR2(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 3 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR3(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 3, k, x) +#else +#define KRML_MAYBE_FOR3(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 4 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR4(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 4, k, x) +#else +#define KRML_MAYBE_FOR4(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 5 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR5(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 5, k, x) +#else +#define KRML_MAYBE_FOR5(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 6 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR6(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 6, k, x) +#else +#define KRML_MAYBE_FOR6(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 7 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR7(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 7, k, x) +#else +#define KRML_MAYBE_FOR7(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 8 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR8(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 8, k, x) +#else +#define KRML_MAYBE_FOR8(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 9 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR9(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 9, k, x) +#else +#define KRML_MAYBE_FOR9(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 10 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR10(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 10, k, x) +#else +#define KRML_MAYBE_FOR10(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 11 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR11(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 11, k, x) +#else +#define KRML_MAYBE_FOR11(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 12 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR12(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 12, k, x) +#else +#define KRML_MAYBE_FOR12(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 13 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR13(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 13, k, x) +#else +#define KRML_MAYBE_FOR13(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 14 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR14(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 14, k, x) +#else +#define KRML_MAYBE_FOR14(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 15 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR15(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 15, k, x) +#else +#define KRML_MAYBE_FOR15(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif + +#if 16 <= KRML_UNROLL_MAX +#define KRML_MAYBE_FOR16(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 16, k, x) +#else +#define KRML_MAYBE_FOR16(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) +#endif +#endif diff --git a/Modules/_hacl/include/krml/lowstar_endianness.h b/Modules/_hacl/include/krml/lowstar_endianness.h new file mode 100644 index 00000000000000..32a7391e817ebb --- /dev/null +++ b/Modules/_hacl/include/krml/lowstar_endianness.h @@ -0,0 +1,230 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +#ifndef __LOWSTAR_ENDIANNESS_H +#define __LOWSTAR_ENDIANNESS_H + +#include +#include + +/******************************************************************************/ +/* Implementing C.fst (part 2: endian-ness macros) */ +/******************************************************************************/ + +/* ... for Linux */ +#if defined(__linux__) || defined(__CYGWIN__) || defined (__USE_SYSTEM_ENDIAN_H__) || defined(__GLIBC__) +# include + +/* ... for OSX */ +#elif defined(__APPLE__) +# include +# define htole64(x) OSSwapHostToLittleInt64(x) +# define le64toh(x) OSSwapLittleToHostInt64(x) +# define htobe64(x) OSSwapHostToBigInt64(x) +# define be64toh(x) OSSwapBigToHostInt64(x) + +# define htole16(x) OSSwapHostToLittleInt16(x) +# define le16toh(x) OSSwapLittleToHostInt16(x) +# define htobe16(x) OSSwapHostToBigInt16(x) +# define be16toh(x) OSSwapBigToHostInt16(x) + +# define htole32(x) OSSwapHostToLittleInt32(x) +# define le32toh(x) OSSwapLittleToHostInt32(x) +# define htobe32(x) OSSwapHostToBigInt32(x) +# define be32toh(x) OSSwapBigToHostInt32(x) + +/* ... for Solaris */ +#elif defined(__sun__) +# include +# define htole64(x) LE_64(x) +# define le64toh(x) LE_64(x) +# define htobe64(x) BE_64(x) +# define be64toh(x) BE_64(x) + +# define htole16(x) LE_16(x) +# define le16toh(x) LE_16(x) +# define htobe16(x) BE_16(x) +# define be16toh(x) BE_16(x) + +# define htole32(x) LE_32(x) +# define le32toh(x) LE_32(x) +# define htobe32(x) BE_32(x) +# define be32toh(x) BE_32(x) + +/* ... for the BSDs */ +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) +# include +#elif defined(__OpenBSD__) +# include + +/* ... for Windows (MSVC)... not targeting XBOX 360! */ +#elif defined(_MSC_VER) + +# include +# define htobe16(x) _byteswap_ushort(x) +# define htole16(x) (x) +# define be16toh(x) _byteswap_ushort(x) +# define le16toh(x) (x) + +# define htobe32(x) _byteswap_ulong(x) +# define htole32(x) (x) +# define be32toh(x) _byteswap_ulong(x) +# define le32toh(x) (x) + +# define htobe64(x) _byteswap_uint64(x) +# define htole64(x) (x) +# define be64toh(x) _byteswap_uint64(x) +# define le64toh(x) (x) + +/* ... for Windows (GCC-like, e.g. mingw or clang) */ +#elif (defined(_WIN32) || defined(_WIN64)) && \ + (defined(__GNUC__) || defined(__clang__)) + +# define htobe16(x) __builtin_bswap16(x) +# define htole16(x) (x) +# define be16toh(x) __builtin_bswap16(x) +# define le16toh(x) (x) + +# define htobe32(x) __builtin_bswap32(x) +# define htole32(x) (x) +# define be32toh(x) __builtin_bswap32(x) +# define le32toh(x) (x) + +# define htobe64(x) __builtin_bswap64(x) +# define htole64(x) (x) +# define be64toh(x) __builtin_bswap64(x) +# define le64toh(x) (x) + +/* ... generic big-endian fallback code */ +#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + +/* byte swapping code inspired by: + * https://github.com/rweather/arduinolibs/blob/master/libraries/Crypto/utility/EndianUtil.h + * */ + +# define htobe32(x) (x) +# define be32toh(x) (x) +# define htole32(x) \ + (__extension__({ \ + uint32_t _temp = (x); \ + ((_temp >> 24) & 0x000000FF) | ((_temp >> 8) & 0x0000FF00) | \ + ((_temp << 8) & 0x00FF0000) | ((_temp << 24) & 0xFF000000); \ + })) +# define le32toh(x) (htole32((x))) + +# define htobe64(x) (x) +# define be64toh(x) (x) +# define htole64(x) \ + (__extension__({ \ + uint64_t __temp = (x); \ + uint32_t __low = htobe32((uint32_t)__temp); \ + uint32_t __high = htobe32((uint32_t)(__temp >> 32)); \ + (((uint64_t)__low) << 32) | __high; \ + })) +# define le64toh(x) (htole64((x))) + +/* ... generic little-endian fallback code */ +#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + +# define htole32(x) (x) +# define le32toh(x) (x) +# define htobe32(x) \ + (__extension__({ \ + uint32_t _temp = (x); \ + ((_temp >> 24) & 0x000000FF) | ((_temp >> 8) & 0x0000FF00) | \ + ((_temp << 8) & 0x00FF0000) | ((_temp << 24) & 0xFF000000); \ + })) +# define be32toh(x) (htobe32((x))) + +# define htole64(x) (x) +# define le64toh(x) (x) +# define htobe64(x) \ + (__extension__({ \ + uint64_t __temp = (x); \ + uint32_t __low = htobe32((uint32_t)__temp); \ + uint32_t __high = htobe32((uint32_t)(__temp >> 32)); \ + (((uint64_t)__low) << 32) | __high; \ + })) +# define be64toh(x) (htobe64((x))) + +/* ... couldn't determine endian-ness of the target platform */ +#else +# error "Please define __BYTE_ORDER__!" + +#endif /* defined(__linux__) || ... */ + +/* Loads and stores. These avoid undefined behavior due to unaligned memory + * accesses, via memcpy. */ + +inline static uint16_t load16(uint8_t *b) { + uint16_t x; + memcpy(&x, b, 2); + return x; +} + +inline static uint32_t load32(uint8_t *b) { + uint32_t x; + memcpy(&x, b, 4); + return x; +} + +inline static uint64_t load64(uint8_t *b) { + uint64_t x; + memcpy(&x, b, 8); + return x; +} + +inline static void store16(uint8_t *b, uint16_t i) { + memcpy(b, &i, 2); +} + +inline static void store32(uint8_t *b, uint32_t i) { + memcpy(b, &i, 4); +} + +inline static void store64(uint8_t *b, uint64_t i) { + memcpy(b, &i, 8); +} + +/* Legacy accessors so that this header can serve as an implementation of + * C.Endianness */ +#define load16_le(b) (le16toh(load16(b))) +#define store16_le(b, i) (store16(b, htole16(i))) +#define load16_be(b) (be16toh(load16(b))) +#define store16_be(b, i) (store16(b, htobe16(i))) + +#define load32_le(b) (le32toh(load32(b))) +#define store32_le(b, i) (store32(b, htole32(i))) +#define load32_be(b) (be32toh(load32(b))) +#define store32_be(b, i) (store32(b, htobe32(i))) + +#define load64_le(b) (le64toh(load64(b))) +#define store64_le(b, i) (store64(b, htole64(i))) +#define load64_be(b) (be64toh(load64(b))) +#define store64_be(b, i) (store64(b, htobe64(i))) + +/* Co-existence of LowStar.Endianness and FStar.Endianness generates name + * conflicts, because of course both insist on having no prefixes. Until a + * prefix is added, or until we truly retire FStar.Endianness, solve this issue + * in an elegant way. */ +#define load16_le0 load16_le +#define store16_le0 store16_le +#define load16_be0 load16_be +#define store16_be0 store16_be + +#define load32_le0 load32_le +#define store32_le0 store32_le +#define load32_be0 load32_be +#define store32_be0 store32_be + +#define load64_le0 load64_le +#define store64_le0 store64_le +#define load64_be0 load64_be +#define store64_be0 store64_be + +#define load128_le0 load128_le +#define store128_le0 store128_le +#define load128_be0 load128_be +#define store128_be0 store128_be + +#endif diff --git a/Modules/_hacl/include/python_hacl_namespaces.h b/Modules/_hacl/include/python_hacl_namespaces.h new file mode 100644 index 00000000000000..af390459311fe8 --- /dev/null +++ b/Modules/_hacl/include/python_hacl_namespaces.h @@ -0,0 +1,28 @@ +#ifndef _PYTHON_HACL_NAMESPACES_H +#define _PYTHON_HACL_NAMESPACES_H + +/* + * C's excuse for namespaces: Use globally unique names to avoid linkage + * conflicts with builds linking or dynamically loading other code potentially + * using HACL* libraries. + */ + +#define Hacl_Streaming_SHA2_state_sha2_224_s python_hashlib_Hacl_Streaming_SHA2_state_sha2_224_s +#define Hacl_Streaming_SHA2_state_sha2_224 python_hashlib_Hacl_Streaming_SHA2_state_sha2_224 +#define Hacl_Streaming_SHA2_state_sha2_256 python_hashlib_Hacl_Streaming_SHA2_state_sha2_256 +#define Hacl_Streaming_SHA2_create_in_256 python_hashlib_Hacl_Streaming_SHA2_create_in_256 +#define Hacl_Streaming_SHA2_create_in_224 python_hashlib_Hacl_Streaming_SHA2_create_in_224 +#define Hacl_Streaming_SHA2_copy_256 python_hashlib_Hacl_Streaming_SHA2_copy_256 +#define Hacl_Streaming_SHA2_copy_224 python_hashlib_Hacl_Streaming_SHA2_copy_224 +#define Hacl_Streaming_SHA2_init_256 python_hashlib_Hacl_Streaming_SHA2_init_256 +#define Hacl_Streaming_SHA2_init_224 python_hashlib_Hacl_Streaming_SHA2_init_224 +#define Hacl_Streaming_SHA2_update_256 python_hashlib_Hacl_Streaming_SHA2_update_256 +#define Hacl_Streaming_SHA2_update_224 python_hashlib_Hacl_Streaming_SHA2_update_224 +#define Hacl_Streaming_SHA2_finish_256 python_hashlib_Hacl_Streaming_SHA2_finish_256 +#define Hacl_Streaming_SHA2_finish_224 python_hashlib_Hacl_Streaming_SHA2_finish_224 +#define Hacl_Streaming_SHA2_free_256 python_hashlib_Hacl_Streaming_SHA2_free_256 +#define Hacl_Streaming_SHA2_free_224 python_hashlib_Hacl_Streaming_SHA2_free_224 +#define Hacl_Streaming_SHA2_sha256 python_hashlib_Hacl_Streaming_SHA2_sha256 +#define Hacl_Streaming_SHA2_sha224 python_hashlib_Hacl_Streaming_SHA2_sha224 + +#endif // _PYTHON_HACL_NAMESPACES_H diff --git a/Modules/_hacl/internal/Hacl_SHA2_Generic.h b/Modules/_hacl/internal/Hacl_SHA2_Generic.h new file mode 100644 index 00000000000000..23f7cea1eb3884 --- /dev/null +++ b/Modules/_hacl/internal/Hacl_SHA2_Generic.h @@ -0,0 +1,135 @@ +/* MIT License + * + * Copyright (c) 2016-2022 INRIA, CMU and Microsoft Corporation + * Copyright (c) 2022-2023 HACL* Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef __internal_Hacl_SHA2_Generic_H +#define __internal_Hacl_SHA2_Generic_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include "krml/FStar_UInt_8_16_32_64.h" +#include "krml/lowstar_endianness.h" +#include "krml/internal/target.h" + + + + +static const +uint32_t +Hacl_Impl_SHA2_Generic_h224[8U] = + { + (uint32_t)0xc1059ed8U, (uint32_t)0x367cd507U, (uint32_t)0x3070dd17U, (uint32_t)0xf70e5939U, + (uint32_t)0xffc00b31U, (uint32_t)0x68581511U, (uint32_t)0x64f98fa7U, (uint32_t)0xbefa4fa4U + }; + +static const +uint32_t +Hacl_Impl_SHA2_Generic_h256[8U] = + { + (uint32_t)0x6a09e667U, (uint32_t)0xbb67ae85U, (uint32_t)0x3c6ef372U, (uint32_t)0xa54ff53aU, + (uint32_t)0x510e527fU, (uint32_t)0x9b05688cU, (uint32_t)0x1f83d9abU, (uint32_t)0x5be0cd19U + }; + +static const +uint64_t +Hacl_Impl_SHA2_Generic_h384[8U] = + { + (uint64_t)0xcbbb9d5dc1059ed8U, (uint64_t)0x629a292a367cd507U, (uint64_t)0x9159015a3070dd17U, + (uint64_t)0x152fecd8f70e5939U, (uint64_t)0x67332667ffc00b31U, (uint64_t)0x8eb44a8768581511U, + (uint64_t)0xdb0c2e0d64f98fa7U, (uint64_t)0x47b5481dbefa4fa4U + }; + +static const +uint64_t +Hacl_Impl_SHA2_Generic_h512[8U] = + { + (uint64_t)0x6a09e667f3bcc908U, (uint64_t)0xbb67ae8584caa73bU, (uint64_t)0x3c6ef372fe94f82bU, + (uint64_t)0xa54ff53a5f1d36f1U, (uint64_t)0x510e527fade682d1U, (uint64_t)0x9b05688c2b3e6c1fU, + (uint64_t)0x1f83d9abfb41bd6bU, (uint64_t)0x5be0cd19137e2179U + }; + +static const +uint32_t +Hacl_Impl_SHA2_Generic_k224_256[64U] = + { + (uint32_t)0x428a2f98U, (uint32_t)0x71374491U, (uint32_t)0xb5c0fbcfU, (uint32_t)0xe9b5dba5U, + (uint32_t)0x3956c25bU, (uint32_t)0x59f111f1U, (uint32_t)0x923f82a4U, (uint32_t)0xab1c5ed5U, + (uint32_t)0xd807aa98U, (uint32_t)0x12835b01U, (uint32_t)0x243185beU, (uint32_t)0x550c7dc3U, + (uint32_t)0x72be5d74U, (uint32_t)0x80deb1feU, (uint32_t)0x9bdc06a7U, (uint32_t)0xc19bf174U, + (uint32_t)0xe49b69c1U, (uint32_t)0xefbe4786U, (uint32_t)0x0fc19dc6U, (uint32_t)0x240ca1ccU, + (uint32_t)0x2de92c6fU, (uint32_t)0x4a7484aaU, (uint32_t)0x5cb0a9dcU, (uint32_t)0x76f988daU, + (uint32_t)0x983e5152U, (uint32_t)0xa831c66dU, (uint32_t)0xb00327c8U, (uint32_t)0xbf597fc7U, + (uint32_t)0xc6e00bf3U, (uint32_t)0xd5a79147U, (uint32_t)0x06ca6351U, (uint32_t)0x14292967U, + (uint32_t)0x27b70a85U, (uint32_t)0x2e1b2138U, (uint32_t)0x4d2c6dfcU, (uint32_t)0x53380d13U, + (uint32_t)0x650a7354U, (uint32_t)0x766a0abbU, (uint32_t)0x81c2c92eU, (uint32_t)0x92722c85U, + (uint32_t)0xa2bfe8a1U, (uint32_t)0xa81a664bU, (uint32_t)0xc24b8b70U, (uint32_t)0xc76c51a3U, + (uint32_t)0xd192e819U, (uint32_t)0xd6990624U, (uint32_t)0xf40e3585U, (uint32_t)0x106aa070U, + (uint32_t)0x19a4c116U, (uint32_t)0x1e376c08U, (uint32_t)0x2748774cU, (uint32_t)0x34b0bcb5U, + (uint32_t)0x391c0cb3U, (uint32_t)0x4ed8aa4aU, (uint32_t)0x5b9cca4fU, (uint32_t)0x682e6ff3U, + (uint32_t)0x748f82eeU, (uint32_t)0x78a5636fU, (uint32_t)0x84c87814U, (uint32_t)0x8cc70208U, + (uint32_t)0x90befffaU, (uint32_t)0xa4506cebU, (uint32_t)0xbef9a3f7U, (uint32_t)0xc67178f2U + }; + +static const +uint64_t +Hacl_Impl_SHA2_Generic_k384_512[80U] = + { + (uint64_t)0x428a2f98d728ae22U, (uint64_t)0x7137449123ef65cdU, (uint64_t)0xb5c0fbcfec4d3b2fU, + (uint64_t)0xe9b5dba58189dbbcU, (uint64_t)0x3956c25bf348b538U, (uint64_t)0x59f111f1b605d019U, + (uint64_t)0x923f82a4af194f9bU, (uint64_t)0xab1c5ed5da6d8118U, (uint64_t)0xd807aa98a3030242U, + (uint64_t)0x12835b0145706fbeU, (uint64_t)0x243185be4ee4b28cU, (uint64_t)0x550c7dc3d5ffb4e2U, + (uint64_t)0x72be5d74f27b896fU, (uint64_t)0x80deb1fe3b1696b1U, (uint64_t)0x9bdc06a725c71235U, + (uint64_t)0xc19bf174cf692694U, (uint64_t)0xe49b69c19ef14ad2U, (uint64_t)0xefbe4786384f25e3U, + (uint64_t)0x0fc19dc68b8cd5b5U, (uint64_t)0x240ca1cc77ac9c65U, (uint64_t)0x2de92c6f592b0275U, + (uint64_t)0x4a7484aa6ea6e483U, (uint64_t)0x5cb0a9dcbd41fbd4U, (uint64_t)0x76f988da831153b5U, + (uint64_t)0x983e5152ee66dfabU, (uint64_t)0xa831c66d2db43210U, (uint64_t)0xb00327c898fb213fU, + (uint64_t)0xbf597fc7beef0ee4U, (uint64_t)0xc6e00bf33da88fc2U, (uint64_t)0xd5a79147930aa725U, + (uint64_t)0x06ca6351e003826fU, (uint64_t)0x142929670a0e6e70U, (uint64_t)0x27b70a8546d22ffcU, + (uint64_t)0x2e1b21385c26c926U, (uint64_t)0x4d2c6dfc5ac42aedU, (uint64_t)0x53380d139d95b3dfU, + (uint64_t)0x650a73548baf63deU, (uint64_t)0x766a0abb3c77b2a8U, (uint64_t)0x81c2c92e47edaee6U, + (uint64_t)0x92722c851482353bU, (uint64_t)0xa2bfe8a14cf10364U, (uint64_t)0xa81a664bbc423001U, + (uint64_t)0xc24b8b70d0f89791U, (uint64_t)0xc76c51a30654be30U, (uint64_t)0xd192e819d6ef5218U, + (uint64_t)0xd69906245565a910U, (uint64_t)0xf40e35855771202aU, (uint64_t)0x106aa07032bbd1b8U, + (uint64_t)0x19a4c116b8d2d0c8U, (uint64_t)0x1e376c085141ab53U, (uint64_t)0x2748774cdf8eeb99U, + (uint64_t)0x34b0bcb5e19b48a8U, (uint64_t)0x391c0cb3c5c95a63U, (uint64_t)0x4ed8aa4ae3418acbU, + (uint64_t)0x5b9cca4f7763e373U, (uint64_t)0x682e6ff3d6b2b8a3U, (uint64_t)0x748f82ee5defb2fcU, + (uint64_t)0x78a5636f43172f60U, (uint64_t)0x84c87814a1f0ab72U, (uint64_t)0x8cc702081a6439ecU, + (uint64_t)0x90befffa23631e28U, (uint64_t)0xa4506cebde82bde9U, (uint64_t)0xbef9a3f7b2c67915U, + (uint64_t)0xc67178f2e372532bU, (uint64_t)0xca273eceea26619cU, (uint64_t)0xd186b8c721c0c207U, + (uint64_t)0xeada7dd6cde0eb1eU, (uint64_t)0xf57d4f7fee6ed178U, (uint64_t)0x06f067aa72176fbaU, + (uint64_t)0x0a637dc5a2c898a6U, (uint64_t)0x113f9804bef90daeU, (uint64_t)0x1b710b35131c471bU, + (uint64_t)0x28db77f523047d84U, (uint64_t)0x32caab7b40c72493U, (uint64_t)0x3c9ebe0a15c9bebcU, + (uint64_t)0x431d67c49c100d4cU, (uint64_t)0x4cc5d4becb3e42b6U, (uint64_t)0x597f299cfc657e2aU, + (uint64_t)0x5fcb6fab3ad6faecU, (uint64_t)0x6c44198c4a475817U + }; + +#if defined(__cplusplus) +} +#endif + +#define __internal_Hacl_SHA2_Generic_H_DEFINED +#endif diff --git a/Modules/_hacl/refresh.sh b/Modules/_hacl/refresh.sh new file mode 100755 index 00000000000000..594873862a2db0 --- /dev/null +++ b/Modules/_hacl/refresh.sh @@ -0,0 +1,132 @@ +#!/usr/bin/env bash +# +# Use this script to update the HACL generated hash algorithm implementation +# code from a local checkout of the upstream hacl-star repository. +# + +set -e +set -o pipefail + +if [[ "${BASH_VERSINFO[0]}" -lt 4 ]]; then + echo "A bash version >= 4 required. Got: $BASH_VERSION" >&2 + exit 1 +fi + +if [[ $1 == "" ]]; then + echo "Usage: $0 path-to-hacl-directory" + echo "" + echo " path-to-hacl-directory should be a local git checkout of a" + echo " https://github.com/hacl-star/hacl-star/ repo." + exit 1 +fi + +# Update this when updating to a new version after verifying that the changes +# the update brings in are good. +expected_hacl_star_rev=94aabbb4cf71347d3779a8db486c761403c6d036 + +hacl_dir="$(realpath "$1")" +cd "$(dirname "$0")" +actual_rev=$(cd "$hacl_dir" && git rev-parse HEAD) + +if [[ "$actual_rev" != "$expected_hacl_star_rev" ]]; then + echo "WARNING: HACL* in '$hacl_dir' is at revision:" >&2 + echo " $actual_rev" >&2 + echo "but expected revision:" >&2 + echo " $expected_hacl_star_rev" >&2 + echo "Edit the expected rev if the changes pulled in are what you want." +fi + +# Step 1: copy files + +declare -a dist_files +dist_files=( + Hacl_Streaming_SHA2.h + internal/Hacl_SHA2_Generic.h + Hacl_Streaming_SHA2.c +) + +declare -a include_files +include_files=( + include/krml/lowstar_endianness.h + include/krml/internal/target.h +) + +declare -a lib_files +lib_files=( + krmllib/dist/minimal/FStar_UInt_8_16_32_64.h +) + +# C files for the algorithms themselves: current directory +(cd "$hacl_dir/dist/gcc-compatible" && tar cf - "${dist_files[@]}") | tar xf - + +# Support header files (e.g. endianness macros): stays in include/ +(cd "$hacl_dir/dist/karamel" && tar cf - "${include_files[@]}") | tar xf - + +# Special treatment: we don't bother with an extra directory and move krmllib +# files to the same include directory +for f in "${lib_files[@]}"; do + cp "$hacl_dir/dist/karamel/$f" include/krml/ +done + +# Step 2: some in-place modifications to keep things simple and minimal + +# This is basic, but refreshes of the vendored HACL code are infrequent, so +# let's not over-engineer this. +if [[ $(uname) == "Darwin" ]]; then + # You're already running with homebrew or macports to satisfy the + # bash>=4 requirement, so requiring GNU sed is entirely reasonable. + sed=gsed +else + sed=sed +fi + +readarray -t all_files < <(find . -name '*.h' -or -name '*.c') + +# types.h is a simple wrapper that defines the uint128 type then proceeds to +# include FStar_UInt_8_16_32_64.h; we jump the types.h step since our current +# selection of algorithms does not necessitate the use of uint128 +$sed -i 's!#include.*types.h"!#include "krml/FStar_UInt_8_16_32_64.h"!g' "${all_files[@]}" +$sed -i 's!#include.*compat.h"!!g' "${all_files[@]}" + +# FStar_UInt_8_16_32_64 contains definitions useful in the general case, but not +# for us; trim! +$sed -i -z 's!\(extern\|typedef\)[^;]*;\n\n!!g' include/krml/FStar_UInt_8_16_32_64.h + +# This contains static inline prototypes that are defined in +# FStar_UInt_8_16_32_64; they are by default repeated for safety of separate +# compilation, but this is not necessary. +$sed -i 's!#include.*Hacl_Krmllib.h"!!g' "${all_files[@]}" + +# This header is useful for *other* algorithms that refer to SHA2, e.g. Ed25519 +# which needs to compute a digest of a message before signing it. Here, since no +# other algorithm builds upon SHA2, this internal header is useless (and is not +# included in $dist_files). +$sed -i 's!#include.*internal/Hacl_Streaming_SHA2.h"!#include "Hacl_Streaming_SHA2.h"!g' "${all_files[@]}" + +# The SHA2 file contains all variants of SHA2. We strip 384 and 512 for the time +# being, to be included later. +# This regexp matches a separator (two new lines), followed by: +# +# * +# ... 384 or 512 ... { +# * +# } +# +# The first non-empty lines are the comment block. The second ... may spill over +# the next following lines if the arguments are printed in one-per-line mode. +$sed -i -z 's/\n\n\([^\n]\+\n\)*[^\n]*\(384\|512\)[^{]*{\n\?\( [^\n]*\n\)*}//g' Hacl_Streaming_SHA2.c + +# Same thing with function prototypes +$sed -i -z 's/\n\n\([^\n]\+\n\)*[^\n]*\(384\|512\)[^;]*;//g' Hacl_Streaming_SHA2.h + +# Use globally unique names for the Hacl_ C APIs to avoid linkage conflicts. +$sed -i -z 's!#include \n!#include \n#include "python_hacl_namespaces.h"\n!' Hacl_Streaming_SHA2.h + +# Finally, we remove a bunch of ifdefs from target.h that are, again, useful in +# the general case, but not exercised by the subset of HACL* that we vendor. +$sed -z -i 's!#ifndef KRML_\(HOST_PRINTF\|HOST_EXIT\|PRE_ALIGN\|POST_ALIGN\|ALIGNED_MALLOC\|ALIGNED_FREE\|HOST_TIME\)\n\(\n\|# [^\n]*\n\|[^#][^\n]*\n\)*#endif\n\n!!g' include/krml/internal/target.h +$sed -z -i 's!\n\n\([^#][^\n]*\n\)*#define KRML_\(EABORT\|EXIT\|CHECK_SIZE\)[^\n]*\(\n [^\n]*\)*!!g' include/krml/internal/target.h +$sed -z -i 's!\n\n\([^#][^\n]*\n\)*#if [^\n]*\n\( [^\n]*\n\)*#define KRML_\(EABORT\|EXIT\|CHECK_SIZE\)[^\n]*\(\n [^\n]*\)*!!g' include/krml/internal/target.h +$sed -z -i 's!\n\n\([^#][^\n]*\n\)*#if [^\n]*\n\( [^\n]*\n\)*# define _\?KRML_\(DEPRECATED\|CHECK_SIZE_PRAGMA\|HOST_EPRINTF\|HOST_SNPRINTF\)[^\n]*\n\([^#][^\n]*\n\|#el[^\n]*\n\|# [^\n]*\n\)*#endif!!g' include/krml/internal/target.h + +echo "Updated; verify all is okay using git diff and git status." diff --git a/Modules/sha256module.c b/Modules/sha256module.c index 17ee86683b7a89..630e4bf03bbe96 100644 --- a/Modules/sha256module.c +++ b/Modules/sha256module.c @@ -31,29 +31,33 @@ class SHA256Type "SHAobject *" "&PyType_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=71a39174d4f0a744]*/ -/* Some useful types */ -typedef unsigned char SHA_BYTE; -typedef uint32_t SHA_INT32; /* 32-bit integer */ - -/* The SHA block size and message digest sizes, in bytes */ +/* The SHA block size and maximum message digest sizes, in bytes */ #define SHA_BLOCKSIZE 64 -#define SHA_DIGESTSIZE 32 +#define SHA_DIGESTSIZE 32 + +/* The SHA2-224 and SHA2-256 implementations defer to the HACL* verified + * library. */ -/* The structure for storing SHA info */ +#include "_hacl/Hacl_Streaming_SHA2.h" typedef struct { - PyObject_HEAD - SHA_INT32 digest[8]; /* Message digest */ - SHA_INT32 count_lo, count_hi; /* 64-bit bit count */ - SHA_BYTE data[SHA_BLOCKSIZE]; /* SHA data buffer */ - int local; /* unprocessed amount in data */ - int digestsize; + PyObject_HEAD + // Even though one could conceivably perform run-type checks to tell apart a + // sha224_type from a sha256_type (and thus deduce the digest size), we must + // keep this field because it's exposed as a member field on the underlying + // python object. + // TODO: could we transform this into a getter and get rid of the redundant + // field? + int digestsize; + Hacl_Streaming_SHA2_state_sha2_256 *state; } SHAobject; #include "clinic/sha256module.c.h" +/* We shall use run-time type information in the remainder of this module to + * tell apart SHA2-224 and SHA2-256 */ typedef struct { PyTypeObject* sha224_type; PyTypeObject* sha256_type; @@ -67,321 +71,12 @@ _sha256_get_state(PyObject *module) return (_sha256_state *)state; } -/* When run on a little-endian CPU we need to perform byte reversal on an - array of longwords. */ - -#if PY_LITTLE_ENDIAN -static void longReverse(SHA_INT32 *buffer, int byteCount) -{ - byteCount /= sizeof(*buffer); - for (; byteCount--; buffer++) { - *buffer = _Py_bswap32(*buffer); - } -} -#endif - static void SHAcopy(SHAobject *src, SHAobject *dest) { - dest->local = src->local; dest->digestsize = src->digestsize; - dest->count_lo = src->count_lo; - dest->count_hi = src->count_hi; - memcpy(dest->digest, src->digest, sizeof(src->digest)); - memcpy(dest->data, src->data, sizeof(src->data)); -} - - -/* ------------------------------------------------------------------------ - * - * This code for the SHA-256 algorithm was noted as public domain. The - * original headers are pasted below. - * - * Several changes have been made to make it more compatible with the - * Python environment and desired interface. - * - */ - -/* LibTomCrypt, modular cryptographic library -- Tom St Denis - * - * LibTomCrypt is a library that provides various cryptographic - * algorithms in a highly modular and flexible manner. - * - * The library is free for all purposes without any express - * guarantee it works. - * - * Tom St Denis, tomstdenis@iahu.ca, https://www.libtom.net - */ - - -/* SHA256 by Tom St Denis */ - -/* Various logical functions */ -#define ROR(x, y)\ -( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | \ -((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) -#define Ch(x,y,z) (z ^ (x & (y ^ z))) -#define Maj(x,y,z) (((x | y) & z) | (x & y)) -#define S(x, n) ROR((x),(n)) -#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) -#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) -#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) -#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) -#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) - - -static void -sha_transform(SHAobject *sha_info) -{ - int i; - SHA_INT32 S[8], W[64], t0, t1; - - memcpy(W, sha_info->data, sizeof(sha_info->data)); -#if PY_LITTLE_ENDIAN - longReverse(W, (int)sizeof(sha_info->data)); -#endif - - for (i = 16; i < 64; ++i) { - W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; - } - for (i = 0; i < 8; ++i) { - S[i] = sha_info->digest[i]; - } - - /* Compress */ -#define RND(a,b,c,d,e,f,g,h,i,ki) \ - t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \ - t1 = Sigma0(a) + Maj(a, b, c); \ - d += t0; \ - h = t0 + t1; - - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2); - -#undef RND - - /* feedback */ - for (i = 0; i < 8; i++) { - sha_info->digest[i] = sha_info->digest[i] + S[i]; - } - -} - - - -/* initialize the SHA digest */ - -static void -sha_init(SHAobject *sha_info) -{ - sha_info->digest[0] = 0x6A09E667L; - sha_info->digest[1] = 0xBB67AE85L; - sha_info->digest[2] = 0x3C6EF372L; - sha_info->digest[3] = 0xA54FF53AL; - sha_info->digest[4] = 0x510E527FL; - sha_info->digest[5] = 0x9B05688CL; - sha_info->digest[6] = 0x1F83D9ABL; - sha_info->digest[7] = 0x5BE0CD19L; - sha_info->count_lo = 0L; - sha_info->count_hi = 0L; - sha_info->local = 0; - sha_info->digestsize = 32; + dest->state = Hacl_Streaming_SHA2_copy_256(src->state); } -static void -sha224_init(SHAobject *sha_info) -{ - sha_info->digest[0] = 0xc1059ed8L; - sha_info->digest[1] = 0x367cd507L; - sha_info->digest[2] = 0x3070dd17L; - sha_info->digest[3] = 0xf70e5939L; - sha_info->digest[4] = 0xffc00b31L; - sha_info->digest[5] = 0x68581511L; - sha_info->digest[6] = 0x64f98fa7L; - sha_info->digest[7] = 0xbefa4fa4L; - sha_info->count_lo = 0L; - sha_info->count_hi = 0L; - sha_info->local = 0; - sha_info->digestsize = 28; -} - - -/* update the SHA digest */ - -static void -sha_update(SHAobject *sha_info, SHA_BYTE *buffer, Py_ssize_t count) -{ - Py_ssize_t i; - SHA_INT32 clo; - - clo = sha_info->count_lo + ((SHA_INT32) count << 3); - if (clo < sha_info->count_lo) { - ++sha_info->count_hi; - } - sha_info->count_lo = clo; - sha_info->count_hi += (SHA_INT32) count >> 29; - if (sha_info->local) { - i = SHA_BLOCKSIZE - sha_info->local; - if (i > count) { - i = count; - } - memcpy(((SHA_BYTE *) sha_info->data) + sha_info->local, buffer, i); - count -= i; - buffer += i; - sha_info->local += (int)i; - if (sha_info->local == SHA_BLOCKSIZE) { - sha_transform(sha_info); - } - else { - return; - } - } - while (count >= SHA_BLOCKSIZE) { - memcpy(sha_info->data, buffer, SHA_BLOCKSIZE); - buffer += SHA_BLOCKSIZE; - count -= SHA_BLOCKSIZE; - sha_transform(sha_info); - } - memcpy(sha_info->data, buffer, count); - sha_info->local = (int)count; -} - -/* finish computing the SHA digest */ - -static void -sha_final(unsigned char digest[SHA_DIGESTSIZE], SHAobject *sha_info) -{ - int count; - SHA_INT32 lo_bit_count, hi_bit_count; - - lo_bit_count = sha_info->count_lo; - hi_bit_count = sha_info->count_hi; - count = (int) ((lo_bit_count >> 3) & 0x3f); - ((SHA_BYTE *) sha_info->data)[count++] = 0x80; - if (count > SHA_BLOCKSIZE - 8) { - memset(((SHA_BYTE *) sha_info->data) + count, 0, - SHA_BLOCKSIZE - count); - sha_transform(sha_info); - memset((SHA_BYTE *) sha_info->data, 0, SHA_BLOCKSIZE - 8); - } - else { - memset(((SHA_BYTE *) sha_info->data) + count, 0, - SHA_BLOCKSIZE - 8 - count); - } - - /* GJS: note that we add the hi/lo in big-endian. sha_transform will - swap these values into host-order. */ - sha_info->data[56] = (hi_bit_count >> 24) & 0xff; - sha_info->data[57] = (hi_bit_count >> 16) & 0xff; - sha_info->data[58] = (hi_bit_count >> 8) & 0xff; - sha_info->data[59] = (hi_bit_count >> 0) & 0xff; - sha_info->data[60] = (lo_bit_count >> 24) & 0xff; - sha_info->data[61] = (lo_bit_count >> 16) & 0xff; - sha_info->data[62] = (lo_bit_count >> 8) & 0xff; - sha_info->data[63] = (lo_bit_count >> 0) & 0xff; - sha_transform(sha_info); - digest[ 0] = (unsigned char) ((sha_info->digest[0] >> 24) & 0xff); - digest[ 1] = (unsigned char) ((sha_info->digest[0] >> 16) & 0xff); - digest[ 2] = (unsigned char) ((sha_info->digest[0] >> 8) & 0xff); - digest[ 3] = (unsigned char) ((sha_info->digest[0] ) & 0xff); - digest[ 4] = (unsigned char) ((sha_info->digest[1] >> 24) & 0xff); - digest[ 5] = (unsigned char) ((sha_info->digest[1] >> 16) & 0xff); - digest[ 6] = (unsigned char) ((sha_info->digest[1] >> 8) & 0xff); - digest[ 7] = (unsigned char) ((sha_info->digest[1] ) & 0xff); - digest[ 8] = (unsigned char) ((sha_info->digest[2] >> 24) & 0xff); - digest[ 9] = (unsigned char) ((sha_info->digest[2] >> 16) & 0xff); - digest[10] = (unsigned char) ((sha_info->digest[2] >> 8) & 0xff); - digest[11] = (unsigned char) ((sha_info->digest[2] ) & 0xff); - digest[12] = (unsigned char) ((sha_info->digest[3] >> 24) & 0xff); - digest[13] = (unsigned char) ((sha_info->digest[3] >> 16) & 0xff); - digest[14] = (unsigned char) ((sha_info->digest[3] >> 8) & 0xff); - digest[15] = (unsigned char) ((sha_info->digest[3] ) & 0xff); - digest[16] = (unsigned char) ((sha_info->digest[4] >> 24) & 0xff); - digest[17] = (unsigned char) ((sha_info->digest[4] >> 16) & 0xff); - digest[18] = (unsigned char) ((sha_info->digest[4] >> 8) & 0xff); - digest[19] = (unsigned char) ((sha_info->digest[4] ) & 0xff); - digest[20] = (unsigned char) ((sha_info->digest[5] >> 24) & 0xff); - digest[21] = (unsigned char) ((sha_info->digest[5] >> 16) & 0xff); - digest[22] = (unsigned char) ((sha_info->digest[5] >> 8) & 0xff); - digest[23] = (unsigned char) ((sha_info->digest[5] ) & 0xff); - digest[24] = (unsigned char) ((sha_info->digest[6] >> 24) & 0xff); - digest[25] = (unsigned char) ((sha_info->digest[6] >> 16) & 0xff); - digest[26] = (unsigned char) ((sha_info->digest[6] >> 8) & 0xff); - digest[27] = (unsigned char) ((sha_info->digest[6] ) & 0xff); - digest[28] = (unsigned char) ((sha_info->digest[7] >> 24) & 0xff); - digest[29] = (unsigned char) ((sha_info->digest[7] >> 16) & 0xff); - digest[30] = (unsigned char) ((sha_info->digest[7] >> 8) & 0xff); - digest[31] = (unsigned char) ((sha_info->digest[7] ) & 0xff); -} - -/* - * End of copied SHA code. - * - * ------------------------------------------------------------------------ - */ - - static SHAobject * newSHA224object(_sha256_state *state) { @@ -409,14 +104,31 @@ SHA_traverse(PyObject *ptr, visitproc visit, void *arg) } static void -SHA_dealloc(PyObject *ptr) +SHA_dealloc(SHAobject *ptr) { + Hacl_Streaming_SHA2_free_256(ptr->state); PyTypeObject *tp = Py_TYPE(ptr); PyObject_GC_UnTrack(ptr); PyObject_GC_Del(ptr); Py_DECREF(tp); } +/* HACL* takes a uint32_t for the length of its parameter, but Py_ssize_t can be + * 64 bits. */ +static void update_256(Hacl_Streaming_SHA2_state_sha2_256 *state, uint8_t *buf, Py_ssize_t len) { + /* Note: we explicitly ignore the error code on the basis that it would take > + * 1 billion years to overflow the maximum admissible length for SHA2-256 + * (namely, 2^61-1 bytes). */ + while (len > UINT32_MAX) { + Hacl_Streaming_SHA2_update_256(state, buf, UINT32_MAX); + len -= UINT32_MAX; + buf += UINT32_MAX; + } + /* Cast to uint32_t is safe: upon exiting the loop, len <= UINT32_MAX, and + * therefore fits in a uint32_t */ + Hacl_Streaming_SHA2_update_256(state, buf, (uint32_t) len); +} + /* External methods for a hash object */ @@ -458,11 +170,10 @@ static PyObject * SHA256Type_digest_impl(SHAobject *self) /*[clinic end generated code: output=46616a5e909fbc3d input=f1f4cfea5cbde35c]*/ { - unsigned char digest[SHA_DIGESTSIZE]; - SHAobject temp; - - SHAcopy(self, &temp); - sha_final(digest, &temp); + uint8_t digest[SHA_DIGESTSIZE]; + // HACL performs copies under the hood so that self->state remains valid + // after this call. + Hacl_Streaming_SHA2_finish_256(self->state, digest); return PyBytes_FromStringAndSize((const char *)digest, self->digestsize); } @@ -476,13 +187,8 @@ static PyObject * SHA256Type_hexdigest_impl(SHAobject *self) /*[clinic end generated code: output=725f8a7041ae97f3 input=0cc4c714693010d1]*/ { - unsigned char digest[SHA_DIGESTSIZE]; - SHAobject temp; - - /* Get the raw (binary) digest value */ - SHAcopy(self, &temp); - sha_final(digest, &temp); - + uint8_t digest[SHA_DIGESTSIZE]; + Hacl_Streaming_SHA2_finish_256(self->state, digest); return _Py_strhex((const char *)digest, self->digestsize); } @@ -503,7 +209,7 @@ SHA256Type_update(SHAobject *self, PyObject *obj) GET_BUFFER_VIEW_OR_ERROUT(obj, &buf); - sha_update(self, buf.buf, buf.len); + update_256(self->state, buf.buf, buf.len); PyBuffer_Release(&buf); Py_RETURN_NONE; @@ -524,12 +230,12 @@ SHA256_get_block_size(PyObject *self, void *closure) } static PyObject * -SHA256_get_name(PyObject *self, void *closure) +SHA256_get_name(SHAobject *self, void *closure) { - if (((SHAobject *)self)->digestsize == 32) - return PyUnicode_FromStringAndSize("sha256", 6); - else + if (self->digestsize == 28) { return PyUnicode_FromStringAndSize("sha224", 6); + } + return PyUnicode_FromStringAndSize("sha256", 6); } static PyGetSetDef SHA_getseters[] = { @@ -606,7 +312,8 @@ _sha256_sha256_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } - sha_init(new); + new->state = Hacl_Streaming_SHA2_create_in_256(); + new->digestsize = 32; if (PyErr_Occurred()) { Py_DECREF(new); @@ -616,7 +323,7 @@ _sha256_sha256_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } if (string) { - sha_update(new, buf.buf, buf.len); + update_256(new->state, buf.buf, buf.len); PyBuffer_Release(&buf); } @@ -651,7 +358,8 @@ _sha256_sha224_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } - sha224_init(new); + new->state = Hacl_Streaming_SHA2_create_in_224(); + new->digestsize = 28; if (PyErr_Occurred()) { Py_DECREF(new); @@ -661,7 +369,7 @@ _sha256_sha224_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } if (string) { - sha_update(new, buf.buf, buf.len); + update_256(new->state, buf.buf, buf.len); PyBuffer_Release(&buf); } diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 397d22abe23503..e8e9ff01e306bc 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -100,7 +100,7 @@ /Zm200 %(AdditionalOptions) - $(PySourcePath)Python;%(AdditionalIncludeDirectories) + $(PySourcePath)Modules\_hacl\include;$(PySourcePath)Modules\_hacl\internal;$(PySourcePath)Python;%(AdditionalIncludeDirectories) $(zlibDir);%(AdditionalIncludeDirectories) _USRDLL;Py_BUILD_CORE;Py_BUILD_CORE_BUILTIN;Py_ENABLE_SHARED;MS_DLL_ID="$(SysWinVer)";%(PreprocessorDefinitions) _Py_HAVE_ZLIB;%(PreprocessorDefinitions) @@ -407,6 +407,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index bcbedcc3235b3e..4820db6f2c32dc 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -866,6 +866,9 @@ Modules + + Modules + Modules diff --git a/configure b/configure index aef8103085bc20..97694c602d1cc8 100755 --- a/configure +++ b/configure @@ -24426,6 +24426,7 @@ SRCDIRS="\ Modules/_ctypes \ Modules/_decimal \ Modules/_decimal/libmpdec \ + Modules/_hacl \ Modules/_io \ Modules/_multiprocessing \ Modules/_sha3 \ @@ -26966,7 +26967,7 @@ fi as_fn_append MODULE_BLOCK "MODULE__SHA256_STATE=$py_cv_module__sha256$as_nl" if test "x$py_cv_module__sha256" = xyes; then : - + as_fn_append MODULE_BLOCK "MODULE__SHA256_CFLAGS=-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE$as_nl" fi diff --git a/configure.ac b/configure.ac index 010bca855f0059..09369b985b33f6 100644 --- a/configure.ac +++ b/configure.ac @@ -6508,6 +6508,7 @@ SRCDIRS="\ Modules/_ctypes \ Modules/_decimal \ Modules/_decimal/libmpdec \ + Modules/_hacl \ Modules/_io \ Modules/_multiprocessing \ Modules/_sha3 \ @@ -7197,7 +7198,9 @@ dnl By default we always compile these even when OpenSSL is available dnl (issue #14693). The modules are small. PY_STDLIB_MOD([_md5], [test "$with_builtin_md5" = yes]) PY_STDLIB_MOD([_sha1], [test "$with_builtin_sha1" = yes]) -PY_STDLIB_MOD([_sha256], [test "$with_builtin_sha256" = yes]) +PY_STDLIB_MOD([_sha256], + [test "$with_builtin_sha256" = yes], [], + [-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE]) PY_STDLIB_MOD([_sha512], [test "$with_builtin_sha512" = yes]) PY_STDLIB_MOD([_sha3], [test "$with_builtin_sha3" = yes]) PY_STDLIB_MOD([_blake2], From 694e346a01f407c7f78138331db006dea79f82a8 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 6 Feb 2023 21:03:58 -0800 Subject: [PATCH 101/225] gh-98831: Move DSL documentation here from ideas repo (#101629) --- Tools/cases_generator/README.md | 9 +- .../cases_generator/interpreter_definition.md | 409 ++++++++++++++++++ 2 files changed, 415 insertions(+), 3 deletions(-) create mode 100644 Tools/cases_generator/interpreter_definition.md diff --git a/Tools/cases_generator/README.md b/Tools/cases_generator/README.md index dc055ead1941cd..c595a932ac4470 100644 --- a/Tools/cases_generator/README.md +++ b/Tools/cases_generator/README.md @@ -1,5 +1,8 @@ # Tooling to generate interpreters +Documentation for the instruction definitions in `Python/bytecodes.c` +("the DSL") is [here](interpreter_definition.md). + What's currently here: - `lexer.py`: lexer for C, originally written by Mark Shannon @@ -7,10 +10,10 @@ What's currently here: - `parser.py`: Parser for instruction definition DSL; main class `Parser` - `generate_cases.py`: driver script to read `Python/bytecodes.c` and write `Python/generated_cases.c.h` +- `test_generator.py`: tests, require manual running using `pytest` -The DSL for the instruction definitions in `Python/bytecodes.c` is described -[here](https://github.com/faster-cpython/ideas/blob/main/3.12/interpreter_definition.md). -Note that there is some dummy C code at the top and bottom of the file +Note that there is some dummy C code at the top and bottom of +`Python/bytecodes.c` to fool text editors like VS Code into believing this is valid C code. ## A bit about the parser diff --git a/Tools/cases_generator/interpreter_definition.md b/Tools/cases_generator/interpreter_definition.md new file mode 100644 index 00000000000000..c7bd38d32ff411 --- /dev/null +++ b/Tools/cases_generator/interpreter_definition.md @@ -0,0 +1,409 @@ +# A higher level definition of the bytecode interpreter + +## Abstract + +The CPython interpreter is defined in C, meaning that the semantics of the +bytecode instructions, the dispatching mechanism, error handling, and +tracing and instrumentation are all intermixed. + +This document proposes defining a custom C-like DSL for defining the +instruction semantics and tools for generating the code deriving from +the instruction definitions. + +These tools would be used to: +* Generate the main interpreter (done) +* Generate the tier 2 interpreter +* Generate documentation for instructions +* Generate metadata about instructions, such as stack use (done). + +Having a single definition file ensures that there is a single source +of truth for bytecode semantics. + +Other tools that operate on bytecodes, like `frame.setlineno` +and the `dis` module, will be derived from the common semantic +definition, reducing errors. + +## Motivation + +The bytecode interpreter of CPython has traditionally been defined as standard +C code, but with a lot of macros. +The presence of these macros and the nature of bytecode interpreters means +that the interpreter is effectively defined in a domain specific language (DSL). + +Rather than using an ad-hoc DSL embedded in the C code for the interpreter, +a custom DSL should be defined and the semantics of the bytecode instructions, +and the instructions defined in that DSL. + +Generating the interpreter decouples low-level details of dispatching +and error handling from the semantics of the instructions, resulting +in more maintainable code and a potentially faster interpreter. + +It also provides the ability to create and check optimizers and optimization +passes from the semantic definition, reducing errors. + +## Rationale + +As we improve the performance of CPython, we need to optimize larger regions +of code, use more complex optimizations and, ultimately, translate to machine +code. + +All of these steps introduce the possibility of more bugs, and require more code +to be written. One way to mitigate this is through the use of code generators. +Code generators decouple the debugging of the code (the generator) from checking +the correctness (the DSL input). + +For example, we are likely to want a new interpreter for the tier 2 optimizer +to be added in 3.12. That interpreter will have a different API, a different +set of instructions and potentially different dispatching mechanism. +But the instructions it will interpret will be built from the same building +blocks as the instructions for the tier 1 (PEP 659) interpreter. + +Rewriting all the instructions is tedious and error-prone, and changing the +instructions is a maintenance headache as both versions need to be kept in sync. + +By using a code generator and using a common source for the instructions, or +parts of instructions, we can reduce the potential for errors considerably. + + +## Specification + +This specification is at an early stage and is likely to change considerably. + +Syntax +------ + +Each op definition has a kind, a name, a stack and instruction stream effect, +and a piece of C code describing its semantics:: + +``` + file: + (definition | family)+ + + definition: + "inst" "(" NAME ["," stack_effect] ")" "{" C-code "}" + | + "op" "(" NAME "," stack_effect ")" "{" C-code "}" + | + "macro" "(" NAME ")" "=" uop ("+" uop)* ";" + | + "super" "(" NAME ")" "=" NAME ("+" NAME)* ";" + + stack_effect: + "(" [inputs] "--" [outputs] ")" + + inputs: + input ("," input)* + + outputs: + output ("," output)* + + input: + object | stream | array + + output: + object | array + + uop: + NAME | stream + + object: + NAME [":" type] [ "if" "(" C-expression ")" ] + + type: + NAME + + stream: + NAME "/" size + + size: + INTEGER + + array: + object "[" C-expression "]" + + family: + "family" "(" NAME ")" = "{" NAME ("," NAME)+ "}" ";" +``` + +The following definitions may occur: + +* `inst`: A normal instruction, as previously defined by `TARGET(NAME)` in `ceval.c`. +* `op`: A part instruction from which macros can be constructed. +* `macro`: A bytecode instruction constructed from ops and cache effects. +* `super`: A super-instruction, such as `LOAD_FAST__LOAD_FAST`, constructed from + normal or macro instructions. + +`NAME` can be any ASCII identifier that is a C identifier and not a C or Python keyword. +`foo_1` is legal. `$` is not legal, nor is `struct` or `class`. + +The optional `type` in an `object` is the C type. It defaults to `PyObject *`. +The objects before the "--" are the objects on top of the the stack at the start +of the instruction. Those after the "--" are the objects on top of the the stack +at the end of the instruction. + +An `inst` without `stack_effect` is a transitional form to allow the original C code +definitions to be copied. It lacks information to generate anything other than the +interpreter, but is useful for initial porting of code. + +Stack effect names may be `unused`, indicating the space is to be reserved +but no use of it will be made in the instruction definition. +This is useful to ensure that all instructions in a family have the same +stack effect. + +The number in a `stream` define how many codeunits are consumed from the +instruction stream. It returns a 16, 32 or 64 bit value. +If the name is `unused` the size can be any value and that many codeunits +will be skipped in the instruction stream. + +By convention cache effects (`stream`) must precede the input effects. + +The name `oparg` is pre-defined as a 32 bit value fetched from the instruction stream. + +The C code may include special functions that are understood by the tools as +part of the DSL. + +Those functions include: + +* `DEOPT_IF(cond, instruction)`. Deoptimize if `cond` is met. +* `ERROR_IF(cond, label)`. Jump to error handler if `cond` is true. +* `DECREF_INPUTS()`. Generate `Py_DECREF()` calls for the input stack effects. + +Variables can either be defined in the input, output, or in the C code. +Variables defined in the input may not be assigned in the C code. +If an `ERROR_IF` occurs, all values will be removed from the stack; +they must already be `DECREF`'ed by the code block. +If a `DEOPT_IF` occurs, no values will be removed from the stack or +the instruction stream; no values must have been `DECREF`'ed or created. + +These requirements result in the following constraints on the use of +`DEOPT_IF` and `ERROR_IF` in any instruction's code block: + +1. Until the last `DEOPT_IF`, no objects may be allocated, `INCREF`ed, + or `DECREF`ed. +2. Before the first `ERROR_IF`, all input values must be `DECREF`ed, + and no objects may be allocated or `INCREF`ed, with the exception + of attempting to create an object and checking for success using + `ERROR_IF(result == NULL, label)`. (TODO: Unclear what to do with + intermediate results.) +3. No `DEOPT_IF` may follow an `ERROR_IF` in the same block. + +Semantics +--------- + +The underlying execution model is a stack machine. +Operations pop values from the stack, and push values to the stack. +They also can look at, and consume, values from the instruction stream. + +All members of a family must have the same stack and instruction stream effect. + +Examples +-------- + +(Another source of examples can be found in the [tests](test_generator.py).) + +Some examples: + +### Output stack effect +```C + inst ( LOAD_FAST, (-- value) ) { + value = frame->f_localsplus[oparg]; + Py_INCREF(value); + } +``` +This would generate: +```C + TARGET(LOAD_FAST) { + PyObject *value; + value = frame->f_localsplus[oparg]; + Py_INCREF(value); + PUSH(value); + DISPATCH(); + } +``` + +### Input stack effect +```C + inst ( STORE_FAST, (value --) ) { + SETLOCAL(oparg, value); + } +``` +This would generate: +```C + TARGET(STORE_FAST) { + PyObject *value = PEEK(1); + SETLOCAL(oparg, value); + STACK_SHRINK(1); + DISPATCH(); + } +``` + +### Super-instruction definition + +```C + super ( LOAD_FAST__LOAD_FAST ) = LOAD_FAST + LOAD_FAST ; +``` +This might get translated into the following: +```C + TARGET(LOAD_FAST__LOAD_FAST) { + PyObject *value; + value = frame->f_localsplus[oparg]; + Py_INCREF(value); + PUSH(value); + NEXTOPARG(); + next_instr++; + value = frame->f_localsplus[oparg]; + Py_INCREF(value); + PUSH(value); + DISPATCH(); + } +``` + +### Input stack effect and cache effect +```C + op ( CHECK_OBJECT_TYPE, (owner, type_version/2 -- owner) ) { + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version); + } +``` +This might become (if it was an instruction): +```C + TARGET(CHECK_OBJECT_TYPE) { + PyObject *owner = PEEK(1); + uint32 type_version = read32(next_instr); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version); + next_instr += 2; + DISPATCH(); + } +``` + +### More examples + +For explanations see "Generating the interpreter" below.) +```C + op ( CHECK_HAS_INSTANCE_VALUES, (owner -- owner) ) { + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + DEOPT_IF(!_PyDictOrValues_IsValues(dorv)); + } +``` +```C + op ( LOAD_INSTANCE_VALUE, (owner, index/1 -- null if (oparg & 1), res) ) { + res = _PyDictOrValues_GetValues(dorv)->values[index]; + DEOPT_IF(res == NULL); + Py_INCREF(res); + null = NULL; + Py_DECREF(owner); + } +``` +```C + macro ( LOAD_ATTR_INSTANCE_VALUE ) = + counter/1 + CHECK_OBJECT_TYPE + CHECK_HAS_INSTANCE_VALUES + + LOAD_INSTANCE_VALUE + unused/4 ; +``` +```C + op ( LOAD_SLOT, (owner, index/1 -- null if (oparg & 1), res) ) { + char *addr = (char *)owner + index; + res = *(PyObject **)addr; + DEOPT_IF(res == NULL); + Py_INCREF(res); + null = NULL; + Py_DECREF(owner); + } +``` +```C + macro ( LOAD_ATTR_SLOT ) = counter/1 + CHECK_OBJECT_TYPE + LOAD_SLOT + unused/4; +``` +```C + inst ( BUILD_TUPLE, (items[oparg] -- tuple) ) { + tuple = _PyTuple_FromArraySteal(items, oparg); + ERROR_IF(tuple == NULL, error); + } +``` +```C + inst ( PRINT_EXPR ) { + PyObject *value = POP(); + PyObject *hook = _PySys_GetAttr(tstate, &_Py_ID(displayhook)); + PyObject *res; + if (hook == NULL) { + _PyErr_SetString(tstate, PyExc_RuntimeError, + "lost sys.displayhook"); + Py_DECREF(value); + goto error; + } + res = PyObject_CallOneArg(hook, value); + Py_DECREF(value); + ERROR_IF(res == NULL); + Py_DECREF(res); + } +``` + +### Define an instruction family +These opcodes all share the same instruction format): +```C + family(load_attr) = { LOAD_ATTR, LOAD_ATTR_INSTANCE_VALUE, LOAD_SLOT } ; +``` + +Generating the interpreter +========================== + +The generated C code for a single instruction includes a preamble and dispatch at the end +which can be easily inserted. What is more complex is ensuring the correct stack effects +and not generating excess pops and pushes. + +For example, in `CHECK_HAS_INSTANCE_VALUES`, `owner` occurs in the input, so it cannot be +redefined. Thus it doesn't need to written and can be read without adjusting the stack pointer. +The C code generated for `CHECK_HAS_INSTANCE_VALUES` would look something like: + +```C + { + PyObject *owner = stack_pointer[-1]; + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + DEOPT_IF(!_PyDictOrValues_IsValues(dorv)); + } +``` + +When combining ops together to form instructions, temporary values should be used, +rather than popping and pushing, such that `LOAD_ATTR_SLOT` would look something like: + +```C + case LOAD_ATTR_SLOT: { + PyObject *s1 = stack_pointer[-1]; + /* CHECK_OBJECT_TYPE */ + { + PyObject *owner = s1; + uint32_t type_version = read32(next_instr + 1); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + if (tp->tp_version_tag != type_version) goto deopt; + } + /* LOAD_SLOT */ + { + PyObject *owner = s1; + uint16_t index = *(next_instr + 1 + 2); + char *addr = (char *)owner + index; + PyObject *null; + PyObject *res = *(PyObject **)addr; + if (res == NULL) goto deopt; + Py_INCREF(res); + null = NULL; + Py_DECREF(owner); + if (oparg & 1) { + stack_pointer[0] = null; + stack_pointer += 1; + } + s1 = res; + } + next_instr += (1 + 1 + 2 + 1 + 4); + stack_pointer[-1] = s1; + DISPATCH(); + } +``` + +Other tools +=========== + +From the instruction definitions we can generate the stack marking code used in `frame.set_lineno()`, +and the tables for use by disassemblers. + From c4de6b1d52304a0a9cdfafc1dad5098993710404 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Tue, 7 Feb 2023 00:25:42 -0500 Subject: [PATCH 102/225] gh-85747: Active voice & suggested edits, 'running/stopping loop' & 'callbacks' subsections of asyncio-eventloop.rst (#100270) Co-authored-by: C.A.M. Gerlach Co-authored-by: Terry Jan Reedy --- Doc/library/asyncio-eventloop.rst | 42 ++++++++++++++++++------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index db63a5dd11ad6e..f86e784288029c 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -186,19 +186,24 @@ Running and stopping the loop .. coroutinemethod:: loop.shutdown_default_executor(timeout=None) Schedule the closure of the default executor and wait for it to join all of - the threads in the :class:`ThreadPoolExecutor`. After calling this method, a - :exc:`RuntimeError` will be raised if :meth:`loop.run_in_executor` is called - while using the default executor. + the threads in the :class:`~concurrent.futures.ThreadPoolExecutor`. + Once this method has been called, + using the default executor with :meth:`loop.run_in_executor` + will raise a :exc:`RuntimeError`. - The *timeout* parameter specifies the amount of time the executor will - be given to finish joining. The default value is ``None``, which means the - executor will be given an unlimited amount of time. + The *timeout* parameter specifies the amount of time + (in :class:`float` seconds) the executor will be given to finish joining. + With the default, ``None``, + the executor is allowed an unlimited amount of time. - If the timeout duration is reached, a warning is emitted and executor is - terminated without waiting for its threads to finish joining. + If the *timeout* is reached, a :exc:`RuntimeWarning` is emitted + and the default executor is terminated + without waiting for its threads to finish joining. - Note that there is no need to call this function when - :func:`asyncio.run` is used. + .. note:: + + Do not call this method when using :func:`asyncio.run`, + as the latter handles default executor shutdown automatically. .. versionadded:: 3.9 @@ -213,22 +218,23 @@ Scheduling callbacks Schedule the *callback* :term:`callback` to be called with *args* arguments at the next iteration of the event loop. + Return an instance of :class:`asyncio.Handle`, + which can be used later to cancel the callback. + Callbacks are called in the order in which they are registered. Each callback will be called exactly once. - An optional keyword-only *context* argument allows specifying a + The optional keyword-only *context* argument specifies a custom :class:`contextvars.Context` for the *callback* to run in. - The current context is used when no *context* is provided. - - An instance of :class:`asyncio.Handle` is returned, which can be - used later to cancel the callback. + Callbacks use the current context when no *context* is provided. - This method is not thread-safe. + Unlike :meth:`call_soon_threadsafe`, this method is not thread-safe. .. method:: loop.call_soon_threadsafe(callback, *args, context=None) - A thread-safe variant of :meth:`call_soon`. Must be used to - schedule callbacks *from another thread*. + A thread-safe variant of :meth:`call_soon`. When scheduling callbacks from + another thread, this function *must* be used, since :meth:`call_soon` is not + thread-safe. Raises :exc:`RuntimeError` if called on a loop that's been closed. This can happen on a secondary thread when the main application is From ae62bddaf81176a7e55f95f18d55621c9c46c23d Mon Sep 17 00:00:00 2001 From: Matthieu Dartiailh Date: Tue, 7 Feb 2023 10:34:21 +0100 Subject: [PATCH 103/225] gh-101072: support default and kw default in PyEval_EvalCodeEx for 3.11+ (#101127) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Łukasz Langa --- Lib/test/test_capi/test_eval_code_ex.py | 56 +++++++ ...3-02-06-20-13-36.gh-issue-92173.RQE0mk.rst | 8 + Modules/_testcapimodule.c | 141 +++++++++++++++++- Objects/funcobject.c | 4 +- Python/ceval.c | 3 - 5 files changed, 206 insertions(+), 6 deletions(-) create mode 100644 Lib/test/test_capi/test_eval_code_ex.py create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst diff --git a/Lib/test/test_capi/test_eval_code_ex.py b/Lib/test/test_capi/test_eval_code_ex.py new file mode 100644 index 00000000000000..2d28e5289eff94 --- /dev/null +++ b/Lib/test/test_capi/test_eval_code_ex.py @@ -0,0 +1,56 @@ +import unittest + +from test.support import import_helper + + +# Skip this test if the _testcapi module isn't available. +_testcapi = import_helper.import_module('_testcapi') + + +class PyEval_EvalCodeExTests(unittest.TestCase): + + def test_simple(self): + def f(): + return a + + self.assertEqual(_testcapi.eval_code_ex(f.__code__, dict(a=1)), 1) + + # Need to force the compiler to use LOAD_NAME + # def test_custom_locals(self): + # def f(): + # return + + def test_with_args(self): + def f(a, b, c): + return a + + self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (1, 2, 3)), 1) + + def test_with_kwargs(self): + def f(a, b, c): + return a + + self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), dict(a=1, b=2, c=3)), 1) + + def test_with_default(self): + def f(a): + return a + + self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), {}, (1,)), 1) + + def test_with_kwarg_default(self): + def f(*, a): + return a + + self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), {}, (), dict(a=1)), 1) + + def test_with_closure(self): + a = 1 + def f(): + return a + + self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), {}, (), {}, f.__closure__), 1) + + +if __name__ == "__main__": + unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst new file mode 100644 index 00000000000000..2d991f6ca21b6f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst @@ -0,0 +1,8 @@ +macOS #.. section: IDLE #.. section: Tools/Demos #.. section: C API + +# Write your Misc/NEWS entry below. It should be a simple ReST paragraph. # +Don't start with "- Issue #: " or "- gh-issue-: " or that sort of +stuff. +########################################################################### + +Fix the ``defs`` and ``kwdefs`` arguments to :c:func:`PyEval_EvalCodeEx`. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 5e47f4975a2d54..5a6097ef0ac5a6 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2237,7 +2237,7 @@ dict_get_version(PyObject *self, PyObject *args) return NULL; _Py_COMP_DIAG_PUSH - _Py_COMP_DIAG_IGNORE_DEPR_DECLS + _Py_COMP_DIAG_IGNORE_DEPR_DECLS version = dict->ma_version_tag; _Py_COMP_DIAG_POP @@ -3064,6 +3064,144 @@ eval_get_func_desc(PyObject *self, PyObject *func) return PyUnicode_FromString(PyEval_GetFuncDesc(func)); } +static PyObject * +eval_eval_code_ex(PyObject *mod, PyObject *pos_args) +{ + PyObject *result = NULL; + PyObject *code; + PyObject *globals; + PyObject *locals = NULL; + PyObject *args = NULL; + PyObject *kwargs = NULL; + PyObject *defaults = NULL; + PyObject *kw_defaults = NULL; + PyObject *closure = NULL; + + PyObject **c_kwargs = NULL; + + if (!PyArg_UnpackTuple(pos_args, + "eval_code_ex", + 2, + 8, + &code, + &globals, + &locals, + &args, + &kwargs, + &defaults, + &kw_defaults, + &closure)) + { + goto exit; + } + + if (!PyCode_Check(code)) { + PyErr_SetString(PyExc_TypeError, + "code must be a Python code object"); + goto exit; + } + + if (!PyDict_Check(globals)) { + PyErr_SetString(PyExc_TypeError, "globals must be a dict"); + goto exit; + } + + if (locals && !PyMapping_Check(locals)) { + PyErr_SetString(PyExc_TypeError, "locals must be a mapping"); + goto exit; + } + if (locals == Py_None) { + locals = NULL; + } + + PyObject **c_args = NULL; + Py_ssize_t c_args_len = 0; + + if (args) + { + if (!PyTuple_Check(args)) { + PyErr_SetString(PyExc_TypeError, "args must be a tuple"); + goto exit; + } else { + c_args = &PyTuple_GET_ITEM(args, 0); + c_args_len = PyTuple_Size(args); + } + } + + Py_ssize_t c_kwargs_len = 0; + + if (kwargs) + { + if (!PyDict_Check(kwargs)) { + PyErr_SetString(PyExc_TypeError, "keywords must be a dict"); + goto exit; + } else { + c_kwargs_len = PyDict_Size(kwargs); + if (c_kwargs_len > 0) { + c_kwargs = PyMem_NEW(PyObject*, 2 * c_kwargs_len); + if (!c_kwargs) { + PyErr_NoMemory(); + goto exit; + } + + Py_ssize_t i = 0; + Py_ssize_t pos = 0; + + while (PyDict_Next(kwargs, + &pos, + &c_kwargs[i], + &c_kwargs[i + 1])) + { + i += 2; + } + c_kwargs_len = i / 2; + /* XXX This is broken if the caller deletes dict items! */ + } + } + } + + + PyObject **c_defaults = NULL; + Py_ssize_t c_defaults_len = 0; + + if (defaults && PyTuple_Check(defaults)) { + c_defaults = &PyTuple_GET_ITEM(defaults, 0); + c_defaults_len = PyTuple_Size(defaults); + } + + if (kw_defaults && !PyDict_Check(kw_defaults)) { + PyErr_SetString(PyExc_TypeError, "kw_defaults must be a dict"); + goto exit; + } + + if (closure && !PyTuple_Check(closure)) { + PyErr_SetString(PyExc_TypeError, "closure must be a tuple of cells"); + goto exit; + } + + + result = PyEval_EvalCodeEx( + code, + globals, + locals, + c_args, + c_args_len, + c_kwargs, + c_kwargs_len, + c_defaults, + c_defaults_len, + kw_defaults, + closure + ); + +exit: + if (c_kwargs) { + PyMem_DEL(c_kwargs); + } + + return result; +} + static PyObject * get_feature_macros(PyObject *self, PyObject *Py_UNUSED(args)) { @@ -3385,6 +3523,7 @@ static PyMethodDef TestMethods[] = { {"set_exc_info", test_set_exc_info, METH_VARARGS}, {"argparsing", argparsing, METH_VARARGS}, {"code_newempty", code_newempty, METH_VARARGS}, + {"eval_code_ex", eval_eval_code_ex, METH_VARARGS}, {"make_exception_with_doc", _PyCFunction_CAST(make_exception_with_doc), METH_VARARGS | METH_KEYWORDS}, {"make_memoryview_from_NULL_pointer", make_memoryview_from_NULL_pointer, diff --git a/Objects/funcobject.c b/Objects/funcobject.c index baa360381a7724..91a6b3dd40a232 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -87,8 +87,8 @@ _PyFunction_FromConstructor(PyFrameConstructor *constr) op->func_name = Py_NewRef(constr->fc_name); op->func_qualname = Py_NewRef(constr->fc_qualname); op->func_code = Py_NewRef(constr->fc_code); - op->func_defaults = NULL; - op->func_kwdefaults = NULL; + op->func_defaults = Py_XNewRef(constr->fc_defaults); + op->func_kwdefaults = Py_XNewRef(constr->fc_kwdefaults); op->func_closure = Py_XNewRef(constr->fc_closure); op->func_doc = Py_NewRef(Py_None); op->func_dict = NULL; diff --git a/Python/ceval.c b/Python/ceval.c index 2e6fed580dede4..ecb5bf9655553e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1761,9 +1761,6 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, } allargs = newargs; } - for (int i = 0; i < kwcount; i++) { - PyTuple_SET_ITEM(kwnames, i, Py_NewRef(kws[2*i])); - } PyFrameConstructor constr = { .fc_globals = globals, .fc_builtins = builtins, From 79903240480429a6e545177416a7b782b0e5b9bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Tue, 7 Feb 2023 10:50:39 +0100 Subject: [PATCH 104/225] [gh-101072] Fix Blurb for GH-101127 --- .../2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst index 2d991f6ca21b6f..6b98aac2a46545 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst @@ -1,8 +1,2 @@ -macOS #.. section: IDLE #.. section: Tools/Demos #.. section: C API - -# Write your Misc/NEWS entry below. It should be a simple ReST paragraph. # -Don't start with "- Issue #: " or "- gh-issue-: " or that sort of -stuff. -########################################################################### - -Fix the ``defs`` and ``kwdefs`` arguments to :c:func:`PyEval_EvalCodeEx`. +Fix the ``defs`` and ``kwdefs`` arguments to :c:func:`PyEval_EvalCodeEx` +and a reference leak in that function. From 3c67ec394faac79d260804d569a18fab43018af0 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Tue, 7 Feb 2023 13:17:26 +0100 Subject: [PATCH 105/225] Python 3.12.0a5 --- Include/patchlevel.h | 4 +- Lib/pydoc_data/topics.py | 14 +- Misc/NEWS.d/3.12.0a5.rst | 664 ++++++++++++++++++ ...2-08-30-10-16-31.gh-issue-96305.274i8B.rst | 2 - ...2-10-25-11-53-55.gh-issue-98636.e0RPAr.rst | 2 - ...2-10-27-09-57-12.gh-issue-98705.H11XmR.rst | 2 - ...-01-15-11-22-15.gh-issue-101060.0mYk9E.rst | 3 - ...-01-17-21-32-51.gh-issue-100340.i9zRGM.rst | 2 - ...-01-21-10-31-35.gh-issue-101152.xvM8pL.rst | 3 - ...3-01-26-19-02-11.gh-issue-77532.cXD8bg.rst | 1 - ...-02-02-23-43-46.gh-issue-101522.lnUDta.rst | 2 - ...-02-04-06-59-07.gh-issue-101282.7sQz5l.rst | 2 - .../2018-02-05-21-54-46.bpo-32780.Dtiz8z.rst | 3 - ...-01-03-14-33-23.gh-issue-100712.po6xyB.rst | 1 - ...-01-03-20-59-20.gh-issue-100726.W9huFl.rst | 1 - ...3-01-06-09-22-21.gh-issue-91351.iq2vZ_.rst | 5 - ...-01-10-14-11-17.gh-issue-100892.qfBVYI.rst | 1 - ...-01-10-16-59-33.gh-issue-100923.ypJAX-.rst | 2 - ...-01-11-22-52-19.gh-issue-100942.ontOy_.rst | 2 - ...-01-12-13-46-49.gh-issue-100982.mJ234s.rst | 4 - ...-01-13-12-56-20.gh-issue-100762.YvHaQJ.rst | 3 - ...-01-14-17-03-08.gh-issue-101037.9ATNuf.rst | 2 - ...-01-15-03-26-04.gh-issue-101046.g2CM4S.rst | 2 - ...-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst | 2 - ...-01-28-13-11-52.gh-issue-101266.AxV3OF.rst | 1 - ...-01-28-20-31-42.gh-issue-101372.8BcpCC.rst | 2 - ...-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst | 2 - ...3-01-30-11-56-09.gh-issue-59956.7xqnC_.rst | 3 - ...3-02-06-20-13-36.gh-issue-92173.RQE0mk.rst | 2 - ...2-06-19-22-04-47.gh-issue-88324.GHhSQ1.rst | 3 - ...8-05-21-17-18-00.gh-issue-77772.Fhg84L.rst | 3 - .../2020-04-18-17-45-03.bpo-29847.Uxtbq0.rst | 1 - .../2020-11-20-21-06-08.bpo-40077.M-iZq3.rst | 1 - .../2022-02-05-12-01-58.bpo-38941.8IhvyG.rst | 4 - ...2-07-22-13-38-37.gh-issue-94518._ZP0cz.rst | 2 - ...2-09-26-21-18-47.gh-issue-60580.0hBgde.rst | 3 - ...2-11-14-03-06-03.gh-issue-88597.EYJA-Q.rst | 1 - ...2-11-15-23-30-39.gh-issue-86682.gK9i1N.rst | 2 - ...2-11-24-21-52-31.gh-issue-99266.88GcV9.rst | 1 - ...2-12-10-15-30-17.gh-issue-67790.P9YUZM.rst | 2 - ...2-12-11-14-38-59.gh-issue-99952.IYGLzr.rst | 2 - ...2-12-19-23-19-26.gh-issue-96290.qFjsi6.rst | 5 - ...-12-21-17-49-50.gh-issue-100160.N0NHRj.rst | 3 - ...-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst | 1 - ...3-01-08-00-12-44.gh-issue-39615.gn4PhB.rst | 3 - ...-01-12-01-18-13.gh-issue-100573.KDskqo.rst | 1 - ...-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst | 3 - ...-01-14-12-58-21.gh-issue-101015.stWFid.rst | 2 - ...3-01-15-09-11-30.gh-issue-94518.jvxtxm.rst | 3 - ...-01-18-17-58-50.gh-issue-101144.FHd8Un.rst | 4 - ...-01-20-10-46-59.gh-issue-101143.hJo8hu.rst | 2 - ...-01-21-16-50-22.gh-issue-100795.NPMZf7.rst | 3 - ...3-01-24-12-53-59.gh-issue-92123.jf6TO5.rst | 2 - ...-01-25-18-07-20.gh-issue-101326.KL4SFv.rst | 1 - ...-01-26-01-25-56.gh-issue-101317.vWaS1x.rst | 2 - ...-01-26-06-44-35.gh-issue-101323.h8Hk11.rst | 2 - ...-02-04-21-01-49.gh-issue-101570.lbtUsD.rst | 1 - ...-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst | 1 - ...2-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst | 4 - ...-02-04-17-24-33.gh-issue-101334._yOqwg.rst | 1 - ...-01-11-14-42-11.gh-issue-100247.YfEmSz.rst | 2 - ...-01-11-16-28-09.gh-issue-100320.2DU2it.rst | 3 - ...3-01-17-18-17-58.gh-issue-82052.mWyysT.rst | 1 - ...-01-18-18-25-18.gh-issue-101135.HF9VlG.rst | 3 - ...3-01-25-00-23-31.gh-issue-99834.WN41lc.rst | 1 - ...-01-31-16-50-07.gh-issue-101467.ye9t-L.rst | 3 - ...-02-03-17-53-06.gh-issue-101543.cORAT4.rst | 2 - README.rst | 2 +- 68 files changed, 680 insertions(+), 145 deletions(-) create mode 100644 Misc/NEWS.d/3.12.0a5.rst delete mode 100644 Misc/NEWS.d/next/Build/2022-08-30-10-16-31.gh-issue-96305.274i8B.rst delete mode 100644 Misc/NEWS.d/next/Build/2022-10-25-11-53-55.gh-issue-98636.e0RPAr.rst delete mode 100644 Misc/NEWS.d/next/Build/2022-10-27-09-57-12.gh-issue-98705.H11XmR.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-01-15-11-22-15.gh-issue-101060.0mYk9E.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-01-17-21-32-51.gh-issue-100340.i9zRGM.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-01-21-10-31-35.gh-issue-101152.xvM8pL.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-01-26-19-02-11.gh-issue-77532.cXD8bg.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-02-02-23-43-46.gh-issue-101522.lnUDta.rst delete mode 100644 Misc/NEWS.d/next/Build/2023-02-04-06-59-07.gh-issue-101282.7sQz5l.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2018-02-05-21-54-46.bpo-32780.Dtiz8z.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-03-14-33-23.gh-issue-100712.po6xyB.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-03-20-59-20.gh-issue-100726.W9huFl.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-06-09-22-21.gh-issue-91351.iq2vZ_.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-10-16-59-33.gh-issue-100923.ypJAX-.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-12-13-46-49.gh-issue-100982.mJ234s.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-13-12-56-20.gh-issue-100762.YvHaQJ.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-14-17-03-08.gh-issue-101037.9ATNuf.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-30-11-56-09.gh-issue-59956.7xqnC_.rst delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst delete mode 100644 Misc/NEWS.d/next/Documentation/2022-06-19-22-04-47.gh-issue-88324.GHhSQ1.rst delete mode 100644 Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst delete mode 100644 Misc/NEWS.d/next/Library/2020-04-18-17-45-03.bpo-29847.Uxtbq0.rst delete mode 100644 Misc/NEWS.d/next/Library/2020-11-20-21-06-08.bpo-40077.M-iZq3.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-02-05-12-01-58.bpo-38941.8IhvyG.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-07-22-13-38-37.gh-issue-94518._ZP0cz.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-09-26-21-18-47.gh-issue-60580.0hBgde.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-11-14-03-06-03.gh-issue-88597.EYJA-Q.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-11-15-23-30-39.gh-issue-86682.gK9i1N.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-11-24-21-52-31.gh-issue-99266.88GcV9.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-12-10-15-30-17.gh-issue-67790.P9YUZM.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-12-19-23-19-26.gh-issue-96290.qFjsi6.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-12-21-17-49-50.gh-issue-100160.N0NHRj.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-08-00-12-44.gh-issue-39615.gn4PhB.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-14-12-58-21.gh-issue-101015.stWFid.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-15-09-11-30.gh-issue-94518.jvxtxm.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-24-12-53-59.gh-issue-92123.jf6TO5.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-26-01-25-56.gh-issue-101317.vWaS1x.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-02-04-21-01-49.gh-issue-101570.lbtUsD.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst delete mode 100644 Misc/NEWS.d/next/Security/2022-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst delete mode 100644 Misc/NEWS.d/next/Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-01-11-14-42-11.gh-issue-100247.YfEmSz.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-01-11-16-28-09.gh-issue-100320.2DU2it.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-01-18-18-25-18.gh-issue-101135.HF9VlG.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-01-25-00-23-31.gh-issue-99834.WN41lc.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-01-31-16-50-07.gh-issue-101467.ye9t-L.rst delete mode 100644 Misc/NEWS.d/next/Windows/2023-02-03-17-53-06.gh-issue-101543.cORAT4.rst diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 3a3e40c2e09229..df6098be8bbcc9 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -20,10 +20,10 @@ #define PY_MINOR_VERSION 12 #define PY_MICRO_VERSION 0 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA -#define PY_RELEASE_SERIAL 4 +#define PY_RELEASE_SERIAL 5 /* Version as a string */ -#define PY_VERSION "3.12.0a4+" +#define PY_VERSION "3.12.0a5" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index 11b75037e78b46..e7f403d3ffbf12 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Tue Jan 10 13:08:32 2023 +# Autogenerated by Sphinx on Tue Feb 7 13:18:04 2023 topics = {'assert': 'The "assert" statement\n' '**********************\n' '\n' @@ -4647,6 +4647,18 @@ 'the source. The extension interface uses the modules "bdb" and ' '"cmd".\n' '\n' + 'See also:\n' + '\n' + ' Module "faulthandler"\n' + ' Used to dump Python tracebacks explicitly, on a fault, ' + 'after a\n' + ' timeout, or on a user signal.\n' + '\n' + ' Module "traceback"\n' + ' Standard interface to extract, format and print stack ' + 'traces of\n' + ' Python programs.\n' + '\n' 'The debugger’s prompt is "(Pdb)". Typical usage to run a program ' 'under\n' 'control of the debugger is:\n' diff --git a/Misc/NEWS.d/3.12.0a5.rst b/Misc/NEWS.d/3.12.0a5.rst new file mode 100644 index 00000000000000..f6f8de46cf70d9 --- /dev/null +++ b/Misc/NEWS.d/3.12.0a5.rst @@ -0,0 +1,664 @@ +.. date: 2022-11-08-12-06-52 +.. gh-issue: 99108 +.. nonce: 4Wrsuh +.. release date: 2023-02-07 +.. section: Security + +Replace the builtin :mod:`hashlib` implementations of SHA2-224 and SHA2-256 +originally from LibTomCrypt with formally verified, side-channel resistant +code from the `HACL* `_ project. +The builtins remain a fallback only used when OpenSSL does not provide them. + +.. + +.. date: 2023-02-06-20-13-36 +.. gh-issue: 92173 +.. nonce: RQE0mk +.. section: Core and Builtins + +Fix the ``defs`` and ``kwdefs`` arguments to :c:func:`PyEval_EvalCodeEx` and +a reference leak in that function. + +.. + +.. date: 2023-01-30-11-56-09 +.. gh-issue: 59956 +.. nonce: 7xqnC_ +.. section: Core and Builtins + +The GILState API is now partially compatible with subinterpreters. +Previously, ``PyThreadState_GET()`` and ``PyGILState_GetThisThreadState()`` +would get out of sync, causing inconsistent behavior and crashes. + +.. + +.. date: 2023-01-30-08-59-47 +.. gh-issue: 101400 +.. nonce: Di_ZFm +.. section: Core and Builtins + +Fix wrong lineno in exception message on :keyword:`continue` or +:keyword:`break` which are not in a loop. Patch by Dong-hee Na. + +.. + +.. date: 2023-01-28-20-31-42 +.. gh-issue: 101372 +.. nonce: 8BcpCC +.. section: Core and Builtins + +Fix :func:`~unicodedata.is_normalized` to properly handle the UCD 3.2.0 +cases. Patch by Dong-hee Na. + +.. + +.. date: 2023-01-28-13-11-52 +.. gh-issue: 101266 +.. nonce: AxV3OF +.. section: Core and Builtins + +Fix :func:`sys.getsizeof` reporting for :class:`int` subclasses. + +.. + +.. date: 2023-01-24-17-13-32 +.. gh-issue: 101291 +.. nonce: Yr6u_c +.. section: Core and Builtins + +Refactor the ``PyLongObject`` struct into a normal Python object header and +a ``PyLongValue`` struct. + +.. + +.. date: 2023-01-15-03-26-04 +.. gh-issue: 101046 +.. nonce: g2CM4S +.. section: Core and Builtins + +Fix a possible memory leak in the parser when raising :exc:`MemoryError`. +Patch by Pablo Galindo + +.. + +.. date: 2023-01-14-17-03-08 +.. gh-issue: 101037 +.. nonce: 9ATNuf +.. section: Core and Builtins + +Fix potential memory underallocation issue for instances of :class:`int` +subclasses with value zero. + +.. + +.. date: 2023-01-13-12-56-20 +.. gh-issue: 100762 +.. nonce: YvHaQJ +.. section: Core and Builtins + +Record the (virtual) exception block depth in the oparg of +:opcode:`YIELD_VALUE`. Use this to avoid the expensive ``throw()`` when +closing generators (and coroutines) that can be closed trivially. + +.. + +.. date: 2023-01-12-13-46-49 +.. gh-issue: 100982 +.. nonce: mJ234s +.. section: Core and Builtins + +Adds a new :opcode:`COMPARE_AND_BRANCH` instruction. This is a bit more +efficient when performing a comparison immediately followed by a branch, and +restores the design intent of PEP 659 that specializations are local to a +single instruction. + +.. + +.. date: 2023-01-11-22-52-19 +.. gh-issue: 100942 +.. nonce: ontOy_ +.. section: Core and Builtins + +Fixed segfault in property.getter/setter/deleter that occurred when a +property subclass overrode the ``__new__`` method to return a non-property +instance. + +.. + +.. date: 2023-01-10-16-59-33 +.. gh-issue: 100923 +.. nonce: ypJAX- +.. section: Core and Builtins + +Remove the ``mask`` cache entry for the :opcode:`COMPARE_OP` instruction and +embed the mask into the oparg. + +.. + +.. date: 2023-01-10-14-11-17 +.. gh-issue: 100892 +.. nonce: qfBVYI +.. section: Core and Builtins + +Fix race while iterating over thread states in clearing +:class:`threading.local`. Patch by Kumar Aditya. + +.. + +.. date: 2023-01-06-09-22-21 +.. gh-issue: 91351 +.. nonce: iq2vZ_ +.. section: Core and Builtins + +Fix a case where re-entrant imports could corrupt the import deadlock +detection code and cause a :exc:`KeyError` to be raised out of +:mod:`importlib/_bootstrap`. In addition to the straightforward cases, this +could also happen when garbage collection leads to a warning being emitted +-- as happens when it collects an open socket or file) + +.. + +.. date: 2023-01-03-20-59-20 +.. gh-issue: 100726 +.. nonce: W9huFl +.. section: Core and Builtins + +Optimize construction of ``range`` object for medium size integers. + +.. + +.. date: 2023-01-03-14-33-23 +.. gh-issue: 100712 +.. nonce: po6xyB +.. section: Core and Builtins + +Added option to build cpython with specialization disabled, by setting +``ENABLE_SPECIALIZATION=False`` in :mod:`opcode`, followed by ``make +regen-all``. + +.. + +.. bpo: 32780 +.. date: 2018-02-05-21-54-46 +.. nonce: Dtiz8z +.. section: Core and Builtins + +Inter-field padding is now inserted into the PEP3118 format strings obtained +from :class:`ctypes.Structure` objects, reflecting their true representation +in memory. + +.. + +.. date: 2023-02-05-14-39-49 +.. gh-issue: 101541 +.. nonce: Mo3ppp +.. section: Library + +[Enum] - fix psuedo-flag creation + +.. + +.. date: 2023-02-04-21-01-49 +.. gh-issue: 101570 +.. nonce: lbtUsD +.. section: Library + +Upgrade pip wheel bundled with ensurepip (pip 23.0) + +.. + +.. date: 2023-01-26-06-44-35 +.. gh-issue: 101323 +.. nonce: h8Hk11 +.. section: Library + +Fix a bug where errors where not thrown by zlib._ZlibDecompressor if +encountered during decompressing. + +.. + +.. date: 2023-01-26-01-25-56 +.. gh-issue: 101317 +.. nonce: vWaS1x +.. section: Library + +Add *ssl_shutdown_timeout* parameter for +:meth:`asyncio.StreamWriter.start_tls`. + +.. + +.. date: 2023-01-25-18-07-20 +.. gh-issue: 101326 +.. nonce: KL4SFv +.. section: Library + +Fix regression when passing ``None`` as second or third argument to +``FutureIter.throw``. + +.. + +.. date: 2023-01-24-12-53-59 +.. gh-issue: 92123 +.. nonce: jf6TO5 +.. section: Library + +Adapt the ``_elementtree`` extension module to multi-phase init +(:pep:`489`). Patches by Erlend E. Aasland. + +.. + +.. date: 2023-01-21-16-50-22 +.. gh-issue: 100795 +.. nonce: NPMZf7 +.. section: Library + +Avoid potential unexpected ``freeaddrinfo`` call (double free) in +:mod:`socket` when when a libc ``getaddrinfo()`` implementation leaves +garbage in an output pointer when returning an error. Original patch by +Sergey G. Brester. + +.. + +.. date: 2023-01-20-10-46-59 +.. gh-issue: 101143 +.. nonce: hJo8hu +.. section: Library + +Remove unused references to :class:`~asyncio.TimerHandle` in +``asyncio.base_events.BaseEventLoop._add_callback``. + +.. + +.. date: 2023-01-18-17-58-50 +.. gh-issue: 101144 +.. nonce: FHd8Un +.. section: Library + +Make :func:`zipfile.Path.open` and :func:`zipfile.Path.read_text` also +accept ``encoding`` as a positional argument. This was the behavior in +Python 3.9 and earlier. 3.10 introduced a regression where supplying it as +a positional argument would lead to a :exc:`TypeError`. + +.. + +.. date: 2023-01-15-09-11-30 +.. gh-issue: 94518 +.. nonce: jvxtxm +.. section: Library + +Group-related variables of ``_posixsubprocess`` module are renamed to stress +that supplimentary group affinity is added to a fork, not replace the +inherited ones. Patch by Oleg Iarygin. + +.. + +.. date: 2023-01-14-12-58-21 +.. gh-issue: 101015 +.. nonce: stWFid +.. section: Library + +Fix :func:`typing.get_type_hints` on ``'*tuple[...]'`` and ``*tuple[...]``. +It must not drop the ``Unpack`` part. + +.. + +.. date: 2023-01-12-21-22-20 +.. gh-issue: 101000 +.. nonce: wz4Xgc +.. section: Library + +Add :func:`os.path.splitroot()`, which splits a path into a 3-item tuple +``(drive, root, tail)``. This new function is used by :mod:`pathlib` to +improve the performance of path construction by up to a third. + +.. + +.. date: 2023-01-12-01-18-13 +.. gh-issue: 100573 +.. nonce: KDskqo +.. section: Library + +Fix a Windows :mod:`asyncio` bug with named pipes where a client doing +``os.stat()`` on the pipe would cause an error in the server that disabled +serving future requests. + +.. + +.. date: 2023-01-08-00-12-44 +.. gh-issue: 39615 +.. nonce: gn4PhB +.. section: Library + +:func:`warnings.warn` now has the ability to skip stack frames based on code +filename prefix rather than only a numeric ``stacklevel`` via the new +``skip_file_prefixes`` keyword argument. + +.. + +.. date: 2023-01-04-14-42-59 +.. gh-issue: 100750 +.. nonce: iFJs5Y +.. section: Library + +pass encoding kwarg to subprocess in platform + +.. + +.. date: 2022-12-21-17-49-50 +.. gh-issue: 100160 +.. nonce: N0NHRj +.. section: Library + +Emit a deprecation warning in +:meth:`asyncio.DefaultEventLoopPolicy.get_event_loop` if there is no current +event loop set and it decides to create one. + +.. + +.. date: 2022-12-19-23-19-26 +.. gh-issue: 96290 +.. nonce: qFjsi6 +.. section: Library + +Fix handling of partial and invalid UNC drives in ``ntpath.splitdrive()``, +and in ``ntpath.normpath()`` on non-Windows systems. Paths such as +'\\server' and '\\' are now considered by ``splitdrive()`` to contain only a +drive, and consequently are not modified by ``normpath()`` on non-Windows +systems. The behaviour of ``normpath()`` on Windows systems is unaffected, +as native OS APIs are used. Patch by Eryk Sun, with contributions by Barney +Gale. + +.. + +.. date: 2022-12-11-14-38-59 +.. gh-issue: 99952 +.. nonce: IYGLzr +.. section: Library + +Fix a reference undercounting issue in :class:`ctypes.Structure` with +``from_param()`` results larger than a C pointer. + +.. + +.. date: 2022-12-10-15-30-17 +.. gh-issue: 67790 +.. nonce: P9YUZM +.. section: Library + +Add float-style formatting support for :class:`fractions.Fraction` +instances. + +.. + +.. date: 2022-11-24-21-52-31 +.. gh-issue: 99266 +.. nonce: 88GcV9 +.. section: Library + +Preserve more detailed error messages in :mod:`ctypes`. + +.. + +.. date: 2022-11-15-23-30-39 +.. gh-issue: 86682 +.. nonce: gK9i1N +.. section: Library + +Ensure runtime-created collections have the correct module name using the +newly added (internal) :func:`sys._getframemodulename`. + +.. + +.. date: 2022-11-14-03-06-03 +.. gh-issue: 88597 +.. nonce: EYJA-Q +.. section: Library + +:mod:`uuid` now has a command line interface. Try ``python -m uuid -h``. + +.. + +.. date: 2022-09-26-21-18-47 +.. gh-issue: 60580 +.. nonce: 0hBgde +.. section: Library + +:data:`ctypes.wintypes.BYTE` definition changed from :data:`~ctypes.c_byte` +to :data:`~ctypes.c_ubyte` to match Windows SDK. Patch by Anatoly Techtonik +and Oleg Iarygin. + +.. + +.. date: 2022-07-22-13-38-37 +.. gh-issue: 94518 +.. nonce: _ZP0cz +.. section: Library + +``_posixsubprocess`` now initializes all UID and GID variables using a +reserved ``-1`` value instead of a separate flag. Patch by Oleg Iarygin. + +.. + +.. bpo: 38941 +.. date: 2022-02-05-12-01-58 +.. nonce: 8IhvyG +.. section: Library + +The :mod:`xml.etree.ElementTree` module now emits :exc:`DeprecationWarning` +when testing the truth value of an :class:`xml.etree.ElementTree.Element`. +Before, the Python implementation emitted :exc:`FutureWarning`, and the C +implementation emitted nothing. + +.. + +.. bpo: 40077 +.. date: 2020-11-20-21-06-08 +.. nonce: M-iZq3 +.. section: Library + +Convert :mod:`elementtree` types to heap types. Patch by Erlend E. Aasland. + +.. + +.. bpo: 29847 +.. date: 2020-04-18-17-45-03 +.. nonce: Uxtbq0 +.. section: Library + +Fix a bug where :class:`pathlib.Path` accepted and ignored keyword +arguments. Patch provided by Yurii Karabas. + +.. + +.. date: 2018-05-21-17-18-00 +.. gh-issue: 77772 +.. nonce: Fhg84L +.. section: Library + +:class:`ctypes.CDLL`, :class:`ctypes.OleDLL`, :class:`ctypes.WinDLL`, and +:class:`ctypes.PyDLL` now accept :term:`path-like objects ` as their ``name`` argument. Patch by Robert Hoelzl. + +.. + +.. date: 2022-06-19-22-04-47 +.. gh-issue: 88324 +.. nonce: GHhSQ1 +.. section: Documentation + +Reword :mod:`subprocess` to emphasize default behavior of *stdin*, *stdout*, +and *stderr* arguments. Remove inaccurate statement about child file handle +inheritance. + +.. + +.. date: 2023-02-04-17-24-33 +.. gh-issue: 101334 +.. nonce: _yOqwg +.. section: Tests + +``test_tarfile`` has been updated to pass when run as a high UID. + +.. + +.. date: 2023-02-04-06-59-07 +.. gh-issue: 101282 +.. nonce: 7sQz5l +.. section: Build + +Update BOLT configration not to use depreacted usage of ``--split +functions``. Patch by Dong-hee Na. + +.. + +.. date: 2023-02-02-23-43-46 +.. gh-issue: 101522 +.. nonce: lnUDta +.. section: Build + +Allow overriding Windows dependencies versions and paths using MSBuild +properties. + +.. + +.. date: 2023-01-26-19-02-11 +.. gh-issue: 77532 +.. nonce: cXD8bg +.. section: Build + +Minor fixes to allow building with ``PlatformToolset=ClangCL`` on Windows. + +.. + +.. date: 2023-01-21-10-31-35 +.. gh-issue: 101152 +.. nonce: xvM8pL +.. section: Build + +In accordance with :PEP:`699`, the ``ma_version_tag`` field in +:c:type:`PyDictObject` is deprecated for extension modules. Accessing this +field will generate a compiler warning at compile time. This field will be +removed in Python 3.14. + +.. + +.. date: 2023-01-17-21-32-51 +.. gh-issue: 100340 +.. nonce: i9zRGM +.. section: Build + +Allows -Wno-int-conversion for wasm-sdk 17 and onwards, thus enables +building WASI builds once against the latest sdk. + +.. + +.. date: 2023-01-15-11-22-15 +.. gh-issue: 101060 +.. nonce: 0mYk9E +.. section: Build + +Conditionally add ``-fno-reorder-blocks-and-partition`` in configure. +Effectively fixes ``--enable-bolt`` when using Clang, as this appears to be +a GCC-only flag. + +.. + +.. date: 2022-10-27-09-57-12 +.. gh-issue: 98705 +.. nonce: H11XmR +.. section: Build + +``__bool__`` is defined in AIX system header files which breaks the build in +AIX, so undefine it. + +.. + +.. date: 2022-10-25-11-53-55 +.. gh-issue: 98636 +.. nonce: e0RPAr +.. section: Build + +Fix a regression in detecting ``gdbm_compat`` library for the ``_gdbm`` +module build. + +.. + +.. date: 2022-08-30-10-16-31 +.. gh-issue: 96305 +.. nonce: 274i8B +.. section: Build + +``_aix_support`` now uses a simple code to get platform details rather than +the now non-existent ``_bootsubprocess`` during bootstrap. + +.. + +.. date: 2023-02-03-17-53-06 +.. gh-issue: 101543 +.. nonce: cORAT4 +.. section: Windows + +Ensure the install path in the registry is only used when the standard +library hasn't been located in any other way. + +.. + +.. date: 2023-01-31-16-50-07 +.. gh-issue: 101467 +.. nonce: ye9t-L +.. section: Windows + +The ``py.exe`` launcher now correctly filters when only a single runtime is +installed. It also correctly handles prefix matches on tags so that ``-3.1`` +does not match ``3.11``, but would still match ``3.1-32``. + +.. + +.. date: 2023-01-25-00-23-31 +.. gh-issue: 99834 +.. nonce: WN41lc +.. section: Windows + +Updates bundled copy of Tcl/Tk to 8.6.13.0 + +.. + +.. date: 2023-01-18-18-25-18 +.. gh-issue: 101135 +.. nonce: HF9VlG +.. section: Windows + +Restore ability to launch older 32-bit versions from the :file:`py.exe` +launcher when both 32-bit and 64-bit installs of the same version are +available. + +.. + +.. date: 2023-01-17-18-17-58 +.. gh-issue: 82052 +.. nonce: mWyysT +.. section: Windows + +Fixed an issue where writing more than 32K of Unicode output to the console +screen in one go can result in mojibake. + +.. + +.. date: 2023-01-11-16-28-09 +.. gh-issue: 100320 +.. nonce: 2DU2it +.. section: Windows + +Ensures the ``PythonPath`` registry key from an install is used when +launching from a different copy of Python that relies on an existing install +to provide a copy of its modules and standard library. + +.. + +.. date: 2023-01-11-14-42-11 +.. gh-issue: 100247 +.. nonce: YfEmSz +.. section: Windows + +Restores support for the :file:`py.exe` launcher finding shebang commands in +its configuration file using the full command name. diff --git a/Misc/NEWS.d/next/Build/2022-08-30-10-16-31.gh-issue-96305.274i8B.rst b/Misc/NEWS.d/next/Build/2022-08-30-10-16-31.gh-issue-96305.274i8B.rst deleted file mode 100644 index 64a48da658f21f..00000000000000 --- a/Misc/NEWS.d/next/Build/2022-08-30-10-16-31.gh-issue-96305.274i8B.rst +++ /dev/null @@ -1,2 +0,0 @@ -``_aix_support`` now uses a simple code to get platform details rather than -the now non-existent ``_bootsubprocess`` during bootstrap. diff --git a/Misc/NEWS.d/next/Build/2022-10-25-11-53-55.gh-issue-98636.e0RPAr.rst b/Misc/NEWS.d/next/Build/2022-10-25-11-53-55.gh-issue-98636.e0RPAr.rst deleted file mode 100644 index 26a7cc8acaf243..00000000000000 --- a/Misc/NEWS.d/next/Build/2022-10-25-11-53-55.gh-issue-98636.e0RPAr.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a regression in detecting ``gdbm_compat`` library for the ``_gdbm`` -module build. diff --git a/Misc/NEWS.d/next/Build/2022-10-27-09-57-12.gh-issue-98705.H11XmR.rst b/Misc/NEWS.d/next/Build/2022-10-27-09-57-12.gh-issue-98705.H11XmR.rst deleted file mode 100644 index 4519853cdbadd1..00000000000000 --- a/Misc/NEWS.d/next/Build/2022-10-27-09-57-12.gh-issue-98705.H11XmR.rst +++ /dev/null @@ -1,2 +0,0 @@ -``__bool__`` is defined in AIX system header files which breaks the build in -AIX, so undefine it. diff --git a/Misc/NEWS.d/next/Build/2023-01-15-11-22-15.gh-issue-101060.0mYk9E.rst b/Misc/NEWS.d/next/Build/2023-01-15-11-22-15.gh-issue-101060.0mYk9E.rst deleted file mode 100644 index bebbf8c898d547..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-01-15-11-22-15.gh-issue-101060.0mYk9E.rst +++ /dev/null @@ -1,3 +0,0 @@ -Conditionally add ``-fno-reorder-blocks-and-partition`` in configure. -Effectively fixes ``--enable-bolt`` when using Clang, as this appears to be -a GCC-only flag. diff --git a/Misc/NEWS.d/next/Build/2023-01-17-21-32-51.gh-issue-100340.i9zRGM.rst b/Misc/NEWS.d/next/Build/2023-01-17-21-32-51.gh-issue-100340.i9zRGM.rst deleted file mode 100644 index 3a37f798dc6c6d..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-01-17-21-32-51.gh-issue-100340.i9zRGM.rst +++ /dev/null @@ -1,2 +0,0 @@ -Allows -Wno-int-conversion for wasm-sdk 17 and onwards, thus enables -building WASI builds once against the latest sdk. diff --git a/Misc/NEWS.d/next/Build/2023-01-21-10-31-35.gh-issue-101152.xvM8pL.rst b/Misc/NEWS.d/next/Build/2023-01-21-10-31-35.gh-issue-101152.xvM8pL.rst deleted file mode 100644 index e35b6178aa4cf9..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-01-21-10-31-35.gh-issue-101152.xvM8pL.rst +++ /dev/null @@ -1,3 +0,0 @@ -In accordance with :PEP:`699`, the ``ma_version_tag`` field in :c:type:`PyDictObject` -is deprecated for extension modules. Accessing this field will generate a compiler -warning at compile time. This field will be removed in Python 3.14. diff --git a/Misc/NEWS.d/next/Build/2023-01-26-19-02-11.gh-issue-77532.cXD8bg.rst b/Misc/NEWS.d/next/Build/2023-01-26-19-02-11.gh-issue-77532.cXD8bg.rst deleted file mode 100644 index 5a746dca2e7d8d..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-01-26-19-02-11.gh-issue-77532.cXD8bg.rst +++ /dev/null @@ -1 +0,0 @@ -Minor fixes to allow building with ``PlatformToolset=ClangCL`` on Windows. diff --git a/Misc/NEWS.d/next/Build/2023-02-02-23-43-46.gh-issue-101522.lnUDta.rst b/Misc/NEWS.d/next/Build/2023-02-02-23-43-46.gh-issue-101522.lnUDta.rst deleted file mode 100644 index 2e7f9029e9ee54..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-02-02-23-43-46.gh-issue-101522.lnUDta.rst +++ /dev/null @@ -1,2 +0,0 @@ -Allow overriding Windows dependencies versions and paths using MSBuild -properties. diff --git a/Misc/NEWS.d/next/Build/2023-02-04-06-59-07.gh-issue-101282.7sQz5l.rst b/Misc/NEWS.d/next/Build/2023-02-04-06-59-07.gh-issue-101282.7sQz5l.rst deleted file mode 100644 index 49d48564667349..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-02-04-06-59-07.gh-issue-101282.7sQz5l.rst +++ /dev/null @@ -1,2 +0,0 @@ -Update BOLT configration not to use depreacted usage of ``--split -functions``. Patch by Dong-hee Na. diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-02-05-21-54-46.bpo-32780.Dtiz8z.rst b/Misc/NEWS.d/next/Core and Builtins/2018-02-05-21-54-46.bpo-32780.Dtiz8z.rst deleted file mode 100644 index 8996d471159a64..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2018-02-05-21-54-46.bpo-32780.Dtiz8z.rst +++ /dev/null @@ -1,3 +0,0 @@ -Inter-field padding is now inserted into the PEP3118 format strings obtained -from :class:`ctypes.Structure` objects, reflecting their true representation in -memory. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-03-14-33-23.gh-issue-100712.po6xyB.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-03-14-33-23.gh-issue-100712.po6xyB.rst deleted file mode 100644 index 3ebee0dd2aa48f..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-03-14-33-23.gh-issue-100712.po6xyB.rst +++ /dev/null @@ -1 +0,0 @@ -Added option to build cpython with specialization disabled, by setting ``ENABLE_SPECIALIZATION=False`` in :mod:`opcode`, followed by ``make regen-all``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-03-20-59-20.gh-issue-100726.W9huFl.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-03-20-59-20.gh-issue-100726.W9huFl.rst deleted file mode 100644 index 2c93098b347a7f..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-03-20-59-20.gh-issue-100726.W9huFl.rst +++ /dev/null @@ -1 +0,0 @@ -Optimize construction of ``range`` object for medium size integers. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-06-09-22-21.gh-issue-91351.iq2vZ_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-06-09-22-21.gh-issue-91351.iq2vZ_.rst deleted file mode 100644 index 19de1f8d0fb31e..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-06-09-22-21.gh-issue-91351.iq2vZ_.rst +++ /dev/null @@ -1,5 +0,0 @@ -Fix a case where re-entrant imports could corrupt the import deadlock -detection code and cause a :exc:`KeyError` to be raised out of -:mod:`importlib/_bootstrap`. In addition to the straightforward cases, this -could also happen when garbage collection leads to a warning being emitted -- -as happens when it collects an open socket or file) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst deleted file mode 100644 index f2576becc2fcfc..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst +++ /dev/null @@ -1 +0,0 @@ -Fix race while iterating over thread states in clearing :class:`threading.local`. Patch by Kumar Aditya. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-10-16-59-33.gh-issue-100923.ypJAX-.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-10-16-59-33.gh-issue-100923.ypJAX-.rst deleted file mode 100644 index b6b3f1d0c58f8e..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-10-16-59-33.gh-issue-100923.ypJAX-.rst +++ /dev/null @@ -1,2 +0,0 @@ -Remove the ``mask`` cache entry for the :opcode:`COMPARE_OP` instruction and -embed the mask into the oparg. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst deleted file mode 100644 index daccea255b1626..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed segfault in property.getter/setter/deleter that occurred when a property -subclass overrode the ``__new__`` method to return a non-property instance. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-12-13-46-49.gh-issue-100982.mJ234s.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-12-13-46-49.gh-issue-100982.mJ234s.rst deleted file mode 100644 index 4f43e783cd6a19..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-12-13-46-49.gh-issue-100982.mJ234s.rst +++ /dev/null @@ -1,4 +0,0 @@ -Adds a new :opcode:`COMPARE_AND_BRANCH` instruction. This is a bit more -efficient when performing a comparison immediately followed by a branch, and -restores the design intent of PEP 659 that specializations are local to a -single instruction. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-13-12-56-20.gh-issue-100762.YvHaQJ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-13-12-56-20.gh-issue-100762.YvHaQJ.rst deleted file mode 100644 index 2f6b121439a985..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-13-12-56-20.gh-issue-100762.YvHaQJ.rst +++ /dev/null @@ -1,3 +0,0 @@ -Record the (virtual) exception block depth in the oparg of -:opcode:`YIELD_VALUE`. Use this to avoid the expensive ``throw()`` when -closing generators (and coroutines) that can be closed trivially. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-14-17-03-08.gh-issue-101037.9ATNuf.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-14-17-03-08.gh-issue-101037.9ATNuf.rst deleted file mode 100644 index a48756657a29d3..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-14-17-03-08.gh-issue-101037.9ATNuf.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix potential memory underallocation issue for instances of :class:`int` -subclasses with value zero. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst deleted file mode 100644 index f600473620f109..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a possible memory leak in the parser when raising :exc:`MemoryError`. -Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst deleted file mode 100644 index b585ff5a817edf..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst +++ /dev/null @@ -1,2 +0,0 @@ -Refactor the ``PyLongObject`` struct into a normal Python object header and -a ``PyLongValue`` struct. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst deleted file mode 100644 index 51999bacb8de07..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :func:`sys.getsizeof` reporting for :class:`int` subclasses. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst deleted file mode 100644 index 65a207e3f7e436..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-28-20-31-42.gh-issue-101372.8BcpCC.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :func:`~unicodedata.is_normalized` to properly handle the UCD 3.2.0 -cases. Patch by Dong-hee Na. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst deleted file mode 100644 index f3dd783c01e7c0..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix wrong lineno in exception message on :keyword:`continue` or -:keyword:`break` which are not in a loop. Patch by Dong-hee Na. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-30-11-56-09.gh-issue-59956.7xqnC_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-30-11-56-09.gh-issue-59956.7xqnC_.rst deleted file mode 100644 index b3c1896b9493e1..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-30-11-56-09.gh-issue-59956.7xqnC_.rst +++ /dev/null @@ -1,3 +0,0 @@ -The GILState API is now partially compatible with subinterpreters. -Previously, ``PyThreadState_GET()`` and ``PyGILState_GetThisThreadState()`` -would get out of sync, causing inconsistent behavior and crashes. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst deleted file mode 100644 index 6b98aac2a46545..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-02-06-20-13-36.gh-issue-92173.RQE0mk.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix the ``defs`` and ``kwdefs`` arguments to :c:func:`PyEval_EvalCodeEx` -and a reference leak in that function. diff --git a/Misc/NEWS.d/next/Documentation/2022-06-19-22-04-47.gh-issue-88324.GHhSQ1.rst b/Misc/NEWS.d/next/Documentation/2022-06-19-22-04-47.gh-issue-88324.GHhSQ1.rst deleted file mode 100644 index 6c8d192daa7955..00000000000000 --- a/Misc/NEWS.d/next/Documentation/2022-06-19-22-04-47.gh-issue-88324.GHhSQ1.rst +++ /dev/null @@ -1,3 +0,0 @@ -Reword :mod:`subprocess` to emphasize default behavior of *stdin*, *stdout*, -and *stderr* arguments. Remove inaccurate statement about child file handle -inheritance. diff --git a/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst b/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst deleted file mode 100644 index 3a7c6d45297ba4..00000000000000 --- a/Misc/NEWS.d/next/Library/2018-05-21-17-18-00.gh-issue-77772.Fhg84L.rst +++ /dev/null @@ -1,3 +0,0 @@ -:class:`ctypes.CDLL`, :class:`ctypes.OleDLL`, :class:`ctypes.WinDLL`, -and :class:`ctypes.PyDLL` now accept :term:`path-like objects -` as their ``name`` argument. Patch by Robert Hoelzl. diff --git a/Misc/NEWS.d/next/Library/2020-04-18-17-45-03.bpo-29847.Uxtbq0.rst b/Misc/NEWS.d/next/Library/2020-04-18-17-45-03.bpo-29847.Uxtbq0.rst deleted file mode 100644 index 010d775a0d98ee..00000000000000 --- a/Misc/NEWS.d/next/Library/2020-04-18-17-45-03.bpo-29847.Uxtbq0.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a bug where :class:`pathlib.Path` accepted and ignored keyword arguments. Patch provided by Yurii Karabas. diff --git a/Misc/NEWS.d/next/Library/2020-11-20-21-06-08.bpo-40077.M-iZq3.rst b/Misc/NEWS.d/next/Library/2020-11-20-21-06-08.bpo-40077.M-iZq3.rst deleted file mode 100644 index 8a74477a4b359d..00000000000000 --- a/Misc/NEWS.d/next/Library/2020-11-20-21-06-08.bpo-40077.M-iZq3.rst +++ /dev/null @@ -1 +0,0 @@ -Convert :mod:`elementtree` types to heap types. Patch by Erlend E. Aasland. diff --git a/Misc/NEWS.d/next/Library/2022-02-05-12-01-58.bpo-38941.8IhvyG.rst b/Misc/NEWS.d/next/Library/2022-02-05-12-01-58.bpo-38941.8IhvyG.rst deleted file mode 100644 index 5f996042260d09..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-02-05-12-01-58.bpo-38941.8IhvyG.rst +++ /dev/null @@ -1,4 +0,0 @@ -The :mod:`xml.etree.ElementTree` module now emits :exc:`DeprecationWarning` -when testing the truth value of an :class:`xml.etree.ElementTree.Element`. -Before, the Python implementation emitted :exc:`FutureWarning`, and the C -implementation emitted nothing. diff --git a/Misc/NEWS.d/next/Library/2022-07-22-13-38-37.gh-issue-94518._ZP0cz.rst b/Misc/NEWS.d/next/Library/2022-07-22-13-38-37.gh-issue-94518._ZP0cz.rst deleted file mode 100644 index a9d6d69f7effac..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-07-22-13-38-37.gh-issue-94518._ZP0cz.rst +++ /dev/null @@ -1,2 +0,0 @@ -``_posixsubprocess`` now initializes all UID and GID variables using a -reserved ``-1`` value instead of a separate flag. Patch by Oleg Iarygin. diff --git a/Misc/NEWS.d/next/Library/2022-09-26-21-18-47.gh-issue-60580.0hBgde.rst b/Misc/NEWS.d/next/Library/2022-09-26-21-18-47.gh-issue-60580.0hBgde.rst deleted file mode 100644 index 630e56cd2f7b87..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-09-26-21-18-47.gh-issue-60580.0hBgde.rst +++ /dev/null @@ -1,3 +0,0 @@ -:data:`ctypes.wintypes.BYTE` definition changed from -:data:`~ctypes.c_byte` to :data:`~ctypes.c_ubyte` to match Windows -SDK. Patch by Anatoly Techtonik and Oleg Iarygin. diff --git a/Misc/NEWS.d/next/Library/2022-11-14-03-06-03.gh-issue-88597.EYJA-Q.rst b/Misc/NEWS.d/next/Library/2022-11-14-03-06-03.gh-issue-88597.EYJA-Q.rst deleted file mode 100644 index a98e1ab4d15734..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-11-14-03-06-03.gh-issue-88597.EYJA-Q.rst +++ /dev/null @@ -1 +0,0 @@ -:mod:`uuid` now has a command line interface. Try ``python -m uuid -h``. diff --git a/Misc/NEWS.d/next/Library/2022-11-15-23-30-39.gh-issue-86682.gK9i1N.rst b/Misc/NEWS.d/next/Library/2022-11-15-23-30-39.gh-issue-86682.gK9i1N.rst deleted file mode 100644 index 64ef42a9a1c0b2..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-11-15-23-30-39.gh-issue-86682.gK9i1N.rst +++ /dev/null @@ -1,2 +0,0 @@ -Ensure runtime-created collections have the correct module name using -the newly added (internal) :func:`sys._getframemodulename`. diff --git a/Misc/NEWS.d/next/Library/2022-11-24-21-52-31.gh-issue-99266.88GcV9.rst b/Misc/NEWS.d/next/Library/2022-11-24-21-52-31.gh-issue-99266.88GcV9.rst deleted file mode 100644 index 97e9569e40a9bf..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-11-24-21-52-31.gh-issue-99266.88GcV9.rst +++ /dev/null @@ -1 +0,0 @@ -Preserve more detailed error messages in :mod:`ctypes`. diff --git a/Misc/NEWS.d/next/Library/2022-12-10-15-30-17.gh-issue-67790.P9YUZM.rst b/Misc/NEWS.d/next/Library/2022-12-10-15-30-17.gh-issue-67790.P9YUZM.rst deleted file mode 100644 index ba0db774f8b318..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-10-15-30-17.gh-issue-67790.P9YUZM.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add float-style formatting support for :class:`fractions.Fraction` -instances. diff --git a/Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst b/Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst deleted file mode 100644 index 09ec961249534f..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a reference undercounting issue in :class:`ctypes.Structure` with ``from_param()`` -results larger than a C pointer. diff --git a/Misc/NEWS.d/next/Library/2022-12-19-23-19-26.gh-issue-96290.qFjsi6.rst b/Misc/NEWS.d/next/Library/2022-12-19-23-19-26.gh-issue-96290.qFjsi6.rst deleted file mode 100644 index 33f98602bd1b71..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-19-23-19-26.gh-issue-96290.qFjsi6.rst +++ /dev/null @@ -1,5 +0,0 @@ -Fix handling of partial and invalid UNC drives in ``ntpath.splitdrive()``, and in -``ntpath.normpath()`` on non-Windows systems. Paths such as '\\server' and '\\' are now considered -by ``splitdrive()`` to contain only a drive, and consequently are not modified by ``normpath()`` on -non-Windows systems. The behaviour of ``normpath()`` on Windows systems is unaffected, as native -OS APIs are used. Patch by Eryk Sun, with contributions by Barney Gale. diff --git a/Misc/NEWS.d/next/Library/2022-12-21-17-49-50.gh-issue-100160.N0NHRj.rst b/Misc/NEWS.d/next/Library/2022-12-21-17-49-50.gh-issue-100160.N0NHRj.rst deleted file mode 100644 index d5cc785722d7fd..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-21-17-49-50.gh-issue-100160.N0NHRj.rst +++ /dev/null @@ -1,3 +0,0 @@ -Emit a deprecation warning in -:meth:`asyncio.DefaultEventLoopPolicy.get_event_loop` if there is no current -event loop set and it decides to create one. diff --git a/Misc/NEWS.d/next/Library/2023-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst b/Misc/NEWS.d/next/Library/2023-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst deleted file mode 100644 index be351532822c4b..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst +++ /dev/null @@ -1 +0,0 @@ -pass encoding kwarg to subprocess in platform diff --git a/Misc/NEWS.d/next/Library/2023-01-08-00-12-44.gh-issue-39615.gn4PhB.rst b/Misc/NEWS.d/next/Library/2023-01-08-00-12-44.gh-issue-39615.gn4PhB.rst deleted file mode 100644 index 1d04cc2cd54b1e..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-08-00-12-44.gh-issue-39615.gn4PhB.rst +++ /dev/null @@ -1,3 +0,0 @@ -:func:`warnings.warn` now has the ability to skip stack frames based on code -filename prefix rather than only a numeric ``stacklevel`` via the new -``skip_file_prefixes`` keyword argument. diff --git a/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst b/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst deleted file mode 100644 index 97b95d18d1e426..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a Windows :mod:`asyncio` bug with named pipes where a client doing ``os.stat()`` on the pipe would cause an error in the server that disabled serving future requests. diff --git a/Misc/NEWS.d/next/Library/2023-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst b/Misc/NEWS.d/next/Library/2023-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst deleted file mode 100644 index 2082361c41d697..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add :func:`os.path.splitroot()`, which splits a path into a 3-item tuple -``(drive, root, tail)``. This new function is used by :mod:`pathlib` to -improve the performance of path construction by up to a third. diff --git a/Misc/NEWS.d/next/Library/2023-01-14-12-58-21.gh-issue-101015.stWFid.rst b/Misc/NEWS.d/next/Library/2023-01-14-12-58-21.gh-issue-101015.stWFid.rst deleted file mode 100644 index b9d73ff9855236..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-14-12-58-21.gh-issue-101015.stWFid.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :func:`typing.get_type_hints` on ``'*tuple[...]'`` and ``*tuple[...]``. -It must not drop the ``Unpack`` part. diff --git a/Misc/NEWS.d/next/Library/2023-01-15-09-11-30.gh-issue-94518.jvxtxm.rst b/Misc/NEWS.d/next/Library/2023-01-15-09-11-30.gh-issue-94518.jvxtxm.rst deleted file mode 100644 index 77563090464dbc..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-15-09-11-30.gh-issue-94518.jvxtxm.rst +++ /dev/null @@ -1,3 +0,0 @@ -Group-related variables of ``_posixsubprocess`` module are renamed to -stress that supplimentary group affinity is added to a fork, not -replace the inherited ones. Patch by Oleg Iarygin. diff --git a/Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst b/Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst deleted file mode 100644 index 297652259949fc..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst +++ /dev/null @@ -1,4 +0,0 @@ -Make :func:`zipfile.Path.open` and :func:`zipfile.Path.read_text` also accept -``encoding`` as a positional argument. This was the behavior in Python 3.9 and -earlier. 3.10 introduced a regression where supplying it as a positional -argument would lead to a :exc:`TypeError`. diff --git a/Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst b/Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst deleted file mode 100644 index d14b9e25a691fc..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst +++ /dev/null @@ -1,2 +0,0 @@ -Remove unused references to :class:`~asyncio.TimerHandle` in -``asyncio.base_events.BaseEventLoop._add_callback``. diff --git a/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst b/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst deleted file mode 100644 index 4cb56ea0f0e58d..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst +++ /dev/null @@ -1,3 +0,0 @@ -Avoid potential unexpected ``freeaddrinfo`` call (double free) in :mod:`socket` -when when a libc ``getaddrinfo()`` implementation leaves garbage in an output -pointer when returning an error. Original patch by Sergey G. Brester. diff --git a/Misc/NEWS.d/next/Library/2023-01-24-12-53-59.gh-issue-92123.jf6TO5.rst b/Misc/NEWS.d/next/Library/2023-01-24-12-53-59.gh-issue-92123.jf6TO5.rst deleted file mode 100644 index 4b4443a55fdb1a..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-24-12-53-59.gh-issue-92123.jf6TO5.rst +++ /dev/null @@ -1,2 +0,0 @@ -Adapt the ``_elementtree`` extension module to multi-phase init (:pep:`489`). -Patches by Erlend E. Aasland. diff --git a/Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst b/Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst deleted file mode 100644 index 54b69b9430910d..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst +++ /dev/null @@ -1 +0,0 @@ -Fix regression when passing ``None`` as second or third argument to ``FutureIter.throw``. diff --git a/Misc/NEWS.d/next/Library/2023-01-26-01-25-56.gh-issue-101317.vWaS1x.rst b/Misc/NEWS.d/next/Library/2023-01-26-01-25-56.gh-issue-101317.vWaS1x.rst deleted file mode 100644 index f1ce0e0a527661..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-26-01-25-56.gh-issue-101317.vWaS1x.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add *ssl_shutdown_timeout* parameter for :meth:`asyncio.StreamWriter.start_tls`. - diff --git a/Misc/NEWS.d/next/Library/2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst b/Misc/NEWS.d/next/Library/2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst deleted file mode 100644 index f8419e130dbcd7..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-26-06-44-35.gh-issue-101323.h8Hk11.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a bug where errors where not thrown by zlib._ZlibDecompressor if -encountered during decompressing. diff --git a/Misc/NEWS.d/next/Library/2023-02-04-21-01-49.gh-issue-101570.lbtUsD.rst b/Misc/NEWS.d/next/Library/2023-02-04-21-01-49.gh-issue-101570.lbtUsD.rst deleted file mode 100644 index 599edab6c07117..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-02-04-21-01-49.gh-issue-101570.lbtUsD.rst +++ /dev/null @@ -1 +0,0 @@ -Upgrade pip wheel bundled with ensurepip (pip 23.0) diff --git a/Misc/NEWS.d/next/Library/2023-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst b/Misc/NEWS.d/next/Library/2023-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst deleted file mode 100644 index 0f149e80dc54a2..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-02-05-14-39-49.gh-issue-101541.Mo3ppp.rst +++ /dev/null @@ -1 +0,0 @@ -[Enum] - fix psuedo-flag creation diff --git a/Misc/NEWS.d/next/Security/2022-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst b/Misc/NEWS.d/next/Security/2022-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst deleted file mode 100644 index 64acc09c482ecb..00000000000000 --- a/Misc/NEWS.d/next/Security/2022-11-08-12-06-52.gh-issue-99108.4Wrsuh.rst +++ /dev/null @@ -1,4 +0,0 @@ -Replace the builtin :mod:`hashlib` implementations of SHA2-224 and SHA2-256 -originally from LibTomCrypt with formally verified, side-channel resistant -code from the `HACL* `_ project. The -builtins remain a fallback only used when OpenSSL does not provide them. diff --git a/Misc/NEWS.d/next/Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst b/Misc/NEWS.d/next/Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst deleted file mode 100644 index 2a95fd9ae53c86..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-02-04-17-24-33.gh-issue-101334._yOqwg.rst +++ /dev/null @@ -1 +0,0 @@ -``test_tarfile`` has been updated to pass when run as a high UID. diff --git a/Misc/NEWS.d/next/Windows/2023-01-11-14-42-11.gh-issue-100247.YfEmSz.rst b/Misc/NEWS.d/next/Windows/2023-01-11-14-42-11.gh-issue-100247.YfEmSz.rst deleted file mode 100644 index 7bfcbd7ddecf5f..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-01-11-14-42-11.gh-issue-100247.YfEmSz.rst +++ /dev/null @@ -1,2 +0,0 @@ -Restores support for the :file:`py.exe` launcher finding shebang commands in -its configuration file using the full command name. diff --git a/Misc/NEWS.d/next/Windows/2023-01-11-16-28-09.gh-issue-100320.2DU2it.rst b/Misc/NEWS.d/next/Windows/2023-01-11-16-28-09.gh-issue-100320.2DU2it.rst deleted file mode 100644 index c206fc8520a5d9..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-01-11-16-28-09.gh-issue-100320.2DU2it.rst +++ /dev/null @@ -1,3 +0,0 @@ -Ensures the ``PythonPath`` registry key from an install is used when -launching from a different copy of Python that relies on an existing install -to provide a copy of its modules and standard library. diff --git a/Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst b/Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst deleted file mode 100644 index 4f7ab200b85cba..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed an issue where writing more than 32K of Unicode output to the console screen in one go can result in mojibake. diff --git a/Misc/NEWS.d/next/Windows/2023-01-18-18-25-18.gh-issue-101135.HF9VlG.rst b/Misc/NEWS.d/next/Windows/2023-01-18-18-25-18.gh-issue-101135.HF9VlG.rst deleted file mode 100644 index 2e6d6371340d89..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-01-18-18-25-18.gh-issue-101135.HF9VlG.rst +++ /dev/null @@ -1,3 +0,0 @@ -Restore ability to launch older 32-bit versions from the :file:`py.exe` -launcher when both 32-bit and 64-bit installs of the same version are -available. diff --git a/Misc/NEWS.d/next/Windows/2023-01-25-00-23-31.gh-issue-99834.WN41lc.rst b/Misc/NEWS.d/next/Windows/2023-01-25-00-23-31.gh-issue-99834.WN41lc.rst deleted file mode 100644 index d3894fa4ea3012..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-01-25-00-23-31.gh-issue-99834.WN41lc.rst +++ /dev/null @@ -1 +0,0 @@ -Updates bundled copy of Tcl/Tk to 8.6.13.0 diff --git a/Misc/NEWS.d/next/Windows/2023-01-31-16-50-07.gh-issue-101467.ye9t-L.rst b/Misc/NEWS.d/next/Windows/2023-01-31-16-50-07.gh-issue-101467.ye9t-L.rst deleted file mode 100644 index 4d4da05afa2d16..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-01-31-16-50-07.gh-issue-101467.ye9t-L.rst +++ /dev/null @@ -1,3 +0,0 @@ -The ``py.exe`` launcher now correctly filters when only a single runtime is -installed. It also correctly handles prefix matches on tags so that ``-3.1`` -does not match ``3.11``, but would still match ``3.1-32``. diff --git a/Misc/NEWS.d/next/Windows/2023-02-03-17-53-06.gh-issue-101543.cORAT4.rst b/Misc/NEWS.d/next/Windows/2023-02-03-17-53-06.gh-issue-101543.cORAT4.rst deleted file mode 100644 index d4e2c6f23013b6..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-02-03-17-53-06.gh-issue-101543.cORAT4.rst +++ /dev/null @@ -1,2 +0,0 @@ -Ensure the install path in the registry is only used when the standard -library hasn't been located in any other way. diff --git a/README.rst b/README.rst index 814efef83a357a..b1756e20c141ab 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -This is Python version 3.12.0 alpha 4 +This is Python version 3.12.0 alpha 5 ===================================== .. image:: https://github.com/python/cpython/workflows/Tests/badge.svg From a757d721271974c45e2feacef739af4e86ec7350 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 7 Feb 2023 15:59:26 +0200 Subject: [PATCH 106/225] Doctest: Pin sphinxext-opengraph==0.7.5 to prevent importing NumPy (#101642) --- Doc/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/requirements.txt b/Doc/requirements.txt index 134f39d6d7b3d4..71d3cd61e53877 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -8,7 +8,7 @@ sphinx==4.5.0 blurb sphinx-lint==0.6.7 -sphinxext-opengraph>=0.7.1 +sphinxext-opengraph==0.7.5 # The theme used by the documentation is stored separately, so we need # to install that as well. From a687ae9eb5c0aea06c52de1e426904b79f767f4e Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Tue, 7 Feb 2023 18:54:47 +0400 Subject: [PATCH 107/225] Fix nesting of 'Pending Removal in Python 3.14' (#101637) --- Doc/whatsnew/3.12.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 0c5a70b64574ef..b723b70154f08d 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -479,7 +479,7 @@ APIs: * :class:`webbrowser.MacOSX` (:gh:`86421`) Pending Removal in Python 3.14 -============================== +------------------------------ * Deprecated the following :mod:`importlib.abc` classes, scheduled for removal in Python 3.14: From d54b8d8fbd76c05e9006175ab26d737c4b055dfb Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 7 Feb 2023 08:28:28 -0800 Subject: [PATCH 108/225] gh-98831: Modernize the FOR_ITER family of instructions (#101626) Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> --- Python/bytecodes.c | 83 +++++++++++++++++++++----------------- Python/generated_cases.c.h | 66 +++++++++++++++++++----------- Python/opcode_metadata.h | 30 +++++++------- 3 files changed, 104 insertions(+), 75 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 0fc0b3b8280f8b..ec0439af98e632 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2066,27 +2066,35 @@ dummy_func( PREDICT(LOAD_CONST); } - // stack effect: ( -- __0) - inst(FOR_ITER) { + // Most members of this family are "secretly" super-instructions. + // When the loop is exhausted, they jump, and the jump target is + // always END_FOR, which pops two values off the stack. + // This is optimized by skipping that instruction and combining + // its effect (popping 'iter' instead of pushing 'next'.) + + family(for_iter, INLINE_CACHE_ENTRIES_FOR_ITER) = { + FOR_ITER, + FOR_ITER_LIST, + FOR_ITER_TUPLE, + FOR_ITER_RANGE, + FOR_ITER_GEN, + }; + + inst(FOR_ITER, (unused/1, iter -- iter, next)) { #if ENABLE_SPECIALIZATION _PyForIterCache *cache = (_PyForIterCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); next_instr--; - _Py_Specialize_ForIter(TOP(), next_instr, oparg); + _Py_Specialize_ForIter(iter, next_instr, oparg); DISPATCH_SAME_OPARG(); } STAT_INC(FOR_ITER, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - /* before: [iter]; after: [iter, iter()] *or* [] */ - PyObject *iter = TOP(); - PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter); - if (next != NULL) { - PUSH(next); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); - } - else { + /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */ + next = (*Py_TYPE(iter)->tp_iternext)(iter); + if (next == NULL) { if (_PyErr_Occurred(tstate)) { if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { goto error; @@ -2098,63 +2106,66 @@ dummy_func( } /* iterator ended normally */ assert(_Py_OPCODE(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg]) == END_FOR); - STACK_SHRINK(1); Py_DECREF(iter); - /* Skip END_FOR */ + STACK_SHRINK(1); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); } + // Common case: no jump, leave it to the code generator } - // stack effect: ( -- __0) - inst(FOR_ITER_LIST) { + inst(FOR_ITER_LIST, (unused/1, iter -- iter, next)) { assert(cframe.use_tracing == 0); - _PyListIterObject *it = (_PyListIterObject *)TOP(); - DEOPT_IF(Py_TYPE(it) != &PyListIter_Type, FOR_ITER); + DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type, FOR_ITER); + _PyListIterObject *it = (_PyListIterObject *)iter; STAT_INC(FOR_ITER, hit); PyListObject *seq = it->it_seq; if (seq) { if (it->it_index < PyList_GET_SIZE(seq)) { - PyObject *next = PyList_GET_ITEM(seq, it->it_index++); - PUSH(Py_NewRef(next)); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); + next = Py_NewRef(PyList_GET_ITEM(seq, it->it_index++)); goto end_for_iter_list; // End of this instruction } it->it_seq = NULL; Py_DECREF(seq); } + Py_DECREF(iter); STACK_SHRINK(1); - Py_DECREF(it); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); end_for_iter_list: + // Common case: no jump, leave it to the code generator } - // stack effect: ( -- __0) - inst(FOR_ITER_TUPLE) { + inst(FOR_ITER_TUPLE, (unused/1, iter -- iter, next)) { assert(cframe.use_tracing == 0); - _PyTupleIterObject *it = (_PyTupleIterObject *)TOP(); + _PyTupleIterObject *it = (_PyTupleIterObject *)iter; DEOPT_IF(Py_TYPE(it) != &PyTupleIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); PyTupleObject *seq = it->it_seq; if (seq) { if (it->it_index < PyTuple_GET_SIZE(seq)) { - PyObject *next = PyTuple_GET_ITEM(seq, it->it_index++); - PUSH(Py_NewRef(next)); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); + next = Py_NewRef(PyTuple_GET_ITEM(seq, it->it_index++)); goto end_for_iter_tuple; // End of this instruction } it->it_seq = NULL; Py_DECREF(seq); } + Py_DECREF(iter); STACK_SHRINK(1); - Py_DECREF(it); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); end_for_iter_tuple: + // Common case: no jump, leave it to the code generator } - // stack effect: ( -- __0) - inst(FOR_ITER_RANGE) { + // This is slightly different, when the loop isn't terminated we + // jump over the immediately following STORE_FAST instruction. + inst(FOR_ITER_RANGE, (unused/1, iter -- iter, unused)) { assert(cframe.use_tracing == 0); - _PyRangeIterObject *r = (_PyRangeIterObject *)TOP(); + _PyRangeIterObject *r = (_PyRangeIterObject *)iter; DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER]; @@ -2162,6 +2173,7 @@ dummy_func( if (r->len <= 0) { STACK_SHRINK(1); Py_DECREF(r); + // Jump over END_FOR instruction. JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); } else { @@ -2174,11 +2186,13 @@ dummy_func( // The STORE_FAST is already done. JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + 1); } + DISPATCH(); } - inst(FOR_ITER_GEN) { + // This is *not* a super-instruction, unique in the family. + inst(FOR_ITER_GEN, (unused/1, iter -- iter, unused)) { assert(cframe.use_tracing == 0); - PyGenObject *gen = (PyGenObject *)TOP(); + PyGenObject *gen = (PyGenObject *)iter; DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER); DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER); STAT_INC(FOR_ITER, hit); @@ -3168,9 +3182,6 @@ family(call, INLINE_CACHE_ENTRIES_CALL) = { CALL_NO_KW_LIST_APPEND, CALL_NO_KW_METHOD_DESCRIPTOR_FAST, CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, CALL_NO_KW_METHOD_DESCRIPTOR_O, CALL_NO_KW_STR_1, CALL_NO_KW_TUPLE_1, CALL_NO_KW_TYPE_1 }; -family(for_iter, INLINE_CACHE_ENTRIES_FOR_ITER) = { - FOR_ITER, FOR_ITER_LIST, - FOR_ITER_RANGE }; family(store_fast) = { STORE_FAST, STORE_FAST__LOAD_FAST, STORE_FAST__STORE_FAST }; family(unpack_sequence, INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE) = { UNPACK_SEQUENCE, UNPACK_SEQUENCE_LIST, diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index f0f314a143c2c0..4e511f43d95028 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2619,25 +2619,23 @@ TARGET(FOR_ITER) { PREDICTED(FOR_ITER); + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); + PyObject *iter = PEEK(1); + PyObject *next; #if ENABLE_SPECIALIZATION _PyForIterCache *cache = (_PyForIterCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); next_instr--; - _Py_Specialize_ForIter(TOP(), next_instr, oparg); + _Py_Specialize_ForIter(iter, next_instr, oparg); DISPATCH_SAME_OPARG(); } STAT_INC(FOR_ITER, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - /* before: [iter]; after: [iter, iter()] *or* [] */ - PyObject *iter = TOP(); - PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter); - if (next != NULL) { - PUSH(next); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); - } - else { + /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */ + next = (*Py_TYPE(iter)->tp_iternext)(iter); + if (next == NULL) { if (_PyErr_Occurred(tstate)) { if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { goto error; @@ -2649,63 +2647,81 @@ } /* iterator ended normally */ assert(_Py_OPCODE(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg]) == END_FOR); - STACK_SHRINK(1); Py_DECREF(iter); - /* Skip END_FOR */ + STACK_SHRINK(1); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); } + // Common case: no jump, leave it to the code generator + STACK_GROW(1); + POKE(1, next); + JUMPBY(1); DISPATCH(); } TARGET(FOR_ITER_LIST) { + PyObject *iter = PEEK(1); + PyObject *next; assert(cframe.use_tracing == 0); - _PyListIterObject *it = (_PyListIterObject *)TOP(); - DEOPT_IF(Py_TYPE(it) != &PyListIter_Type, FOR_ITER); + DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type, FOR_ITER); + _PyListIterObject *it = (_PyListIterObject *)iter; STAT_INC(FOR_ITER, hit); PyListObject *seq = it->it_seq; if (seq) { if (it->it_index < PyList_GET_SIZE(seq)) { - PyObject *next = PyList_GET_ITEM(seq, it->it_index++); - PUSH(Py_NewRef(next)); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); + next = Py_NewRef(PyList_GET_ITEM(seq, it->it_index++)); goto end_for_iter_list; // End of this instruction } it->it_seq = NULL; Py_DECREF(seq); } + Py_DECREF(iter); STACK_SHRINK(1); - Py_DECREF(it); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); end_for_iter_list: + // Common case: no jump, leave it to the code generator + STACK_GROW(1); + POKE(1, next); + JUMPBY(1); DISPATCH(); } TARGET(FOR_ITER_TUPLE) { + PyObject *iter = PEEK(1); + PyObject *next; assert(cframe.use_tracing == 0); - _PyTupleIterObject *it = (_PyTupleIterObject *)TOP(); + _PyTupleIterObject *it = (_PyTupleIterObject *)iter; DEOPT_IF(Py_TYPE(it) != &PyTupleIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); PyTupleObject *seq = it->it_seq; if (seq) { if (it->it_index < PyTuple_GET_SIZE(seq)) { - PyObject *next = PyTuple_GET_ITEM(seq, it->it_index++); - PUSH(Py_NewRef(next)); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); + next = Py_NewRef(PyTuple_GET_ITEM(seq, it->it_index++)); goto end_for_iter_tuple; // End of this instruction } it->it_seq = NULL; Py_DECREF(seq); } + Py_DECREF(iter); STACK_SHRINK(1); - Py_DECREF(it); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); end_for_iter_tuple: + // Common case: no jump, leave it to the code generator + STACK_GROW(1); + POKE(1, next); + JUMPBY(1); DISPATCH(); } TARGET(FOR_ITER_RANGE) { + PyObject *iter = PEEK(1); assert(cframe.use_tracing == 0); - _PyRangeIterObject *r = (_PyRangeIterObject *)TOP(); + _PyRangeIterObject *r = (_PyRangeIterObject *)iter; DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER]; @@ -2713,6 +2729,7 @@ if (r->len <= 0) { STACK_SHRINK(1); Py_DECREF(r); + // Jump over END_FOR instruction. JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); } else { @@ -2729,8 +2746,9 @@ } TARGET(FOR_ITER_GEN) { + PyObject *iter = PEEK(1); assert(cframe.use_tracing == 0); - PyGenObject *gen = (PyGenObject *)TOP(); + PyGenObject *gen = (PyGenObject *)iter; DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER); DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER); STAT_INC(FOR_ITER, hit); diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 948d17519e2709..ed26ff00c7b182 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -261,15 +261,15 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case GET_YIELD_FROM_ITER: return 1; case FOR_ITER: - return -1; + return 1; case FOR_ITER_LIST: - return -1; + return 1; case FOR_ITER_TUPLE: - return -1; + return 1; case FOR_ITER_RANGE: - return -1; + return 1; case FOR_ITER_GEN: - return -1; + return 1; case BEFORE_ASYNC_WITH: return 1; case BEFORE_WITH: @@ -607,15 +607,15 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case GET_YIELD_FROM_ITER: return 1; case FOR_ITER: - return -1; + return 2; case FOR_ITER_LIST: - return -1; + return 2; case FOR_ITER_TUPLE: - return -1; + return 2; case FOR_ITER_RANGE: - return -1; + return 2; case FOR_ITER_GEN: - return -1; + return 2; case BEFORE_ASYNC_WITH: return 2; case BEFORE_WITH: @@ -829,11 +829,11 @@ struct opcode_metadata { [MATCH_KEYS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_YIELD_FROM_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FOR_ITER_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [FOR_ITER_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [BEFORE_ASYNC_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [BEFORE_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [WITH_EXCEPT_START] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, From 6fd5eb640af19b535f4f2ba27b1b61b8d17f02e9 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Tue, 7 Feb 2023 11:22:58 -0600 Subject: [PATCH 109/225] Make use of TESTFN_ASCII in test_fileio (GH-101645) testBytesOpen requires an ASCII filename, but TESTFN usually isn't ASCII. Automerge-Triggered-By: GH:zware --- Lib/test/test_fileio.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py index 2263604ed1f97d..ebfcffd1829174 100644 --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -12,7 +12,9 @@ from test.support import ( cpython_only, swap_attr, gc_collect, is_emscripten, is_wasi ) -from test.support.os_helper import (TESTFN, TESTFN_UNICODE, make_bad_fd) +from test.support.os_helper import ( + TESTFN, TESTFN_ASCII, TESTFN_UNICODE, make_bad_fd, + ) from test.support.warnings_helper import check_warnings from collections import UserList @@ -431,18 +433,15 @@ def testUnicodeOpen(self): def testBytesOpen(self): # Opening a bytes filename - try: - fn = TESTFN.encode("ascii") - except UnicodeEncodeError: - self.skipTest('could not encode %r to ascii' % TESTFN) + fn = TESTFN_ASCII.encode("ascii") f = self.FileIO(fn, "w") try: f.write(b"abc") f.close() - with open(TESTFN, "rb") as f: + with open(TESTFN_ASCII, "rb") as f: self.assertEqual(f.read(), b"abc") finally: - os.unlink(TESTFN) + os.unlink(TESTFN_ASCII) @unittest.skipIf(sys.getfilesystemencoding() != 'utf-8', "test only works for utf-8 filesystems") From f87f6e23964d7a4c38b655089cda65538a24ec36 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Tue, 7 Feb 2023 22:04:31 +0400 Subject: [PATCH 110/225] gh-97725: Fix documentation for the default file of `asyncio.Task.print_stack` (#101652) --- Doc/library/asyncio-task.rst | 2 +- .../Documentation/2023-02-07-21-43-24.gh-issue-97725.cuY7Cd.rst | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Documentation/2023-02-07-21-43-24.gh-issue-97725.cuY7Cd.rst diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index 39112580285cc0..9b984243282268 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -1097,7 +1097,7 @@ Task Object The *limit* argument is passed to :meth:`get_stack` directly. The *file* argument is an I/O stream to which the output - is written; by default output is written to :data:`sys.stderr`. + is written; by default output is written to :data:`sys.stdout`. .. method:: get_coro() diff --git a/Misc/NEWS.d/next/Documentation/2023-02-07-21-43-24.gh-issue-97725.cuY7Cd.rst b/Misc/NEWS.d/next/Documentation/2023-02-07-21-43-24.gh-issue-97725.cuY7Cd.rst new file mode 100644 index 00000000000000..fd9ea049c23968 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-02-07-21-43-24.gh-issue-97725.cuY7Cd.rst @@ -0,0 +1,2 @@ +Fix :meth:`asyncio.Task.print_stack` description for ``file=None``. +Patch by Oleg Iarygin. From dec1ab03879e959f7efb910a723caf4a9ce453cf Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 7 Feb 2023 20:37:43 +0000 Subject: [PATCH 111/225] gh-98831: rewrite UNPACK_EX, UNPACK_SEQUENCE, UNPACK_SEQUENCE_TWO_TUPLE in the instruction definition DSL (#101641) --- Python/bytecodes.c | 37 ++++++++++------------------------ Python/generated_cases.c.h | 41 +++++++++++++++++++------------------- Python/opcode_metadata.h | 16 +++++++-------- 3 files changed, 40 insertions(+), 54 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index ec0439af98e632..b43625fd283cc3 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -859,13 +859,11 @@ dummy_func( } } - // stack effect: (__0 -- __array[oparg]) - inst(UNPACK_SEQUENCE) { + inst(UNPACK_SEQUENCE, (unused/1, seq -- unused[oparg])) { #if ENABLE_SPECIALIZATION _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); - PyObject *seq = TOP(); next_instr--; _Py_Specialize_UnpackSequence(seq, next_instr, oparg); DISPATCH_SAME_OPARG(); @@ -873,27 +871,19 @@ dummy_func( STAT_INC(UNPACK_SEQUENCE, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - PyObject *seq = POP(); - PyObject **top = stack_pointer + oparg; - if (!unpack_iterable(tstate, seq, oparg, -1, top)) { - Py_DECREF(seq); - goto error; - } - STACK_GROW(oparg); + PyObject **top = stack_pointer + oparg - 1; + int res = unpack_iterable(tstate, seq, oparg, -1, top); Py_DECREF(seq); - JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); + ERROR_IF(res == 0, error); } - // stack effect: (__0 -- __array[oparg]) - inst(UNPACK_SEQUENCE_TWO_TUPLE) { - PyObject *seq = TOP(); + inst(UNPACK_SEQUENCE_TWO_TUPLE, (unused/1, seq -- v1, v0)) { DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE); STAT_INC(UNPACK_SEQUENCE, hit); - SET_TOP(Py_NewRef(PyTuple_GET_ITEM(seq, 1))); - PUSH(Py_NewRef(PyTuple_GET_ITEM(seq, 0))); + v1 = Py_NewRef(PyTuple_GET_ITEM(seq, 1)); + v0 = Py_NewRef(PyTuple_GET_ITEM(seq, 0)); Py_DECREF(seq); - JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); } // stack effect: (__0 -- __array[oparg]) @@ -926,17 +916,12 @@ dummy_func( JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); } - // error: UNPACK_EX has irregular stack effect - inst(UNPACK_EX) { + inst(UNPACK_EX, (seq -- unused[oparg & 0xFF], unused, unused[oparg >> 8])) { int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8); - PyObject *seq = POP(); - PyObject **top = stack_pointer + totalargs; - if (!unpack_iterable(tstate, seq, oparg & 0xFF, oparg >> 8, top)) { - Py_DECREF(seq); - goto error; - } - STACK_GROW(totalargs); + PyObject **top = stack_pointer + totalargs - 1; + int res = unpack_iterable(tstate, seq, oparg & 0xFF, oparg >> 8, top); Py_DECREF(seq); + ERROR_IF(res == 0, error); } family(store_attr, INLINE_CACHE_ENTRIES_STORE_ATTR) = { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 4e511f43d95028..ab19f431710644 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1108,11 +1108,11 @@ TARGET(UNPACK_SEQUENCE) { PREDICTED(UNPACK_SEQUENCE); + PyObject *seq = PEEK(1); #if ENABLE_SPECIALIZATION _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); - PyObject *seq = TOP(); next_instr--; _Py_Specialize_UnpackSequence(seq, next_instr, oparg); DISPATCH_SAME_OPARG(); @@ -1120,27 +1120,30 @@ STAT_INC(UNPACK_SEQUENCE, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - PyObject *seq = POP(); - PyObject **top = stack_pointer + oparg; - if (!unpack_iterable(tstate, seq, oparg, -1, top)) { - Py_DECREF(seq); - goto error; - } - STACK_GROW(oparg); + PyObject **top = stack_pointer + oparg - 1; + int res = unpack_iterable(tstate, seq, oparg, -1, top); Py_DECREF(seq); - JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); + if (res == 0) goto pop_1_error; + STACK_SHRINK(1); + STACK_GROW(oparg); + JUMPBY(1); DISPATCH(); } TARGET(UNPACK_SEQUENCE_TWO_TUPLE) { - PyObject *seq = TOP(); + PyObject *seq = PEEK(1); + PyObject *v1; + PyObject *v0; DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE); STAT_INC(UNPACK_SEQUENCE, hit); - SET_TOP(Py_NewRef(PyTuple_GET_ITEM(seq, 1))); - PUSH(Py_NewRef(PyTuple_GET_ITEM(seq, 0))); + v1 = Py_NewRef(PyTuple_GET_ITEM(seq, 1)); + v0 = Py_NewRef(PyTuple_GET_ITEM(seq, 0)); Py_DECREF(seq); - JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); + STACK_GROW(1); + POKE(1, v0); + POKE(2, v1); + JUMPBY(1); DISPATCH(); } @@ -1175,15 +1178,13 @@ } TARGET(UNPACK_EX) { + PyObject *seq = PEEK(1); int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8); - PyObject *seq = POP(); - PyObject **top = stack_pointer + totalargs; - if (!unpack_iterable(tstate, seq, oparg & 0xFF, oparg >> 8, top)) { - Py_DECREF(seq); - goto error; - } - STACK_GROW(totalargs); + PyObject **top = stack_pointer + totalargs - 1; + int res = unpack_iterable(tstate, seq, oparg & 0xFF, oparg >> 8, top); Py_DECREF(seq); + if (res == 0) goto pop_1_error; + STACK_GROW((oparg & 0xFF) + (oparg >> 8)); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index ed26ff00c7b182..d2585351f69fd2 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -121,15 +121,15 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case DELETE_NAME: return 0; case UNPACK_SEQUENCE: - return -1; + return 1; case UNPACK_SEQUENCE_TWO_TUPLE: - return -1; + return 1; case UNPACK_SEQUENCE_TUPLE: return -1; case UNPACK_SEQUENCE_LIST: return -1; case UNPACK_EX: - return -1; + return 1; case STORE_ATTR: return 2; case DELETE_ATTR: @@ -467,15 +467,15 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case DELETE_NAME: return 0; case UNPACK_SEQUENCE: - return -1; + return oparg; case UNPACK_SEQUENCE_TWO_TUPLE: - return -1; + return 2; case UNPACK_SEQUENCE_TUPLE: return -1; case UNPACK_SEQUENCE_LIST: return -1; case UNPACK_EX: - return -1; + return (oparg & 0xFF) + (oparg >> 8) + 1; case STORE_ATTR: return 0; case DELETE_ATTR: @@ -759,8 +759,8 @@ struct opcode_metadata { [LOAD_BUILD_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [STORE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [DELETE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNPACK_SEQUENCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNPACK_SEQUENCE_TWO_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [UNPACK_SEQUENCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [UNPACK_SEQUENCE_TWO_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, [UNPACK_SEQUENCE_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [UNPACK_SEQUENCE_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [UNPACK_EX] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, From acc2f3b19d28d4bf3f8fb32357f581cba5ba24c7 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 8 Feb 2023 00:43:33 +0300 Subject: [PATCH 112/225] gh-101656: Fix "conversion from Py_ssize_t to int" warning in `_testcapimodule` (#101657) --- Modules/_testcapimodule.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 5a6097ef0ac5a6..8b2ce1a2cfd4bd 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3185,11 +3185,11 @@ eval_eval_code_ex(PyObject *mod, PyObject *pos_args) globals, locals, c_args, - c_args_len, + (int)c_args_len, c_kwargs, - c_kwargs_len, + (int)c_kwargs_len, c_defaults, - c_defaults_len, + (int)c_defaults_len, kw_defaults, closure ); From 46f461be56ab90891d2d43240d80a0e19d100ba9 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Tue, 7 Feb 2023 23:13:10 +0100 Subject: [PATCH 113/225] Post 3.12.0a5 --- Include/patchlevel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h index df6098be8bbcc9..7957220ed7cf9f 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -23,7 +23,7 @@ #define PY_RELEASE_SERIAL 5 /* Version as a string */ -#define PY_VERSION "3.12.0a5" +#define PY_VERSION "3.12.0a5+" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. From 753fc8a5d64369cd228c3e43fef1811ac3cfde83 Mon Sep 17 00:00:00 2001 From: penguin_wwy <940375606@qq.com> Date: Wed, 8 Feb 2023 06:32:21 +0800 Subject: [PATCH 114/225] gh-101632: Add the new RETURN_CONST opcode (#101633) --- Doc/library/dis.rst | 7 + Include/internal/pycore_opcode.h | 10 +- Include/opcode.h | 16 +- Lib/dis.py | 3 +- Lib/importlib/_bootstrap_external.py | 3 +- Lib/opcode.py | 2 + Lib/test/test_ast.py | 2 +- Lib/test/test_code.py | 1 + Lib/test/test_compile.py | 18 +- Lib/test/test_dis.py | 155 ++++++++---------- Lib/test/test_peepholer.py | 6 +- ...-02-07-14-56-43.gh-issue-101632.Fd1yxk.rst | 1 + Objects/frameobject.c | 2 + Programs/test_frozenmain.h | 50 +++--- Python/bytecodes.c | 17 ++ Python/compile.c | 17 +- Python/generated_cases.c.h | 17 ++ Python/opcode_metadata.h | 5 + Python/opcode_targets.h | 8 +- 19 files changed, 186 insertions(+), 154 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-07-14-56-43.gh-issue-101632.Fd1yxk.rst diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 1fe2d5d6227d61..b1e61d7e77b2f5 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -694,6 +694,13 @@ iterations of the loop. Returns with ``STACK[-1]`` to the caller of the function. +.. opcode:: RETURN_CONST (consti) + + Returns with ``co_consts[consti]`` to the caller of the function. + + .. versionadded:: 3.12 + + .. opcode:: YIELD_VALUE Yields ``STACK.pop()`` from a :term:`generator`. diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index 05c0485b0641d8..47c84721335196 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -192,6 +192,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [RAISE_VARARGS] = RAISE_VARARGS, [RERAISE] = RERAISE, [RESUME] = RESUME, + [RETURN_CONST] = RETURN_CONST, [RETURN_GENERATOR] = RETURN_GENERATOR, [RETURN_VALUE] = RETURN_VALUE, [SEND] = SEND, @@ -349,7 +350,7 @@ static const char *const _PyOpcode_OpName[263] = { [CONTAINS_OP] = "CONTAINS_OP", [RERAISE] = "RERAISE", [COPY] = "COPY", - [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST", + [RETURN_CONST] = "RETURN_CONST", [BINARY_OP] = "BINARY_OP", [SEND] = "SEND", [LOAD_FAST] = "LOAD_FAST", @@ -371,7 +372,7 @@ static const char *const _PyOpcode_OpName[263] = { [JUMP_BACKWARD] = "JUMP_BACKWARD", [COMPARE_AND_BRANCH] = "COMPARE_AND_BRANCH", [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX", - [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", + [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST", [EXTENDED_ARG] = "EXTENDED_ARG", [LIST_APPEND] = "LIST_APPEND", [SET_ADD] = "SET_ADD", @@ -381,15 +382,15 @@ static const char *const _PyOpcode_OpName[263] = { [YIELD_VALUE] = "YIELD_VALUE", [RESUME] = "RESUME", [MATCH_CLASS] = "MATCH_CLASS", + [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT", - [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT", [FORMAT_VALUE] = "FORMAT_VALUE", [BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP", [BUILD_STRING] = "BUILD_STRING", + [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT", [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST", [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE", [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE", - [161] = "<161>", [LIST_EXTEND] = "LIST_EXTEND", [SET_UPDATE] = "SET_UPDATE", [DICT_MERGE] = "DICT_MERGE", @@ -495,7 +496,6 @@ static const char *const _PyOpcode_OpName[263] = { #endif #define EXTRA_CASES \ - case 161: \ case 166: \ case 167: \ case 168: \ diff --git a/Include/opcode.h b/Include/opcode.h index 827f9931beb3e6..77ad7c22440d72 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -76,6 +76,7 @@ extern "C" { #define CONTAINS_OP 118 #define RERAISE 119 #define COPY 120 +#define RETURN_CONST 121 #define BINARY_OP 122 #define SEND 123 #define LOAD_FAST 124 @@ -179,13 +180,13 @@ extern "C" { #define STORE_ATTR_INSTANCE_VALUE 86 #define STORE_ATTR_SLOT 87 #define STORE_ATTR_WITH_HINT 113 -#define STORE_FAST__LOAD_FAST 121 -#define STORE_FAST__STORE_FAST 143 -#define STORE_SUBSCR_DICT 153 -#define STORE_SUBSCR_LIST_INT 154 -#define UNPACK_SEQUENCE_LIST 158 -#define UNPACK_SEQUENCE_TUPLE 159 -#define UNPACK_SEQUENCE_TWO_TUPLE 160 +#define STORE_FAST__LOAD_FAST 143 +#define STORE_FAST__STORE_FAST 153 +#define STORE_SUBSCR_DICT 154 +#define STORE_SUBSCR_LIST_INT 158 +#define UNPACK_SEQUENCE_LIST 159 +#define UNPACK_SEQUENCE_TUPLE 160 +#define UNPACK_SEQUENCE_TWO_TUPLE 161 #define DO_TRACING 255 #define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\ @@ -196,6 +197,7 @@ extern "C" { #define HAS_CONST(op) (false\ || ((op) == LOAD_CONST) \ + || ((op) == RETURN_CONST) \ || ((op) == KW_NAMES) \ ) diff --git a/Lib/dis.py b/Lib/dis.py index 72ab9536a2bf6a..a6921008d9d0e5 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -34,6 +34,7 @@ MAKE_FUNCTION_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure') LOAD_CONST = opmap['LOAD_CONST'] +RETURN_CONST = opmap['RETURN_CONST'] LOAD_GLOBAL = opmap['LOAD_GLOBAL'] BINARY_OP = opmap['BINARY_OP'] JUMP_BACKWARD = opmap['JUMP_BACKWARD'] @@ -363,7 +364,7 @@ def _get_const_value(op, arg, co_consts): assert op in hasconst argval = UNKNOWN - if op == LOAD_CONST: + if op == LOAD_CONST or op == RETURN_CONST: if co_consts is not None: argval = co_consts[arg] return argval diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index e760fbb15759d4..933c8c7d7e0590 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -431,6 +431,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.12a5 3515 (Embed jump mask in COMPARE_OP oparg) # Python 3.12a5 3516 (Add COMPARE_AND_BRANCH instruction) # Python 3.12a5 3517 (Change YIELD_VALUE oparg to exception block depth) +# Python 3.12a5 3518 (Add RETURN_CONST instruction) # Python 3.13 will start with 3550 @@ -443,7 +444,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3517).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3518).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c diff --git a/Lib/opcode.py b/Lib/opcode.py index c317e23beae62b..5f163d2ccb80df 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -164,6 +164,8 @@ def pseudo_op(name, op, real_ops): def_op('CONTAINS_OP', 118) def_op('RERAISE', 119) def_op('COPY', 120) +def_op('RETURN_CONST', 121) +hasconst.append(121) def_op('BINARY_OP', 122) jrel_op('SEND', 123) # Number of bytes to skip def_op('LOAD_FAST', 124) # Local variable number, no null check diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 98d9b603bbc1cb..7c9a57c685df75 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -1900,7 +1900,7 @@ def get_load_const(self, tree): co = compile(tree, '', 'exec') consts = [] for instr in dis.get_instructions(co): - if instr.opname == 'LOAD_CONST': + if instr.opname == 'LOAD_CONST' or instr.opname == 'RETURN_CONST': consts.append(instr.argval) return consts diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 67ed1694205cd6..9c2ac83e1b69e3 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -723,6 +723,7 @@ def f(): pass PY_CODE_LOCATION_INFO_NO_COLUMNS = 13 f.__code__ = f.__code__.replace( + co_stacksize=1, co_firstlineno=42, co_code=bytes( [ diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 05a5ed1fa9a637..90b067bcf30912 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -741,7 +741,7 @@ def unused_code_at_end(): # RETURN_VALUE opcode. This does not always crash an interpreter. # When you build with the clang memory sanitizer it reliably aborts. self.assertEqual( - 'RETURN_VALUE', + 'RETURN_CONST', list(dis.get_instructions(unused_code_at_end))[-1].opname) def test_dont_merge_constants(self): @@ -822,10 +822,9 @@ def unused_block_while_else(): for func in funcs: opcodes = list(dis.get_instructions(func)) - self.assertLessEqual(len(opcodes), 4) - self.assertEqual('LOAD_CONST', opcodes[-2].opname) - self.assertEqual(None, opcodes[-2].argval) - self.assertEqual('RETURN_VALUE', opcodes[-1].opname) + self.assertLessEqual(len(opcodes), 3) + self.assertEqual('RETURN_CONST', opcodes[-1].opname) + self.assertEqual(None, opcodes[-1].argval) def test_false_while_loop(self): def break_in_while(): @@ -841,10 +840,9 @@ def continue_in_while(): # Check that we did not raise but we also don't generate bytecode for func in funcs: opcodes = list(dis.get_instructions(func)) - self.assertEqual(3, len(opcodes)) - self.assertEqual('LOAD_CONST', opcodes[1].opname) + self.assertEqual(2, len(opcodes)) + self.assertEqual('RETURN_CONST', opcodes[1].opname) self.assertEqual(None, opcodes[1].argval) - self.assertEqual('RETURN_VALUE', opcodes[2].opname) def test_consts_in_conditionals(self): def and_true(x): @@ -1311,7 +1309,7 @@ def test_multiline_generator_expression(self): line=1, end_line=2, column=1, end_column=8, occurrence=1) self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD', line=1, end_line=2, column=1, end_column=8, occurrence=1) - self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE', + self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_CONST', line=1, end_line=6, column=0, end_column=32, occurrence=1) def test_multiline_async_generator_expression(self): @@ -1328,7 +1326,7 @@ def test_multiline_async_generator_expression(self): self.assertIsInstance(compiled_code, types.CodeType) self.assertOpcodeSourcePositionIs(compiled_code, 'YIELD_VALUE', line=1, end_line=2, column=1, end_column=8, occurrence=2) - self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE', + self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_CONST', line=1, end_line=6, column=0, end_column=32, occurrence=1) def test_multiline_list_comprehension(self): diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index bdf48c15309296..1050b15e16eaaa 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -49,8 +49,7 @@ def cm(cls, x): COMPARE_OP 32 (==) LOAD_FAST 0 (self) STORE_ATTR 0 (x) - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ % (_C.__init__.__code__.co_firstlineno, _C.__init__.__code__.co_firstlineno + 1,) dis_c_instance_method_bytes = """\ @@ -60,8 +59,7 @@ def cm(cls, x): COMPARE_OP 32 (==) LOAD_FAST 0 STORE_ATTR 0 - LOAD_CONST 0 - RETURN_VALUE + RETURN_CONST 0 """ dis_c_class_method = """\ @@ -72,8 +70,7 @@ def cm(cls, x): COMPARE_OP 32 (==) LOAD_FAST 0 (cls) STORE_ATTR 0 (x) - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ % (_C.cm.__code__.co_firstlineno, _C.cm.__code__.co_firstlineno + 2,) dis_c_static_method = """\ @@ -83,8 +80,7 @@ def cm(cls, x): LOAD_CONST 1 (1) COMPARE_OP 32 (==) STORE_FAST 0 (x) - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ % (_C.sm.__code__.co_firstlineno, _C.sm.__code__.co_firstlineno + 2,) # Class disassembling info has an extra newline at end. @@ -111,8 +107,7 @@ def _f(a): CALL 1 POP_TOP -%3d LOAD_CONST 1 (1) - RETURN_VALUE +%3d RETURN_CONST 1 (1) """ % (_f.__code__.co_firstlineno, _f.__code__.co_firstlineno + 1, _f.__code__.co_firstlineno + 2) @@ -124,8 +119,7 @@ def _f(a): LOAD_FAST 0 CALL 1 POP_TOP - LOAD_CONST 1 - RETURN_VALUE + RETURN_CONST 1 """ @@ -150,8 +144,7 @@ def bug708901(): %3d JUMP_BACKWARD 4 (to 30) %3d >> END_FOR - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ % (bug708901.__code__.co_firstlineno, bug708901.__code__.co_firstlineno + 1, bug708901.__code__.co_firstlineno + 2, @@ -198,8 +191,7 @@ def bug42562(): dis_bug42562 = """\ RESUME 0 - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ # Extended arg followed by NOP @@ -240,8 +232,7 @@ def bug42562(): %3d LOAD_GLOBAL 0 (spam) POP_TOP - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ _BIG_LINENO_FORMAT2 = """\ @@ -249,20 +240,17 @@ def bug42562(): %4d LOAD_GLOBAL 0 (spam) POP_TOP - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ dis_module_expected_results = """\ Disassembly of f: 4 RESUME 0 - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) Disassembly of g: 5 RESUME 0 - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ @@ -286,8 +274,7 @@ def bug42562(): LOAD_CONST 0 (1) BINARY_OP 0 (+) STORE_NAME 0 (x) - LOAD_CONST 1 (None) - RETURN_VALUE + RETURN_CONST 1 (None) """ annot_stmt_str = """\ @@ -326,8 +313,7 @@ def bug42562(): STORE_SUBSCR LOAD_NAME 1 (int) POP_TOP - LOAD_CONST 4 (None) - RETURN_VALUE + RETURN_CONST 4 (None) """ compound_stmt_str = """\ @@ -447,12 +433,11 @@ def _with(c): %3d LOAD_CONST 2 (2) STORE_FAST 2 (y) - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) %3d >> PUSH_EXC_INFO WITH_EXCEPT_START - POP_JUMP_IF_TRUE 1 (to 46) + POP_JUMP_IF_TRUE 1 (to 44) RERAISE 2 >> POP_TOP POP_EXCEPT @@ -461,8 +446,7 @@ def _with(c): %3d LOAD_CONST 2 (2) STORE_FAST 2 (y) - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) >> COPY 3 POP_EXCEPT RERAISE 1 @@ -514,23 +498,22 @@ async def _asyncwith(c): %3d LOAD_CONST 2 (2) STORE_FAST 2 (y) - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) %3d >> CLEANUP_THROW - JUMP_BACKWARD 24 (to 22) + JUMP_BACKWARD 23 (to 22) >> CLEANUP_THROW - JUMP_BACKWARD 9 (to 56) + JUMP_BACKWARD 8 (to 56) >> PUSH_EXC_INFO WITH_EXCEPT_START GET_AWAITABLE 2 LOAD_CONST 0 (None) - >> SEND 4 (to 92) + >> SEND 4 (to 90) YIELD_VALUE 3 RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 4 (to 82) + JUMP_BACKWARD_NO_INTERRUPT 4 (to 80) >> CLEANUP_THROW - >> POP_JUMP_IF_TRUE 1 (to 96) + >> POP_JUMP_IF_TRUE 1 (to 94) RERAISE 2 >> POP_TOP POP_EXCEPT @@ -539,8 +522,7 @@ async def _asyncwith(c): %3d LOAD_CONST 2 (2) STORE_FAST 2 (y) - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) >> COPY 3 POP_EXCEPT RERAISE 1 @@ -610,8 +592,7 @@ def _tryfinallyconst(b): LOAD_FAST 0 (b) CALL 0 POP_TOP - LOAD_CONST 1 (1) - RETURN_VALUE + RETURN_CONST 1 (1) PUSH_EXC_INFO PUSH_NULL LOAD_FAST 0 (b) @@ -754,8 +735,7 @@ def loop_test(): JUMP_BACKWARD 17 (to 16) %3d >> END_FOR - LOAD_CONST 0 (None) - RETURN_VALUE + RETURN_CONST 0 (None) """ % (loop_test.__code__.co_firstlineno, loop_test.__code__.co_firstlineno + 1, loop_test.__code__.co_firstlineno + 2, @@ -772,8 +752,7 @@ def extended_arg_quick(): 6 UNPACK_EX 256 8 STORE_FAST 0 (_) 10 STORE_FAST 0 (_) - 12 LOAD_CONST 0 (None) - 14 RETURN_VALUE + 12 RETURN_CONST 0 (None) """% (extended_arg_quick.__code__.co_firstlineno, extended_arg_quick.__code__.co_firstlineno + 1,) @@ -1549,8 +1528,7 @@ def _prepare_test_cases(): Instruction(opname='LOAD_FAST', opcode=124, arg=1, argval='f', argrepr='f', offset=26, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='CALL', opcode=171, arg=6, argval=6, argrepr='', offset=28, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=38, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=40, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=42, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RETURN_CONST', opcode=121, arg=0, argval=None, argrepr='None', offset=40, starts_line=None, is_jump_target=False, positions=None) ] expected_opinfo_jumpy = [ @@ -1630,52 +1608,50 @@ def _prepare_test_cases(): Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=286, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=288, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=298, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=300, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=302, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=304, starts_line=25, is_jump_target=False, positions=None), - Instruction(opname='WITH_EXCEPT_START', opcode=49, arg=None, argval=None, argrepr='', offset=306, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=1, argval=312, argrepr='to 312', offset=308, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=2, argval=2, argrepr='', offset=310, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=312, starts_line=None, is_jump_target=True, positions=None), - Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=314, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RETURN_CONST', opcode=121, arg=0, argval=None, argrepr='None', offset=300, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=302, starts_line=25, is_jump_target=False, positions=None), + Instruction(opname='WITH_EXCEPT_START', opcode=49, arg=None, argval=None, argrepr='', offset=304, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=1, argval=310, argrepr='to 310', offset=306, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=2, argval=2, argrepr='', offset=308, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=310, starts_line=None, is_jump_target=True, positions=None), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=312, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=314, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=316, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=318, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=140, arg=24, argval=274, argrepr='to 274', offset=320, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=322, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=324, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=326, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=328, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=330, starts_line=22, is_jump_target=False, positions=None), - Instruction(opname='CHECK_EXC_MATCH', opcode=36, arg=None, argval=None, argrepr='', offset=342, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=16, argval=378, argrepr='to 378', offset=344, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=346, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=348, starts_line=23, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=360, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=362, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=372, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=374, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=140, arg=52, argval=274, argrepr='to 274', offset=376, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=0, argval=0, argrepr='', offset=378, starts_line=22, is_jump_target=True, positions=None), - Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=380, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=382, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=384, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=386, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=388, starts_line=28, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=400, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=402, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=412, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=0, argval=0, argrepr='', offset=414, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=416, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=418, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=420, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=140, arg=23, argval=274, argrepr='to 274', offset=318, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=320, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=322, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=324, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=326, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=328, starts_line=22, is_jump_target=False, positions=None), + Instruction(opname='CHECK_EXC_MATCH', opcode=36, arg=None, argval=None, argrepr='', offset=340, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=16, argval=376, argrepr='to 376', offset=342, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=344, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=346, starts_line=23, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=358, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=360, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=370, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=372, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=140, arg=51, argval=274, argrepr='to 274', offset=374, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=0, argval=0, argrepr='', offset=376, starts_line=22, is_jump_target=True, positions=None), + Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=378, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=380, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=382, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=384, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=386, starts_line=28, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=398, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=400, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=410, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=0, argval=0, argrepr='', offset=412, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=414, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=416, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=418, starts_line=None, is_jump_target=False, positions=None) ] # One last piece of inspect fodder to check the default line number handling def simple(): pass expected_opinfo_simple = [ Instruction(opname='RESUME', opcode=151, arg=0, argval=0, argrepr='', offset=0, starts_line=simple.__code__.co_firstlineno, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=2, starts_line=None, is_jump_target=False), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=4, starts_line=None, is_jump_target=False) + Instruction(opname='RETURN_CONST', opcode=121, arg=0, argval=None, argrepr='None', offset=2, starts_line=None, is_jump_target=False), ] @@ -1736,7 +1712,6 @@ def test_co_positions(self): (2, 2, 8, 9), (1, 3, 0, 1), (1, 3, 0, 1), - (1, 3, 0, 1), (1, 3, 0, 1) ] self.assertEqual(positions, expected) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 239c9d03fd9d1f..707ff821b31a8a 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -117,7 +117,7 @@ def f(): return None self.assertNotInBytecode(f, 'LOAD_GLOBAL') - self.assertInBytecode(f, 'LOAD_CONST', None) + self.assertInBytecode(f, 'RETURN_CONST', None) self.check_lnotab(f) def test_while_one(self): @@ -134,7 +134,7 @@ def f(): def test_pack_unpack(self): for line, elem in ( - ('a, = a,', 'LOAD_CONST',), + ('a, = a,', 'RETURN_CONST',), ('a, b = a, b', 'SWAP',), ('a, b, c = a, b, c', 'SWAP',), ): @@ -165,7 +165,7 @@ def test_folding_of_tuples_of_constants(self): # One LOAD_CONST for the tuple, one for the None return value load_consts = [instr for instr in dis.get_instructions(code) if instr.opname == 'LOAD_CONST'] - self.assertEqual(len(load_consts), 2) + self.assertEqual(len(load_consts), 1) self.check_lnotab(code) # Bug 1053819: Tuple of constants misidentified when presented with: diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-07-14-56-43.gh-issue-101632.Fd1yxk.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-07-14-56-43.gh-issue-101632.Fd1yxk.rst new file mode 100644 index 00000000000000..136909ca699903 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-07-14-56-43.gh-issue-101632.Fd1yxk.rst @@ -0,0 +1 @@ +Adds a new :opcode:`RETURN_CONST` instruction. diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 6bc04bc8e848fc..0e52a3e2399c06 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -397,6 +397,8 @@ mark_stacks(PyCodeObject *code_obj, int len) assert(pop_value(next_stack) == EMPTY_STACK); assert(top_of_stack(next_stack) == Object); break; + case RETURN_CONST: + break; case RAISE_VARARGS: break; case RERAISE: diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index 95f78b19e65eb6..8e5055bd7bceb1 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -1,7 +1,7 @@ // Auto-generated by Programs/freeze_test_frozenmain.py unsigned char M_test_frozenmain[] = { 227,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0, - 0,0,0,0,0,243,184,0,0,0,151,0,100,0,100,1, + 0,0,0,0,0,243,182,0,0,0,151,0,100,0,100,1, 108,0,90,0,100,0,100,1,108,1,90,1,2,0,101,2, 100,2,171,1,0,0,0,0,0,0,0,0,1,0,2,0, 101,2,100,3,101,0,106,6,0,0,0,0,0,0,0,0, @@ -12,28 +12,28 @@ unsigned char M_test_frozenmain[] = { 0,0,0,0,90,5,100,5,68,0,93,23,0,0,90,6, 2,0,101,2,100,6,101,6,155,0,100,7,101,5,101,6, 25,0,0,0,0,0,0,0,0,0,155,0,157,4,171,1, - 0,0,0,0,0,0,0,0,1,0,140,25,4,0,100,1, - 83,0,41,8,233,0,0,0,0,78,122,18,70,114,111,122, - 101,110,32,72,101,108,108,111,32,87,111,114,108,100,122,8, - 115,121,115,46,97,114,103,118,218,6,99,111,110,102,105,103, - 41,5,218,12,112,114,111,103,114,97,109,95,110,97,109,101, - 218,10,101,120,101,99,117,116,97,98,108,101,218,15,117,115, - 101,95,101,110,118,105,114,111,110,109,101,110,116,218,17,99, - 111,110,102,105,103,117,114,101,95,99,95,115,116,100,105,111, - 218,14,98,117,102,102,101,114,101,100,95,115,116,100,105,111, - 122,7,99,111,110,102,105,103,32,122,2,58,32,41,7,218, - 3,115,121,115,218,17,95,116,101,115,116,105,110,116,101,114, - 110,97,108,99,97,112,105,218,5,112,114,105,110,116,218,4, - 97,114,103,118,218,11,103,101,116,95,99,111,110,102,105,103, - 115,114,3,0,0,0,218,3,107,101,121,169,0,243,0,0, - 0,0,250,18,116,101,115,116,95,102,114,111,122,101,110,109, - 97,105,110,46,112,121,250,8,60,109,111,100,117,108,101,62, - 114,18,0,0,0,1,0,0,0,115,100,0,0,0,240,3, - 1,1,1,243,8,0,1,11,219,0,24,225,0,5,208,6, - 26,213,0,27,217,0,5,128,106,144,35,151,40,145,40,213, - 0,27,216,9,38,208,9,26,215,9,38,209,9,38,212,9, - 40,168,24,212,9,50,128,6,240,2,6,12,2,242,0,7, - 1,42,128,67,241,14,0,5,10,208,10,40,144,67,209,10, - 40,152,54,160,35,156,59,209,10,40,214,4,41,242,15,7, - 1,42,114,16,0,0,0, + 0,0,0,0,0,0,0,0,1,0,140,25,4,0,121,1, + 41,8,233,0,0,0,0,78,122,18,70,114,111,122,101,110, + 32,72,101,108,108,111,32,87,111,114,108,100,122,8,115,121, + 115,46,97,114,103,118,218,6,99,111,110,102,105,103,41,5, + 218,12,112,114,111,103,114,97,109,95,110,97,109,101,218,10, + 101,120,101,99,117,116,97,98,108,101,218,15,117,115,101,95, + 101,110,118,105,114,111,110,109,101,110,116,218,17,99,111,110, + 102,105,103,117,114,101,95,99,95,115,116,100,105,111,218,14, + 98,117,102,102,101,114,101,100,95,115,116,100,105,111,122,7, + 99,111,110,102,105,103,32,122,2,58,32,41,7,218,3,115, + 121,115,218,17,95,116,101,115,116,105,110,116,101,114,110,97, + 108,99,97,112,105,218,5,112,114,105,110,116,218,4,97,114, + 103,118,218,11,103,101,116,95,99,111,110,102,105,103,115,114, + 3,0,0,0,218,3,107,101,121,169,0,243,0,0,0,0, + 250,18,116,101,115,116,95,102,114,111,122,101,110,109,97,105, + 110,46,112,121,250,8,60,109,111,100,117,108,101,62,114,18, + 0,0,0,1,0,0,0,115,100,0,0,0,240,3,1,1, + 1,243,8,0,1,11,219,0,24,225,0,5,208,6,26,213, + 0,27,217,0,5,128,106,144,35,151,40,145,40,213,0,27, + 216,9,38,208,9,26,215,9,38,209,9,38,212,9,40,168, + 24,212,9,50,128,6,240,2,6,12,2,242,0,7,1,42, + 128,67,241,14,0,5,10,208,10,40,144,67,209,10,40,152, + 54,160,35,156,59,209,10,40,214,4,41,241,15,7,1,42, + 114,16,0,0,0, }; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index b43625fd283cc3..0d7d922816ce91 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -555,6 +555,23 @@ dummy_func( goto resume_frame; } + inst(RETURN_CONST, (--)) { + PyObject *retval = GETITEM(consts, oparg); + Py_INCREF(retval); + assert(EMPTY()); + _PyFrame_SetStackPointer(frame, stack_pointer); + TRACE_FUNCTION_EXIT(); + DTRACE_FUNCTION_EXIT(); + _Py_LeaveRecursiveCallPy(tstate); + assert(frame != &entry_frame); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = cframe.current_frame = dying->previous; + _PyEvalFrameClearAndPop(tstate, dying); + _PyFrame_StackPush(frame, retval); + goto resume_frame; + } + inst(GET_AITER, (obj -- iter)) { unaryfunc getter = NULL; PyTypeObject *type = Py_TYPE(obj); diff --git a/Python/compile.c b/Python/compile.c index d9ec68958972b5..df2dffb95bbd7e 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -124,6 +124,7 @@ #define IS_SCOPE_EXIT_OPCODE(opcode) \ ((opcode) == RETURN_VALUE || \ + (opcode) == RETURN_CONST || \ (opcode) == RAISE_VARARGS || \ (opcode) == RERAISE) @@ -354,7 +355,7 @@ basicblock_last_instr(const basicblock *b) { static inline int basicblock_returns(const basicblock *b) { struct instr *last = basicblock_last_instr(b); - return last && last->i_opcode == RETURN_VALUE; + return last && (last->i_opcode == RETURN_VALUE || last->i_opcode == RETURN_CONST); } static inline int @@ -1119,6 +1120,8 @@ stack_effect(int opcode, int oparg, int jump) case RETURN_VALUE: return -1; + case RETURN_CONST: + return 0; case SETUP_ANNOTATIONS: return 0; case YIELD_VALUE: @@ -9261,6 +9264,10 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) } Py_DECREF(cnt); break; + case RETURN_VALUE: + INSTR_SET_OP1(inst, RETURN_CONST, oparg); + INSTR_SET_OP0(&bb->b_instr[i + 1], NOP); + break; } break; } @@ -9723,9 +9730,7 @@ remove_unused_consts(basicblock *entryblock, PyObject *consts) /* mark used consts */ for (basicblock *b = entryblock; b != NULL; b = b->b_next) { for (int i = 0; i < b->b_iused; i++) { - if (b->b_instr[i].i_opcode == LOAD_CONST || - b->b_instr[i].i_opcode == KW_NAMES) { - + if (HAS_CONST(b->b_instr[i].i_opcode)) { int index = b->b_instr[i].i_oparg; index_map[index] = index; } @@ -9780,9 +9785,7 @@ remove_unused_consts(basicblock *entryblock, PyObject *consts) for (basicblock *b = entryblock; b != NULL; b = b->b_next) { for (int i = 0; i < b->b_iused; i++) { - if (b->b_instr[i].i_opcode == LOAD_CONST || - b->b_instr[i].i_opcode == KW_NAMES) { - + if (HAS_CONST(b->b_instr[i].i_opcode)) { int index = b->b_instr[i].i_oparg; assert(reverse_index_map[index] >= 0); assert(reverse_index_map[index] < n_used_consts); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index ab19f431710644..de98b1a4f2ed72 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -742,6 +742,23 @@ goto resume_frame; } + TARGET(RETURN_CONST) { + PyObject *retval = GETITEM(consts, oparg); + Py_INCREF(retval); + assert(EMPTY()); + _PyFrame_SetStackPointer(frame, stack_pointer); + TRACE_FUNCTION_EXIT(); + DTRACE_FUNCTION_EXIT(); + _Py_LeaveRecursiveCallPy(tstate); + assert(frame != &entry_frame); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = cframe.current_frame = dying->previous; + _PyEvalFrameClearAndPop(tstate, dying); + _PyFrame_StackPush(frame, retval); + goto resume_frame; + } + TARGET(GET_AITER) { PyObject *obj = PEEK(1); PyObject *iter; diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index d2585351f69fd2..bae5492c0496e6 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -92,6 +92,8 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 1; case RETURN_VALUE: return 1; + case RETURN_CONST: + return 0; case GET_AITER: return 1; case GET_ANEXT: @@ -438,6 +440,8 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return 0; case RETURN_VALUE: return 0; + case RETURN_CONST: + return 0; case GET_AITER: return 1; case GET_ANEXT: @@ -745,6 +749,7 @@ struct opcode_metadata { [RAISE_VARARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [INTERPRETER_EXIT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [RETURN_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [RETURN_CONST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [GET_AITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_ANEXT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_AWAITABLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index f1c3f3e0c4ee17..eceb246fac4909 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -120,7 +120,7 @@ static void *opcode_targets[256] = { &&TARGET_CONTAINS_OP, &&TARGET_RERAISE, &&TARGET_COPY, - &&TARGET_STORE_FAST__LOAD_FAST, + &&TARGET_RETURN_CONST, &&TARGET_BINARY_OP, &&TARGET_SEND, &&TARGET_LOAD_FAST, @@ -142,7 +142,7 @@ static void *opcode_targets[256] = { &&TARGET_JUMP_BACKWARD, &&TARGET_COMPARE_AND_BRANCH, &&TARGET_CALL_FUNCTION_EX, - &&TARGET_STORE_FAST__STORE_FAST, + &&TARGET_STORE_FAST__LOAD_FAST, &&TARGET_EXTENDED_ARG, &&TARGET_LIST_APPEND, &&TARGET_SET_ADD, @@ -152,15 +152,15 @@ static void *opcode_targets[256] = { &&TARGET_YIELD_VALUE, &&TARGET_RESUME, &&TARGET_MATCH_CLASS, + &&TARGET_STORE_FAST__STORE_FAST, &&TARGET_STORE_SUBSCR_DICT, - &&TARGET_STORE_SUBSCR_LIST_INT, &&TARGET_FORMAT_VALUE, &&TARGET_BUILD_CONST_KEY_MAP, &&TARGET_BUILD_STRING, + &&TARGET_STORE_SUBSCR_LIST_INT, &&TARGET_UNPACK_SEQUENCE_LIST, &&TARGET_UNPACK_SEQUENCE_TUPLE, &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, - &&_unknown_opcode, &&TARGET_LIST_EXTEND, &&TARGET_SET_UPDATE, &&TARGET_DICT_MERGE, From aacbdb0c650492756738b044e6ddf8b72f89a1a2 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 7 Feb 2023 15:44:37 -0800 Subject: [PATCH 115/225] gh-98831: Finish the UNPACK_SEQUENCE family (#101666) New generator feature: Generate useful glue for output arrays, so you can just write values to the output array (no bounds checking). Rewrote UNPACK_SEQUENCE_TWO_TUPLE to use this, and also UNPACK_SEQUENCE_{TUPLE,LIST}. --- Python/bytecodes.c | 37 +++++++++++------------- Python/generated_cases.c.h | 38 ++++++++++++++----------- Python/opcode_metadata.h | 16 +++++------ Tools/cases_generator/generate_cases.py | 25 +++++++++++----- Tools/cases_generator/test_generator.py | 19 ++++++------- 5 files changed, 72 insertions(+), 63 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 0d7d922816ce91..c6c00a7ab9b0cf 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -876,6 +876,13 @@ dummy_func( } } + family(unpack_sequence, INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE) = { + UNPACK_SEQUENCE, + UNPACK_SEQUENCE_TWO_TUPLE, + UNPACK_SEQUENCE_TUPLE, + UNPACK_SEQUENCE_LIST, + }; + inst(UNPACK_SEQUENCE, (unused/1, seq -- unused[oparg])) { #if ENABLE_SPECIALIZATION _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr; @@ -894,43 +901,36 @@ dummy_func( ERROR_IF(res == 0, error); } - inst(UNPACK_SEQUENCE_TWO_TUPLE, (unused/1, seq -- v1, v0)) { + inst(UNPACK_SEQUENCE_TWO_TUPLE, (unused/1, seq -- values[oparg])) { DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE); + assert(oparg == 2); STAT_INC(UNPACK_SEQUENCE, hit); - v1 = Py_NewRef(PyTuple_GET_ITEM(seq, 1)); - v0 = Py_NewRef(PyTuple_GET_ITEM(seq, 0)); + values[0] = Py_NewRef(PyTuple_GET_ITEM(seq, 1)); + values[1] = Py_NewRef(PyTuple_GET_ITEM(seq, 0)); Py_DECREF(seq); } - // stack effect: (__0 -- __array[oparg]) - inst(UNPACK_SEQUENCE_TUPLE) { - PyObject *seq = TOP(); + inst(UNPACK_SEQUENCE_TUPLE, (unused/1, seq -- values[oparg])) { DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyTuple_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); STAT_INC(UNPACK_SEQUENCE, hit); - STACK_SHRINK(1); PyObject **items = _PyTuple_ITEMS(seq); - while (oparg--) { - PUSH(Py_NewRef(items[oparg])); + for (int i = oparg; --i >= 0; ) { + *values++ = Py_NewRef(items[i]); } Py_DECREF(seq); - JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); } - // stack effect: (__0 -- __array[oparg]) - inst(UNPACK_SEQUENCE_LIST) { - PyObject *seq = TOP(); + inst(UNPACK_SEQUENCE_LIST, (unused/1, seq -- values[oparg])) { DEOPT_IF(!PyList_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyList_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); STAT_INC(UNPACK_SEQUENCE, hit); - STACK_SHRINK(1); PyObject **items = _PyList_ITEMS(seq); - while (oparg--) { - PUSH(Py_NewRef(items[oparg])); + for (int i = oparg; --i >= 0; ) { + *values++ = Py_NewRef(items[i]); } Py_DECREF(seq); - JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); } inst(UNPACK_EX, (seq -- unused[oparg & 0xFF], unused, unused[oparg >> 8])) { @@ -3185,6 +3185,3 @@ family(call, INLINE_CACHE_ENTRIES_CALL) = { CALL_NO_KW_METHOD_DESCRIPTOR_O, CALL_NO_KW_STR_1, CALL_NO_KW_TUPLE_1, CALL_NO_KW_TYPE_1 }; family(store_fast) = { STORE_FAST, STORE_FAST__LOAD_FAST, STORE_FAST__STORE_FAST }; -family(unpack_sequence, INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE) = { - UNPACK_SEQUENCE, UNPACK_SEQUENCE_LIST, - UNPACK_SEQUENCE_TUPLE, UNPACK_SEQUENCE_TWO_TUPLE }; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index de98b1a4f2ed72..ded68d011c6ba1 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1125,6 +1125,7 @@ TARGET(UNPACK_SEQUENCE) { PREDICTED(UNPACK_SEQUENCE); + static_assert(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE == 1, "incorrect cache size"); PyObject *seq = PEEK(1); #if ENABLE_SPECIALIZATION _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr; @@ -1149,48 +1150,51 @@ TARGET(UNPACK_SEQUENCE_TWO_TUPLE) { PyObject *seq = PEEK(1); - PyObject *v1; - PyObject *v0; + PyObject **values = stack_pointer - (1); DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE); + assert(oparg == 2); STAT_INC(UNPACK_SEQUENCE, hit); - v1 = Py_NewRef(PyTuple_GET_ITEM(seq, 1)); - v0 = Py_NewRef(PyTuple_GET_ITEM(seq, 0)); + values[0] = Py_NewRef(PyTuple_GET_ITEM(seq, 1)); + values[1] = Py_NewRef(PyTuple_GET_ITEM(seq, 0)); Py_DECREF(seq); - STACK_GROW(1); - POKE(1, v0); - POKE(2, v1); + STACK_SHRINK(1); + STACK_GROW(oparg); JUMPBY(1); DISPATCH(); } TARGET(UNPACK_SEQUENCE_TUPLE) { - PyObject *seq = TOP(); + PyObject *seq = PEEK(1); + PyObject **values = stack_pointer - (1); DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyTuple_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); STAT_INC(UNPACK_SEQUENCE, hit); - STACK_SHRINK(1); PyObject **items = _PyTuple_ITEMS(seq); - while (oparg--) { - PUSH(Py_NewRef(items[oparg])); + for (int i = oparg; --i >= 0; ) { + *values++ = Py_NewRef(items[i]); } Py_DECREF(seq); - JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); + STACK_SHRINK(1); + STACK_GROW(oparg); + JUMPBY(1); DISPATCH(); } TARGET(UNPACK_SEQUENCE_LIST) { - PyObject *seq = TOP(); + PyObject *seq = PEEK(1); + PyObject **values = stack_pointer - (1); DEOPT_IF(!PyList_CheckExact(seq), UNPACK_SEQUENCE); DEOPT_IF(PyList_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); STAT_INC(UNPACK_SEQUENCE, hit); - STACK_SHRINK(1); PyObject **items = _PyList_ITEMS(seq); - while (oparg--) { - PUSH(Py_NewRef(items[oparg])); + for (int i = oparg; --i >= 0; ) { + *values++ = Py_NewRef(items[i]); } Py_DECREF(seq); - JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); + STACK_SHRINK(1); + STACK_GROW(oparg); + JUMPBY(1); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index bae5492c0496e6..c1e12a4bbede89 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -127,9 +127,9 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case UNPACK_SEQUENCE_TWO_TUPLE: return 1; case UNPACK_SEQUENCE_TUPLE: - return -1; + return 1; case UNPACK_SEQUENCE_LIST: - return -1; + return 1; case UNPACK_EX: return 1; case STORE_ATTR: @@ -473,11 +473,11 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case UNPACK_SEQUENCE: return oparg; case UNPACK_SEQUENCE_TWO_TUPLE: - return 2; + return oparg; case UNPACK_SEQUENCE_TUPLE: - return -1; + return oparg; case UNPACK_SEQUENCE_LIST: - return -1; + return oparg; case UNPACK_EX: return (oparg & 0xFF) + (oparg >> 8) + 1; case STORE_ATTR: @@ -765,9 +765,9 @@ struct opcode_metadata { [STORE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [DELETE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [UNPACK_SEQUENCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [UNPACK_SEQUENCE_TWO_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [UNPACK_SEQUENCE_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNPACK_SEQUENCE_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNPACK_SEQUENCE_TWO_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [UNPACK_SEQUENCE_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [UNPACK_SEQUENCE_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [UNPACK_EX] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [STORE_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, [DELETE_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 3925583b40e728..4f94b48d114de8 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -180,11 +180,8 @@ def assign(self, dst: StackEffect, src: StackEffect): stmt = f"if ({src.cond}) {{ {stmt} }}" self.emit(stmt) elif m := re.match(r"^&PEEK\(.*\)$", dst.name): - # NOTE: MOVE_ITEMS() does not actually exist. - # The only supported output array forms are: - # - unused[...] - # - X[...] where X[...] matches an input array exactly - self.emit(f"MOVE_ITEMS({dst.name}, {src.name}, {src.size});") + # The user code is responsible for writing to the output array. + pass elif m := re.match(r"^REG\(oparg(\d+)\)$", dst.name): self.emit(f"Py_XSETREF({dst.name}, {cast}{src.name});") else: @@ -309,10 +306,24 @@ def write(self, out: Formatter) -> None: out.declare(ieffect, src) # Write output stack effect variable declarations + isize = string_effect_size(list_effect_size(self.input_effects)) input_names = {ieffect.name for ieffect in self.input_effects} - for oeffect in self.output_effects: + for i, oeffect in enumerate(self.output_effects): if oeffect.name not in input_names: - out.declare(oeffect, None) + if oeffect.size: + osize = string_effect_size( + list_effect_size([oeff for oeff in self.output_effects[:i]]) + ) + offset = "stack_pointer" + if isize != osize: + if isize != "0": + offset += f" - ({isize})" + if osize != "0": + offset += f" + {osize}" + src = StackEffect(offset, "PyObject **") + out.declare(oeffect, src) + else: + out.declare(oeffect, None) # out.emit(f"JUMPBY(OPSIZE({self.inst.name}) - 1);") diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index 9df97d24ab6f43..0c3d04b45dd959 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -424,20 +424,18 @@ def test_array_input(): def test_array_output(): input = """ - inst(OP, (-- below, values[oparg*3], above)) { - spam(); + inst(OP, (unused, unused -- below, values[oparg*3], above)) { + spam(values, oparg); } """ output = """ TARGET(OP) { PyObject *below; - PyObject **values; + PyObject **values = stack_pointer - (2) + 1; PyObject *above; - spam(); - STACK_GROW(2); + spam(values, oparg); STACK_GROW(oparg*3); POKE(1, above); - MOVE_ITEMS(&PEEK(1 + oparg*3), values, oparg*3); POKE(2 + oparg*3, below); DISPATCH(); } @@ -446,18 +444,17 @@ def test_array_output(): def test_array_input_output(): input = """ - inst(OP, (below, values[oparg] -- values[oparg], above)) { - spam(); + inst(OP, (values[oparg] -- values[oparg], above)) { + spam(values, oparg); } """ output = """ TARGET(OP) { PyObject **values = &PEEK(oparg); - PyObject *below = PEEK(1 + oparg); PyObject *above; - spam(); + spam(values, oparg); + STACK_GROW(1); POKE(1, above); - MOVE_ITEMS(&PEEK(1 + oparg), values, oparg); DISPATCH(); } """ From b2b85b5db9cfdb24f966b61757536a898abc3830 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 7 Feb 2023 17:35:55 -0800 Subject: [PATCH 116/225] gh-98831: Modernize FORMAT_VALUE (#101628) Generator update: support balanced parentheses and brackets in conditions and size expressions. --- Python/bytecodes.c | 18 +++--------------- Python/generated_cases.c.h | 20 +++++++------------- Python/opcode_metadata.h | 4 ++-- Tools/cases_generator/parser.py | 9 ++++++++- Tools/cases_generator/test_generator.py | 8 ++++---- 5 files changed, 24 insertions(+), 35 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index c6c00a7ab9b0cf..d0f0513a36f8d5 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -3054,18 +3054,10 @@ dummy_func( ERROR_IF(slice == NULL, error); } - // error: FORMAT_VALUE has irregular stack effect - inst(FORMAT_VALUE) { + inst(FORMAT_VALUE, (value, fmt_spec if ((oparg & FVS_MASK) == FVS_HAVE_SPEC) -- result)) { /* Handles f-string value formatting. */ - PyObject *result; - PyObject *fmt_spec; - PyObject *value; PyObject *(*conv_fn)(PyObject *); int which_conversion = oparg & FVC_MASK; - int have_fmt_spec = (oparg & FVS_MASK) == FVS_HAVE_SPEC; - - fmt_spec = have_fmt_spec ? POP() : NULL; - value = POP(); /* See if any conversion is specified. */ switch (which_conversion) { @@ -3088,7 +3080,7 @@ dummy_func( Py_DECREF(value); if (result == NULL) { Py_XDECREF(fmt_spec); - goto error; + ERROR_IF(true, error); } value = result; } @@ -3106,12 +3098,8 @@ dummy_func( result = PyObject_Format(value, fmt_spec); Py_DECREF(value); Py_XDECREF(fmt_spec); - if (result == NULL) { - goto error; - } + ERROR_IF(result == NULL, error); } - - PUSH(result); } inst(COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index ded68d011c6ba1..3ef808691e0171 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3703,16 +3703,12 @@ } TARGET(FORMAT_VALUE) { - /* Handles f-string value formatting. */ + PyObject *fmt_spec = ((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? PEEK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)) : NULL; + PyObject *value = PEEK(1 + (((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)); PyObject *result; - PyObject *fmt_spec; - PyObject *value; + /* Handles f-string value formatting. */ PyObject *(*conv_fn)(PyObject *); int which_conversion = oparg & FVC_MASK; - int have_fmt_spec = (oparg & FVS_MASK) == FVS_HAVE_SPEC; - - fmt_spec = have_fmt_spec ? POP() : NULL; - value = POP(); /* See if any conversion is specified. */ switch (which_conversion) { @@ -3735,7 +3731,7 @@ Py_DECREF(value); if (result == NULL) { Py_XDECREF(fmt_spec); - goto error; + if (true) { STACK_SHRINK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)); goto pop_1_error; } } value = result; } @@ -3753,12 +3749,10 @@ result = PyObject_Format(value, fmt_spec); Py_DECREF(value); Py_XDECREF(fmt_spec); - if (result == NULL) { - goto error; - } + if (result == NULL) { STACK_SHRINK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)); goto pop_1_error; } } - - PUSH(result); + STACK_SHRINK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)); + POKE(1, result); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index c1e12a4bbede89..52bab1c680e3e4 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -333,7 +333,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case BUILD_SLICE: return ((oparg == 3) ? 1 : 0) + 2; case FORMAT_VALUE: - return -1; + return (((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0) + 1; case COPY: return (oparg-1) + 1; case BINARY_OP: @@ -681,7 +681,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case BUILD_SLICE: return 1; case FORMAT_VALUE: - return -1; + return 1; case COPY: return (oparg-1) + 2; case BINARY_OP: diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py index ced66faee4931f..c7c8d8af6b7318 100644 --- a/Tools/cases_generator/parser.py +++ b/Tools/cases_generator/parser.py @@ -263,7 +263,14 @@ def stack_effect(self) -> StackEffect | None: @contextual def expression(self) -> Expression | None: tokens: list[lx.Token] = [] - while (tkn := self.peek()) and tkn.kind not in (lx.RBRACKET, lx.RPAREN): + level = 1 + while tkn := self.peek(): + if tkn.kind in (lx.LBRACKET, lx.LPAREN): + level += 1 + elif tkn.kind in (lx.RBRACKET, lx.RPAREN): + level -= 1 + if level == 0: + break tokens.append(tkn) self.next() if not tokens: diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index 0c3d04b45dd959..33bba7ee340a49 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -500,20 +500,20 @@ def test_register(): def test_cond_effect(): input = """ - inst(OP, (aa, input if (oparg & 1), cc -- xx, output if (oparg & 2), zz)) { + inst(OP, (aa, input if ((oparg & 1) == 1), cc -- xx, output if (oparg & 2), zz)) { output = spam(oparg, input); } """ output = """ TARGET(OP) { PyObject *cc = PEEK(1); - PyObject *input = (oparg & 1) ? PEEK(1 + ((oparg & 1) ? 1 : 0)) : NULL; - PyObject *aa = PEEK(2 + ((oparg & 1) ? 1 : 0)); + PyObject *input = ((oparg & 1) == 1) ? PEEK(1 + (((oparg & 1) == 1) ? 1 : 0)) : NULL; + PyObject *aa = PEEK(2 + (((oparg & 1) == 1) ? 1 : 0)); PyObject *xx; PyObject *output = NULL; PyObject *zz; output = spam(oparg, input); - STACK_SHRINK(((oparg & 1) ? 1 : 0)); + STACK_SHRINK((((oparg & 1) == 1) ? 1 : 0)); STACK_GROW(((oparg & 2) ? 1 : 0)); POKE(1, zz); if (oparg & 2) { POKE(1 + ((oparg & 2) ? 1 : 0), output); } From 790ff6bc6a56b4bd6e403aa43a984b99f7171dd7 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 8 Feb 2023 05:01:10 +0300 Subject: [PATCH 117/225] gh-101446: Change `repr` of `collections.OrderedDict` (#101661) --- Lib/collections/__init__.py | 2 +- Lib/test/test_ordered_dict.py | 4 +- ...-02-07-22-21-46.gh-issue-101446.-c0FdK.rst | 2 + Objects/odictobject.c | 53 +++---------------- 4 files changed, 11 insertions(+), 50 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-07-22-21-46.gh-issue-101446.-c0FdK.rst diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index b5e4d16e9dbcad..a5393aad4249c0 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -267,7 +267,7 @@ def __repr__(self): 'od.__repr__() <==> repr(od)' if not self: return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, list(self.items())) + return '%s(%r)' % (self.__class__.__name__, dict(self.items())) def __reduce__(self): 'Return state information for pickling' diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py index 37447fd249b8c0..decbcc2419c9fc 100644 --- a/Lib/test/test_ordered_dict.py +++ b/Lib/test/test_ordered_dict.py @@ -362,7 +362,7 @@ def test_repr(self): OrderedDict = self.OrderedDict od = OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]) self.assertEqual(repr(od), - "OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])") + "OrderedDict({'c': 1, 'b': 2, 'a': 3, 'd': 4, 'e': 5, 'f': 6})") self.assertEqual(eval(repr(od)), od) self.assertEqual(repr(OrderedDict()), "OrderedDict()") @@ -372,7 +372,7 @@ def test_repr_recursive(self): od = OrderedDict.fromkeys('abc') od['x'] = od self.assertEqual(repr(od), - "OrderedDict([('a', None), ('b', None), ('c', None), ('x', ...)])") + "OrderedDict({'a': None, 'b': None, 'c': None, 'x': ...})") def test_repr_recursive_values(self): OrderedDict = self.OrderedDict diff --git a/Misc/NEWS.d/next/Library/2023-02-07-22-21-46.gh-issue-101446.-c0FdK.rst b/Misc/NEWS.d/next/Library/2023-02-07-22-21-46.gh-issue-101446.-c0FdK.rst new file mode 100644 index 00000000000000..ddf897b71bb1d1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-07-22-21-46.gh-issue-101446.-c0FdK.rst @@ -0,0 +1,2 @@ +Change repr of :class:`collections.OrderedDict` to use regular dictionary +formating instead of pairs of keys and values. diff --git a/Objects/odictobject.c b/Objects/odictobject.c index 4976b70b5dff5a..ab2bbed35873de 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -1367,7 +1367,7 @@ static PyObject * odict_repr(PyODictObject *self) { int i; - PyObject *pieces = NULL, *result = NULL; + PyObject *result = NULL, *dcopy = NULL; if (PyODict_SIZE(self) == 0) return PyUnicode_FromFormat("%s()", _PyType_Name(Py_TYPE(self))); @@ -1377,57 +1377,16 @@ odict_repr(PyODictObject *self) return i > 0 ? PyUnicode_FromString("...") : NULL; } - if (PyODict_CheckExact(self)) { - Py_ssize_t count = 0; - _ODictNode *node; - pieces = PyList_New(PyODict_SIZE(self)); - if (pieces == NULL) - goto Done; - - _odict_FOREACH(self, node) { - PyObject *pair; - PyObject *key = _odictnode_KEY(node); - PyObject *value = _odictnode_VALUE(node, self); - if (value == NULL) { - if (!PyErr_Occurred()) - PyErr_SetObject(PyExc_KeyError, key); - goto Done; - } - pair = PyTuple_Pack(2, key, value); - if (pair == NULL) - goto Done; - - if (count < PyList_GET_SIZE(pieces)) - PyList_SET_ITEM(pieces, count, pair); /* steals reference */ - else { - if (PyList_Append(pieces, pair) < 0) { - Py_DECREF(pair); - goto Done; - } - Py_DECREF(pair); - } - count++; - } - if (count < PyList_GET_SIZE(pieces)) { - Py_SET_SIZE(pieces, count); - } - } - else { - PyObject *items = PyObject_CallMethodNoArgs( - (PyObject *)self, &_Py_ID(items)); - if (items == NULL) - goto Done; - pieces = PySequence_List(items); - Py_DECREF(items); - if (pieces == NULL) - goto Done; + dcopy = PyDict_Copy((PyObject *)self); + if (dcopy == NULL) { + goto Done; } result = PyUnicode_FromFormat("%s(%R)", - _PyType_Name(Py_TYPE(self)), pieces); + _PyType_Name(Py_TYPE(self)), + dcopy); Done: - Py_XDECREF(pieces); Py_ReprLeave((PyObject *)self); return result; } From a9f01448a99c6a2ae34d448806176f2df3a5b323 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 7 Feb 2023 20:03:22 -0800 Subject: [PATCH 118/225] gh-98831: Modernize CALL_FUNCTION_EX (#101627) New generator feature: Move CHECK_EVAL_BREAKER() call to just before DISPATCH(). --- Python/bytecodes.c | 23 +++++++-------------- Python/generated_cases.c.h | 27 ++++++++++++------------- Python/opcode_metadata.h | 4 ++-- Tools/cases_generator/generate_cases.py | 15 +++++++++++--- Tools/cases_generator/test_generator.py | 9 +++++++-- 5 files changed, 41 insertions(+), 37 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index d0f0513a36f8d5..9633f34212a68d 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2951,26 +2951,21 @@ dummy_func( CHECK_EVAL_BREAKER(); } - // error: CALL_FUNCTION_EX has irregular stack effect - inst(CALL_FUNCTION_EX) { - PyObject *func, *callargs, *kwargs = NULL, *result; - if (oparg & 0x01) { - kwargs = POP(); + inst(CALL_FUNCTION_EX, (unused, func, callargs, kwargs if (oparg & 1) -- result)) { + if (oparg & 1) { // DICT_MERGE is called before this opcode if there are kwargs. // It converts all dict subtypes in kwargs into regular dicts. assert(PyDict_CheckExact(kwargs)); } - callargs = POP(); - func = TOP(); if (!PyTuple_CheckExact(callargs)) { if (check_args_iterable(tstate, func, callargs) < 0) { - Py_DECREF(callargs); goto error; } - Py_SETREF(callargs, PySequence_Tuple(callargs)); - if (callargs == NULL) { + PyObject *tuple = PySequence_Tuple(callargs); + if (tuple == NULL) { goto error; } + Py_SETREF(callargs, tuple); } assert(PyTuple_CheckExact(callargs)); @@ -2979,12 +2974,8 @@ dummy_func( Py_DECREF(callargs); Py_XDECREF(kwargs); - STACK_SHRINK(1); - assert(TOP() == NULL); - SET_TOP(result); - if (result == NULL) { - goto error; - } + assert(PEEK(3 + (oparg & 1)) == NULL); + ERROR_IF(result == NULL, error); CHECK_EVAL_BREAKER(); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 3ef808691e0171..f38286441be4b3 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3587,24 +3587,24 @@ TARGET(CALL_FUNCTION_EX) { PREDICTED(CALL_FUNCTION_EX); - PyObject *func, *callargs, *kwargs = NULL, *result; - if (oparg & 0x01) { - kwargs = POP(); + PyObject *kwargs = (oparg & 1) ? PEEK(((oparg & 1) ? 1 : 0)) : NULL; + PyObject *callargs = PEEK(1 + ((oparg & 1) ? 1 : 0)); + PyObject *func = PEEK(2 + ((oparg & 1) ? 1 : 0)); + PyObject *result; + if (oparg & 1) { // DICT_MERGE is called before this opcode if there are kwargs. // It converts all dict subtypes in kwargs into regular dicts. assert(PyDict_CheckExact(kwargs)); } - callargs = POP(); - func = TOP(); if (!PyTuple_CheckExact(callargs)) { if (check_args_iterable(tstate, func, callargs) < 0) { - Py_DECREF(callargs); goto error; } - Py_SETREF(callargs, PySequence_Tuple(callargs)); - if (callargs == NULL) { + PyObject *tuple = PySequence_Tuple(callargs); + if (tuple == NULL) { goto error; } + Py_SETREF(callargs, tuple); } assert(PyTuple_CheckExact(callargs)); @@ -3613,12 +3613,11 @@ Py_DECREF(callargs); Py_XDECREF(kwargs); - STACK_SHRINK(1); - assert(TOP() == NULL); - SET_TOP(result); - if (result == NULL) { - goto error; - } + assert(PEEK(3 + (oparg & 1)) == NULL); + if (result == NULL) { STACK_SHRINK(((oparg & 1) ? 1 : 0)); goto pop_3_error; } + STACK_SHRINK(((oparg & 1) ? 1 : 0)); + STACK_SHRINK(2); + POKE(1, result); CHECK_EVAL_BREAKER(); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 52bab1c680e3e4..054ef6c2998234 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -325,7 +325,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case CALL_NO_KW_METHOD_DESCRIPTOR_FAST: return -1; case CALL_FUNCTION_EX: - return -1; + return ((oparg & 1) ? 1 : 0) + 3; case MAKE_FUNCTION: return ((oparg & 0x01) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x08) ? 1 : 0) + 1; case RETURN_GENERATOR: @@ -673,7 +673,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case CALL_NO_KW_METHOD_DESCRIPTOR_FAST: return -1; case CALL_FUNCTION_EX: - return -1; + return 1; case MAKE_FUNCTION: return 1; case RETURN_GENERATOR: diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 4f94b48d114de8..9b5aa914cdee86 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -227,7 +227,8 @@ def __init__(self, inst: parser.InstDef): self.kind = inst.kind self.name = inst.name self.block = inst.block - self.block_text, self.predictions = extract_block_text(self.block) + self.block_text, self.check_eval_breaker, self.predictions = \ + extract_block_text(self.block) self.always_exits = always_exits(self.block_text) self.cache_effects = [ effect for effect in inst.inputs if isinstance(effect, parser.CacheEffect) @@ -1027,6 +1028,8 @@ def write_instr(self, instr: Instruction) -> None: if not instr.always_exits: for prediction in instr.predictions: self.out.emit(f"PREDICT({prediction});") + if instr.check_eval_breaker: + self.out.emit("CHECK_EVAL_BREAKER();") self.out.emit(f"DISPATCH();") def write_super(self, sup: SuperInstruction) -> None: @@ -1102,7 +1105,7 @@ def wrap_super_or_macro(self, up: SuperOrMacroInstruction): self.out.emit(f"DISPATCH();") -def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]: +def extract_block_text(block: parser.Block) -> tuple[list[str], bool, list[str]]: # Get lines of text with proper dedent blocklines = block.text.splitlines(True) @@ -1122,6 +1125,12 @@ def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]: while blocklines and not blocklines[-1].strip(): blocklines.pop() + # Separate CHECK_EVAL_BREAKER() macro from end + check_eval_breaker = \ + blocklines != [] and blocklines[-1].strip() == "CHECK_EVAL_BREAKER();" + if check_eval_breaker: + del blocklines[-1] + # Separate PREDICT(...) macros from end predictions: list[str] = [] while blocklines and ( @@ -1130,7 +1139,7 @@ def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]: predictions.insert(0, m.group(1)) blocklines.pop() - return blocklines, predictions + return blocklines, check_eval_breaker, predictions def always_exits(lines: list[str]) -> bool: diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index 33bba7ee340a49..036094ac8ef487 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -177,15 +177,16 @@ def test_overlap(): """ run_cases_test(input, output) -def test_predictions(): +def test_predictions_and_eval_breaker(): input = """ inst(OP1, (--)) { } inst(OP2, (--)) { } - inst(OP3, (--)) { + inst(OP3, (arg -- res)) { DEOPT_IF(xxx, OP1); PREDICT(OP2); + CHECK_EVAL_BREAKER(); } """ output = """ @@ -200,8 +201,12 @@ def test_predictions(): } TARGET(OP3) { + PyObject *arg = PEEK(1); + PyObject *res; DEOPT_IF(xxx, OP1); + POKE(1, res); PREDICT(OP2); + CHECK_EVAL_BREAKER(); DISPATCH(); } """ From 027adf42cd85db41fee05b0a40d89ef822876c97 Mon Sep 17 00:00:00 2001 From: Stanley <46876382+slateny@users.noreply.github.com> Date: Wed, 8 Feb 2023 00:12:46 -0800 Subject: [PATCH 119/225] gh-47937: Note that Popen attributes are read-only (#93070) * Note that Popen attributes aren't meant to be set by users by rewording the text about the attributes. * Also update some universal_newlines references to mention the modern text parameter name while in the area. Co-authored-by: Gregory P. Smith --- Doc/library/subprocess.rst | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index 785251afdf262e..a87369a2461a54 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -457,7 +457,7 @@ functions. - :const:`0` means unbuffered (read and write are one system call and can return short) - :const:`1` means line buffered - (only usable if ``universal_newlines=True`` i.e., in a text mode) + (only usable if ``text=True`` or ``universal_newlines=True``) - any other positive value means use a buffer of approximately that size - negative bufsize (the default) means the system default of @@ -847,7 +847,8 @@ Instances of the :class:`Popen` class have the following methods: On Windows :meth:`kill` is an alias for :meth:`terminate`. -The following attributes are also available: +The following attributes are also set by the class for you to access. +Reassigning them to new values is unsupported: .. attribute:: Popen.args @@ -860,9 +861,9 @@ The following attributes are also available: If the *stdin* argument was :data:`PIPE`, this attribute is a writeable stream object as returned by :func:`open`. If the *encoding* or *errors* - arguments were specified or the *universal_newlines* argument was ``True``, - the stream is a text stream, otherwise it is a byte stream. If the *stdin* - argument was not :data:`PIPE`, this attribute is ``None``. + arguments were specified or the *text* or *universal_newlines* argument + was ``True``, the stream is a text stream, otherwise it is a byte stream. + If the *stdin* argument was not :data:`PIPE`, this attribute is ``None``. .. attribute:: Popen.stdout @@ -870,9 +871,9 @@ The following attributes are also available: If the *stdout* argument was :data:`PIPE`, this attribute is a readable stream object as returned by :func:`open`. Reading from the stream provides output from the child process. If the *encoding* or *errors* arguments were - specified or the *universal_newlines* argument was ``True``, the stream is a - text stream, otherwise it is a byte stream. If the *stdout* argument was not - :data:`PIPE`, this attribute is ``None``. + specified or the *text* or *universal_newlines* argument was ``True``, the + stream is a text stream, otherwise it is a byte stream. If the *stdout* + argument was not :data:`PIPE`, this attribute is ``None``. .. attribute:: Popen.stderr @@ -880,9 +881,9 @@ The following attributes are also available: If the *stderr* argument was :data:`PIPE`, this attribute is a readable stream object as returned by :func:`open`. Reading from the stream provides error output from the child process. If the *encoding* or *errors* arguments - were specified or the *universal_newlines* argument was ``True``, the stream - is a text stream, otherwise it is a byte stream. If the *stderr* argument was - not :data:`PIPE`, this attribute is ``None``. + were specified or the *text* or *universal_newlines* argument was ``True``, the + stream is a text stream, otherwise it is a byte stream. If the *stderr* argument + was not :data:`PIPE`, this attribute is ``None``. .. warning:: From feec49c40736fc05626a183a8d14c4ebbea5ae28 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 8 Feb 2023 09:31:12 +0000 Subject: [PATCH 120/225] GH-101578: Normalize the current exception (GH-101607) * Make sure that the current exception is always normalized. * Remove redundant type and traceback fields for the current exception. * Add new API functions: PyErr_GetRaisedException, PyErr_SetRaisedException * Add new API functions: PyException_GetArgs, PyException_SetArgs --- Doc/c-api/exceptions.rst | 79 +++++- Doc/data/stable_abi.dat | 4 + Include/cpython/pyerrors.h | 1 + Include/cpython/pystate.h | 4 +- Include/internal/pycore_pyerrors.h | 11 +- Include/pyerrors.h | 6 + Lib/test/test_capi/test_misc.py | 39 +++ Lib/test/test_exceptions.py | 8 +- Lib/test/test_stable_abi_ctypes.py | 4 + ...-02-06-16-14-30.gh-issue-101578.PW5fA9.rst | 13 + Misc/stable_abi.toml | 9 + Modules/_testcapi/heaptype.c | 24 +- Modules/_testcapimodule.c | 37 +++ Modules/gcmodule.c | 5 +- Objects/dictobject.c | 6 +- Objects/exceptions.c | 75 +++++- Objects/object.c | 12 +- PC/python3dll.c | 4 + Parser/pegen.c | 15 +- Python/bytecodes.c | 4 +- Python/ceval.c | 12 +- Python/errors.c | 225 +++++++++++------- Python/generated_cases.c.h | 4 +- Python/import.c | 7 + Python/initconfig.c | 5 +- Python/pystate.c | 4 +- Python/pythonrun.c | 11 +- Python/sysmodule.c | 10 +- Python/traceback.c | 9 +- 29 files changed, 476 insertions(+), 171 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2023-02-06-16-14-30.gh-issue-101578.PW5fA9.rst diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 087e0a61d12d59..de9b15edd6859a 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -400,8 +400,61 @@ Querying the error indicator recursively in subtuples) are searched for a match. +.. c:function:: PyObject *PyErr_GetRaisedException(void) + + Returns the exception currently being raised, clearing the exception at + the same time. Do not confuse this with the exception currently being + handled which can be accessed with :c:func:`PyErr_GetHandledException`. + + .. note:: + + This function is normally only used by code that needs to catch exceptions or + by code that needs to save and restore the error indicator temporarily, e.g.:: + + { + PyObject *exc = PyErr_GetRaisedException(); + + /* ... code that might produce other errors ... */ + + PyErr_SetRaisedException(exc); + } + + .. versionadded:: 3.12 + + +.. c:function:: void PyErr_SetRaisedException(PyObject *exc) + + Sets the exception currently being raised ``exc``. + If the exception is already set, it is cleared first. + + ``exc`` must be a valid exception. + (Violating this rules will cause subtle problems later.) + This call consumes a reference to the ``exc`` object: you must own a + reference to that object before the call and after the call you no longer own + that reference. + (If you don't understand this, don't use this function. I warned you.) + + .. note:: + + This function is normally only used by code that needs to save and restore the + error indicator temporarily. Use :c:func:`PyErr_GetRaisedException` to save + the current exception, e.g.:: + + { + PyObject *exc = PyErr_GetRaisedException(); + + /* ... code that might produce other errors ... */ + + PyErr_SetRaisedException(exc); + } + + .. versionadded:: 3.12 + + .. c:function:: void PyErr_Fetch(PyObject **ptype, PyObject **pvalue, PyObject **ptraceback) + As of 3.12, this function is deprecated. Use :c:func:`PyErr_GetRaisedException` instead. + Retrieve the error indicator into three variables whose addresses are passed. If the error indicator is not set, set all three variables to ``NULL``. If it is set, it will be cleared and you own a reference to each object retrieved. The @@ -421,10 +474,14 @@ Querying the error indicator PyErr_Restore(type, value, traceback); } + .. deprecated:: 3.12 + .. c:function:: void PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback) - Set the error indicator from the three objects. If the error indicator is + As of 3.12, this function is deprecated. Use :c:func:`PyErr_SetRaisedException` instead. + + Set the error indicator from the three objects. If the error indicator is already set, it is cleared first. If the objects are ``NULL``, the error indicator is cleared. Do not pass a ``NULL`` type and non-``NULL`` value or traceback. The exception type should be a class. Do not pass an invalid @@ -440,9 +497,15 @@ Querying the error indicator error indicator temporarily. Use :c:func:`PyErr_Fetch` to save the current error indicator. + .. deprecated:: 3.12 + .. c:function:: void PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) + As of 3.12, this function is deprecated. + Use :c:func:`PyErr_GetRaisedException` instead of :c:func:`PyErr_Fetch` to avoid + any possible de-normalization. + Under certain circumstances, the values returned by :c:func:`PyErr_Fetch` below can be "unnormalized", meaning that ``*exc`` is a class object but ``*val`` is not an instance of the same class. This function can be used to instantiate @@ -459,6 +522,8 @@ Querying the error indicator PyException_SetTraceback(val, tb); } + .. deprecated:: 3.12 + .. c:function:: PyObject* PyErr_GetHandledException(void) @@ -704,6 +769,18 @@ Exception Objects :attr:`__suppress_context__` is implicitly set to ``True`` by this function. +.. c:function:: PyObject* PyException_GetArgs(PyObject *ex) + + Return args of the given exception as a new reference, + as accessible from Python through :attr:`args`. + + +.. c:function:: void PyException_SetArgs(PyObject *ex, PyObject *args) + + Set the args of the given exception, + as accessible from Python through :attr:`args`. + + .. _unicodeexceptions: Unicode Exception Objects diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 53895bbced8408..68ab0b5061f434 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -139,6 +139,7 @@ function,PyErr_Format,3.2,, function,PyErr_FormatV,3.5,, function,PyErr_GetExcInfo,3.7,, function,PyErr_GetHandledException,3.11,, +function,PyErr_GetRaisedException,3.12,, function,PyErr_GivenExceptionMatches,3.2,, function,PyErr_NewException,3.2,, function,PyErr_NewExceptionWithDoc,3.2,, @@ -168,6 +169,7 @@ function,PyErr_SetInterrupt,3.2,, function,PyErr_SetInterruptEx,3.10,, function,PyErr_SetNone,3.2,, function,PyErr_SetObject,3.2,, +function,PyErr_SetRaisedException,3.12,, function,PyErr_SetString,3.2,, function,PyErr_SyntaxLocation,3.2,, function,PyErr_SyntaxLocationEx,3.7,, @@ -266,9 +268,11 @@ var,PyExc_Warning,3.2,, var,PyExc_WindowsError,3.7,on Windows, var,PyExc_ZeroDivisionError,3.2,, function,PyExceptionClass_Name,3.8,, +function,PyException_GetArgs,3.12,, function,PyException_GetCause,3.2,, function,PyException_GetContext,3.2,, function,PyException_GetTraceback,3.2,, +function,PyException_SetArgs,3.12,, function,PyException_SetCause,3.2,, function,PyException_SetContext,3.2,, function,PyException_SetTraceback,3.2,, diff --git a/Include/cpython/pyerrors.h b/Include/cpython/pyerrors.h index 141341667795e8..0d9cc9922f7368 100644 --- a/Include/cpython/pyerrors.h +++ b/Include/cpython/pyerrors.h @@ -99,6 +99,7 @@ PyAPI_FUNC(void) _PyErr_GetExcInfo(PyThreadState *, PyObject **, PyObject **, Py /* Context manipulation (PEP 3134) */ PyAPI_FUNC(void) _PyErr_ChainExceptions(PyObject *, PyObject *, PyObject *); +PyAPI_FUNC(void) _PyErr_ChainExceptions1(PyObject *); /* Like PyErr_Format(), but saves current exception as __context__ and __cause__. diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index f5db52f76e8f4f..be1fcb61fa2244 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -166,9 +166,7 @@ struct _ts { PyObject *c_traceobj; /* The exception currently being raised */ - PyObject *curexc_type; - PyObject *curexc_value; - PyObject *curexc_traceback; + PyObject *current_exception; /* Pointer to the top of the exception stack for the exceptions * we may be currently handling. (See _PyErr_StackItem above.) diff --git a/Include/internal/pycore_pyerrors.h b/Include/internal/pycore_pyerrors.h index 66f37942ef916a..1bb4a9aa103898 100644 --- a/Include/internal/pycore_pyerrors.h +++ b/Include/internal/pycore_pyerrors.h @@ -20,7 +20,10 @@ extern void _PyErr_FiniTypes(PyInterpreterState *); static inline PyObject* _PyErr_Occurred(PyThreadState *tstate) { assert(tstate != NULL); - return tstate->curexc_type; + if (tstate->current_exception == NULL) { + return NULL; + } + return (PyObject *)Py_TYPE(tstate->current_exception); } static inline void _PyErr_ClearExcState(_PyErr_StackItem *exc_state) @@ -37,10 +40,16 @@ PyAPI_FUNC(void) _PyErr_Fetch( PyObject **value, PyObject **traceback); +extern PyObject * +_PyErr_GetRaisedException(PyThreadState *tstate); + PyAPI_FUNC(int) _PyErr_ExceptionMatches( PyThreadState *tstate, PyObject *exc); +void +_PyErr_SetRaisedException(PyThreadState *tstate, PyObject *exc); + PyAPI_FUNC(void) _PyErr_Restore( PyThreadState *tstate, PyObject *type, diff --git a/Include/pyerrors.h b/Include/pyerrors.h index d5ac6af5b32c6c..d089fa71779330 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -18,6 +18,8 @@ PyAPI_FUNC(PyObject *) PyErr_Occurred(void); PyAPI_FUNC(void) PyErr_Clear(void); PyAPI_FUNC(void) PyErr_Fetch(PyObject **, PyObject **, PyObject **); PyAPI_FUNC(void) PyErr_Restore(PyObject *, PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyErr_GetRaisedException(void); +PyAPI_FUNC(void) PyErr_SetRaisedException(PyObject *); #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030b0000 PyAPI_FUNC(PyObject*) PyErr_GetHandledException(void); PyAPI_FUNC(void) PyErr_SetHandledException(PyObject *); @@ -51,6 +53,10 @@ PyAPI_FUNC(void) PyException_SetCause(PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyException_GetContext(PyObject *); PyAPI_FUNC(void) PyException_SetContext(PyObject *, PyObject *); + +PyAPI_FUNC(PyObject *) PyException_GetArgs(PyObject *); +PyAPI_FUNC(void) PyException_SetArgs(PyObject *, PyObject *); + /* */ #define PyExceptionClass_Check(x) \ diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 03e22d7a2d382d..7612cddb1f6576 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -1553,5 +1553,44 @@ def func2(x=None): self.do_test(func2) +class Test_ErrSetAndRestore(unittest.TestCase): + + def test_err_set_raised(self): + with self.assertRaises(ValueError): + _testcapi.err_set_raised(ValueError()) + v = ValueError() + try: + _testcapi.err_set_raised(v) + except ValueError as ex: + self.assertIs(v, ex) + + def test_err_restore(self): + with self.assertRaises(ValueError): + _testcapi.err_restore(ValueError) + with self.assertRaises(ValueError): + _testcapi.err_restore(ValueError, 1) + with self.assertRaises(ValueError): + _testcapi.err_restore(ValueError, 1, None) + with self.assertRaises(ValueError): + _testcapi.err_restore(ValueError, ValueError()) + try: + _testcapi.err_restore(KeyError, "hi") + except KeyError as k: + self.assertEqual("hi", k.args[0]) + try: + 1/0 + except Exception as e: + tb = e.__traceback__ + with self.assertRaises(ValueError): + _testcapi.err_restore(ValueError, 1, tb) + with self.assertRaises(TypeError): + _testcapi.err_restore(ValueError, 1, 0) + try: + _testcapi.err_restore(ValueError, 1, tb) + except ValueError as v: + self.assertEqual(1, v.args[0]) + self.assertIs(tb, v.__traceback__.tb_next) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index f629321458d8ae..4ae71e431c56dc 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -347,6 +347,7 @@ def test_capi2(): _testcapi.raise_exception(BadException, 0) except RuntimeError as err: exc, err, tb = sys.exc_info() + tb = tb.tb_next co = tb.tb_frame.f_code self.assertEqual(co.co_name, "__init__") self.assertTrue(co.co_filename.endswith('test_exceptions.py')) @@ -1415,8 +1416,8 @@ def gen(): @cpython_only def test_recursion_normalizing_infinite_exception(self): # Issue #30697. Test that a RecursionError is raised when - # PyErr_NormalizeException() maximum recursion depth has been - # exceeded. + # maximum recursion depth has been exceeded when creating + # an exception code = """if 1: import _testcapi try: @@ -1426,8 +1427,7 @@ def test_recursion_normalizing_infinite_exception(self): """ rc, out, err = script_helper.assert_python_failure("-c", code) self.assertEqual(rc, 1) - self.assertIn(b'RecursionError: maximum recursion depth exceeded ' - b'while normalizing an exception', err) + self.assertIn(b'RecursionError: maximum recursion depth exceeded', err) self.assertIn(b'Done.', out) diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index 67c653428a6dee..e77c1c8409880d 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -172,6 +172,7 @@ def test_windows_feature_macros(self): "PyErr_FormatV", "PyErr_GetExcInfo", "PyErr_GetHandledException", + "PyErr_GetRaisedException", "PyErr_GivenExceptionMatches", "PyErr_NewException", "PyErr_NewExceptionWithDoc", @@ -195,6 +196,7 @@ def test_windows_feature_macros(self): "PyErr_SetInterruptEx", "PyErr_SetNone", "PyErr_SetObject", + "PyErr_SetRaisedException", "PyErr_SetString", "PyErr_SyntaxLocation", "PyErr_SyntaxLocationEx", @@ -292,9 +294,11 @@ def test_windows_feature_macros(self): "PyExc_Warning", "PyExc_ZeroDivisionError", "PyExceptionClass_Name", + "PyException_GetArgs", "PyException_GetCause", "PyException_GetContext", "PyException_GetTraceback", + "PyException_SetArgs", "PyException_SetCause", "PyException_SetContext", "PyException_SetTraceback", diff --git a/Misc/NEWS.d/next/C API/2023-02-06-16-14-30.gh-issue-101578.PW5fA9.rst b/Misc/NEWS.d/next/C API/2023-02-06-16-14-30.gh-issue-101578.PW5fA9.rst new file mode 100644 index 00000000000000..fc694f6e051b53 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-02-06-16-14-30.gh-issue-101578.PW5fA9.rst @@ -0,0 +1,13 @@ +Add new C-API functions for saving and restoring the current exception: +``PyErr_GetRaisedException`` and ``PyErr_SetRaisedException``. +These functions take and return a single exception rather than +the triple of ``PyErr_Fetch`` and ``PyErr_Restore``. +This is less error prone and a bit more efficient. + +The three arguments forms of saving and restoring the +current exception: ``PyErr_Fetch`` and ``PyErr_Restore`` +are deprecated. + +Also add ``PyException_GetArgs`` and ``PyException_SetArgs`` +as convenience functions to help dealing with +exceptions in the C API. diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index c716f403d638ac..21ff9616133445 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -2333,6 +2333,15 @@ added = '3.12' [function.PyVectorcall_Call] added = '3.12' +[function.PyErr_GetRaisedException] + added = '3.12' +[function.PyErr_SetRaisedException] + added = '3.12' +[function.PyException_GetArgs] + added = '3.12' +[function.PyException_SetArgs] + added = '3.12' + [typedef.vectorcallfunc] added = '3.12' [function.PyObject_Vectorcall] diff --git a/Modules/_testcapi/heaptype.c b/Modules/_testcapi/heaptype.c index bf80fd64d80b35..39639f7ed048f2 100644 --- a/Modules/_testcapi/heaptype.c +++ b/Modules/_testcapi/heaptype.c @@ -116,10 +116,10 @@ test_from_spec_invalid_metatype_inheritance(PyObject *self, PyObject *Py_UNUSED( PyObject *bases = NULL; PyObject *new = NULL; PyObject *meta_error_string = NULL; - PyObject *exc_type = NULL; - PyObject *exc_value = NULL; - PyObject *exc_traceback = NULL; + PyObject *exc = NULL; PyObject *result = NULL; + PyObject *message = NULL; + PyObject *args = NULL; metaclass_a = PyType_FromSpecWithBases(&MinimalMetaclass_spec, (PyObject*)&PyType_Type); if (metaclass_a == NULL) { @@ -156,13 +156,19 @@ test_from_spec_invalid_metatype_inheritance(PyObject *self, PyObject *Py_UNUSED( // Assert that the correct exception was raised if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); - + exc = PyErr_GetRaisedException(); + args = PyException_GetArgs(exc); + if (!PyTuple_Check(args) || PyTuple_Size(args) != 1) { + PyErr_SetString(PyExc_AssertionError, + "TypeError args are not a one-tuple"); + goto finally; + } + message = Py_NewRef(PyTuple_GET_ITEM(args, 0)); meta_error_string = PyUnicode_FromString("metaclass conflict:"); if (meta_error_string == NULL) { goto finally; } - int res = PyUnicode_Contains(exc_value, meta_error_string); + int res = PyUnicode_Contains(message, meta_error_string); if (res < 0) { goto finally; } @@ -179,11 +185,11 @@ test_from_spec_invalid_metatype_inheritance(PyObject *self, PyObject *Py_UNUSED( Py_XDECREF(bases); Py_XDECREF(new); Py_XDECREF(meta_error_string); - Py_XDECREF(exc_type); - Py_XDECREF(exc_value); - Py_XDECREF(exc_traceback); + Py_XDECREF(exc); + Py_XDECREF(message); Py_XDECREF(class_a); Py_XDECREF(class_b); + Py_XDECREF(args); return result; } diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 8b2ce1a2cfd4bd..3c411fa0d76358 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3470,6 +3470,41 @@ function_set_kw_defaults(PyObject *self, PyObject *args) Py_RETURN_NONE; } +static PyObject * +err_set_raised(PyObject *self, PyObject *exc) +{ + Py_INCREF(exc); + PyErr_SetRaisedException(exc); + assert(PyErr_Occurred()); + return NULL; +} + +static PyObject * +err_restore(PyObject *self, PyObject *args) { + PyObject *type = NULL, *value = NULL, *traceback = NULL; + switch(PyTuple_Size(args)) { + case 3: + traceback = PyTuple_GetItem(args, 2); + Py_INCREF(traceback); + /* fall through */ + case 2: + value = PyTuple_GetItem(args, 1); + Py_INCREF(value); + /* fall through */ + case 1: + type = PyTuple_GetItem(args, 0); + Py_INCREF(type); + break; + default: + PyErr_SetString(PyExc_TypeError, + "wrong number of arguments"); + return NULL; + } + PyErr_Restore(type, value, traceback); + assert(PyErr_Occurred()); + return NULL; +} + static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *); static PyMethodDef TestMethods[] = { @@ -3622,6 +3657,8 @@ static PyMethodDef TestMethods[] = { {"function_set_defaults", function_set_defaults, METH_VARARGS, NULL}, {"function_get_kw_defaults", function_get_kw_defaults, METH_O, NULL}, {"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL}, + {"err_set_raised", err_set_raised, METH_O, NULL}, + {"err_restore", err_restore, METH_VARARGS, NULL}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 6630faa6f4471d..5879c5e11fe14a 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -2082,11 +2082,10 @@ PyGC_Collect(void) n = 0; } else { - PyObject *exc, *value, *tb; gcstate->collecting = 1; - _PyErr_Fetch(tstate, &exc, &value, &tb); + PyObject *exc = _PyErr_GetRaisedException(tstate); n = gc_collect_with_callback(tstate, NUM_GENERATIONS - 1); - _PyErr_Restore(tstate, exc, value, tb); + _PyErr_SetRaisedException(tstate, exc); gcstate->collecting = 0; } diff --git a/Objects/dictobject.c b/Objects/dictobject.c index b9067213820b52..fc658ca2f4b7f8 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1663,15 +1663,15 @@ PyDict_GetItem(PyObject *op, PyObject *key) #endif /* Preserve the existing exception */ - PyObject *exc_type, *exc_value, *exc_tb; PyObject *value; Py_ssize_t ix; (void)ix; - _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb); + + PyObject *exc = _PyErr_GetRaisedException(tstate); ix = _Py_dict_lookup(mp, key, hash, &value); /* Ignore any exception raised by the lookup */ - _PyErr_Restore(tstate, exc_type, exc_value, exc_tb); + _PyErr_SetRaisedException(tstate, exc); assert(ix >= 0 || value == NULL); diff --git a/Objects/exceptions.c b/Objects/exceptions.c index db6f7d52804d6a..976f84dbf63c93 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -8,6 +8,7 @@ #include #include #include "pycore_ceval.h" // _Py_EnterRecursiveCall +#include "pycore_pyerrors.h" // struct _PyErr_SetRaisedException #include "pycore_exceptions.h" // struct _Py_exc_state #include "pycore_initconfig.h" #include "pycore_object.h" @@ -288,13 +289,17 @@ BaseException_set_tb(PyBaseExceptionObject *self, PyObject *tb, void *Py_UNUSED( PyErr_SetString(PyExc_TypeError, "__traceback__ may not be deleted"); return -1; } - else if (!(tb == Py_None || PyTraceBack_Check(tb))) { + if (PyTraceBack_Check(tb)) { + Py_XSETREF(self->traceback, Py_NewRef(tb)); + } + else if (tb == Py_None) { + Py_CLEAR(self->traceback); + } + else { PyErr_SetString(PyExc_TypeError, "__traceback__ must be a traceback or None"); return -1; } - - Py_XSETREF(self->traceback, Py_NewRef(tb)); return 0; } @@ -413,6 +418,20 @@ PyException_SetContext(PyObject *self, PyObject *context) Py_XSETREF(_PyBaseExceptionObject_cast(self)->context, context); } +PyObject * +PyException_GetArgs(PyObject *self) +{ + PyObject *args = _PyBaseExceptionObject_cast(self)->args; + return Py_NewRef(args); +} + +void +PyException_SetArgs(PyObject *self, PyObject *args) +{ + Py_INCREF(args); + Py_XSETREF(_PyBaseExceptionObject_cast(self)->args, args); +} + const char * PyExceptionClass_Name(PyObject *ob) { @@ -3188,20 +3207,19 @@ SimpleExtendsException(PyExc_Exception, ReferenceError, #define MEMERRORS_SAVE 16 +static PyBaseExceptionObject last_resort_memory_error; + static PyObject * -MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +get_memory_error(int allow_allocation, PyObject *args, PyObject *kwds) { PyBaseExceptionObject *self; - - /* If this is a subclass of MemoryError, don't use the freelist - * and just return a fresh object */ - if (type != (PyTypeObject *) PyExc_MemoryError) { - return BaseException_new(type, args, kwds); - } - struct _Py_exc_state *state = get_exc_state(); if (state->memerrors_freelist == NULL) { - return BaseException_new(type, args, kwds); + if (!allow_allocation) { + return Py_NewRef(&last_resort_memory_error); + } + PyObject *result = BaseException_new((PyTypeObject *)PyExc_MemoryError, args, kwds); + return result; } /* Fetch object from freelist and revive it */ @@ -3221,6 +3239,35 @@ MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return (PyObject *)self; } +static PyBaseExceptionObject last_resort_memory_error; + +static PyObject * +MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + /* If this is a subclass of MemoryError, don't use the freelist + * and just return a fresh object */ + if (type != (PyTypeObject *) PyExc_MemoryError) { + return BaseException_new(type, args, kwds); + } + return get_memory_error(1, args, kwds); +} + +PyObject * +_PyErr_NoMemory(PyThreadState *tstate) +{ + if (Py_IS_TYPE(PyExc_MemoryError, NULL)) { + /* PyErr_NoMemory() has been called before PyExc_MemoryError has been + initialized by _PyExc_Init() */ + Py_FatalError("Out of memory and PyExc_MemoryError is not " + "initialized yet"); + } + PyObject *err = get_memory_error(0, NULL, NULL); + if (err != NULL) { + _PyErr_SetRaisedException(tstate, err); + } + return NULL; +} + static void MemoryError_dealloc(PyBaseExceptionObject *self) { @@ -3252,6 +3299,7 @@ preallocate_memerrors(void) /* We create enough MemoryErrors and then decref them, which will fill up the freelist. */ int i; + PyObject *errors[MEMERRORS_SAVE]; for (i = 0; i < MEMERRORS_SAVE; i++) { errors[i] = MemoryError_new((PyTypeObject *) PyExc_MemoryError, @@ -3291,6 +3339,9 @@ static PyTypeObject _PyExc_MemoryError = { }; PyObject *PyExc_MemoryError = (PyObject *) &_PyExc_MemoryError; +static PyBaseExceptionObject last_resort_memory_error = { + _PyObject_IMMORTAL_INIT(&_PyExc_MemoryError) +}; /* * BufferError extends Exception diff --git a/Objects/object.c b/Objects/object.c index e940222c657e3c..7817c04ef5f5be 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2416,10 +2416,10 @@ _Py_Dealloc(PyObject *op) destructor dealloc = type->tp_dealloc; #ifdef Py_DEBUG PyThreadState *tstate = _PyThreadState_GET(); - PyObject *old_exc_type = tstate != NULL ? tstate->curexc_type : NULL; + PyObject *old_exc = tstate != NULL ? tstate->current_exception : NULL; // Keep the old exception type alive to prevent undefined behavior // on (tstate->curexc_type != old_exc_type) below - Py_XINCREF(old_exc_type); + Py_XINCREF(old_exc); // Make sure that type->tp_name remains valid Py_INCREF(type); #endif @@ -2432,12 +2432,12 @@ _Py_Dealloc(PyObject *op) #ifdef Py_DEBUG // gh-89373: The tp_dealloc function must leave the current exception // unchanged. - if (tstate != NULL && tstate->curexc_type != old_exc_type) { + if (tstate != NULL && tstate->current_exception != old_exc) { const char *err; - if (old_exc_type == NULL) { + if (old_exc == NULL) { err = "Deallocator of type '%s' raised an exception"; } - else if (tstate->curexc_type == NULL) { + else if (tstate->current_exception == NULL) { err = "Deallocator of type '%s' cleared the current exception"; } else { @@ -2448,7 +2448,7 @@ _Py_Dealloc(PyObject *op) } _Py_FatalErrorFormat(__func__, err, type->tp_name); } - Py_XDECREF(old_exc_type); + Py_XDECREF(old_exc); Py_DECREF(type); #endif } diff --git a/PC/python3dll.c b/PC/python3dll.c index 931f316bb99843..e300819365756e 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -198,6 +198,7 @@ EXPORT_FUNC(PyErr_Format) EXPORT_FUNC(PyErr_FormatV) EXPORT_FUNC(PyErr_GetExcInfo) EXPORT_FUNC(PyErr_GetHandledException) +EXPORT_FUNC(PyErr_GetRaisedException) EXPORT_FUNC(PyErr_GivenExceptionMatches) EXPORT_FUNC(PyErr_NewException) EXPORT_FUNC(PyErr_NewExceptionWithDoc) @@ -227,6 +228,7 @@ EXPORT_FUNC(PyErr_SetInterrupt) EXPORT_FUNC(PyErr_SetInterruptEx) EXPORT_FUNC(PyErr_SetNone) EXPORT_FUNC(PyErr_SetObject) +EXPORT_FUNC(PyErr_SetRaisedException) EXPORT_FUNC(PyErr_SetString) EXPORT_FUNC(PyErr_SyntaxLocation) EXPORT_FUNC(PyErr_SyntaxLocationEx) @@ -255,9 +257,11 @@ EXPORT_FUNC(PyEval_ReleaseThread) EXPORT_FUNC(PyEval_RestoreThread) EXPORT_FUNC(PyEval_SaveThread) EXPORT_FUNC(PyEval_ThreadsInitialized) +EXPORT_FUNC(PyException_GetArgs) EXPORT_FUNC(PyException_GetCause) EXPORT_FUNC(PyException_GetContext) EXPORT_FUNC(PyException_GetTraceback) +EXPORT_FUNC(PyException_SetArgs) EXPORT_FUNC(PyException_SetCause) EXPORT_FUNC(PyException_SetContext) EXPORT_FUNC(PyException_SetTraceback) diff --git a/Parser/pegen.c b/Parser/pegen.c index d84e06861edefc..94dd9de8a431c1 100644 --- a/Parser/pegen.c +++ b/Parser/pegen.c @@ -643,13 +643,10 @@ _PyPegen_number_token(Parser *p) PyThreadState *tstate = _PyThreadState_GET(); // The only way a ValueError should happen in _this_ code is via // PyLong_FromString hitting a length limit. - if (tstate->curexc_type == PyExc_ValueError && - tstate->curexc_value != NULL) { - PyObject *type, *value, *tb; - // This acts as PyErr_Clear() as we're replacing curexc. - PyErr_Fetch(&type, &value, &tb); - Py_XDECREF(tb); - Py_DECREF(type); + if (tstate->current_exception != NULL && + Py_TYPE(tstate->current_exception) == (PyTypeObject *)PyExc_ValueError + ) { + PyObject *exc = PyErr_GetRaisedException(); /* Intentionally omitting columns to avoid a wall of 1000s of '^'s * on the error message. Nobody is going to overlook their huge * numeric literal once given the line. */ @@ -659,8 +656,8 @@ _PyPegen_number_token(Parser *p) t->end_lineno, -1 /* end_col_offset */, "%S - Consider hexadecimal for huge integer literals " "to avoid decimal conversion limits.", - value); - Py_DECREF(value); + exc); + Py_DECREF(exc); } return NULL; } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 9633f34212a68d..1169d8d172dd57 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -804,9 +804,7 @@ dummy_func( DECREF_INPUTS(); } else { - PyObject *exc_type = Py_NewRef(Py_TYPE(exc_value)); - PyObject *exc_traceback = PyException_GetTraceback(exc_value); - _PyErr_Restore(tstate, exc_type, Py_NewRef(exc_value), exc_traceback); + _PyErr_SetRaisedException(tstate, Py_NewRef(exc_value)); goto exception_unwind; } } diff --git a/Python/ceval.c b/Python/ceval.c index ecb5bf9655553e..a91f5baca8853e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2902,13 +2902,13 @@ format_kwargs_error(PyThreadState *tstate, PyObject *func, PyObject *kwargs) } } else if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { - PyObject *exc, *val, *tb; - _PyErr_Fetch(tstate, &exc, &val, &tb); - if (val && PyTuple_Check(val) && PyTuple_GET_SIZE(val) == 1) { + PyObject *exc = _PyErr_GetRaisedException(tstate); + PyObject *args = ((PyBaseExceptionObject *)exc)->args; + if (exc && PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1) { _PyErr_Clear(tstate); PyObject *funcstr = _PyObject_FunctionStr(func); if (funcstr != NULL) { - PyObject *key = PyTuple_GET_ITEM(val, 0); + PyObject *key = PyTuple_GET_ITEM(args, 0); _PyErr_Format( tstate, PyExc_TypeError, "%U got multiple values for keyword argument '%S'", @@ -2916,11 +2916,9 @@ format_kwargs_error(PyThreadState *tstate, PyObject *func, PyObject *kwargs) Py_DECREF(funcstr); } Py_XDECREF(exc); - Py_XDECREF(val); - Py_XDECREF(tb); } else { - _PyErr_Restore(tstate, exc, val, tb); + _PyErr_SetRaisedException(tstate, exc); } } } diff --git a/Python/errors.c b/Python/errors.c index 05ef62246ec0a4..f573bed3d63ef0 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -27,32 +27,84 @@ static PyObject * _PyErr_FormatV(PyThreadState *tstate, PyObject *exception, const char *format, va_list vargs); - void -_PyErr_Restore(PyThreadState *tstate, PyObject *type, PyObject *value, - PyObject *traceback) +_PyErr_SetRaisedException(PyThreadState *tstate, PyObject *exc) { - PyObject *oldtype, *oldvalue, *oldtraceback; + PyObject *old_exc = tstate->current_exception; + tstate->current_exception = exc; + Py_XDECREF(old_exc); +} - if (traceback != NULL && !PyTraceBack_Check(traceback)) { - /* XXX Should never happen -- fatal error instead? */ - /* Well, it could be None. */ - Py_SETREF(traceback, NULL); +static PyObject* +_PyErr_CreateException(PyObject *exception_type, PyObject *value) +{ + PyObject *exc; + + if (value == NULL || value == Py_None) { + exc = _PyObject_CallNoArgs(exception_type); + } + else if (PyTuple_Check(value)) { + exc = PyObject_Call(exception_type, value, NULL); + } + else { + exc = PyObject_CallOneArg(exception_type, value); } - /* Save these in locals to safeguard against recursive - invocation through Py_XDECREF */ - oldtype = tstate->curexc_type; - oldvalue = tstate->curexc_value; - oldtraceback = tstate->curexc_traceback; + if (exc != NULL && !PyExceptionInstance_Check(exc)) { + PyErr_Format(PyExc_TypeError, + "calling %R should have returned an instance of " + "BaseException, not %s", + exception_type, Py_TYPE(exc)->tp_name); + Py_CLEAR(exc); + } - tstate->curexc_type = type; - tstate->curexc_value = value; - tstate->curexc_traceback = traceback; + return exc; +} - Py_XDECREF(oldtype); - Py_XDECREF(oldvalue); - Py_XDECREF(oldtraceback); +void +_PyErr_Restore(PyThreadState *tstate, PyObject *type, PyObject *value, + PyObject *traceback) +{ + if (type == NULL) { + assert(value == NULL); + assert(traceback == NULL); + _PyErr_SetRaisedException(tstate, NULL); + return; + } + assert(PyExceptionClass_Check(type)); + if (value != NULL && type == (PyObject *)Py_TYPE(value)) { + /* Already normalized */ + assert(((PyBaseExceptionObject *)value)->traceback != Py_None); + } + else { + PyObject *exc = _PyErr_CreateException(type, value); + Py_XDECREF(value); + if (exc == NULL) { + Py_DECREF(type); + Py_XDECREF(traceback); + return; + } + value = exc; + } + assert(PyExceptionInstance_Check(value)); + if (traceback != NULL && !PyTraceBack_Check(traceback)) { + if (traceback == Py_None) { + Py_DECREF(Py_None); + traceback = NULL; + } + else { + PyErr_SetString(PyExc_TypeError, "traceback must be a Traceback or None"); + Py_XDECREF(value); + Py_DECREF(type); + Py_XDECREF(traceback); + return; + } + } + PyObject *old_traceback = ((PyBaseExceptionObject *)value)->traceback; + ((PyBaseExceptionObject *)value)->traceback = traceback; + Py_XDECREF(old_traceback); + _PyErr_SetRaisedException(tstate, value); + Py_DECREF(type); } void @@ -62,6 +114,12 @@ PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback) _PyErr_Restore(tstate, type, value, traceback); } +void +PyErr_SetRaisedException(PyObject *exc) +{ + PyThreadState *tstate = _PyThreadState_GET(); + _PyErr_SetRaisedException(tstate, exc); +} _PyErr_StackItem * _PyErr_GetTopmostException(PyThreadState *tstate) @@ -77,32 +135,6 @@ _PyErr_GetTopmostException(PyThreadState *tstate) return exc_info; } -static PyObject* -_PyErr_CreateException(PyObject *exception_type, PyObject *value) -{ - PyObject *exc; - - if (value == NULL || value == Py_None) { - exc = _PyObject_CallNoArgs(exception_type); - } - else if (PyTuple_Check(value)) { - exc = PyObject_Call(exception_type, value, NULL); - } - else { - exc = PyObject_CallOneArg(exception_type, value); - } - - if (exc != NULL && !PyExceptionInstance_Check(exc)) { - PyErr_Format(PyExc_TypeError, - "calling %R should have returned an instance of " - "BaseException, not %s", - exception_type, Py_TYPE(exc)->tp_name); - Py_CLEAR(exc); - } - - return exc; -} - void _PyErr_SetObject(PyThreadState *tstate, PyObject *exception, PyObject *value) { @@ -117,30 +149,29 @@ _PyErr_SetObject(PyThreadState *tstate, PyObject *exception, PyObject *value) exception); return; } - Py_XINCREF(value); - exc_value = _PyErr_GetTopmostException(tstate)->exc_value; - if (exc_value != NULL && exc_value != Py_None) { - /* Implicit exception chaining */ - Py_INCREF(exc_value); - if (value == NULL || !PyExceptionInstance_Check(value)) { - /* We must normalize the value right now */ - PyObject *fixed_value; - - /* Issue #23571: functions must not be called with an - exception set */ - _PyErr_Clear(tstate); + /* Normalize the exception */ + if (value == NULL || (PyObject *)Py_TYPE(value) != exception) { + /* We must normalize the value right now */ + PyObject *fixed_value; - fixed_value = _PyErr_CreateException(exception, value); - Py_XDECREF(value); - if (fixed_value == NULL) { - Py_DECREF(exc_value); - return; - } + /* Issue #23571: functions must not be called with an + exception set */ + _PyErr_Clear(tstate); - value = fixed_value; + fixed_value = _PyErr_CreateException(exception, value); + Py_XDECREF(value); + if (fixed_value == NULL) { + return; } + value = fixed_value; + } + + exc_value = _PyErr_GetTopmostException(tstate)->exc_value; + if (exc_value != NULL && exc_value != Py_None) { + /* Implicit exception chaining */ + Py_INCREF(exc_value); /* Avoid creating new reference cycles through the context chain, while taking care not to hang on pre-existing ones. @@ -414,17 +445,34 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) } +PyObject * +_PyErr_GetRaisedException(PyThreadState *tstate) { + PyObject *exc = tstate->current_exception; + tstate->current_exception = NULL; + return exc; +} + +PyObject * +PyErr_GetRaisedException(void) +{ + PyThreadState *tstate = _PyThreadState_GET(); + return _PyErr_GetRaisedException(tstate); +} + void _PyErr_Fetch(PyThreadState *tstate, PyObject **p_type, PyObject **p_value, PyObject **p_traceback) { - *p_type = tstate->curexc_type; - *p_value = tstate->curexc_value; - *p_traceback = tstate->curexc_traceback; - - tstate->curexc_type = NULL; - tstate->curexc_value = NULL; - tstate->curexc_traceback = NULL; + PyObject *exc = _PyErr_GetRaisedException(tstate); + *p_value = exc; + if (exc == NULL) { + *p_type = NULL; + *p_traceback = NULL; + } + else { + *p_type = Py_NewRef(Py_TYPE(exc)); + *p_traceback = Py_XNewRef(((PyBaseExceptionObject *)exc)->traceback); + } } @@ -597,6 +645,28 @@ _PyErr_ChainExceptions(PyObject *typ, PyObject *val, PyObject *tb) } } +/* Like PyErr_SetRaisedException(), but if an exception is already set, + set the context associated with it. + + The caller is responsible for ensuring that this call won't create + any cycles in the exception context chain. */ +void +_PyErr_ChainExceptions1(PyObject *exc) +{ + if (exc == NULL) { + return; + } + PyThreadState *tstate = _PyThreadState_GET(); + if (_PyErr_Occurred(tstate)) { + PyObject *exc2 = _PyErr_GetRaisedException(tstate); + PyException_SetContext(exc2, exc); + _PyErr_SetRaisedException(tstate, exc2); + } + else { + _PyErr_SetRaisedException(tstate, exc); + } +} + /* Set the currently set exception's context to the given exception. If the provided exc_info is NULL, then the current Python thread state's @@ -706,19 +776,6 @@ PyErr_BadArgument(void) return 0; } -PyObject * -_PyErr_NoMemory(PyThreadState *tstate) -{ - if (Py_IS_TYPE(PyExc_MemoryError, NULL)) { - /* PyErr_NoMemory() has been called before PyExc_MemoryError has been - initialized by _PyExc_Init() */ - Py_FatalError("Out of memory and PyExc_MemoryError is not " - "initialized yet"); - } - _PyErr_SetNone(tstate, PyExc_MemoryError); - return NULL; -} - PyObject * PyErr_NoMemory(void) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index f38286441be4b3..09eb6893ebf6b4 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1036,9 +1036,7 @@ Py_DECREF(exc_value); } else { - PyObject *exc_type = Py_NewRef(Py_TYPE(exc_value)); - PyObject *exc_traceback = PyException_GetTraceback(exc_value); - _PyErr_Restore(tstate, exc_type, Py_NewRef(exc_value), exc_traceback); + _PyErr_SetRaisedException(tstate, Py_NewRef(exc_value)); goto exception_unwind; } STACK_SHRINK(2); diff --git a/Python/import.c b/Python/import.c index da6c15c5fd4144..1318c09d9b3212 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1592,6 +1592,13 @@ remove_importlib_frames(PyThreadState *tstate) Py_DECREF(code); tb = next; } + assert(PyExceptionInstance_Check(value)); + assert((PyObject *)Py_TYPE(value) == exception); + if (base_tb == NULL) { + base_tb = Py_None; + Py_INCREF(Py_None); + } + PyException_SetTraceback(value, base_tb); done: _PyErr_Restore(tstate, exception, value, base_tb); } diff --git a/Python/initconfig.c b/Python/initconfig.c index d7b2dc4a297425..deec805a6b1ca4 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -3143,8 +3143,7 @@ init_dump_ascii_wstr(const wchar_t *str) void _Py_DumpPathConfig(PyThreadState *tstate) { - PyObject *exc_type, *exc_value, *exc_tb; - _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb); + PyObject *exc = _PyErr_GetRaisedException(tstate); PySys_WriteStderr("Python path configuration:\n"); @@ -3202,5 +3201,5 @@ _Py_DumpPathConfig(PyThreadState *tstate) PySys_WriteStderr(" ]\n"); } - _PyErr_Restore(tstate, exc_type, exc_value, exc_tb); + _PyErr_SetRaisedException(tstate, exc); } diff --git a/Python/pystate.c b/Python/pystate.c index ed8c2e212a5539..1261092d1435fa 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1375,9 +1375,7 @@ PyThreadState_Clear(PyThreadState *tstate) Py_CLEAR(tstate->dict); Py_CLEAR(tstate->async_exc); - Py_CLEAR(tstate->curexc_type); - Py_CLEAR(tstate->curexc_value); - Py_CLEAR(tstate->curexc_traceback); + Py_CLEAR(tstate->current_exception); Py_CLEAR(tstate->exc_state.exc_value); diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 35292b6478a833..6a4d593768690a 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -748,13 +748,10 @@ _Py_HandleSystemExit(int *exitcode_p) } done: - /* Restore and clear the exception info, in order to properly decref - * the exception, value, and traceback. If we just exit instead, - * these leak, which confuses PYTHONDUMPREFS output, and may prevent - * some finalizers from running. - */ - PyErr_Restore(exception, value, tb); - PyErr_Clear(); + /* Cleanup the exception */ + Py_CLEAR(exception); + Py_CLEAR(value); + Py_CLEAR(tb); *exitcode_p = exitcode; return 1; } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index f9f766a94d1464..6e81ef92b67f70 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -66,12 +66,11 @@ _PySys_GetAttr(PyThreadState *tstate, PyObject *name) if (sd == NULL) { return NULL; } - PyObject *exc_type, *exc_value, *exc_tb; - _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb); + PyObject *exc = _PyErr_GetRaisedException(tstate); /* XXX Suppress a new exception if it was raised and restore * the old one. */ PyObject *value = _PyDict_GetItemWithError(sd, name); - _PyErr_Restore(tstate, exc_type, exc_value, exc_tb); + _PyErr_SetRaisedException(tstate, exc); return value; } @@ -3704,11 +3703,10 @@ static void sys_format(PyObject *key, FILE *fp, const char *format, va_list va) { PyObject *file, *message; - PyObject *error_type, *error_value, *error_traceback; const char *utf8; PyThreadState *tstate = _PyThreadState_GET(); - _PyErr_Fetch(tstate, &error_type, &error_value, &error_traceback); + PyObject *error = _PyErr_GetRaisedException(tstate); file = _PySys_GetAttr(tstate, key); message = PyUnicode_FromFormatV(format, va); if (message != NULL) { @@ -3720,7 +3718,7 @@ sys_format(PyObject *key, FILE *fp, const char *format, va_list va) } Py_DECREF(message); } - _PyErr_Restore(tstate, error_type, error_value, error_traceback); + _PyErr_SetRaisedException(tstate, error); } void diff --git a/Python/traceback.c b/Python/traceback.c index da26c9b260a3bd..31b85e77575efa 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -249,6 +249,8 @@ PyTraceBack_Here(PyFrameObject *frame) _PyErr_ChainExceptions(exc, val, tb); return -1; } + assert(PyExceptionInstance_Check(val)); + PyException_SetTraceback(val, newtb); PyErr_Restore(exc, val, newtb); Py_XDECREF(tb); return 0; @@ -260,13 +262,12 @@ void _PyTraceback_Add(const char *funcname, const char *filename, int lineno) PyObject *globals; PyCodeObject *code; PyFrameObject *frame; - PyObject *exc, *val, *tb; PyThreadState *tstate = _PyThreadState_GET(); /* Save and clear the current exception. Python functions must not be called with an exception set. Calling Python functions happens when the codec of the filesystem encoding is implemented in pure Python. */ - _PyErr_Fetch(tstate, &exc, &val, &tb); + PyObject *exc = _PyErr_GetRaisedException(tstate); globals = PyDict_New(); if (!globals) @@ -283,13 +284,13 @@ void _PyTraceback_Add(const char *funcname, const char *filename, int lineno) goto error; frame->f_lineno = lineno; - _PyErr_Restore(tstate, exc, val, tb); + _PyErr_SetRaisedException(tstate, exc); PyTraceBack_Here(frame); Py_DECREF(frame); return; error: - _PyErr_ChainExceptions(exc, val, tb); + _PyErr_ChainExceptions1(exc); } static PyObject * From eb49d32b9af0b3b01a5588626179187f11d145c9 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 8 Feb 2023 13:13:43 +0300 Subject: [PATCH 121/225] gh-100933: Improve `check_element` helper in `test_xml_etree` (#100934) Items checked by this test are always `str` and `dict` instances. --- Lib/test/test_xml_etree.py | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index ca5bb562996b52..11efee00582e01 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -203,25 +203,6 @@ def serialize_check(self, elem, expected): def test_interface(self): # Test element tree interface. - def check_string(string): - len(string) - for char in string: - self.assertEqual(len(char), 1, - msg="expected one-character string, got %r" % char) - new_string = string + "" - new_string = string + " " - string[:0] - - def check_mapping(mapping): - len(mapping) - keys = mapping.keys() - items = mapping.items() - for key in keys: - item = mapping[key] - mapping["key"] = "value" - self.assertEqual(mapping["key"], "value", - msg="expected value string, got %r" % mapping["key"]) - def check_element(element): self.assertTrue(ET.iselement(element), msg="not an element") direlem = dir(element) @@ -231,12 +212,12 @@ def check_element(element): self.assertIn(attr, direlem, msg='no %s visible by dir' % attr) - check_string(element.tag) - check_mapping(element.attrib) + self.assertIsInstance(element.tag, str) + self.assertIsInstance(element.attrib, dict) if element.text is not None: - check_string(element.text) + self.assertIsInstance(element.text, str) if element.tail is not None: - check_string(element.tail) + self.assertIsInstance(element.tail, str) for elem in element: check_element(elem) From 3a88de7a0af00872d9d57e1d98bc2f035cb15a1c Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Wed, 8 Feb 2023 14:23:57 +0000 Subject: [PATCH 122/225] gh-101614: Don't treat python3_d.dll as a Python DLL when checking extension modules for incompatibility (GH-101615) --- .../2023-02-07-18-22-54.gh-issue-101614.NjVP0n.rst | 1 + Python/dynload_win.c | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-02-07-18-22-54.gh-issue-101614.NjVP0n.rst diff --git a/Misc/NEWS.d/next/Windows/2023-02-07-18-22-54.gh-issue-101614.NjVP0n.rst b/Misc/NEWS.d/next/Windows/2023-02-07-18-22-54.gh-issue-101614.NjVP0n.rst new file mode 100644 index 00000000000000..8ed0995d78925b --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-02-07-18-22-54.gh-issue-101614.NjVP0n.rst @@ -0,0 +1 @@ +Correctly handle extensions built against debug binaries that reference ``python3_d.dll``. diff --git a/Python/dynload_win.c b/Python/dynload_win.c index c03bc5602bffee..7bd04d573df4ad 100644 --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -125,14 +125,15 @@ static char *GetPythonImport (HINSTANCE hModule) !strncmp(import_name,"python",6)) { char *pch; -#ifndef _DEBUG - /* In a release version, don't claim that python3.dll is - a Python DLL. */ + /* Don't claim that python3.dll is a Python DLL. */ +#ifdef _DEBUG + if (strcmp(import_name, "python3_d.dll") == 0) { +#else if (strcmp(import_name, "python3.dll") == 0) { +#endif import_data += 20; continue; } -#endif /* Ensure python prefix is followed only by numbers to the end of the basename */ From 86ebd5c3fa9ac0fba3b651f1d4abfca79614af5f Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Wed, 8 Feb 2023 09:34:24 -0500 Subject: [PATCH 123/225] gh-101196: Make isdir/isfile/exists faster on Windows (GH-101324) Co-authored-by: Eryk Sun --- Include/pyport.h | 4 + Lib/genericpath.py | 14 +- Lib/ntpath.py | 27 +- Lib/posixpath.py | 12 - Lib/test/test_ntpath.py | 32 +- Lib/test/test_os.py | 2 + ...-01-25-11-33-54.gh-issue-101196.wAX_2g.rst | 3 + Modules/clinic/posixmodule.c.h | 254 +++++++++++++- Modules/posixmodule.c | 310 ++++++++++++++++++ 9 files changed, 624 insertions(+), 34 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-01-25-11-33-54.gh-issue-101196.wAX_2g.rst diff --git a/Include/pyport.h b/Include/pyport.h index 22085049a30487..40092c2f81ad48 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -247,6 +247,10 @@ typedef Py_ssize_t Py_ssize_clean_t; #define S_ISCHR(x) (((x) & S_IFMT) == S_IFCHR) #endif +#ifndef S_ISLNK +#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK) +#endif + #ifdef __cplusplus /* Move this down here since some C++ #include's don't like to be included inside an extern "C" */ diff --git a/Lib/genericpath.py b/Lib/genericpath.py index ce36451a3af01c..1bd5b3897c3af9 100644 --- a/Lib/genericpath.py +++ b/Lib/genericpath.py @@ -7,7 +7,7 @@ import stat __all__ = ['commonprefix', 'exists', 'getatime', 'getctime', 'getmtime', - 'getsize', 'isdir', 'isfile', 'samefile', 'sameopenfile', + 'getsize', 'isdir', 'isfile', 'islink', 'samefile', 'sameopenfile', 'samestat'] @@ -45,6 +45,18 @@ def isdir(s): return stat.S_ISDIR(st.st_mode) +# Is a path a symbolic link? +# This will always return false on systems where os.lstat doesn't exist. + +def islink(path): + """Test whether a path is a symbolic link""" + try: + st = os.lstat(path) + except (OSError, ValueError, AttributeError): + return False + return stat.S_ISLNK(st.st_mode) + + def getsize(filename): """Return the size of a file, reported by os.stat().""" return os.stat(filename).st_size diff --git a/Lib/ntpath.py b/Lib/ntpath.py index f9ee8e02a576b7..e93a5e69600973 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -276,19 +276,6 @@ def dirname(p): """Returns the directory component of a pathname""" return split(p)[0] -# Is a path a symbolic link? -# This will always return false on systems where os.lstat doesn't exist. - -def islink(path): - """Test whether a path is a symbolic link. - This will always return false for Windows prior to 6.0. - """ - try: - st = os.lstat(path) - except (OSError, ValueError, AttributeError): - return False - return stat.S_ISLNK(st.st_mode) - # Is a path a junction? @@ -870,11 +857,13 @@ def commonpath(paths): try: - # The genericpath.isdir implementation uses os.stat and checks the mode - # attribute to tell whether or not the path is a directory. - # This is overkill on Windows - just pass the path to GetFileAttributes - # and check the attribute from there. - from nt import _isdir as isdir + # The isdir(), isfile(), islink() and exists() implementations in + # genericpath use os.stat(). This is overkill on Windows. Use simpler + # builtin functions if they are available. + from nt import _path_isdir as isdir + from nt import _path_isfile as isfile + from nt import _path_islink as islink + from nt import _path_exists as exists except ImportError: - # Use genericpath.isdir as imported above. + # Use genericpath.* as imported above pass diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 32b5d6e105dde9..e4f155e41a3221 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -187,18 +187,6 @@ def dirname(p): return head -# Is a path a symbolic link? -# This will always return false on systems where os.lstat doesn't exist. - -def islink(path): - """Test whether a path is a symbolic link""" - try: - st = os.lstat(path) - except (OSError, ValueError, AttributeError): - return False - return stat.S_ISLNK(st.st_mode) - - # Is a path a junction? def isjunction(path): diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index bce38a534a6a98..b32900697874b1 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -1,9 +1,10 @@ +import inspect import ntpath import os import sys import unittest import warnings -from test.support import os_helper +from test.support import cpython_only, os_helper from test.support import TestFailed, is_emscripten from test.support.os_helper import FakePath from test import test_genericpath @@ -938,6 +939,35 @@ def test_isjunction(self): self.assertFalse(ntpath.isjunction('tmpdir')) self.assertPathEqual(ntpath.realpath('testjunc'), ntpath.realpath('tmpdir')) + @unittest.skipIf(sys.platform != 'win32', "drive letters are a windows concept") + def test_isfile_driveletter(self): + drive = os.environ.get('SystemDrive') + if drive is None or len(drive) != 2 or drive[1] != ':': + raise unittest.SkipTest('SystemDrive is not defined or malformed') + self.assertFalse(os.path.isfile('\\\\.\\' + drive)) + + @unittest.skipIf(sys.platform != 'win32', "windows only") + def test_con_device(self): + self.assertFalse(os.path.isfile(r"\\.\CON")) + self.assertFalse(os.path.isdir(r"\\.\CON")) + self.assertFalse(os.path.islink(r"\\.\CON")) + self.assertTrue(os.path.exists(r"\\.\CON")) + + @unittest.skipIf(sys.platform != 'win32', "Fast paths are only for win32") + @cpython_only + def test_fast_paths_in_use(self): + # There are fast paths of these functions implemented in posixmodule.c. + # Confirm that they are being used, and not the Python fallbacks in + # genericpath.py. + self.assertTrue(os.path.isdir is nt._path_isdir) + self.assertFalse(inspect.isfunction(os.path.isdir)) + self.assertTrue(os.path.isfile is nt._path_isfile) + self.assertFalse(inspect.isfunction(os.path.isfile)) + self.assertTrue(os.path.islink is nt._path_islink) + self.assertFalse(inspect.isfunction(os.path.islink)) + self.assertTrue(os.path.exists is nt._path_exists) + self.assertFalse(inspect.isfunction(os.path.exists)) + class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase): pathmodule = ntpath diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 58e04dd1348fd1..387d2581c06fc6 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -742,6 +742,7 @@ def test_access_denied(self): ) result = os.stat(fname) self.assertNotEqual(result.st_size, 0) + self.assertTrue(os.path.isfile(fname)) @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") def test_stat_block_device(self): @@ -2860,6 +2861,7 @@ def test_appexeclink(self): self.assertEqual(st, os.stat(alias)) self.assertFalse(stat.S_ISLNK(st.st_mode)) self.assertEqual(st.st_reparse_tag, stat.IO_REPARSE_TAG_APPEXECLINK) + self.assertTrue(os.path.isfile(alias)) # testing the first one we see is sufficient break else: diff --git a/Misc/NEWS.d/next/Windows/2023-01-25-11-33-54.gh-issue-101196.wAX_2g.rst b/Misc/NEWS.d/next/Windows/2023-01-25-11-33-54.gh-issue-101196.wAX_2g.rst new file mode 100644 index 00000000000000..c61e9b90fb5373 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-25-11-33-54.gh-issue-101196.wAX_2g.rst @@ -0,0 +1,3 @@ +The functions ``os.path.isdir``, ``os.path.isfile``, ``os.path.islink`` and +``os.path.exists`` are now 13% to 28% faster on Windows, by making fewer Win32 +API calls. diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index d4722cc533cbab..5e04507ddd6917 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -1794,6 +1794,242 @@ os__path_splitroot(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py #endif /* defined(MS_WINDOWS) */ +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(os__path_isdir__doc__, +"_path_isdir($module, /, path)\n" +"--\n" +"\n" +"Return true if the pathname refers to an existing directory."); + +#define OS__PATH_ISDIR_METHODDEF \ + {"_path_isdir", _PyCFunction_CAST(os__path_isdir), METH_FASTCALL|METH_KEYWORDS, os__path_isdir__doc__}, + +static PyObject * +os__path_isdir_impl(PyObject *module, PyObject *path); + +static PyObject * +os__path_isdir(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(path), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"path", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_path_isdir", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *path; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + path = args[0]; + return_value = os__path_isdir_impl(module, path); + +exit: + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(os__path_isfile__doc__, +"_path_isfile($module, /, path)\n" +"--\n" +"\n" +"Test whether a path is a regular file"); + +#define OS__PATH_ISFILE_METHODDEF \ + {"_path_isfile", _PyCFunction_CAST(os__path_isfile), METH_FASTCALL|METH_KEYWORDS, os__path_isfile__doc__}, + +static PyObject * +os__path_isfile_impl(PyObject *module, PyObject *path); + +static PyObject * +os__path_isfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(path), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"path", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_path_isfile", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *path; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + path = args[0]; + return_value = os__path_isfile_impl(module, path); + +exit: + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(os__path_exists__doc__, +"_path_exists($module, /, path)\n" +"--\n" +"\n" +"Test whether a path exists. Returns False for broken symbolic links"); + +#define OS__PATH_EXISTS_METHODDEF \ + {"_path_exists", _PyCFunction_CAST(os__path_exists), METH_FASTCALL|METH_KEYWORDS, os__path_exists__doc__}, + +static PyObject * +os__path_exists_impl(PyObject *module, PyObject *path); + +static PyObject * +os__path_exists(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(path), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"path", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_path_exists", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *path; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + path = args[0]; + return_value = os__path_exists_impl(module, path); + +exit: + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(os__path_islink__doc__, +"_path_islink($module, /, path)\n" +"--\n" +"\n" +"Test whether a path is a symbolic link"); + +#define OS__PATH_ISLINK_METHODDEF \ + {"_path_islink", _PyCFunction_CAST(os__path_islink), METH_FASTCALL|METH_KEYWORDS, os__path_islink__doc__}, + +static PyObject * +os__path_islink_impl(PyObject *module, PyObject *path); + +static PyObject * +os__path_islink(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(path), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"path", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_path_islink", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *path; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + path = args[0]; + return_value = os__path_islink_impl(module, path); + +exit: + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + PyDoc_STRVAR(os__path_normpath__doc__, "_path_normpath($module, /, path)\n" "--\n" @@ -11041,6 +11277,22 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #define OS__PATH_SPLITROOT_METHODDEF #endif /* !defined(OS__PATH_SPLITROOT_METHODDEF) */ +#ifndef OS__PATH_ISDIR_METHODDEF + #define OS__PATH_ISDIR_METHODDEF +#endif /* !defined(OS__PATH_ISDIR_METHODDEF) */ + +#ifndef OS__PATH_ISFILE_METHODDEF + #define OS__PATH_ISFILE_METHODDEF +#endif /* !defined(OS__PATH_ISFILE_METHODDEF) */ + +#ifndef OS__PATH_EXISTS_METHODDEF + #define OS__PATH_EXISTS_METHODDEF +#endif /* !defined(OS__PATH_EXISTS_METHODDEF) */ + +#ifndef OS__PATH_ISLINK_METHODDEF + #define OS__PATH_ISLINK_METHODDEF +#endif /* !defined(OS__PATH_ISLINK_METHODDEF) */ + #ifndef OS_NICE_METHODDEF #define OS_NICE_METHODDEF #endif /* !defined(OS_NICE_METHODDEF) */ @@ -11560,4 +11812,4 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF #define OS_WAITSTATUS_TO_EXITCODE_METHODDEF #endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */ -/*[clinic end generated code: output=41eab6c3523792a9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=a3f76228b549e8ec input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index b84fb0d280f4e3..cba6cea48b77e1 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4490,6 +4490,311 @@ os__path_splitroot_impl(PyObject *module, path_t *path) } +/*[clinic input] +os._path_isdir + + path: 'O' + +Return true if the pathname refers to an existing directory. + +[clinic start generated code]*/ + +static PyObject * +os__path_isdir_impl(PyObject *module, PyObject *path) +/*[clinic end generated code: output=00faea0af309669d input=b1d2571cf7291aaf]*/ +{ + HANDLE hfile; + BOOL close_file = TRUE; + FILE_BASIC_INFO info; + path_t _path = PATH_T_INITIALIZE("isdir", "path", 0, 1); + int result; + + if (!path_converter(path, &_path)) { + path_cleanup(&_path); + if (PyErr_ExceptionMatches(PyExc_ValueError)) { + PyErr_Clear(); + Py_RETURN_FALSE; + } + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + if (_path.fd != -1) { + hfile = _Py_get_osfhandle_noraise(_path.fd); + close_file = FALSE; + } + else { + hfile = CreateFileW(_path.wide, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + } + if (hfile != INVALID_HANDLE_VALUE) { + if (GetFileInformationByHandleEx(hfile, FileBasicInfo, &info, + sizeof(info))) + { + result = info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY; + } + else { + result = 0; + } + if (close_file) { + CloseHandle(hfile); + } + } + else { + STRUCT_STAT st; + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_CANT_ACCESS_FILE: + case ERROR_INVALID_PARAMETER: + if (STAT(_path.wide, &st)) { + result = 0; + } + else { + result = S_ISDIR(st.st_mode); + } + break; + default: + result = 0; + } + } + Py_END_ALLOW_THREADS + + path_cleanup(&_path); + if (result) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + + +/*[clinic input] +os._path_isfile + + path: 'O' + +Test whether a path is a regular file + +[clinic start generated code]*/ + +static PyObject * +os__path_isfile_impl(PyObject *module, PyObject *path) +/*[clinic end generated code: output=2394ed7c4b5cfd85 input=de22d74960ade365]*/ +{ + HANDLE hfile; + BOOL close_file = TRUE; + FILE_BASIC_INFO info; + path_t _path = PATH_T_INITIALIZE("isfile", "path", 0, 1); + int result; + + if (!path_converter(path, &_path)) { + path_cleanup(&_path); + if (PyErr_ExceptionMatches(PyExc_ValueError)) { + PyErr_Clear(); + Py_RETURN_FALSE; + } + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + if (_path.fd != -1) { + hfile = _Py_get_osfhandle_noraise(_path.fd); + close_file = FALSE; + } + else { + hfile = CreateFileW(_path.wide, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + } + if (hfile != INVALID_HANDLE_VALUE) { + if (GetFileInformationByHandleEx(hfile, FileBasicInfo, &info, + sizeof(info))) + { + result = !(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY); + } + else { + result = 0; + } + if (close_file) { + CloseHandle(hfile); + } + } + else { + STRUCT_STAT st; + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_CANT_ACCESS_FILE: + case ERROR_INVALID_PARAMETER: + if (STAT(_path.wide, &st)) { + result = 0; + } + else { + result = S_ISREG(st.st_mode); + } + break; + default: + result = 0; + } + } + Py_END_ALLOW_THREADS + + path_cleanup(&_path); + if (result) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + + +/*[clinic input] +os._path_exists + + path: 'O' + +Test whether a path exists. Returns False for broken symbolic links + +[clinic start generated code]*/ + +static PyObject * +os__path_exists_impl(PyObject *module, PyObject *path) +/*[clinic end generated code: output=f508c3b35e13a249 input=380f77cdfa0f7ae8]*/ +{ + HANDLE hfile; + BOOL close_file = TRUE; + path_t _path = PATH_T_INITIALIZE("exists", "path", 0, 1); + int result; + + if (!path_converter(path, &_path)) { + path_cleanup(&_path); + if (PyErr_ExceptionMatches(PyExc_ValueError)) { + PyErr_Clear(); + Py_RETURN_FALSE; + } + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + if (_path.fd != -1) { + hfile = _Py_get_osfhandle_noraise(_path.fd); + close_file = FALSE; + } + else { + hfile = CreateFileW(_path.wide, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + } + if (hfile != INVALID_HANDLE_VALUE) { + result = 1; + if (close_file) { + CloseHandle(hfile); + } + } + else { + STRUCT_STAT st; + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_CANT_ACCESS_FILE: + case ERROR_INVALID_PARAMETER: + if (STAT(_path.wide, &st)) { + result = 0; + } + else { + result = 1; + } + break; + default: + result = 0; + } + } + Py_END_ALLOW_THREADS + + path_cleanup(&_path); + if (result) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + + +/*[clinic input] +os._path_islink + + path: 'O' + +Test whether a path is a symbolic link + +[clinic start generated code]*/ + +static PyObject * +os__path_islink_impl(PyObject *module, PyObject *path) +/*[clinic end generated code: output=6d8640b1a390c054 input=38a3cb937ccf59bf]*/ +{ + HANDLE hfile; + BOOL close_file = TRUE; + FILE_ATTRIBUTE_TAG_INFO info; + path_t _path = PATH_T_INITIALIZE("islink", "path", 0, 1); + int result; + + if (!path_converter(path, &_path)) { + path_cleanup(&_path); + if (PyErr_ExceptionMatches(PyExc_ValueError)) { + PyErr_Clear(); + Py_RETURN_FALSE; + } + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + if (_path.fd != -1) { + hfile = _Py_get_osfhandle_noraise(_path.fd); + close_file = FALSE; + } + else { + hfile = CreateFileW(_path.wide, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, + NULL); + } + if (hfile != INVALID_HANDLE_VALUE) { + if (GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &info, + sizeof(info))) + { + result = (info.ReparseTag == IO_REPARSE_TAG_SYMLINK); + } + else { + result = 0; + } + if (close_file) { + CloseHandle(hfile); + } + } + else { + STRUCT_STAT st; + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_CANT_ACCESS_FILE: + case ERROR_INVALID_PARAMETER: + if (LSTAT(_path.wide, &st)) { + result = 0; + } + else { + result = S_ISLNK(st.st_mode); + } + break; + default: + result = 0; + } + } + Py_END_ALLOW_THREADS + + path_cleanup(&_path); + if (result) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + #endif /* MS_WINDOWS */ @@ -15150,6 +15455,11 @@ static PyMethodDef posix_methods[] = { OS_WAITSTATUS_TO_EXITCODE_METHODDEF OS_SETNS_METHODDEF OS_UNSHARE_METHODDEF + + OS__PATH_ISDIR_METHODDEF + OS__PATH_ISFILE_METHODDEF + OS__PATH_ISLINK_METHODDEF + OS__PATH_EXISTS_METHODDEF {NULL, NULL} /* Sentinel */ }; From 35dd55005ee9aea2843eff7f514ee689a0995df8 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 8 Feb 2023 18:49:04 +0300 Subject: [PATCH 124/225] gh-101670: typo fix in PyImport_AppendInittab() (GH-101672) --- Python/import.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/import.c b/Python/import.c index 1318c09d9b3212..795d368966481e 100644 --- a/Python/import.c +++ b/Python/import.c @@ -2699,7 +2699,7 @@ PyImport_AppendInittab(const char *name, PyObject* (*initfunc)(void)) struct _inittab newtab[2]; if (_PyRuntime.imports.inittab != NULL) { - Py_FatalError("PyImport_AppendInittab() may be be called after Py_Initialize()"); + Py_FatalError("PyImport_AppendInittab() may not be called after Py_Initialize()"); } memset(newtab, '\0', sizeof newtab); From 2a8bf2580441147f1a15e61229d669abc0ab86ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Wed, 8 Feb 2023 17:50:43 +0100 Subject: [PATCH 125/225] gh-100221: Fix creating dirs in `make sharedinstall` (GH-100329) Fix creating install directories in `make sharedinstall` if they exist already outside `DESTDIR`. The previous make rules assumed that the directories would be created via a dependency on a rule for `$(DESTSHARED)` that did not fire if the directory did exist outside `$(DESTDIR)`. While technically `$(DESTDIR)` could be prepended to the rule name, moving the rules for creating directories straight into the `sharedinstall` rule seems to fit the common practices better. Since the rule explicitly checks whether the individual directories exist anyway, there seems to be no reason to rely on make determining that implicitly as well. --- Makefile.pre.in | 21 ++++++++----------- ...-12-18-08-33-28.gh-issue-100221.K94Ct3.rst | 2 ++ 2 files changed, 11 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2022-12-18-08-33-28.gh-issue-100221.K94Ct3.rst diff --git a/Makefile.pre.in b/Makefile.pre.in index 3641c4eeebeee3..2559df8e74952c 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1816,7 +1816,15 @@ commoninstall: check-clean-src @FRAMEWORKALTINSTALLFIRST@ \ # Install shared libraries enabled by Setup DESTDIRS= $(exec_prefix) $(LIBDIR) $(BINLIBDEST) $(DESTSHARED) -sharedinstall: $(DESTSHARED) all +sharedinstall: all + @for i in $(DESTDIRS); \ + do \ + if test ! -d $(DESTDIR)$$i; then \ + echo "Creating directory $$i"; \ + $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \ + else true; \ + fi; \ + done @for i in X $(SHAREDMODS); do \ if test $$i != X; then \ echo $(INSTALL_SHARED) $$i $(DESTSHARED)/`basename $$i`; \ @@ -1828,17 +1836,6 @@ sharedinstall: $(DESTSHARED) all fi; \ done - -$(DESTSHARED): - @for i in $(DESTDIRS); \ - do \ - if test ! -d $(DESTDIR)$$i; then \ - echo "Creating directory $$i"; \ - $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \ - else true; \ - fi; \ - done - # Install the interpreter with $(VERSION) affixed # This goes into $(exec_prefix) altbininstall: $(BUILDPYTHON) @FRAMEWORKPYTHONW@ diff --git a/Misc/NEWS.d/next/Build/2022-12-18-08-33-28.gh-issue-100221.K94Ct3.rst b/Misc/NEWS.d/next/Build/2022-12-18-08-33-28.gh-issue-100221.K94Ct3.rst new file mode 100644 index 00000000000000..27c948330cfc17 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2022-12-18-08-33-28.gh-issue-100221.K94Ct3.rst @@ -0,0 +1,2 @@ +Fix creating install directories in ``make sharedinstall`` if they exist +outside ``DESTDIR`` already. From d9de0792482d2ded364b0c7d2867b97a5da41b12 Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Wed, 8 Feb 2023 23:32:15 +0530 Subject: [PATCH 126/225] GH-101696: invalidate type version tag in `_PyStaticType_Dealloc` (#101697) --- .../2023-02-08-17-13-31.gh-issue-101696.seJhTt.rst | 1 + Objects/typeobject.c | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-08-17-13-31.gh-issue-101696.seJhTt.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-08-17-13-31.gh-issue-101696.seJhTt.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-08-17-13-31.gh-issue-101696.seJhTt.rst new file mode 100644 index 00000000000000..ff2bbb4b564252 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-08-17-13-31.gh-issue-101696.seJhTt.rst @@ -0,0 +1 @@ +Invalidate type version tag in ``_PyStaticType_Dealloc`` for static types, avoiding bug where a false cache hit could crash the interpreter. Patch by Kumar Aditya. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 59e0bf2995bac2..bf6ccdb77a90f0 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4469,6 +4469,8 @@ _PyStaticType_Dealloc(PyTypeObject *type) } type->tp_flags &= ~Py_TPFLAGS_READY; + type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG; + type->tp_version_tag = 0; if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { _PyStaticType_ClearWeakRefs(type); From 616aec1ff148ba4570aa2d4b8ea420c715c206e4 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 8 Feb 2023 11:40:10 -0800 Subject: [PATCH 127/225] gh-98831: Modernize CALL and family (#101508) Includes a slight improvement to `DECREF_INPUTS()`. --- Python/bytecodes.c | 532 +++++++++++----------- Python/ceval.c | 6 - Python/ceval_macros.h | 3 + Python/generated_cases.c.h | 560 ++++++++++++++---------- Python/opcode_metadata.h | 112 ++--- Tools/cases_generator/generate_cases.py | 21 +- 6 files changed, 646 insertions(+), 588 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 1169d8d172dd57..2b9f12fefa14e9 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2360,70 +2360,87 @@ dummy_func( assert(oparg & 1); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_BOUND_METHOD_EXACT_ARGS) { - DEOPT_IF(is_method(stack_pointer, oparg), CALL); - PyObject *function = PEEK(oparg + 1); - DEOPT_IF(Py_TYPE(function) != &PyMethod_Type, CALL); - STAT_INC(CALL, hit); - PyObject *self = ((PyMethodObject *)function)->im_self; - PEEK(oparg + 1) = Py_NewRef(self); - PyObject *meth = ((PyMethodObject *)function)->im_func; - PEEK(oparg + 2) = Py_NewRef(meth); - Py_DECREF(function); - GO_TO_INSTRUCTION(CALL_PY_EXACT_ARGS); - } - inst(KW_NAMES, (--)) { assert(kwnames == NULL); assert(oparg < PyTuple_GET_SIZE(consts)); kwnames = GETITEM(consts, oparg); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL) { + // Cache layout: counter/1, func_version/2, min_args/1 + // Neither CALL_INTRINSIC_1 nor CALL_FUNCTION_EX are members! + family(call, INLINE_CACHE_ENTRIES_CALL) = { + CALL, + CALL_BOUND_METHOD_EXACT_ARGS, + CALL_PY_EXACT_ARGS, + CALL_PY_WITH_DEFAULTS, + CALL_NO_KW_TYPE_1, + CALL_NO_KW_STR_1, + CALL_NO_KW_TUPLE_1, + CALL_BUILTIN_CLASS, + CALL_NO_KW_BUILTIN_O, + CALL_NO_KW_BUILTIN_FAST, + CALL_BUILTIN_FAST_WITH_KEYWORDS, + CALL_NO_KW_LEN, + CALL_NO_KW_ISINSTANCE, + CALL_NO_KW_LIST_APPEND, + CALL_NO_KW_METHOD_DESCRIPTOR_O, + CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, + CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, + CALL_NO_KW_METHOD_DESCRIPTOR_FAST, + }; + + // On entry, the stack is either + // [NULL, callable, arg1, arg2, ...] + // or + // [method, self, arg1, arg2, ...] + // (Some args may be keywords, see KW_NAMES, which sets 'kwnames'.) + // On exit, the stack is [result]. + // When calling Python, inline the call using DISPATCH_INLINED(). + inst(CALL, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) { + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } #if ENABLE_SPECIALIZATION _PyCallCache *cache = (_PyCallCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); - int is_meth = is_method(stack_pointer, oparg); - int nargs = oparg + is_meth; - PyObject *callable = PEEK(nargs + 1); next_instr--; - _Py_Specialize_Call(callable, next_instr, nargs, kwnames); + _Py_Specialize_Call(callable, next_instr, total_args, kwnames); DISPATCH_SAME_OPARG(); } STAT_INC(CALL, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - int total_args, is_meth; - is_meth = is_method(stack_pointer, oparg); - PyObject *function = PEEK(oparg + 1); - if (!is_meth && Py_TYPE(function) == &PyMethod_Type) { - PyObject *self = ((PyMethodObject *)function)->im_self; - PEEK(oparg+1) = Py_NewRef(self); - PyObject *meth = ((PyMethodObject *)function)->im_func; - PEEK(oparg+2) = Py_NewRef(meth); - Py_DECREF(function); - is_meth = 1; - } - total_args = oparg + is_meth; - function = PEEK(total_args + 1); + if (!is_meth && Py_TYPE(callable) == &PyMethod_Type) { + is_meth = 1; // For consistenct; it's dead, though + args--; + total_args++; + PyObject *self = ((PyMethodObject *)callable)->im_self; + args[0] = Py_NewRef(self); + method = ((PyMethodObject *)callable)->im_func; + args[-1] = Py_NewRef(method); + Py_DECREF(callable); + callable = method; + } int positional_args = total_args - KWNAMES_LEN(); // Check if the call can be inlined or not - if (Py_TYPE(function) == &PyFunction_Type && + if (Py_TYPE(callable) == &PyFunction_Type && tstate->interp->eval_frame == NULL && - ((PyFunctionObject *)function)->vectorcall == _PyFunction_Vectorcall) + ((PyFunctionObject *)callable)->vectorcall == _PyFunction_Vectorcall) { - int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(function))->co_flags; - PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(function)); - STACK_SHRINK(total_args); + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable)); _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( - tstate, (PyFunctionObject *)function, locals, - stack_pointer, positional_args, kwnames + tstate, (PyFunctionObject *)callable, locals, + args, positional_args, kwnames ); kwnames = NULL; - STACK_SHRINK(2-is_meth); + // Manipulate stack directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 2); // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. if (new_frame == NULL) { @@ -2433,190 +2450,180 @@ dummy_func( DISPATCH_INLINED(new_frame); } /* Callable is not a normal Python function */ - PyObject *res; if (cframe.use_tracing) { res = trace_call_function( - tstate, function, stack_pointer-total_args, + tstate, callable, args, positional_args, kwnames); } else { res = PyObject_Vectorcall( - function, stack_pointer-total_args, + callable, args, positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames); } kwnames = NULL; assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - Py_DECREF(function); - /* Clear the stack */ - STACK_SHRINK(total_args); + Py_DECREF(callable); for (int i = 0; i < total_args; i++) { - Py_DECREF(stack_pointer[i]); + Py_DECREF(args[i]); } - STACK_SHRINK(2-is_meth); - PUSH(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_PY_EXACT_ARGS) { + // Start out with [NULL, bound_method, arg1, arg2, ...] + // Transform to [callable, self, arg1, arg2, ...] + // Then fall through to CALL_PY_EXACT_ARGS + inst(CALL_BOUND_METHOD_EXACT_ARGS, (unused/1, unused/2, unused/1, method, callable, unused[oparg] -- unused)) { + DEOPT_IF(method != NULL, CALL); + DEOPT_IF(Py_TYPE(callable) != &PyMethod_Type, CALL); + STAT_INC(CALL, hit); + PyObject *self = ((PyMethodObject *)callable)->im_self; + PEEK(oparg + 1) = Py_NewRef(self); // callable + PyObject *meth = ((PyMethodObject *)callable)->im_func; + PEEK(oparg + 2) = Py_NewRef(meth); // method + Py_DECREF(callable); + GO_TO_INSTRUCTION(CALL_PY_EXACT_ARGS); + } + + inst(CALL_PY_EXACT_ARGS, (unused/1, func_version/2, unused/1, method, callable, args[oparg] -- unused)) { assert(kwnames == NULL); DEOPT_IF(tstate->interp->eval_frame, CALL); - _PyCallCache *cache = (_PyCallCache *)next_instr; - int is_meth = is_method(stack_pointer, oparg); - int argcount = oparg + is_meth; - PyObject *callable = PEEK(argcount + 1); + int is_meth = method != NULL; + int argcount = oparg; + if (is_meth) { + callable = method; + args--; + argcount++; + } DEOPT_IF(!PyFunction_Check(callable), CALL); PyFunctionObject *func = (PyFunctionObject *)callable; - DEOPT_IF(func->func_version != read_u32(cache->func_version), CALL); + DEOPT_IF(func->func_version != func_version, CALL); PyCodeObject *code = (PyCodeObject *)func->func_code; DEOPT_IF(code->co_argcount != argcount, CALL); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); STAT_INC(CALL, hit); _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, argcount); - STACK_SHRINK(argcount); for (int i = 0; i < argcount; i++) { - new_frame->localsplus[i] = stack_pointer[i]; + new_frame->localsplus[i] = args[i]; } - STACK_SHRINK(2-is_meth); + // Manipulate stack directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 2); JUMPBY(INLINE_CACHE_ENTRIES_CALL); DISPATCH_INLINED(new_frame); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_PY_WITH_DEFAULTS) { + inst(CALL_PY_WITH_DEFAULTS, (unused/1, func_version/2, min_args/1, method, callable, args[oparg] -- unused)) { assert(kwnames == NULL); DEOPT_IF(tstate->interp->eval_frame, CALL); - _PyCallCache *cache = (_PyCallCache *)next_instr; - int is_meth = is_method(stack_pointer, oparg); - int argcount = oparg + is_meth; - PyObject *callable = PEEK(argcount + 1); + int is_meth = method != NULL; + int argcount = oparg; + if (is_meth) { + callable = method; + args--; + argcount++; + } DEOPT_IF(!PyFunction_Check(callable), CALL); PyFunctionObject *func = (PyFunctionObject *)callable; - DEOPT_IF(func->func_version != read_u32(cache->func_version), CALL); + DEOPT_IF(func->func_version != func_version, CALL); PyCodeObject *code = (PyCodeObject *)func->func_code; DEOPT_IF(argcount > code->co_argcount, CALL); - int minargs = cache->min_args; - DEOPT_IF(argcount < minargs, CALL); + DEOPT_IF(argcount < min_args, CALL); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); STAT_INC(CALL, hit); _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, code->co_argcount); - STACK_SHRINK(argcount); for (int i = 0; i < argcount; i++) { - new_frame->localsplus[i] = stack_pointer[i]; + new_frame->localsplus[i] = args[i]; } for (int i = argcount; i < code->co_argcount; i++) { - PyObject *def = PyTuple_GET_ITEM(func->func_defaults, - i - minargs); + PyObject *def = PyTuple_GET_ITEM(func->func_defaults, i - min_args); new_frame->localsplus[i] = Py_NewRef(def); } - STACK_SHRINK(2-is_meth); + // Manipulate stack and cache directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 2); JUMPBY(INLINE_CACHE_ENTRIES_CALL); DISPATCH_INLINED(new_frame); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_TYPE_1) { + inst(CALL_NO_KW_TYPE_1, (unused/1, unused/2, unused/1, null, callable, args[oparg] -- res)) { assert(kwnames == NULL); assert(cframe.use_tracing == 0); assert(oparg == 1); - DEOPT_IF(is_method(stack_pointer, 1), CALL); - PyObject *obj = TOP(); - PyObject *callable = SECOND(); + DEOPT_IF(null != NULL, CALL); + PyObject *obj = args[0]; DEOPT_IF(callable != (PyObject *)&PyType_Type, CALL); STAT_INC(CALL, hit); - JUMPBY(INLINE_CACHE_ENTRIES_CALL); - PyObject *res = Py_NewRef(Py_TYPE(obj)); - Py_DECREF(callable); + res = Py_NewRef(Py_TYPE(obj)); Py_DECREF(obj); - STACK_SHRINK(2); - SET_TOP(res); + Py_DECREF(&PyType_Type); // I.e., callable } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_STR_1) { + inst(CALL_NO_KW_STR_1, (unused/1, unused/2, unused/1, null, callable, args[oparg] -- res)) { assert(kwnames == NULL); assert(cframe.use_tracing == 0); assert(oparg == 1); - DEOPT_IF(is_method(stack_pointer, 1), CALL); - PyObject *callable = PEEK(2); + DEOPT_IF(null != NULL, CALL); DEOPT_IF(callable != (PyObject *)&PyUnicode_Type, CALL); STAT_INC(CALL, hit); - PyObject *arg = TOP(); - PyObject *res = PyObject_Str(arg); + PyObject *arg = args[0]; + res = PyObject_Str(arg); Py_DECREF(arg); - Py_DECREF(&PyUnicode_Type); - STACK_SHRINK(2); - SET_TOP(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + Py_DECREF(&PyUnicode_Type); // I.e., callable + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_TUPLE_1) { + inst(CALL_NO_KW_TUPLE_1, (unused/1, unused/2, unused/1, null, callable, args[oparg] -- res)) { assert(kwnames == NULL); assert(oparg == 1); - DEOPT_IF(is_method(stack_pointer, 1), CALL); - PyObject *callable = PEEK(2); + DEOPT_IF(null != NULL, CALL); DEOPT_IF(callable != (PyObject *)&PyTuple_Type, CALL); STAT_INC(CALL, hit); - PyObject *arg = TOP(); - PyObject *res = PySequence_Tuple(arg); + PyObject *arg = args[0]; + res = PySequence_Tuple(arg); Py_DECREF(arg); - Py_DECREF(&PyTuple_Type); - STACK_SHRINK(2); - SET_TOP(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + Py_DECREF(&PyTuple_Type); // I.e., tuple + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_BUILTIN_CLASS) { - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + inst(CALL_BUILTIN_CLASS, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) { + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } int kwnames_len = KWNAMES_LEN(); - PyObject *callable = PEEK(total_args + 1); DEOPT_IF(!PyType_Check(callable), CALL); PyTypeObject *tp = (PyTypeObject *)callable; DEOPT_IF(tp->tp_vectorcall == NULL, CALL); STAT_INC(CALL, hit); - STACK_SHRINK(total_args); - PyObject *res = tp->tp_vectorcall((PyObject *)tp, stack_pointer, - total_args-kwnames_len, kwnames); + res = tp->tp_vectorcall((PyObject *)tp, args, + total_args - kwnames_len, kwnames); kwnames = NULL; /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - Py_DECREF(stack_pointer[i]); + Py_DECREF(args[i]); } Py_DECREF(tp); - STACK_SHRINK(1-is_meth); - SET_TOP(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_BUILTIN_O) { + inst(CALL_NO_KW_BUILTIN_O, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) { assert(cframe.use_tracing == 0); /* Builtin METH_O functions */ assert(kwnames == NULL); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(total_args != 1, CALL); - PyObject *callable = PEEK(total_args + 1); DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_O, CALL); STAT_INC(CALL, hit); @@ -2626,81 +2633,74 @@ dummy_func( if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { goto error; } - PyObject *arg = TOP(); - PyObject *res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg); + PyObject *arg = args[0]; + res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg); _Py_LeaveRecursiveCallTstate(tstate); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(arg); Py_DECREF(callable); - STACK_SHRINK(2-is_meth); - SET_TOP(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_BUILTIN_FAST) { + inst(CALL_NO_KW_BUILTIN_FAST, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) { assert(cframe.use_tracing == 0); /* Builtin METH_FASTCALL functions, without keywords */ assert(kwnames == NULL); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; - PyObject *callable = PEEK(total_args + 1); + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); - DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_FASTCALL, - CALL); + DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_FASTCALL, CALL); STAT_INC(CALL, hit); PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); - STACK_SHRINK(total_args); /* res = func(self, args, nargs) */ - PyObject *res = ((_PyCFunctionFast)(void(*)(void))cfunc)( + res = ((_PyCFunctionFast)(void(*)(void))cfunc)( PyCFunction_GET_SELF(callable), - stack_pointer, + args, total_args); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - Py_DECREF(stack_pointer[i]); + Py_DECREF(args[i]); } - STACK_SHRINK(2-is_meth); - PUSH(res); Py_DECREF(callable); - if (res == NULL) { + ERROR_IF(res == NULL, error); /* Not deopting because this doesn't mean our optimization was wrong. `res` can be NULL for valid reasons. Eg. getattr(x, 'invalid'). In those cases an exception is set, so we must handle it. */ - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_BUILTIN_FAST_WITH_KEYWORDS) { + inst(CALL_BUILTIN_FAST_WITH_KEYWORDS, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) { assert(cframe.use_tracing == 0); /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; - PyObject *callable = PEEK(total_args + 1); + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); DEOPT_IF(PyCFunction_GET_FLAGS(callable) != (METH_FASTCALL | METH_KEYWORDS), CALL); STAT_INC(CALL, hit); - STACK_SHRINK(total_args); /* res = func(self, args, nargs, kwnames) */ _PyCFunctionFastWithKeywords cfunc = (_PyCFunctionFastWithKeywords)(void(*)(void)) PyCFunction_GET_FUNCTION(callable); - PyObject *res = cfunc( + res = cfunc( PyCFunction_GET_SELF(callable), - stack_pointer, + args, total_args - KWNAMES_LEN(), kwnames ); @@ -2709,117 +2709,109 @@ dummy_func( /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - Py_DECREF(stack_pointer[i]); + Py_DECREF(args[i]); } - STACK_SHRINK(2-is_meth); - PUSH(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_LEN) { + inst(CALL_NO_KW_LEN, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) { assert(cframe.use_tracing == 0); assert(kwnames == NULL); /* len(o) */ - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(total_args != 1, CALL); - PyObject *callable = PEEK(total_args + 1); PyInterpreterState *interp = _PyInterpreterState_GET(); DEOPT_IF(callable != interp->callable_cache.len, CALL); STAT_INC(CALL, hit); - PyObject *arg = TOP(); + PyObject *arg = args[0]; Py_ssize_t len_i = PyObject_Length(arg); if (len_i < 0) { goto error; } - PyObject *res = PyLong_FromSsize_t(len_i); + res = PyLong_FromSsize_t(len_i); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - STACK_SHRINK(2-is_meth); - SET_TOP(res); Py_DECREF(callable); Py_DECREF(arg); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_ISINSTANCE) { + inst(CALL_NO_KW_ISINSTANCE, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) { assert(cframe.use_tracing == 0); assert(kwnames == NULL); /* isinstance(o, o2) */ - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; - PyObject *callable = PEEK(total_args + 1); + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(total_args != 2, CALL); PyInterpreterState *interp = _PyInterpreterState_GET(); DEOPT_IF(callable != interp->callable_cache.isinstance, CALL); STAT_INC(CALL, hit); - PyObject *cls = POP(); - PyObject *inst = TOP(); + PyObject *cls = args[1]; + PyObject *inst = args[0]; int retval = PyObject_IsInstance(inst, cls); if (retval < 0) { - Py_DECREF(cls); goto error; } - PyObject *res = PyBool_FromLong(retval); + res = PyBool_FromLong(retval); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - STACK_SHRINK(2-is_meth); - SET_TOP(res); Py_DECREF(inst); Py_DECREF(cls); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_LIST_APPEND) { + // This is secretly a super-instruction + inst(CALL_NO_KW_LIST_APPEND, (unused/1, unused/2, unused/1, method, self, args[oparg] -- unused)) { assert(cframe.use_tracing == 0); assert(kwnames == NULL); assert(oparg == 1); - PyObject *callable = PEEK(3); + assert(method != NULL); PyInterpreterState *interp = _PyInterpreterState_GET(); - DEOPT_IF(callable != interp->callable_cache.list_append, CALL); - PyObject *list = SECOND(); - DEOPT_IF(!PyList_Check(list), CALL); + DEOPT_IF(method != interp->callable_cache.list_append, CALL); + DEOPT_IF(!PyList_Check(self), CALL); STAT_INC(CALL, hit); - PyObject *arg = POP(); - if (_PyList_AppendTakeRef((PyListObject *)list, arg) < 0) { - goto error; + if (_PyList_AppendTakeRef((PyListObject *)self, args[0]) < 0) { + goto pop_1_error; // Since arg is DECREF'ed already } - STACK_SHRINK(2); - Py_DECREF(list); - Py_DECREF(callable); + Py_DECREF(self); + Py_DECREF(method); + STACK_SHRINK(3); // CALL + POP_TOP JUMPBY(INLINE_CACHE_ENTRIES_CALL + 1); assert(_Py_OPCODE(next_instr[-1]) == POP_TOP); + DISPATCH(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_METHOD_DESCRIPTOR_O) { + inst(CALL_NO_KW_METHOD_DESCRIPTOR_O, (unused/1, unused/2, unused/1, method, unused, args[oparg] -- res)) { assert(kwnames == NULL); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } PyMethodDescrObject *callable = (PyMethodDescrObject *)PEEK(total_args + 1); DEOPT_IF(total_args != 2, CALL); DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); PyMethodDef *meth = callable->d_method; DEOPT_IF(meth->ml_flags != METH_O, CALL); - PyObject *arg = TOP(); - PyObject *self = SECOND(); + PyObject *arg = args[1]; + PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL); STAT_INC(CALL, hit); PyCFunction cfunc = meth->ml_meth; @@ -2828,69 +2820,62 @@ dummy_func( if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { goto error; } - PyObject *res = _PyCFunction_TrampolineCall(cfunc, self, arg); + res = _PyCFunction_TrampolineCall(cfunc, self, arg); _Py_LeaveRecursiveCallTstate(tstate); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(self); Py_DECREF(arg); - STACK_SHRINK(oparg + 1); - SET_TOP(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) { - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + inst(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, (unused/1, unused/2, unused/1, method, unused, args[oparg] -- res)) { + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } PyMethodDescrObject *callable = (PyMethodDescrObject *)PEEK(total_args + 1); DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); PyMethodDef *meth = callable->d_method; DEOPT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS), CALL); PyTypeObject *d_type = callable->d_common.d_type; - PyObject *self = PEEK(total_args); + PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL); STAT_INC(CALL, hit); - int nargs = total_args-1; - STACK_SHRINK(nargs); + int nargs = total_args - 1; _PyCFunctionFastWithKeywords cfunc = (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; - PyObject *res = cfunc(self, stack_pointer, nargs - KWNAMES_LEN(), - kwnames); + res = cfunc(self, args + 1, nargs - KWNAMES_LEN(), kwnames); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); kwnames = NULL; /* Free the arguments. */ - for (int i = 0; i < nargs; i++) { - Py_DECREF(stack_pointer[i]); + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); } - Py_DECREF(self); - STACK_SHRINK(2-is_meth); - SET_TOP(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS) { + inst(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, (unused/1, unused/2, unused/1, method, unused, args[oparg] -- res)) { assert(kwnames == NULL); assert(oparg == 0 || oparg == 1); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } DEOPT_IF(total_args != 1, CALL); PyMethodDescrObject *callable = (PyMethodDescrObject *)SECOND(); DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); PyMethodDef *meth = callable->d_method; - PyObject *self = TOP(); + PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL); DEOPT_IF(meth->ml_flags != METH_NOARGS, CALL); STAT_INC(CALL, hit); @@ -2900,52 +2885,43 @@ dummy_func( if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { goto error; } - PyObject *res = _PyCFunction_TrampolineCall(cfunc, self, NULL); + res = _PyCFunction_TrampolineCall(cfunc, self, NULL); _Py_LeaveRecursiveCallTstate(tstate); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(self); - STACK_SHRINK(oparg + 1); - SET_TOP(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } - // stack effect: (__0, __array[oparg] -- ) - inst(CALL_NO_KW_METHOD_DESCRIPTOR_FAST) { + inst(CALL_NO_KW_METHOD_DESCRIPTOR_FAST, (unused/1, unused/2, unused/1, method, unused, args[oparg] -- res)) { assert(kwnames == NULL); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } PyMethodDescrObject *callable = (PyMethodDescrObject *)PEEK(total_args + 1); /* Builtin METH_FASTCALL methods, without keywords */ DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); PyMethodDef *meth = callable->d_method; DEOPT_IF(meth->ml_flags != METH_FASTCALL, CALL); - PyObject *self = PEEK(total_args); + PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL); STAT_INC(CALL, hit); _PyCFunctionFast cfunc = (_PyCFunctionFast)(void(*)(void))meth->ml_meth; - int nargs = total_args-1; - STACK_SHRINK(nargs); - PyObject *res = cfunc(self, stack_pointer, nargs); + int nargs = total_args - 1; + res = cfunc(self, args + 1, nargs); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); /* Clear the stack of the arguments. */ - for (int i = 0; i < nargs; i++) { - Py_DECREF(stack_pointer[i]); + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); } - Py_DECREF(self); - STACK_SHRINK(2-is_meth); - SET_TOP(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + ERROR_IF(res == NULL, error); CHECK_EVAL_BREAKER(); } @@ -3153,12 +3129,4 @@ dummy_func( // Future families go below this point // -family(call, INLINE_CACHE_ENTRIES_CALL) = { - CALL, CALL_PY_EXACT_ARGS, - CALL_PY_WITH_DEFAULTS, CALL_BOUND_METHOD_EXACT_ARGS, CALL_BUILTIN_CLASS, - CALL_BUILTIN_FAST_WITH_KEYWORDS, CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, CALL_NO_KW_BUILTIN_FAST, - CALL_NO_KW_BUILTIN_O, CALL_NO_KW_ISINSTANCE, CALL_NO_KW_LEN, - CALL_NO_KW_LIST_APPEND, CALL_NO_KW_METHOD_DESCRIPTOR_FAST, CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, - CALL_NO_KW_METHOD_DESCRIPTOR_O, CALL_NO_KW_STR_1, CALL_NO_KW_TUPLE_1, - CALL_NO_KW_TYPE_1 }; family(store_fast) = { STORE_FAST, STORE_FAST__LOAD_FAST, STORE_FAST__STORE_FAST }; diff --git a/Python/ceval.c b/Python/ceval.c index a91f5baca8853e..611d62b0eba9af 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -688,12 +688,6 @@ static inline void _Py_LeaveRecursiveCallPy(PyThreadState *tstate) { } -// GH-89279: Must be a macro to be sure it's inlined by MSVC. -#define is_method(stack_pointer, args) (PEEK((args)+2) != NULL) - -#define KWNAMES_LEN() \ - (kwnames == NULL ? 0 : ((int)PyTuple_GET_SIZE(kwnames))) - /* Disable unused label warnings. They are handy for debugging, even if computed gotos aren't used. */ diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index d7a8f0beeec872..691bf8e1caae95 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -347,3 +347,6 @@ GETITEM(PyObject *v, Py_ssize_t i) { } while (0); #define NAME_ERROR_MSG "name '%.200s' is not defined" + +#define KWNAMES_LEN() \ + (kwnames == NULL ? 0 : ((int)PyTuple_GET_SIZE(kwnames))) diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 09eb6893ebf6b4..a224d4eb892785 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2994,19 +2994,6 @@ DISPATCH(); } - TARGET(CALL_BOUND_METHOD_EXACT_ARGS) { - DEOPT_IF(is_method(stack_pointer, oparg), CALL); - PyObject *function = PEEK(oparg + 1); - DEOPT_IF(Py_TYPE(function) != &PyMethod_Type, CALL); - STAT_INC(CALL, hit); - PyObject *self = ((PyMethodObject *)function)->im_self; - PEEK(oparg + 1) = Py_NewRef(self); - PyObject *meth = ((PyMethodObject *)function)->im_func; - PEEK(oparg + 2) = Py_NewRef(meth); - Py_DECREF(function); - GO_TO_INSTRUCTION(CALL_PY_EXACT_ARGS); - } - TARGET(KW_NAMES) { assert(kwnames == NULL); assert(oparg < PyTuple_GET_SIZE(consts)); @@ -3016,48 +3003,55 @@ TARGET(CALL) { PREDICTED(CALL); + static_assert(INLINE_CACHE_ENTRIES_CALL == 4, "incorrect cache size"); + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } #if ENABLE_SPECIALIZATION _PyCallCache *cache = (_PyCallCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); - int is_meth = is_method(stack_pointer, oparg); - int nargs = oparg + is_meth; - PyObject *callable = PEEK(nargs + 1); next_instr--; - _Py_Specialize_Call(callable, next_instr, nargs, kwnames); + _Py_Specialize_Call(callable, next_instr, total_args, kwnames); DISPATCH_SAME_OPARG(); } STAT_INC(CALL, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - int total_args, is_meth; - is_meth = is_method(stack_pointer, oparg); - PyObject *function = PEEK(oparg + 1); - if (!is_meth && Py_TYPE(function) == &PyMethod_Type) { - PyObject *self = ((PyMethodObject *)function)->im_self; - PEEK(oparg+1) = Py_NewRef(self); - PyObject *meth = ((PyMethodObject *)function)->im_func; - PEEK(oparg+2) = Py_NewRef(meth); - Py_DECREF(function); - is_meth = 1; - } - total_args = oparg + is_meth; - function = PEEK(total_args + 1); + if (!is_meth && Py_TYPE(callable) == &PyMethod_Type) { + is_meth = 1; // For consistenct; it's dead, though + args--; + total_args++; + PyObject *self = ((PyMethodObject *)callable)->im_self; + args[0] = Py_NewRef(self); + method = ((PyMethodObject *)callable)->im_func; + args[-1] = Py_NewRef(method); + Py_DECREF(callable); + callable = method; + } int positional_args = total_args - KWNAMES_LEN(); // Check if the call can be inlined or not - if (Py_TYPE(function) == &PyFunction_Type && + if (Py_TYPE(callable) == &PyFunction_Type && tstate->interp->eval_frame == NULL && - ((PyFunctionObject *)function)->vectorcall == _PyFunction_Vectorcall) + ((PyFunctionObject *)callable)->vectorcall == _PyFunction_Vectorcall) { - int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(function))->co_flags; - PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(function)); - STACK_SHRINK(total_args); + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable)); _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( - tstate, (PyFunctionObject *)function, locals, - stack_pointer, positional_args, kwnames + tstate, (PyFunctionObject *)callable, locals, + args, positional_args, kwnames ); kwnames = NULL; - STACK_SHRINK(2-is_meth); + // Manipulate stack directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 2); // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. if (new_frame == NULL) { @@ -3067,189 +3061,234 @@ DISPATCH_INLINED(new_frame); } /* Callable is not a normal Python function */ - PyObject *res; if (cframe.use_tracing) { res = trace_call_function( - tstate, function, stack_pointer-total_args, + tstate, callable, args, positional_args, kwnames); } else { res = PyObject_Vectorcall( - function, stack_pointer-total_args, + callable, args, positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames); } kwnames = NULL; assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - Py_DECREF(function); - /* Clear the stack */ - STACK_SHRINK(total_args); + Py_DECREF(callable); for (int i = 0; i < total_args; i++) { - Py_DECREF(stack_pointer[i]); + Py_DECREF(args[i]); } - STACK_SHRINK(2-is_meth); - PUSH(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } + TARGET(CALL_BOUND_METHOD_EXACT_ARGS) { + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + DEOPT_IF(method != NULL, CALL); + DEOPT_IF(Py_TYPE(callable) != &PyMethod_Type, CALL); + STAT_INC(CALL, hit); + PyObject *self = ((PyMethodObject *)callable)->im_self; + PEEK(oparg + 1) = Py_NewRef(self); // callable + PyObject *meth = ((PyMethodObject *)callable)->im_func; + PEEK(oparg + 2) = Py_NewRef(meth); // method + Py_DECREF(callable); + GO_TO_INSTRUCTION(CALL_PY_EXACT_ARGS); + } + TARGET(CALL_PY_EXACT_ARGS) { PREDICTED(CALL_PY_EXACT_ARGS); + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + uint32_t func_version = read_u32(&next_instr[1].cache); assert(kwnames == NULL); DEOPT_IF(tstate->interp->eval_frame, CALL); - _PyCallCache *cache = (_PyCallCache *)next_instr; - int is_meth = is_method(stack_pointer, oparg); - int argcount = oparg + is_meth; - PyObject *callable = PEEK(argcount + 1); + int is_meth = method != NULL; + int argcount = oparg; + if (is_meth) { + callable = method; + args--; + argcount++; + } DEOPT_IF(!PyFunction_Check(callable), CALL); PyFunctionObject *func = (PyFunctionObject *)callable; - DEOPT_IF(func->func_version != read_u32(cache->func_version), CALL); + DEOPT_IF(func->func_version != func_version, CALL); PyCodeObject *code = (PyCodeObject *)func->func_code; DEOPT_IF(code->co_argcount != argcount, CALL); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); STAT_INC(CALL, hit); _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, argcount); - STACK_SHRINK(argcount); for (int i = 0; i < argcount; i++) { - new_frame->localsplus[i] = stack_pointer[i]; + new_frame->localsplus[i] = args[i]; } - STACK_SHRINK(2-is_meth); + // Manipulate stack directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 2); JUMPBY(INLINE_CACHE_ENTRIES_CALL); DISPATCH_INLINED(new_frame); } TARGET(CALL_PY_WITH_DEFAULTS) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + uint32_t func_version = read_u32(&next_instr[1].cache); + uint16_t min_args = read_u16(&next_instr[3].cache); assert(kwnames == NULL); DEOPT_IF(tstate->interp->eval_frame, CALL); - _PyCallCache *cache = (_PyCallCache *)next_instr; - int is_meth = is_method(stack_pointer, oparg); - int argcount = oparg + is_meth; - PyObject *callable = PEEK(argcount + 1); + int is_meth = method != NULL; + int argcount = oparg; + if (is_meth) { + callable = method; + args--; + argcount++; + } DEOPT_IF(!PyFunction_Check(callable), CALL); PyFunctionObject *func = (PyFunctionObject *)callable; - DEOPT_IF(func->func_version != read_u32(cache->func_version), CALL); + DEOPT_IF(func->func_version != func_version, CALL); PyCodeObject *code = (PyCodeObject *)func->func_code; DEOPT_IF(argcount > code->co_argcount, CALL); - int minargs = cache->min_args; - DEOPT_IF(argcount < minargs, CALL); + DEOPT_IF(argcount < min_args, CALL); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); STAT_INC(CALL, hit); _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, code->co_argcount); - STACK_SHRINK(argcount); for (int i = 0; i < argcount; i++) { - new_frame->localsplus[i] = stack_pointer[i]; + new_frame->localsplus[i] = args[i]; } for (int i = argcount; i < code->co_argcount; i++) { - PyObject *def = PyTuple_GET_ITEM(func->func_defaults, - i - minargs); + PyObject *def = PyTuple_GET_ITEM(func->func_defaults, i - min_args); new_frame->localsplus[i] = Py_NewRef(def); } - STACK_SHRINK(2-is_meth); + // Manipulate stack and cache directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 2); JUMPBY(INLINE_CACHE_ENTRIES_CALL); DISPATCH_INLINED(new_frame); } TARGET(CALL_NO_KW_TYPE_1) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *null = PEEK(2 + oparg); + PyObject *res; assert(kwnames == NULL); assert(cframe.use_tracing == 0); assert(oparg == 1); - DEOPT_IF(is_method(stack_pointer, 1), CALL); - PyObject *obj = TOP(); - PyObject *callable = SECOND(); + DEOPT_IF(null != NULL, CALL); + PyObject *obj = args[0]; DEOPT_IF(callable != (PyObject *)&PyType_Type, CALL); STAT_INC(CALL, hit); - JUMPBY(INLINE_CACHE_ENTRIES_CALL); - PyObject *res = Py_NewRef(Py_TYPE(obj)); - Py_DECREF(callable); + res = Py_NewRef(Py_TYPE(obj)); Py_DECREF(obj); - STACK_SHRINK(2); - SET_TOP(res); + Py_DECREF(&PyType_Type); // I.e., callable + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); DISPATCH(); } TARGET(CALL_NO_KW_STR_1) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *null = PEEK(2 + oparg); + PyObject *res; assert(kwnames == NULL); assert(cframe.use_tracing == 0); assert(oparg == 1); - DEOPT_IF(is_method(stack_pointer, 1), CALL); - PyObject *callable = PEEK(2); + DEOPT_IF(null != NULL, CALL); DEOPT_IF(callable != (PyObject *)&PyUnicode_Type, CALL); STAT_INC(CALL, hit); - PyObject *arg = TOP(); - PyObject *res = PyObject_Str(arg); + PyObject *arg = args[0]; + res = PyObject_Str(arg); Py_DECREF(arg); - Py_DECREF(&PyUnicode_Type); - STACK_SHRINK(2); - SET_TOP(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + Py_DECREF(&PyUnicode_Type); // I.e., callable + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_TUPLE_1) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *null = PEEK(2 + oparg); + PyObject *res; assert(kwnames == NULL); assert(oparg == 1); - DEOPT_IF(is_method(stack_pointer, 1), CALL); - PyObject *callable = PEEK(2); + DEOPT_IF(null != NULL, CALL); DEOPT_IF(callable != (PyObject *)&PyTuple_Type, CALL); STAT_INC(CALL, hit); - PyObject *arg = TOP(); - PyObject *res = PySequence_Tuple(arg); + PyObject *arg = args[0]; + res = PySequence_Tuple(arg); Py_DECREF(arg); - Py_DECREF(&PyTuple_Type); - STACK_SHRINK(2); - SET_TOP(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + Py_DECREF(&PyTuple_Type); // I.e., tuple + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_BUILTIN_CLASS) { - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } int kwnames_len = KWNAMES_LEN(); - PyObject *callable = PEEK(total_args + 1); DEOPT_IF(!PyType_Check(callable), CALL); PyTypeObject *tp = (PyTypeObject *)callable; DEOPT_IF(tp->tp_vectorcall == NULL, CALL); STAT_INC(CALL, hit); - STACK_SHRINK(total_args); - PyObject *res = tp->tp_vectorcall((PyObject *)tp, stack_pointer, - total_args-kwnames_len, kwnames); + res = tp->tp_vectorcall((PyObject *)tp, args, + total_args - kwnames_len, kwnames); kwnames = NULL; /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - Py_DECREF(stack_pointer[i]); + Py_DECREF(args[i]); } Py_DECREF(tp); - STACK_SHRINK(1-is_meth); - SET_TOP(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_BUILTIN_O) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; assert(cframe.use_tracing == 0); /* Builtin METH_O functions */ assert(kwnames == NULL); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(total_args != 1, CALL); - PyObject *callable = PEEK(total_args + 1); DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_O, CALL); STAT_INC(CALL, hit); @@ -3259,81 +3298,92 @@ if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { goto error; } - PyObject *arg = TOP(); - PyObject *res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg); + PyObject *arg = args[0]; + res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg); _Py_LeaveRecursiveCallTstate(tstate); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(arg); Py_DECREF(callable); - STACK_SHRINK(2-is_meth); - SET_TOP(res); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_BUILTIN_FAST) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; assert(cframe.use_tracing == 0); /* Builtin METH_FASTCALL functions, without keywords */ assert(kwnames == NULL); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; - PyObject *callable = PEEK(total_args + 1); + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); - DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_FASTCALL, - CALL); + DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_FASTCALL, CALL); STAT_INC(CALL, hit); PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); - STACK_SHRINK(total_args); /* res = func(self, args, nargs) */ - PyObject *res = ((_PyCFunctionFast)(void(*)(void))cfunc)( + res = ((_PyCFunctionFast)(void(*)(void))cfunc)( PyCFunction_GET_SELF(callable), - stack_pointer, + args, total_args); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - Py_DECREF(stack_pointer[i]); + Py_DECREF(args[i]); } - STACK_SHRINK(2-is_meth); - PUSH(res); Py_DECREF(callable); - if (res == NULL) { + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } /* Not deopting because this doesn't mean our optimization was wrong. `res` can be NULL for valid reasons. Eg. getattr(x, 'invalid'). In those cases an exception is set, so we must handle it. */ - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; assert(cframe.use_tracing == 0); /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; - PyObject *callable = PEEK(total_args + 1); + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); DEOPT_IF(PyCFunction_GET_FLAGS(callable) != (METH_FASTCALL | METH_KEYWORDS), CALL); STAT_INC(CALL, hit); - STACK_SHRINK(total_args); /* res = func(self, args, nargs, kwnames) */ _PyCFunctionFastWithKeywords cfunc = (_PyCFunctionFastWithKeywords)(void(*)(void)) PyCFunction_GET_FUNCTION(callable); - PyObject *res = cfunc( + res = cfunc( PyCFunction_GET_SELF(callable), - stack_pointer, + args, total_args - KWNAMES_LEN(), kwnames ); @@ -3342,99 +3392,112 @@ /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - Py_DECREF(stack_pointer[i]); + Py_DECREF(args[i]); } - STACK_SHRINK(2-is_meth); - PUSH(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_LEN) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; assert(cframe.use_tracing == 0); assert(kwnames == NULL); /* len(o) */ - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(total_args != 1, CALL); - PyObject *callable = PEEK(total_args + 1); PyInterpreterState *interp = _PyInterpreterState_GET(); DEOPT_IF(callable != interp->callable_cache.len, CALL); STAT_INC(CALL, hit); - PyObject *arg = TOP(); + PyObject *arg = args[0]; Py_ssize_t len_i = PyObject_Length(arg); if (len_i < 0) { goto error; } - PyObject *res = PyLong_FromSsize_t(len_i); + res = PyLong_FromSsize_t(len_i); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - STACK_SHRINK(2-is_meth); - SET_TOP(res); Py_DECREF(callable); Py_DECREF(arg); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); DISPATCH(); } TARGET(CALL_NO_KW_ISINSTANCE) { + PyObject **args = &PEEK(oparg); + PyObject *callable = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; assert(cframe.use_tracing == 0); assert(kwnames == NULL); /* isinstance(o, o2) */ - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; - PyObject *callable = PEEK(total_args + 1); + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + callable = method; + args--; + total_args++; + } DEOPT_IF(total_args != 2, CALL); PyInterpreterState *interp = _PyInterpreterState_GET(); DEOPT_IF(callable != interp->callable_cache.isinstance, CALL); STAT_INC(CALL, hit); - PyObject *cls = POP(); - PyObject *inst = TOP(); + PyObject *cls = args[1]; + PyObject *inst = args[0]; int retval = PyObject_IsInstance(inst, cls); if (retval < 0) { - Py_DECREF(cls); goto error; } - PyObject *res = PyBool_FromLong(retval); + res = PyBool_FromLong(retval); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - STACK_SHRINK(2-is_meth); - SET_TOP(res); Py_DECREF(inst); Py_DECREF(cls); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); DISPATCH(); } TARGET(CALL_NO_KW_LIST_APPEND) { + PyObject **args = &PEEK(oparg); + PyObject *self = PEEK(1 + oparg); + PyObject *method = PEEK(2 + oparg); assert(cframe.use_tracing == 0); assert(kwnames == NULL); assert(oparg == 1); - PyObject *callable = PEEK(3); + assert(method != NULL); PyInterpreterState *interp = _PyInterpreterState_GET(); - DEOPT_IF(callable != interp->callable_cache.list_append, CALL); - PyObject *list = SECOND(); - DEOPT_IF(!PyList_Check(list), CALL); + DEOPT_IF(method != interp->callable_cache.list_append, CALL); + DEOPT_IF(!PyList_Check(self), CALL); STAT_INC(CALL, hit); - PyObject *arg = POP(); - if (_PyList_AppendTakeRef((PyListObject *)list, arg) < 0) { - goto error; + if (_PyList_AppendTakeRef((PyListObject *)self, args[0]) < 0) { + goto pop_1_error; // Since arg is DECREF'ed already } - STACK_SHRINK(2); - Py_DECREF(list); - Py_DECREF(callable); + Py_DECREF(self); + Py_DECREF(method); + STACK_SHRINK(3); // CALL + POP_TOP JUMPBY(INLINE_CACHE_ENTRIES_CALL + 1); assert(_Py_OPCODE(next_instr[-1]) == POP_TOP); @@ -3442,17 +3505,24 @@ } TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_O) { + PyObject **args = &PEEK(oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; assert(kwnames == NULL); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } PyMethodDescrObject *callable = (PyMethodDescrObject *)PEEK(total_args + 1); DEOPT_IF(total_args != 2, CALL); DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); PyMethodDef *meth = callable->d_method; DEOPT_IF(meth->ml_flags != METH_O, CALL); - PyObject *arg = TOP(); - PyObject *self = SECOND(); + PyObject *arg = args[1]; + PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL); STAT_INC(CALL, hit); PyCFunction cfunc = meth->ml_meth; @@ -3461,69 +3531,78 @@ if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { goto error; } - PyObject *res = _PyCFunction_TrampolineCall(cfunc, self, arg); + res = _PyCFunction_TrampolineCall(cfunc, self, arg); _Py_LeaveRecursiveCallTstate(tstate); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(self); Py_DECREF(arg); - STACK_SHRINK(oparg + 1); - SET_TOP(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) { - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + PyObject **args = &PEEK(oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } PyMethodDescrObject *callable = (PyMethodDescrObject *)PEEK(total_args + 1); DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); PyMethodDef *meth = callable->d_method; DEOPT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS), CALL); PyTypeObject *d_type = callable->d_common.d_type; - PyObject *self = PEEK(total_args); + PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL); STAT_INC(CALL, hit); - int nargs = total_args-1; - STACK_SHRINK(nargs); + int nargs = total_args - 1; _PyCFunctionFastWithKeywords cfunc = (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; - PyObject *res = cfunc(self, stack_pointer, nargs - KWNAMES_LEN(), - kwnames); + res = cfunc(self, args + 1, nargs - KWNAMES_LEN(), kwnames); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); kwnames = NULL; /* Free the arguments. */ - for (int i = 0; i < nargs; i++) { - Py_DECREF(stack_pointer[i]); + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); } - Py_DECREF(self); - STACK_SHRINK(2-is_meth); - SET_TOP(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS) { + PyObject **args = &PEEK(oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; assert(kwnames == NULL); assert(oparg == 0 || oparg == 1); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } DEOPT_IF(total_args != 1, CALL); PyMethodDescrObject *callable = (PyMethodDescrObject *)SECOND(); DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); PyMethodDef *meth = callable->d_method; - PyObject *self = TOP(); + PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL); DEOPT_IF(meth->ml_flags != METH_NOARGS, CALL); STAT_INC(CALL, hit); @@ -3533,52 +3612,55 @@ if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { goto error; } - PyObject *res = _PyCFunction_TrampolineCall(cfunc, self, NULL); + res = _PyCFunction_TrampolineCall(cfunc, self, NULL); _Py_LeaveRecursiveCallTstate(tstate); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(self); - STACK_SHRINK(oparg + 1); - SET_TOP(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_FAST) { + PyObject **args = &PEEK(oparg); + PyObject *method = PEEK(2 + oparg); + PyObject *res; assert(kwnames == NULL); - int is_meth = is_method(stack_pointer, oparg); - int total_args = oparg + is_meth; + int is_meth = method != NULL; + int total_args = oparg; + if (is_meth) { + args--; + total_args++; + } PyMethodDescrObject *callable = (PyMethodDescrObject *)PEEK(total_args + 1); /* Builtin METH_FASTCALL methods, without keywords */ DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL); PyMethodDef *meth = callable->d_method; DEOPT_IF(meth->ml_flags != METH_FASTCALL, CALL); - PyObject *self = PEEK(total_args); + PyObject *self = args[0]; DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL); STAT_INC(CALL, hit); _PyCFunctionFast cfunc = (_PyCFunctionFast)(void(*)(void))meth->ml_meth; - int nargs = total_args-1; - STACK_SHRINK(nargs); - PyObject *res = cfunc(self, stack_pointer, nargs); + int nargs = total_args - 1; + res = cfunc(self, args + 1, nargs); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); /* Clear the stack of the arguments. */ - for (int i = 0; i < nargs; i++) { - Py_DECREF(stack_pointer[i]); + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); } - Py_DECREF(self); - STACK_SHRINK(2-is_meth); - SET_TOP(res); Py_DECREF(callable); - if (res == NULL) { - goto error; - } - JUMPBY(INLINE_CACHE_ENTRIES_CALL); + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + POKE(1, res); + JUMPBY(4); CHECK_EVAL_BREAKER(); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 054ef6c2998234..98791043f55271 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -286,44 +286,44 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 1; case LOAD_ATTR_METHOD_LAZY_DICT: return 1; - case CALL_BOUND_METHOD_EXACT_ARGS: - return -1; case KW_NAMES: return 0; case CALL: - return -1; + return oparg + 2; + case CALL_BOUND_METHOD_EXACT_ARGS: + return oparg + 2; case CALL_PY_EXACT_ARGS: - return -1; + return oparg + 2; case CALL_PY_WITH_DEFAULTS: - return -1; + return oparg + 2; case CALL_NO_KW_TYPE_1: - return -1; + return oparg + 2; case CALL_NO_KW_STR_1: - return -1; + return oparg + 2; case CALL_NO_KW_TUPLE_1: - return -1; + return oparg + 2; case CALL_BUILTIN_CLASS: - return -1; + return oparg + 2; case CALL_NO_KW_BUILTIN_O: - return -1; + return oparg + 2; case CALL_NO_KW_BUILTIN_FAST: - return -1; + return oparg + 2; case CALL_BUILTIN_FAST_WITH_KEYWORDS: - return -1; + return oparg + 2; case CALL_NO_KW_LEN: - return -1; + return oparg + 2; case CALL_NO_KW_ISINSTANCE: - return -1; + return oparg + 2; case CALL_NO_KW_LIST_APPEND: - return -1; + return oparg + 2; case CALL_NO_KW_METHOD_DESCRIPTOR_O: - return -1; + return oparg + 2; case CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: - return -1; + return oparg + 2; case CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS: - return -1; + return oparg + 2; case CALL_NO_KW_METHOD_DESCRIPTOR_FAST: - return -1; + return oparg + 2; case CALL_FUNCTION_EX: return ((oparg & 1) ? 1 : 0) + 3; case MAKE_FUNCTION: @@ -634,44 +634,44 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return ((oparg & 1) ? 1 : 0) + 1; case LOAD_ATTR_METHOD_LAZY_DICT: return ((oparg & 1) ? 1 : 0) + 1; - case CALL_BOUND_METHOD_EXACT_ARGS: - return -1; case KW_NAMES: return 0; case CALL: - return -1; + return 1; + case CALL_BOUND_METHOD_EXACT_ARGS: + return 1; case CALL_PY_EXACT_ARGS: - return -1; + return 1; case CALL_PY_WITH_DEFAULTS: - return -1; + return 1; case CALL_NO_KW_TYPE_1: - return -1; + return 1; case CALL_NO_KW_STR_1: - return -1; + return 1; case CALL_NO_KW_TUPLE_1: - return -1; + return 1; case CALL_BUILTIN_CLASS: - return -1; + return 1; case CALL_NO_KW_BUILTIN_O: - return -1; + return 1; case CALL_NO_KW_BUILTIN_FAST: - return -1; + return 1; case CALL_BUILTIN_FAST_WITH_KEYWORDS: - return -1; + return 1; case CALL_NO_KW_LEN: - return -1; + return 1; case CALL_NO_KW_ISINSTANCE: - return -1; + return 1; case CALL_NO_KW_LIST_APPEND: - return -1; + return 1; case CALL_NO_KW_METHOD_DESCRIPTOR_O: - return -1; + return 1; case CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: - return -1; + return 1; case CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS: - return -1; + return 1; case CALL_NO_KW_METHOD_DESCRIPTOR_FAST: - return -1; + return 1; case CALL_FUNCTION_EX: return 1; case MAKE_FUNCTION: @@ -846,25 +846,25 @@ struct opcode_metadata { [LOAD_ATTR_METHOD_WITH_VALUES] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, [LOAD_ATTR_METHOD_NO_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, [LOAD_ATTR_METHOD_LAZY_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, - [CALL_BOUND_METHOD_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [KW_NAMES] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_PY_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_PY_WITH_DEFAULTS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_TYPE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_STR_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_TUPLE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_BUILTIN_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_BUILTIN_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_BUILTIN_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_LEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_ISINSTANCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_LIST_APPEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_METHOD_DESCRIPTOR_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_BOUND_METHOD_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_PY_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_PY_WITH_DEFAULTS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_TYPE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_STR_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_TUPLE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_BUILTIN_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_BUILTIN_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_BUILTIN_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_LEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_ISINSTANCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_LIST_APPEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_METHOD_DESCRIPTOR_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, [CALL_FUNCTION_EX] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [MAKE_FUNCTION] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [RETURN_GENERATOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 9b5aa914cdee86..1fcfbb67709029 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -391,9 +391,11 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None # Write the body, substituting a goto for ERROR_IF() and other stuff assert dedent <= 0 extra = " " * -dedent + names_to_skip = self.unmoved_names | frozenset({UNUSED, "null"}) for line in self.block_text: if m := re.match(r"(\s*)ERROR_IF\((.+), (\w+)\);\s*(?://.*)?$", line): space, cond, label = m.groups() + space = extra + space # ERROR_IF() must pop the inputs from the stack. # The code block is responsible for DECREF()ing them. # NOTE: If the label doesn't exist, just add it to ceval.c. @@ -412,16 +414,25 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None symbolic = "" if symbolic: out.write_raw( - f"{extra}{space}if ({cond}) {{ STACK_SHRINK({symbolic}); goto {label}; }}\n" + f"{space}if ({cond}) {{ STACK_SHRINK({symbolic}); goto {label}; }}\n" ) else: - out.write_raw(f"{extra}{space}if ({cond}) goto {label};\n") + out.write_raw(f"{space}if ({cond}) goto {label};\n") elif m := re.match(r"(\s*)DECREF_INPUTS\(\);\s*(?://.*)?$", line): if not self.register: - space = m.group(1) + space = extra + m.group(1) for ieff in self.input_effects: - if ieff.name not in self.unmoved_names: - out.write_raw(f"{extra}{space}Py_DECREF({ieff.name});\n") + if ieff.name in names_to_skip: + continue + if ieff.size: + out.write_raw( + f"{space}for (int _i = {ieff.size}; --_i >= 0;) {{\n" + ) + out.write_raw(f"{space} Py_DECREF({ieff.name}[_i]);\n") + out.write_raw(f"{space}}}\n") + else: + decref = "XDECREF" if ieff.cond else "DECREF" + out.write_raw(f"{space}Py_{decref}({ieff.name});\n") else: out.write_raw(extra + line) From de3669ebcb33ca8e3373fbbaed646c5f287979b8 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 8 Feb 2023 21:25:42 +0100 Subject: [PATCH 128/225] gh-101277: Port more itertools static types to heap types (#101304) Add accumulate, compress, count, filterfalse, pairwise, product, and zip_longest types to module state. --- Modules/clinic/itertoolsmodule.c.h | 6 +- Modules/itertoolsmodule.c | 521 +++++++++++------------------ 2 files changed, 200 insertions(+), 327 deletions(-) diff --git a/Modules/clinic/itertoolsmodule.c.h b/Modules/clinic/itertoolsmodule.c.h index be44246cc9705a..d15d5f0890ca98 100644 --- a/Modules/clinic/itertoolsmodule.c.h +++ b/Modules/clinic/itertoolsmodule.c.h @@ -102,7 +102,7 @@ static PyObject * pairwise_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - PyTypeObject *base_tp = &pairwise_type; + PyTypeObject *base_tp = clinic_state()->pairwise_type; PyObject *iterable; if ((type == base_tp || type->tp_init == base_tp->tp_init) && @@ -821,7 +821,7 @@ static PyObject * itertools_filterfalse(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - PyTypeObject *base_tp = &filterfalse_type; + PyTypeObject *base_tp = clinic_state()->filterfalse_type; PyObject *func; PyObject *seq; @@ -913,4 +913,4 @@ itertools_count(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=b86fcd99bd32145e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=a08b58d7dac825da input=a9049054013a1b77]*/ diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index c9baa47e2c0edd..ce8720d0fd9228 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -12,15 +12,22 @@ */ typedef struct { + PyTypeObject *accumulate_type; PyTypeObject *combinations_type; + PyTypeObject *compress_type; + PyTypeObject *count_type; PyTypeObject *cwr_type; PyTypeObject *cycle_type; PyTypeObject *dropwhile_type; + PyTypeObject *filterfalse_type; PyTypeObject *groupby_type; PyTypeObject *_grouper_type; + PyTypeObject *pairwise_type; PyTypeObject *permutations_type; + PyTypeObject *product_type; PyTypeObject *starmap_type; PyTypeObject *takewhile_type; + PyTypeObject *ziplongest_type; } itertools_state; static inline itertools_state * @@ -48,7 +55,6 @@ find_state_by_type(PyTypeObject *tp) assert(mod != NULL); return get_module_state(mod); } -#define clinic_state() (find_state_by_type(type)) /*[clinic input] module itertools @@ -65,23 +71,19 @@ class itertools.chain "chainobject *" "&chain_type" class itertools.combinations "combinationsobject *" "clinic_state()->combinations_type" class itertools.combinations_with_replacement "cwr_object *" "clinic_state()->cwr_type" class itertools.permutations "permutationsobject *" "clinic_state()->permutations_type" -class itertools.accumulate "accumulateobject *" "&accumulate_type" -class itertools.compress "compressobject *" "&compress_type" -class itertools.filterfalse "filterfalseobject *" "&filterfalse_type" -class itertools.count "countobject *" "&count_type" -class itertools.pairwise "pairwiseobject *" "&pairwise_type" +class itertools.accumulate "accumulateobject *" "clinic_state()->accumulate_type" +class itertools.compress "compressobject *" "clinic_state()->compress_type" +class itertools.filterfalse "filterfalseobject *" "clinic_state()->filterfalse_type" +class itertools.count "countobject *" "clinic_state()->count_type" +class itertools.pairwise "pairwiseobject *" "clinic_state()->pairwise_type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=1790ac655869a651]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=28ffff5c0c93eed7]*/ static PyTypeObject teedataobject_type; static PyTypeObject tee_type; static PyTypeObject batched_type; -static PyTypeObject accumulate_type; -static PyTypeObject compress_type; -static PyTypeObject filterfalse_type; -static PyTypeObject count_type; -static PyTypeObject pairwise_type; +#define clinic_state() (find_state_by_type(type)) #define clinic_state_by_cls() (get_module_state_by_cls(base_tp)) #include "clinic/itertoolsmodule.c.h" #undef clinic_state_by_cls @@ -308,15 +310,18 @@ pairwise_new_impl(PyTypeObject *type, PyObject *iterable) static void pairwise_dealloc(pairwiseobject *po) { + PyTypeObject *tp = Py_TYPE(po); PyObject_GC_UnTrack(po); Py_XDECREF(po->it); Py_XDECREF(po->old); - Py_TYPE(po)->tp_free(po); + tp->tp_free(po); + Py_DECREF(tp); } static int pairwise_traverse(pairwiseobject *po, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(po)); Py_VISIT(po->it); Py_VISIT(po->old); return 0; @@ -351,48 +356,25 @@ pairwise_next(pairwiseobject *po) return result; } -static PyTypeObject pairwise_type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "itertools.pairwise", /* tp_name */ - sizeof(pairwiseobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)pairwise_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - pairwise_new__doc__, /* tp_doc */ - (traverseproc)pairwise_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)pairwise_next, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - PyType_GenericAlloc, /* tp_alloc */ - pairwise_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot pairwise_slots[] = { + {Py_tp_dealloc, pairwise_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)pairwise_new__doc__}, + {Py_tp_traverse, pairwise_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, pairwise_next}, + {Py_tp_alloc, PyType_GenericAlloc}, + {Py_tp_new, pairwise_new}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec pairwise_spec = { + .name = "itertools.pairwise", + .basicsize = sizeof(pairwiseobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = pairwise_slots, }; @@ -2300,8 +2282,6 @@ typedef struct { int stopped; /* set to 1 when the iterator is exhausted */ } productobject; -static PyTypeObject product_type; - static PyObject * product_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { @@ -2388,12 +2368,14 @@ product_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static void product_dealloc(productobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->pools); Py_XDECREF(lz->result); if (lz->indices != NULL) PyMem_Free(lz->indices); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static PyObject * @@ -2409,6 +2391,7 @@ PyDoc_STRVAR(sizeof_doc, "Returns size in memory, in bytes."); static int product_traverse(productobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->pools); Py_VISIT(lz->result); return 0; @@ -2600,48 +2583,25 @@ product(A, repeat=4) means the same as product(A, A, A, A).\n\n\ product('ab', range(3)) --> ('a',0) ('a',1) ('a',2) ('b',0) ('b',1) ('b',2)\n\ product((0,1), (0,1), (0,1)) --> (0,0,0) (0,0,1) (0,1,0) (0,1,1) (1,0,0) ..."); -static PyTypeObject product_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.product", /* tp_name */ - sizeof(productobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)product_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - product_doc, /* tp_doc */ - (traverseproc)product_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)product_next, /* tp_iternext */ - product_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - product_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot product_slots[] = { + {Py_tp_dealloc, product_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)product_doc}, + {Py_tp_traverse, product_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, product_next}, + {Py_tp_methods, product_methods}, + {Py_tp_new, product_new}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec product_spec = { + .name = "itertools.product", + .basicsize = sizeof(productobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = product_slots, }; @@ -3658,17 +3618,20 @@ itertools_accumulate_impl(PyTypeObject *type, PyObject *iterable, static void accumulate_dealloc(accumulateobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->binop); Py_XDECREF(lz->total); Py_XDECREF(lz->it); Py_XDECREF(lz->initial); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int accumulate_traverse(accumulateobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->binop); Py_VISIT(lz->it); Py_VISIT(lz->total); @@ -3762,48 +3725,25 @@ static PyMethodDef accumulate_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject accumulate_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.accumulate", /* tp_name */ - sizeof(accumulateobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)accumulate_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - itertools_accumulate__doc__, /* tp_doc */ - (traverseproc)accumulate_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)accumulate_next, /* tp_iternext */ - accumulate_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_accumulate, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot accumulate_slots[] = { + {Py_tp_dealloc, accumulate_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_accumulate__doc__}, + {Py_tp_traverse, accumulate_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, accumulate_next}, + {Py_tp_methods, accumulate_methods}, + {Py_tp_new, itertools_accumulate}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec accumulate_spec = { + .name = "itertools.accumulate", + .basicsize = sizeof(accumulateobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = accumulate_slots, }; @@ -3864,15 +3804,18 @@ itertools_compress_impl(PyTypeObject *type, PyObject *seq1, PyObject *seq2) static void compress_dealloc(compressobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->data); Py_XDECREF(lz->selectors); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int compress_traverse(compressobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->data); Py_VISIT(lz->selectors); return 0; @@ -3927,48 +3870,25 @@ static PyMethodDef compress_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject compress_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.compress", /* tp_name */ - sizeof(compressobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)compress_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - itertools_compress__doc__, /* tp_doc */ - (traverseproc)compress_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)compress_next, /* tp_iternext */ - compress_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_compress, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot compress_slots[] = { + {Py_tp_dealloc, compress_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_compress__doc__}, + {Py_tp_traverse, compress_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, compress_next}, + {Py_tp_methods, compress_methods}, + {Py_tp_new, itertools_compress}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec compress_spec = { + .name = "itertools.compress", + .basicsize = sizeof(compressobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = compress_slots, }; @@ -4018,15 +3938,18 @@ itertools_filterfalse_impl(PyTypeObject *type, PyObject *func, PyObject *seq) static void filterfalse_dealloc(filterfalseobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->func); Py_XDECREF(lz->it); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int filterfalse_traverse(filterfalseobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->it); Py_VISIT(lz->func); return 0; @@ -4078,48 +4001,25 @@ static PyMethodDef filterfalse_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject filterfalse_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.filterfalse", /* tp_name */ - sizeof(filterfalseobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)filterfalse_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - itertools_filterfalse__doc__, /* tp_doc */ - (traverseproc)filterfalse_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)filterfalse_next, /* tp_iternext */ - filterfalse_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_filterfalse, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot filterfalse_slots[] = { + {Py_tp_dealloc, filterfalse_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_filterfalse__doc__}, + {Py_tp_traverse, filterfalse_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, filterfalse_next}, + {Py_tp_methods, filterfalse_methods}, + {Py_tp_new, itertools_filterfalse}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec filterfalse_spec = { + .name = "itertools.filterfalse", + .basicsize = sizeof(filterfalseobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = filterfalse_slots, }; @@ -4245,15 +4145,18 @@ itertools_count_impl(PyTypeObject *type, PyObject *long_cnt, static void count_dealloc(countobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->long_cnt); Py_XDECREF(lz->long_step); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int count_traverse(countobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->long_cnt); Py_VISIT(lz->long_step); return 0; @@ -4327,48 +4230,26 @@ static PyMethodDef count_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject count_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.count", /* tp_name */ - sizeof(countobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)count_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)count_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - itertools_count__doc__, /* tp_doc */ - (traverseproc)count_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)count_next, /* tp_iternext */ - count_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_count, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot count_slots[] = { + {Py_tp_dealloc, count_dealloc}, + {Py_tp_repr, count_repr}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_count__doc__}, + {Py_tp_traverse, count_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, count_next}, + {Py_tp_methods, count_methods}, + {Py_tp_new, itertools_count}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec count_spec = { + .name = "itertools.count", + .basicsize = sizeof(countobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = count_slots, }; @@ -4536,8 +4417,6 @@ typedef struct { PyObject *fillvalue; } ziplongestobject; -static PyTypeObject ziplongest_type; - static PyObject * zip_longest_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { @@ -4609,16 +4488,19 @@ zip_longest_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static void zip_longest_dealloc(ziplongestobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->ittuple); Py_XDECREF(lz->result); Py_XDECREF(lz->fillvalue); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int zip_longest_traverse(ziplongestobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->ittuple); Py_VISIT(lz->result); Py_VISIT(lz->fillvalue); @@ -4752,48 +4634,25 @@ are exhausted, the fillvalue is substituted in their place. The fillvalue\n\ defaults to None or can be specified by a keyword argument.\n\ "); -static PyTypeObject ziplongest_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.zip_longest", /* tp_name */ - sizeof(ziplongestobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)zip_longest_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - zip_longest_doc, /* tp_doc */ - (traverseproc)zip_longest_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)zip_longest_next, /* tp_iternext */ - zip_longest_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - zip_longest_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot ziplongest_slots[] = { + {Py_tp_dealloc, zip_longest_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)zip_longest_doc}, + {Py_tp_traverse, zip_longest_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, zip_longest_next}, + {Py_tp_methods, zip_longest_methods}, + {Py_tp_new, zip_longest_new}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec ziplongest_spec = { + .name = "itertools.zip_longest", + .basicsize = sizeof(ziplongestobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = ziplongest_slots, }; @@ -4835,15 +4694,22 @@ static int itertoolsmodule_traverse(PyObject *mod, visitproc visit, void *arg) { itertools_state *state = get_module_state(mod); + Py_VISIT(state->accumulate_type); Py_VISIT(state->combinations_type); + Py_VISIT(state->compress_type); + Py_VISIT(state->count_type); Py_VISIT(state->cwr_type); Py_VISIT(state->cycle_type); Py_VISIT(state->dropwhile_type); + Py_VISIT(state->filterfalse_type); Py_VISIT(state->groupby_type); Py_VISIT(state->_grouper_type); + Py_VISIT(state->pairwise_type); Py_VISIT(state->permutations_type); + Py_VISIT(state->product_type); Py_VISIT(state->starmap_type); Py_VISIT(state->takewhile_type); + Py_VISIT(state->ziplongest_type); return 0; } @@ -4851,15 +4717,22 @@ static int itertoolsmodule_clear(PyObject *mod) { itertools_state *state = get_module_state(mod); + Py_CLEAR(state->accumulate_type); Py_CLEAR(state->combinations_type); + Py_CLEAR(state->compress_type); + Py_CLEAR(state->count_type); Py_CLEAR(state->cwr_type); Py_CLEAR(state->cycle_type); Py_CLEAR(state->dropwhile_type); + Py_CLEAR(state->filterfalse_type); Py_CLEAR(state->groupby_type); Py_CLEAR(state->_grouper_type); + Py_CLEAR(state->pairwise_type); Py_CLEAR(state->permutations_type); + Py_CLEAR(state->product_type); Py_CLEAR(state->starmap_type); Py_CLEAR(state->takewhile_type); + Py_CLEAR(state->ziplongest_type); return 0; } @@ -4884,27 +4757,27 @@ static int itertoolsmodule_exec(PyObject *mod) { itertools_state *state = get_module_state(mod); + ADD_TYPE(mod, state->accumulate_type, &accumulate_spec); ADD_TYPE(mod, state->combinations_type, &combinations_spec); + ADD_TYPE(mod, state->compress_type, &compress_spec); + ADD_TYPE(mod, state->count_type, &count_spec); ADD_TYPE(mod, state->cwr_type, &cwr_spec); ADD_TYPE(mod, state->cycle_type, &cycle_spec); ADD_TYPE(mod, state->dropwhile_type, &dropwhile_spec); + ADD_TYPE(mod, state->filterfalse_type, &filterfalse_spec); ADD_TYPE(mod, state->groupby_type, &groupby_spec); ADD_TYPE(mod, state->_grouper_type, &_grouper_spec); + ADD_TYPE(mod, state->pairwise_type, &pairwise_spec); ADD_TYPE(mod, state->permutations_type, &permutations_spec); + ADD_TYPE(mod, state->product_type, &product_spec); ADD_TYPE(mod, state->starmap_type, &starmap_spec); ADD_TYPE(mod, state->takewhile_type, &takewhile_spec); + ADD_TYPE(mod, state->ziplongest_type, &ziplongest_spec); PyTypeObject *typelist[] = { - &accumulate_type, &batched_type, &islice_type, &chain_type, - &compress_type, - &filterfalse_type, - &count_type, - &ziplongest_type, - &pairwise_type, - &product_type, &repeat_type, &tee_type, &teedataobject_type From 23751ed826ee63fb486e874ec25934ea87dd8519 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Thu, 9 Feb 2023 02:12:19 +0400 Subject: [PATCH 129/225] gh-101283: Improved fallback logic for subprocess with shell=True on Windows (GH-101286) --- Doc/library/subprocess.rst | 40 +++++++++++++++++++ Lib/subprocess.py | 16 +++++++- ...-01-24-16-12-00.gh-issue-101283.9tqu39.rst | 3 ++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Security/2023-01-24-16-12-00.gh-issue-101283.9tqu39.rst diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index a87369a2461a54..c93319e7011c20 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -111,6 +111,14 @@ underlying :class:`Popen` interface can be used directly. Added the *text* parameter, as a more understandable alias of *universal_newlines*. Added the *capture_output* parameter. + .. versionchanged:: 3.11.2 + + Changed Windows shell search order for ``shell=True``. The current + directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and + ``%SystemRoot%\System32\cmd.exe``. As a result, dropping a + malicious program named ``cmd.exe`` into a current directory no + longer works. + .. class:: CompletedProcess The return value from :func:`run`, representing a process that has finished. @@ -487,6 +495,14 @@ functions. *executable* parameter accepts a bytes and :term:`path-like object` on Windows. + .. versionchanged:: 3.11.2 + + Changed Windows shell search order for ``shell=True``. The current + directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and + ``%SystemRoot%\System32\cmd.exe``. As a result, dropping a + malicious program named ``cmd.exe`` into a current directory no + longer works. + *stdin*, *stdout* and *stderr* specify the executed program's standard input, standard output and standard error file handles, respectively. Valid values are ``None``, :data:`PIPE`, :data:`DEVNULL`, an existing file descriptor (a @@ -1158,6 +1174,14 @@ calls these functions. .. versionchanged:: 3.3 *timeout* was added. + .. versionchanged:: 3.11.2 + + Changed Windows shell search order for ``shell=True``. The current + directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and + ``%SystemRoot%\System32\cmd.exe``. As a result, dropping a + malicious program named ``cmd.exe`` into a current directory no + longer works. + .. function:: check_call(args, *, stdin=None, stdout=None, stderr=None, \ shell=False, cwd=None, timeout=None, \ **other_popen_kwargs) @@ -1190,6 +1214,14 @@ calls these functions. .. versionchanged:: 3.3 *timeout* was added. + .. versionchanged:: 3.11.2 + + Changed Windows shell search order for ``shell=True``. The current + directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and + ``%SystemRoot%\System32\cmd.exe``. As a result, dropping a + malicious program named ``cmd.exe`` into a current directory no + longer works. + .. function:: check_output(args, *, stdin=None, stderr=None, shell=False, \ cwd=None, encoding=None, errors=None, \ @@ -1245,6 +1277,14 @@ calls these functions. .. versionadded:: 3.7 *text* was added as a more readable alias for *universal_newlines*. + .. versionchanged:: 3.11.2 + + Changed Windows shell search order for ``shell=True``. The current + directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and + ``%SystemRoot%\System32\cmd.exe``. As a result, dropping a + malicious program named ``cmd.exe`` into a current directory no + longer works. + .. _subprocess-replacements: diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 9cadd1bf8e622c..fa527d50ebb44d 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -1480,7 +1480,21 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, if shell: startupinfo.dwFlags |= _winapi.STARTF_USESHOWWINDOW startupinfo.wShowWindow = _winapi.SW_HIDE - comspec = os.environ.get("COMSPEC", "cmd.exe") + if not executable: + # gh-101283: without a fully-qualified path, before Windows + # checks the system directories, it first looks in the + # application directory, and also the current directory if + # NeedCurrentDirectoryForExePathW(ExeName) is true, so try + # to avoid executing unqualified "cmd.exe". + comspec = os.environ.get('ComSpec') + if not comspec: + system_root = os.environ.get('SystemRoot', '') + comspec = os.path.join(system_root, 'System32', 'cmd.exe') + if not os.path.isabs(comspec): + raise FileNotFoundError('shell not found: neither %ComSpec% nor %SystemRoot% is set') + if os.path.isabs(comspec): + executable = comspec + args = '{} /c "{}"'.format (comspec, args) if cwd is not None: diff --git a/Misc/NEWS.d/next/Security/2023-01-24-16-12-00.gh-issue-101283.9tqu39.rst b/Misc/NEWS.d/next/Security/2023-01-24-16-12-00.gh-issue-101283.9tqu39.rst new file mode 100644 index 00000000000000..0efdfa10234185 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2023-01-24-16-12-00.gh-issue-101283.9tqu39.rst @@ -0,0 +1,3 @@ +:class:`subprocess.Popen` now uses a safer approach to find +``cmd.exe`` when launching with ``shell=True``. Patch by Eryk Sun, +based on a patch by Oleg Iarygin. From 20cf32e761fb9eaccc142415b389998896869263 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 8 Feb 2023 23:38:56 +0000 Subject: [PATCH 130/225] gh-101283: Fix use of unbound variable (GH-101712) --- Lib/subprocess.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/subprocess.py b/Lib/subprocess.py index fa527d50ebb44d..1f203bd00d3500 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -1494,6 +1494,8 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, raise FileNotFoundError('shell not found: neither %ComSpec% nor %SystemRoot% is set') if os.path.isabs(comspec): executable = comspec + else: + comspec = executable args = '{} /c "{}"'.format (comspec, args) From 0e0c5d8baaa6aa91f4221c5aa57d5586e58e8652 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 8 Feb 2023 23:52:03 +0000 Subject: [PATCH 131/225] gh-101283: Version was just released, so should be changed in 3.11.3 (GH-101719) --- Doc/library/subprocess.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index c93319e7011c20..d792a43eeb271f 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -111,7 +111,7 @@ underlying :class:`Popen` interface can be used directly. Added the *text* parameter, as a more understandable alias of *universal_newlines*. Added the *capture_output* parameter. - .. versionchanged:: 3.11.2 + .. versionchanged:: 3.11.3 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and @@ -495,7 +495,7 @@ functions. *executable* parameter accepts a bytes and :term:`path-like object` on Windows. - .. versionchanged:: 3.11.2 + .. versionchanged:: 3.11.3 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and @@ -1174,7 +1174,7 @@ calls these functions. .. versionchanged:: 3.3 *timeout* was added. - .. versionchanged:: 3.11.2 + .. versionchanged:: 3.11.3 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and @@ -1214,7 +1214,7 @@ calls these functions. .. versionchanged:: 3.3 *timeout* was added. - .. versionchanged:: 3.11.2 + .. versionchanged:: 3.11.3 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and @@ -1277,7 +1277,7 @@ calls these functions. .. versionadded:: 3.7 *text* was added as a more readable alias for *universal_newlines*. - .. versionchanged:: 3.11.2 + .. versionchanged:: 3.11.3 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and From 65b7b6bd23ea789357777f3a0a6f25a79bb04177 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 8 Feb 2023 16:23:19 -0800 Subject: [PATCH 132/225] gh-98831: Use opcode metadata for stack_effect() (#101704) * Write output and metadata in a single run This halves the time to run the cases generator (most of the time goes into parsing the input). * Declare or define opcode metadata based on NEED_OPCODE_TABLES * Use generated metadata for stack_effect() * compile.o depends on opcode_metadata.h * Return -1 from _PyOpcode_num_popped/pushed for unknown opcode --- Makefile.pre.in | 17 +- Python/compile.c | 258 ++++-------------------- Python/opcode_metadata.h | 25 ++- Tools/cases_generator/generate_cases.py | 44 ++-- 4 files changed, 88 insertions(+), 256 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index 2559df8e74952c..7a84b953d97962 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1445,24 +1445,21 @@ regen-opcode-targets: .PHONY: regen-cases regen-cases: - # Regenerate Python/generated_cases.c.h from Python/bytecodes.c + # Regenerate Python/generated_cases.c.h + # and Python/opcode_metadata.h + # from Python/bytecodes.c # using Tools/cases_generator/generate_cases.py PYTHONPATH=$(srcdir)/Tools/cases_generator \ $(PYTHON_FOR_REGEN) \ $(srcdir)/Tools/cases_generator/generate_cases.py \ -i $(srcdir)/Python/bytecodes.c \ - -o $(srcdir)/Python/generated_cases.c.h.new + -o $(srcdir)/Python/generated_cases.c.h.new \ + -m $(srcdir)/Python/opcode_metadata.h.new $(UPDATE_FILE) $(srcdir)/Python/generated_cases.c.h $(srcdir)/Python/generated_cases.c.h.new - # Regenerate Python/opcode_metadata.h from Python/bytecodes.c - # using Tools/cases_generator/generate_cases.py --metadata - PYTHONPATH=$(srcdir)/Tools/cases_generator \ - $(PYTHON_FOR_REGEN) \ - $(srcdir)/Tools/cases_generator/generate_cases.py \ - --metadata \ - -i $(srcdir)/Python/bytecodes.c \ - -o $(srcdir)/Python/opcode_metadata.h.new $(UPDATE_FILE) $(srcdir)/Python/opcode_metadata.h $(srcdir)/Python/opcode_metadata.h.new +Python/compile.o: $(srcdir)/Python/opcode_metadata.h + Python/ceval.o: \ $(srcdir)/Python/ceval_macros.h \ $(srcdir)/Python/condvar.h \ diff --git a/Python/compile.c b/Python/compile.c index df2dffb95bbd7e..a3c915c3c14a96 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1074,135 +1074,49 @@ basicblock_next_instr(basicblock *b) static int stack_effect(int opcode, int oparg, int jump) { - switch (opcode) { - case NOP: - case EXTENDED_ARG: - case RESUME: - case CACHE: - return 0; - - /* Stack manipulation */ - case POP_TOP: - return -1; - case SWAP: - return 0; - case END_FOR: - return -2; - - /* Unary operators */ - case UNARY_NEGATIVE: - case UNARY_NOT: - case UNARY_INVERT: - return 0; - - case SET_ADD: - case LIST_APPEND: - return -1; - case MAP_ADD: - return -2; - - case BINARY_SUBSCR: - return -1; - case BINARY_SLICE: - return -2; - case STORE_SUBSCR: - return -3; - case STORE_SLICE: - return -4; - case DELETE_SUBSCR: - return -2; - - case GET_ITER: - return 0; - - case LOAD_BUILD_CLASS: - return 1; + if (0 <= opcode && opcode <= MAX_REAL_OPCODE) { + if (_PyOpcode_Deopt[opcode] != opcode) { + // Specialized instructions are not supported. + return PY_INVALID_STACK_EFFECT; + } + int popped, pushed; + if (jump > 0) { + popped = _PyOpcode_num_popped(opcode, oparg, true); + pushed = _PyOpcode_num_pushed(opcode, oparg, true); + } + else { + popped = _PyOpcode_num_popped(opcode, oparg, false); + pushed = _PyOpcode_num_pushed(opcode, oparg, false); + } + if (popped < 0 || pushed < 0) { + return PY_INVALID_STACK_EFFECT; + } + if (jump >= 0) { + return pushed - popped; + } + if (jump < 0) { + // Compute max(pushed - popped, alt_pushed - alt_popped) + int alt_popped = _PyOpcode_num_popped(opcode, oparg, true); + int alt_pushed = _PyOpcode_num_pushed(opcode, oparg, true); + if (alt_popped < 0 || alt_pushed < 0) { + return PY_INVALID_STACK_EFFECT; + } + int diff = pushed - popped; + int alt_diff = alt_pushed - alt_popped; + if (alt_diff > diff) { + return alt_diff; + } + return diff; + } + } - case RETURN_VALUE: - return -1; - case RETURN_CONST: - return 0; - case SETUP_ANNOTATIONS: - return 0; - case YIELD_VALUE: - return 0; + // Pseudo ops + switch (opcode) { case POP_BLOCK: - return 0; - case POP_EXCEPT: - return -1; - - case STORE_NAME: - return -1; - case DELETE_NAME: - return 0; - case UNPACK_SEQUENCE: - return oparg-1; - case UNPACK_EX: - return (oparg&0xFF) + (oparg>>8); - case FOR_ITER: - return 1; - case SEND: - return jump > 0 ? -1 : 0; - case STORE_ATTR: - return -2; - case DELETE_ATTR: - return -1; - case STORE_GLOBAL: - return -1; - case DELETE_GLOBAL: - return 0; - case LOAD_CONST: - return 1; - case LOAD_NAME: - return 1; - case BUILD_TUPLE: - case BUILD_LIST: - case BUILD_SET: - case BUILD_STRING: - return 1-oparg; - case BUILD_MAP: - return 1 - 2*oparg; - case BUILD_CONST_KEY_MAP: - return -oparg; - case LOAD_ATTR: - return (oparg & 1); - case COMPARE_OP: - case IS_OP: - case CONTAINS_OP: - return -1; - case CHECK_EXC_MATCH: - return 0; - case CHECK_EG_MATCH: - return 0; - case IMPORT_NAME: - return -1; - case IMPORT_FROM: - return 1; - - /* Jumps */ - case JUMP_FORWARD: - case JUMP_BACKWARD: case JUMP: - case JUMP_BACKWARD_NO_INTERRUPT: case JUMP_NO_INTERRUPT: return 0; - case JUMP_IF_TRUE_OR_POP: - case JUMP_IF_FALSE_OR_POP: - return jump ? 0 : -1; - - case POP_JUMP_IF_NONE: - case POP_JUMP_IF_NOT_NONE: - case POP_JUMP_IF_FALSE: - case POP_JUMP_IF_TRUE: - return -1; - - case COMPARE_AND_BRANCH: - return -2; - - case LOAD_GLOBAL: - return (oparg & 1) + 1; - /* Exception handling pseudo-instructions */ case SETUP_FINALLY: /* 0 in the normal flow. @@ -1218,109 +1132,13 @@ stack_effect(int opcode, int oparg, int jump) * of __(a)enter__ and push 2 values before jumping to the handler * if an exception be raised. */ return jump ? 1 : 0; - case PREP_RERAISE_STAR: - return -1; - case RERAISE: - return -1; - case PUSH_EXC_INFO: - return 1; - - case WITH_EXCEPT_START: - return 1; - - case LOAD_FAST: - case LOAD_FAST_CHECK: - return 1; - case STORE_FAST: - return -1; - case DELETE_FAST: - return 0; - - case RETURN_GENERATOR: - return 0; - case RAISE_VARARGS: - return -oparg; - - /* Functions and calls */ - case KW_NAMES: - return 0; - case CALL: - return -1-oparg; - case CALL_INTRINSIC_1: - return 0; - case CALL_FUNCTION_EX: - return -2 - ((oparg & 0x01) != 0); - case MAKE_FUNCTION: - return 0 - ((oparg & 0x01) != 0) - ((oparg & 0x02) != 0) - - ((oparg & 0x04) != 0) - ((oparg & 0x08) != 0); - case BUILD_SLICE: - if (oparg == 3) - return -2; - else - return -1; - - /* Closures */ - case MAKE_CELL: - case COPY_FREE_VARS: - return 0; - case LOAD_CLOSURE: - return 1; - case LOAD_DEREF: - case LOAD_CLASSDEREF: - return 1; - case STORE_DEREF: - return -1; - case DELETE_DEREF: - return 0; - - /* Iterators and generators */ - case GET_AWAITABLE: - return 0; - - case BEFORE_ASYNC_WITH: - case BEFORE_WITH: - return 1; - case GET_AITER: - return 0; - case GET_ANEXT: - return 1; - case GET_YIELD_FROM_ITER: - return 0; - case END_ASYNC_FOR: - return -2; - case CLEANUP_THROW: - return -2; - case FORMAT_VALUE: - /* If there's a fmt_spec on the stack, we go from 2->1, - else 1->1. */ - return (oparg & FVS_MASK) == FVS_HAVE_SPEC ? -1 : 0; case LOAD_METHOD: return 1; - case LOAD_ASSERTION_ERROR: - return 1; - case LIST_EXTEND: - case SET_UPDATE: - case DICT_MERGE: - case DICT_UPDATE: - return -1; - case MATCH_CLASS: - return -2; - case GET_LEN: - case MATCH_MAPPING: - case MATCH_SEQUENCE: - case MATCH_KEYS: - return 1; - case COPY: - case PUSH_NULL: - return 1; - case BINARY_OP: - return -1; - case INTERPRETER_EXIT: - return -1; default: return PY_INVALID_STACK_EFFECT; } + return PY_INVALID_STACK_EFFECT; /* not reachable */ } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 98791043f55271..db1dfd37a90132 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -2,8 +2,10 @@ // from Python/bytecodes.c // Do not edit! -#ifndef NDEBUG -static int +#ifndef NEED_OPCODE_TABLES +extern int _PyOpcode_num_popped(int opcode, int oparg, bool jump); +#else +int _PyOpcode_num_popped(int opcode, int oparg, bool jump) { switch(opcode) { case NOP: @@ -345,13 +347,15 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case CACHE: return 0; default: - Py_UNREACHABLE(); + return -1; } } #endif -#ifndef NDEBUG -static int +#ifndef NEED_OPCODE_TABLES +extern int _PyOpcode_num_pushed(int opcode, int oparg, bool jump); +#else +int _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { switch(opcode) { case NOP: @@ -693,10 +697,11 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case CACHE: return 0; default: - Py_UNREACHABLE(); + return -1; } } #endif + enum Direction { DIR_NONE, DIR_READ, DIR_WRITE }; enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBC0000, INSTR_FMT_IBC00000000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 }; struct opcode_metadata { @@ -705,7 +710,12 @@ struct opcode_metadata { enum Direction dir_op3; bool valid_entry; enum InstructionFormat instr_format; -} _PyOpcode_opcode_metadata[256] = { +}; + +#ifndef NEED_OPCODE_TABLES +extern const struct opcode_metadata _PyOpcode_opcode_metadata[256]; +#else +const struct opcode_metadata _PyOpcode_opcode_metadata[256] = { [NOP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [RESUME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [LOAD_CLOSURE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, @@ -876,3 +886,4 @@ struct opcode_metadata { [EXTENDED_ARG] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [CACHE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, }; +#endif diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 1fcfbb67709029..aa8e14075c8738 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -43,10 +43,7 @@ "-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT ) arg_parser.add_argument( - "-m", - "--metadata", - action="store_true", - help=f"Generate metadata instead, changes output default to {DEFAULT_METADATA_OUTPUT}", + "-m", "--metadata", type=str, help="Generated metadata", default=DEFAULT_METADATA_OUTPUT ) @@ -498,13 +495,15 @@ class Analyzer: filename: str output_filename: str + metadata_filename: str src: str errors: int = 0 - def __init__(self, filename: str, output_filename: str): + def __init__(self, filename: str, output_filename: str, metadata_filename: str): """Read the input file.""" self.filename = filename self.output_filename = output_filename + self.metadata_filename = metadata_filename with open(filename) as f: self.src = f.read() @@ -889,21 +888,25 @@ def write_stack_effect_functions(self) -> None: def write_function( direction: str, data: list[tuple[AnyInstruction, str]] ) -> None: - self.out.emit("\n#ifndef NDEBUG") - self.out.emit("static int") + self.out.emit("") + self.out.emit("#ifndef NEED_OPCODE_TABLES") + self.out.emit(f"extern int _PyOpcode_num_{direction}(int opcode, int oparg, bool jump);") + self.out.emit("#else") + self.out.emit("int") self.out.emit(f"_PyOpcode_num_{direction}(int opcode, int oparg, bool jump) {{") self.out.emit(" switch(opcode) {") for instr, effect in data: self.out.emit(f" case {instr.name}:") self.out.emit(f" return {effect};") self.out.emit(" default:") - self.out.emit(" Py_UNREACHABLE();") + self.out.emit(" return -1;") self.out.emit(" }") self.out.emit("}") self.out.emit("#endif") write_function("popped", popped_data) write_function("pushed", pushed_data) + self.out.emit("") def write_metadata(self) -> None: """Write instruction metadata to output file.""" @@ -924,7 +927,7 @@ def write_metadata(self) -> None: # Turn it into a list of enum definitions. format_enums = [INSTR_FMT_PREFIX + format for format in sorted(all_formats)] - with open(self.output_filename, "w") as f: + with open(self.metadata_filename, "w") as f: # Write provenance header f.write(f"// This file is generated by {THIS} --metadata\n") f.write(f"// from {os.path.relpath(self.filename, ROOT)}\n") @@ -935,7 +938,7 @@ def write_metadata(self) -> None: self.write_stack_effect_functions() - # Write variable definition + # Write type definitions self.out.emit("enum Direction { DIR_NONE, DIR_READ, DIR_WRITE };") self.out.emit(f"enum InstructionFormat {{ {', '.join(format_enums)} }};") self.out.emit("struct opcode_metadata {") @@ -945,7 +948,14 @@ def write_metadata(self) -> None: self.out.emit("enum Direction dir_op3;") self.out.emit("bool valid_entry;") self.out.emit("enum InstructionFormat instr_format;") - self.out.emit("} _PyOpcode_opcode_metadata[256] = {") + self.out.emit("};") + self.out.emit("") + + # Write metadata array declaration + self.out.emit("#ifndef NEED_OPCODE_TABLES") + self.out.emit("extern const struct opcode_metadata _PyOpcode_opcode_metadata[256];") + self.out.emit("#else") + self.out.emit("const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {") # Write metadata for each instruction for thing in self.everything: @@ -962,6 +972,7 @@ def write_metadata(self) -> None: # Write end of array self.out.emit("};") + self.out.emit("#endif") def write_metadata_for_inst(self, instr: Instruction) -> None: """Write metadata for a single instruction.""" @@ -1184,18 +1195,13 @@ def variable_used(node: parser.Node, name: str) -> bool: def main(): """Parse command line, parse input, analyze, write output.""" args = arg_parser.parse_args() # Prints message and sys.exit(2) on error - if args.metadata: - if args.output == DEFAULT_OUTPUT: - args.output = DEFAULT_METADATA_OUTPUT - a = Analyzer(args.input, args.output) # Raises OSError if input unreadable + a = Analyzer(args.input, args.output, args.metadata) # Raises OSError if input unreadable a.parse() # Raises SyntaxError on failure a.analyze() # Prints messages and sets a.errors on failure if a.errors: sys.exit(f"Found {a.errors} errors") - if args.metadata: - a.write_metadata() - else: - a.write_instructions() # Raises OSError if output can't be written + a.write_instructions() # Raises OSError if output can't be written + a.write_metadata() if __name__ == "__main__": From 244d4cd9d22d73fb3c0938937c4f435bd68f32d4 Mon Sep 17 00:00:00 2001 From: Soumendra Ganguly <67527439+8vasu@users.noreply.github.com> Date: Wed, 8 Feb 2023 19:00:17 -0600 Subject: [PATCH 133/225] gh-85984: Remove legacy Lib/pty.py code. (#92365) Refactored the implementation of pty.fork to use os.login_tty. A DeprecationWarning is now raised by pty.master_open() and pty.slave_open(). They were undocumented and deprecated long long ago in the docstring in favor of pty.openpty. Signed-off-by: Soumendra Ganguly Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Gregory P. Smith --- Doc/whatsnew/3.12.rst | 4 ++++ Lib/pty.py | 20 +++++++------------ ...3-02-05-21-40-15.gh-issue-85984.Kfzbb2.rst | 4 ++++ 3 files changed, 15 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-05-21-40-15.gh-issue-85984.Kfzbb2.rst diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index b723b70154f08d..45a5e5062d55b6 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -512,6 +512,10 @@ Pending Removal in Python 3.14 :func:`~multiprocessing.set_start_method` APIs to explicitly specify when your code *requires* ``'fork'``. See :ref:`multiprocessing-start-methods`. +* :mod:`pty` has two undocumented ``master_open()`` and ``slave_open()`` + functions that have been deprecated since Python 2 but only gained a + proper :exc:`DeprecationWarning` in 3.12. Remove them in 3.14. + Pending Removal in Future Versions ---------------------------------- diff --git a/Lib/pty.py b/Lib/pty.py index 03073f07c92c05..6571050886bd1d 100644 --- a/Lib/pty.py +++ b/Lib/pty.py @@ -40,6 +40,9 @@ def master_open(): Open a pty master and return the fd, and the filename of the slave end. Deprecated, use openpty() instead.""" + import warnings + warnings.warn("Use pty.openpty() instead.", DeprecationWarning, stacklevel=2) # Remove API in 3.14 + try: master_fd, slave_fd = os.openpty() except (AttributeError, OSError): @@ -69,6 +72,9 @@ def slave_open(tty_name): opened filedescriptor. Deprecated, use openpty() instead.""" + import warnings + warnings.warn("Use pty.openpty() instead.", DeprecationWarning, stacklevel=2) # Remove API in 3.14 + result = os.open(tty_name, os.O_RDWR) try: from fcntl import ioctl, I_PUSH @@ -101,20 +107,8 @@ def fork(): master_fd, slave_fd = openpty() pid = os.fork() if pid == CHILD: - # Establish a new session. - os.setsid() os.close(master_fd) - - # Slave becomes stdin/stdout/stderr of child. - os.dup2(slave_fd, STDIN_FILENO) - os.dup2(slave_fd, STDOUT_FILENO) - os.dup2(slave_fd, STDERR_FILENO) - if slave_fd > STDERR_FILENO: - os.close(slave_fd) - - # Explicitly open the tty to make it become a controlling tty. - tmp_fd = os.open(os.ttyname(STDOUT_FILENO), os.O_RDWR) - os.close(tmp_fd) + os.login_tty(slave_fd) else: os.close(slave_fd) diff --git a/Misc/NEWS.d/next/Library/2023-02-05-21-40-15.gh-issue-85984.Kfzbb2.rst b/Misc/NEWS.d/next/Library/2023-02-05-21-40-15.gh-issue-85984.Kfzbb2.rst new file mode 100644 index 00000000000000..c91829f2c739af --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-05-21-40-15.gh-issue-85984.Kfzbb2.rst @@ -0,0 +1,4 @@ +Refactored the implementation of :func:`pty.fork` to use :func:`os.login_tty`. + +A :exc:`DeprecationWarning` is now raised by ``pty.master_open()`` and ``pty.slave_open()``. They were +undocumented and deprecated long long ago in the docstring in favor of :func:`pty.openpty`. From 58395759b04273edccf3d199606088e0703ae6b1 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 9 Feb 2023 11:40:52 +0300 Subject: [PATCH 134/225] gh-101678: refactor the math module to use special functions from c11 (GH-101679) Shouldn't affect users, hence no news. Automerge-Triggered-By: GH:mdickinson --- Modules/_math.h | 5 +- Modules/mathmodule.c | 187 ++----------------------------------------- 2 files changed, 8 insertions(+), 184 deletions(-) diff --git a/Modules/_math.h b/Modules/_math.h index 4a6bc223ef5fb5..2285b64747c0bd 100644 --- a/Modules/_math.h +++ b/Modules/_math.h @@ -7,8 +7,9 @@ static double _Py_log1p(double x) { - /* Some platforms supply a log1p function but don't respect the sign of - zero: log1p(-0.0) gives 0.0 instead of the correct result of -0.0. + /* Some platforms (e.g. MacOS X 10.8, see gh-59682) supply a log1p function + but don't respect the sign of zero: log1p(-0.0) gives 0.0 instead of + the correct result of -0.0. To save fiddling with configure tests and platform checks, we handle the special case of zero input directly on all platforms. diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 992957e675a7a3..939954c95d9ff2 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -101,10 +101,6 @@ get_math_module_state(PyObject *module) static const double pi = 3.141592653589793238462643383279502884197; static const double logpi = 1.144729885849400174143427351353058711647; -#if !defined(HAVE_ERF) || !defined(HAVE_ERFC) -static const double sqrtpi = 1.772453850905516027298167483341145182798; -#endif /* !defined(HAVE_ERF) || !defined(HAVE_ERFC) */ - /* Version of PyFloat_AsDouble() with in-line fast paths for exact floats and integers. Gives a substantial @@ -162,7 +158,9 @@ m_sinpi(double x) return copysign(1.0, x)*r; } -/* Implementation of the real gamma function. In extensive but non-exhaustive +/* Implementation of the real gamma function. Kept here to work around + issues (see e.g. gh-70309) with quality of libm's tgamma/lgamma implementations + on various platforms (Windows, MacOS). In extensive but non-exhaustive random tests, this function proved accurate to within <= 10 ulps across the entire float domain. Note that accuracy may depend on the quality of the system math functions, the pow function in particular. Special cases @@ -458,163 +456,6 @@ m_lgamma(double x) return r; } -#if !defined(HAVE_ERF) || !defined(HAVE_ERFC) - -/* - Implementations of the error function erf(x) and the complementary error - function erfc(x). - - Method: we use a series approximation for erf for small x, and a continued - fraction approximation for erfc(x) for larger x; - combined with the relations erf(-x) = -erf(x) and erfc(x) = 1.0 - erf(x), - this gives us erf(x) and erfc(x) for all x. - - The series expansion used is: - - erf(x) = x*exp(-x*x)/sqrt(pi) * [ - 2/1 + 4/3 x**2 + 8/15 x**4 + 16/105 x**6 + ...] - - The coefficient of x**(2k-2) here is 4**k*factorial(k)/factorial(2*k). - This series converges well for smallish x, but slowly for larger x. - - The continued fraction expansion used is: - - erfc(x) = x*exp(-x*x)/sqrt(pi) * [1/(0.5 + x**2 -) 0.5/(2.5 + x**2 - ) - 3.0/(4.5 + x**2 - ) 7.5/(6.5 + x**2 - ) ...] - - after the first term, the general term has the form: - - k*(k-0.5)/(2*k+0.5 + x**2 - ...). - - This expansion converges fast for larger x, but convergence becomes - infinitely slow as x approaches 0.0. The (somewhat naive) continued - fraction evaluation algorithm used below also risks overflow for large x; - but for large x, erfc(x) == 0.0 to within machine precision. (For - example, erfc(30.0) is approximately 2.56e-393). - - Parameters: use series expansion for abs(x) < ERF_SERIES_CUTOFF and - continued fraction expansion for ERF_SERIES_CUTOFF <= abs(x) < - ERFC_CONTFRAC_CUTOFF. ERFC_SERIES_TERMS and ERFC_CONTFRAC_TERMS are the - numbers of terms to use for the relevant expansions. */ - -#define ERF_SERIES_CUTOFF 1.5 -#define ERF_SERIES_TERMS 25 -#define ERFC_CONTFRAC_CUTOFF 30.0 -#define ERFC_CONTFRAC_TERMS 50 - -/* - Error function, via power series. - - Given a finite float x, return an approximation to erf(x). - Converges reasonably fast for small x. -*/ - -static double -m_erf_series(double x) -{ - double x2, acc, fk, result; - int i, saved_errno; - - x2 = x * x; - acc = 0.0; - fk = (double)ERF_SERIES_TERMS + 0.5; - for (i = 0; i < ERF_SERIES_TERMS; i++) { - acc = 2.0 + x2 * acc / fk; - fk -= 1.0; - } - /* Make sure the exp call doesn't affect errno; - see m_erfc_contfrac for more. */ - saved_errno = errno; - result = acc * x * exp(-x2) / sqrtpi; - errno = saved_errno; - return result; -} - -/* - Complementary error function, via continued fraction expansion. - - Given a positive float x, return an approximation to erfc(x). Converges - reasonably fast for x large (say, x > 2.0), and should be safe from - overflow if x and nterms are not too large. On an IEEE 754 machine, with x - <= 30.0, we're safe up to nterms = 100. For x >= 30.0, erfc(x) is smaller - than the smallest representable nonzero float. */ - -static double -m_erfc_contfrac(double x) -{ - double x2, a, da, p, p_last, q, q_last, b, result; - int i, saved_errno; - - if (x >= ERFC_CONTFRAC_CUTOFF) - return 0.0; - - x2 = x*x; - a = 0.0; - da = 0.5; - p = 1.0; p_last = 0.0; - q = da + x2; q_last = 1.0; - for (i = 0; i < ERFC_CONTFRAC_TERMS; i++) { - double temp; - a += da; - da += 2.0; - b = da + x2; - temp = p; p = b*p - a*p_last; p_last = temp; - temp = q; q = b*q - a*q_last; q_last = temp; - } - /* Issue #8986: On some platforms, exp sets errno on underflow to zero; - save the current errno value so that we can restore it later. */ - saved_errno = errno; - result = p / q * x * exp(-x2) / sqrtpi; - errno = saved_errno; - return result; -} - -#endif /* !defined(HAVE_ERF) || !defined(HAVE_ERFC) */ - -/* Error function erf(x), for general x */ - -static double -m_erf(double x) -{ -#ifdef HAVE_ERF - return erf(x); -#else - double absx, cf; - - if (Py_IS_NAN(x)) - return x; - absx = fabs(x); - if (absx < ERF_SERIES_CUTOFF) - return m_erf_series(x); - else { - cf = m_erfc_contfrac(absx); - return x > 0.0 ? 1.0 - cf : cf - 1.0; - } -#endif -} - -/* Complementary error function erfc(x), for general x. */ - -static double -m_erfc(double x) -{ -#ifdef HAVE_ERFC - return erfc(x); -#else - double absx, cf; - - if (Py_IS_NAN(x)) - return x; - absx = fabs(x); - if (absx < ERF_SERIES_CUTOFF) - return 1.0 - m_erf_series(x); - else { - cf = m_erfc_contfrac(absx); - return x > 0.0 ? cf : 2.0 - cf; - } -#endif -} - /* wrapper for atan2 that deals directly with special cases before delegating to the platform libm for the remaining cases. This @@ -801,25 +642,7 @@ m_log2(double x) } if (x > 0.0) { -#ifdef HAVE_LOG2 return log2(x); -#else - double m; - int e; - m = frexp(x, &e); - /* We want log2(m * 2**e) == log(m) / log(2) + e. Care is needed when - * x is just greater than 1.0: in that case e is 1, log(m) is negative, - * and we get significant cancellation error from the addition of - * log(m) / log(2) to e. The slight rewrite of the expression below - * avoids this problem. - */ - if (x >= 1.0) { - return log(2.0 * m) / log(2.0) + (e - 1); - } - else { - return log(m) / log(2.0) + e; - } -#endif } else if (x == 0.0) { errno = EDOM; @@ -1261,10 +1084,10 @@ FUNC1(cos, cos, 0, FUNC1(cosh, cosh, 1, "cosh($module, x, /)\n--\n\n" "Return the hyperbolic cosine of x.") -FUNC1A(erf, m_erf, +FUNC1A(erf, erf, "erf($module, x, /)\n--\n\n" "Error function at x.") -FUNC1A(erfc, m_erfc, +FUNC1A(erfc, erfc, "erfc($module, x, /)\n--\n\n" "Complementary error function at x.") FUNC1(exp, exp, 1, From 45fa12aec8f508c224a1521cfe3ae597f1026264 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 9 Feb 2023 12:40:13 +0300 Subject: [PATCH 135/225] gh-101678: Merge math_1_to_whatever() and math_1() (#101730) `math_1_to_whatever()` is no longer useful, since all existing uses of it convert to `float`. Earlier versions of Python used `math_1_to_whatever` with an integer target; see gh-16991 for the PR where that use was removed. --- Modules/mathmodule.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 939954c95d9ff2..544560e8322c72 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -875,9 +875,7 @@ is_error(double x) */ static PyObject * -math_1_to_whatever(PyObject *arg, double (*func) (double), - PyObject *(*from_double_func) (double), - int can_overflow) +math_1(PyObject *arg, double (*func) (double), int can_overflow) { double x, r; x = PyFloat_AsDouble(arg); @@ -903,7 +901,7 @@ math_1_to_whatever(PyObject *arg, double (*func) (double), /* this branch unnecessary on most platforms */ return NULL; - return (*from_double_func)(r); + return PyFloat_FromDouble(r); } /* variant of math_1, to be used when the function being wrapped is known to @@ -951,12 +949,6 @@ math_1a(PyObject *arg, double (*func) (double)) OverflowError. */ -static PyObject * -math_1(PyObject *arg, double (*func) (double), int can_overflow) -{ - return math_1_to_whatever(arg, func, PyFloat_FromDouble, can_overflow); -} - static PyObject * math_2(PyObject *const *args, Py_ssize_t nargs, double (*func) (double, double), const char *funcname) From 1c49e61b9b18d550b9c5cff69a1dd3bb218e544a Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Thu, 9 Feb 2023 21:01:32 +0900 Subject: [PATCH 136/225] no-issue: Add Dong-hee Na as the cjkcodecs codeowner (gh-101731) --- .github/CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8dd07d911f5b18..fc1bb3388976d5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -151,6 +151,8 @@ Lib/ast.py @isidentical **/*sysconfig* @FFY00 +**/*cjkcodecs* @corona10 + # macOS /Mac/ @python/macos-team **/*osx_support* @python/macos-team From ecfd2d37c529c1952dc11fabe1758156924de67a Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 9 Feb 2023 13:05:53 +0000 Subject: [PATCH 137/225] GH-99293: Document that `Py_TPFLAGS_VALID_VERSION_TAG` shouldn't be used. (#GH-101736) Document that Py_TPFLAGS_VALID_VERSION_TAG shouldn't be used. --- Doc/c-api/typeobj.rst | 10 ++++++++++ .../2023-02-09-10-38-20.gh-issue-99293.mFqfpp.rst | 2 ++ 2 files changed, 12 insertions(+) create mode 100644 Misc/NEWS.d/next/C API/2023-02-09-10-38-20.gh-issue-99293.mFqfpp.rst diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 644830b940b417..fd8f49ccb1caab 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -1313,6 +1313,16 @@ and :c:type:`PyType_Type` effectively act as defaults.) .. versionadded:: 3.10 + .. data:: Py_TPFLAGS_VALID_VERSION_TAG + + Internal. Do not set or unset this flag. + To indicate that a class has changed call :c:func:`PyType_Modified` + + .. warning:: + This flag is present in header files, but is an internal feature and should + not be used. It will be removed in a future version of CPython + + .. c:member:: const char* PyTypeObject.tp_doc An optional pointer to a NUL-terminated C string giving the docstring for this diff --git a/Misc/NEWS.d/next/C API/2023-02-09-10-38-20.gh-issue-99293.mFqfpp.rst b/Misc/NEWS.d/next/C API/2023-02-09-10-38-20.gh-issue-99293.mFqfpp.rst new file mode 100644 index 00000000000000..8c0f05543747dc --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-02-09-10-38-20.gh-issue-99293.mFqfpp.rst @@ -0,0 +1,2 @@ +Document that the Py_TPFLAGS_VALID_VERSION_TAG is an internal feature, +should not be used, and will be removed. From cb2411886a181d25e0cff2c870f331d2949874d5 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 9 Feb 2023 19:49:02 +0300 Subject: [PATCH 138/225] gh-101670: typo fix in PyImport_ExtendInittab() (#101723) Co-authored-by: Eric Snow --- Python/import.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/import.c b/Python/import.c index 795d368966481e..302255d76edcd5 100644 --- a/Python/import.c +++ b/Python/import.c @@ -2651,7 +2651,7 @@ PyImport_ExtendInittab(struct _inittab *newtab) int res = 0; if (_PyRuntime.imports.inittab != NULL) { - Py_FatalError("PyImport_ExtendInittab() may be be called after Py_Initialize()"); + Py_FatalError("PyImport_ExtendInittab() may not be called after Py_Initialize()"); } /* Count the number of entries in both tables */ From f23371fbc9bb283207ecebf8efd81a22538b4327 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 9 Feb 2023 17:15:19 +0000 Subject: [PATCH 139/225] LibFFI build requires x64 Cygwin, and skip the ARM build (GH-101743) --- PCbuild/prepare_libffi.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PCbuild/prepare_libffi.bat b/PCbuild/prepare_libffi.bat index 7e7842a2fc97a4..ef36c36e058a15 100644 --- a/PCbuild/prepare_libffi.bat +++ b/PCbuild/prepare_libffi.bat @@ -60,7 +60,7 @@ goto :Usage if NOT DEFINED BUILD_X64 if NOT DEFINED BUILD_X86 if NOT DEFINED BUILD_ARM32 if NOT DEFINED BUILD_ARM64 ( set BUILD_X64=1 set BUILD_X86=1 - set BUILD_ARM32=1 + set BUILD_ARM32=0 set BUILD_ARM64=1 set COPY_LICENSE=1 ) @@ -204,7 +204,7 @@ if NOT DEFINED CYG_CACHE (set CYG_CACHE=C:/cygwin/var/cache/setup) if NOT DEFINED CYG_MIRROR (set CYG_MIRROR=http://mirrors.kernel.org/sourceware/cygwin/) powershell -c "md $env:CYG_ROOT -ErrorAction SilentlyContinue" -powershell -c "$setup = $env:CYG_ROOT+'/setup.exe'; if (!(Test-Path $setup)){invoke-webrequest https://cygwin.com/setup-x86.exe -outfile $setup} +powershell -c "$setup = $env:CYG_ROOT+'/setup.exe'; if (!(Test-Path $setup)){invoke-webrequest https://cygwin.com/setup-x86_64.exe -outfile $setup} %CYG_ROOT%/setup.exe -qnNdO -R "%CYG_ROOT%" -s "%CYG_MIRROR%" -l "%CYG_CACHE%" -P make -P autoconf -P automake -P libtool -P dejagnu endlocal From 6d92373f500eb81a175516b3abb16e21f0806c1f Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Thu, 9 Feb 2023 21:36:24 +0400 Subject: [PATCH 140/225] gh-101283: Fix 'versionchanged' for the shell=True fallback on Windows in 3.12 (GH-101728) --- Doc/library/subprocess.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index d792a43eeb271f..ccc431b2d92e07 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -111,7 +111,7 @@ underlying :class:`Popen` interface can be used directly. Added the *text* parameter, as a more understandable alias of *universal_newlines*. Added the *capture_output* parameter. - .. versionchanged:: 3.11.3 + .. versionchanged:: 3.12 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and @@ -495,7 +495,7 @@ functions. *executable* parameter accepts a bytes and :term:`path-like object` on Windows. - .. versionchanged:: 3.11.3 + .. versionchanged:: 3.12 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and @@ -1174,7 +1174,7 @@ calls these functions. .. versionchanged:: 3.3 *timeout* was added. - .. versionchanged:: 3.11.3 + .. versionchanged:: 3.12 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and @@ -1214,7 +1214,7 @@ calls these functions. .. versionchanged:: 3.3 *timeout* was added. - .. versionchanged:: 3.11.3 + .. versionchanged:: 3.12 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and @@ -1277,7 +1277,7 @@ calls these functions. .. versionadded:: 3.7 *text* was added as a more readable alias for *universal_newlines*. - .. versionchanged:: 3.11.3 + .. versionchanged:: 3.12 Changed Windows shell search order for ``shell=True``. The current directory and ``%PATH%`` are replaced with ``%COMSPEC%`` and From b41c47cd0606e8273aef4813e83fe2deaf9ab33b Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Thu, 9 Feb 2023 09:40:51 -0800 Subject: [PATCH 141/225] gh-101726: Update the OpenSSL version to 1.1.1t (GH-101727) Fixes CVE-2023-0286 (High) and a couple of Medium security issues. https://www.openssl.org/news/secadv/20230207.txt --- .azure-pipelines/ci.yml | 4 ++-- .azure-pipelines/pr.yml | 4 ++-- .github/workflows/build.yml | 6 +++--- Mac/BuildScript/build-installer.py | 6 +++--- .../Security/2023-02-08-22-03-04.gh-issue-101727.9P5eZz.rst | 4 ++++ PCbuild/get_externals.bat | 4 ++-- PCbuild/python.props | 4 ++-- PCbuild/readme.txt | 2 +- Tools/ssl/multissltests.py | 4 ++-- 9 files changed, 21 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2023-02-08-22-03-04.gh-issue-101727.9P5eZz.rst diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml index e45dc2d4365999..6302b547982118 100644 --- a/.azure-pipelines/ci.yml +++ b/.azure-pipelines/ci.yml @@ -57,7 +57,7 @@ jobs: variables: testRunTitle: '$(build.sourceBranchName)-linux' testRunPlatform: linux - openssl_version: 1.1.1q + openssl_version: 1.1.1t steps: - template: ./posix-steps.yml @@ -83,7 +83,7 @@ jobs: variables: testRunTitle: '$(Build.SourceBranchName)-linux-coverage' testRunPlatform: linux-coverage - openssl_version: 1.1.1q + openssl_version: 1.1.1t steps: - template: ./posix-steps.yml diff --git a/.azure-pipelines/pr.yml b/.azure-pipelines/pr.yml index af94ebf78c8488..5f7218768c18af 100644 --- a/.azure-pipelines/pr.yml +++ b/.azure-pipelines/pr.yml @@ -57,7 +57,7 @@ jobs: variables: testRunTitle: '$(system.pullRequest.TargetBranch)-linux' testRunPlatform: linux - openssl_version: 1.1.1q + openssl_version: 1.1.1t steps: - template: ./posix-steps.yml @@ -83,7 +83,7 @@ jobs: variables: testRunTitle: '$(Build.SourceBranchName)-linux-coverage' testRunPlatform: linux-coverage - openssl_version: 1.1.1q + openssl_version: 1.1.1t steps: - template: ./posix-steps.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f798992d8af61c..97ea2d94598e2c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -176,7 +176,7 @@ jobs: needs: check_source if: needs.check_source.outputs.run_tests == 'true' env: - OPENSSL_VER: 1.1.1s + OPENSSL_VER: 1.1.1t PYTHONSTRICTEXTENSIONBUILD: 1 steps: - uses: actions/checkout@v3 @@ -235,7 +235,7 @@ jobs: strategy: fail-fast: false matrix: - openssl_ver: [1.1.1s, 3.0.7, 3.1.0-beta1] + openssl_ver: [1.1.1t, 3.0.8, 3.1.0-beta1] env: OPENSSL_VER: ${{ matrix.openssl_ver }} MULTISSL_DIR: ${{ github.workspace }}/multissl @@ -282,7 +282,7 @@ jobs: needs: check_source if: needs.check_source.outputs.run_tests == 'true' env: - OPENSSL_VER: 1.1.1s + OPENSSL_VER: 1.1.1t PYTHONSTRICTEXTENSIONBUILD: 1 ASAN_OPTIONS: detect_leaks=0:allocator_may_return_null=1:handle_segv=0 steps: diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index cf97b5558c2ddc..048cb01379607e 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -246,9 +246,9 @@ def library_recipes(): result.extend([ dict( - name="OpenSSL 1.1.1s", - url="https://www.openssl.org/source/openssl-1.1.1s.tar.gz", - checksum='c5ac01e760ee6ff0dab61d6b2bbd30146724d063eb322180c6f18a6f74e4b6aa', + name="OpenSSL 1.1.1t", + url="https://www.openssl.org/source/openssl-1.1.1t.tar.gz", + checksum='8dee9b24bdb1dcbf0c3d1e9b02fb8f6bf22165e807f45adeb7c9677536859d3b', buildrecipe=build_universal_openssl, configure=None, install=None, diff --git a/Misc/NEWS.d/next/Security/2023-02-08-22-03-04.gh-issue-101727.9P5eZz.rst b/Misc/NEWS.d/next/Security/2023-02-08-22-03-04.gh-issue-101727.9P5eZz.rst new file mode 100644 index 00000000000000..43acc82063fd7a --- /dev/null +++ b/Misc/NEWS.d/next/Security/2023-02-08-22-03-04.gh-issue-101727.9P5eZz.rst @@ -0,0 +1,4 @@ +Updated the OpenSSL version used in Windows and macOS binary release builds +to 1.1.1t to address CVE-2023-0286, CVE-2022-4303, and CVE-2022-4303 per +`the OpenSSL 2023-02-07 security advisory +`_. diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 0a41d131a3e887..d4d96bd49d72c6 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -53,7 +53,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.3 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1s +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1t set libraries=%libraries% sqlite-3.39.4.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.13.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.13.0 @@ -77,7 +77,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.3 -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1s +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1t if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.13.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff --git a/PCbuild/python.props b/PCbuild/python.props index 57360e57baba66..5926c7ded4708d 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -74,8 +74,8 @@ $(ExternalsDir)libffi-3.4.3\ $(libffiDir)$(ArchName)\ $(libffiOutDir)include - $(ExternalsDir)openssl-1.1.1s\ - $(ExternalsDir)openssl-bin-1.1.1s\$(ArchName)\ + $(ExternalsDir)openssl-1.1.1t\ + $(ExternalsDir)openssl-bin-1.1.1t\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.2.13\ diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index 3ed26a47b066b9..347be8aeeca398 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -169,7 +169,7 @@ _lzma Homepage: https://tukaani.org/xz/ _ssl - Python wrapper for version 1.1.1q of the OpenSSL secure sockets + Python wrapper for version 1.1.1t of the OpenSSL secure sockets library, which is downloaded from our binaries repository at https://github.com/python/cpython-bin-deps. diff --git a/Tools/ssl/multissltests.py b/Tools/ssl/multissltests.py index 5ad597c8347e56..c0fbee9ca6f98f 100755 --- a/Tools/ssl/multissltests.py +++ b/Tools/ssl/multissltests.py @@ -46,8 +46,8 @@ ] OPENSSL_RECENT_VERSIONS = [ - "1.1.1s", - "3.0.7" + "1.1.1t", + "3.0.8" ] LIBRESSL_OLD_VERSIONS = [ From 272da55affe6f2b3b73ff5474e1246089517d051 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Fri, 10 Feb 2023 02:45:58 +0900 Subject: [PATCH 142/225] Fix typo in `test_fstring.py` (#101600) --- Lib/test/test_fstring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 318f38a6ed5b14..a50056da116e32 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -667,7 +667,7 @@ def test_missing_expression(self): "f'''{\t\f\r\n}'''", ]) - # Different error messeges are raised when a specfier ('!', ':' or '=') is used after an empty expression + # Different error messages are raised when a specfier ('!', ':' or '=') is used after an empty expression self.assertAllRaise(SyntaxError, "f-string: expression required before '!'", ["f'{!r}'", "f'{ !r}'", From f1f3af7b8245e61a2e0abef03b2c6c5902ed7df8 Mon Sep 17 00:00:00 2001 From: "Partha P. Mukherjee" Date: Thu, 9 Feb 2023 12:46:40 -0500 Subject: [PATCH 143/225] GH-101228: Fix typo in docstring for read method of `_io.TextIOWrapper` class (#101227) --- Modules/_io/textio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 32ab8a44c62151..ea2ea32c336954 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -56,10 +56,10 @@ textiobase_detach(PyObject *self, PyObject *Py_UNUSED(ignored)) } PyDoc_STRVAR(textiobase_read_doc, - "Read at most n characters from stream.\n" + "Read at most size characters from stream.\n" "\n" - "Read from underlying buffer until we have n characters or we hit EOF.\n" - "If n is negative or omitted, read until EOF.\n" + "Read from underlying buffer until we have size characters or we hit EOF.\n" + "If size is negative or omitted, read until EOF.\n" ); static PyObject * From 5b946d371979a772120e6ee7d37f9b735769d433 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Fri, 10 Feb 2023 08:30:03 +0900 Subject: [PATCH 144/225] gh-101430: Update tracemalloc to handle presize properly. (gh-101745) --- ...-02-10-01-15-57.gh-issue-101430.T3Gegb.rst | 2 ++ Modules/_tracemalloc.c | 24 +++++++------------ Objects/object.c | 9 ++----- 3 files changed, 12 insertions(+), 23 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-10-01-15-57.gh-issue-101430.T3Gegb.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-10-01-15-57.gh-issue-101430.T3Gegb.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-10-01-15-57.gh-issue-101430.T3Gegb.rst new file mode 100644 index 00000000000000..e617d85242144e --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-10-01-15-57.gh-issue-101430.T3Gegb.rst @@ -0,0 +1,2 @@ +Update :mod:`tracemalloc` to handle presize of object properly. Patch by +Dong-hee Na. diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index 9826ad2935beaa..d69c5636486da9 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -2,6 +2,7 @@ #include "pycore_fileutils.h" // _Py_write_noraise() #include "pycore_gc.h" // PyGC_Head #include "pycore_hashtable.h" // _Py_hashtable_t +#include "pycore_object.h" // _PyType_PreHeaderSize #include "pycore_pymem.h" // _Py_tracemalloc_config #include "pycore_runtime.h" // _Py_ID() #include "pycore_traceback.h" @@ -1400,20 +1401,16 @@ _tracemalloc__get_object_traceback(PyObject *module, PyObject *obj) /*[clinic end generated code: output=41ee0553a658b0aa input=29495f1b21c53212]*/ { PyTypeObject *type; - void *ptr; traceback_t *traceback; type = Py_TYPE(obj); - if (PyType_IS_GC(type)) { - ptr = (void *)((char *)obj - sizeof(PyGC_Head)); - } - else { - ptr = (void *)obj; - } + const size_t presize = _PyType_PreHeaderSize(type); + uintptr_t ptr = (uintptr_t)((char *)obj - presize); - traceback = tracemalloc_get_traceback(DEFAULT_DOMAIN, (uintptr_t)ptr); - if (traceback == NULL) + traceback = tracemalloc_get_traceback(DEFAULT_DOMAIN, ptr); + if (traceback == NULL) { Py_RETURN_NONE; + } return traceback_to_pyobject(traceback, NULL); } @@ -1723,14 +1720,9 @@ _PyTraceMalloc_NewReference(PyObject *op) return -1; } - uintptr_t ptr; PyTypeObject *type = Py_TYPE(op); - if (PyType_IS_GC(type)) { - ptr = (uintptr_t)((char *)op - sizeof(PyGC_Head)); - } - else { - ptr = (uintptr_t)op; - } + const size_t presize = _PyType_PreHeaderSize(type); + uintptr_t ptr = (uintptr_t)((char *)op - presize); int res = -1; diff --git a/Objects/object.c b/Objects/object.c index 7817c04ef5f5be..446c7b1f5f0302 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2387,14 +2387,9 @@ _PyObject_AssertFailed(PyObject *obj, const char *expr, const char *msg, /* Display the traceback where the object has been allocated. Do it before dumping repr(obj), since repr() is more likely to crash than dumping the traceback. */ - void *ptr; PyTypeObject *type = Py_TYPE(obj); - if (_PyType_IS_GC(type)) { - ptr = (void *)((char *)obj - sizeof(PyGC_Head)); - } - else { - ptr = (void *)obj; - } + const size_t presize = _PyType_PreHeaderSize(type); + void *ptr = (void *)((char *)obj - presize); _PyMem_DumpTraceback(fileno(stderr), ptr); /* This might succeed or fail, but we're about to abort, so at least From 34c50ceb1e2d40f7faab673d2033ecaafd3c611a Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 10 Feb 2023 06:00:58 +0300 Subject: [PATCH 145/225] gh-101747: Fix refleak in new `OrderedDict` repr (GH-101748) --- Objects/odictobject.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/odictobject.c b/Objects/odictobject.c index ab2bbed35873de..215a8af54fb266 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -1385,6 +1385,7 @@ odict_repr(PyODictObject *self) result = PyUnicode_FromFormat("%s(%R)", _PyType_Name(Py_TYPE(self)), dcopy); + Py_DECREF(dcopy); Done: Py_ReprLeave((PyObject *)self); From 448c7d154e72506158d0a7a766e9f1cb8adf3dec Mon Sep 17 00:00:00 2001 From: abel1502 <32196516+abel1502@users.noreply.github.com> Date: Fri, 10 Feb 2023 06:10:46 +0300 Subject: [PATCH 146/225] Fix some typos in asdl_c.py (GH-101757) --- Parser/asdl_c.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index 3e307610b635f4..db0e597b7a5aa4 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -73,7 +73,7 @@ def reflow_c_string(s, depth): def is_simple(sum_type): """Return True if a sum is a simple. - A sum is simple if it's types have no fields and itself + A sum is simple if its types have no fields and itself doesn't have any attributes. Instances of these types are cached at C level, and they act like singletons when propagating parser generated nodes into Python level, e.g. @@ -352,7 +352,7 @@ def visitSum(self, sum, name): self.visit(t, name, sum.attributes) def get_args(self, fields): - """Return list of C argument into, one for each field. + """Return list of C argument info, one for each field. Argument info is 3-tuple of a C type, variable name, and flag that is true if type can be NULL. From d40a23c0a11060ba7fa076d50980c18a11a13a40 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 10 Feb 2023 08:25:02 +0100 Subject: [PATCH 147/225] gh-101759: Update macOS installer to SQLite 3.40.1 (#101761) --- Mac/BuildScript/build-installer.py | 6 +++--- .../macOS/2023-02-09-22-07-17.gh-issue-101759.B0JP2H.rst | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/macOS/2023-02-09-22-07-17.gh-issue-101759.B0JP2H.rst diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 048cb01379607e..5ea2cfc5183ef5 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -359,9 +359,9 @@ def library_recipes(): ), ), dict( - name="SQLite 3.39.4", - url="https://sqlite.org/2022/sqlite-autoconf-3390400.tar.gz", - checksum="44b7e6691b0954086f717a6c43b622a5", + name="SQLite 3.40.1", + url="https://sqlite.org/2022/sqlite-autoconf-3400100.tar.gz", + checksum="5498af3a357753d473ee713e363fa5b7", extra_cflags=('-Os ' '-DSQLITE_ENABLE_FTS5 ' '-DSQLITE_ENABLE_FTS4 ' diff --git a/Misc/NEWS.d/next/macOS/2023-02-09-22-07-17.gh-issue-101759.B0JP2H.rst b/Misc/NEWS.d/next/macOS/2023-02-09-22-07-17.gh-issue-101759.B0JP2H.rst new file mode 100644 index 00000000000000..fc53d08bffc4fd --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-02-09-22-07-17.gh-issue-101759.B0JP2H.rst @@ -0,0 +1 @@ +Update macOS installer to SQLite 3.40.1. From 826bf0e6957fd0addc321d1baee1fa846e9457eb Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 10 Feb 2023 12:58:14 +0100 Subject: [PATCH 148/225] gh-101277: Finalise isolating itertools (GH-101305) Add repeat, islice, chain, tee, teedataobject, and batched types to module state. Automerge-Triggered-By: GH:erlend-aasland --- ...-01-25-00-14-52.gh-issue-101277.FceHX7.rst | 2 + Modules/clinic/itertoolsmodule.c.h | 6 +- Modules/itertoolsmodule.c | 534 +++++++----------- Tools/c-analyzer/cpython/globals-to-fix.tsv | 22 - 4 files changed, 220 insertions(+), 344 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-01-25-00-14-52.gh-issue-101277.FceHX7.rst diff --git a/Misc/NEWS.d/next/Library/2023-01-25-00-14-52.gh-issue-101277.FceHX7.rst b/Misc/NEWS.d/next/Library/2023-01-25-00-14-52.gh-issue-101277.FceHX7.rst new file mode 100644 index 00000000000000..e09c0e09fb388f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-25-00-14-52.gh-issue-101277.FceHX7.rst @@ -0,0 +1,2 @@ +Remove global state from :mod:`itertools` module (:pep:`687`). Patches by +Erlend E. Aasland. diff --git a/Modules/clinic/itertoolsmodule.c.h b/Modules/clinic/itertoolsmodule.c.h index d15d5f0890ca98..32278bf715aa98 100644 --- a/Modules/clinic/itertoolsmodule.c.h +++ b/Modules/clinic/itertoolsmodule.c.h @@ -232,7 +232,7 @@ static PyObject * itertools_teedataobject(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - PyTypeObject *base_tp = &teedataobject_type; + PyTypeObject *base_tp = clinic_state()->teedataobject_type; PyObject *it; PyObject *values; PyObject *next; @@ -270,7 +270,7 @@ static PyObject * itertools__tee(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - PyTypeObject *base_tp = &tee_type; + PyTypeObject *base_tp = clinic_state()->tee_type; PyObject *iterable; if ((type == base_tp || type->tp_init == base_tp->tp_init) && @@ -913,4 +913,4 @@ itertools_count(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=a08b58d7dac825da input=a9049054013a1b77]*/ +/*[clinic end generated code: output=111cbd102c2a23c9 input=a9049054013a1b77]*/ diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index ce8720d0fd9228..6986695e47b1ae 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -5,6 +5,7 @@ #include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_object.h" // _PyObject_GC_TRACK() #include "pycore_tuple.h" // _PyTuple_ITEMS() +#include "structmember.h" // PyMemberDef #include // offsetof() /* Itertools module written and maintained @@ -13,6 +14,8 @@ typedef struct { PyTypeObject *accumulate_type; + PyTypeObject *batched_type; + PyTypeObject *chain_type; PyTypeObject *combinations_type; PyTypeObject *compress_type; PyTypeObject *count_type; @@ -22,11 +25,15 @@ typedef struct { PyTypeObject *filterfalse_type; PyTypeObject *groupby_type; PyTypeObject *_grouper_type; + PyTypeObject *islice_type; PyTypeObject *pairwise_type; PyTypeObject *permutations_type; PyTypeObject *product_type; + PyTypeObject *repeat_type; PyTypeObject *starmap_type; PyTypeObject *takewhile_type; + PyTypeObject *tee_type; + PyTypeObject *teedataobject_type; PyTypeObject *ziplongest_type; } itertools_state; @@ -60,14 +67,14 @@ find_state_by_type(PyTypeObject *tp) module itertools class itertools.groupby "groupbyobject *" "clinic_state()->groupby_type" class itertools._grouper "_grouperobject *" "clinic_state()->_grouper_type" -class itertools.teedataobject "teedataobject *" "&teedataobject_type" -class itertools._tee "teeobject *" "&tee_type" -class itertools.batched "batchedobject *" "&batched_type" +class itertools.teedataobject "teedataobject *" "clinic_state()->teedataobject_type" +class itertools._tee "teeobject *" "clinic_state()->tee_type" +class itertools.batched "batchedobject *" "clinic_state()->batched_type" class itertools.cycle "cycleobject *" "clinic_state()->cycle_type" class itertools.dropwhile "dropwhileobject *" "clinic_state()->dropwhile_type" class itertools.takewhile "takewhileobject *" "clinic_state()->takewhile_type" class itertools.starmap "starmapobject *" "clinic_state()->starmap_type" -class itertools.chain "chainobject *" "&chain_type" +class itertools.chain "chainobject *" "clinic_state()->chain_type" class itertools.combinations "combinationsobject *" "clinic_state()->combinations_type" class itertools.combinations_with_replacement "cwr_object *" "clinic_state()->cwr_type" class itertools.permutations "permutationsobject *" "clinic_state()->permutations_type" @@ -77,11 +84,7 @@ class itertools.filterfalse "filterfalseobject *" "clinic_state()->filterfalse_t class itertools.count "countobject *" "clinic_state()->count_type" class itertools.pairwise "pairwiseobject *" "clinic_state()->pairwise_type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=28ffff5c0c93eed7]*/ - -static PyTypeObject teedataobject_type; -static PyTypeObject tee_type; -static PyTypeObject batched_type; +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=aa48fe4de9d4080f]*/ #define clinic_state() (find_state_by_type(type)) #define clinic_state_by_cls() (get_module_state_by_cls(base_tp)) @@ -162,17 +165,18 @@ batched_new_impl(PyTypeObject *type, PyObject *iterable, Py_ssize_t n) static void batched_dealloc(batchedobject *bo) { + PyTypeObject *tp = Py_TYPE(bo); PyObject_GC_UnTrack(bo); Py_XDECREF(bo->it); - Py_TYPE(bo)->tp_free(bo); + tp->tp_free(bo); + Py_DECREF(tp); } static int batched_traverse(batchedobject *bo, visitproc visit, void *arg) { - if (bo->it != NULL) { - Py_VISIT(bo->it); - } + Py_VISIT(Py_TYPE(bo)); + Py_VISIT(bo->it); return 0; } @@ -222,48 +226,25 @@ batched_next(batchedobject *bo) return result; } -static PyTypeObject batched_type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "itertools.batched", /* tp_name */ - sizeof(batchedobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)batched_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - batched_new__doc__, /* tp_doc */ - (traverseproc)batched_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)batched_next, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - PyType_GenericAlloc, /* tp_alloc */ - batched_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot batched_slots[] = { + {Py_tp_dealloc, batched_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)batched_new__doc__}, + {Py_tp_traverse, batched_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, batched_next}, + {Py_tp_alloc, PyType_GenericAlloc}, + {Py_tp_new, batched_new}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec batched_spec = { + .name = "itertools.batched", + .basicsize = sizeof(batchedobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = batched_slots, }; @@ -737,14 +718,15 @@ typedef struct { teedataobject *dataobj; int index; /* 0 <= index <= LINKCELLS */ PyObject *weakreflist; + itertools_state *state; } teeobject; static PyObject * -teedataobject_newinternal(PyObject *it) +teedataobject_newinternal(itertools_state *state, PyObject *it) { teedataobject *tdo; - tdo = PyObject_GC_New(teedataobject, &teedataobject_type); + tdo = PyObject_GC_New(teedataobject, state->teedataobject_type); if (tdo == NULL) return NULL; @@ -757,10 +739,10 @@ teedataobject_newinternal(PyObject *it) } static PyObject * -teedataobject_jumplink(teedataobject *tdo) +teedataobject_jumplink(itertools_state *state, teedataobject *tdo) { if (tdo->nextlink == NULL) - tdo->nextlink = teedataobject_newinternal(tdo->it); + tdo->nextlink = teedataobject_newinternal(state, tdo->it); return Py_XNewRef(tdo->nextlink); } @@ -796,6 +778,7 @@ teedataobject_traverse(teedataobject *tdo, visitproc visit, void * arg) { int i; + Py_VISIT(Py_TYPE(tdo)); Py_VISIT(tdo->it); for (i = 0; i < tdo->numread; i++) Py_VISIT(tdo->values[i]); @@ -804,9 +787,9 @@ teedataobject_traverse(teedataobject *tdo, visitproc visit, void * arg) } static void -teedataobject_safe_decref(PyObject *obj) +teedataobject_safe_decref(PyObject *obj, PyTypeObject *tdo_type) { - while (obj && Py_IS_TYPE(obj, &teedataobject_type) && + while (obj && Py_IS_TYPE(obj, tdo_type) && Py_REFCNT(obj) == 1) { PyObject *nextlink = ((teedataobject *)obj)->nextlink; ((teedataobject *)obj)->nextlink = NULL; @@ -826,16 +809,19 @@ teedataobject_clear(teedataobject *tdo) Py_CLEAR(tdo->values[i]); tmp = tdo->nextlink; tdo->nextlink = NULL; - teedataobject_safe_decref(tmp); + itertools_state *state = get_module_state_by_cls(Py_TYPE(tdo)); + teedataobject_safe_decref(tmp, state->teedataobject_type); return 0; } static void teedataobject_dealloc(teedataobject *tdo) { + PyTypeObject *tp = Py_TYPE(tdo); PyObject_GC_UnTrack(tdo); teedataobject_clear(tdo); PyObject_GC_Del(tdo); + Py_DECREF(tp); } static PyObject * @@ -874,9 +860,10 @@ itertools_teedataobject_impl(PyTypeObject *type, PyObject *it, teedataobject *tdo; Py_ssize_t i, len; - assert(type == &teedataobject_type); + itertools_state *state = get_module_state_by_cls(type); + assert(type == state->teedataobject_type); - tdo = (teedataobject *)teedataobject_newinternal(it); + tdo = (teedataobject *)teedataobject_newinternal(state, it); if (!tdo) return NULL; @@ -892,7 +879,7 @@ itertools_teedataobject_impl(PyTypeObject *type, PyObject *it, if (len == LINKCELLS) { if (next != Py_None) { - if (!Py_IS_TYPE(next, &teedataobject_type)) + if (!Py_IS_TYPE(next, state->teedataobject_type)) goto err; assert(tdo->nextlink == NULL); tdo->nextlink = Py_NewRef(next); @@ -915,47 +902,24 @@ static PyMethodDef teedataobject_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject teedataobject_type = { - PyVarObject_HEAD_INIT(0, 0) /* Must fill in type value later */ - "itertools._tee_dataobject", /* tp_name */ - sizeof(teedataobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)teedataobject_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - itertools_teedataobject__doc__, /* tp_doc */ - (traverseproc)teedataobject_traverse, /* tp_traverse */ - (inquiry)teedataobject_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - teedataobject_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools_teedataobject, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot teedataobject_slots[] = { + {Py_tp_dealloc, teedataobject_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)itertools_teedataobject__doc__}, + {Py_tp_traverse, teedataobject_traverse}, + {Py_tp_clear, teedataobject_clear}, + {Py_tp_methods, teedataobject_methods}, + {Py_tp_new, itertools_teedataobject}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec teedataobject_spec = { + .name = "itertools._tee_dataobject", + .basicsize = sizeof(teedataobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = teedataobject_slots, }; @@ -965,7 +929,7 @@ tee_next(teeobject *to) PyObject *value, *link; if (to->index >= LINKCELLS) { - link = teedataobject_jumplink(to->dataobj); + link = teedataobject_jumplink(to->state, to->dataobj); if (link == NULL) return NULL; Py_SETREF(to->dataobj, (teedataobject *)link); @@ -981,6 +945,7 @@ tee_next(teeobject *to) static int tee_traverse(teeobject *to, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(to)); Py_VISIT((PyObject *)to->dataobj); return 0; } @@ -990,12 +955,13 @@ tee_copy(teeobject *to, PyObject *Py_UNUSED(ignored)) { teeobject *newto; - newto = PyObject_GC_New(teeobject, &tee_type); + newto = PyObject_GC_New(teeobject, Py_TYPE(to)); if (newto == NULL) return NULL; newto->dataobj = (teedataobject*)Py_NewRef(to->dataobj); newto->index = to->index; newto->weakreflist = NULL; + newto->state = to->state; PyObject_GC_Track(newto); return (PyObject *)newto; } @@ -1003,7 +969,7 @@ tee_copy(teeobject *to, PyObject *Py_UNUSED(ignored)) PyDoc_STRVAR(teecopy_doc, "Returns an independent iterator."); static PyObject * -tee_fromiterable(PyObject *iterable) +tee_fromiterable(itertools_state *state, PyObject *iterable) { teeobject *to; PyObject *it; @@ -1011,17 +977,17 @@ tee_fromiterable(PyObject *iterable) it = PyObject_GetIter(iterable); if (it == NULL) return NULL; - if (PyObject_TypeCheck(it, &tee_type)) { + if (PyObject_TypeCheck(it, state->tee_type)) { to = (teeobject *)tee_copy((teeobject *)it, NULL); goto done; } - PyObject *dataobj = teedataobject_newinternal(it); + PyObject *dataobj = teedataobject_newinternal(state, it); if (!dataobj) { to = NULL; goto done; } - to = PyObject_GC_New(teeobject, &tee_type); + to = PyObject_GC_New(teeobject, state->tee_type); if (to == NULL) { Py_DECREF(dataobj); goto done; @@ -1029,6 +995,7 @@ tee_fromiterable(PyObject *iterable) to->dataobj = (teedataobject *)dataobj; to->index = 0; to->weakreflist = NULL; + to->state = state; PyObject_GC_Track(to); done: Py_DECREF(it); @@ -1047,7 +1014,8 @@ static PyObject * itertools__tee_impl(PyTypeObject *type, PyObject *iterable) /*[clinic end generated code: output=b02d3fd26c810c3f input=adc0779d2afe37a2]*/ { - return tee_fromiterable(iterable); + itertools_state *state = get_module_state_by_cls(type); + return tee_fromiterable(state, iterable); } static int @@ -1062,9 +1030,11 @@ tee_clear(teeobject *to) static void tee_dealloc(teeobject *to) { + PyTypeObject *tp = Py_TYPE(to); PyObject_GC_UnTrack(to); tee_clear(to); PyObject_GC_Del(to); + Py_DECREF(tp); } static PyObject * @@ -1082,7 +1052,8 @@ tee_setstate(teeobject *to, PyObject *state) PyErr_SetString(PyExc_TypeError, "state is not a tuple"); return NULL; } - if (!PyArg_ParseTuple(state, "O!i", &teedataobject_type, &tdo, &index)) { + PyTypeObject *tdo_type = to->state->teedataobject_type; + if (!PyArg_ParseTuple(state, "O!i", tdo_type, &tdo, &index)) { return NULL; } if (index < 0 || index > LINKCELLS) { @@ -1102,47 +1073,31 @@ static PyMethodDef tee_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject tee_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools._tee", /* tp_name */ - sizeof(teeobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)tee_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - itertools__tee__doc__, /* tp_doc */ - (traverseproc)tee_traverse, /* tp_traverse */ - (inquiry)tee_clear, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(teeobject, weakreflist), /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)tee_next, /* tp_iternext */ - tee_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itertools__tee, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyMemberDef tee_members[] = { + {"__weaklistoffset__", T_PYSSIZET, offsetof(teeobject, weakreflist), READONLY}, + {NULL}, +}; + +static PyType_Slot tee_slots[] = { + {Py_tp_dealloc, tee_dealloc}, + {Py_tp_doc, (void *)itertools__tee__doc__}, + {Py_tp_traverse, tee_traverse}, + {Py_tp_clear, tee_clear}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, tee_next}, + {Py_tp_methods, tee_methods}, + {Py_tp_members, tee_members}, + {Py_tp_new, itertools__tee}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec tee_spec = { + .name = "itertools._tee", + .basicsize = sizeof(teeobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = tee_slots, }; /*[clinic input] @@ -1184,7 +1139,8 @@ itertools_tee_impl(PyObject *module, PyObject *iterable, Py_ssize_t n) copyable = it; } else { - copyable = tee_fromiterable(it); + itertools_state *state = get_module_state(module); + copyable = tee_fromiterable(state, it); Py_DECREF(it); if (copyable == NULL) { Py_DECREF(result); @@ -1682,8 +1638,6 @@ typedef struct { Py_ssize_t cnt; } isliceobject; -static PyTypeObject islice_type; - static PyObject * islice_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { @@ -1693,7 +1647,9 @@ islice_new(PyTypeObject *type, PyObject *args, PyObject *kwds) Py_ssize_t numargs; isliceobject *lz; - if ((type == &islice_type || type->tp_init == islice_type.tp_init) && + itertools_state *st = find_state_by_type(type); + PyTypeObject *islice_type = st->islice_type; + if ((type == islice_type || type->tp_init == islice_type->tp_init) && !_PyArg_NoKeywords("islice", kwds)) return NULL; @@ -1772,14 +1728,17 @@ islice_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static void islice_dealloc(isliceobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->it); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int islice_traverse(isliceobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->it); return 0; } @@ -1885,48 +1844,25 @@ specified as another value, step determines how many values are\n\ skipped between successive calls. Works like a slice() on a list\n\ but returns an iterator."); -static PyTypeObject islice_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.islice", /* tp_name */ - sizeof(isliceobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)islice_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - islice_doc, /* tp_doc */ - (traverseproc)islice_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)islice_next, /* tp_iternext */ - islice_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - islice_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot islice_slots[] = { + {Py_tp_dealloc, islice_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)islice_doc}, + {Py_tp_traverse, islice_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, islice_next}, + {Py_tp_methods, islice_methods}, + {Py_tp_new, islice_new}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec islice_spec = { + .name = "itertools.islice", + .basicsize = sizeof(isliceobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = islice_slots, }; @@ -2056,8 +1992,6 @@ typedef struct { PyObject *active; /* Currently running input iterator */ } chainobject; -static PyTypeObject chain_type; - static PyObject * chain_new_internal(PyTypeObject *type, PyObject *source) { @@ -2079,7 +2013,9 @@ chain_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *source; - if ((type == &chain_type || type->tp_init == chain_type.tp_init) && + itertools_state *state = find_state_by_type(type); + PyTypeObject *chain_type = state->chain_type; + if ((type == chain_type || type->tp_init == chain_type->tp_init) && !_PyArg_NoKeywords("chain", kwds)) return NULL; @@ -2114,15 +2050,18 @@ itertools_chain_from_iterable(PyTypeObject *type, PyObject *arg) static void chain_dealloc(chainobject *lz) { + PyTypeObject *tp = Py_TYPE(lz); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->active); Py_XDECREF(lz->source); - Py_TYPE(lz)->tp_free(lz); + tp->tp_free(lz); + Py_DECREF(tp); } static int chain_traverse(chainobject *lz, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(lz)); Py_VISIT(lz->source); Py_VISIT(lz->active); return 0; @@ -2227,48 +2166,25 @@ static PyMethodDef chain_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyTypeObject chain_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.chain", /* tp_name */ - sizeof(chainobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)chain_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - chain_doc, /* tp_doc */ - (traverseproc)chain_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)chain_next, /* tp_iternext */ - chain_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - chain_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot chain_slots[] = { + {Py_tp_dealloc, chain_dealloc}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)chain_doc}, + {Py_tp_traverse, chain_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, chain_next}, + {Py_tp_methods, chain_methods}, + {Py_tp_new, chain_new}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec chain_spec = { + .name = "itertools.chain", + .basicsize = sizeof(chainobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = chain_slots, }; @@ -3574,6 +3490,7 @@ typedef struct { PyObject *it; PyObject *binop; PyObject *initial; + itertools_state *state; } accumulateobject; /*[clinic input] @@ -3612,6 +3529,7 @@ itertools_accumulate_impl(PyTypeObject *type, PyObject *iterable, lz->total = NULL; lz->it = it; lz->initial = Py_XNewRef(initial); + lz->state = find_state_by_type(type); return (PyObject *)lz; } @@ -3674,13 +3592,13 @@ accumulate_next(accumulateobject *lz) static PyObject * accumulate_reduce(accumulateobject *lz, PyObject *Py_UNUSED(ignored)) { + itertools_state *state = lz->state; + if (lz->initial != Py_None) { PyObject *it; assert(lz->total == NULL); - if (PyType_Ready(&chain_type) < 0) - return NULL; - it = PyObject_CallFunction((PyObject *)&chain_type, "(O)O", + it = PyObject_CallFunction((PyObject *)(state->chain_type), "(O)O", lz->initial, lz->it); if (it == NULL) return NULL; @@ -3690,11 +3608,7 @@ accumulate_reduce(accumulateobject *lz, PyObject *Py_UNUSED(ignored)) if (lz->total == Py_None) { PyObject *it; - if (PyType_Ready(&chain_type) < 0) - return NULL; - if (PyType_Ready(&islice_type) < 0) - return NULL; - it = PyObject_CallFunction((PyObject *)&chain_type, "(O)O", + it = PyObject_CallFunction((PyObject *)(state->chain_type), "(O)O", lz->total, lz->it); if (it == NULL) return NULL; @@ -3702,7 +3616,8 @@ accumulate_reduce(accumulateobject *lz, PyObject *Py_UNUSED(ignored)) it, lz->binop ? lz->binop : Py_None); if (it == NULL) return NULL; - return Py_BuildValue("O(NiO)", &islice_type, it, 1, Py_None); + + return Py_BuildValue("O(NiO)", state->islice_type, it, 1, Py_None); } return Py_BuildValue("O(OO)O", Py_TYPE(lz), lz->it, lz->binop?lz->binop:Py_None, @@ -4261,8 +4176,6 @@ typedef struct { Py_ssize_t cnt; } repeatobject; -static PyTypeObject repeat_type; - static PyObject * repeat_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { @@ -4292,14 +4205,17 @@ repeat_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static void repeat_dealloc(repeatobject *ro) { + PyTypeObject *tp = Py_TYPE(ro); PyObject_GC_UnTrack(ro); Py_XDECREF(ro->element); - Py_TYPE(ro)->tp_free(ro); + tp->tp_free(ro); + Py_DECREF(tp); } static int repeat_traverse(repeatobject *ro, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(ro)); Py_VISIT(ro->element); return 0; } @@ -4361,48 +4277,26 @@ PyDoc_STRVAR(repeat_doc, for the specified number of times. If not specified, returns the object\n\ endlessly."); -static PyTypeObject repeat_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.repeat", /* tp_name */ - sizeof(repeatobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)repeat_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)repeat_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - repeat_doc, /* tp_doc */ - (traverseproc)repeat_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)repeat_next, /* tp_iternext */ - repeat_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - repeat_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ +static PyType_Slot repeat_slots[] = { + {Py_tp_dealloc, repeat_dealloc}, + {Py_tp_repr, repeat_repr}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_doc, (void *)repeat_doc}, + {Py_tp_traverse, repeat_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, repeat_next}, + {Py_tp_methods, repeat_methods}, + {Py_tp_new, repeat_new}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; + +static PyType_Spec repeat_spec = { + .name = "itertools.repeat", + .basicsize = sizeof(repeatobject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = repeat_slots, }; @@ -4695,6 +4589,8 @@ itertoolsmodule_traverse(PyObject *mod, visitproc visit, void *arg) { itertools_state *state = get_module_state(mod); Py_VISIT(state->accumulate_type); + Py_VISIT(state->batched_type); + Py_VISIT(state->chain_type); Py_VISIT(state->combinations_type); Py_VISIT(state->compress_type); Py_VISIT(state->count_type); @@ -4704,11 +4600,15 @@ itertoolsmodule_traverse(PyObject *mod, visitproc visit, void *arg) Py_VISIT(state->filterfalse_type); Py_VISIT(state->groupby_type); Py_VISIT(state->_grouper_type); + Py_VISIT(state->islice_type); Py_VISIT(state->pairwise_type); Py_VISIT(state->permutations_type); Py_VISIT(state->product_type); + Py_VISIT(state->repeat_type); Py_VISIT(state->starmap_type); Py_VISIT(state->takewhile_type); + Py_VISIT(state->tee_type); + Py_VISIT(state->teedataobject_type); Py_VISIT(state->ziplongest_type); return 0; } @@ -4718,6 +4618,8 @@ itertoolsmodule_clear(PyObject *mod) { itertools_state *state = get_module_state(mod); Py_CLEAR(state->accumulate_type); + Py_CLEAR(state->batched_type); + Py_CLEAR(state->chain_type); Py_CLEAR(state->combinations_type); Py_CLEAR(state->compress_type); Py_CLEAR(state->count_type); @@ -4727,11 +4629,15 @@ itertoolsmodule_clear(PyObject *mod) Py_CLEAR(state->filterfalse_type); Py_CLEAR(state->groupby_type); Py_CLEAR(state->_grouper_type); + Py_CLEAR(state->islice_type); Py_CLEAR(state->pairwise_type); Py_CLEAR(state->permutations_type); Py_CLEAR(state->product_type); + Py_CLEAR(state->repeat_type); Py_CLEAR(state->starmap_type); Py_CLEAR(state->takewhile_type); + Py_CLEAR(state->tee_type); + Py_CLEAR(state->teedataobject_type); Py_CLEAR(state->ziplongest_type); return 0; } @@ -4758,6 +4664,8 @@ itertoolsmodule_exec(PyObject *mod) { itertools_state *state = get_module_state(mod); ADD_TYPE(mod, state->accumulate_type, &accumulate_spec); + ADD_TYPE(mod, state->batched_type, &batched_spec); + ADD_TYPE(mod, state->chain_type, &chain_spec); ADD_TYPE(mod, state->combinations_type, &combinations_spec); ADD_TYPE(mod, state->compress_type, &compress_spec); ADD_TYPE(mod, state->count_type, &count_spec); @@ -4767,30 +4675,18 @@ itertoolsmodule_exec(PyObject *mod) ADD_TYPE(mod, state->filterfalse_type, &filterfalse_spec); ADD_TYPE(mod, state->groupby_type, &groupby_spec); ADD_TYPE(mod, state->_grouper_type, &_grouper_spec); + ADD_TYPE(mod, state->islice_type, &islice_spec); ADD_TYPE(mod, state->pairwise_type, &pairwise_spec); ADD_TYPE(mod, state->permutations_type, &permutations_spec); ADD_TYPE(mod, state->product_type, &product_spec); + ADD_TYPE(mod, state->repeat_type, &repeat_spec); ADD_TYPE(mod, state->starmap_type, &starmap_spec); ADD_TYPE(mod, state->takewhile_type, &takewhile_spec); + ADD_TYPE(mod, state->tee_type, &tee_spec); + ADD_TYPE(mod, state->teedataobject_type, &teedataobject_spec); ADD_TYPE(mod, state->ziplongest_type, &ziplongest_spec); - PyTypeObject *typelist[] = { - &batched_type, - &islice_type, - &chain_type, - &repeat_type, - &tee_type, - &teedataobject_type - }; - - Py_SET_TYPE(&teedataobject_type, &PyType_Type); - - for (size_t i = 0; i < Py_ARRAY_LENGTH(typelist); i++) { - if (PyModule_AddType(mod, typelist[i]) < 0) { - return -1; - } - } - + Py_SET_TYPE(state->teedataobject_type, &PyType_Type); return 0; } diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index cf2d5c368f1bda..52ea0b4901d4bb 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -335,28 +335,6 @@ Modules/_testcapi/vectorcall.c - MethodDescriptorBase_Type - Modules/_testcapi/vectorcall.c - MethodDescriptorDerived_Type - Modules/_testcapi/vectorcall.c - MethodDescriptorNopGet_Type - Modules/_testcapi/vectorcall.c - MethodDescriptor2_Type - -Modules/itertoolsmodule.c - _grouper_type - -Modules/itertoolsmodule.c - accumulate_type - -Modules/itertoolsmodule.c - batched_type - -Modules/itertoolsmodule.c - chain_type - -Modules/itertoolsmodule.c - combinations_type - -Modules/itertoolsmodule.c - compress_type - -Modules/itertoolsmodule.c - count_type - -Modules/itertoolsmodule.c - cwr_type - -Modules/itertoolsmodule.c - cycle_type - -Modules/itertoolsmodule.c - dropwhile_type - -Modules/itertoolsmodule.c - filterfalse_type - -Modules/itertoolsmodule.c - groupby_type - -Modules/itertoolsmodule.c - islice_type - -Modules/itertoolsmodule.c - pairwise_type - -Modules/itertoolsmodule.c - permutations_type - -Modules/itertoolsmodule.c - product_type - -Modules/itertoolsmodule.c - repeat_type - -Modules/itertoolsmodule.c - starmap_type - -Modules/itertoolsmodule.c - takewhile_type - -Modules/itertoolsmodule.c - tee_type - -Modules/itertoolsmodule.c - teedataobject_type - -Modules/itertoolsmodule.c - ziplongest_type - ################################## From 5d15224011217487e1a174c144af0e5f5826c17c Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 10 Feb 2023 17:38:26 +0100 Subject: [PATCH 149/225] gh-101759: Update Windows installer to SQLite 3.40.1 (#101762) --- .../next/Windows/2023-02-09-22-09-27.gh-issue-101759.zFlqSH.rst | 1 + PCbuild/get_externals.bat | 2 +- PCbuild/python.props | 2 +- PCbuild/readme.txt | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-02-09-22-09-27.gh-issue-101759.zFlqSH.rst diff --git a/Misc/NEWS.d/next/Windows/2023-02-09-22-09-27.gh-issue-101759.zFlqSH.rst b/Misc/NEWS.d/next/Windows/2023-02-09-22-09-27.gh-issue-101759.zFlqSH.rst new file mode 100644 index 00000000000000..62bcac34397d2e --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-02-09-22-09-27.gh-issue-101759.zFlqSH.rst @@ -0,0 +1 @@ +Update Windows installer to SQLite 3.40.1. diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index d4d96bd49d72c6..2c424517eae4b1 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -54,7 +54,7 @@ set libraries= set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.3 if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1t -set libraries=%libraries% sqlite-3.39.4.0 +set libraries=%libraries% sqlite-3.40.1.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.13.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.13.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tix-8.4.3.6 diff --git a/PCbuild/python.props b/PCbuild/python.props index 5926c7ded4708d..28ee74d8594759 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -68,7 +68,7 @@ - $(ExternalsDir)sqlite-3.39.4.0\ + $(ExternalsDir)sqlite-3.40.1.0\ $(ExternalsDir)bzip2-1.0.8\ $(ExternalsDir)xz-5.2.5\ $(ExternalsDir)libffi-3.4.3\ diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index 347be8aeeca398..4c799b64c461c1 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -188,7 +188,7 @@ _ssl again when building. _sqlite3 - Wraps SQLite 3.39.4, which is itself built by sqlite3.vcxproj + Wraps SQLite 3.40.1, which is itself built by sqlite3.vcxproj Homepage: https://www.sqlite.org/ _tkinter From 366b94905869d680b3f1d4801fb497e78811e511 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Fri, 10 Feb 2023 16:49:29 +0000 Subject: [PATCH 150/225] gh-101517: make bdb avoid looking up in linecache with lineno=None (#101787) --- Lib/bdb.py | 7 ++++--- Lib/test/test_bdb.py | 6 ++++++ .../Library/2023-02-10-16-02-29.gh-issue-101517.r7S2u8.rst | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-10-16-02-29.gh-issue-101517.r7S2u8.rst diff --git a/Lib/bdb.py b/Lib/bdb.py index 81fbb8514acb6f..7f9b09514ffd00 100644 --- a/Lib/bdb.py +++ b/Lib/bdb.py @@ -570,9 +570,10 @@ def format_stack_entry(self, frame_lineno, lprefix=': '): rv = frame.f_locals['__return__'] s += '->' s += reprlib.repr(rv) - line = linecache.getline(filename, lineno, frame.f_globals) - if line: - s += lprefix + line.strip() + if lineno is not None: + line = linecache.getline(filename, lineno, frame.f_globals) + if line: + s += lprefix + line.strip() return s # The following methods can be called by clients to use diff --git a/Lib/test/test_bdb.py b/Lib/test/test_bdb.py index 87a5ac308a12df..042c2daea7f797 100644 --- a/Lib/test/test_bdb.py +++ b/Lib/test/test_bdb.py @@ -1203,5 +1203,11 @@ def main(): tracer.runcall(tfunc_import) +class TestRegressions(unittest.TestCase): + def test_format_stack_entry_no_lineno(self): + # See gh-101517 + Bdb().format_stack_entry((sys._getframe(), None)) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2023-02-10-16-02-29.gh-issue-101517.r7S2u8.rst b/Misc/NEWS.d/next/Library/2023-02-10-16-02-29.gh-issue-101517.r7S2u8.rst new file mode 100644 index 00000000000000..a5f6bdfa5ac2f0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-10-16-02-29.gh-issue-101517.r7S2u8.rst @@ -0,0 +1 @@ +Fixed bug where :mod:`bdb` looks up the source line with :mod:`linecache` with a ``lineno=None``, which causes it to fail with an unhandled exception. From e1aadedf099e645fd2eb1aa8bdcde5a105cee95d Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 10 Feb 2023 16:57:30 +0000 Subject: [PATCH 151/225] gh-101763: Update bundled copy of libffi to 3.4.4 on Windows (GH-101784) --- .../Windows/2023-02-10-14-26-05.gh-issue-101763.RPaj7r.rst | 1 + PCbuild/get_externals.bat | 4 ++-- PCbuild/python.props | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-02-10-14-26-05.gh-issue-101763.RPaj7r.rst diff --git a/Misc/NEWS.d/next/Windows/2023-02-10-14-26-05.gh-issue-101763.RPaj7r.rst b/Misc/NEWS.d/next/Windows/2023-02-10-14-26-05.gh-issue-101763.RPaj7r.rst new file mode 100644 index 00000000000000..e7e5a73afeb532 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-02-10-14-26-05.gh-issue-101763.RPaj7r.rst @@ -0,0 +1 @@ +Updates copy of libffi bundled with Windows installs to 3.4.4. diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 2c424517eae4b1..128241393f9f09 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -52,7 +52,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.8 -if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.3 +if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.4 if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1t set libraries=%libraries% sqlite-3.40.1.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.13.0 @@ -76,7 +76,7 @@ for %%e in (%libraries%) do ( echo.Fetching external binaries... set binaries= -if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.3 +if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.4 if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1t if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.13.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff --git a/PCbuild/python.props b/PCbuild/python.props index 28ee74d8594759..7994fbe7cd5e0b 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -71,7 +71,7 @@ $(ExternalsDir)sqlite-3.40.1.0\ $(ExternalsDir)bzip2-1.0.8\ $(ExternalsDir)xz-5.2.5\ - $(ExternalsDir)libffi-3.4.3\ + $(ExternalsDir)libffi-3.4.4\ $(libffiDir)$(ArchName)\ $(libffiOutDir)include $(ExternalsDir)openssl-1.1.1t\ From 2037ebf81bd4bbe5421421b822bd57cfd665a1e9 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 10 Feb 2023 18:54:04 +0100 Subject: [PATCH 152/225] Docs: use parameter list for sqlite3.Cursor.execute* (#101782) Co-authored-by: Alex Waygood --- Doc/library/sqlite3.rst | 45 ++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index bbdc891c930cf4..8ffc0aad91995c 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1418,15 +1418,22 @@ Cursor objects .. method:: execute(sql, parameters=(), /) - Execute SQL statement *sql*. - Bind values to the statement using :ref:`placeholders - ` that map to the :term:`sequence` or :class:`dict` - *parameters*. + Execute SQL a single SQL statement, + optionally binding Python values using + :ref:`placeholders `. - :meth:`execute` will only execute a single SQL statement. If you try to execute - more than one statement with it, it will raise a :exc:`ProgrammingError`. Use - :meth:`executescript` if you want to execute multiple SQL statements with one - call. + :param str sql: + A single SQL statement. + + :param parameters: + Python values to bind to placeholders in *sql*. + A :class:`!dict` if named placeholders are used. + A :term:`!sequence` if unnamed placeholders are used. + See :ref:`sqlite3-placeholders`. + :type parameters: :class:`dict` | :term:`sequence` + + :raises ProgrammingError: + If *sql* contains more than one SQL statement. If :attr:`~Connection.autocommit` is :data:`LEGACY_TRANSACTION_CONTROL`, @@ -1435,15 +1442,29 @@ Cursor objects and there is no open transaction, a transaction is implicitly opened before executing *sql*. + Use :meth:`executescript` to execute multiple SQL statements. .. method:: executemany(sql, parameters, /) - Execute :ref:`parameterized ` SQL statement *sql* - against all parameter sequences or mappings found in the sequence - *parameters*. It is also possible to use an - :term:`iterator` yielding parameters instead of a sequence. + For every item in *parameters*, + repeatedly execute the :ref:`parameterized ` + SQL statement *sql*. + Uses the same implicit transaction handling as :meth:`~Cursor.execute`. + :param str sql: + A single SQL :abbr:`DML (Data Manipulation Language)` statement. + + :param parameters: + An :term:`!iterable` of parameters to bind with + the placeholders in *sql*. + See :ref:`sqlite3-placeholders`. + :type parameters: :term:`iterable` + + :raises ProgrammingError: + If *sql* contains more than one SQL statement, + or is not a DML statment. + Example: .. testcode:: sqlite3.cursor From 61f2be08661949e2f6dfc94143436297e60d47de Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 10 Feb 2023 20:46:12 +0200 Subject: [PATCH 153/225] Docs: Fix getstatus() -> getcode() typos (#101296) --- Doc/library/http.client.rst | 2 +- Doc/library/urllib.request.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst index 48582219695b41..ad3416135e307b 100644 --- a/Doc/library/http.client.rst +++ b/Doc/library/http.client.rst @@ -532,7 +532,7 @@ statement. .. deprecated:: 3.9 Deprecated in favor of :attr:`~HTTPResponse.headers`. -.. method:: HTTPResponse.getstatus() +.. method:: HTTPResponse.getcode() .. deprecated:: 3.9 Deprecated in favor of :attr:`~HTTPResponse.status`. diff --git a/Doc/library/urllib.request.rst b/Doc/library/urllib.request.rst index 59e1f2da828a83..64cc9c388ec30d 100644 --- a/Doc/library/urllib.request.rst +++ b/Doc/library/urllib.request.rst @@ -1630,7 +1630,7 @@ The typical response object is a :class:`urllib.response.addinfourl` instance: .. deprecated:: 3.9 Deprecated in favor of :attr:`~addinfourl.status`. - .. method:: getstatus() + .. method:: getcode() .. deprecated:: 3.9 Deprecated in favor of :attr:`~addinfourl.status`. From 17143e2c30ae5e51945e04eeaec7ebb0e1f07fb5 Mon Sep 17 00:00:00 2001 From: busywhitespace Date: Sat, 11 Feb 2023 00:29:24 +0100 Subject: [PATCH 154/225] gh-101390: Fix docs for `imporlib.util.LazyLoader.factory` to properly call it a class method (GH-101391) --- Doc/library/importlib.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 3fc1531c0cdf19..89efa64c6b5203 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1387,7 +1387,7 @@ an :term:`importer`. .. classmethod:: factory(loader) - A static method which returns a callable that creates a lazy loader. This + A class method which returns a callable that creates a lazy loader. This is meant to be used in situations where the loader is passed by class instead of by instance. :: From b652d40f1c88fcd8595cd401513f6b7f8e499471 Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Sat, 11 Feb 2023 14:07:39 +0530 Subject: [PATCH 155/225] GH-101797: allocate `PyExpat_CAPI` capsule on heap (#101798) --- Modules/pyexpat.c | 72 +++++++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index 63a3392d5efe7d..0a744998b6c514 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -1878,6 +1878,18 @@ add_features(PyObject *mod) } #endif +static void +pyexpat_capsule_destructor(PyObject *capsule) +{ + void *p = PyCapsule_GetPointer(capsule, PyExpat_CAPSULE_NAME); + if (p == NULL) { + PyErr_WriteUnraisable(capsule); + return; + } + PyMem_Free(p); +} + + static int pyexpat_exec(PyObject *mod) { @@ -1965,40 +1977,46 @@ pyexpat_exec(PyObject *mod) MYCONST(XML_PARAM_ENTITY_PARSING_ALWAYS); #undef MYCONST - static struct PyExpat_CAPI capi; + struct PyExpat_CAPI *capi = PyMem_Malloc(sizeof(*capi)); + if (capi == NULL) { + PyErr_NoMemory(); + return -1; + } /* initialize pyexpat dispatch table */ - capi.size = sizeof(capi); - capi.magic = PyExpat_CAPI_MAGIC; - capi.MAJOR_VERSION = XML_MAJOR_VERSION; - capi.MINOR_VERSION = XML_MINOR_VERSION; - capi.MICRO_VERSION = XML_MICRO_VERSION; - capi.ErrorString = XML_ErrorString; - capi.GetErrorCode = XML_GetErrorCode; - capi.GetErrorColumnNumber = XML_GetErrorColumnNumber; - capi.GetErrorLineNumber = XML_GetErrorLineNumber; - capi.Parse = XML_Parse; - capi.ParserCreate_MM = XML_ParserCreate_MM; - capi.ParserFree = XML_ParserFree; - capi.SetCharacterDataHandler = XML_SetCharacterDataHandler; - capi.SetCommentHandler = XML_SetCommentHandler; - capi.SetDefaultHandlerExpand = XML_SetDefaultHandlerExpand; - capi.SetElementHandler = XML_SetElementHandler; - capi.SetNamespaceDeclHandler = XML_SetNamespaceDeclHandler; - capi.SetProcessingInstructionHandler = XML_SetProcessingInstructionHandler; - capi.SetUnknownEncodingHandler = XML_SetUnknownEncodingHandler; - capi.SetUserData = XML_SetUserData; - capi.SetStartDoctypeDeclHandler = XML_SetStartDoctypeDeclHandler; - capi.SetEncoding = XML_SetEncoding; - capi.DefaultUnknownEncodingHandler = PyUnknownEncodingHandler; + capi->size = sizeof(*capi); + capi->magic = PyExpat_CAPI_MAGIC; + capi->MAJOR_VERSION = XML_MAJOR_VERSION; + capi->MINOR_VERSION = XML_MINOR_VERSION; + capi->MICRO_VERSION = XML_MICRO_VERSION; + capi->ErrorString = XML_ErrorString; + capi->GetErrorCode = XML_GetErrorCode; + capi->GetErrorColumnNumber = XML_GetErrorColumnNumber; + capi->GetErrorLineNumber = XML_GetErrorLineNumber; + capi->Parse = XML_Parse; + capi->ParserCreate_MM = XML_ParserCreate_MM; + capi->ParserFree = XML_ParserFree; + capi->SetCharacterDataHandler = XML_SetCharacterDataHandler; + capi->SetCommentHandler = XML_SetCommentHandler; + capi->SetDefaultHandlerExpand = XML_SetDefaultHandlerExpand; + capi->SetElementHandler = XML_SetElementHandler; + capi->SetNamespaceDeclHandler = XML_SetNamespaceDeclHandler; + capi->SetProcessingInstructionHandler = XML_SetProcessingInstructionHandler; + capi->SetUnknownEncodingHandler = XML_SetUnknownEncodingHandler; + capi->SetUserData = XML_SetUserData; + capi->SetStartDoctypeDeclHandler = XML_SetStartDoctypeDeclHandler; + capi->SetEncoding = XML_SetEncoding; + capi->DefaultUnknownEncodingHandler = PyUnknownEncodingHandler; #if XML_COMBINED_VERSION >= 20100 - capi.SetHashSalt = XML_SetHashSalt; + capi->SetHashSalt = XML_SetHashSalt; #else - capi.SetHashSalt = NULL; + capi->SetHashSalt = NULL; #endif /* export using capsule */ - PyObject *capi_object = PyCapsule_New(&capi, PyExpat_CAPSULE_NAME, NULL); + PyObject *capi_object = PyCapsule_New(capi, PyExpat_CAPSULE_NAME, + pyexpat_capsule_destructor); if (capi_object == NULL) { + PyMem_Free(capi); return -1; } From 3eb12df8b526aa5a2ca6b43f21a1c5e7d38ee634 Mon Sep 17 00:00:00 2001 From: mjoerg Date: Sat, 11 Feb 2023 16:34:15 +0100 Subject: [PATCH 156/225] Fix typo in test_fstring.py (#101823) --- Lib/test/test_fstring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index a50056da116e32..b3f6ef41d77b8f 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -667,7 +667,7 @@ def test_missing_expression(self): "f'''{\t\f\r\n}'''", ]) - # Different error messages are raised when a specfier ('!', ':' or '=') is used after an empty expression + # Different error messages are raised when a specifier ('!', ':' or '=') is used after an empty expression self.assertAllRaise(SyntaxError, "f-string: expression required before '!'", ["f'{!r}'", "f'{ !r}'", From 1d194235e4d5981b5fea25c75318d61189103a58 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 11 Feb 2023 20:54:28 -0800 Subject: [PATCH 157/225] gh-89792: Prevent test_tools from copying 1000M of "source" in freeze test (#101837) Prevent test_tools from copying 1000M of "source" It doesn't need a git repo, just the checkout. We skip .git metadata, Doc/build, Doc/venv, and `__pycache__` subdirs, that developers often have in their clients to reduce the size of the source tree copy ten-fold. This should significantly reduce IO and presumably time on buildbots during this long test. --- .../2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst | 3 +++ Tools/freeze/test/freeze.py | 14 +++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst diff --git a/Misc/NEWS.d/next/Tests/2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst b/Misc/NEWS.d/next/Tests/2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst new file mode 100644 index 00000000000000..a3a3070d7f3790 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst @@ -0,0 +1,3 @@ +``test_tools`` now copies up to 10x less source data to a temporary +directory during the ``freeze`` test by ignoring git metadata and other +artifacts. diff --git a/Tools/freeze/test/freeze.py b/Tools/freeze/test/freeze.py index ddbfd7fc9c2f41..0ae983b15c98f0 100644 --- a/Tools/freeze/test/freeze.py +++ b/Tools/freeze/test/freeze.py @@ -80,7 +80,19 @@ def copy_source_tree(newroot, oldroot): if newroot == SRCDIR: raise Exception('this probably isn\'t what you wanted') shutil.rmtree(newroot) - shutil.copytree(oldroot, newroot) + + def ignore_non_src(src, names): + """Turns what could be a 1000M copy into a 100M copy.""" + # Don't copy the ~600M+ of needless git repo metadata. + # source only, ignore cached .pyc files. + subdirs_to_skip = {'.git', '__pycache__'} + if os.path.basename(src) == 'Doc': + # Another potential ~250M+ of non test related data. + subdirs_to_skip.add('build') + subdirs_to_skip.add('venv') + return subdirs_to_skip + + shutil.copytree(oldroot, newroot, ignore=ignore_non_src) if os.path.exists(os.path.join(newroot, 'Makefile')): _run_quiet([MAKE, 'clean'], newroot) From da2fb9264315dc30ac3012b4dbf5ba76d3f34433 Mon Sep 17 00:00:00 2001 From: Soumendra Ganguly <67527439+8vasu@users.noreply.github.com> Date: Sat, 11 Feb 2023 23:24:43 -0600 Subject: [PATCH 158/225] gh-85984: Utilize new "winsize" functions from termios in pty tests. (#101831) Utilize new functions termios.tcgetwinsize() and termios.tcsetwinsize in test_pty.py. Signed-off-by: Soumendra Ganguly Co-authored-by: Gregory P. Smith --- Lib/test/test_pty.py | 96 ++++++------------- ...3-02-11-22-36-10.gh-issue-85984.EVXjT9.rst | 1 + 2 files changed, 28 insertions(+), 69 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2023-02-11-22-36-10.gh-issue-85984.EVXjT9.rst diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index fa0dbcc16f3ce8..c723bb362c5d87 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -3,6 +3,8 @@ # Skip these tests if termios or fcntl are not available import_module('termios') +# fcntl is a proxy for not being one of the wasm32 platforms even though we +# don't use this module... a proper check for what crashes those is needed. import_module("fcntl") import errno @@ -15,20 +17,12 @@ import socket import io # readline import unittest - -import struct -import fcntl import warnings TEST_STRING_1 = b"I wish to buy a fish license.\n" TEST_STRING_2 = b"For my pet fish, Eric.\n" -try: - _TIOCGWINSZ = tty.TIOCGWINSZ - _TIOCSWINSZ = tty.TIOCSWINSZ - _HAVE_WINSZ = True -except AttributeError: - _HAVE_WINSZ = False +_HAVE_WINSZ = hasattr(tty, "TIOCGWINSZ") and hasattr(tty, "TIOCSWINSZ") if verbose: def debug(msg): @@ -82,14 +76,6 @@ def expectedFailureIfStdinIsTTY(fun): pass return fun -def _get_term_winsz(fd): - s = struct.pack("HHHH", 0, 0, 0, 0) - return fcntl.ioctl(fd, _TIOCGWINSZ, s) - -def _set_term_winsz(fd, winsz): - fcntl.ioctl(fd, _TIOCSWINSZ, winsz) - - # Marginal testing of pty suite. Cannot do extensive 'do or fail' testing # because pty code is not too portable. class PtyTest(unittest.TestCase): @@ -105,18 +91,14 @@ def setUp(self): self.addCleanup(signal.alarm, 0) signal.alarm(10) - # Save original stdin window size - self.stdin_rows = None - self.stdin_cols = None + # Save original stdin window size. + self.stdin_dim = None if _HAVE_WINSZ: try: - stdin_dim = os.get_terminal_size(pty.STDIN_FILENO) - self.stdin_rows = stdin_dim.lines - self.stdin_cols = stdin_dim.columns - old_stdin_winsz = struct.pack("HHHH", self.stdin_rows, - self.stdin_cols, 0, 0) - self.addCleanup(_set_term_winsz, pty.STDIN_FILENO, old_stdin_winsz) - except OSError: + self.stdin_dim = tty.tcgetwinsize(pty.STDIN_FILENO) + self.addCleanup(tty.tcsetwinsize, pty.STDIN_FILENO, + self.stdin_dim) + except tty.error: pass def handle_sig(self, sig, frame): @@ -131,41 +113,40 @@ def test_openpty(self): try: mode = tty.tcgetattr(pty.STDIN_FILENO) except tty.error: - # not a tty or bad/closed fd + # Not a tty or bad/closed fd. debug("tty.tcgetattr(pty.STDIN_FILENO) failed") mode = None - new_stdin_winsz = None - if self.stdin_rows is not None and self.stdin_cols is not None: + new_dim = None + if self.stdin_dim: try: # Modify pty.STDIN_FILENO window size; we need to # check if pty.openpty() is able to set pty slave # window size accordingly. - debug("Setting pty.STDIN_FILENO window size") - debug(f"original size: (rows={self.stdin_rows}, cols={self.stdin_cols})") - target_stdin_rows = self.stdin_rows + 1 - target_stdin_cols = self.stdin_cols + 1 - debug(f"target size: (rows={target_stdin_rows}, cols={target_stdin_cols})") - target_stdin_winsz = struct.pack("HHHH", target_stdin_rows, - target_stdin_cols, 0, 0) - _set_term_winsz(pty.STDIN_FILENO, target_stdin_winsz) + debug("Setting pty.STDIN_FILENO window size.") + debug(f"original size: (row, col) = {self.stdin_dim}") + target_dim = (self.stdin_dim[0] + 1, self.stdin_dim[1] + 1) + debug(f"target size: (row, col) = {target_dim}") + tty.tcsetwinsize(pty.STDIN_FILENO, target_dim) # Were we able to set the window size # of pty.STDIN_FILENO successfully? - new_stdin_winsz = _get_term_winsz(pty.STDIN_FILENO) - self.assertEqual(new_stdin_winsz, target_stdin_winsz, + new_dim = tty.tcgetwinsize(pty.STDIN_FILENO) + self.assertEqual(new_dim, target_dim, "pty.STDIN_FILENO window size unchanged") except OSError: - warnings.warn("Failed to set pty.STDIN_FILENO window size") + warnings.warn("Failed to set pty.STDIN_FILENO window size.") pass try: debug("Calling pty.openpty()") try: - master_fd, slave_fd = pty.openpty(mode, new_stdin_winsz) + master_fd, slave_fd, slave_name = pty.openpty(mode, new_dim, + True) except TypeError: master_fd, slave_fd = pty.openpty() - debug(f"Got master_fd '{master_fd}', slave_fd '{slave_fd}'") + slave_name = None + debug(f"Got {master_fd=}, {slave_fd=}, {slave_name=}") except OSError: # " An optional feature could not be imported " ... ? raise unittest.SkipTest("Pseudo-terminals (seemingly) not functional.") @@ -181,8 +162,8 @@ def test_openpty(self): if mode: self.assertEqual(tty.tcgetattr(slave_fd), mode, "openpty() failed to set slave termios") - if new_stdin_winsz: - self.assertEqual(_get_term_winsz(slave_fd), new_stdin_winsz, + if new_dim: + self.assertEqual(tty.tcgetwinsize(slave_fd), new_dim, "openpty() failed to set slave window size") # Ensure the fd is non-blocking in case there's nothing to read. @@ -367,9 +348,8 @@ def _socketpair(self): self.files.extend(socketpair) return socketpair - def _mock_select(self, rfds, wfds, xfds, timeout=0): + def _mock_select(self, rfds, wfds, xfds): # This will raise IndexError when no more expected calls exist. - # This ignores the timeout self.assertEqual(self.select_rfds_lengths.pop(0), len(rfds)) return self.select_rfds_results.pop(0), [], [] @@ -409,28 +389,6 @@ def test__copy_to_each(self): self.assertEqual(os.read(read_from_stdout_fd, 20), b'from master') self.assertEqual(os.read(masters[1], 20), b'from stdin') - def test__copy_eof_on_all(self): - """Test the empty read EOF case on both master_fd and stdin.""" - read_from_stdout_fd, mock_stdout_fd = self._pipe() - pty.STDOUT_FILENO = mock_stdout_fd - mock_stdin_fd, write_to_stdin_fd = self._pipe() - pty.STDIN_FILENO = mock_stdin_fd - socketpair = self._socketpair() - masters = [s.fileno() for s in socketpair] - - socketpair[1].close() - os.close(write_to_stdin_fd) - - pty.select = self._mock_select - self.select_rfds_lengths.append(2) - self.select_rfds_results.append([mock_stdin_fd, masters[0]]) - # We expect that both fds were removed from the fds list as they - # both encountered an EOF before the second select call. - self.select_rfds_lengths.append(0) - - # We expect the function to return without error. - self.assertEqual(pty._copy(masters[0]), None) - def test__restore_tty_mode_normal_return(self): """Test that spawn resets the tty mode no when _copy returns normally.""" diff --git a/Misc/NEWS.d/next/Tests/2023-02-11-22-36-10.gh-issue-85984.EVXjT9.rst b/Misc/NEWS.d/next/Tests/2023-02-11-22-36-10.gh-issue-85984.EVXjT9.rst new file mode 100644 index 00000000000000..402f99ea6c6ebf --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-02-11-22-36-10.gh-issue-85984.EVXjT9.rst @@ -0,0 +1 @@ +Utilize new "winsize" functions from termios in pty tests. From dfc2e065a2e71011017077e549cd2f9bf4944c54 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 11 Feb 2023 22:07:52 -0800 Subject: [PATCH 159/225] gh-89792: Limit test_tools freeze test build parallelism based on the number of cores (#101841) unhardcode freeze test build parallelism. base it on the number of cpus, don't use more than max(2, os.cpu_count()/3). --- .../2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst | 7 ++++--- Tools/freeze/test/freeze.py | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Misc/NEWS.d/next/Tests/2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst b/Misc/NEWS.d/next/Tests/2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst index a3a3070d7f3790..9de278919ef2f8 100644 --- a/Misc/NEWS.d/next/Tests/2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst +++ b/Misc/NEWS.d/next/Tests/2023-02-11-20-28-08.gh-issue-89792.S-Y5BZ.rst @@ -1,3 +1,4 @@ -``test_tools`` now copies up to 10x less source data to a temporary -directory during the ``freeze`` test by ignoring git metadata and other -artifacts. +``test_tools`` now copies up to 10x less source data to a temporary directory +during the ``freeze`` test by ignoring git metadata and other artifacts. It +also limits its python build parallelism based on os.cpu_count instead of hard +coding it as 8 cores. diff --git a/Tools/freeze/test/freeze.py b/Tools/freeze/test/freeze.py index 0ae983b15c98f0..b4c76ff36a873b 100644 --- a/Tools/freeze/test/freeze.py +++ b/Tools/freeze/test/freeze.py @@ -163,16 +163,25 @@ def prepare(script=None, outdir=None): if not MAKE: raise UnsupportedError('make') + cores = os.cpu_count() + if cores and cores >= 3: + # this test is most often run as part of the whole suite with a lot + # of other tests running in parallel, from 1-2 vCPU systems up to + # people's NNN core beasts. Don't attempt to use it all. + parallel = f'-j{cores*2//3}' + else: + parallel = '-j2' + # Build python. - print(f'building python in {builddir}...') + print(f'building python {parallel=} in {builddir}...') if os.path.exists(os.path.join(srcdir, 'Makefile')): # Out-of-tree builds require a clean srcdir. _run_quiet([MAKE, '-C', srcdir, 'clean']) - _run_quiet([MAKE, '-C', builddir, '-j8']) + _run_quiet([MAKE, '-C', builddir, parallel]) # Install the build. print(f'installing python into {prefix}...') - _run_quiet([MAKE, '-C', builddir, '-j8', 'install']) + _run_quiet([MAKE, '-C', builddir, 'install']) python = os.path.join(prefix, 'bin', 'python3') return outdir, scriptfile, python From 6ef6915d3530e844243893f91bf4bd702dfef570 Mon Sep 17 00:00:00 2001 From: Jean Abou-Samra Date: Sun, 12 Feb 2023 15:20:11 +0100 Subject: [PATCH 160/225] gh-101845: pyspecific: Fix i18n for availability directive (GH-101846) pyspecific: Fix i18n for availability directive If the directive has content, the previous code would nest paragraph nodes from that content inside a general paragraph node, which confuses Sphinx and leads it to drop the content when translating. Instead, use a container node for the body. Also use set_source_info so that any warnings have location info. --- Doc/tools/extensions/pyspecific.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py index db7bb3b44219d2..d659a4a54b9d11 100644 --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py @@ -28,6 +28,7 @@ from sphinx.environment import NoUri from sphinx.locale import _ as sphinx_gettext from sphinx.util import status_iterator, logging +from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import split_explicit_title from sphinx.writers.text import TextWriter, TextTranslator @@ -119,7 +120,7 @@ def run(self): # Support for documenting platform availability -class Availability(Directive): +class Availability(SphinxDirective): has_content = True required_arguments = 1 @@ -139,18 +140,19 @@ class Availability(Directive): def run(self): availability_ref = ':ref:`Availability `: ' + avail_nodes, avail_msgs = self.state.inline_text( + availability_ref + self.arguments[0], + self.lineno) pnode = nodes.paragraph(availability_ref + self.arguments[0], - classes=["availability"],) - n, m = self.state.inline_text(availability_ref, self.lineno) - pnode.extend(n + m) - n, m = self.state.inline_text(self.arguments[0], self.lineno) - pnode.extend(n + m) + '', *avail_nodes, *avail_msgs) + self.set_source_info(pnode) + cnode = nodes.container("", pnode, classes=["availability"]) + self.set_source_info(cnode) if self.content: - self.state.nested_parse(self.content, self.content_offset, pnode) - + self.state.nested_parse(self.content, self.content_offset, cnode) self.parse_platforms() - return [pnode] + return [cnode] def parse_platforms(self): """Parse platform information from arguments From a1f08f5f19753c7c9295f51b5ae1262c7a1c838f Mon Sep 17 00:00:00 2001 From: Steve Kowalik Date: Mon, 13 Feb 2023 20:11:43 +1100 Subject: [PATCH 161/225] Correct trivial grammar in reset_mock docs (#101861) --- Doc/library/unittest.mock.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst index e009f303fef317..d6d8e5e9557d5c 100644 --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -406,7 +406,7 @@ the *new_callable* argument to :func:`patch`. False .. versionchanged:: 3.6 - Added two keyword only argument to the reset_mock function. + Added two keyword-only arguments to the reset_mock function. This can be useful where you want to make a series of assertions that reuse the same object. Note that :meth:`reset_mock` *doesn't* clear the @@ -416,8 +416,8 @@ the *new_callable* argument to :func:`patch`. parameter as ``True``. Child mocks and the return value mock (if any) are reset as well. - .. note:: *return_value*, and :attr:`side_effect` are keyword only - argument. + .. note:: *return_value*, and :attr:`side_effect` are keyword-only + arguments. .. method:: mock_add_spec(spec, spec_set=False) From 160f2fe2b90ed5ec7838cb4141dd35768422891f Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 13 Feb 2023 11:24:55 +0000 Subject: [PATCH 162/225] GH-87849: Simplify stack effect of SEND and specialize it for generators and coroutines. (GH-101788) --- Include/internal/pycore_code.h | 7 ++ Include/internal/pycore_opcode.h | 5 +- Include/opcode.h | 1 + Lib/dis.py | 9 +- Lib/importlib/_bootstrap_external.py | 3 +- Lib/opcode.py | 8 +- Lib/test/test_dis.py | 25 +++--- ...3-02-10-15-54-57.gh-issue-87849.IUVvPz.rst | 3 + Objects/frameobject.c | 6 +- Python/bytecodes.c | 88 ++++++++++-------- Python/compile.c | 2 + Python/generated_cases.c.h | 89 +++++++++++-------- Python/opcode_metadata.h | 11 ++- Python/opcode_targets.h | 2 +- Python/specialize.c | 25 +++++- 15 files changed, 185 insertions(+), 99 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-10-15-54-57.gh-issue-87849.IUVvPz.rst diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index a287250acc1912..10f1e320a12ff4 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -92,6 +92,12 @@ typedef struct { #define INLINE_CACHE_ENTRIES_FOR_ITER CACHE_ENTRIES(_PyForIterCache) +typedef struct { + uint16_t counter; +} _PySendCache; + +#define INLINE_CACHE_ENTRIES_SEND CACHE_ENTRIES(_PySendCache) + // Borrowed references to common callables: struct callable_cache { PyObject *isinstance; @@ -233,6 +239,7 @@ extern void _Py_Specialize_CompareAndBranch(PyObject *lhs, PyObject *rhs, extern void _Py_Specialize_UnpackSequence(PyObject *seq, _Py_CODEUNIT *instr, int oparg); extern void _Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg); +extern void _Py_Specialize_Send(PyObject *receiver, _Py_CODEUNIT *instr); /* Finalizer function for static codeobjects used in deepfreeze.py */ extern void _PyStaticCode_Fini(PyCodeObject *co); diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index 47c84721335196..5e65adee9e00a5 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -50,6 +50,7 @@ const uint8_t _PyOpcode_Caches[256] = { [COMPARE_OP] = 1, [LOAD_GLOBAL] = 5, [BINARY_OP] = 1, + [SEND] = 1, [COMPARE_AND_BRANCH] = 1, [CALL] = 4, }; @@ -196,6 +197,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [RETURN_GENERATOR] = RETURN_GENERATOR, [RETURN_VALUE] = RETURN_VALUE, [SEND] = SEND, + [SEND_GEN] = SEND, [SETUP_ANNOTATIONS] = SETUP_ANNOTATIONS, [SET_ADD] = SET_ADD, [SET_UPDATE] = SET_UPDATE, @@ -395,7 +397,7 @@ static const char *const _PyOpcode_OpName[263] = { [SET_UPDATE] = "SET_UPDATE", [DICT_MERGE] = "DICT_MERGE", [DICT_UPDATE] = "DICT_UPDATE", - [166] = "<166>", + [SEND_GEN] = "SEND_GEN", [167] = "<167>", [168] = "<168>", [169] = "<169>", @@ -496,7 +498,6 @@ static const char *const _PyOpcode_OpName[263] = { #endif #define EXTRA_CASES \ - case 166: \ case 167: \ case 168: \ case 169: \ diff --git a/Include/opcode.h b/Include/opcode.h index 77ad7c22440d72..d643741c3c3aa0 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -187,6 +187,7 @@ extern "C" { #define UNPACK_SEQUENCE_LIST 159 #define UNPACK_SEQUENCE_TUPLE 160 #define UNPACK_SEQUENCE_TWO_TUPLE 161 +#define SEND_GEN 166 #define DO_TRACING 255 #define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\ diff --git a/Lib/dis.py b/Lib/dis.py index a6921008d9d0e5..9edde6ae8258da 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -39,6 +39,7 @@ BINARY_OP = opmap['BINARY_OP'] JUMP_BACKWARD = opmap['JUMP_BACKWARD'] FOR_ITER = opmap['FOR_ITER'] +SEND = opmap['SEND'] LOAD_ATTR = opmap['LOAD_ATTR'] CACHE = opmap["CACHE"] @@ -453,6 +454,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None, argrepr = '' positions = Positions(*next(co_positions, ())) deop = _deoptop(op) + caches = _inline_cache_entries[deop] if arg is not None: # Set argval to the dereferenced value of the argument when # available, and argrepr to the string representation of argval. @@ -478,8 +480,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None, elif deop in hasjrel: signed_arg = -arg if _is_backward_jump(deop) else arg argval = offset + 2 + signed_arg*2 - if deop == FOR_ITER: - argval += 2 + argval += 2 * caches argrepr = "to " + repr(argval) elif deop in haslocal or deop in hasfree: argval, argrepr = _get_name_info(arg, varname_from_oparg) @@ -633,12 +634,12 @@ def findlabels(code): for offset, op, arg in _unpack_opargs(code): if arg is not None: deop = _deoptop(op) + caches = _inline_cache_entries[deop] if deop in hasjrel: if _is_backward_jump(deop): arg = -arg label = offset + 2 + arg*2 - if deop == FOR_ITER: - label += 2 + label += 2 * caches elif deop in hasjabs: label = arg*2 else: diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 933c8c7d7e0590..38d4a384c2cc95 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -432,6 +432,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.12a5 3516 (Add COMPARE_AND_BRANCH instruction) # Python 3.12a5 3517 (Change YIELD_VALUE oparg to exception block depth) # Python 3.12a5 3518 (Add RETURN_CONST instruction) +# Python 3.12a5 3519 (Modify SEND instruction) # Python 3.13 will start with 3550 @@ -444,7 +445,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3518).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3519).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c diff --git a/Lib/opcode.py b/Lib/opcode.py index 5f163d2ccb80df..b69cd1bbdd61ca 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -167,7 +167,7 @@ def pseudo_op(name, op, real_ops): def_op('RETURN_CONST', 121) hasconst.append(121) def_op('BINARY_OP', 122) -jrel_op('SEND', 123) # Number of bytes to skip +jrel_op('SEND', 123) # Number of words to skip def_op('LOAD_FAST', 124) # Local variable number, no null check haslocal.append(124) def_op('STORE_FAST', 125) # Local variable number @@ -370,6 +370,9 @@ def pseudo_op(name, op, real_ops): "UNPACK_SEQUENCE_TUPLE", "UNPACK_SEQUENCE_TWO_TUPLE", ], + "SEND": [ + "SEND_GEN", + ], } _specialized_instructions = [ opcode for family in _specializations.values() for opcode in family @@ -429,6 +432,9 @@ def pseudo_op(name, op, real_ops): "STORE_SUBSCR": { "counter": 1, }, + "SEND": { + "counter": 1, + }, } _inline_cache_entries = [ diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 1050b15e16eaaa..9086824dd6f40c 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -475,11 +475,13 @@ async def _asyncwith(c): BEFORE_ASYNC_WITH GET_AWAITABLE 1 LOAD_CONST 0 (None) - >> SEND 3 (to 22) + >> SEND 3 (to 24) YIELD_VALUE 2 RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 4 (to 14) - >> POP_TOP + JUMP_BACKWARD_NO_INTERRUPT 5 (to 14) + >> SWAP 2 + POP_TOP + POP_TOP %3d LOAD_CONST 1 (1) STORE_FAST 1 (x) @@ -490,30 +492,33 @@ async def _asyncwith(c): CALL 2 GET_AWAITABLE 2 LOAD_CONST 0 (None) - >> SEND 3 (to 56) + >> SEND 3 (to 64) YIELD_VALUE 2 RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 4 (to 48) + JUMP_BACKWARD_NO_INTERRUPT 5 (to 54) >> POP_TOP + POP_TOP %3d LOAD_CONST 2 (2) STORE_FAST 2 (y) RETURN_CONST 0 (None) %3d >> CLEANUP_THROW - JUMP_BACKWARD 23 (to 22) + JUMP_BACKWARD 27 (to 24) >> CLEANUP_THROW - JUMP_BACKWARD 8 (to 56) + JUMP_BACKWARD 9 (to 64) >> PUSH_EXC_INFO WITH_EXCEPT_START GET_AWAITABLE 2 LOAD_CONST 0 (None) - >> SEND 4 (to 90) + >> SEND 4 (to 102) YIELD_VALUE 3 RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 4 (to 80) + JUMP_BACKWARD_NO_INTERRUPT 5 (to 90) >> CLEANUP_THROW - >> POP_JUMP_IF_TRUE 1 (to 94) + >> SWAP 2 + POP_TOP + POP_JUMP_IF_TRUE 1 (to 110) RERAISE 2 >> POP_TOP POP_EXCEPT diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-10-15-54-57.gh-issue-87849.IUVvPz.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-10-15-54-57.gh-issue-87849.IUVvPz.rst new file mode 100644 index 00000000000000..da5f3ff79fd575 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-10-15-54-57.gh-issue-87849.IUVvPz.rst @@ -0,0 +1,3 @@ +Change the ``SEND`` instruction to leave the receiver on the stack. This +allows the specialized form of ``SEND`` to skip the chain of C calls and jump +directly to the ``RESUME`` in the generator or coroutine. diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 0e52a3e2399c06..581ed2d214c4d9 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -334,10 +334,10 @@ mark_stacks(PyCodeObject *code_obj, int len) break; } case SEND: - j = get_arg(code, i) + i + 1; + j = get_arg(code, i) + i + INLINE_CACHE_ENTRIES_SEND + 1; assert(j < len); - assert(stacks[j] == UNINITIALIZED || stacks[j] == pop_value(next_stack)); - stacks[j] = pop_value(next_stack); + assert(stacks[j] == UNINITIALIZED || stacks[j] == next_stack); + stacks[j] = next_stack; stacks[i+1] = next_stack; break; case JUMP_FORWARD: diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 2b9f12fefa14e9..429cd7fdafa168 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -680,51 +680,66 @@ dummy_func( PREDICT(LOAD_CONST); } - inst(SEND, (receiver, v -- receiver if (!jump), retval)) { + family(for_iter, INLINE_CACHE_ENTRIES_FOR_ITER) = { + SEND, + SEND_GEN, + }; + + inst(SEND, (unused/1, receiver, v -- receiver, retval)) { + #if ENABLE_SPECIALIZATION + _PySendCache *cache = (_PySendCache *)next_instr; + if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { + assert(cframe.use_tracing == 0); + next_instr--; + _Py_Specialize_Send(receiver, next_instr); + DISPATCH_SAME_OPARG(); + } + STAT_INC(SEND, deferred); + DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ assert(frame != &entry_frame); - bool jump = false; - PySendResult gen_status; - if (tstate->c_tracefunc == NULL) { - gen_status = PyIter_Send(receiver, v, &retval); - } else { - if (Py_IsNone(v) && PyIter_Check(receiver)) { - retval = Py_TYPE(receiver)->tp_iternext(receiver); - } - else { - retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v); - } - if (retval == NULL) { - if (tstate->c_tracefunc != NULL - && _PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) - call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame); - if (_PyGen_FetchStopIterationValue(&retval) == 0) { - gen_status = PYGEN_RETURN; - } - else { - gen_status = PYGEN_ERROR; - } + if (Py_IsNone(v) && PyIter_Check(receiver)) { + retval = Py_TYPE(receiver)->tp_iternext(receiver); + } + else { + retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v); + } + if (retval == NULL) { + if (tstate->c_tracefunc != NULL + && _PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) + call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame); + if (_PyGen_FetchStopIterationValue(&retval) == 0) { + assert(retval != NULL); + JUMPBY(oparg); } else { - gen_status = PYGEN_NEXT; + assert(retval == NULL); + goto error; } } - if (gen_status == PYGEN_ERROR) { - assert(retval == NULL); - goto error; - } - Py_DECREF(v); - if (gen_status == PYGEN_RETURN) { - assert(retval != NULL); - Py_DECREF(receiver); - JUMPBY(oparg); - jump = true; - } else { - assert(gen_status == PYGEN_NEXT); assert(retval != NULL); } } + inst(SEND_GEN, (unused/1, receiver, v -- receiver)) { + assert(cframe.use_tracing == 0); + PyGenObject *gen = (PyGenObject *)receiver; + DEOPT_IF(Py_TYPE(gen) != &PyGen_Type && + Py_TYPE(gen) != &PyCoro_Type, SEND); + DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, SEND); + STAT_INC(SEND, hit); + _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; + frame->yield_offset = oparg; + STACK_SHRINK(1); + _PyFrame_StackPush(gen_frame, v); + gen->gi_frame_state = FRAME_EXECUTING; + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + JUMPBY(INLINE_CACHE_ENTRIES_SEND + oparg); + DISPATCH_INLINED(gen_frame); + } + inst(YIELD_VALUE, (retval -- unused)) { // NOTE: It's important that YIELD_VALUE never raises an exception! // The compiler treats any exception raised here as a failed close() @@ -796,12 +811,13 @@ dummy_func( } } - inst(CLEANUP_THROW, (sub_iter, last_sent_val, exc_value -- value)) { + inst(CLEANUP_THROW, (sub_iter, last_sent_val, exc_value -- none, value)) { assert(throwflag); assert(exc_value && PyExceptionInstance_Check(exc_value)); if (PyErr_GivenExceptionMatches(exc_value, PyExc_StopIteration)) { value = Py_NewRef(((PyStopIterationObject *)exc_value)->value); DECREF_INPUTS(); + none = Py_NewRef(Py_None); } else { _PyErr_SetRaisedException(tstate, Py_NewRef(exc_value)); diff --git a/Python/compile.c b/Python/compile.c index a3c915c3c14a96..b49eda314eeef1 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1789,6 +1789,8 @@ compiler_add_yield_from(struct compiler *c, location loc, int await) ADDOP(c, loc, CLEANUP_THROW); USE_LABEL(c, exit); + ADDOP_I(c, loc, SWAP, 2); + ADDOP(c, loc, POP_TOP); return SUCCESS; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index a224d4eb892785..093ebff026b509 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -882,57 +882,69 @@ } TARGET(SEND) { + PREDICTED(SEND); PyObject *v = PEEK(1); PyObject *receiver = PEEK(2); PyObject *retval; + #if ENABLE_SPECIALIZATION + _PySendCache *cache = (_PySendCache *)next_instr; + if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { + assert(cframe.use_tracing == 0); + next_instr--; + _Py_Specialize_Send(receiver, next_instr); + DISPATCH_SAME_OPARG(); + } + STAT_INC(SEND, deferred); + DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ assert(frame != &entry_frame); - bool jump = false; - PySendResult gen_status; - if (tstate->c_tracefunc == NULL) { - gen_status = PyIter_Send(receiver, v, &retval); - } else { - if (Py_IsNone(v) && PyIter_Check(receiver)) { - retval = Py_TYPE(receiver)->tp_iternext(receiver); - } - else { - retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v); - } - if (retval == NULL) { - if (tstate->c_tracefunc != NULL - && _PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) - call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame); - if (_PyGen_FetchStopIterationValue(&retval) == 0) { - gen_status = PYGEN_RETURN; - } - else { - gen_status = PYGEN_ERROR; - } + if (Py_IsNone(v) && PyIter_Check(receiver)) { + retval = Py_TYPE(receiver)->tp_iternext(receiver); + } + else { + retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v); + } + if (retval == NULL) { + if (tstate->c_tracefunc != NULL + && _PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) + call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame); + if (_PyGen_FetchStopIterationValue(&retval) == 0) { + assert(retval != NULL); + JUMPBY(oparg); } else { - gen_status = PYGEN_NEXT; + assert(retval == NULL); + goto error; } } - if (gen_status == PYGEN_ERROR) { - assert(retval == NULL); - goto error; - } - Py_DECREF(v); - if (gen_status == PYGEN_RETURN) { - assert(retval != NULL); - Py_DECREF(receiver); - JUMPBY(oparg); - jump = true; - } else { - assert(gen_status == PYGEN_NEXT); assert(retval != NULL); } - STACK_SHRINK(1); - STACK_GROW(((!jump) ? 1 : 0)); POKE(1, retval); + JUMPBY(1); DISPATCH(); } + TARGET(SEND_GEN) { + PyObject *v = PEEK(1); + PyObject *receiver = PEEK(2); + assert(cframe.use_tracing == 0); + PyGenObject *gen = (PyGenObject *)receiver; + DEOPT_IF(Py_TYPE(gen) != &PyGen_Type && + Py_TYPE(gen) != &PyCoro_Type, SEND); + DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, SEND); + STAT_INC(SEND, hit); + _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; + frame->yield_offset = oparg; + STACK_SHRINK(1); + _PyFrame_StackPush(gen_frame, v); + gen->gi_frame_state = FRAME_EXECUTING; + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + JUMPBY(INLINE_CACHE_ENTRIES_SEND + oparg); + DISPATCH_INLINED(gen_frame); + } + TARGET(YIELD_VALUE) { PyObject *retval = PEEK(1); // NOTE: It's important that YIELD_VALUE never raises an exception! @@ -1026,6 +1038,7 @@ PyObject *exc_value = PEEK(1); PyObject *last_sent_val = PEEK(2); PyObject *sub_iter = PEEK(3); + PyObject *none; PyObject *value; assert(throwflag); assert(exc_value && PyExceptionInstance_Check(exc_value)); @@ -1034,13 +1047,15 @@ Py_DECREF(sub_iter); Py_DECREF(last_sent_val); Py_DECREF(exc_value); + none = Py_NewRef(Py_None); } else { _PyErr_SetRaisedException(tstate, Py_NewRef(exc_value)); goto exception_unwind; } - STACK_SHRINK(2); + STACK_SHRINK(1); POKE(1, value); + POKE(2, none); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index db1dfd37a90132..d622eb12c8cb2d 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -104,6 +104,8 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 1; case SEND: return 2; + case SEND_GEN: + return 2; case YIELD_VALUE: return 1; case POP_EXCEPT: @@ -453,7 +455,9 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case GET_AWAITABLE: return 1; case SEND: - return ((!jump) ? 1 : 0) + 1; + return 2; + case SEND_GEN: + return 1; case YIELD_VALUE: return 1; case POP_EXCEPT: @@ -465,7 +469,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case END_ASYNC_FOR: return 0; case CLEANUP_THROW: - return 1; + return 2; case LOAD_ASSERTION_ERROR: return 1; case LOAD_BUILD_CLASS: @@ -763,7 +767,8 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = { [GET_AITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_ANEXT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_AWAITABLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [SEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [SEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [SEND_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [YIELD_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [POP_EXCEPT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [RERAISE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index eceb246fac4909..301ec6e005dad6 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -165,7 +165,7 @@ static void *opcode_targets[256] = { &&TARGET_SET_UPDATE, &&TARGET_DICT_MERGE, &&TARGET_DICT_UPDATE, - &&_unknown_opcode, + &&TARGET_SEND_GEN, &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, diff --git a/Python/specialize.c b/Python/specialize.c index 908ad6dceb57f3..4ede3122d38046 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -128,6 +128,7 @@ print_spec_stats(FILE *out, OpcodeStats *stats) fprintf(out, "opcode[%d].specializable : 1\n", BINARY_SLICE); fprintf(out, "opcode[%d].specializable : 1\n", COMPARE_OP); fprintf(out, "opcode[%d].specializable : 1\n", STORE_SLICE); + fprintf(out, "opcode[%d].specializable : 1\n", SEND); for (int i = 0; i < 256; i++) { if (_PyOpcode_Caches[i]) { fprintf(out, "opcode[%d].specializable : 1\n", i); @@ -1084,7 +1085,7 @@ PyObject *descr, DescriptorClassification kind) if (dict) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_NOT_MANAGED_DICT); return 0; - } + } assert(owner_cls->tp_dictoffset > 0); assert(owner_cls->tp_dictoffset <= INT16_MAX); _py_set_opcode(instr, LOAD_ATTR_METHOD_LAZY_DICT); @@ -2183,3 +2184,25 @@ _Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg) STAT_INC(FOR_ITER, success); cache->counter = adaptive_counter_cooldown(); } + +void +_Py_Specialize_Send(PyObject *receiver, _Py_CODEUNIT *instr) +{ + assert(ENABLE_SPECIALIZATION); + assert(_PyOpcode_Caches[SEND] == INLINE_CACHE_ENTRIES_SEND); + _PySendCache *cache = (_PySendCache *)(instr + 1); + PyTypeObject *tp = Py_TYPE(receiver); + if (tp == &PyGen_Type || tp == &PyCoro_Type) { + _py_set_opcode(instr, SEND_GEN); + goto success; + } + SPECIALIZATION_FAIL(SEND, + _PySpecialization_ClassifyIterator(receiver)); + STAT_INC(SEND, failure); + _py_set_opcode(instr, SEND); + cache->counter = adaptive_counter_backoff(cache->counter); + return; +success: + STAT_INC(SEND, success); + cache->counter = adaptive_counter_cooldown(); +} From d9199175c7386a95aaac91822a2197b9365eb0e8 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 13 Feb 2023 11:31:15 +0000 Subject: [PATCH 163/225] GH-100987: Refactor `_PyInterpreterFrame` a bit, to assist generator improvement. (GH-100988) Refactor _PyInterpreterFrame a bit, to assist generator improvement. --- Include/internal/pycore_frame.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index f12b225ebfccf2..81d16b219c305b 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -47,15 +47,13 @@ enum _frameowner { }; typedef struct _PyInterpreterFrame { - /* "Specials" section */ + PyCodeObject *f_code; /* Strong reference */ + struct _PyInterpreterFrame *previous; PyObject *f_funcobj; /* Strong reference. Only valid if not on C stack */ PyObject *f_globals; /* Borrowed reference. Only valid if not on C stack */ PyObject *f_builtins; /* Borrowed reference. Only valid if not on C stack */ PyObject *f_locals; /* Strong reference, may be NULL. Only valid if not on C stack */ - PyCodeObject *f_code; /* Strong reference */ PyFrameObject *frame_obj; /* Strong reference, may be NULL. Only valid if not on C stack */ - /* Linkage section */ - struct _PyInterpreterFrame *previous; // NOTE: This is not necessarily the last instruction started in the given // frame. Rather, it is the code unit *prior to* the *next* instruction. For // example, it may be an inline CACHE entry, an instruction we just jumped From 2db2c4b45501eebef5b3ff89118554bd5eb92ed4 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 13 Feb 2023 13:36:42 +0100 Subject: [PATCH 164/225] gh-92547: Purge sqlite3_enable_shared_cache() detection from configure (#101873) --- configure | 51 --------------------------------------------------- configure.ac | 1 - 2 files changed, 52 deletions(-) diff --git a/configure b/configure index 97694c602d1cc8..35088f9e5cafd6 100755 --- a/configure +++ b/configure @@ -13689,57 +13689,6 @@ fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqlite3_enable_shared_cache in -lsqlite3" >&5 -$as_echo_n "checking for sqlite3_enable_shared_cache in -lsqlite3... " >&6; } -if ${ac_cv_lib_sqlite3_sqlite3_enable_shared_cache+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lsqlite3 $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char sqlite3_enable_shared_cache (); -int -main () -{ -return sqlite3_enable_shared_cache (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_sqlite3_sqlite3_enable_shared_cache=yes -else - ac_cv_lib_sqlite3_sqlite3_enable_shared_cache=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_enable_shared_cache" >&5 -$as_echo "$ac_cv_lib_sqlite3_sqlite3_enable_shared_cache" >&6; } -if test "x$ac_cv_lib_sqlite3_sqlite3_enable_shared_cache" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBSQLITE3 1 -_ACEOF - - LIBS="-lsqlite3 $LIBS" - -else - - have_supported_sqlite3=no - -fi - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqlite3_progress_handler in -lsqlite3" >&5 $as_echo_n "checking for sqlite3_progress_handler in -lsqlite3... " >&6; } if ${ac_cv_lib_sqlite3_sqlite3_progress_handler+:} false; then : diff --git a/configure.ac b/configure.ac index 09369b985b33f6..1ab48e0d1c160a 100644 --- a/configure.ac +++ b/configure.ac @@ -3867,7 +3867,6 @@ dnl hence CPPFLAGS instead of CFLAGS. PY_CHECK_SQLITE_FUNC([sqlite3_column_decltype]) PY_CHECK_SQLITE_FUNC([sqlite3_column_double]) PY_CHECK_SQLITE_FUNC([sqlite3_complete]) - PY_CHECK_SQLITE_FUNC([sqlite3_enable_shared_cache]) PY_CHECK_SQLITE_FUNC([sqlite3_progress_handler]) PY_CHECK_SQLITE_FUNC([sqlite3_result_double]) PY_CHECK_SQLITE_FUNC([sqlite3_set_authorizer]) From 95cbb3d908175ccd855078b3fab7f99e7d0bca88 Mon Sep 17 00:00:00 2001 From: James Lee <49257044+juria90@users.noreply.github.com> Date: Mon, 13 Feb 2023 05:49:44 -0800 Subject: [PATCH 165/225] gh-101810: Remove duplicated st_ino calculation (GH-101811) --- Python/fileutils.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Python/fileutils.c b/Python/fileutils.c index 244bd899b3bd24..22b2257a56d0ec 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -1162,8 +1162,6 @@ _Py_fstat_noraise(int fd, struct _Py_stat_struct *status) } _Py_attribute_data_to_stat(&info, 0, status); - /* specific to fstat() */ - status->st_ino = (((uint64_t)info.nFileIndexHigh) << 32) + info.nFileIndexLow; return 0; #else return fstat(fd, status); From 0c6fe81dce9d6bb1dce5e4503f1b42bc5355ba24 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 13 Feb 2023 20:33:48 +0000 Subject: [PATCH 166/225] gh-101849: Add upgrade codes for old versions of launcher that ended up with later version numbers (GH-101877) --- ...-02-13-16-32-50.gh-issue-101849.7lm_53.rst | 1 + Tools/msi/common.wxs | 2 +- Tools/msi/launcher/launcher.wxs | 21 +++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-02-13-16-32-50.gh-issue-101849.7lm_53.rst diff --git a/Misc/NEWS.d/next/Windows/2023-02-13-16-32-50.gh-issue-101849.7lm_53.rst b/Misc/NEWS.d/next/Windows/2023-02-13-16-32-50.gh-issue-101849.7lm_53.rst new file mode 100644 index 00000000000000..861d4de9f9a650 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-02-13-16-32-50.gh-issue-101849.7lm_53.rst @@ -0,0 +1 @@ +Ensures installer will correctly upgrade existing ``py.exe`` launcher installs. diff --git a/Tools/msi/common.wxs b/Tools/msi/common.wxs index 55cb44860d02c0..54fa749ab17cdd 100644 --- a/Tools/msi/common.wxs +++ b/Tools/msi/common.wxs @@ -25,7 +25,6 @@ - @@ -42,6 +41,7 @@ UPGRADE + diff --git a/Tools/msi/launcher/launcher.wxs b/Tools/msi/launcher/launcher.wxs index b83058c63bf6d9..49f1f7b8c1762e 100644 --- a/Tools/msi/launcher/launcher.wxs +++ b/Tools/msi/launcher/launcher.wxs @@ -34,13 +34,34 @@ NOT Installed AND NOT ALLUSERS=1 NOT Installed AND ALLUSERS=1 + + UPGRADE or REMOVE_350_LAUNCHER or REMOVE_360A1_LAUNCHER or UPGRADE_3_11_0 or UPGRADE_3_11_1 + UPGRADE or REMOVE_350_LAUNCHER or REMOVE_360A1_LAUNCHER + + + Installed OR NOT DOWNGRADE OR UPGRADE_3_11_0 OR UPGRADE_3_11_1 + + Installed OR NOT DOWNGRADE + + + + + + + From 928752ce4c23f47d3175dd47ecacf08d86a99c9d Mon Sep 17 00:00:00 2001 From: Radek Smejkal Date: Tue, 14 Feb 2023 02:37:34 +0100 Subject: [PATCH 167/225] gh-74895: getaddrinfo no longer raises OverflowError (#2435) `socket.getaddrinfo()` no longer raises `OverflowError` based on the **port** argument. Error reporting (or not) for its value is left up to the underlying C library `getaddrinfo()` implementation. --- Lib/test/test_socket.py | 48 +++++++++++++++++++ Misc/ACKS | 1 + ...3-02-13-22-21-58.gh-issue-74895.esMNtq.rst | 5 ++ Modules/getaddrinfo.c | 6 ++- Modules/socketmodule.c | 14 ++++-- 5 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-13-22-21-58.gh-issue-74895.esMNtq.rst diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index f1b4018c265e18..a313da29b4a4fd 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1600,6 +1600,54 @@ def testGetaddrinfo(self): except socket.gaierror: pass + def test_getaddrinfo_int_port_overflow(self): + # gh-74895: Test that getaddrinfo does not raise OverflowError on port. + # + # POSIX getaddrinfo() never specify the valid range for "service" + # decimal port number values. For IPv4 and IPv6 they are technically + # unsigned 16-bit values, but the API is protocol agnostic. Which values + # trigger an error from the C library function varies by platform as + # they do not all perform validation. + + # The key here is that we don't want to produce OverflowError as Python + # prior to 3.12 did for ints outside of a [LONG_MIN, LONG_MAX] range. + # Leave the error up to the underlying string based platform C API. + + from _testcapi import ULONG_MAX, LONG_MAX, LONG_MIN + try: + socket.getaddrinfo(None, ULONG_MAX + 1) + except OverflowError: + # Platforms differ as to what values consitute a getaddrinfo() error + # return. Some fail for LONG_MAX+1, others ULONG_MAX+1, and Windows + # silently accepts such huge "port" aka "service" numeric values. + self.fail("Either no error or socket.gaierror expected.") + except socket.gaierror: + pass + + try: + socket.getaddrinfo(None, LONG_MAX + 1) + except OverflowError: + self.fail("Either no error or socket.gaierror expected.") + except socket.gaierror: + pass + + try: + socket.getaddrinfo(None, LONG_MAX - 0xffff + 1) + except OverflowError: + self.fail("Either no error or socket.gaierror expected.") + except socket.gaierror: + pass + + try: + socket.getaddrinfo(None, LONG_MIN - 1) + except OverflowError: + self.fail("Either no error or socket.gaierror expected.") + except socket.gaierror: + pass + + socket.getaddrinfo(None, 0) # No error expected. + socket.getaddrinfo(None, 0xffff) # No error expected. + def test_getnameinfo(self): # only IP addresses are allowed self.assertRaises(OSError, socket.getnameinfo, ('mail.python.org',0), 0) diff --git a/Misc/ACKS b/Misc/ACKS index e12cbea0ebd6ed..ca92608868f23f 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1688,6 +1688,7 @@ Roman Skurikhin Ville Skyttä Michael Sloan Nick Sloan +Radek Smejkal Václav Šmilauer Casper W. Smet Allen W. Smith diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-13-22-21-58.gh-issue-74895.esMNtq.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-13-22-21-58.gh-issue-74895.esMNtq.rst new file mode 100644 index 00000000000000..adbbb601634a60 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-13-22-21-58.gh-issue-74895.esMNtq.rst @@ -0,0 +1,5 @@ +:mod:`socket.getaddrinfo` no longer raises :class:`OverflowError` for +:class:`int` **port** values outside of the C long range. Out of range values +are left up to the underlying string based C library API to report. A +:class:`socket.gaierror` ``SAI_SERVICE`` may occur instead, or no error at all +as not all platform C libraries generate an error. diff --git a/Modules/getaddrinfo.c b/Modules/getaddrinfo.c index 0b4620ed683de9..f1c28d7d9312ac 100644 --- a/Modules/getaddrinfo.c +++ b/Modules/getaddrinfo.c @@ -342,7 +342,11 @@ getaddrinfo(const char*hostname, const char*servname, pai->ai_socktype = SOCK_DGRAM; pai->ai_protocol = IPPROTO_UDP; } - port = htons((u_short)atoi(servname)); + long maybe_port = strtol(servname, NULL, 10); + if (maybe_port < 0 || maybe_port > 0xffff) { + ERR(EAI_SERVICE); + } + port = htons((u_short)maybe_port); } else { struct servent *sp; const char *proto; diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 0a9e46512b157b..2d300f19436b1a 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -6650,7 +6650,7 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs) struct addrinfo *res0 = NULL; PyObject *hobj = NULL; PyObject *pobj = (PyObject *)NULL; - char pbuf[30]; + PyObject *pstr = NULL; const char *hptr, *pptr; int family, socktype, protocol, flags; int error; @@ -6680,11 +6680,13 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs) return NULL; } if (PyLong_CheckExact(pobj)) { - long value = PyLong_AsLong(pobj); - if (value == -1 && PyErr_Occurred()) + pstr = PyObject_Str(pobj); + if (pstr == NULL) + goto err; + assert(PyUnicode_Check(pstr)); + pptr = PyUnicode_AsUTF8(pstr); + if (pptr == NULL) goto err; - PyOS_snprintf(pbuf, sizeof(pbuf), "%ld", value); - pptr = pbuf; } else if (PyUnicode_Check(pobj)) { pptr = PyUnicode_AsUTF8(pobj); if (pptr == NULL) @@ -6750,12 +6752,14 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs) Py_DECREF(single); } Py_XDECREF(idna); + Py_XDECREF(pstr); if (res0) freeaddrinfo(res0); return all; err: Py_XDECREF(all); Py_XDECREF(idna); + Py_XDECREF(pstr); if (res0) freeaddrinfo(res0); return (PyObject *)NULL; From 8be8101bca34b60481ec3d7ecaea4a3379fb7dbb Mon Sep 17 00:00:00 2001 From: Sam James Date: Tue, 14 Feb 2023 07:21:58 +0000 Subject: [PATCH 168/225] gh-101857: Allow xattr detection on musl libc (#101858) Previously, we checked exclusively for `__GLIBC__` (AND'd with some other conditions). Checking for `__linux__` instead should be fine. This fixes using e.g. `os.listxattr()` on systems using musl libc. Bug: https://bugs.gentoo.org/894130 Co-authored-by: Gregory P. Smith --- .../2023-02-12-22-40-22.gh-issue-101857._bribG.rst | 1 + Modules/posixmodule.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-12-22-40-22.gh-issue-101857._bribG.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-12-22-40-22.gh-issue-101857._bribG.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-12-22-40-22.gh-issue-101857._bribG.rst new file mode 100644 index 00000000000000..832cc300fa9433 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-12-22-40-22.gh-issue-101857._bribG.rst @@ -0,0 +1 @@ +Fix xattr support detection on Linux systems by widening the check to linux, not just glibc. This fixes support for musl. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index cba6cea48b77e1..d9e93473aeadaa 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -274,8 +274,9 @@ corresponding Unix manual entries for more information on calls."); # undef HAVE_SCHED_SETAFFINITY #endif -#if defined(HAVE_SYS_XATTR_H) && defined(__GLIBC__) && !defined(__FreeBSD_kernel__) && !defined(__GNU__) +#if defined(HAVE_SYS_XATTR_H) && defined(__linux__) && !defined(__FreeBSD_kernel__) && !defined(__GNU__) # define USE_XATTRS +# include // Needed for XATTR_SIZE_MAX on musl libc. #endif #ifdef USE_XATTRS From e5da9ab2c82c6b4e4f8ffa699a9a609ea1bea255 Mon Sep 17 00:00:00 2001 From: Jonathan Protzenko Date: Tue, 14 Feb 2023 01:25:16 -0800 Subject: [PATCH 169/225] gh-99108: Import SHA2-384/512 from HACL* (#101707) Replace the builtin hashlib implementations of SHA2-384 and SHA2-512 originally from LibTomCrypt with formally verified, side-channel resistant code from the [HACL*](https://github.com/hacl-star/hacl-star/) project. The builtins remain a fallback only used when OpenSSL does not provide them. --- Makefile.pre.in | 2 +- ...3-02-08-12-57-35.gh-issue-99108.6tnmhA.rst | 4 + Modules/Setup.stdlib.in | 2 +- Modules/_hacl/Hacl_Streaming_SHA2.c | 654 ++++++++++++++++++ Modules/_hacl/Hacl_Streaming_SHA2.h | 85 ++- .../include/krml/FStar_UInt128_Verified.h | 347 ++++++++++ .../include/krml/FStar_UInt_8_16_32_64.h | 2 +- .../krml/fstar_uint128_struct_endianness.h | 68 ++ Modules/_hacl/include/krml/types.h | 14 + .../_hacl/include/python_hacl_namespaces.h | 17 + Modules/_hacl/internal/Hacl_SHA2_Generic.h | 5 +- Modules/_hacl/refresh.sh | 45 +- Modules/sha256module.c | 1 + Modules/sha512module.c | 441 ++---------- configure | 2 +- configure.ac | 4 +- 16 files changed, 1259 insertions(+), 434 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2023-02-08-12-57-35.gh-issue-99108.6tnmhA.rst create mode 100644 Modules/_hacl/include/krml/FStar_UInt128_Verified.h create mode 100644 Modules/_hacl/include/krml/fstar_uint128_struct_endianness.h create mode 100644 Modules/_hacl/include/krml/types.h diff --git a/Makefile.pre.in b/Makefile.pre.in index 7a84b953d97962..d42d4d8a3c1c9f 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2608,7 +2608,7 @@ MODULE__MD5_DEPS=$(srcdir)/Modules/hashlib.h MODULE__SHA1_DEPS=$(srcdir)/Modules/hashlib.h MODULE__SHA256_DEPS=$(srcdir)/Modules/hashlib.h $(srcdir)/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h $(srcdir)/Modules/_hacl/include/krml/lowstar_endianness.h $(srcdir)/Modules/_hacl/include/krml/internal/target.h $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.h MODULE__SHA3_DEPS=$(srcdir)/Modules/_sha3/sha3.c $(srcdir)/Modules/_sha3/sha3.h $(srcdir)/Modules/hashlib.h -MODULE__SHA512_DEPS=$(srcdir)/Modules/hashlib.h +MODULE__SHA512_DEPS=$(srcdir)/Modules/hashlib.h $(srcdir)/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h $(srcdir)/Modules/_hacl/include/krml/lowstar_endianness.h $(srcdir)/Modules/_hacl/include/krml/internal/target.h $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.h MODULE__SOCKET_DEPS=$(srcdir)/Modules/socketmodule.h $(srcdir)/Modules/addrinfo.h $(srcdir)/Modules/getaddrinfo.c $(srcdir)/Modules/getnameinfo.c MODULE__SSL_DEPS=$(srcdir)/Modules/_ssl.h $(srcdir)/Modules/_ssl/cert.c $(srcdir)/Modules/_ssl/debughelpers.c $(srcdir)/Modules/_ssl/misc.c $(srcdir)/Modules/_ssl_data.h $(srcdir)/Modules/_ssl_data_111.h $(srcdir)/Modules/_ssl_data_300.h $(srcdir)/Modules/socketmodule.h MODULE__TESTCAPI_DEPS=$(srcdir)/Modules/_testcapi/testcapi_long.h $(srcdir)/Modules/_testcapi/parts.h diff --git a/Misc/NEWS.d/next/Security/2023-02-08-12-57-35.gh-issue-99108.6tnmhA.rst b/Misc/NEWS.d/next/Security/2023-02-08-12-57-35.gh-issue-99108.6tnmhA.rst new file mode 100644 index 00000000000000..6a7a309dad5d8f --- /dev/null +++ b/Misc/NEWS.d/next/Security/2023-02-08-12-57-35.gh-issue-99108.6tnmhA.rst @@ -0,0 +1,4 @@ +Replace the builtin :mod:`hashlib` implementations of SHA2-384 and SHA2-512 +originally from LibTomCrypt with formally verified, side-channel resistant +code from the `HACL* `_ project. +The builtins remain a fallback only used when OpenSSL does not provide them. diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index f72783810f9415..b6d13e04d3fa87 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -80,7 +80,7 @@ @MODULE__MD5_TRUE@_md5 md5module.c @MODULE__SHA1_TRUE@_sha1 sha1module.c @MODULE__SHA256_TRUE@_sha256 sha256module.c _hacl/Hacl_Streaming_SHA2.c -@MODULE__SHA512_TRUE@_sha512 sha512module.c +@MODULE__SHA512_TRUE@_sha512 sha512module.c _hacl/Hacl_Streaming_SHA2.c @MODULE__SHA3_TRUE@_sha3 _sha3/sha3module.c @MODULE__BLAKE2_TRUE@_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c diff --git a/Modules/_hacl/Hacl_Streaming_SHA2.c b/Modules/_hacl/Hacl_Streaming_SHA2.c index 84566571792a3c..8169c7a356731e 100644 --- a/Modules/_hacl/Hacl_Streaming_SHA2.c +++ b/Modules/_hacl/Hacl_Streaming_SHA2.c @@ -250,6 +250,229 @@ static inline void sha224_finish(uint32_t *st, uint8_t *h) memcpy(h, hbuf, (uint32_t)28U * sizeof (uint8_t)); } +void Hacl_SHA2_Scalar32_sha512_init(uint64_t *hash) +{ + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + uint64_t *os = hash; + uint64_t x = Hacl_Impl_SHA2_Generic_h512[i]; + os[i] = x;); +} + +static inline void sha512_update(uint8_t *b, uint64_t *hash) +{ + uint64_t hash_old[8U] = { 0U }; + uint64_t ws[16U] = { 0U }; + memcpy(hash_old, hash, (uint32_t)8U * sizeof (uint64_t)); + uint8_t *b10 = b; + uint64_t u = load64_be(b10); + ws[0U] = u; + uint64_t u0 = load64_be(b10 + (uint32_t)8U); + ws[1U] = u0; + uint64_t u1 = load64_be(b10 + (uint32_t)16U); + ws[2U] = u1; + uint64_t u2 = load64_be(b10 + (uint32_t)24U); + ws[3U] = u2; + uint64_t u3 = load64_be(b10 + (uint32_t)32U); + ws[4U] = u3; + uint64_t u4 = load64_be(b10 + (uint32_t)40U); + ws[5U] = u4; + uint64_t u5 = load64_be(b10 + (uint32_t)48U); + ws[6U] = u5; + uint64_t u6 = load64_be(b10 + (uint32_t)56U); + ws[7U] = u6; + uint64_t u7 = load64_be(b10 + (uint32_t)64U); + ws[8U] = u7; + uint64_t u8 = load64_be(b10 + (uint32_t)72U); + ws[9U] = u8; + uint64_t u9 = load64_be(b10 + (uint32_t)80U); + ws[10U] = u9; + uint64_t u10 = load64_be(b10 + (uint32_t)88U); + ws[11U] = u10; + uint64_t u11 = load64_be(b10 + (uint32_t)96U); + ws[12U] = u11; + uint64_t u12 = load64_be(b10 + (uint32_t)104U); + ws[13U] = u12; + uint64_t u13 = load64_be(b10 + (uint32_t)112U); + ws[14U] = u13; + uint64_t u14 = load64_be(b10 + (uint32_t)120U); + ws[15U] = u14; + KRML_MAYBE_FOR5(i0, + (uint32_t)0U, + (uint32_t)5U, + (uint32_t)1U, + KRML_MAYBE_FOR16(i, + (uint32_t)0U, + (uint32_t)16U, + (uint32_t)1U, + uint64_t k_t = Hacl_Impl_SHA2_Generic_k384_512[(uint32_t)16U * i0 + i]; + uint64_t ws_t = ws[i]; + uint64_t a0 = hash[0U]; + uint64_t b0 = hash[1U]; + uint64_t c0 = hash[2U]; + uint64_t d0 = hash[3U]; + uint64_t e0 = hash[4U]; + uint64_t f0 = hash[5U]; + uint64_t g0 = hash[6U]; + uint64_t h02 = hash[7U]; + uint64_t k_e_t = k_t; + uint64_t + t1 = + h02 + + + ((e0 << (uint32_t)50U | e0 >> (uint32_t)14U) + ^ + ((e0 << (uint32_t)46U | e0 >> (uint32_t)18U) + ^ (e0 << (uint32_t)23U | e0 >> (uint32_t)41U))) + + ((e0 & f0) ^ (~e0 & g0)) + + k_e_t + + ws_t; + uint64_t + t2 = + ((a0 << (uint32_t)36U | a0 >> (uint32_t)28U) + ^ + ((a0 << (uint32_t)30U | a0 >> (uint32_t)34U) + ^ (a0 << (uint32_t)25U | a0 >> (uint32_t)39U))) + + ((a0 & b0) ^ ((a0 & c0) ^ (b0 & c0))); + uint64_t a1 = t1 + t2; + uint64_t b1 = a0; + uint64_t c1 = b0; + uint64_t d1 = c0; + uint64_t e1 = d0 + t1; + uint64_t f1 = e0; + uint64_t g1 = f0; + uint64_t h12 = g0; + hash[0U] = a1; + hash[1U] = b1; + hash[2U] = c1; + hash[3U] = d1; + hash[4U] = e1; + hash[5U] = f1; + hash[6U] = g1; + hash[7U] = h12;); + if (i0 < (uint32_t)4U) + { + KRML_MAYBE_FOR16(i, + (uint32_t)0U, + (uint32_t)16U, + (uint32_t)1U, + uint64_t t16 = ws[i]; + uint64_t t15 = ws[(i + (uint32_t)1U) % (uint32_t)16U]; + uint64_t t7 = ws[(i + (uint32_t)9U) % (uint32_t)16U]; + uint64_t t2 = ws[(i + (uint32_t)14U) % (uint32_t)16U]; + uint64_t + s1 = + (t2 << (uint32_t)45U | t2 >> (uint32_t)19U) + ^ ((t2 << (uint32_t)3U | t2 >> (uint32_t)61U) ^ t2 >> (uint32_t)6U); + uint64_t + s0 = + (t15 << (uint32_t)63U | t15 >> (uint32_t)1U) + ^ ((t15 << (uint32_t)56U | t15 >> (uint32_t)8U) ^ t15 >> (uint32_t)7U); + ws[i] = s1 + t7 + s0 + t16;); + }); + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + uint64_t *os = hash; + uint64_t x = hash[i] + hash_old[i]; + os[i] = x;); +} + +static inline void sha512_update_nblocks(uint32_t len, uint8_t *b, uint64_t *st) +{ + uint32_t blocks = len / (uint32_t)128U; + for (uint32_t i = (uint32_t)0U; i < blocks; i++) + { + uint8_t *b0 = b; + uint8_t *mb = b0 + i * (uint32_t)128U; + sha512_update(mb, st); + } +} + +static inline void +sha512_update_last(FStar_UInt128_uint128 totlen, uint32_t len, uint8_t *b, uint64_t *hash) +{ + uint32_t blocks; + if (len + (uint32_t)16U + (uint32_t)1U <= (uint32_t)128U) + { + blocks = (uint32_t)1U; + } + else + { + blocks = (uint32_t)2U; + } + uint32_t fin = blocks * (uint32_t)128U; + uint8_t last[256U] = { 0U }; + uint8_t totlen_buf[16U] = { 0U }; + FStar_UInt128_uint128 total_len_bits = FStar_UInt128_shift_left(totlen, (uint32_t)3U); + store128_be(totlen_buf, total_len_bits); + uint8_t *b0 = b; + memcpy(last, b0, len * sizeof (uint8_t)); + last[len] = (uint8_t)0x80U; + memcpy(last + fin - (uint32_t)16U, totlen_buf, (uint32_t)16U * sizeof (uint8_t)); + uint8_t *last00 = last; + uint8_t *last10 = last + (uint32_t)128U; + uint8_t *l0 = last00; + uint8_t *l1 = last10; + uint8_t *lb0 = l0; + uint8_t *lb1 = l1; + uint8_t *last0 = lb0; + uint8_t *last1 = lb1; + sha512_update(last0, hash); + if (blocks > (uint32_t)1U) + { + sha512_update(last1, hash); + return; + } +} + +static inline void sha512_finish(uint64_t *st, uint8_t *h) +{ + uint8_t hbuf[64U] = { 0U }; + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + store64_be(hbuf + i * (uint32_t)8U, st[i]);); + memcpy(h, hbuf, (uint32_t)64U * sizeof (uint8_t)); +} + +static inline void sha384_init(uint64_t *hash) +{ + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + uint64_t *os = hash; + uint64_t x = Hacl_Impl_SHA2_Generic_h384[i]; + os[i] = x;); +} + +static inline void sha384_update_nblocks(uint32_t len, uint8_t *b, uint64_t *st) +{ + sha512_update_nblocks(len, b, st); +} + +static void +sha384_update_last(FStar_UInt128_uint128 totlen, uint32_t len, uint8_t *b, uint64_t *st) +{ + sha512_update_last(totlen, len, b, st); +} + +static inline void sha384_finish(uint64_t *st, uint8_t *h) +{ + uint8_t hbuf[64U] = { 0U }; + KRML_MAYBE_FOR8(i, + (uint32_t)0U, + (uint32_t)8U, + (uint32_t)1U, + store64_be(hbuf + i * (uint32_t)8U, st[i]);); + memcpy(h, hbuf, (uint32_t)48U * sizeof (uint8_t)); +} + /** Allocate initial state for the SHA2_256 hash. The state is to be freed by calling `free_256`. @@ -680,3 +903,434 @@ void Hacl_Streaming_SHA2_sha224(uint8_t *input, uint32_t input_len, uint8_t *dst sha224_finish(st, rb); } +Hacl_Streaming_SHA2_state_sha2_384 *Hacl_Streaming_SHA2_create_in_512(void) +{ + uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)128U, sizeof (uint8_t)); + uint64_t *block_state = (uint64_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint64_t)); + Hacl_Streaming_SHA2_state_sha2_384 + s = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + Hacl_Streaming_SHA2_state_sha2_384 + *p = + (Hacl_Streaming_SHA2_state_sha2_384 *)KRML_HOST_MALLOC(sizeof ( + Hacl_Streaming_SHA2_state_sha2_384 + )); + p[0U] = s; + Hacl_SHA2_Scalar32_sha512_init(block_state); + return p; +} + +/** +Copies the state passed as argument into a newly allocated state (deep copy). +The state is to be freed by calling `free_512`. Cloning the state this way is +useful, for instance, if your control-flow diverges and you need to feed +more (different) data into the hash in each branch. +*/ +Hacl_Streaming_SHA2_state_sha2_384 +*Hacl_Streaming_SHA2_copy_512(Hacl_Streaming_SHA2_state_sha2_384 *s0) +{ + Hacl_Streaming_SHA2_state_sha2_384 scrut = *s0; + uint64_t *block_state0 = scrut.block_state; + uint8_t *buf0 = scrut.buf; + uint64_t total_len0 = scrut.total_len; + uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)128U, sizeof (uint8_t)); + memcpy(buf, buf0, (uint32_t)128U * sizeof (uint8_t)); + uint64_t *block_state = (uint64_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint64_t)); + memcpy(block_state, block_state0, (uint32_t)8U * sizeof (uint64_t)); + Hacl_Streaming_SHA2_state_sha2_384 + s = { .block_state = block_state, .buf = buf, .total_len = total_len0 }; + Hacl_Streaming_SHA2_state_sha2_384 + *p = + (Hacl_Streaming_SHA2_state_sha2_384 *)KRML_HOST_MALLOC(sizeof ( + Hacl_Streaming_SHA2_state_sha2_384 + )); + p[0U] = s; + return p; +} + +void Hacl_Streaming_SHA2_init_512(Hacl_Streaming_SHA2_state_sha2_384 *s) +{ + Hacl_Streaming_SHA2_state_sha2_384 scrut = *s; + uint8_t *buf = scrut.buf; + uint64_t *block_state = scrut.block_state; + Hacl_SHA2_Scalar32_sha512_init(block_state); + Hacl_Streaming_SHA2_state_sha2_384 + tmp = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + s[0U] = tmp; +} + +static inline uint32_t +update_384_512(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *data, uint32_t len) +{ + Hacl_Streaming_SHA2_state_sha2_384 s = *p; + uint64_t total_len = s.total_len; + if ((uint64_t)len > (uint64_t)18446744073709551615U - total_len) + { + return (uint32_t)1U; + } + uint32_t sz; + if (total_len % (uint64_t)(uint32_t)128U == (uint64_t)0U && total_len > (uint64_t)0U) + { + sz = (uint32_t)128U; + } + else + { + sz = (uint32_t)(total_len % (uint64_t)(uint32_t)128U); + } + if (len <= (uint32_t)128U - sz) + { + Hacl_Streaming_SHA2_state_sha2_384 s1 = *p; + uint64_t *block_state1 = s1.block_state; + uint8_t *buf = s1.buf; + uint64_t total_len1 = s1.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)128U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)128U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)128U); + } + uint8_t *buf2 = buf + sz1; + memcpy(buf2, data, len * sizeof (uint8_t)); + uint64_t total_len2 = total_len1 + (uint64_t)len; + *p + = + ( + (Hacl_Streaming_SHA2_state_sha2_384){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len2 + } + ); + } + else if (sz == (uint32_t)0U) + { + Hacl_Streaming_SHA2_state_sha2_384 s1 = *p; + uint64_t *block_state1 = s1.block_state; + uint8_t *buf = s1.buf; + uint64_t total_len1 = s1.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)128U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)128U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)128U); + } + if (!(sz1 == (uint32_t)0U)) + { + sha512_update_nblocks((uint32_t)128U, buf, block_state1); + } + uint32_t ite; + if ((uint64_t)len % (uint64_t)(uint32_t)128U == (uint64_t)0U && (uint64_t)len > (uint64_t)0U) + { + ite = (uint32_t)128U; + } + else + { + ite = (uint32_t)((uint64_t)len % (uint64_t)(uint32_t)128U); + } + uint32_t n_blocks = (len - ite) / (uint32_t)128U; + uint32_t data1_len = n_blocks * (uint32_t)128U; + uint32_t data2_len = len - data1_len; + uint8_t *data1 = data; + uint8_t *data2 = data + data1_len; + sha512_update_nblocks(data1_len, data1, block_state1); + uint8_t *dst = buf; + memcpy(dst, data2, data2_len * sizeof (uint8_t)); + *p + = + ( + (Hacl_Streaming_SHA2_state_sha2_384){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len1 + (uint64_t)len + } + ); + } + else + { + uint32_t diff = (uint32_t)128U - sz; + uint8_t *data1 = data; + uint8_t *data2 = data + diff; + Hacl_Streaming_SHA2_state_sha2_384 s1 = *p; + uint64_t *block_state10 = s1.block_state; + uint8_t *buf0 = s1.buf; + uint64_t total_len10 = s1.total_len; + uint32_t sz10; + if (total_len10 % (uint64_t)(uint32_t)128U == (uint64_t)0U && total_len10 > (uint64_t)0U) + { + sz10 = (uint32_t)128U; + } + else + { + sz10 = (uint32_t)(total_len10 % (uint64_t)(uint32_t)128U); + } + uint8_t *buf2 = buf0 + sz10; + memcpy(buf2, data1, diff * sizeof (uint8_t)); + uint64_t total_len2 = total_len10 + (uint64_t)diff; + *p + = + ( + (Hacl_Streaming_SHA2_state_sha2_384){ + .block_state = block_state10, + .buf = buf0, + .total_len = total_len2 + } + ); + Hacl_Streaming_SHA2_state_sha2_384 s10 = *p; + uint64_t *block_state1 = s10.block_state; + uint8_t *buf = s10.buf; + uint64_t total_len1 = s10.total_len; + uint32_t sz1; + if (total_len1 % (uint64_t)(uint32_t)128U == (uint64_t)0U && total_len1 > (uint64_t)0U) + { + sz1 = (uint32_t)128U; + } + else + { + sz1 = (uint32_t)(total_len1 % (uint64_t)(uint32_t)128U); + } + if (!(sz1 == (uint32_t)0U)) + { + sha512_update_nblocks((uint32_t)128U, buf, block_state1); + } + uint32_t ite; + if + ( + (uint64_t)(len - diff) + % (uint64_t)(uint32_t)128U + == (uint64_t)0U + && (uint64_t)(len - diff) > (uint64_t)0U + ) + { + ite = (uint32_t)128U; + } + else + { + ite = (uint32_t)((uint64_t)(len - diff) % (uint64_t)(uint32_t)128U); + } + uint32_t n_blocks = (len - diff - ite) / (uint32_t)128U; + uint32_t data1_len = n_blocks * (uint32_t)128U; + uint32_t data2_len = len - diff - data1_len; + uint8_t *data11 = data2; + uint8_t *data21 = data2 + data1_len; + sha512_update_nblocks(data1_len, data11, block_state1); + uint8_t *dst = buf; + memcpy(dst, data21, data2_len * sizeof (uint8_t)); + *p + = + ( + (Hacl_Streaming_SHA2_state_sha2_384){ + .block_state = block_state1, + .buf = buf, + .total_len = total_len1 + (uint64_t)(len - diff) + } + ); + } + return (uint32_t)0U; +} + +/** +Feed an arbitrary amount of data into the hash. This function returns 0 for +success, or 1 if the combined length of all of the data passed to `update_512` +(since the last call to `init_512`) exceeds 2^125-1 bytes. + +This function is identical to the update function for SHA2_384. +*/ +uint32_t +Hacl_Streaming_SHA2_update_512( + Hacl_Streaming_SHA2_state_sha2_384 *p, + uint8_t *input, + uint32_t input_len +) +{ + return update_384_512(p, input, input_len); +} + +/** +Write the resulting hash into `dst`, an array of 64 bytes. The state remains +valid after a call to `finish_512`, meaning the user may feed more data into +the hash via `update_512`. (The finish_512 function operates on an internal copy of +the state and therefore does not invalidate the client-held state `p`.) +*/ +void Hacl_Streaming_SHA2_finish_512(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *dst) +{ + Hacl_Streaming_SHA2_state_sha2_384 scrut = *p; + uint64_t *block_state = scrut.block_state; + uint8_t *buf_ = scrut.buf; + uint64_t total_len = scrut.total_len; + uint32_t r; + if (total_len % (uint64_t)(uint32_t)128U == (uint64_t)0U && total_len > (uint64_t)0U) + { + r = (uint32_t)128U; + } + else + { + r = (uint32_t)(total_len % (uint64_t)(uint32_t)128U); + } + uint8_t *buf_1 = buf_; + uint64_t tmp_block_state[8U] = { 0U }; + memcpy(tmp_block_state, block_state, (uint32_t)8U * sizeof (uint64_t)); + uint32_t ite; + if (r % (uint32_t)128U == (uint32_t)0U && r > (uint32_t)0U) + { + ite = (uint32_t)128U; + } + else + { + ite = r % (uint32_t)128U; + } + uint8_t *buf_last = buf_1 + r - ite; + uint8_t *buf_multi = buf_1; + sha512_update_nblocks((uint32_t)0U, buf_multi, tmp_block_state); + uint64_t prev_len_last = total_len - (uint64_t)r; + sha512_update_last(FStar_UInt128_add(FStar_UInt128_uint64_to_uint128(prev_len_last), + FStar_UInt128_uint64_to_uint128((uint64_t)r)), + r, + buf_last, + tmp_block_state); + sha512_finish(tmp_block_state, dst); +} + +/** +Free a state allocated with `create_in_512`. + +This function is identical to the free function for SHA2_384. +*/ +void Hacl_Streaming_SHA2_free_512(Hacl_Streaming_SHA2_state_sha2_384 *s) +{ + Hacl_Streaming_SHA2_state_sha2_384 scrut = *s; + uint8_t *buf = scrut.buf; + uint64_t *block_state = scrut.block_state; + KRML_HOST_FREE(block_state); + KRML_HOST_FREE(buf); + KRML_HOST_FREE(s); +} + +/** +Hash `input`, of len `input_len`, into `dst`, an array of 64 bytes. +*/ +void Hacl_Streaming_SHA2_sha512(uint8_t *input, uint32_t input_len, uint8_t *dst) +{ + uint8_t *ib = input; + uint8_t *rb = dst; + uint64_t st[8U] = { 0U }; + Hacl_SHA2_Scalar32_sha512_init(st); + uint32_t rem = input_len % (uint32_t)128U; + FStar_UInt128_uint128 len_ = FStar_UInt128_uint64_to_uint128((uint64_t)input_len); + sha512_update_nblocks(input_len, ib, st); + uint32_t rem1 = input_len % (uint32_t)128U; + uint8_t *b0 = ib; + uint8_t *lb = b0 + input_len - rem1; + sha512_update_last(len_, rem, lb, st); + sha512_finish(st, rb); +} + +Hacl_Streaming_SHA2_state_sha2_384 *Hacl_Streaming_SHA2_create_in_384(void) +{ + uint8_t *buf = (uint8_t *)KRML_HOST_CALLOC((uint32_t)128U, sizeof (uint8_t)); + uint64_t *block_state = (uint64_t *)KRML_HOST_CALLOC((uint32_t)8U, sizeof (uint64_t)); + Hacl_Streaming_SHA2_state_sha2_384 + s = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + Hacl_Streaming_SHA2_state_sha2_384 + *p = + (Hacl_Streaming_SHA2_state_sha2_384 *)KRML_HOST_MALLOC(sizeof ( + Hacl_Streaming_SHA2_state_sha2_384 + )); + p[0U] = s; + sha384_init(block_state); + return p; +} + +void Hacl_Streaming_SHA2_init_384(Hacl_Streaming_SHA2_state_sha2_384 *s) +{ + Hacl_Streaming_SHA2_state_sha2_384 scrut = *s; + uint8_t *buf = scrut.buf; + uint64_t *block_state = scrut.block_state; + sha384_init(block_state); + Hacl_Streaming_SHA2_state_sha2_384 + tmp = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)(uint32_t)0U }; + s[0U] = tmp; +} + +uint32_t +Hacl_Streaming_SHA2_update_384( + Hacl_Streaming_SHA2_state_sha2_384 *p, + uint8_t *input, + uint32_t input_len +) +{ + return update_384_512(p, input, input_len); +} + +/** +Write the resulting hash into `dst`, an array of 48 bytes. The state remains +valid after a call to `finish_384`, meaning the user may feed more data into +the hash via `update_384`. +*/ +void Hacl_Streaming_SHA2_finish_384(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *dst) +{ + Hacl_Streaming_SHA2_state_sha2_384 scrut = *p; + uint64_t *block_state = scrut.block_state; + uint8_t *buf_ = scrut.buf; + uint64_t total_len = scrut.total_len; + uint32_t r; + if (total_len % (uint64_t)(uint32_t)128U == (uint64_t)0U && total_len > (uint64_t)0U) + { + r = (uint32_t)128U; + } + else + { + r = (uint32_t)(total_len % (uint64_t)(uint32_t)128U); + } + uint8_t *buf_1 = buf_; + uint64_t tmp_block_state[8U] = { 0U }; + memcpy(tmp_block_state, block_state, (uint32_t)8U * sizeof (uint64_t)); + uint32_t ite; + if (r % (uint32_t)128U == (uint32_t)0U && r > (uint32_t)0U) + { + ite = (uint32_t)128U; + } + else + { + ite = r % (uint32_t)128U; + } + uint8_t *buf_last = buf_1 + r - ite; + uint8_t *buf_multi = buf_1; + sha384_update_nblocks((uint32_t)0U, buf_multi, tmp_block_state); + uint64_t prev_len_last = total_len - (uint64_t)r; + sha384_update_last(FStar_UInt128_add(FStar_UInt128_uint64_to_uint128(prev_len_last), + FStar_UInt128_uint64_to_uint128((uint64_t)r)), + r, + buf_last, + tmp_block_state); + sha384_finish(tmp_block_state, dst); +} + +void Hacl_Streaming_SHA2_free_384(Hacl_Streaming_SHA2_state_sha2_384 *p) +{ + Hacl_Streaming_SHA2_free_512(p); +} + +/** +Hash `input`, of len `input_len`, into `dst`, an array of 48 bytes. +*/ +void Hacl_Streaming_SHA2_sha384(uint8_t *input, uint32_t input_len, uint8_t *dst) +{ + uint8_t *ib = input; + uint8_t *rb = dst; + uint64_t st[8U] = { 0U }; + sha384_init(st); + uint32_t rem = input_len % (uint32_t)128U; + FStar_UInt128_uint128 len_ = FStar_UInt128_uint64_to_uint128((uint64_t)input_len); + sha384_update_nblocks(input_len, ib, st); + uint32_t rem1 = input_len % (uint32_t)128U; + uint8_t *b0 = ib; + uint8_t *lb = b0 + input_len - rem1; + sha384_update_last(len_, rem, lb, st); + sha384_finish(st, rb); +} + diff --git a/Modules/_hacl/Hacl_Streaming_SHA2.h b/Modules/_hacl/Hacl_Streaming_SHA2.h index c83a835afe70fd..2c905854f336fd 100644 --- a/Modules/_hacl/Hacl_Streaming_SHA2.h +++ b/Modules/_hacl/Hacl_Streaming_SHA2.h @@ -32,13 +32,12 @@ extern "C" { #include #include "python_hacl_namespaces.h" -#include "krml/FStar_UInt_8_16_32_64.h" +#include "krml/types.h" #include "krml/lowstar_endianness.h" #include "krml/internal/target.h" - typedef struct Hacl_Streaming_SHA2_state_sha2_224_s { uint32_t *block_state; @@ -49,6 +48,16 @@ Hacl_Streaming_SHA2_state_sha2_224; typedef Hacl_Streaming_SHA2_state_sha2_224 Hacl_Streaming_SHA2_state_sha2_256; +typedef struct Hacl_Streaming_SHA2_state_sha2_384_s +{ + uint64_t *block_state; + uint8_t *buf; + uint64_t total_len; +} +Hacl_Streaming_SHA2_state_sha2_384; + +typedef Hacl_Streaming_SHA2_state_sha2_384 Hacl_Streaming_SHA2_state_sha2_512; + /** Allocate initial state for the SHA2_256 hash. The state is to be freed by calling `free_256`. @@ -128,6 +137,78 @@ Hash `input`, of len `input_len`, into `dst`, an array of 28 bytes. */ void Hacl_Streaming_SHA2_sha224(uint8_t *input, uint32_t input_len, uint8_t *dst); +Hacl_Streaming_SHA2_state_sha2_384 *Hacl_Streaming_SHA2_create_in_512(void); + +/** +Copies the state passed as argument into a newly allocated state (deep copy). +The state is to be freed by calling `free_512`. Cloning the state this way is +useful, for instance, if your control-flow diverges and you need to feed +more (different) data into the hash in each branch. +*/ +Hacl_Streaming_SHA2_state_sha2_384 +*Hacl_Streaming_SHA2_copy_512(Hacl_Streaming_SHA2_state_sha2_384 *s0); + +void Hacl_Streaming_SHA2_init_512(Hacl_Streaming_SHA2_state_sha2_384 *s); + +/** +Feed an arbitrary amount of data into the hash. This function returns 0 for +success, or 1 if the combined length of all of the data passed to `update_512` +(since the last call to `init_512`) exceeds 2^125-1 bytes. + +This function is identical to the update function for SHA2_384. +*/ +uint32_t +Hacl_Streaming_SHA2_update_512( + Hacl_Streaming_SHA2_state_sha2_384 *p, + uint8_t *input, + uint32_t input_len +); + +/** +Write the resulting hash into `dst`, an array of 64 bytes. The state remains +valid after a call to `finish_512`, meaning the user may feed more data into +the hash via `update_512`. (The finish_512 function operates on an internal copy of +the state and therefore does not invalidate the client-held state `p`.) +*/ +void Hacl_Streaming_SHA2_finish_512(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *dst); + +/** +Free a state allocated with `create_in_512`. + +This function is identical to the free function for SHA2_384. +*/ +void Hacl_Streaming_SHA2_free_512(Hacl_Streaming_SHA2_state_sha2_384 *s); + +/** +Hash `input`, of len `input_len`, into `dst`, an array of 64 bytes. +*/ +void Hacl_Streaming_SHA2_sha512(uint8_t *input, uint32_t input_len, uint8_t *dst); + +Hacl_Streaming_SHA2_state_sha2_384 *Hacl_Streaming_SHA2_create_in_384(void); + +void Hacl_Streaming_SHA2_init_384(Hacl_Streaming_SHA2_state_sha2_384 *s); + +uint32_t +Hacl_Streaming_SHA2_update_384( + Hacl_Streaming_SHA2_state_sha2_384 *p, + uint8_t *input, + uint32_t input_len +); + +/** +Write the resulting hash into `dst`, an array of 48 bytes. The state remains +valid after a call to `finish_384`, meaning the user may feed more data into +the hash via `update_384`. +*/ +void Hacl_Streaming_SHA2_finish_384(Hacl_Streaming_SHA2_state_sha2_384 *p, uint8_t *dst); + +void Hacl_Streaming_SHA2_free_384(Hacl_Streaming_SHA2_state_sha2_384 *p); + +/** +Hash `input`, of len `input_len`, into `dst`, an array of 48 bytes. +*/ +void Hacl_Streaming_SHA2_sha384(uint8_t *input, uint32_t input_len, uint8_t *dst); + #if defined(__cplusplus) } #endif diff --git a/Modules/_hacl/include/krml/FStar_UInt128_Verified.h b/Modules/_hacl/include/krml/FStar_UInt128_Verified.h new file mode 100644 index 00000000000000..ee160193539e28 --- /dev/null +++ b/Modules/_hacl/include/krml/FStar_UInt128_Verified.h @@ -0,0 +1,347 @@ +/* + Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. +*/ + + +#ifndef __FStar_UInt128_Verified_H +#define __FStar_UInt128_Verified_H + + + +#include "FStar_UInt_8_16_32_64.h" +#include +#include +#include "krml/types.h" +#include "krml/internal/target.h" +static inline uint64_t FStar_UInt128_constant_time_carry(uint64_t a, uint64_t b) +{ + return (a ^ ((a ^ b) | ((a - b) ^ b))) >> (uint32_t)63U; +} + +static inline uint64_t FStar_UInt128_carry(uint64_t a, uint64_t b) +{ + return FStar_UInt128_constant_time_carry(a, b); +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_add(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low + b.low; + lit.high = a.high + b.high + FStar_UInt128_carry(a.low + b.low, b.low); + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_add_underspec(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low + b.low; + lit.high = a.high + b.high + FStar_UInt128_carry(a.low + b.low, b.low); + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_add_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low + b.low; + lit.high = a.high + b.high + FStar_UInt128_carry(a.low + b.low, b.low); + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_sub(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low - b.low; + lit.high = a.high - b.high - FStar_UInt128_carry(a.low, a.low - b.low); + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_sub_underspec(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low - b.low; + lit.high = a.high - b.high - FStar_UInt128_carry(a.low, a.low - b.low); + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_sub_mod_impl(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low - b.low; + lit.high = a.high - b.high - FStar_UInt128_carry(a.low, a.low - b.low); + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_sub_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return FStar_UInt128_sub_mod_impl(a, b); +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_logand(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low & b.low; + lit.high = a.high & b.high; + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_logxor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low ^ b.low; + lit.high = a.high ^ b.high; + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_logor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = a.low | b.low; + lit.high = a.high | b.high; + return lit; +} + +static inline FStar_UInt128_uint128 FStar_UInt128_lognot(FStar_UInt128_uint128 a) +{ + FStar_UInt128_uint128 lit; + lit.low = ~a.low; + lit.high = ~a.high; + return lit; +} + +static uint32_t FStar_UInt128_u32_64 = (uint32_t)64U; + +static inline uint64_t FStar_UInt128_add_u64_shift_left(uint64_t hi, uint64_t lo, uint32_t s) +{ + return (hi << s) + (lo >> (FStar_UInt128_u32_64 - s)); +} + +static inline uint64_t +FStar_UInt128_add_u64_shift_left_respec(uint64_t hi, uint64_t lo, uint32_t s) +{ + return FStar_UInt128_add_u64_shift_left(hi, lo, s); +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_shift_left_small(FStar_UInt128_uint128 a, uint32_t s) +{ + if (s == (uint32_t)0U) + { + return a; + } + else + { + FStar_UInt128_uint128 lit; + lit.low = a.low << s; + lit.high = FStar_UInt128_add_u64_shift_left_respec(a.high, a.low, s); + return lit; + } +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_shift_left_large(FStar_UInt128_uint128 a, uint32_t s) +{ + FStar_UInt128_uint128 lit; + lit.low = (uint64_t)0U; + lit.high = a.low << (s - FStar_UInt128_u32_64); + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_shift_left(FStar_UInt128_uint128 a, uint32_t s) +{ + if (s < FStar_UInt128_u32_64) + { + return FStar_UInt128_shift_left_small(a, s); + } + else + { + return FStar_UInt128_shift_left_large(a, s); + } +} + +static inline uint64_t FStar_UInt128_add_u64_shift_right(uint64_t hi, uint64_t lo, uint32_t s) +{ + return (lo >> s) + (hi << (FStar_UInt128_u32_64 - s)); +} + +static inline uint64_t +FStar_UInt128_add_u64_shift_right_respec(uint64_t hi, uint64_t lo, uint32_t s) +{ + return FStar_UInt128_add_u64_shift_right(hi, lo, s); +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_shift_right_small(FStar_UInt128_uint128 a, uint32_t s) +{ + if (s == (uint32_t)0U) + { + return a; + } + else + { + FStar_UInt128_uint128 lit; + lit.low = FStar_UInt128_add_u64_shift_right_respec(a.high, a.low, s); + lit.high = a.high >> s; + return lit; + } +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_shift_right_large(FStar_UInt128_uint128 a, uint32_t s) +{ + FStar_UInt128_uint128 lit; + lit.low = a.high >> (s - FStar_UInt128_u32_64); + lit.high = (uint64_t)0U; + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_shift_right(FStar_UInt128_uint128 a, uint32_t s) +{ + if (s < FStar_UInt128_u32_64) + { + return FStar_UInt128_shift_right_small(a, s); + } + else + { + return FStar_UInt128_shift_right_large(a, s); + } +} + +static inline bool FStar_UInt128_eq(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return a.low == b.low && a.high == b.high; +} + +static inline bool FStar_UInt128_gt(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return a.high > b.high || (a.high == b.high && a.low > b.low); +} + +static inline bool FStar_UInt128_lt(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return a.high < b.high || (a.high == b.high && a.low < b.low); +} + +static inline bool FStar_UInt128_gte(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return a.high > b.high || (a.high == b.high && a.low >= b.low); +} + +static inline bool FStar_UInt128_lte(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return a.high < b.high || (a.high == b.high && a.low <= b.low); +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_eq_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = FStar_UInt64_eq_mask(a.low, b.low) & FStar_UInt64_eq_mask(a.high, b.high); + lit.high = FStar_UInt64_eq_mask(a.low, b.low) & FStar_UInt64_eq_mask(a.high, b.high); + return lit; +} + +static inline FStar_UInt128_uint128 +FStar_UInt128_gte_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 lit; + lit.low = + (FStar_UInt64_gte_mask(a.high, b.high) & ~FStar_UInt64_eq_mask(a.high, b.high)) + | (FStar_UInt64_eq_mask(a.high, b.high) & FStar_UInt64_gte_mask(a.low, b.low)); + lit.high = + (FStar_UInt64_gte_mask(a.high, b.high) & ~FStar_UInt64_eq_mask(a.high, b.high)) + | (FStar_UInt64_eq_mask(a.high, b.high) & FStar_UInt64_gte_mask(a.low, b.low)); + return lit; +} + +static inline FStar_UInt128_uint128 FStar_UInt128_uint64_to_uint128(uint64_t a) +{ + FStar_UInt128_uint128 lit; + lit.low = a; + lit.high = (uint64_t)0U; + return lit; +} + +static inline uint64_t FStar_UInt128_uint128_to_uint64(FStar_UInt128_uint128 a) +{ + return a.low; +} + +static inline uint64_t FStar_UInt128_u64_mod_32(uint64_t a) +{ + return a & (uint64_t)0xffffffffU; +} + +static uint32_t FStar_UInt128_u32_32 = (uint32_t)32U; + +static inline uint64_t FStar_UInt128_u32_combine(uint64_t hi, uint64_t lo) +{ + return lo + (hi << FStar_UInt128_u32_32); +} + +static inline FStar_UInt128_uint128 FStar_UInt128_mul32(uint64_t x, uint32_t y) +{ + FStar_UInt128_uint128 lit; + lit.low = + FStar_UInt128_u32_combine((x >> FStar_UInt128_u32_32) + * (uint64_t)y + + (FStar_UInt128_u64_mod_32(x) * (uint64_t)y >> FStar_UInt128_u32_32), + FStar_UInt128_u64_mod_32(FStar_UInt128_u64_mod_32(x) * (uint64_t)y)); + lit.high = + ((x >> FStar_UInt128_u32_32) + * (uint64_t)y + + (FStar_UInt128_u64_mod_32(x) * (uint64_t)y >> FStar_UInt128_u32_32)) + >> FStar_UInt128_u32_32; + return lit; +} + +static inline uint64_t FStar_UInt128_u32_combine_(uint64_t hi, uint64_t lo) +{ + return lo + (hi << FStar_UInt128_u32_32); +} + +static inline FStar_UInt128_uint128 FStar_UInt128_mul_wide(uint64_t x, uint64_t y) +{ + FStar_UInt128_uint128 lit; + lit.low = + FStar_UInt128_u32_combine_(FStar_UInt128_u64_mod_32(x) + * (y >> FStar_UInt128_u32_32) + + + FStar_UInt128_u64_mod_32((x >> FStar_UInt128_u32_32) + * FStar_UInt128_u64_mod_32(y) + + (FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y) >> FStar_UInt128_u32_32)), + FStar_UInt128_u64_mod_32(FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y))); + lit.high = + (x >> FStar_UInt128_u32_32) + * (y >> FStar_UInt128_u32_32) + + + (((x >> FStar_UInt128_u32_32) + * FStar_UInt128_u64_mod_32(y) + + (FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y) >> FStar_UInt128_u32_32)) + >> FStar_UInt128_u32_32) + + + ((FStar_UInt128_u64_mod_32(x) + * (y >> FStar_UInt128_u32_32) + + + FStar_UInt128_u64_mod_32((x >> FStar_UInt128_u32_32) + * FStar_UInt128_u64_mod_32(y) + + (FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y) >> FStar_UInt128_u32_32))) + >> FStar_UInt128_u32_32); + return lit; +} + + +#define __FStar_UInt128_Verified_H_DEFINED +#endif diff --git a/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h b/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h index 3e2e4b32b22f96..965afc836fd12b 100644 --- a/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h +++ b/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h @@ -14,7 +14,7 @@ #include #include "krml/lowstar_endianness.h" -#include "krml/FStar_UInt_8_16_32_64.h" +#include "krml/types.h" #include "krml/internal/target.h" static inline uint64_t FStar_UInt64_eq_mask(uint64_t a, uint64_t b) { diff --git a/Modules/_hacl/include/krml/fstar_uint128_struct_endianness.h b/Modules/_hacl/include/krml/fstar_uint128_struct_endianness.h new file mode 100644 index 00000000000000..e2b6d62859a5f1 --- /dev/null +++ b/Modules/_hacl/include/krml/fstar_uint128_struct_endianness.h @@ -0,0 +1,68 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +#ifndef FSTAR_UINT128_STRUCT_ENDIANNESS_H +#define FSTAR_UINT128_STRUCT_ENDIANNESS_H + +/* Hand-written implementation of endianness-related uint128 functions + * for the extracted uint128 implementation */ + +/* Access 64-bit fields within the int128. */ +#define HIGH64_OF(x) ((x)->high) +#define LOW64_OF(x) ((x)->low) + +/* A series of definitions written using pointers. */ + +inline static void load128_le_(uint8_t *b, uint128_t *r) { + LOW64_OF(r) = load64_le(b); + HIGH64_OF(r) = load64_le(b + 8); +} + +inline static void store128_le_(uint8_t *b, uint128_t *n) { + store64_le(b, LOW64_OF(n)); + store64_le(b + 8, HIGH64_OF(n)); +} + +inline static void load128_be_(uint8_t *b, uint128_t *r) { + HIGH64_OF(r) = load64_be(b); + LOW64_OF(r) = load64_be(b + 8); +} + +inline static void store128_be_(uint8_t *b, uint128_t *n) { + store64_be(b, HIGH64_OF(n)); + store64_be(b + 8, LOW64_OF(n)); +} + +#ifndef KRML_NOSTRUCT_PASSING + +inline static uint128_t load128_le(uint8_t *b) { + uint128_t r; + load128_le_(b, &r); + return r; +} + +inline static void store128_le(uint8_t *b, uint128_t n) { + store128_le_(b, &n); +} + +inline static uint128_t load128_be(uint8_t *b) { + uint128_t r; + load128_be_(b, &r); + return r; +} + +inline static void store128_be(uint8_t *b, uint128_t n) { + store128_be_(b, &n); +} + +#else /* !defined(KRML_STRUCT_PASSING) */ + +# define print128 print128_ +# define load128_le load128_le_ +# define store128_le store128_le_ +# define load128_be load128_be_ +# define store128_be store128_be_ + +#endif /* KRML_STRUCT_PASSING */ + +#endif diff --git a/Modules/_hacl/include/krml/types.h b/Modules/_hacl/include/krml/types.h new file mode 100644 index 00000000000000..509f555536e4c6 --- /dev/null +++ b/Modules/_hacl/include/krml/types.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct FStar_UInt128_uint128_s { + uint64_t low; + uint64_t high; +} FStar_UInt128_uint128, uint128_t; + +#define KRML_VERIFIED_UINT128 + +#include "krml/lowstar_endianness.h" +#include "krml/fstar_uint128_struct_endianness.h" +#include "krml/FStar_UInt128_Verified.h" diff --git a/Modules/_hacl/include/python_hacl_namespaces.h b/Modules/_hacl/include/python_hacl_namespaces.h index af390459311fe8..65608d1fd283c4 100644 --- a/Modules/_hacl/include/python_hacl_namespaces.h +++ b/Modules/_hacl/include/python_hacl_namespaces.h @@ -10,19 +10,36 @@ #define Hacl_Streaming_SHA2_state_sha2_224_s python_hashlib_Hacl_Streaming_SHA2_state_sha2_224_s #define Hacl_Streaming_SHA2_state_sha2_224 python_hashlib_Hacl_Streaming_SHA2_state_sha2_224 #define Hacl_Streaming_SHA2_state_sha2_256 python_hashlib_Hacl_Streaming_SHA2_state_sha2_256 +#define Hacl_Streaming_SHA2_state_sha2_384_s python_hashlib_Hacl_Streaming_SHA2_state_sha2_384_s +#define Hacl_Streaming_SHA2_state_sha2_384 python_hashlib_Hacl_Streaming_SHA2_state_sha2_384 +#define Hacl_Streaming_SHA2_state_sha2_512 python_hashlib_Hacl_Streaming_SHA2_state_sha2_512 #define Hacl_Streaming_SHA2_create_in_256 python_hashlib_Hacl_Streaming_SHA2_create_in_256 #define Hacl_Streaming_SHA2_create_in_224 python_hashlib_Hacl_Streaming_SHA2_create_in_224 +#define Hacl_Streaming_SHA2_create_in_512 python_hashlib_Hacl_Streaming_SHA2_create_in_512 +#define Hacl_Streaming_SHA2_create_in_384 python_hashlib_Hacl_Streaming_SHA2_create_in_384 #define Hacl_Streaming_SHA2_copy_256 python_hashlib_Hacl_Streaming_SHA2_copy_256 #define Hacl_Streaming_SHA2_copy_224 python_hashlib_Hacl_Streaming_SHA2_copy_224 +#define Hacl_Streaming_SHA2_copy_512 python_hashlib_Hacl_Streaming_SHA2_copy_512 +#define Hacl_Streaming_SHA2_copy_384 python_hashlib_Hacl_Streaming_SHA2_copy_384 #define Hacl_Streaming_SHA2_init_256 python_hashlib_Hacl_Streaming_SHA2_init_256 #define Hacl_Streaming_SHA2_init_224 python_hashlib_Hacl_Streaming_SHA2_init_224 +#define Hacl_Streaming_SHA2_init_512 python_hashlib_Hacl_Streaming_SHA2_init_512 +#define Hacl_Streaming_SHA2_init_384 python_hashlib_Hacl_Streaming_SHA2_init_384 #define Hacl_Streaming_SHA2_update_256 python_hashlib_Hacl_Streaming_SHA2_update_256 #define Hacl_Streaming_SHA2_update_224 python_hashlib_Hacl_Streaming_SHA2_update_224 +#define Hacl_Streaming_SHA2_update_512 python_hashlib_Hacl_Streaming_SHA2_update_512 +#define Hacl_Streaming_SHA2_update_384 python_hashlib_Hacl_Streaming_SHA2_update_384 #define Hacl_Streaming_SHA2_finish_256 python_hashlib_Hacl_Streaming_SHA2_finish_256 #define Hacl_Streaming_SHA2_finish_224 python_hashlib_Hacl_Streaming_SHA2_finish_224 +#define Hacl_Streaming_SHA2_finish_512 python_hashlib_Hacl_Streaming_SHA2_finish_512 +#define Hacl_Streaming_SHA2_finish_384 python_hashlib_Hacl_Streaming_SHA2_finish_384 #define Hacl_Streaming_SHA2_free_256 python_hashlib_Hacl_Streaming_SHA2_free_256 #define Hacl_Streaming_SHA2_free_224 python_hashlib_Hacl_Streaming_SHA2_free_224 +#define Hacl_Streaming_SHA2_free_512 python_hashlib_Hacl_Streaming_SHA2_free_512 +#define Hacl_Streaming_SHA2_free_384 python_hashlib_Hacl_Streaming_SHA2_free_384 #define Hacl_Streaming_SHA2_sha256 python_hashlib_Hacl_Streaming_SHA2_sha256 #define Hacl_Streaming_SHA2_sha224 python_hashlib_Hacl_Streaming_SHA2_sha224 +#define Hacl_Streaming_SHA2_sha512 python_hashlib_Hacl_Streaming_SHA2_sha512 +#define Hacl_Streaming_SHA2_sha384 python_hashlib_Hacl_Streaming_SHA2_sha384 #endif // _PYTHON_HACL_NAMESPACES_H diff --git a/Modules/_hacl/internal/Hacl_SHA2_Generic.h b/Modules/_hacl/internal/Hacl_SHA2_Generic.h index 23f7cea1eb3884..6ac47f3cf7ed36 100644 --- a/Modules/_hacl/internal/Hacl_SHA2_Generic.h +++ b/Modules/_hacl/internal/Hacl_SHA2_Generic.h @@ -31,13 +31,10 @@ extern "C" { #endif #include -#include "krml/FStar_UInt_8_16_32_64.h" +#include "krml/types.h" #include "krml/lowstar_endianness.h" #include "krml/internal/target.h" - - - static const uint32_t Hacl_Impl_SHA2_Generic_h224[8U] = diff --git a/Modules/_hacl/refresh.sh b/Modules/_hacl/refresh.sh index 594873862a2db0..dba8cb3972ea17 100755 --- a/Modules/_hacl/refresh.sh +++ b/Modules/_hacl/refresh.sh @@ -22,7 +22,7 @@ fi # Update this when updating to a new version after verifying that the changes # the update brings in are good. -expected_hacl_star_rev=94aabbb4cf71347d3779a8db486c761403c6d036 +expected_hacl_star_rev=4751fc2b11639f651718abf8522fcc36902ca67c hacl_dir="$(realpath "$1")" cd "$(dirname "$0")" @@ -54,6 +54,8 @@ include_files=( declare -a lib_files lib_files=( krmllib/dist/minimal/FStar_UInt_8_16_32_64.h + krmllib/dist/minimal/fstar_uint128_struct_endianness.h + krmllib/dist/minimal/FStar_UInt128_Verified.h ) # C files for the algorithms themselves: current directory @@ -82,10 +84,27 @@ fi readarray -t all_files < <(find . -name '*.h' -or -name '*.c') -# types.h is a simple wrapper that defines the uint128 type then proceeds to -# include FStar_UInt_8_16_32_64.h; we jump the types.h step since our current -# selection of algorithms does not necessitate the use of uint128 -$sed -i 's!#include.*types.h"!#include "krml/FStar_UInt_8_16_32_64.h"!g' "${all_files[@]}" +# types.h originally contains a complex series of if-defs and auxiliary type +# definitions; here, we just need a proper uint128 type in scope +# is a simple wrapper that defines the uint128 type +cat > include/krml/types.h < + +typedef struct FStar_UInt128_uint128_s { + uint64_t low; + uint64_t high; +} FStar_UInt128_uint128, uint128_t; + +#define KRML_VERIFIED_UINT128 + +#include "krml/lowstar_endianness.h" +#include "krml/fstar_uint128_struct_endianness.h" +#include "krml/FStar_UInt128_Verified.h" +EOF +# Adjust the include path to reflect the local directory structure +$sed -i 's!#include.*types.h"!#include "krml/types.h"!g' "${all_files[@]}" $sed -i 's!#include.*compat.h"!!g' "${all_files[@]}" # FStar_UInt_8_16_32_64 contains definitions useful in the general case, but not @@ -103,22 +122,6 @@ $sed -i 's!#include.*Hacl_Krmllib.h"!!g' "${all_files[@]}" # included in $dist_files). $sed -i 's!#include.*internal/Hacl_Streaming_SHA2.h"!#include "Hacl_Streaming_SHA2.h"!g' "${all_files[@]}" -# The SHA2 file contains all variants of SHA2. We strip 384 and 512 for the time -# being, to be included later. -# This regexp matches a separator (two new lines), followed by: -# -# * -# ... 384 or 512 ... { -# * -# } -# -# The first non-empty lines are the comment block. The second ... may spill over -# the next following lines if the arguments are printed in one-per-line mode. -$sed -i -z 's/\n\n\([^\n]\+\n\)*[^\n]*\(384\|512\)[^{]*{\n\?\( [^\n]*\n\)*}//g' Hacl_Streaming_SHA2.c - -# Same thing with function prototypes -$sed -i -z 's/\n\n\([^\n]\+\n\)*[^\n]*\(384\|512\)[^;]*;//g' Hacl_Streaming_SHA2.h - # Use globally unique names for the Hacl_ C APIs to avoid linkage conflicts. $sed -i -z 's!#include \n!#include \n#include "python_hacl_namespaces.h"\n!' Hacl_Streaming_SHA2.h diff --git a/Modules/sha256module.c b/Modules/sha256module.c index 630e4bf03bbe96..301c9837bb6720 100644 --- a/Modules/sha256module.c +++ b/Modules/sha256module.c @@ -8,6 +8,7 @@ Andrew Kuchling (amk@amk.ca) Greg Stein (gstein@lyra.org) Trevor Perrin (trevp@trevp.net) + Jonathan Protzenko (jonathan@protzenko.fr) Copyright (C) 2005-2007 Gregory P. Smith (greg@krypto.org) Licensed to PSF under a Contributor Agreement. diff --git a/Modules/sha512module.c b/Modules/sha512module.c index bf4408b455f2c4..d7dfed4e5db03a 100644 --- a/Modules/sha512module.c +++ b/Modules/sha512module.c @@ -8,6 +8,7 @@ Andrew Kuchling (amk@amk.ca) Greg Stein (gstein@lyra.org) Trevor Perrin (trevp@trevp.net) + Jonathan Protzenko (jonathan@protzenko.fr) Copyright (C) 2005-2007 Gregory P. Smith (greg@krypto.org) Licensed to PSF under a Contributor Agreement. @@ -31,400 +32,32 @@ class SHA512Type "SHAobject *" "&PyType_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=81a3ccde92bcfe8d]*/ -/* Some useful types */ - -typedef unsigned char SHA_BYTE; -typedef uint32_t SHA_INT32; /* 32-bit integer */ -typedef uint64_t SHA_INT64; /* 64-bit integer */ /* The SHA block size and message digest sizes, in bytes */ #define SHA_BLOCKSIZE 128 #define SHA_DIGESTSIZE 64 -/* The structure for storing SHA info */ +/* The SHA2-384 and SHA2-512 implementations defer to the HACL* verified + * library. */ + +#include "_hacl/Hacl_Streaming_SHA2.h" typedef struct { PyObject_HEAD - SHA_INT64 digest[8]; /* Message digest */ - SHA_INT32 count_lo, count_hi; /* 64-bit bit count */ - SHA_BYTE data[SHA_BLOCKSIZE]; /* SHA data buffer */ - int local; /* unprocessed amount in data */ int digestsize; + Hacl_Streaming_SHA2_state_sha2_512 *state; } SHAobject; #include "clinic/sha512module.c.h" -/* When run on a little-endian CPU we need to perform byte reversal on an - array of longwords. */ - -#if PY_LITTLE_ENDIAN -static void longReverse(SHA_INT64 *buffer, int byteCount) -{ - byteCount /= sizeof(*buffer); - for (; byteCount--; buffer++) { - *buffer = _Py_bswap64(*buffer); - } -} -#endif static void SHAcopy(SHAobject *src, SHAobject *dest) { - dest->local = src->local; dest->digestsize = src->digestsize; - dest->count_lo = src->count_lo; - dest->count_hi = src->count_hi; - memcpy(dest->digest, src->digest, sizeof(src->digest)); - memcpy(dest->data, src->data, sizeof(src->data)); -} - - -/* ------------------------------------------------------------------------ - * - * This code for the SHA-512 algorithm was noted as public domain. The - * original headers are pasted below. - * - * Several changes have been made to make it more compatible with the - * Python environment and desired interface. - * - */ - -/* LibTomCrypt, modular cryptographic library -- Tom St Denis - * - * LibTomCrypt is a library that provides various cryptographic - * algorithms in a highly modular and flexible manner. - * - * The library is free for all purposes without any express - * guarantee it works. - * - * Tom St Denis, tomstdenis@iahu.ca, https://www.libtom.net - */ - - -/* SHA512 by Tom St Denis */ - -/* Various logical functions */ -#define ROR64(x, y) \ - ( ((((x) & 0xFFFFFFFFFFFFFFFFULL)>>((unsigned long long)(y) & 63)) | \ - ((x)<<((unsigned long long)(64-((y) & 63))))) & 0xFFFFFFFFFFFFFFFFULL) -#define Ch(x,y,z) (z ^ (x & (y ^ z))) -#define Maj(x,y,z) (((x | y) & z) | (x & y)) -#define S(x, n) ROR64((x),(n)) -#define R(x, n) (((x) & 0xFFFFFFFFFFFFFFFFULL) >> ((unsigned long long)n)) -#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) -#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) -#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) -#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) - - -static void -sha512_transform(SHAobject *sha_info) -{ - int i; - SHA_INT64 S[8], W[80], t0, t1; - - memcpy(W, sha_info->data, sizeof(sha_info->data)); -#if PY_LITTLE_ENDIAN - longReverse(W, (int)sizeof(sha_info->data)); -#endif - - for (i = 16; i < 80; ++i) { - W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; - } - for (i = 0; i < 8; ++i) { - S[i] = sha_info->digest[i]; - } - - /* Compress */ -#define RND(a,b,c,d,e,f,g,h,i,ki) \ - t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \ - t1 = Sigma0(a) + Maj(a, b, c); \ - d += t0; \ - h = t0 + t1; - - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98d728ae22ULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x7137449123ef65cdULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcfec4d3b2fULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba58189dbbcULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25bf348b538ULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1b605d019ULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4af194f9bULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5da6d8118ULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98a3030242ULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b0145706fbeULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be4ee4b28cULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3d5ffb4e2ULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74f27b896fULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe3b1696b1ULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a725c71235ULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174cf692694ULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c19ef14ad2ULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786384f25e3ULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc68b8cd5b5ULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc77ac9c65ULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f592b0275ULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa6ea6e483ULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dcbd41fbd4ULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da831153b5ULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152ee66dfabULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d2db43210ULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c898fb213fULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7beef0ee4ULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf33da88fc2ULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147930aa725ULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351e003826fULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x142929670a0e6e70ULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a8546d22ffcULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b21385c26c926ULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc5ac42aedULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d139d95b3dfULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a73548baf63deULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb3c77b2a8ULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e47edaee6ULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c851482353bULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a14cf10364ULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664bbc423001ULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70d0f89791ULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a30654be30ULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819d6ef5218ULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd69906245565a910ULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e35855771202aULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa07032bbd1b8ULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116b8d2d0c8ULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c085141ab53ULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774cdf8eeb99ULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5e19b48a8ULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3c5c95a63ULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4ae3418acbULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f7763e373ULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3d6b2b8a3ULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee5defb2fcULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f43172f60ULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814a1f0ab72ULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc702081a6439ecULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa23631e28ULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506cebde82bde9ULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7b2c67915ULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2e372532bULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],64,0xca273eceea26619cULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],65,0xd186b8c721c0c207ULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],66,0xeada7dd6cde0eb1eULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],67,0xf57d4f7fee6ed178ULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],68,0x06f067aa72176fbaULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],69,0x0a637dc5a2c898a6ULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],70,0x113f9804bef90daeULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],71,0x1b710b35131c471bULL); - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],72,0x28db77f523047d84ULL); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],73,0x32caab7b40c72493ULL); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],74,0x3c9ebe0a15c9bebcULL); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],75,0x431d67c49c100d4cULL); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],76,0x4cc5d4becb3e42b6ULL); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],77,0x597f299cfc657e2aULL); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],78,0x5fcb6fab3ad6faecULL); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],79,0x6c44198c4a475817ULL); - -#undef RND - - /* feedback */ - for (i = 0; i < 8; i++) { - sha_info->digest[i] = sha_info->digest[i] + S[i]; - } - -} - - - -/* initialize the SHA digest */ - -static void -sha512_init(SHAobject *sha_info) -{ - sha_info->digest[0] = Py_ULL(0x6a09e667f3bcc908); - sha_info->digest[1] = Py_ULL(0xbb67ae8584caa73b); - sha_info->digest[2] = Py_ULL(0x3c6ef372fe94f82b); - sha_info->digest[3] = Py_ULL(0xa54ff53a5f1d36f1); - sha_info->digest[4] = Py_ULL(0x510e527fade682d1); - sha_info->digest[5] = Py_ULL(0x9b05688c2b3e6c1f); - sha_info->digest[6] = Py_ULL(0x1f83d9abfb41bd6b); - sha_info->digest[7] = Py_ULL(0x5be0cd19137e2179); - sha_info->count_lo = 0L; - sha_info->count_hi = 0L; - sha_info->local = 0; - sha_info->digestsize = 64; -} - -static void -sha384_init(SHAobject *sha_info) -{ - sha_info->digest[0] = Py_ULL(0xcbbb9d5dc1059ed8); - sha_info->digest[1] = Py_ULL(0x629a292a367cd507); - sha_info->digest[2] = Py_ULL(0x9159015a3070dd17); - sha_info->digest[3] = Py_ULL(0x152fecd8f70e5939); - sha_info->digest[4] = Py_ULL(0x67332667ffc00b31); - sha_info->digest[5] = Py_ULL(0x8eb44a8768581511); - sha_info->digest[6] = Py_ULL(0xdb0c2e0d64f98fa7); - sha_info->digest[7] = Py_ULL(0x47b5481dbefa4fa4); - sha_info->count_lo = 0L; - sha_info->count_hi = 0L; - sha_info->local = 0; - sha_info->digestsize = 48; -} - - -/* update the SHA digest */ - -static void -sha512_update(SHAobject *sha_info, SHA_BYTE *buffer, Py_ssize_t count) -{ - Py_ssize_t i; - SHA_INT32 clo; - - clo = sha_info->count_lo + ((SHA_INT32) count << 3); - if (clo < sha_info->count_lo) { - ++sha_info->count_hi; - } - sha_info->count_lo = clo; - sha_info->count_hi += (SHA_INT32) count >> 29; - if (sha_info->local) { - i = SHA_BLOCKSIZE - sha_info->local; - if (i > count) { - i = count; - } - memcpy(((SHA_BYTE *) sha_info->data) + sha_info->local, buffer, i); - count -= i; - buffer += i; - sha_info->local += (int)i; - if (sha_info->local == SHA_BLOCKSIZE) { - sha512_transform(sha_info); - } - else { - return; - } - } - while (count >= SHA_BLOCKSIZE) { - memcpy(sha_info->data, buffer, SHA_BLOCKSIZE); - buffer += SHA_BLOCKSIZE; - count -= SHA_BLOCKSIZE; - sha512_transform(sha_info); - } - memcpy(sha_info->data, buffer, count); - sha_info->local = (int)count; + dest->state = Hacl_Streaming_SHA2_copy_512(src->state); } -/* finish computing the SHA digest */ - -static void -sha512_final(unsigned char digest[SHA_DIGESTSIZE], SHAobject *sha_info) -{ - int count; - SHA_INT32 lo_bit_count, hi_bit_count; - - lo_bit_count = sha_info->count_lo; - hi_bit_count = sha_info->count_hi; - count = (int) ((lo_bit_count >> 3) & 0x7f); - ((SHA_BYTE *) sha_info->data)[count++] = 0x80; - if (count > SHA_BLOCKSIZE - 16) { - memset(((SHA_BYTE *) sha_info->data) + count, 0, - SHA_BLOCKSIZE - count); - sha512_transform(sha_info); - memset((SHA_BYTE *) sha_info->data, 0, SHA_BLOCKSIZE - 16); - } - else { - memset(((SHA_BYTE *) sha_info->data) + count, 0, - SHA_BLOCKSIZE - 16 - count); - } - - /* GJS: note that we add the hi/lo in big-endian. sha512_transform will - swap these values into host-order. */ - sha_info->data[112] = 0; - sha_info->data[113] = 0; - sha_info->data[114] = 0; - sha_info->data[115] = 0; - sha_info->data[116] = 0; - sha_info->data[117] = 0; - sha_info->data[118] = 0; - sha_info->data[119] = 0; - sha_info->data[120] = (hi_bit_count >> 24) & 0xff; - sha_info->data[121] = (hi_bit_count >> 16) & 0xff; - sha_info->data[122] = (hi_bit_count >> 8) & 0xff; - sha_info->data[123] = (hi_bit_count >> 0) & 0xff; - sha_info->data[124] = (lo_bit_count >> 24) & 0xff; - sha_info->data[125] = (lo_bit_count >> 16) & 0xff; - sha_info->data[126] = (lo_bit_count >> 8) & 0xff; - sha_info->data[127] = (lo_bit_count >> 0) & 0xff; - sha512_transform(sha_info); - digest[ 0] = (unsigned char) ((sha_info->digest[0] >> 56) & 0xff); - digest[ 1] = (unsigned char) ((sha_info->digest[0] >> 48) & 0xff); - digest[ 2] = (unsigned char) ((sha_info->digest[0] >> 40) & 0xff); - digest[ 3] = (unsigned char) ((sha_info->digest[0] >> 32) & 0xff); - digest[ 4] = (unsigned char) ((sha_info->digest[0] >> 24) & 0xff); - digest[ 5] = (unsigned char) ((sha_info->digest[0] >> 16) & 0xff); - digest[ 6] = (unsigned char) ((sha_info->digest[0] >> 8) & 0xff); - digest[ 7] = (unsigned char) ((sha_info->digest[0] ) & 0xff); - digest[ 8] = (unsigned char) ((sha_info->digest[1] >> 56) & 0xff); - digest[ 9] = (unsigned char) ((sha_info->digest[1] >> 48) & 0xff); - digest[10] = (unsigned char) ((sha_info->digest[1] >> 40) & 0xff); - digest[11] = (unsigned char) ((sha_info->digest[1] >> 32) & 0xff); - digest[12] = (unsigned char) ((sha_info->digest[1] >> 24) & 0xff); - digest[13] = (unsigned char) ((sha_info->digest[1] >> 16) & 0xff); - digest[14] = (unsigned char) ((sha_info->digest[1] >> 8) & 0xff); - digest[15] = (unsigned char) ((sha_info->digest[1] ) & 0xff); - digest[16] = (unsigned char) ((sha_info->digest[2] >> 56) & 0xff); - digest[17] = (unsigned char) ((sha_info->digest[2] >> 48) & 0xff); - digest[18] = (unsigned char) ((sha_info->digest[2] >> 40) & 0xff); - digest[19] = (unsigned char) ((sha_info->digest[2] >> 32) & 0xff); - digest[20] = (unsigned char) ((sha_info->digest[2] >> 24) & 0xff); - digest[21] = (unsigned char) ((sha_info->digest[2] >> 16) & 0xff); - digest[22] = (unsigned char) ((sha_info->digest[2] >> 8) & 0xff); - digest[23] = (unsigned char) ((sha_info->digest[2] ) & 0xff); - digest[24] = (unsigned char) ((sha_info->digest[3] >> 56) & 0xff); - digest[25] = (unsigned char) ((sha_info->digest[3] >> 48) & 0xff); - digest[26] = (unsigned char) ((sha_info->digest[3] >> 40) & 0xff); - digest[27] = (unsigned char) ((sha_info->digest[3] >> 32) & 0xff); - digest[28] = (unsigned char) ((sha_info->digest[3] >> 24) & 0xff); - digest[29] = (unsigned char) ((sha_info->digest[3] >> 16) & 0xff); - digest[30] = (unsigned char) ((sha_info->digest[3] >> 8) & 0xff); - digest[31] = (unsigned char) ((sha_info->digest[3] ) & 0xff); - digest[32] = (unsigned char) ((sha_info->digest[4] >> 56) & 0xff); - digest[33] = (unsigned char) ((sha_info->digest[4] >> 48) & 0xff); - digest[34] = (unsigned char) ((sha_info->digest[4] >> 40) & 0xff); - digest[35] = (unsigned char) ((sha_info->digest[4] >> 32) & 0xff); - digest[36] = (unsigned char) ((sha_info->digest[4] >> 24) & 0xff); - digest[37] = (unsigned char) ((sha_info->digest[4] >> 16) & 0xff); - digest[38] = (unsigned char) ((sha_info->digest[4] >> 8) & 0xff); - digest[39] = (unsigned char) ((sha_info->digest[4] ) & 0xff); - digest[40] = (unsigned char) ((sha_info->digest[5] >> 56) & 0xff); - digest[41] = (unsigned char) ((sha_info->digest[5] >> 48) & 0xff); - digest[42] = (unsigned char) ((sha_info->digest[5] >> 40) & 0xff); - digest[43] = (unsigned char) ((sha_info->digest[5] >> 32) & 0xff); - digest[44] = (unsigned char) ((sha_info->digest[5] >> 24) & 0xff); - digest[45] = (unsigned char) ((sha_info->digest[5] >> 16) & 0xff); - digest[46] = (unsigned char) ((sha_info->digest[5] >> 8) & 0xff); - digest[47] = (unsigned char) ((sha_info->digest[5] ) & 0xff); - digest[48] = (unsigned char) ((sha_info->digest[6] >> 56) & 0xff); - digest[49] = (unsigned char) ((sha_info->digest[6] >> 48) & 0xff); - digest[50] = (unsigned char) ((sha_info->digest[6] >> 40) & 0xff); - digest[51] = (unsigned char) ((sha_info->digest[6] >> 32) & 0xff); - digest[52] = (unsigned char) ((sha_info->digest[6] >> 24) & 0xff); - digest[53] = (unsigned char) ((sha_info->digest[6] >> 16) & 0xff); - digest[54] = (unsigned char) ((sha_info->digest[6] >> 8) & 0xff); - digest[55] = (unsigned char) ((sha_info->digest[6] ) & 0xff); - digest[56] = (unsigned char) ((sha_info->digest[7] >> 56) & 0xff); - digest[57] = (unsigned char) ((sha_info->digest[7] >> 48) & 0xff); - digest[58] = (unsigned char) ((sha_info->digest[7] >> 40) & 0xff); - digest[59] = (unsigned char) ((sha_info->digest[7] >> 32) & 0xff); - digest[60] = (unsigned char) ((sha_info->digest[7] >> 24) & 0xff); - digest[61] = (unsigned char) ((sha_info->digest[7] >> 16) & 0xff); - digest[62] = (unsigned char) ((sha_info->digest[7] >> 8) & 0xff); - digest[63] = (unsigned char) ((sha_info->digest[7] ) & 0xff); -} - -/* - * End of copied SHA code. - * - * ------------------------------------------------------------------------ - */ - typedef struct { PyTypeObject* sha384_type; PyTypeObject* sha512_type; @@ -463,14 +96,31 @@ SHA_traverse(PyObject *ptr, visitproc visit, void *arg) } static void -SHA512_dealloc(PyObject *ptr) +SHA512_dealloc(SHAobject *ptr) { + Hacl_Streaming_SHA2_free_512(ptr->state); PyTypeObject *tp = Py_TYPE(ptr); PyObject_GC_UnTrack(ptr); PyObject_GC_Del(ptr); Py_DECREF(tp); } +/* HACL* takes a uint32_t for the length of its parameter, but Py_ssize_t can be + * 64 bits. */ +static void update_512(Hacl_Streaming_SHA2_state_sha2_512 *state, uint8_t *buf, Py_ssize_t len) { + /* Note: we explicitly ignore the error code on the basis that it would take > + * 1 billion years to overflow the maximum admissible length for this API + * (namely, 2^64-1 bytes). */ + while (len > UINT32_MAX) { + Hacl_Streaming_SHA2_update_512(state, buf, UINT32_MAX); + len -= UINT32_MAX; + buf += UINT32_MAX; + } + /* Cast to uint32_t is safe: upon exiting the loop, len <= UINT32_MAX, and + * therefore fits in a uint32_t */ + Hacl_Streaming_SHA2_update_512(state, buf, (uint32_t) len); +} + /* External methods for a hash object */ @@ -514,11 +164,10 @@ static PyObject * SHA512Type_digest_impl(SHAobject *self) /*[clinic end generated code: output=1080bbeeef7dde1b input=f6470dd359071f4b]*/ { - unsigned char digest[SHA_DIGESTSIZE]; - SHAobject temp; - - SHAcopy(self, &temp); - sha512_final(digest, &temp); + uint8_t digest[SHA_DIGESTSIZE]; + // HACL performs copies under the hood so that self->state remains valid + // after this call. + Hacl_Streaming_SHA2_finish_512(self->state, digest); return PyBytes_FromStringAndSize((const char *)digest, self->digestsize); } @@ -532,13 +181,8 @@ static PyObject * SHA512Type_hexdigest_impl(SHAobject *self) /*[clinic end generated code: output=7373305b8601e18b input=498b877b25cbe0a2]*/ { - unsigned char digest[SHA_DIGESTSIZE]; - SHAobject temp; - - /* Get the raw (binary) digest value */ - SHAcopy(self, &temp); - sha512_final(digest, &temp); - + uint8_t digest[SHA_DIGESTSIZE]; + Hacl_Streaming_SHA2_finish_512(self->state, digest); return _Py_strhex((const char *)digest, self->digestsize); } @@ -559,7 +203,7 @@ SHA512Type_update(SHAobject *self, PyObject *obj) GET_BUFFER_VIEW_OR_ERROUT(obj, &buf); - sha512_update(self, buf.buf, buf.len); + update_512(self->state, buf.buf, buf.len); PyBuffer_Release(&buf); Py_RETURN_NONE; @@ -622,15 +266,6 @@ static PyType_Spec sha512_sha384_type_spec = { .slots = sha512_sha384_type_slots }; -static PyType_Slot sha512_sha512_type_slots[] = { - {Py_tp_dealloc, SHA512_dealloc}, - {Py_tp_methods, SHA_methods}, - {Py_tp_members, SHA_members}, - {Py_tp_getset, SHA_getseters}, - {Py_tp_traverse, SHA_traverse}, - {0,0} -}; - // Using PyType_GetModuleState() on this type is safe since // it cannot be subclassed: it does not have the Py_TPFLAGS_BASETYPE flag. static PyType_Spec sha512_sha512_type_spec = { @@ -638,7 +273,7 @@ static PyType_Spec sha512_sha512_type_spec = { .basicsize = sizeof(SHAobject), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), - .slots = sha512_sha512_type_slots + .slots = sha512_sha384_type_slots }; /* The single module-level function: new() */ @@ -671,7 +306,8 @@ _sha512_sha512_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } - sha512_init(new); + new->state = Hacl_Streaming_SHA2_create_in_512(); + new->digestsize = 64; if (PyErr_Occurred()) { Py_DECREF(new); @@ -680,7 +316,7 @@ _sha512_sha512_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } if (string) { - sha512_update(new, buf.buf, buf.len); + update_512(new->state, buf.buf, buf.len); PyBuffer_Release(&buf); } @@ -715,7 +351,8 @@ _sha512_sha384_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } - sha384_init(new); + new->state = Hacl_Streaming_SHA2_create_in_384(); + new->digestsize = 48; if (PyErr_Occurred()) { Py_DECREF(new); @@ -724,7 +361,7 @@ _sha512_sha384_impl(PyObject *module, PyObject *string, int usedforsecurity) return NULL; } if (string) { - sha512_update(new, buf.buf, buf.len); + update_512(new->state, buf.buf, buf.len); PyBuffer_Release(&buf); } diff --git a/configure b/configure index 35088f9e5cafd6..c00a1e1d2ec986 100755 --- a/configure +++ b/configure @@ -26950,7 +26950,7 @@ fi as_fn_append MODULE_BLOCK "MODULE__SHA512_STATE=$py_cv_module__sha512$as_nl" if test "x$py_cv_module__sha512" = xyes; then : - + as_fn_append MODULE_BLOCK "MODULE__SHA512_CFLAGS=-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE$as_nl" fi diff --git a/configure.ac b/configure.ac index 1ab48e0d1c160a..92a05c011026f2 100644 --- a/configure.ac +++ b/configure.ac @@ -7200,7 +7200,9 @@ PY_STDLIB_MOD([_sha1], [test "$with_builtin_sha1" = yes]) PY_STDLIB_MOD([_sha256], [test "$with_builtin_sha256" = yes], [], [-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE]) -PY_STDLIB_MOD([_sha512], [test "$with_builtin_sha512" = yes]) +PY_STDLIB_MOD([_sha512], + [test "$with_builtin_sha512" = yes], [], + [-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE]) PY_STDLIB_MOD([_sha3], [test "$with_builtin_sha3" = yes]) PY_STDLIB_MOD([_blake2], [test "$with_builtin_blake2" = yes], [], From 3690688149dca11589af59b7704541336613199a Mon Sep 17 00:00:00 2001 From: Furkan Onder Date: Tue, 14 Feb 2023 10:20:11 +0000 Subject: [PATCH 170/225] GH-101898: Fix missing term references for hashable definition (#101899) Fix missing term references for hashable definition --- Doc/c-api/dict.rst | 2 +- Doc/c-api/object.rst | 2 +- Doc/faq/programming.rst | 2 +- Doc/library/abc.rst | 2 +- Doc/library/collections.abc.rst | 4 ++-- Doc/library/collections.rst | 4 ++-- Doc/library/datetime.rst | 2 +- Doc/library/fractions.rst | 2 +- Doc/library/functools.rst | 2 +- Doc/library/graphlib.rst | 4 ++-- Doc/library/inspect.rst | 4 ++-- Doc/library/operator.rst | 2 +- Doc/library/pathlib.rst | 2 +- Doc/library/stdtypes.rst | 6 +++--- Doc/library/typing.rst | 2 +- Doc/reference/datamodel.rst | 2 +- 16 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index e5f28b59a701e0..34106ee6b1f30d 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -80,7 +80,7 @@ Dictionary Objects .. c:function:: int PyDict_DelItem(PyObject *p, PyObject *key) - Remove the entry in dictionary *p* with key *key*. *key* must be hashable; + Remove the entry in dictionary *p* with key *key*. *key* must be :term:`hashable`; if it isn't, :exc:`TypeError` is raised. If *key* is not in the dictionary, :exc:`KeyError` is raised. Return ``0`` on success or ``-1`` on failure. diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 5a25a2b6c9d3db..84c72e7e108b64 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -281,7 +281,7 @@ Object Protocol .. c:function:: Py_hash_t PyObject_HashNotImplemented(PyObject *o) - Set a :exc:`TypeError` indicating that ``type(o)`` is not hashable and return ``-1``. + Set a :exc:`TypeError` indicating that ``type(o)`` is not :term:`hashable` and return ``-1``. This function receives special treatment when stored in a ``tp_hash`` slot, allowing a type to explicitly indicate to the interpreter that it is not hashable. diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index ba42289f3466c2..38f9b171618b26 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -1979,7 +1979,7 @@ method result will be released right away. The disadvantage is that if instances accumulate, so too will the accumulated method results. They can grow without bound. -The *lru_cache* approach works with methods that have hashable +The *lru_cache* approach works with methods that have :term:`hashable` arguments. It creates a reference to the instance unless special efforts are made to pass in weak references. diff --git a/Doc/library/abc.rst b/Doc/library/abc.rst index 3b74622e7ff46c..274b2d69f0ab5c 100644 --- a/Doc/library/abc.rst +++ b/Doc/library/abc.rst @@ -21,7 +21,7 @@ The :mod:`collections` module has some concrete classes that derive from ABCs; these can, of course, be further derived. In addition, the :mod:`collections.abc` submodule has some ABCs that can be used to test whether a class or instance provides a particular interface, for example, if it is -hashable or if it is a mapping. +:term:`hashable` or if it is a mapping. This module provides the metaclass :class:`ABCMeta` for defining ABCs and diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index 132b0ce7192ac1..1ada0d352a0cc6 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -22,7 +22,7 @@ This module provides :term:`abstract base classes ` that can be used to test whether a class provides a particular interface; for -example, whether it is hashable or whether it is a mapping. +example, whether it is :term:`hashable` or whether it is a mapping. An :func:`issubclass` or :func:`isinstance` test for an interface works in one of three ways. @@ -406,7 +406,7 @@ Notes on using :class:`Set` and :class:`MutableSet` as a mixin: (3) The :class:`Set` mixin provides a :meth:`_hash` method to compute a hash value for the set; however, :meth:`__hash__` is not defined because not all sets - are hashable or immutable. To add set hashability using mixins, + are :term:`hashable` or immutable. To add set hashability using mixins, inherit from both :meth:`Set` and :meth:`Hashable`, then define ``__hash__ = Set._hash``. diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst index 2cffc2300a2298..bb46782c06e1c8 100644 --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -25,7 +25,7 @@ Python's general purpose built-in containers, :class:`dict`, :class:`list`, :func:`namedtuple` factory function for creating tuple subclasses with named fields :class:`deque` list-like container with fast appends and pops on either end :class:`ChainMap` dict-like class for creating a single view of multiple mappings -:class:`Counter` dict subclass for counting hashable objects +:class:`Counter` dict subclass for counting :term:`hashable` objects :class:`OrderedDict` dict subclass that remembers the order entries were added :class:`defaultdict` dict subclass that calls a factory function to supply missing values :class:`UserDict` wrapper around dictionary objects for easier dict subclassing @@ -242,7 +242,7 @@ For example:: .. class:: Counter([iterable-or-mapping]) - A :class:`Counter` is a :class:`dict` subclass for counting hashable objects. + A :class:`Counter` is a :class:`dict` subclass for counting :term:`hashable` objects. It is a collection where elements are stored as dictionary keys and their counts are stored as dictionary values. Counts are allowed to be any integer value including zero or negative counts. The :class:`Counter` diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 2f1ab7c3dd4b51..50827b27ebea04 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -160,7 +160,7 @@ The :class:`date`, :class:`.datetime`, :class:`.time`, and :class:`timezone` typ share these common features: - Objects of these types are immutable. -- Objects of these types are hashable, meaning that they can be used as +- Objects of these types are :term:`hashable`, meaning that they can be used as dictionary keys. - Objects of these types support efficient pickling via the :mod:`pickle` module. diff --git a/Doc/library/fractions.rst b/Doc/library/fractions.rst index 06b0e038c89bd0..c61bbac892e0e4 100644 --- a/Doc/library/fractions.rst +++ b/Doc/library/fractions.rst @@ -77,7 +77,7 @@ another rational number, or from a string. The :class:`Fraction` class inherits from the abstract base class :class:`numbers.Rational`, and implements all of the methods and - operations from that class. :class:`Fraction` instances are hashable, + operations from that class. :class:`Fraction` instances are :term:`hashable`, and should be treated as immutable. In addition, :class:`Fraction` has the following properties and methods: diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index 2f0a9bd8be8815..80a405e87d8d56 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -147,7 +147,7 @@ The :mod:`functools` module defines the following functions: threads. Since a dictionary is used to cache results, the positional and keyword - arguments to the function must be hashable. + arguments to the function must be :term:`hashable`. Distinct argument patterns may be considered to be distinct calls with separate cache entries. For example, ``f(a=1, b=2)`` and ``f(b=2, a=1)`` diff --git a/Doc/library/graphlib.rst b/Doc/library/graphlib.rst index 2bc80da4ead2a2..fe7932e7a61cb5 100644 --- a/Doc/library/graphlib.rst +++ b/Doc/library/graphlib.rst @@ -17,7 +17,7 @@ .. class:: TopologicalSorter(graph=None) - Provides functionality to topologically sort a graph of hashable nodes. + Provides functionality to topologically sort a graph of :term:`hashable` nodes. A topological order is a linear ordering of the vertices in a graph such that for every directed edge u -> v from vertex u to vertex v, vertex u comes @@ -85,7 +85,7 @@ .. method:: add(node, *predecessors) Add a new node and its predecessors to the graph. Both the *node* and all - elements in *predecessors* must be hashable. + elements in *predecessors* must be :term:`hashable`. If called multiple times with the same node argument, the set of dependencies will be the union of all dependencies passed in. diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 58b84a35a890e3..9c3be5a250a67e 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -689,7 +689,7 @@ function. modified copy. .. versionchanged:: 3.5 - Signature objects are picklable and hashable. + Signature objects are picklable and :term:`hashable`. .. attribute:: Signature.empty @@ -768,7 +768,7 @@ function. you can use :meth:`Parameter.replace` to create a modified copy. .. versionchanged:: 3.5 - Parameter objects are picklable and hashable. + Parameter objects are picklable and :term:`hashable`. .. attribute:: Parameter.empty diff --git a/Doc/library/operator.rst b/Doc/library/operator.rst index 35e9b49ea8bcca..6d90473f2367b5 100644 --- a/Doc/library/operator.rst +++ b/Doc/library/operator.rst @@ -327,7 +327,7 @@ expect a function argument. return g The items can be any type accepted by the operand's :meth:`__getitem__` - method. Dictionaries accept any hashable value. Lists, tuples, and + method. Dictionaries accept any :term:`hashable` value. Lists, tuples, and strings accept an index or a slice: >>> itemgetter(1)('ABCDEFG') diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index f222745a2c56bc..c8a734ecad8e7b 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -186,7 +186,7 @@ these classes, since they don't provide any operation that does system calls. General properties ^^^^^^^^^^^^^^^^^^ -Paths are immutable and hashable. Paths of a same flavour are comparable +Paths are immutable and :term:`hashable`. Paths of a same flavour are comparable and orderable. These properties respect the flavour's case-folding semantics:: diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 0ef03035a572e5..98bda6ded30f79 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -3775,7 +3775,7 @@ copying. >>> data bytearray(b'z1spam') - One-dimensional memoryviews of hashable (read-only) types with formats + One-dimensional memoryviews of :term:`hashable` (read-only) types with formats 'B', 'b' or 'c' are also hashable. The hash is defined as ``hash(m) == hash(m.tobytes())``:: @@ -3789,7 +3789,7 @@ copying. .. versionchanged:: 3.3 One-dimensional memoryviews can now be sliced. - One-dimensional memoryviews with formats 'B', 'b' or 'c' are now hashable. + One-dimensional memoryviews with formats 'B', 'b' or 'c' are now :term:`hashable`. .. versionchanged:: 3.4 memoryview is now registered automatically with @@ -4710,7 +4710,7 @@ support membership tests: .. versionadded:: 3.10 -Keys views are set-like since their entries are unique and hashable. If all +Keys views are set-like since their entries are unique and :term:`hashable`. If all values are hashable, so that ``(key, value)`` pairs are unique and hashable, then the items view is also set-like. (Values views are not treated as set-like since the entries are generally not unique.) For set-like views, all of the diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 356f919a1897b2..169f7196a74ec6 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -439,7 +439,7 @@ are intended primarily for static type checking. A user-defined generic class can have ABCs as base classes without a metaclass conflict. Generic metaclasses are not supported. The outcome of parameterizing -generics is cached, and most types in the typing module are hashable and +generics is cached, and most types in the typing module are :term:`hashable` and comparable for equality. diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 1d2ddf3507aee1..e4e471e50079ae 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1525,7 +1525,7 @@ Basic customization :meth:`__hash__`, its instances will not be usable as items in hashable collections. If a class defines mutable objects and implements an :meth:`__eq__` method, it should not implement :meth:`__hash__`, since the - implementation of hashable collections requires that a key's hash value is + implementation of :term:`hashable` collections requires that a key's hash value is immutable (if the object's hash value changes, it will be in the wrong hash bucket). From 81e3aa835c32363f4547b6566edf1125386f1f6d Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 14 Feb 2023 11:54:13 +0000 Subject: [PATCH 171/225] gh-101799: implement PREP_RERAISE_STAR as an intrinsic function (#101800) --- Doc/library/dis.rst | 26 +++++++++------- Include/internal/pycore_intrinsics.h | 13 ++++++++ Include/internal/pycore_opcode.h | 18 +++++------ Include/opcode.h | 20 ++++++------- Lib/importlib/_bootstrap_external.py | 3 +- Lib/opcode.py | 2 +- ...-02-13-18-21-14.gh-issue-101799.wpHbCn.rst | 2 ++ Python/bytecodes.c | 20 ++++++------- Python/compile.c | 4 +-- Python/generated_cases.c.h | 30 +++++++++---------- Python/intrinsics.c | 18 +++++++++++ Python/opcode_metadata.h | 10 +++---- Python/opcode_targets.h | 14 ++++----- 13 files changed, 107 insertions(+), 73 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-13-18-21-14.gh-issue-101799.wpHbCn.rst diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index b1e61d7e77b2f5..a5bc5e7fb6ea71 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -768,16 +768,6 @@ iterations of the loop. .. versionadded:: 3.11 -.. opcode:: PREP_RERAISE_STAR - - Combines the raised and reraised exceptions list from ``STACK[-1]``, into an - exception group to propagate from a try-except* block. Uses the original exception - group from ``STACK[-2]`` to reconstruct the structure of reraised exceptions. Pops - two items from the stack and pushes the exception to reraise or ``None`` - if there isn't one. - - .. versionadded:: 3.11 - .. opcode:: WITH_EXCEPT_START Calls the function in position 4 on the stack with arguments (type, val, tb) @@ -1515,7 +1505,8 @@ iterations of the loop. .. opcode:: CALL_INTRINSIC_1 Calls an intrinsic function with one argument. Passes ``STACK[-1]`` as the - argument and sets ``STACK[-1]`` to the result. Used to implement functionality that is necessary but not performance critical. + argument and sets ``STACK[-1]`` to the result. Used to implement + functionality that is necessary but not performance critical. The operand determines which intrinsic function is called: @@ -1529,6 +1520,19 @@ iterations of the loop. .. versionadded:: 3.12 +.. opcode:: CALL_INTRINSIC_2 + + Calls an intrinsic function with two arguments. Passes ``STACK[-2]``, ``STACK[-1]`` as the + arguments and sets ``STACK[-1]`` to the result. Used to implement functionality that is + necessary but not performance critical. + + The operand determines which intrinsic function is called: + + * ``0`` Not valid + * ``1`` Calculates the :exc:`ExceptionGroup` to raise from a ``try-except*``. + + .. versionadded:: 3.12 + **Pseudo-instructions** diff --git a/Include/internal/pycore_intrinsics.h b/Include/internal/pycore_intrinsics.h index 1da618f2b4a548..deac145fff7627 100644 --- a/Include/internal/pycore_intrinsics.h +++ b/Include/internal/pycore_intrinsics.h @@ -1,4 +1,6 @@ +/* Unary Functions: */ + #define INTRINSIC_PRINT 1 #define INTRINSIC_IMPORT_STAR 2 #define INTRINSIC_STOPITERATION_ERROR 3 @@ -8,6 +10,17 @@ #define MAX_INTRINSIC_1 6 + +/* Binary Functions: */ + +#define INTRINSIC_PREP_RERAISE_STAR 1 + +#define MAX_INTRINSIC_2 1 + + typedef PyObject *(*instrinsic_func1)(PyThreadState* tstate, PyObject *value); +typedef PyObject *(*instrinsic_func2)(PyThreadState* tstate, PyObject *value1, PyObject *value2); extern instrinsic_func1 _PyIntrinsics_UnaryFunctions[]; +extern instrinsic_func2 _PyIntrinsics_BinaryFunctions[]; + diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index 5e65adee9e00a5..f9ab95ca4bb9d3 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -87,6 +87,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [CALL_BUILTIN_FAST_WITH_KEYWORDS] = CALL, [CALL_FUNCTION_EX] = CALL_FUNCTION_EX, [CALL_INTRINSIC_1] = CALL_INTRINSIC_1, + [CALL_INTRINSIC_2] = CALL_INTRINSIC_2, [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = CALL, [CALL_NO_KW_BUILTIN_FAST] = CALL, [CALL_NO_KW_BUILTIN_O] = CALL, @@ -187,7 +188,6 @@ const uint8_t _PyOpcode_Deopt[256] = { [POP_JUMP_IF_NOT_NONE] = POP_JUMP_IF_NOT_NONE, [POP_JUMP_IF_TRUE] = POP_JUMP_IF_TRUE, [POP_TOP] = POP_TOP, - [PREP_RERAISE_STAR] = PREP_RERAISE_STAR, [PUSH_EXC_INFO] = PUSH_EXC_INFO, [PUSH_NULL] = PUSH_NULL, [RAISE_VARARGS] = RAISE_VARARGS, @@ -319,7 +319,7 @@ static const char *const _PyOpcode_OpName[263] = { [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS", [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE", [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT", - [PREP_RERAISE_STAR] = "PREP_RERAISE_STAR", + [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT", [POP_EXCEPT] = "POP_EXCEPT", [STORE_NAME] = "STORE_NAME", [DELETE_NAME] = "DELETE_NAME", @@ -344,7 +344,7 @@ static const char *const _PyOpcode_OpName[263] = { [JUMP_FORWARD] = "JUMP_FORWARD", [JUMP_IF_FALSE_OR_POP] = "JUMP_IF_FALSE_OR_POP", [JUMP_IF_TRUE_OR_POP] = "JUMP_IF_TRUE_OR_POP", - [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT", + [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST", [POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE", [POP_JUMP_IF_TRUE] = "POP_JUMP_IF_TRUE", [LOAD_GLOBAL] = "LOAD_GLOBAL", @@ -374,7 +374,7 @@ static const char *const _PyOpcode_OpName[263] = { [JUMP_BACKWARD] = "JUMP_BACKWARD", [COMPARE_AND_BRANCH] = "COMPARE_AND_BRANCH", [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX", - [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST", + [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", [EXTENDED_ARG] = "EXTENDED_ARG", [LIST_APPEND] = "LIST_APPEND", [SET_ADD] = "SET_ADD", @@ -384,20 +384,20 @@ static const char *const _PyOpcode_OpName[263] = { [YIELD_VALUE] = "YIELD_VALUE", [RESUME] = "RESUME", [MATCH_CLASS] = "MATCH_CLASS", - [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT", + [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT", [FORMAT_VALUE] = "FORMAT_VALUE", [BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP", [BUILD_STRING] = "BUILD_STRING", - [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT", [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST", [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE", [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE", + [SEND_GEN] = "SEND_GEN", [LIST_EXTEND] = "LIST_EXTEND", [SET_UPDATE] = "SET_UPDATE", [DICT_MERGE] = "DICT_MERGE", [DICT_UPDATE] = "DICT_UPDATE", - [SEND_GEN] = "SEND_GEN", + [166] = "<166>", [167] = "<167>", [168] = "<168>", [169] = "<169>", @@ -405,7 +405,7 @@ static const char *const _PyOpcode_OpName[263] = { [CALL] = "CALL", [KW_NAMES] = "KW_NAMES", [CALL_INTRINSIC_1] = "CALL_INTRINSIC_1", - [174] = "<174>", + [CALL_INTRINSIC_2] = "CALL_INTRINSIC_2", [175] = "<175>", [176] = "<176>", [177] = "<177>", @@ -498,11 +498,11 @@ static const char *const _PyOpcode_OpName[263] = { #endif #define EXTRA_CASES \ + case 166: \ case 167: \ case 168: \ case 169: \ case 170: \ - case 174: \ case 175: \ case 176: \ case 177: \ diff --git a/Include/opcode.h b/Include/opcode.h index d643741c3c3aa0..760ff945f31f9e 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -43,7 +43,6 @@ extern "C" { #define RETURN_GENERATOR 75 #define RETURN_VALUE 83 #define SETUP_ANNOTATIONS 85 -#define PREP_RERAISE_STAR 88 #define POP_EXCEPT 89 #define HAVE_ARGUMENT 90 #define STORE_NAME 90 @@ -117,6 +116,7 @@ extern "C" { #define CALL 171 #define KW_NAMES 172 #define CALL_INTRINSIC_1 173 +#define CALL_INTRINSIC_2 174 #define MIN_PSEUDO_OPCODE 256 #define SETUP_FINALLY 256 #define SETUP_CLEANUP 257 @@ -179,15 +179,15 @@ extern "C" { #define LOAD_GLOBAL_MODULE 84 #define STORE_ATTR_INSTANCE_VALUE 86 #define STORE_ATTR_SLOT 87 -#define STORE_ATTR_WITH_HINT 113 -#define STORE_FAST__LOAD_FAST 143 -#define STORE_FAST__STORE_FAST 153 -#define STORE_SUBSCR_DICT 154 -#define STORE_SUBSCR_LIST_INT 158 -#define UNPACK_SEQUENCE_LIST 159 -#define UNPACK_SEQUENCE_TUPLE 160 -#define UNPACK_SEQUENCE_TWO_TUPLE 161 -#define SEND_GEN 166 +#define STORE_ATTR_WITH_HINT 88 +#define STORE_FAST__LOAD_FAST 113 +#define STORE_FAST__STORE_FAST 143 +#define STORE_SUBSCR_DICT 153 +#define STORE_SUBSCR_LIST_INT 154 +#define UNPACK_SEQUENCE_LIST 158 +#define UNPACK_SEQUENCE_TUPLE 159 +#define UNPACK_SEQUENCE_TWO_TUPLE 160 +#define SEND_GEN 161 #define DO_TRACING 255 #define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\ diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 38d4a384c2cc95..954401cfa85ed3 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -433,6 +433,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.12a5 3517 (Change YIELD_VALUE oparg to exception block depth) # Python 3.12a5 3518 (Add RETURN_CONST instruction) # Python 3.12a5 3519 (Modify SEND instruction) +# Python 3.12a5 3520 (Remove PREP_RERAISE_STAR, add CALL_INTRINSIC_2) # Python 3.13 will start with 3550 @@ -445,7 +446,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3519).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3520).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c diff --git a/Lib/opcode.py b/Lib/opcode.py index b69cd1bbdd61ca..809d24e51676bd 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -127,7 +127,6 @@ def pseudo_op(name, op, real_ops): def_op('SETUP_ANNOTATIONS', 85) -def_op('PREP_RERAISE_STAR', 88) def_op('POP_EXCEPT', 89) HAVE_ARGUMENT = 90 # real opcodes from here have an argument: @@ -224,6 +223,7 @@ def pseudo_op(name, op, real_ops): def_op('KW_NAMES', 172) hasconst.append(172) def_op('CALL_INTRINSIC_1', 173) +def_op('CALL_INTRINSIC_2', 174) hasarg.extend([op for op in opmap.values() if op >= HAVE_ARGUMENT]) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-13-18-21-14.gh-issue-101799.wpHbCn.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-13-18-21-14.gh-issue-101799.wpHbCn.rst new file mode 100644 index 00000000000000..3233a573be7acd --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-13-18-21-14.gh-issue-101799.wpHbCn.rst @@ -0,0 +1,2 @@ +Add :opcode:`CALL_INTRINSIC_2` and use it instead of +:opcode:`PREP_RERAISE_STAR`. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 429cd7fdafa168..be54e5f6f589eb 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -501,7 +501,14 @@ dummy_func( inst(CALL_INTRINSIC_1, (value -- res)) { assert(oparg <= MAX_INTRINSIC_1); res = _PyIntrinsics_UnaryFunctions[oparg](tstate, value); - Py_DECREF(value); + DECREF_INPUTS(); + ERROR_IF(res == NULL, error); + } + + inst(CALL_INTRINSIC_2, (value2, value1 -- res)) { + assert(oparg <= MAX_INTRINSIC_2); + res = _PyIntrinsics_BinaryFunctions[oparg](tstate, value2, value1); + DECREF_INPUTS(); ERROR_IF(res == NULL, error); } @@ -788,15 +795,6 @@ dummy_func( goto exception_unwind; } - inst(PREP_RERAISE_STAR, (orig, excs -- val)) { - assert(PyList_Check(excs)); - - val = _PyExc_PrepReraiseStar(orig, excs); - DECREF_INPUTS(); - - ERROR_IF(val == NULL, error); - } - inst(END_ASYNC_FOR, (awaitable, exc -- )) { assert(exc && PyExceptionInstance_Check(exc)); if (PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration)) { @@ -2383,7 +2381,7 @@ dummy_func( } // Cache layout: counter/1, func_version/2, min_args/1 - // Neither CALL_INTRINSIC_1 nor CALL_FUNCTION_EX are members! + // Neither CALL_INTRINSIC_1/2 nor CALL_FUNCTION_EX are members! family(call, INLINE_CACHE_ENTRIES_CALL) = { CALL, CALL_BOUND_METHOD_EXACT_ARGS, diff --git a/Python/compile.c b/Python/compile.c index b49eda314eeef1..0534b536e3d12e 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -3431,7 +3431,7 @@ compiler_try_except(struct compiler *c, stmt_ty s) [orig, res, rest] Ln+1: LIST_APPEND 1 ) add unhandled exc to res (could be None) - [orig, res] PREP_RERAISE_STAR + [orig, res] CALL_INTRINSIC_2 PREP_RERAISE_STAR [exc] COPY 1 [exc, exc] POP_JUMP_IF_NOT_NONE RER [exc] POP_TOP @@ -3580,7 +3580,7 @@ compiler_try_star_except(struct compiler *c, stmt_ty s) NEW_JUMP_TARGET_LABEL(c, reraise); USE_LABEL(c, reraise_star); - ADDOP(c, NO_LOCATION, PREP_RERAISE_STAR); + ADDOP_I(c, NO_LOCATION, CALL_INTRINSIC_2, INTRINSIC_PREP_RERAISE_STAR); ADDOP_I(c, NO_LOCATION, COPY, 1); ADDOP_JUMP(c, NO_LOCATION, POP_JUMP_IF_NOT_NONE, reraise); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 093ebff026b509..beb797cbd233d7 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -689,6 +689,20 @@ DISPATCH(); } + TARGET(CALL_INTRINSIC_2) { + PyObject *value1 = PEEK(1); + PyObject *value2 = PEEK(2); + PyObject *res; + assert(oparg <= MAX_INTRINSIC_2); + res = _PyIntrinsics_BinaryFunctions[oparg](tstate, value2, value1); + Py_DECREF(value2); + Py_DECREF(value1); + if (res == NULL) goto pop_2_error; + STACK_SHRINK(1); + POKE(1, res); + DISPATCH(); + } + TARGET(RAISE_VARARGS) { PyObject **args = &PEEK(oparg); PyObject *cause = NULL, *exc = NULL; @@ -999,22 +1013,6 @@ goto exception_unwind; } - TARGET(PREP_RERAISE_STAR) { - PyObject *excs = PEEK(1); - PyObject *orig = PEEK(2); - PyObject *val; - assert(PyList_Check(excs)); - - val = _PyExc_PrepReraiseStar(orig, excs); - Py_DECREF(orig); - Py_DECREF(excs); - - if (val == NULL) goto pop_2_error; - STACK_SHRINK(1); - POKE(1, val); - DISPATCH(); - } - TARGET(END_ASYNC_FOR) { PyObject *exc = PEEK(1); PyObject *awaitable = PEEK(2); diff --git a/Python/intrinsics.c b/Python/intrinsics.c index ae1775862d945d..9e90ef32130f1d 100644 --- a/Python/intrinsics.c +++ b/Python/intrinsics.c @@ -9,6 +9,7 @@ #include "pycore_pyerrors.h" +/******** Unary functions ********/ static PyObject * no_intrinsic(PyThreadState* tstate, PyObject *unused) @@ -208,3 +209,20 @@ _PyIntrinsics_UnaryFunctions[] = { [INTRINSIC_UNARY_POSITIVE] = unary_pos, [INTRINSIC_LIST_TO_TUPLE] = list_to_tuple, }; + + +/******** Binary functions ********/ + + +static PyObject * +prep_reraise_star(PyThreadState* unused, PyObject *orig, PyObject *excs) +{ + assert(PyList_Check(excs)); + return _PyExc_PrepReraiseStar(orig, excs); +} + +instrinsic_func2 +_PyIntrinsics_BinaryFunctions[] = { + [INTRINSIC_PREP_RERAISE_STAR] = prep_reraise_star, +}; + diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index d622eb12c8cb2d..f27906a3e349eb 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -88,6 +88,8 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 2; case CALL_INTRINSIC_1: return 1; + case CALL_INTRINSIC_2: + return 2; case RAISE_VARARGS: return oparg; case INTERPRETER_EXIT: @@ -112,8 +114,6 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 1; case RERAISE: return oparg + 1; - case PREP_RERAISE_STAR: - return 2; case END_ASYNC_FOR: return 2; case CLEANUP_THROW: @@ -440,6 +440,8 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return 0; case CALL_INTRINSIC_1: return 1; + case CALL_INTRINSIC_2: + return 1; case RAISE_VARARGS: return 0; case INTERPRETER_EXIT: @@ -464,8 +466,6 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return 0; case RERAISE: return oparg; - case PREP_RERAISE_STAR: - return 1; case END_ASYNC_FOR: return 0; case CLEANUP_THROW: @@ -760,6 +760,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = { [STORE_SUBSCR_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, [DELETE_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [CALL_INTRINSIC_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_INTRINSIC_2] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [RAISE_VARARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [INTERPRETER_EXIT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [RETURN_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, @@ -772,7 +773,6 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = { [YIELD_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [POP_EXCEPT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [RERAISE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [PREP_RERAISE_STAR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [END_ASYNC_FOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [CLEANUP_THROW] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [LOAD_ASSERTION_ERROR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 301ec6e005dad6..bc64bd582fd572 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -87,7 +87,7 @@ static void *opcode_targets[256] = { &&TARGET_SETUP_ANNOTATIONS, &&TARGET_STORE_ATTR_INSTANCE_VALUE, &&TARGET_STORE_ATTR_SLOT, - &&TARGET_PREP_RERAISE_STAR, + &&TARGET_STORE_ATTR_WITH_HINT, &&TARGET_POP_EXCEPT, &&TARGET_STORE_NAME, &&TARGET_DELETE_NAME, @@ -112,7 +112,7 @@ static void *opcode_targets[256] = { &&TARGET_JUMP_FORWARD, &&TARGET_JUMP_IF_FALSE_OR_POP, &&TARGET_JUMP_IF_TRUE_OR_POP, - &&TARGET_STORE_ATTR_WITH_HINT, + &&TARGET_STORE_FAST__LOAD_FAST, &&TARGET_POP_JUMP_IF_FALSE, &&TARGET_POP_JUMP_IF_TRUE, &&TARGET_LOAD_GLOBAL, @@ -142,7 +142,7 @@ static void *opcode_targets[256] = { &&TARGET_JUMP_BACKWARD, &&TARGET_COMPARE_AND_BRANCH, &&TARGET_CALL_FUNCTION_EX, - &&TARGET_STORE_FAST__LOAD_FAST, + &&TARGET_STORE_FAST__STORE_FAST, &&TARGET_EXTENDED_ARG, &&TARGET_LIST_APPEND, &&TARGET_SET_ADD, @@ -152,20 +152,20 @@ static void *opcode_targets[256] = { &&TARGET_YIELD_VALUE, &&TARGET_RESUME, &&TARGET_MATCH_CLASS, - &&TARGET_STORE_FAST__STORE_FAST, &&TARGET_STORE_SUBSCR_DICT, + &&TARGET_STORE_SUBSCR_LIST_INT, &&TARGET_FORMAT_VALUE, &&TARGET_BUILD_CONST_KEY_MAP, &&TARGET_BUILD_STRING, - &&TARGET_STORE_SUBSCR_LIST_INT, &&TARGET_UNPACK_SEQUENCE_LIST, &&TARGET_UNPACK_SEQUENCE_TUPLE, &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, + &&TARGET_SEND_GEN, &&TARGET_LIST_EXTEND, &&TARGET_SET_UPDATE, &&TARGET_DICT_MERGE, &&TARGET_DICT_UPDATE, - &&TARGET_SEND_GEN, + &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, @@ -173,7 +173,7 @@ static void *opcode_targets[256] = { &&TARGET_CALL, &&TARGET_KW_NAMES, &&TARGET_CALL_INTRINSIC_1, - &&_unknown_opcode, + &&TARGET_CALL_INTRINSIC_2, &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, From 096d0097a09e439a4564531b297a998e5d74c9b5 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 14 Feb 2023 14:26:03 -0700 Subject: [PATCH 172/225] gh-101758: Add a Test For Single-Phase Init Module Variants (gh-101891) The new test exercises the most important variants for single-phase init extension modules. We also add some explanation about those variants to import.c. https://github.com/python/cpython/issues/101758 --- Lib/test/test_imp.py | 199 +++++++++++++++++++ Modules/_testsinglephase.c | 394 ++++++++++++++++++++++++++++++++++--- Python/import.c | 105 +++++++++- 3 files changed, 660 insertions(+), 38 deletions(-) diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py index 80abc720c3251a..31dce21587e2ca 100644 --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -251,6 +251,205 @@ def test_issue16421_multiple_modules_in_one_dll(self): with self.assertRaises(ImportError): imp.load_dynamic('nonexistent', pathname) + @requires_load_dynamic + def test_singlephase_variants(self): + '''Exercise the most meaningful variants described in Python/import.c.''' + self.maxDiff = None + + basename = '_testsinglephase' + fileobj, pathname, _ = imp.find_module(basename) + fileobj.close() + + modules = {} + def load(name): + assert name not in modules + module = imp.load_dynamic(name, pathname) + self.assertNotIn(module, modules.values()) + modules[name] = module + return module + + def re_load(name, module): + assert sys.modules[name] is module + before = type(module)(module.__name__) + before.__dict__.update(vars(module)) + + reloaded = imp.load_dynamic(name, pathname) + + return before, reloaded + + def check_common(name, module): + summed = module.sum(1, 2) + lookedup = module.look_up_self() + initialized = module.initialized() + cached = sys.modules[name] + + # module.__name__ might not match, but the spec will. + self.assertEqual(module.__spec__.name, name) + if initialized is not None: + self.assertIsInstance(initialized, float) + self.assertGreater(initialized, 0) + self.assertEqual(summed, 3) + self.assertTrue(issubclass(module.error, Exception)) + self.assertEqual(module.int_const, 1969) + self.assertEqual(module.str_const, 'something different') + self.assertIs(cached, module) + + return lookedup, initialized, cached + + def check_direct(name, module, lookedup): + # The module has its own PyModuleDef, with a matching name. + self.assertEqual(module.__name__, name) + self.assertIs(lookedup, module) + + def check_indirect(name, module, lookedup, orig): + # The module re-uses another's PyModuleDef, with a different name. + assert orig is not module + assert orig.__name__ != name + self.assertNotEqual(module.__name__, name) + self.assertIs(lookedup, module) + + def check_basic(module, initialized): + init_count = module.initialized_count() + + self.assertIsNot(initialized, None) + self.assertIsInstance(init_count, int) + self.assertGreater(init_count, 0) + + return init_count + + def check_common_reloaded(name, module, cached, before, reloaded): + recached = sys.modules[name] + + self.assertEqual(reloaded.__spec__.name, name) + self.assertEqual(reloaded.__name__, before.__name__) + self.assertEqual(before.__dict__, module.__dict__) + self.assertIs(recached, reloaded) + + def check_basic_reloaded(module, lookedup, initialized, init_count, + before, reloaded): + relookedup = reloaded.look_up_self() + reinitialized = reloaded.initialized() + reinit_count = reloaded.initialized_count() + + self.assertIs(reloaded, module) + self.assertIs(reloaded.__dict__, module.__dict__) + # It only happens to be the same but that's good enough here. + # We really just want to verify that the re-loaded attrs + # didn't change. + self.assertIs(relookedup, lookedup) + self.assertEqual(reinitialized, initialized) + self.assertEqual(reinit_count, init_count) + + def check_with_reinit_reloaded(module, lookedup, initialized, + before, reloaded): + relookedup = reloaded.look_up_self() + reinitialized = reloaded.initialized() + + self.assertIsNot(reloaded, module) + self.assertIsNot(reloaded, module) + self.assertNotEqual(reloaded.__dict__, module.__dict__) + self.assertIs(relookedup, reloaded) + if initialized is None: + self.assertIs(reinitialized, None) + else: + self.assertGreater(reinitialized, initialized) + + # Check the "basic" module. + + name = basename + expected_init_count = 1 + with self.subTest(name): + mod = load(name) + lookedup, initialized, cached = check_common(name, mod) + check_direct(name, mod, lookedup) + init_count = check_basic(mod, initialized) + self.assertEqual(init_count, expected_init_count) + + before, reloaded = re_load(name, mod) + check_common_reloaded(name, mod, cached, before, reloaded) + check_basic_reloaded(mod, lookedup, initialized, init_count, + before, reloaded) + basic = mod + + # Check its indirect variants. + + name = f'{basename}_basic_wrapper' + expected_init_count += 1 + with self.subTest(name): + mod = load(name) + lookedup, initialized, cached = check_common(name, mod) + check_indirect(name, mod, lookedup, basic) + init_count = check_basic(mod, initialized) + self.assertEqual(init_count, expected_init_count) + + before, reloaded = re_load(name, mod) + check_common_reloaded(name, mod, cached, before, reloaded) + check_basic_reloaded(mod, lookedup, initialized, init_count, + before, reloaded) + + # Currently _PyState_AddModule() always replaces the cached module. + self.assertIs(basic.look_up_self(), mod) + self.assertEqual(basic.initialized_count(), expected_init_count) + + # The cached module shouldn't be changed after this point. + basic_lookedup = mod + + # Check its direct variant. + + name = f'{basename}_basic_copy' + expected_init_count += 1 + with self.subTest(name): + mod = load(name) + lookedup, initialized, cached = check_common(name, mod) + check_direct(name, mod, lookedup) + init_count = check_basic(mod, initialized) + self.assertEqual(init_count, expected_init_count) + + before, reloaded = re_load(name, mod) + check_common_reloaded(name, mod, cached, before, reloaded) + check_basic_reloaded(mod, lookedup, initialized, init_count, + before, reloaded) + + # This should change the cached module for _testsinglephase. + self.assertIs(basic.look_up_self(), basic_lookedup) + self.assertEqual(basic.initialized_count(), expected_init_count) + + # Check the non-basic variant that has no state. + + name = f'{basename}_with_reinit' + with self.subTest(name): + mod = load(name) + lookedup, initialized, cached = check_common(name, mod) + self.assertIs(initialized, None) + check_direct(name, mod, lookedup) + + before, reloaded = re_load(name, mod) + check_common_reloaded(name, mod, cached, before, reloaded) + check_with_reinit_reloaded(mod, lookedup, initialized, + before, reloaded) + + # This should change the cached module for _testsinglephase. + self.assertIs(basic.look_up_self(), basic_lookedup) + self.assertEqual(basic.initialized_count(), expected_init_count) + + # Check the basic variant that has state. + + name = f'{basename}_with_state' + with self.subTest(name): + mod = load(name) + lookedup, initialized, cached = check_common(name, mod) + self.assertIsNot(initialized, None) + check_direct(name, mod, lookedup) + + before, reloaded = re_load(name, mod) + check_common_reloaded(name, mod, cached, before, reloaded) + check_with_reinit_reloaded(mod, lookedup, initialized, + before, reloaded) + + # This should change the cached module for _testsinglephase. + self.assertIs(basic.look_up_self(), basic_lookedup) + self.assertEqual(basic.initialized_count(), expected_init_count) + @requires_load_dynamic def test_load_dynamic_ImportError_path(self): # Issue #1559549 added `name` and `path` attributes to ImportError diff --git a/Modules/_testsinglephase.c b/Modules/_testsinglephase.c index 3bfe159e54fe49..9e8dd647ee761a 100644 --- a/Modules/_testsinglephase.c +++ b/Modules/_testsinglephase.c @@ -5,74 +5,412 @@ # define Py_BUILD_CORE_MODULE 1 #endif +//#include #include "Python.h" #include "pycore_namespace.h" // _PyNamespace_New() +typedef struct { + _PyTime_t initialized; + PyObject *error; + PyObject *int_const; + PyObject *str_const; +} module_state; + +/* Process-global state is only used by _testsinglephase + since it's the only one that does not support re-init. */ +static struct { + int initialized_count; + module_state module; +} global_state = { .initialized_count = -1 }; + +static inline module_state * +get_module_state(PyObject *module) +{ + PyModuleDef *def = PyModule_GetDef(module); + if (def->m_size == -1) { + return &global_state.module; + } + else if (def->m_size == 0) { + return NULL; + } + else { + module_state *state = (module_state*)PyModule_GetState(module); + assert(state != NULL); + return state; + } +} + +static void +clear_state(module_state *state) +{ + state->initialized = 0; + Py_CLEAR(state->error); + Py_CLEAR(state->int_const); + Py_CLEAR(state->str_const); +} + +static int +_set_initialized(_PyTime_t *initialized) +{ + /* We go strictly monotonic to ensure each time is unique. */ + _PyTime_t prev; + if (_PyTime_GetMonotonicClockWithInfo(&prev, NULL) != 0) { + return -1; + } + /* We do a busy sleep since the interval should be super short. */ + _PyTime_t t; + do { + if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) != 0) { + return -1; + } + } while (t == prev); + + *initialized = t; + return 0; +} + +static int +init_state(module_state *state) +{ + assert(state->initialized == 0 && + state->error == NULL && + state->int_const == NULL && + state->str_const == NULL); + + if (_set_initialized(&state->initialized) != 0) { + goto error; + } + assert(state->initialized > 0); + + /* Add an exception type */ + state->error = PyErr_NewException("_testsinglephase.error", NULL, NULL); + if (state->error == NULL) { + goto error; + } + + state->int_const = PyLong_FromLong(1969); + if (state->int_const == NULL) { + goto error; + } + + state->str_const = PyUnicode_FromString("something different"); + if (state->str_const == NULL) { + goto error; + } + + return 0; + +error: + clear_state(state); + return -1; +} + +static int +init_module(PyObject *module, module_state *state) +{ + if (PyModule_AddObjectRef(module, "error", state->error) != 0) { + return -1; + } + if (PyModule_AddObjectRef(module, "int_const", state->int_const) != 0) { + return -1; + } + if (PyModule_AddObjectRef(module, "str_const", state->str_const) != 0) { + return -1; + } + return 0; +} + + +PyDoc_STRVAR(common_initialized_doc, +"initialized()\n\ +\n\ +Return the seconds-since-epoch when the module was initialized."); + +static PyObject * +common_initialized(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + module_state *state = get_module_state(self); + if (state == NULL) { + Py_RETURN_NONE; + } + double d = _PyTime_AsSecondsDouble(state->initialized); + return PyFloat_FromDouble(d); +} + +#define INITIALIZED_METHODDEF \ + {"initialized", common_initialized, METH_NOARGS, \ + common_initialized_doc} + + +PyDoc_STRVAR(common_look_up_self_doc, +"look_up_self()\n\ +\n\ +Return the module associated with this module's def.m_base.m_index."); + +static PyObject * +common_look_up_self(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyModuleDef *def = PyModule_GetDef(self); + if (def == NULL) { + return NULL; + } + return Py_NewRef( + PyState_FindModule(def)); +} + +#define LOOK_UP_SELF_METHODDEF \ + {"look_up_self", common_look_up_self, METH_NOARGS, common_look_up_self_doc} + + /* Function of two integers returning integer */ -PyDoc_STRVAR(testexport_foo_doc, -"foo(i,j)\n\ +PyDoc_STRVAR(common_sum_doc, +"sum(i,j)\n\ \n\ Return the sum of i and j."); static PyObject * -testexport_foo(PyObject *self, PyObject *args) +common_sum(PyObject *self, PyObject *args) { long i, j; long res; - if (!PyArg_ParseTuple(args, "ll:foo", &i, &j)) + if (!PyArg_ParseTuple(args, "ll:sum", &i, &j)) return NULL; res = i + j; return PyLong_FromLong(res); } +#define SUM_METHODDEF \ + {"sum", common_sum, METH_VARARGS, common_sum_doc} -static PyMethodDef TestMethods[] = { - {"foo", testexport_foo, METH_VARARGS, - testexport_foo_doc}, - {NULL, NULL} /* sentinel */ -}; +PyDoc_STRVAR(basic_initialized_count_doc, +"initialized_count()\n\ +\n\ +Return how many times the module has been initialized."); + +static PyObject * +basic_initialized_count(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + assert(PyModule_GetDef(self)->m_size == -1); + return PyLong_FromLong(global_state.initialized_count); +} + +#define INITIALIZED_COUNT_METHODDEF \ + {"initialized_count", basic_initialized_count, METH_VARARGS, \ + basic_initialized_count_doc} + + +/*********************************************/ +/* the _testsinglephase module (and aliases) */ +/*********************************************/ + +/* This ia more typical of legacy extensions in the wild: + - single-phase init + - no module state + - does not support repeated initialization + (so m_copy is used) + - the module def is cached in _PyRuntime.extensions + (by name/filename) + + Also note that, because the module has single-phase init, + it is cached in interp->module_by_index (using mod->md_def->m_base.m_index). + */ -static struct PyModuleDef _testsinglephase = { +static PyMethodDef TestMethods_Basic[] = { + LOOK_UP_SELF_METHODDEF, + SUM_METHODDEF, + INITIALIZED_METHODDEF, + INITIALIZED_COUNT_METHODDEF, + {NULL, NULL} /* sentinel */ +}; + +static struct PyModuleDef _testsinglephase_basic = { PyModuleDef_HEAD_INIT, .m_name = "_testsinglephase", - .m_doc = PyDoc_STR("Test module _testsinglephase (main)"), + .m_doc = PyDoc_STR("Test module _testsinglephase"), .m_size = -1, // no module state - .m_methods = TestMethods, + .m_methods = TestMethods_Basic, }; +static PyObject * +init__testsinglephase_basic(PyModuleDef *def) +{ + if (global_state.initialized_count == -1) { + global_state.initialized_count = 0; + } + + PyObject *module = PyModule_Create(def); + if (module == NULL) { + return NULL; + } + + module_state *state = &global_state.module; + // It may have been set by a previous run or under a different name. + clear_state(state); + if (init_state(state) < 0) { + Py_CLEAR(module); + return NULL; + } + + if (init_module(module, state) < 0) { + Py_CLEAR(module); + goto finally; + } + + global_state.initialized_count++; + +finally: + return module; +} PyMODINIT_FUNC PyInit__testsinglephase(void) { - PyObject *module = PyModule_Create(&_testsinglephase); + return init__testsinglephase_basic(&_testsinglephase_basic); +} + + +PyMODINIT_FUNC +PyInit__testsinglephase_basic_wrapper(void) +{ + return PyInit__testsinglephase(); +} + + +PyMODINIT_FUNC +PyInit__testsinglephase_basic_copy(void) +{ + static struct PyModuleDef def = { + PyModuleDef_HEAD_INIT, + .m_name = "_testsinglephase_basic_copy", + .m_doc = PyDoc_STR("Test module _testsinglephase_basic_copy"), + .m_size = -1, // no module state + .m_methods = TestMethods_Basic, + }; + return init__testsinglephase_basic(&def); +} + + +/*******************************************/ +/* the _testsinglephase_with_reinit module */ +/*******************************************/ + +/* This ia less typical of legacy extensions in the wild: + - single-phase init (same as _testsinglephase above) + - no module state + - supports repeated initialization + (so m_copy is not used) + - the module def is not cached in _PyRuntime.extensions + + At this point most modules would reach for multi-phase init (PEP 489). + However, module state has been around a while (PEP 3121), + and most extensions predate multi-phase init. + + (This module is basically the same as _testsinglephase, + but supports repeated initialization.) + */ + +static PyMethodDef TestMethods_Reinit[] = { + LOOK_UP_SELF_METHODDEF, + SUM_METHODDEF, + INITIALIZED_METHODDEF, + {NULL, NULL} /* sentinel */ +}; + +static struct PyModuleDef _testsinglephase_with_reinit = { + PyModuleDef_HEAD_INIT, + .m_name = "_testsinglephase_with_reinit", + .m_doc = PyDoc_STR("Test module _testsinglephase_with_reinit"), + .m_size = 0, + .m_methods = TestMethods_Reinit, +}; + +PyMODINIT_FUNC +PyInit__testsinglephase_with_reinit(void) +{ + /* We purposefully do not try PyState_FindModule() first here + since we want to check the behavior of re-loading the module. */ + PyObject *module = PyModule_Create(&_testsinglephase_with_reinit); if (module == NULL) { return NULL; } - /* Add an exception type */ - PyObject *temp = PyErr_NewException("_testsinglephase.error", NULL, NULL); - if (temp == NULL) { - goto error; + assert(get_module_state(module) == NULL); + + module_state state = {0}; + if (init_state(&state) < 0) { + Py_CLEAR(module); + return NULL; } - if (PyModule_AddObject(module, "error", temp) != 0) { - Py_DECREF(temp); - goto error; + + if (init_module(module, &state) < 0) { + Py_CLEAR(module); + goto finally; } - if (PyModule_AddIntConstant(module, "int_const", 1969) != 0) { - goto error; +finally: + /* We only needed the module state for setting the module attrs. */ + clear_state(&state); + return module; +} + + +/******************************************/ +/* the _testsinglephase_with_state module */ +/******************************************/ + +/* This ia less typical of legacy extensions in the wild: + - single-phase init (same as _testsinglephase above) + - has some module state + - supports repeated initialization + (so m_copy is not used) + - the module def is not cached in _PyRuntime.extensions + + At this point most modules would reach for multi-phase init (PEP 489). + However, module state has been around a while (PEP 3121), + and most extensions predate multi-phase init. + */ + +static PyMethodDef TestMethods_WithState[] = { + LOOK_UP_SELF_METHODDEF, + SUM_METHODDEF, + INITIALIZED_METHODDEF, + {NULL, NULL} /* sentinel */ +}; + +static struct PyModuleDef _testsinglephase_with_state = { + PyModuleDef_HEAD_INIT, + .m_name = "_testsinglephase_with_state", + .m_doc = PyDoc_STR("Test module _testsinglephase_with_state"), + .m_size = sizeof(module_state), + .m_methods = TestMethods_WithState, +}; + +PyMODINIT_FUNC +PyInit__testsinglephase_with_state(void) +{ + /* We purposefully do not try PyState_FindModule() first here + since we want to check the behavior of re-loading the module. */ + PyObject *module = PyModule_Create(&_testsinglephase_with_state); + if (module == NULL) { + return NULL; } - if (PyModule_AddStringConstant(module, "str_const", "something different") != 0) { - goto error; + module_state *state = get_module_state(module); + assert(state != NULL); + if (init_state(state) < 0) { + Py_CLEAR(module); + return NULL; } - return module; + if (init_module(module, state) < 0) { + clear_state(state); + Py_CLEAR(module); + goto finally; + } -error: - Py_DECREF(module); - return NULL; +finally: + return module; } diff --git a/Python/import.c b/Python/import.c index 302255d76edcd5..63ed2443657b29 100644 --- a/Python/import.c +++ b/Python/import.c @@ -428,6 +428,71 @@ PyImport_GetMagicTag(void) } +/* +We support a number of kinds of single-phase init builtin/extension modules: + +* "basic" + * no module state (PyModuleDef.m_size == -1) + * does not support repeated init (we use PyModuleDef.m_base.m_copy) + * may have process-global state + * the module's def is cached in _PyRuntime.imports.extensions, + by (name, filename) +* "reinit" + * no module state (PyModuleDef.m_size == 0) + * supports repeated init (m_copy is never used) + * should not have any process-global state + * its def is never cached in _PyRuntime.imports.extensions + (except, currently, under the main interpreter, for some reason) +* "with state" (almost the same as reinit) + * has module state (PyModuleDef.m_size > 0) + * supports repeated init (m_copy is never used) + * should not have any process-global state + * its def is never cached in _PyRuntime.imports.extensions + (except, currently, under the main interpreter, for some reason) + +There are also variants within those classes: + +* two or more modules share a PyModuleDef + * a module's init func uses another module's PyModuleDef + * a module's init func calls another's module's init func + * a module's init "func" is actually a variable statically initialized + to another module's init func +* two or modules share "methods" + * a module's init func copies another module's PyModuleDef + (with a different name) +* (basic-only) two or modules share process-global state + +In the first case, where modules share a PyModuleDef, the following +notable weirdness happens: + +* the module's __name__ matches the def, not the requested name +* the last module (with the same def) to be imported for the first time wins + * returned by PyState_Find_Module() (via interp->modules_by_index) + * (non-basic-only) its init func is used when re-loading any of them + (via the def's m_init) + * (basic-only) the copy of its __dict__ is used when re-loading any of them + (via the def's m_copy) + +However, the following happens as expected: + +* a new module object (with its own __dict__) is created for each request +* the module's __spec__ has the requested name +* the loaded module is cached in sys.modules under the requested name +* the m_index field of the shared def is not changed, + so at least PyState_FindModule() will always look in the same place + +For "basic" modules there are other quirks: + +* (whether sharing a def or not) when loaded the first time, + m_copy is set before _init_module_attrs() is called + in importlib._bootstrap.module_from_spec(), + so when the module is re-loaded, the previous value + for __wpec__ (and others) is reset, possibly unexpectedly. + +Generally, when multiple interpreters are involved, some of the above +gets even messier. +*/ + /* Magic for extension modules (built-in as well as dynamically loaded). To prevent initializing an extension module more than once, we keep a static dictionary 'extensions' keyed by the tuple @@ -489,9 +554,8 @@ _extensions_cache_clear(void) Py_CLEAR(_PyRuntime.imports.extensions); } -int -_PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, - PyObject *filename, PyObject *modules) +static int +fix_up_extension(PyObject *mod, PyObject *name, PyObject *filename) { if (mod == NULL || !PyModule_Check(mod)) { PyErr_BadInternalCall(); @@ -505,16 +569,13 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, } PyThreadState *tstate = _PyThreadState_GET(); - if (PyObject_SetItem(modules, name, mod) < 0) { - return -1; - } if (_PyState_AddModule(tstate, mod, def) < 0) { - PyMapping_DelItem(modules, name); return -1; } // bpo-44050: Extensions and def->m_base.m_copy can be updated // when the extension module doesn't support sub-interpreters. + // XXX Why special-case the main interpreter? if (_Py_IsMainInterpreter(tstate->interp) || def->m_size == -1) { if (def->m_size == -1) { if (def->m_base.m_copy) { @@ -541,15 +602,39 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, return 0; } +int +_PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, + PyObject *filename, PyObject *modules) +{ + if (PyObject_SetItem(modules, name, mod) < 0) { + return -1; + } + if (fix_up_extension(mod, name, filename) < 0) { + PyMapping_DelItem(modules, name); + return -1; + } + return 0; +} + int _PyImport_FixupBuiltin(PyObject *mod, const char *name, PyObject *modules) { - int res; + int res = -1; PyObject *nameobj; nameobj = PyUnicode_InternFromString(name); - if (nameobj == NULL) + if (nameobj == NULL) { return -1; - res = _PyImport_FixupExtensionObject(mod, nameobj, nameobj, modules); + } + if (PyObject_SetItem(modules, nameobj, mod) < 0) { + goto finally; + } + if (fix_up_extension(mod, nameobj, nameobj) < 0) { + PyMapping_DelItem(modules, nameobj); + goto finally; + } + res = 0; + +finally: Py_DECREF(nameobj); return res; } From d777790bab878b8d1a218a1a60894b2823485cca Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Tue, 14 Feb 2023 15:57:01 -0800 Subject: [PATCH 173/225] gh-99108: Build the hashlib HACL* code as a static library. (#101917) This builds HACL* as a library in one place. A followup to #101707 which broke some WASM builds. This fixes 2/4 of them, but the enscripten toolchain in the others don't deduplicate linker arguments and error out. A follow-on PR will address those. --- Makefile.pre.in | 33 +++++++++++++++++-- Modules/Setup.stdlib.in | 4 +-- .../{include => }/python_hacl_namespaces.h | 1 + 3 files changed, 34 insertions(+), 4 deletions(-) rename Modules/_hacl/{include => }/python_hacl_namespaces.h (97%) diff --git a/Makefile.pre.in b/Makefile.pre.in index d42d4d8a3c1c9f..ce3fed3d648536 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -207,6 +207,7 @@ ENSUREPIP= @ENSUREPIP@ # Internal static libraries LIBMPDEC_A= Modules/_decimal/libmpdec/libmpdec.a LIBEXPAT_A= Modules/expat/libexpat.a +LIBHACL_A= Modules/_hacl/libHacl_Streaming_SHA2.a # Module state, compiler flags and linker flags # Empty CFLAGS and LDFLAGS are omitted. @@ -571,6 +572,23 @@ LIBEXPAT_HEADERS= \ Modules/expat/xmltok.h \ Modules/expat/xmltok_impl.h +########################################################################## +# hashlib's HACL* library + +LIBHACL_OBJS= \ + Modules/_hacl/Hacl_Streaming_SHA2.o + +LIBHACL_HEADERS= \ + Modules/_hacl/Hacl_Streaming_SHA2.h \ + Modules/_hacl/include/krml/FStar_UInt128_Verified.h \ + Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h \ + Modules/_hacl/include/krml/fstar_uint128_struct_endianness.h \ + Modules/_hacl/include/krml/internal/target.h \ + Modules/_hacl/include/krml/lowstar_endianness.h \ + Modules/_hacl/include/krml/types.h \ + Modules/_hacl/internal/Hacl_SHA2_Generic.h \ + Modules/_hacl/python_hacl_namespaces.h + ######################################################################### # Rules @@ -890,6 +908,17 @@ $(LIBEXPAT_A): $(LIBEXPAT_OBJS) -rm -f $@ $(AR) $(ARFLAGS) $@ $(LIBEXPAT_OBJS) +########################################################################## +# Build HACL* static libraries for hashlib: libHacl_Streaming_SHA2.a +LIBHACL_CFLAGS=-I$(srcdir)/Modules/_hacl/include -D_BSD_SOURCE -D_DEFAULT_SOURCE $(PY_STDMODULE_CFLAGS) $(CCSHARED) + +Modules/_hacl/Hacl_Streaming_SHA2.o: $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.c $(LIBHACL_HEADERS) + $(CC) -c $(LIBHACL_CFLAGS) -o $@ $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.c + +$(LIBHACL_A): $(LIBHACL_OBJS) + -rm -f $@ + $(AR) $(ARFLAGS) $@ $(LIBHACL_OBJS) + # create relative links from build/lib.platform/egg.so to Modules/egg.so # pybuilddir.txt is created too late. We cannot use it in Makefile # targets. ln --relative is not portable. @@ -2606,9 +2635,9 @@ MODULE__HASHLIB_DEPS=$(srcdir)/Modules/hashlib.h MODULE__IO_DEPS=$(srcdir)/Modules/_io/_iomodule.h MODULE__MD5_DEPS=$(srcdir)/Modules/hashlib.h MODULE__SHA1_DEPS=$(srcdir)/Modules/hashlib.h -MODULE__SHA256_DEPS=$(srcdir)/Modules/hashlib.h $(srcdir)/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h $(srcdir)/Modules/_hacl/include/krml/lowstar_endianness.h $(srcdir)/Modules/_hacl/include/krml/internal/target.h $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.h +MODULE__SHA256_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_HEADERS) $(LIBHACL_A) MODULE__SHA3_DEPS=$(srcdir)/Modules/_sha3/sha3.c $(srcdir)/Modules/_sha3/sha3.h $(srcdir)/Modules/hashlib.h -MODULE__SHA512_DEPS=$(srcdir)/Modules/hashlib.h $(srcdir)/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h $(srcdir)/Modules/_hacl/include/krml/lowstar_endianness.h $(srcdir)/Modules/_hacl/include/krml/internal/target.h $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.h +MODULE__SHA512_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_HEADERS) $(LIBHACL_A) MODULE__SOCKET_DEPS=$(srcdir)/Modules/socketmodule.h $(srcdir)/Modules/addrinfo.h $(srcdir)/Modules/getaddrinfo.c $(srcdir)/Modules/getnameinfo.c MODULE__SSL_DEPS=$(srcdir)/Modules/_ssl.h $(srcdir)/Modules/_ssl/cert.c $(srcdir)/Modules/_ssl/debughelpers.c $(srcdir)/Modules/_ssl/misc.c $(srcdir)/Modules/_ssl_data.h $(srcdir)/Modules/_ssl_data_111.h $(srcdir)/Modules/_ssl_data_300.h $(srcdir)/Modules/socketmodule.h MODULE__TESTCAPI_DEPS=$(srcdir)/Modules/_testcapi/testcapi_long.h $(srcdir)/Modules/_testcapi/parts.h diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index b6d13e04d3fa87..22bcb423db233f 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -79,8 +79,8 @@ # hashing builtins, can be disabled with --without-builtin-hashlib-hashes @MODULE__MD5_TRUE@_md5 md5module.c @MODULE__SHA1_TRUE@_sha1 sha1module.c -@MODULE__SHA256_TRUE@_sha256 sha256module.c _hacl/Hacl_Streaming_SHA2.c -@MODULE__SHA512_TRUE@_sha512 sha512module.c _hacl/Hacl_Streaming_SHA2.c +@MODULE__SHA256_TRUE@_sha256 sha256module.c -I$(srcdir)/Modules/_hacl/include Modules/_hacl/libHacl_Streaming_SHA2.a +@MODULE__SHA512_TRUE@_sha512 sha512module.c -I$(srcdir)/Modules/_hacl/include Modules/_hacl/libHacl_Streaming_SHA2.a @MODULE__SHA3_TRUE@_sha3 _sha3/sha3module.c @MODULE__BLAKE2_TRUE@_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c diff --git a/Modules/_hacl/include/python_hacl_namespaces.h b/Modules/_hacl/python_hacl_namespaces.h similarity index 97% rename from Modules/_hacl/include/python_hacl_namespaces.h rename to Modules/_hacl/python_hacl_namespaces.h index 65608d1fd283c4..ac12f386257b19 100644 --- a/Modules/_hacl/include/python_hacl_namespaces.h +++ b/Modules/_hacl/python_hacl_namespaces.h @@ -25,6 +25,7 @@ #define Hacl_Streaming_SHA2_init_224 python_hashlib_Hacl_Streaming_SHA2_init_224 #define Hacl_Streaming_SHA2_init_512 python_hashlib_Hacl_Streaming_SHA2_init_512 #define Hacl_Streaming_SHA2_init_384 python_hashlib_Hacl_Streaming_SHA2_init_384 +#define Hacl_SHA2_Scalar32_sha512_init python_hashlib_Hacl_SHA2_Scalar32_sha512_init #define Hacl_Streaming_SHA2_update_256 python_hashlib_Hacl_Streaming_SHA2_update_256 #define Hacl_Streaming_SHA2_update_224 python_hashlib_Hacl_Streaming_SHA2_update_224 #define Hacl_Streaming_SHA2_update_512 python_hashlib_Hacl_Streaming_SHA2_update_512 From 8a2b7ee64d1bde762438b458ea7fe88f054a3a88 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 15 Feb 2023 06:27:16 +0100 Subject: [PATCH 174/225] gh-101693: In sqlite3, deprecate using named placeholders with parameters supplied as a sequence (#101698) --- Doc/library/sqlite3.rst | 19 ++++++++++++++++++- Doc/whatsnew/3.12.rst | 7 +++++++ Lib/test/test_sqlite3/test_dbapi.py | 15 +++++++++++++++ ...-02-08-18-20-58.gh-issue-101693.4_LPXj.rst | 6 ++++++ Modules/_sqlite/cursor.c | 13 +++++++++++++ 5 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-08-18-20-58.gh-issue-101693.4_LPXj.rst diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 8ffc0aad91995c..18d0a5e630f6a9 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1442,6 +1442,14 @@ Cursor objects and there is no open transaction, a transaction is implicitly opened before executing *sql*. + .. deprecated-removed:: 3.12 3.14 + + :exc:`DeprecationWarning` is emitted if + :ref:`named placeholders ` are used + and *parameters* is a sequence instead of a :class:`dict`. + Starting with Python 3.14, :exc:`ProgrammingError` will + be raised instead. + Use :meth:`executescript` to execute multiple SQL statements. .. method:: executemany(sql, parameters, /) @@ -1476,6 +1484,15 @@ Cursor objects # cur is an sqlite3.Cursor object cur.executemany("INSERT INTO data VALUES(?)", rows) + .. deprecated-removed:: 3.12 3.14 + + :exc:`DeprecationWarning` is emitted if + :ref:`named placeholders ` are used + and the items in *parameters* are sequences + instead of :class:`dict`\s. + Starting with Python 3.14, :exc:`ProgrammingError` will + be raised instead. + .. method:: executescript(sql_script, /) Execute the SQL statements in *sql_script*. @@ -1971,7 +1988,7 @@ question marks (qmark style) or named placeholders (named style). For the qmark style, *parameters* must be a :term:`sequence` whose length must match the number of placeholders, or a :exc:`ProgrammingError` is raised. -For the named style, *parameters* should be +For the named style, *parameters* must be an instance of a :class:`dict` (or a subclass), which must contain keys for all named parameters; any extra items are ignored. diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 45a5e5062d55b6..c62f462a19a2df 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -415,6 +415,13 @@ Deprecated and tailor them to your needs. (Contributed by Erlend E. Aasland in :gh:`90016`.) +* In :meth:`~sqlite3.Cursor.execute`, :exc:`DeprecationWarning` is now emitted + when :ref:`named placeholders ` are used together with + parameters supplied as a :term:`sequence` instead of as a :class:`dict`. + Starting from Python 3.14, using named placeholders with parameters supplied + as a sequence will raise a :exc:`~sqlite3.ProgrammingError`. + (Contributed by Erlend E. Aasland in :gh:`101698`.) + * The 3-arg signatures (type, value, traceback) of :meth:`~coroutine.throw`, :meth:`~generator.throw` and :meth:`~agen.athrow` are deprecated and may be removed in a future version of Python. Use the single-arg versions diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index 363a308f3e5fec..695e213cdc7b75 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -861,6 +861,21 @@ def __getitem__(slf, x): with self.assertRaises(ZeroDivisionError): self.cu.execute("select name from test where name=?", L()) + def test_execute_named_param_and_sequence(self): + dataset = ( + ("select :a", (1,)), + ("select :a, ?, ?", (1, 2, 3)), + ("select ?, :b, ?", (1, 2, 3)), + ("select ?, ?, :c", (1, 2, 3)), + ("select :a, :b, ?", (1, 2, 3)), + ) + msg = "Binding.*is a named parameter" + for query, params in dataset: + with self.subTest(query=query, params=params): + with self.assertWarnsRegex(DeprecationWarning, msg) as cm: + self.cu.execute(query, params) + self.assertEqual(cm.filename, __file__) + def test_execute_too_many_params(self): category = sqlite.SQLITE_LIMIT_VARIABLE_NUMBER msg = "too many SQL variables" diff --git a/Misc/NEWS.d/next/Library/2023-02-08-18-20-58.gh-issue-101693.4_LPXj.rst b/Misc/NEWS.d/next/Library/2023-02-08-18-20-58.gh-issue-101693.4_LPXj.rst new file mode 100644 index 00000000000000..e436054b15b657 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-08-18-20-58.gh-issue-101693.4_LPXj.rst @@ -0,0 +1,6 @@ +In :meth:`sqlite3.Cursor.execute`, :exc:`DeprecationWarning` is now emitted +when :ref:`named placeholders ` are used together with +parameters supplied as a :term:`sequence` instead of as a :class:`dict`. +Starting from Python 3.14, using named placeholders with parameters supplied +as a sequence will raise a :exc:`~sqlite3.ProgrammingError`. +Patch by Erlend E. Aasland. diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index a4e22bb4a2b58d..6f7970cf8197a2 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -662,6 +662,19 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self, return; } for (i = 0; i < num_params; i++) { + const char *name = sqlite3_bind_parameter_name(self->st, i+1); + if (name != NULL) { + int ret = PyErr_WarnFormat(PyExc_DeprecationWarning, 1, + "Binding %d ('%s') is a named parameter, but you " + "supplied a sequence which requires nameless (qmark) " + "placeholders. Starting with Python 3.14 an " + "sqlite3.ProgrammingError will be raised.", + i+1, name); + if (ret < 0) { + return; + } + } + if (PyTuple_CheckExact(parameters)) { PyObject *item = PyTuple_GET_ITEM(parameters, i); current_param = Py_NewRef(item); From e8b6aaad2faf11fe315410138a5c5943d610d8d8 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 15 Feb 2023 11:18:27 +0100 Subject: [PATCH 175/225] gh-101819: Remove _testcapi dependencies on specific _io symbols (#101918) --- Modules/_io/_iomodule.c | 12 ++---------- Modules/_testcapimodule.c | 8 ++++++-- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index 175fa97479d27d..811b1d221a0122 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -720,16 +720,8 @@ PyInit__io(void) // Add types for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) { PyTypeObject *type = static_types[i]; - // Private type not exposed in the _io module - if (type == &_PyBytesIOBuffer_Type) { - if (PyType_Ready(type) < 0) { - goto fail; - } - } - else { - if (PyModule_AddType(m, type) < 0) { - goto fail; - } + if (PyModule_AddType(m, type) < 0) { + goto fail; } } diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 3c411fa0d76358..5610a7689136f6 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1448,12 +1448,10 @@ test_from_contiguous(PyObject* self, PyObject *Py_UNUSED(ignored)) } #if (defined(__linux__) || defined(__FreeBSD__)) && defined(__GNUC__) -extern PyTypeObject _PyBytesIOBuffer_Type; static PyObject * test_pep3118_obsolete_write_locks(PyObject* self, PyObject *Py_UNUSED(ignored)) { - PyTypeObject *type = &_PyBytesIOBuffer_Type; PyObject *b; char *dummy[1]; int ret, match; @@ -1466,7 +1464,13 @@ test_pep3118_obsolete_write_locks(PyObject* self, PyObject *Py_UNUSED(ignored)) goto error; /* bytesiobuf_getbuffer() */ + PyTypeObject *type = (PyTypeObject *)_PyImport_GetModuleAttrString( + "_io", "_BytesIOBuffer"); + if (type == NULL) { + return NULL; + } b = type->tp_alloc(type, 0); + Py_DECREF(type); if (b == NULL) { return NULL; } From c7766245c14fa03b8afd3aff9be30b13d0069f95 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 15 Feb 2023 12:21:40 +0000 Subject: [PATCH 176/225] GH-87849: Fix refleak in SEND instruction. (GH-101908) Fix refleak in SEND instruction. --- Python/bytecodes.c | 1 + Python/generated_cases.c.h | 1 + 2 files changed, 2 insertions(+) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index be54e5f6f589eb..d5d5034cbfbf74 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -727,6 +727,7 @@ dummy_func( else { assert(retval != NULL); } + Py_DECREF(v); } inst(SEND_GEN, (unused/1, receiver, v -- receiver)) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index beb797cbd233d7..8b8a7161ad898e 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -934,6 +934,7 @@ else { assert(retval != NULL); } + Py_DECREF(v); POKE(1, retval); JUMPBY(1); DISPATCH(); From eb0c485b6c836abb71932537a5058344d11d7bc8 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 15 Feb 2023 14:07:59 +0100 Subject: [PATCH 177/225] gh-101819: Remove _PyWindowsConsoleIO_Type from the Windows DLL (GH-101904) Automerge-Triggered-By: GH:erlend-aasland --- .../pycore_global_objects_fini_generated.h | 2 ++ Include/internal/pycore_global_strings.h | 2 ++ Include/internal/pycore_runtime_init_generated.h | 2 ++ Include/internal/pycore_unicodeobject_generated.h | 4 ++++ Modules/_io/_iomodule.h | 4 ---- Modules/_io/winconsoleio.c | 4 +--- PC/_testconsole.c | 11 +++++++++-- Python/pylifecycle.c | 14 +++++++++----- 8 files changed, 29 insertions(+), 14 deletions(-) diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 8c210111b5899f..dc5cd58d853534 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -577,6 +577,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(True)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(WarningMessage)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_WindowsConsoleIO)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__IOBase_closed)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__abc_tpflags__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__abs__)); @@ -752,6 +753,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_get_sourcefile)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_handle_fromlist)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_initializing)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_io)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_is_text_encoding)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_length_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_limbo)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 6b1c8424424d96..8b23aa15479301 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -63,6 +63,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(True) STRUCT_FOR_ID(WarningMessage) STRUCT_FOR_ID(_) + STRUCT_FOR_ID(_WindowsConsoleIO) STRUCT_FOR_ID(__IOBase_closed) STRUCT_FOR_ID(__abc_tpflags__) STRUCT_FOR_ID(__abs__) @@ -238,6 +239,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(_get_sourcefile) STRUCT_FOR_ID(_handle_fromlist) STRUCT_FOR_ID(_initializing) + STRUCT_FOR_ID(_io) STRUCT_FOR_ID(_is_text_encoding) STRUCT_FOR_ID(_length_) STRUCT_FOR_ID(_limbo) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index fcb613083ffe99..471efadb13bb4f 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -569,6 +569,7 @@ extern "C" { INIT_ID(True), \ INIT_ID(WarningMessage), \ INIT_ID(_), \ + INIT_ID(_WindowsConsoleIO), \ INIT_ID(__IOBase_closed), \ INIT_ID(__abc_tpflags__), \ INIT_ID(__abs__), \ @@ -744,6 +745,7 @@ extern "C" { INIT_ID(_get_sourcefile), \ INIT_ID(_handle_fromlist), \ INIT_ID(_initializing), \ + INIT_ID(_io), \ INIT_ID(_is_text_encoding), \ INIT_ID(_length_), \ INIT_ID(_limbo), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 301aee5210e799..b47d240e492ff9 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -32,6 +32,8 @@ _PyUnicode_InitStaticStrings(void) { PyUnicode_InternInPlace(&string); string = &_Py_ID(_); PyUnicode_InternInPlace(&string); + string = &_Py_ID(_WindowsConsoleIO); + PyUnicode_InternInPlace(&string); string = &_Py_ID(__IOBase_closed); PyUnicode_InternInPlace(&string); string = &_Py_ID(__abc_tpflags__); @@ -382,6 +384,8 @@ _PyUnicode_InitStaticStrings(void) { PyUnicode_InternInPlace(&string); string = &_Py_ID(_initializing); PyUnicode_InternInPlace(&string); + string = &_Py_ID(_io); + PyUnicode_InternInPlace(&string); string = &_Py_ID(_is_text_encoding); PyUnicode_InternInPlace(&string); string = &_Py_ID(_length_); diff --git a/Modules/_io/_iomodule.h b/Modules/_io/_iomodule.h index c260080f0e348b..7617cb8fb70e43 100644 --- a/Modules/_io/_iomodule.h +++ b/Modules/_io/_iomodule.h @@ -21,13 +21,9 @@ extern PyTypeObject PyBufferedRandom_Type; extern PyTypeObject PyTextIOWrapper_Type; extern PyTypeObject PyIncrementalNewlineDecoder_Type; -#ifndef Py_LIMITED_API #ifdef MS_WINDOWS extern PyTypeObject PyWindowsConsoleIO_Type; -PyAPI_DATA(PyObject *) _PyWindowsConsoleIO_Type; -#define PyWindowsConsoleIO_Check(op) (PyObject_TypeCheck((op), (PyTypeObject*)_PyWindowsConsoleIO_Type)) #endif /* MS_WINDOWS */ -#endif /* Py_LIMITED_API */ /* These functions are used as METH_NOARGS methods, are normally called * with args=NULL, and return a new reference. diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index 4f41ab965e2e67..e913d831874617 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -260,7 +260,7 @@ _io__WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj, int fd_is_own = 0; HANDLE handle = NULL; - assert(PyWindowsConsoleIO_Check(self)); + assert(PyObject_TypeCheck(self, (PyTypeObject *)&PyWindowsConsoleIO_Type)); if (self->fd >= 0) { if (self->closefd) { /* Have to close the existing file first. */ @@ -1174,6 +1174,4 @@ PyTypeObject PyWindowsConsoleIO_Type = { 0, /* tp_finalize */ }; -PyObject * _PyWindowsConsoleIO_Type = (PyObject*)&PyWindowsConsoleIO_Type; - #endif /* MS_WINDOWS */ diff --git a/PC/_testconsole.c b/PC/_testconsole.c index a8308835d8f85d..f14a2d45b1be26 100644 --- a/PC/_testconsole.c +++ b/PC/_testconsole.c @@ -10,7 +10,7 @@ #ifdef MS_WINDOWS #include "pycore_fileutils.h" // _Py_get_osfhandle() -#include "..\modules\_io\_iomodule.h" +#include "pycore_runtime.h" // _Py_ID() #define WIN32_LEAN_AND_MEAN #include @@ -51,7 +51,14 @@ _testconsole_write_input_impl(PyObject *module, PyObject *file, { INPUT_RECORD *rec = NULL; - if (!PyWindowsConsoleIO_Check(file)) { + PyTypeObject *winconsoleio_type = (PyTypeObject *)_PyImport_GetModuleAttr( + &_Py_ID(_io), &_Py_ID(_WindowsConsoleIO)); + if (winconsoleio_type == NULL) { + return NULL; + } + int is_subclass = PyObject_TypeCheck(file, winconsoleio_type); + Py_DECREF(winconsoleio_type); + if (!is_subclass) { PyErr_SetString(PyExc_TypeError, "expected raw console object"); return NULL; } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index a8a8e7f3d84f21..045a2996e8988b 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -54,10 +54,6 @@ extern void _PyIO_Fini(void); #ifdef MS_WINDOWS # undef BYTE - - extern PyTypeObject PyWindowsConsoleIO_Type; -# define PyWindowsConsoleIO_Check(op) \ - (PyObject_TypeCheck((op), &PyWindowsConsoleIO_Type)) #endif #define PUTS(fd, str) _Py_write_noraise(fd, str, (int)strlen(str)) @@ -2358,8 +2354,16 @@ create_stdio(const PyConfig *config, PyObject* io, #ifdef MS_WINDOWS /* Windows console IO is always UTF-8 encoded */ - if (PyWindowsConsoleIO_Check(raw)) + PyTypeObject *winconsoleio_type = (PyTypeObject *)_PyImport_GetModuleAttr( + &_Py_ID(_io), &_Py_ID(_WindowsConsoleIO)); + if (winconsoleio_type == NULL) { + goto error; + } + int is_subclass = PyObject_TypeCheck(raw, winconsoleio_type); + Py_DECREF(winconsoleio_type); + if (is_subclass) { encoding = L"utf-8"; + } #endif text = PyUnicode_FromString(name); From c1ce0d178fe57b50f37578b285a343d77485ac02 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 15 Feb 2023 22:58:48 +0100 Subject: [PATCH 178/225] gh-99138: Isolate _zoneinfo (#99218) * Convert zone info type to heap type and add it to module state * Add global variables to module state --- Doc/library/zoneinfo.rst | 2 +- Lib/test/test_zoneinfo/test_zoneinfo.py | 4 +- ...3-01-02-22-41-44.gh-issue-99138.17hp9U.rst | 1 + Modules/_zoneinfo.c | 519 +++++++++++------- Modules/clinic/_zoneinfo.c.h | 215 +++++++- Tools/c-analyzer/cpython/globals-to-fix.tsv | 11 - 6 files changed, 539 insertions(+), 213 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-01-02-22-41-44.gh-issue-99138.17hp9U.rst diff --git a/Doc/library/zoneinfo.rst b/Doc/library/zoneinfo.rst index d2e5619e7e47c2..f8624da6e51dbb 100644 --- a/Doc/library/zoneinfo.rst +++ b/Doc/library/zoneinfo.rst @@ -241,7 +241,7 @@ The following class methods are also available: .. warning:: Invoking this function may change the semantics of datetimes using - ``ZoneInfo`` in surprising ways; this modifies process-wide global state + ``ZoneInfo`` in surprising ways; this modifies module state and thus may have wide-ranging effects. Only use it if you know that you need to. diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index fd0e3bc032ec0c..82041a2b488334 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -1797,12 +1797,10 @@ def test_cache_location(self): self.assertTrue(hasattr(py_zoneinfo.ZoneInfo, "_weak_cache")) def test_gc_tracked(self): - # The pure Python version is tracked by the GC but (for now) the C - # version is not. import gc self.assertTrue(gc.is_tracked(py_zoneinfo.ZoneInfo)) - self.assertFalse(gc.is_tracked(c_zoneinfo.ZoneInfo)) + self.assertTrue(gc.is_tracked(c_zoneinfo.ZoneInfo)) @dataclasses.dataclass(frozen=True) diff --git a/Misc/NEWS.d/next/Library/2023-01-02-22-41-44.gh-issue-99138.17hp9U.rst b/Misc/NEWS.d/next/Library/2023-01-02-22-41-44.gh-issue-99138.17hp9U.rst new file mode 100644 index 00000000000000..3dd4646f40e1e5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-02-22-41-44.gh-issue-99138.17hp9U.rst @@ -0,0 +1 @@ +Apply :pep:`687` to :mod:`zoneinfo`. Patch by Erlend E. Aasland. diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index 9d38589ea3d1b0..9f423559f51a43 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -19,10 +19,6 @@ class zoneinfo.ZoneInfo "PyObject *" "PyTypeObject *" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=d12c73c0eef36df8]*/ -// Imports -static PyObject *io_open = NULL; -static PyObject *_tzpath_find_tzfile = NULL; -static PyObject *_common_mod = NULL; typedef struct TransitionRuleType TransitionRuleType; typedef struct StrongCacheNode StrongCacheNode; @@ -90,15 +86,21 @@ struct StrongCacheNode { PyObject *zone; }; -static PyTypeObject PyZoneInfo_ZoneInfoType; +typedef struct { + PyTypeObject *ZoneInfoType; + + // Imports + PyObject *io_open; + PyObject *_tzpath_find_tzfile; + PyObject *_common_mod; -// Globals -static PyObject *TIMEDELTA_CACHE = NULL; -static PyObject *ZONEINFO_WEAK_CACHE = NULL; -static StrongCacheNode *ZONEINFO_STRONG_CACHE = NULL; -static size_t ZONEINFO_STRONG_CACHE_MAX_SIZE = 8; + // Caches + PyObject *TIMEDELTA_CACHE; + PyObject *ZONEINFO_WEAK_CACHE; + StrongCacheNode *ZONEINFO_STRONG_CACHE; -static _ttinfo NO_TTINFO = {NULL, NULL, NULL, 0}; + _ttinfo NO_TTINFO; +} zoneinfo_state; // Constants static const int EPOCHORDINAL = 719163; @@ -114,9 +116,12 @@ static const int SOURCE_NOCACHE = 0; static const int SOURCE_CACHE = 1; static const int SOURCE_FILE = 2; +static const size_t ZONEINFO_STRONG_CACHE_MAX_SIZE = 8; + // Forward declarations static int -load_data(PyZoneInfo_ZoneInfo *self, PyObject *file_obj); +load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, + PyObject *file_obj); static void utcoff_to_dstoff(size_t *trans_idx, long *utcoffs, long *dstoffs, unsigned char *isdsts, size_t num_transitions, @@ -127,7 +132,7 @@ ts_to_local(size_t *trans_idx, int64_t *trans_utc, long *utcoff, size_t num_transitions); static int -parse_tz_str(PyObject *tz_str_obj, _tzrule *out); +parse_tz_str(zoneinfo_state *state, PyObject *tz_str_obj, _tzrule *out); static Py_ssize_t parse_abbr(const char *const p, PyObject **abbr); @@ -146,26 +151,27 @@ find_tzrule_ttinfo_fromutc(_tzrule *rule, int64_t ts, int year, unsigned char *fold); static int -build_ttinfo(long utcoffset, long dstoffset, PyObject *tzname, _ttinfo *out); +build_ttinfo(zoneinfo_state *state, long utcoffset, long dstoffset, + PyObject *tzname, _ttinfo *out); static void xdecref_ttinfo(_ttinfo *ttinfo); static int ttinfo_eq(const _ttinfo *const tti0, const _ttinfo *const tti1); static int -build_tzrule(PyObject *std_abbr, PyObject *dst_abbr, long std_offset, - long dst_offset, TransitionRuleType *start, +build_tzrule(zoneinfo_state *state, PyObject *std_abbr, PyObject *dst_abbr, + long std_offset, long dst_offset, TransitionRuleType *start, TransitionRuleType *end, _tzrule *out); static void free_tzrule(_tzrule *tzrule); static PyObject * -load_timedelta(long seconds); +load_timedelta(zoneinfo_state *state, long seconds); static int get_local_timestamp(PyObject *dt, int64_t *local_ts); static _ttinfo * -find_ttinfo(PyZoneInfo_ZoneInfo *self, PyObject *dt); +find_ttinfo(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *dt); static int ymd_to_ord(int y, int m, int d); @@ -176,27 +182,57 @@ static size_t _bisect(const int64_t value, const int64_t *arr, size_t size); static int -eject_from_strong_cache(const PyTypeObject *const type, PyObject *key); +eject_from_strong_cache(zoneinfo_state *state, const PyTypeObject *const type, + PyObject *key); static void -clear_strong_cache(const PyTypeObject *const type); +clear_strong_cache(zoneinfo_state *state, const PyTypeObject *const type); static void -update_strong_cache(const PyTypeObject *const type, PyObject *key, - PyObject *zone); +update_strong_cache(zoneinfo_state *state, const PyTypeObject *const type, + PyObject *key, PyObject *zone); static PyObject * -zone_from_strong_cache(const PyTypeObject *const type, PyObject *const key); +zone_from_strong_cache(zoneinfo_state *state, const PyTypeObject *const type, + PyObject *const key); + +static inline zoneinfo_state * +zoneinfo_get_state(PyObject *mod) +{ + zoneinfo_state *state = (zoneinfo_state *)PyModule_GetState(mod); + assert(state != NULL); + return state; +} + +static inline zoneinfo_state * +zoneinfo_get_state_by_cls(PyTypeObject *cls) +{ + zoneinfo_state *state = (zoneinfo_state *)PyType_GetModuleState(cls); + assert(state != NULL); + return state; +} + +static struct PyModuleDef zoneinfomodule; + +static inline zoneinfo_state * +zoneinfo_get_state_by_self(PyTypeObject *self) +{ + PyObject *mod = PyType_GetModuleByDef(self, &zoneinfomodule); + assert(mod != NULL); + return zoneinfo_get_state(mod); +} static PyObject * -zoneinfo_new_instance(PyTypeObject *type, PyObject *key) +zoneinfo_new_instance(zoneinfo_state *state, PyTypeObject *type, PyObject *key) { PyObject *file_obj = NULL; PyObject *file_path = NULL; - file_path = PyObject_CallFunctionObjArgs(_tzpath_find_tzfile, key, NULL); + file_path = PyObject_CallFunctionObjArgs(state->_tzpath_find_tzfile, + key, NULL); if (file_path == NULL) { return NULL; } else if (file_path == Py_None) { - file_obj = PyObject_CallMethod(_common_mod, "load_tzdata", "O", key); + PyObject *meth = state->_common_mod; + file_obj = PyObject_CallMethod(meth, "load_tzdata", "O", key); if (file_obj == NULL) { Py_DECREF(file_path); return NULL; @@ -209,13 +245,14 @@ zoneinfo_new_instance(PyTypeObject *type, PyObject *key) } if (file_obj == NULL) { - file_obj = PyObject_CallFunction(io_open, "Os", file_path, "rb"); + PyObject *func = state->io_open; + file_obj = PyObject_CallFunction(func, "Os", file_path, "rb"); if (file_obj == NULL) { goto error; } } - if (load_data((PyZoneInfo_ZoneInfo *)self, file_obj)) { + if (load_data(state, (PyZoneInfo_ZoneInfo *)self, file_obj)) { goto error; } @@ -248,10 +285,10 @@ zoneinfo_new_instance(PyTypeObject *type, PyObject *key) } static PyObject * -get_weak_cache(PyTypeObject *type) +get_weak_cache(zoneinfo_state *state, PyTypeObject *type) { - if (type == &PyZoneInfo_ZoneInfoType) { - return ZONEINFO_WEAK_CACHE; + if (type == state->ZoneInfoType) { + return state->ZONEINFO_WEAK_CACHE; } else { PyObject *cache = @@ -273,12 +310,13 @@ zoneinfo_new(PyTypeObject *type, PyObject *args, PyObject *kw) return NULL; } - PyObject *instance = zone_from_strong_cache(type, key); + zoneinfo_state *state = zoneinfo_get_state_by_self(type); + PyObject *instance = zone_from_strong_cache(state, type, key); if (instance != NULL || PyErr_Occurred()) { return instance; } - PyObject *weak_cache = get_weak_cache(type); + PyObject *weak_cache = get_weak_cache(state, type); instance = PyObject_CallMethod(weak_cache, "get", "O", key, Py_None); if (instance == NULL) { return NULL; @@ -286,7 +324,7 @@ zoneinfo_new(PyTypeObject *type, PyObject *args, PyObject *kw) if (instance == Py_None) { Py_DECREF(instance); - PyObject *tmp = zoneinfo_new_instance(type, key); + PyObject *tmp = zoneinfo_new_instance(state, type, key); if (tmp == NULL) { return NULL; } @@ -300,14 +338,32 @@ zoneinfo_new(PyTypeObject *type, PyObject *args, PyObject *kw) ((PyZoneInfo_ZoneInfo *)instance)->source = SOURCE_CACHE; } - update_strong_cache(type, key, instance); + update_strong_cache(state, type, key, instance); return instance; } +static int +zoneinfo_traverse(PyZoneInfo_ZoneInfo *self, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(self)); + Py_VISIT(self->key); + return 0; +} + +static int +zoneinfo_clear(PyZoneInfo_ZoneInfo *self) +{ + Py_CLEAR(self->key); + Py_CLEAR(self->file_repr); + return 0; +} + static void zoneinfo_dealloc(PyObject *obj_self) { PyZoneInfo_ZoneInfo *self = (PyZoneInfo_ZoneInfo *)obj_self; + PyTypeObject *tp = Py_TYPE(self); + PyObject_GC_UnTrack(self); if (self->weakreflist != NULL) { PyObject_ClearWeakRefs(obj_self); @@ -336,16 +392,16 @@ zoneinfo_dealloc(PyObject *obj_self) free_tzrule(&(self->tzrule_after)); - Py_XDECREF(self->key); - Py_XDECREF(self->file_repr); - - Py_TYPE(self)->tp_free((PyObject *)self); + zoneinfo_clear(self); + tp->tp_free(obj_self); + Py_DECREF(tp); } /*[clinic input] @classmethod zoneinfo.ZoneInfo.from_file + cls: defining_class file_obj: object / key: object = None @@ -354,9 +410,9 @@ Create a ZoneInfo file from a file object. [clinic start generated code]*/ static PyObject * -zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyObject *file_obj, - PyObject *key) -/*[clinic end generated code: output=68ed2022404ae5be input=ccfe73708133d2e4]*/ +zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *file_obj, PyObject *key) +/*[clinic end generated code: output=77887d1d56a48324 input=d26111f29eed6863]*/ { PyObject *file_repr = NULL; PyZoneInfo_ZoneInfo *self = NULL; @@ -372,7 +428,8 @@ zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyObject *file_obj, goto error; } - if (load_data(self, file_obj)) { + zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); + if (load_data(state, self, file_obj)) { goto error; } @@ -391,16 +448,20 @@ zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyObject *file_obj, @classmethod zoneinfo.ZoneInfo.no_cache + cls: defining_class + / key: object Get a new instance of ZoneInfo, bypassing the cache. [clinic start generated code]*/ static PyObject * -zoneinfo_ZoneInfo_no_cache_impl(PyTypeObject *type, PyObject *key) -/*[clinic end generated code: output=751c6894ad66f91b input=bb24afd84a80ba46]*/ +zoneinfo_ZoneInfo_no_cache_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *key) +/*[clinic end generated code: output=b0b09b3344c171b7 input=0238f3d56b1ea3f1]*/ { - PyObject *out = zoneinfo_new_instance(type, key); + zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); + PyObject *out = zoneinfo_new_instance(state, type, key); if (out != NULL) { ((PyZoneInfo_ZoneInfo *)out)->source = SOURCE_NOCACHE; } @@ -412,6 +473,8 @@ zoneinfo_ZoneInfo_no_cache_impl(PyTypeObject *type, PyObject *key) @classmethod zoneinfo.ZoneInfo.clear_cache + cls: defining_class + / * only_keys: object = None @@ -419,10 +482,12 @@ Clear the ZoneInfo cache. [clinic start generated code]*/ static PyObject * -zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyObject *only_keys) -/*[clinic end generated code: output=eec0a3276f07bd90 input=8cff0182a95f295b]*/ +zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *only_keys) +/*[clinic end generated code: output=114d9b7c8a22e660 input=e32ca3bb396788ba]*/ { - PyObject *weak_cache = get_weak_cache(type); + zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); + PyObject *weak_cache = get_weak_cache(state, type); if (only_keys == NULL || only_keys == Py_None) { PyObject *rv = PyObject_CallMethod(weak_cache, "clear", NULL); @@ -430,7 +495,7 @@ zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyObject *only_keys) Py_DECREF(rv); } - clear_strong_cache(type); + clear_strong_cache(state, type); } else { PyObject *item = NULL; @@ -447,7 +512,7 @@ zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyObject *only_keys) while ((item = PyIter_Next(iter))) { // Remove from strong cache - if (eject_from_strong_cache(type, item) < 0) { + if (eject_from_strong_cache(state, type, item) < 0) { Py_DECREF(item); break; } @@ -473,30 +538,68 @@ zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyObject *only_keys) Py_RETURN_NONE; } +/*[clinic input] +zoneinfo.ZoneInfo.utcoffset + + cls: defining_class + dt: object + / + +Retrieve a timedelta representing the UTC offset in a zone at the given datetime. +[clinic start generated code]*/ + static PyObject * -zoneinfo_utcoffset(PyObject *self, PyObject *dt) +zoneinfo_ZoneInfo_utcoffset_impl(PyObject *self, PyTypeObject *cls, + PyObject *dt) +/*[clinic end generated code: output=b71016c319ba1f91 input=2bb6c5364938f19c]*/ { - _ttinfo *tti = find_ttinfo((PyZoneInfo_ZoneInfo *)self, dt); + zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); + _ttinfo *tti = find_ttinfo(state, (PyZoneInfo_ZoneInfo *)self, dt); if (tti == NULL) { return NULL; } return Py_NewRef(tti->utcoff); } +/*[clinic input] +zoneinfo.ZoneInfo.dst + + cls: defining_class + dt: object + / + +Retrieve a timedelta representing the amount of DST applied in a zone at the given datetime. +[clinic start generated code]*/ + static PyObject * -zoneinfo_dst(PyObject *self, PyObject *dt) +zoneinfo_ZoneInfo_dst_impl(PyObject *self, PyTypeObject *cls, PyObject *dt) +/*[clinic end generated code: output=cb6168d7723a6ae6 input=2167fb80cf8645c6]*/ { - _ttinfo *tti = find_ttinfo((PyZoneInfo_ZoneInfo *)self, dt); + zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); + _ttinfo *tti = find_ttinfo(state, (PyZoneInfo_ZoneInfo *)self, dt); if (tti == NULL) { return NULL; } return Py_NewRef(tti->dstoff); } +/*[clinic input] +zoneinfo.ZoneInfo.tzname + + cls: defining_class + dt: object + / + +Retrieve a string containing the abbreviation for the time zone that applies in a zone at a given datetime. +[clinic start generated code]*/ + static PyObject * -zoneinfo_tzname(PyObject *self, PyObject *dt) +zoneinfo_ZoneInfo_tzname_impl(PyObject *self, PyTypeObject *cls, + PyObject *dt) +/*[clinic end generated code: output=3b6ae6c3053ea75a input=15a59a4f92ed1f1f]*/ { - _ttinfo *tti = find_ttinfo((PyZoneInfo_ZoneInfo *)self, dt); + zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); + _ttinfo *tti = find_ttinfo(state, (PyZoneInfo_ZoneInfo *)self, dt); if (tti == NULL) { return NULL; } @@ -693,28 +796,37 @@ zoneinfo_reduce(PyObject *obj_self, PyObject *unused) return rv; } +/*[clinic input] +@classmethod +zoneinfo.ZoneInfo._unpickle + + cls: defining_class + key: object + from_cache: unsigned_char(bitwise=True) + / + +Private method used in unpickling. +[clinic start generated code]*/ + static PyObject * -zoneinfo__unpickle(PyTypeObject *cls, PyObject *args) +zoneinfo_ZoneInfo__unpickle_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *key, unsigned char from_cache) +/*[clinic end generated code: output=556712fc709deecb input=6ac8c73eed3de316]*/ { - PyObject *key; - unsigned char from_cache; - if (!PyArg_ParseTuple(args, "OB", &key, &from_cache)) { - return NULL; - } - if (from_cache) { PyObject *val_args = Py_BuildValue("(O)", key); if (val_args == NULL) { return NULL; } - PyObject *rv = zoneinfo_new(cls, val_args, NULL); + PyObject *rv = zoneinfo_new(type, val_args, NULL); Py_DECREF(val_args); return rv; } else { - return zoneinfo_new_instance(cls, key); + zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); + return zoneinfo_new_instance(state, type, key); } } @@ -732,14 +844,14 @@ zoneinfo__unpickle(PyTypeObject *cls, PyObject *args) * This returns a new reference to the timedelta. */ static PyObject * -load_timedelta(long seconds) +load_timedelta(zoneinfo_state *state, long seconds) { PyObject *rv; PyObject *pyoffset = PyLong_FromLong(seconds); if (pyoffset == NULL) { return NULL; } - rv = PyDict_GetItemWithError(TIMEDELTA_CACHE, pyoffset); + rv = PyDict_GetItemWithError(state->TIMEDELTA_CACHE, pyoffset); if (rv == NULL) { if (PyErr_Occurred()) { goto error; @@ -751,7 +863,7 @@ load_timedelta(long seconds) goto error; } - rv = PyDict_SetDefault(TIMEDELTA_CACHE, pyoffset, tmp); + rv = PyDict_SetDefault(state->TIMEDELTA_CACHE, pyoffset, tmp); Py_DECREF(tmp); } @@ -768,19 +880,20 @@ load_timedelta(long seconds) * initialized _ttinfo objects. */ static int -build_ttinfo(long utcoffset, long dstoffset, PyObject *tzname, _ttinfo *out) +build_ttinfo(zoneinfo_state *state, long utcoffset, long dstoffset, + PyObject *tzname, _ttinfo *out) { out->utcoff = NULL; out->dstoff = NULL; out->tzname = NULL; out->utcoff_seconds = utcoffset; - out->utcoff = load_timedelta(utcoffset); + out->utcoff = load_timedelta(state, utcoffset); if (out->utcoff == NULL) { return -1; } - out->dstoff = load_timedelta(dstoffset); + out->dstoff = load_timedelta(state, dstoffset); if (out->dstoff == NULL) { return -1; } @@ -836,7 +949,7 @@ ttinfo_eq(const _ttinfo *const tti0, const _ttinfo *const tti1) * the object only needs to be freed / deallocated if this succeeds. */ static int -load_data(PyZoneInfo_ZoneInfo *self, PyObject *file_obj) +load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *file_obj) { PyObject *data_tuple = NULL; @@ -854,7 +967,8 @@ load_data(PyZoneInfo_ZoneInfo *self, PyObject *file_obj) size_t ttinfos_allocated = 0; - data_tuple = PyObject_CallMethod(_common_mod, "load_data", "O", file_obj); + data_tuple = PyObject_CallMethod(state->_common_mod, "load_data", "O", + file_obj); if (data_tuple == NULL) { goto error; @@ -1012,7 +1126,9 @@ load_data(PyZoneInfo_ZoneInfo *self, PyObject *file_obj) } ttinfos_allocated++; - if (build_ttinfo(utcoff[i], dstoff[i], tzname, &(self->_ttinfos[i]))) { + int rc = build_ttinfo(state, utcoff[i], dstoff[i], tzname, + &(self->_ttinfos[i])); + if (rc) { goto error; } } @@ -1044,7 +1160,7 @@ load_data(PyZoneInfo_ZoneInfo *self, PyObject *file_obj) } if (tz_str != Py_None && PyObject_IsTrue(tz_str)) { - if (parse_tz_str(tz_str, &(self->tzrule_after))) { + if (parse_tz_str(state, tz_str, &(self->tzrule_after))) { goto error; } } @@ -1063,8 +1179,8 @@ load_data(PyZoneInfo_ZoneInfo *self, PyObject *file_obj) } _ttinfo *tti = &(self->_ttinfos[idx]); - build_tzrule(tti->tzname, NULL, tti->utcoff_seconds, 0, NULL, NULL, - &(self->tzrule_after)); + build_tzrule(state, tti->tzname, NULL, tti->utcoff_seconds, 0, NULL, + NULL, &(self->tzrule_after)); // We've abused the build_tzrule constructor to construct an STD-only // rule mimicking whatever ttinfo we've picked up, but it's possible @@ -1463,7 +1579,7 @@ find_tzrule_ttinfo_fromutc(_tzrule *rule, int64_t ts, int year, * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html */ static int -parse_tz_str(PyObject *tz_str_obj, _tzrule *out) +parse_tz_str(zoneinfo_state *state, PyObject *tz_str_obj, _tzrule *out) { PyObject *std_abbr = NULL; PyObject *dst_abbr = NULL; @@ -1555,7 +1671,8 @@ parse_tz_str(PyObject *tz_str_obj, _tzrule *out) } complete: - build_tzrule(std_abbr, dst_abbr, std_offset, dst_offset, start, end, out); + build_tzrule(state, std_abbr, dst_abbr, std_offset, dst_offset, + start, end, out); Py_DECREF(std_abbr); Py_XDECREF(dst_abbr); @@ -1913,8 +2030,8 @@ parse_transition_time(const char *const p, int8_t *hour, int8_t *minute, * Returns 0 on success. */ static int -build_tzrule(PyObject *std_abbr, PyObject *dst_abbr, long std_offset, - long dst_offset, TransitionRuleType *start, +build_tzrule(zoneinfo_state *state, PyObject *std_abbr, PyObject *dst_abbr, + long std_offset, long dst_offset, TransitionRuleType *start, TransitionRuleType *end, _tzrule *out) { _tzrule rv = {{0}}; @@ -1922,13 +2039,13 @@ build_tzrule(PyObject *std_abbr, PyObject *dst_abbr, long std_offset, rv.start = start; rv.end = end; - if (build_ttinfo(std_offset, 0, std_abbr, &rv.std)) { + if (build_ttinfo(state, std_offset, 0, std_abbr, &rv.std)) { goto error; } if (dst_abbr != NULL) { rv.dst_diff = dst_offset - std_offset; - if (build_ttinfo(dst_offset, rv.dst_diff, dst_abbr, &rv.dst)) { + if (build_ttinfo(state, dst_offset, rv.dst_diff, dst_abbr, &rv.dst)) { goto error; } } @@ -2132,7 +2249,7 @@ _bisect(const int64_t value, const int64_t *arr, size_t size) /* Find the ttinfo rules that apply at a given local datetime. */ static _ttinfo * -find_ttinfo(PyZoneInfo_ZoneInfo *self, PyObject *dt) +find_ttinfo(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *dt) { // datetime.time has a .tzinfo attribute that passes None as the dt // argument; it only really has meaning for fixed-offset zones. @@ -2141,7 +2258,7 @@ find_ttinfo(PyZoneInfo_ZoneInfo *self, PyObject *dt) return &(self->tzrule_after.std); } else { - return &NO_TTINFO; + return &(state->NO_TTINFO); } } @@ -2317,10 +2434,10 @@ strong_cache_free(StrongCacheNode *root) * the front of the cache. */ static void -remove_from_strong_cache(StrongCacheNode *node) +remove_from_strong_cache(zoneinfo_state *state, StrongCacheNode *node) { - if (ZONEINFO_STRONG_CACHE == node) { - ZONEINFO_STRONG_CACHE = node->next; + if (state->ZONEINFO_STRONG_CACHE == node) { + state->ZONEINFO_STRONG_CACHE = node->next; } if (node->prev != NULL) { @@ -2366,15 +2483,17 @@ find_in_strong_cache(const StrongCacheNode *const root, PyObject *const key) * This function is used to enable the per-key functionality in clear_cache. */ static int -eject_from_strong_cache(const PyTypeObject *const type, PyObject *key) +eject_from_strong_cache(zoneinfo_state *state, const PyTypeObject *const type, + PyObject *key) { - if (type != &PyZoneInfo_ZoneInfoType) { + if (type != state->ZoneInfoType) { return 0; } - StrongCacheNode *node = find_in_strong_cache(ZONEINFO_STRONG_CACHE, key); + StrongCacheNode *cache = state->ZONEINFO_STRONG_CACHE; + StrongCacheNode *node = find_in_strong_cache(cache, key); if (node != NULL) { - remove_from_strong_cache(node); + remove_from_strong_cache(state, node); strong_cache_node_free(node); } @@ -2390,14 +2509,15 @@ eject_from_strong_cache(const PyTypeObject *const type, PyObject *key) * it is not at the front of the cache, it needs to be moved there. */ static void -move_strong_cache_node_to_front(StrongCacheNode **root, StrongCacheNode *node) +move_strong_cache_node_to_front(zoneinfo_state *state, StrongCacheNode **root, + StrongCacheNode *node) { StrongCacheNode *root_p = *root; if (root_p == node) { return; } - remove_from_strong_cache(node); + remove_from_strong_cache(state, node); node->prev = NULL; node->next = root_p; @@ -2419,16 +2539,19 @@ move_strong_cache_node_to_front(StrongCacheNode **root, StrongCacheNode *node) * always returns a cache miss for subclasses. */ static PyObject * -zone_from_strong_cache(const PyTypeObject *const type, PyObject *const key) +zone_from_strong_cache(zoneinfo_state *state, const PyTypeObject *const type, + PyObject *const key) { - if (type != &PyZoneInfo_ZoneInfoType) { + if (type != state->ZoneInfoType) { return NULL; // Strong cache currently only implemented for base class } - StrongCacheNode *node = find_in_strong_cache(ZONEINFO_STRONG_CACHE, key); + StrongCacheNode *cache = state->ZONEINFO_STRONG_CACHE; + StrongCacheNode *node = find_in_strong_cache(cache, key); if (node != NULL) { - move_strong_cache_node_to_front(&ZONEINFO_STRONG_CACHE, node); + StrongCacheNode **root = &(state->ZONEINFO_STRONG_CACHE); + move_strong_cache_node_to_front(state, root, node); return Py_NewRef(node->zone); } @@ -2442,16 +2565,16 @@ zone_from_strong_cache(const PyTypeObject *const type, PyObject *const key) * the cache to at most ZONEINFO_STRONG_CACHE_MAX_SIZE). */ static void -update_strong_cache(const PyTypeObject *const type, PyObject *key, - PyObject *zone) +update_strong_cache(zoneinfo_state *state, const PyTypeObject *const type, + PyObject *key, PyObject *zone) { - if (type != &PyZoneInfo_ZoneInfoType) { + if (type != state->ZoneInfoType) { return; } StrongCacheNode *new_node = strong_cache_node_new(key, zone); - - move_strong_cache_node_to_front(&ZONEINFO_STRONG_CACHE, new_node); + StrongCacheNode **root = &(state->ZONEINFO_STRONG_CACHE); + move_strong_cache_node_to_front(state, root, new_node); StrongCacheNode *node = new_node->next; for (size_t i = 1; i < ZONEINFO_STRONG_CACHE_MAX_SIZE; ++i) { @@ -2476,14 +2599,14 @@ update_strong_cache(const PyTypeObject *const type, PyObject *key, * for everything except the base class. */ void -clear_strong_cache(const PyTypeObject *const type) +clear_strong_cache(zoneinfo_state *state, const PyTypeObject *const type) { - if (type != &PyZoneInfo_ZoneInfoType) { + if (type != state->ZoneInfoType) { return; } - strong_cache_free(ZONEINFO_STRONG_CACHE); - ZONEINFO_STRONG_CACHE = NULL; + strong_cache_free(state->ZONEINFO_STRONG_CACHE); + state->ZONEINFO_STRONG_CACHE = NULL; } static PyObject * @@ -2499,29 +2622,17 @@ new_weak_cache(void) return weak_cache; } +// This function is not idempotent and must be called on a new module object. static int -initialize_caches(void) +initialize_caches(zoneinfo_state *state) { - // TODO: Move to a PyModule_GetState / PEP 573 based caching system. - if (TIMEDELTA_CACHE == NULL) { - TIMEDELTA_CACHE = PyDict_New(); - } - else { - Py_INCREF(TIMEDELTA_CACHE); - } - - if (TIMEDELTA_CACHE == NULL) { + state->TIMEDELTA_CACHE = PyDict_New(); + if (state->TIMEDELTA_CACHE == NULL) { return -1; } - if (ZONEINFO_WEAK_CACHE == NULL) { - ZONEINFO_WEAK_CACHE = new_weak_cache(); - } - else { - Py_INCREF(ZONEINFO_WEAK_CACHE); - } - - if (ZONEINFO_WEAK_CACHE == NULL) { + state->ZONEINFO_WEAK_CACHE = new_weak_cache(); + if (state->ZONEINFO_WEAK_CACHE == NULL) { return -1; } @@ -2551,22 +2662,15 @@ static PyMethodDef zoneinfo_methods[] = { ZONEINFO_ZONEINFO_CLEAR_CACHE_METHODDEF ZONEINFO_ZONEINFO_NO_CACHE_METHODDEF ZONEINFO_ZONEINFO_FROM_FILE_METHODDEF - {"utcoffset", (PyCFunction)zoneinfo_utcoffset, METH_O, - PyDoc_STR("Retrieve a timedelta representing the UTC offset in a zone at " - "the given datetime.")}, - {"dst", (PyCFunction)zoneinfo_dst, METH_O, - PyDoc_STR("Retrieve a timedelta representing the amount of DST applied " - "in a zone at the given datetime.")}, - {"tzname", (PyCFunction)zoneinfo_tzname, METH_O, - PyDoc_STR("Retrieve a string containing the abbreviation for the time " - "zone that applies in a zone at a given datetime.")}, + ZONEINFO_ZONEINFO_UTCOFFSET_METHODDEF + ZONEINFO_ZONEINFO_DST_METHODDEF + ZONEINFO_ZONEINFO_TZNAME_METHODDEF {"fromutc", (PyCFunction)zoneinfo_fromutc, METH_O, PyDoc_STR("Given a datetime with local time in UTC, retrieve an adjusted " "datetime in local time.")}, {"__reduce__", (PyCFunction)zoneinfo_reduce, METH_NOARGS, PyDoc_STR("Function for serialization with the pickle protocol.")}, - {"_unpickle", (PyCFunction)zoneinfo__unpickle, METH_VARARGS | METH_CLASS, - PyDoc_STR("Private method used in unpickling.")}, + ZONEINFO_ZONEINFO__UNPICKLE_METHODDEF {"__init_subclass__", (PyCFunction)(void (*)(void))zoneinfo_init_subclass, METH_VARARGS | METH_KEYWORDS | METH_CLASS, PyDoc_STR("Function to initialize subclasses.")}, @@ -2579,50 +2683,88 @@ static PyMemberDef zoneinfo_members[] = { .type = T_OBJECT_EX, .flags = READONLY, .doc = NULL}, + {.name = "__weaklistoffset__", + .offset = offsetof(PyZoneInfo_ZoneInfo, weakreflist), + .type = T_PYSSIZET, + .flags = READONLY}, {NULL}, /* Sentinel */ }; -static PyTypeObject PyZoneInfo_ZoneInfoType = { - PyVarObject_HEAD_INIT(NULL, 0) // - .tp_name = "zoneinfo.ZoneInfo", - .tp_basicsize = sizeof(PyZoneInfo_ZoneInfo), - .tp_weaklistoffset = offsetof(PyZoneInfo_ZoneInfo, weakreflist), - .tp_repr = (reprfunc)zoneinfo_repr, - .tp_str = (reprfunc)zoneinfo_str, - .tp_getattro = PyObject_GenericGetAttr, - .tp_flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE), - /* .tp_doc = zoneinfo_doc, */ - .tp_methods = zoneinfo_methods, - .tp_members = zoneinfo_members, - .tp_new = zoneinfo_new, - .tp_dealloc = zoneinfo_dealloc, +static PyType_Slot zoneinfo_slots[] = { + {Py_tp_repr, zoneinfo_repr}, + {Py_tp_str, zoneinfo_str}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_methods, zoneinfo_methods}, + {Py_tp_members, zoneinfo_members}, + {Py_tp_new, zoneinfo_new}, + {Py_tp_dealloc, zoneinfo_dealloc}, + {Py_tp_traverse, zoneinfo_traverse}, + {Py_tp_clear, zoneinfo_clear}, + {0, NULL}, +}; + +static PyType_Spec zoneinfo_spec = { + .name = "zoneinfo.ZoneInfo", + .basicsize = sizeof(PyZoneInfo_ZoneInfo), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE), + .slots = zoneinfo_slots, }; ///// // Specify the _zoneinfo module static PyMethodDef module_methods[] = {{NULL, NULL}}; -static void -module_free(void *m) + +static int +module_traverse(PyObject *mod, visitproc visit, void *arg) { - Py_CLEAR(_tzpath_find_tzfile); - Py_CLEAR(_common_mod); - Py_CLEAR(io_open); + zoneinfo_state *state = zoneinfo_get_state(mod); - xdecref_ttinfo(&NO_TTINFO); + Py_VISIT(state->ZoneInfoType); + Py_VISIT(state->io_open); + Py_VISIT(state->_tzpath_find_tzfile); + Py_VISIT(state->_common_mod); + Py_VISIT(state->TIMEDELTA_CACHE); + Py_VISIT(state->ZONEINFO_WEAK_CACHE); - if (TIMEDELTA_CACHE != NULL && Py_REFCNT(TIMEDELTA_CACHE) > 1) { - Py_DECREF(TIMEDELTA_CACHE); - } else { - Py_CLEAR(TIMEDELTA_CACHE); + StrongCacheNode *node = state->ZONEINFO_STRONG_CACHE; + while (node != NULL) { + StrongCacheNode *next = node->next; + Py_VISIT(node->key); + Py_VISIT(node->zone); + node = next; } - if (ZONEINFO_WEAK_CACHE != NULL && Py_REFCNT(ZONEINFO_WEAK_CACHE) > 1) { - Py_DECREF(ZONEINFO_WEAK_CACHE); - } else { - Py_CLEAR(ZONEINFO_WEAK_CACHE); - } + Py_VISIT(state->NO_TTINFO.utcoff); + Py_VISIT(state->NO_TTINFO.dstoff); + Py_VISIT(state->NO_TTINFO.tzname); - clear_strong_cache(&PyZoneInfo_ZoneInfoType); + return 0; +} + +static int +module_clear(PyObject *mod) +{ + zoneinfo_state *state = zoneinfo_get_state(mod); + + Py_CLEAR(state->ZoneInfoType); + Py_CLEAR(state->io_open); + Py_CLEAR(state->_tzpath_find_tzfile); + Py_CLEAR(state->_common_mod); + Py_CLEAR(state->TIMEDELTA_CACHE); + Py_CLEAR(state->ZONEINFO_WEAK_CACHE); + clear_strong_cache(state, state->ZoneInfoType); + Py_CLEAR(state->NO_TTINFO.utcoff); + Py_CLEAR(state->NO_TTINFO.dstoff); + Py_CLEAR(state->NO_TTINFO.tzname); + + return 0; +} + +static void +module_free(void *mod) +{ + (void)module_clear((PyObject *)mod); } static int @@ -2632,39 +2774,45 @@ zoneinfomodule_exec(PyObject *m) if (PyDateTimeAPI == NULL) { goto error; } - PyZoneInfo_ZoneInfoType.tp_base = PyDateTimeAPI->TZInfoType; - if (PyType_Ready(&PyZoneInfo_ZoneInfoType) < 0) { + + zoneinfo_state *state = zoneinfo_get_state(m); + PyObject *base = (PyObject *)PyDateTimeAPI->TZInfoType; + state->ZoneInfoType = (PyTypeObject *)PyType_FromModuleAndSpec(m, + &zoneinfo_spec, base); + if (state->ZoneInfoType == NULL) { goto error; } - if (PyModule_AddObjectRef(m, "ZoneInfo", (PyObject *)&PyZoneInfo_ZoneInfoType) < 0) { + int rc = PyModule_AddObjectRef(m, "ZoneInfo", + (PyObject *)state->ZoneInfoType); + if (rc < 0) { goto error; } /* Populate imports */ - _tzpath_find_tzfile = + state->_tzpath_find_tzfile = _PyImport_GetModuleAttrString("zoneinfo._tzpath", "find_tzfile"); - if (_tzpath_find_tzfile == NULL) { + if (state->_tzpath_find_tzfile == NULL) { goto error; } - io_open = _PyImport_GetModuleAttrString("io", "open"); - if (io_open == NULL) { + state->io_open = _PyImport_GetModuleAttrString("io", "open"); + if (state->io_open == NULL) { goto error; } - _common_mod = PyImport_ImportModule("zoneinfo._common"); - if (_common_mod == NULL) { + state->_common_mod = PyImport_ImportModule("zoneinfo._common"); + if (state->_common_mod == NULL) { goto error; } - if (NO_TTINFO.utcoff == NULL) { - NO_TTINFO.utcoff = Py_NewRef(Py_None); - NO_TTINFO.dstoff = Py_NewRef(Py_None); - NO_TTINFO.tzname = Py_NewRef(Py_None); + if (state->NO_TTINFO.utcoff == NULL) { + state->NO_TTINFO.utcoff = Py_NewRef(Py_None); + state->NO_TTINFO.dstoff = Py_NewRef(Py_None); + state->NO_TTINFO.tzname = Py_NewRef(Py_None); } - if (initialize_caches()) { + if (initialize_caches(state)) { goto error; } @@ -2678,13 +2826,16 @@ static PyModuleDef_Slot zoneinfomodule_slots[] = { {Py_mod_exec, zoneinfomodule_exec}, {0, NULL}}; static struct PyModuleDef zoneinfomodule = { - PyModuleDef_HEAD_INIT, + .m_base = PyModuleDef_HEAD_INIT, .m_name = "_zoneinfo", .m_doc = "C implementation of the zoneinfo module", - .m_size = 0, + .m_size = sizeof(zoneinfo_state), .m_methods = module_methods, .m_slots = zoneinfomodule_slots, - .m_free = (freefunc)module_free}; + .m_traverse = module_traverse, + .m_clear = module_clear, + .m_free = module_free, +}; PyMODINIT_FUNC PyInit__zoneinfo(void) diff --git a/Modules/clinic/_zoneinfo.c.h b/Modules/clinic/_zoneinfo.c.h index 78fcbfa9411bb8..ae62865e0f67df 100644 --- a/Modules/clinic/_zoneinfo.c.h +++ b/Modules/clinic/_zoneinfo.c.h @@ -15,14 +15,14 @@ PyDoc_STRVAR(zoneinfo_ZoneInfo_from_file__doc__, "Create a ZoneInfo file from a file object."); #define ZONEINFO_ZONEINFO_FROM_FILE_METHODDEF \ - {"from_file", _PyCFunction_CAST(zoneinfo_ZoneInfo_from_file), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_from_file__doc__}, + {"from_file", _PyCFunction_CAST(zoneinfo_ZoneInfo_from_file), METH_METHOD|METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_from_file__doc__}, static PyObject * -zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyObject *file_obj, - PyObject *key); +zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *file_obj, PyObject *key); static PyObject * -zoneinfo_ZoneInfo_from_file(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +zoneinfo_ZoneInfo_from_file(PyTypeObject *type, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) @@ -65,7 +65,7 @@ zoneinfo_ZoneInfo_from_file(PyTypeObject *type, PyObject *const *args, Py_ssize_ } key = args[1]; skip_optional_pos: - return_value = zoneinfo_ZoneInfo_from_file_impl(type, file_obj, key); + return_value = zoneinfo_ZoneInfo_from_file_impl(type, cls, file_obj, key); exit: return return_value; @@ -78,13 +78,14 @@ PyDoc_STRVAR(zoneinfo_ZoneInfo_no_cache__doc__, "Get a new instance of ZoneInfo, bypassing the cache."); #define ZONEINFO_ZONEINFO_NO_CACHE_METHODDEF \ - {"no_cache", _PyCFunction_CAST(zoneinfo_ZoneInfo_no_cache), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_no_cache__doc__}, + {"no_cache", _PyCFunction_CAST(zoneinfo_ZoneInfo_no_cache), METH_METHOD|METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_no_cache__doc__}, static PyObject * -zoneinfo_ZoneInfo_no_cache_impl(PyTypeObject *type, PyObject *key); +zoneinfo_ZoneInfo_no_cache_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *key); static PyObject * -zoneinfo_ZoneInfo_no_cache(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +zoneinfo_ZoneInfo_no_cache(PyTypeObject *type, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) @@ -120,7 +121,7 @@ zoneinfo_ZoneInfo_no_cache(PyTypeObject *type, PyObject *const *args, Py_ssize_t goto exit; } key = args[0]; - return_value = zoneinfo_ZoneInfo_no_cache_impl(type, key); + return_value = zoneinfo_ZoneInfo_no_cache_impl(type, cls, key); exit: return return_value; @@ -133,13 +134,14 @@ PyDoc_STRVAR(zoneinfo_ZoneInfo_clear_cache__doc__, "Clear the ZoneInfo cache."); #define ZONEINFO_ZONEINFO_CLEAR_CACHE_METHODDEF \ - {"clear_cache", _PyCFunction_CAST(zoneinfo_ZoneInfo_clear_cache), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_clear_cache__doc__}, + {"clear_cache", _PyCFunction_CAST(zoneinfo_ZoneInfo_clear_cache), METH_METHOD|METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_clear_cache__doc__}, static PyObject * -zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyObject *only_keys); +zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *only_keys); static PyObject * -zoneinfo_ZoneInfo_clear_cache(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +zoneinfo_ZoneInfo_clear_cache(PyTypeObject *type, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) @@ -180,9 +182,194 @@ zoneinfo_ZoneInfo_clear_cache(PyTypeObject *type, PyObject *const *args, Py_ssiz } only_keys = args[0]; skip_optional_kwonly: - return_value = zoneinfo_ZoneInfo_clear_cache_impl(type, only_keys); + return_value = zoneinfo_ZoneInfo_clear_cache_impl(type, cls, only_keys); exit: return return_value; } -/*[clinic end generated code: output=d2da73ef66146b83 input=a9049054013a1b77]*/ + +PyDoc_STRVAR(zoneinfo_ZoneInfo_utcoffset__doc__, +"utcoffset($self, dt, /)\n" +"--\n" +"\n" +"Retrieve a timedelta representing the UTC offset in a zone at the given datetime."); + +#define ZONEINFO_ZONEINFO_UTCOFFSET_METHODDEF \ + {"utcoffset", _PyCFunction_CAST(zoneinfo_ZoneInfo_utcoffset), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, zoneinfo_ZoneInfo_utcoffset__doc__}, + +static PyObject * +zoneinfo_ZoneInfo_utcoffset_impl(PyObject *self, PyTypeObject *cls, + PyObject *dt); + +static PyObject * +zoneinfo_ZoneInfo_utcoffset(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "utcoffset", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *dt; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + dt = args[0]; + return_value = zoneinfo_ZoneInfo_utcoffset_impl(self, cls, dt); + +exit: + return return_value; +} + +PyDoc_STRVAR(zoneinfo_ZoneInfo_dst__doc__, +"dst($self, dt, /)\n" +"--\n" +"\n" +"Retrieve a timedelta representing the amount of DST applied in a zone at the given datetime."); + +#define ZONEINFO_ZONEINFO_DST_METHODDEF \ + {"dst", _PyCFunction_CAST(zoneinfo_ZoneInfo_dst), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, zoneinfo_ZoneInfo_dst__doc__}, + +static PyObject * +zoneinfo_ZoneInfo_dst_impl(PyObject *self, PyTypeObject *cls, PyObject *dt); + +static PyObject * +zoneinfo_ZoneInfo_dst(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "dst", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *dt; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + dt = args[0]; + return_value = zoneinfo_ZoneInfo_dst_impl(self, cls, dt); + +exit: + return return_value; +} + +PyDoc_STRVAR(zoneinfo_ZoneInfo_tzname__doc__, +"tzname($self, dt, /)\n" +"--\n" +"\n" +"Retrieve a string containing the abbreviation for the time zone that applies in a zone at a given datetime."); + +#define ZONEINFO_ZONEINFO_TZNAME_METHODDEF \ + {"tzname", _PyCFunction_CAST(zoneinfo_ZoneInfo_tzname), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, zoneinfo_ZoneInfo_tzname__doc__}, + +static PyObject * +zoneinfo_ZoneInfo_tzname_impl(PyObject *self, PyTypeObject *cls, + PyObject *dt); + +static PyObject * +zoneinfo_ZoneInfo_tzname(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "tzname", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *dt; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + dt = args[0]; + return_value = zoneinfo_ZoneInfo_tzname_impl(self, cls, dt); + +exit: + return return_value; +} + +PyDoc_STRVAR(zoneinfo_ZoneInfo__unpickle__doc__, +"_unpickle($type, key, from_cache, /)\n" +"--\n" +"\n" +"Private method used in unpickling."); + +#define ZONEINFO_ZONEINFO__UNPICKLE_METHODDEF \ + {"_unpickle", _PyCFunction_CAST(zoneinfo_ZoneInfo__unpickle), METH_METHOD|METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo__unpickle__doc__}, + +static PyObject * +zoneinfo_ZoneInfo__unpickle_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *key, unsigned char from_cache); + +static PyObject * +zoneinfo_ZoneInfo__unpickle(PyTypeObject *type, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_unpickle", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *key; + unsigned char from_cache; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + if (!args) { + goto exit; + } + key = args[0]; + { + unsigned long ival = PyLong_AsUnsignedLongMask(args[1]); + if (ival == (unsigned long)-1 && PyErr_Occurred()) { + goto exit; + } + else { + from_cache = (unsigned char) ival; + } + } + return_value = zoneinfo_ZoneInfo__unpickle_impl(type, cls, key, from_cache); + +exit: + return return_value; +} +/*[clinic end generated code: output=54051388dfc408af input=a9049054013a1b77]*/ diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 52ea0b4901d4bb..6011b1604508af 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -403,7 +403,6 @@ Modules/_pickle.c - PicklerMemoProxyType - Modules/_pickle.c - Pickler_Type - Modules/_pickle.c - UnpicklerMemoProxyType - Modules/_pickle.c - Unpickler_Type - -Modules/_zoneinfo.c - PyZoneInfo_ZoneInfoType - Modules/ossaudiodev.c - OSSAudioType - Modules/ossaudiodev.c - OSSMixerType - Modules/socketmodule.c - sock_type - @@ -442,11 +441,6 @@ Modules/xxmodule.c - ErrorObject - Modules/_ctypes/callproc.c _ctypes_get_errobj error_object_name - Modules/_ctypes/_ctypes.c CreateSwappedType suffix - -## other - during module init -Modules/_zoneinfo.c - io_open - -Modules/_zoneinfo.c - _tzpath_find_tzfile - -Modules/_zoneinfo.c - _common_mod - - ##----------------------- ## other @@ -481,8 +475,6 @@ Modules/_tkinter.c - tcl_lock - Modules/_tkinter.c - excInCmd - Modules/_tkinter.c - valInCmd - Modules/_tkinter.c - trbInCmd - -Modules/_zoneinfo.c - TIMEDELTA_CACHE - -Modules/_zoneinfo.c - ZONEINFO_WEAK_CACHE - ################################## @@ -556,9 +548,6 @@ Modules/_tkinter.c - HeadFHCD - Modules/_tkinter.c - stdin_ready - Modules/_tkinter.c - event_tstate - Modules/_xxsubinterpretersmodule.c - _globals - -Modules/_zoneinfo.c - ZONEINFO_STRONG_CACHE - -Modules/_zoneinfo.c - ZONEINFO_STRONG_CACHE_MAX_SIZE - -Modules/_zoneinfo.c - NO_TTINFO - Modules/readline.c - completer_word_break_characters - Modules/readline.c - _history_length - Modules/readline.c - should_auto_add_history - From b2fc5492789623d656953d458f3eeaac03c1ef56 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 15 Feb 2023 15:32:31 -0700 Subject: [PATCH 179/225] gh-101758: Clean Up Uses of Import State (gh-101919) This change is almost entirely moving code around and hiding import state behind internal API. We introduce no changes to behavior, nor to non-internal API. (Since there was already going to be a lot of churn, I took this as an opportunity to re-organize import.c into topically-grouped sections of code.) The motivation is to simplify a number of upcoming changes. Specific changes: * move existing import-related code to import.c, wherever possible * add internal API for interacting with import state (both global and per-interpreter) * use only API outside of import.c (to limit churn there when changing the location, etc.) * consolidate the import-related state of PyInterpreterState into a single struct field (this changes layout slightly) * add macros for import state in import.c (to simplify changing the location) * group code in import.c into sections *remove _PyState_AddModule() https://github.com/python/cpython/issues/101758 --- Include/internal/pycore_import.h | 103 +- Include/internal/pycore_interp.h | 35 +- Include/internal/pycore_pylifecycle.h | 4 - Include/internal/pycore_pystate.h | 6 - Include/internal/pycore_runtime_init.h | 15 +- Include/internal/pycore_sysmodule.h | 3 + Lib/test/test_imp.py | 2 +- Lib/test/test_stable_abi_ctypes.py | 1 - Misc/stable_abi.toml | 3 - Objects/moduleobject.c | 25 +- PC/python3dll.c | 1 - Python/_warnings.c | 11 +- Python/ceval.c | 2 +- Python/dynload_shlib.c | 2 +- Python/import.c | 2210 +++++++++++++++--------- Python/importdl.c | 17 +- Python/pylifecycle.c | 127 +- Python/pystate.c | 151 +- Python/pythonrun.c | 10 +- Python/sysmodule.c | 40 +- 20 files changed, 1586 insertions(+), 1182 deletions(-) diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index 9036dff6725330..da766253ef6b9c 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -36,11 +36,112 @@ struct _import_runtime_state { const char * pkgcontext; }; +struct _import_state { + /* cached sys.modules dictionary */ + PyObject *modules; + /* This is the list of module objects for all legacy (single-phase init) + extension modules ever loaded in this process (i.e. imported + in this interpreter or in any other). Py_None stands in for + modules that haven't actually been imported in this interpreter. + + A module's index (PyModuleDef.m_base.m_index) is used to look up + the corresponding module object for this interpreter, if any. + (See PyState_FindModule().) When any extension module + is initialized during import, its moduledef gets initialized by + PyModuleDef_Init(), and the first time that happens for each + PyModuleDef, its index gets set to the current value of + a global counter (see _PyRuntimeState.imports.last_module_index). + The entry for that index in this interpreter remains unset until + the module is actually imported here. (Py_None is used as + a placeholder.) Note that multi-phase init modules always get + an index for which there will never be a module set. + + This is initialized lazily in PyState_AddModule(), which is also + where modules get added. */ + PyObject *modules_by_index; + /* importlib module._bootstrap */ + PyObject *importlib; + /* override for config->use_frozen_modules (for tests) + (-1: "off", 1: "on", 0: no override) */ + int override_frozen_modules; +#ifdef HAVE_DLOPEN + int dlopenflags; +#endif + PyObject *import_func; +}; + +#ifdef HAVE_DLOPEN +# include +# if HAVE_DECL_RTLD_NOW +# define _Py_DLOPEN_FLAGS RTLD_NOW +# else +# define _Py_DLOPEN_FLAGS RTLD_LAZY +# endif +# define DLOPENFLAGS_INIT .dlopenflags = _Py_DLOPEN_FLAGS, +#else +# define _Py_DLOPEN_FLAGS 0 +# define DLOPENFLAGS_INIT +#endif + +#define IMPORTS_INIT \ + { \ + .override_frozen_modules = 0, \ + DLOPENFLAGS_INIT \ + } + +extern void _PyImport_ClearCore(PyInterpreterState *interp); + +extern Py_ssize_t _PyImport_GetNextModuleIndex(void); +extern const char * _PyImport_ResolveNameWithPackageContext(const char *name); +extern const char * _PyImport_SwapPackageContext(const char *newcontext); + +extern int _PyImport_GetDLOpenFlags(PyInterpreterState *interp); +extern void _PyImport_SetDLOpenFlags(PyInterpreterState *interp, int new_val); + +extern PyObject * _PyImport_InitModules(PyInterpreterState *interp); +extern PyObject * _PyImport_GetModules(PyInterpreterState *interp); +extern void _PyImport_ClearModules(PyInterpreterState *interp); + +extern void _PyImport_ClearModulesByIndex(PyInterpreterState *interp); + +extern int _PyImport_InitDefaultImportFunc(PyInterpreterState *interp); +extern int _PyImport_IsDefaultImportFunc( + PyInterpreterState *interp, + PyObject *func); + +extern PyObject * _PyImport_GetImportlibLoader( + PyInterpreterState *interp, + const char *loader_name); +extern PyObject * _PyImport_GetImportlibExternalLoader( + PyInterpreterState *interp, + const char *loader_name); +extern PyObject * _PyImport_BlessMyLoader( + PyInterpreterState *interp, + PyObject *module_globals); +extern PyObject * _PyImport_ImportlibModuleRepr( + PyInterpreterState *interp, + PyObject *module); + + +extern PyStatus _PyImport_Init(void); +extern void _PyImport_Fini(void); +extern void _PyImport_Fini2(void); + +extern PyStatus _PyImport_InitCore( + PyThreadState *tstate, + PyObject *sysmod, + int importlib); +extern PyStatus _PyImport_InitExternal(PyThreadState *tstate); +extern void _PyImport_FiniCore(PyInterpreterState *interp); +extern void _PyImport_FiniExternal(PyInterpreterState *interp); + #ifdef HAVE_FORK extern PyStatus _PyImport_ReInitLock(void); #endif -extern PyObject* _PyImport_BootstrapImp(PyThreadState *tstate); + + +extern PyObject* _PyImport_GetBuiltinModuleNames(void); struct _module_alias { const char *name; /* ASCII encoded string */ diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 0e3d46852f2e6d..60de31b336f613 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -21,6 +21,7 @@ extern "C" { #include "pycore_function.h" // FUNC_MAX_WATCHERS #include "pycore_genobject.h" // struct _Py_async_gen_state #include "pycore_gc.h" // struct _gc_runtime_state +#include "pycore_import.h" // struct _import_state #include "pycore_list.h" // struct _Py_list_state #include "pycore_global_objects.h" // struct _Py_interp_static_objects #include "pycore_tuple.h" // struct _Py_tuple_state @@ -92,37 +93,12 @@ struct _is { struct _ceval_state ceval; struct _gc_runtime_state gc; - // sys.modules dictionary - PyObject *modules; - /* This is the list of module objects for all legacy (single-phase init) - extension modules ever loaded in this process (i.e. imported - in this interpreter or in any other). Py_None stands in for - modules that haven't actually been imported in this interpreter. - - A module's index (PyModuleDef.m_base.m_index) is used to look up - the corresponding module object for this interpreter, if any. - (See PyState_FindModule().) When any extension module - is initialized during import, its moduledef gets initialized by - PyModuleDef_Init(), and the first time that happens for each - PyModuleDef, its index gets set to the current value of - a global counter (see _PyRuntimeState.imports.last_module_index). - The entry for that index in this interpreter remains unset until - the module is actually imported here. (Py_None is used as - a placeholder.) Note that multi-phase init modules always get - an index for which there will never be a module set. - - This is initialized lazily in _PyState_AddModule(), which is also - where modules get added. */ - PyObject *modules_by_index; + struct _import_state imports; + // Dictionary of the sys module PyObject *sysdict; // Dictionary of the builtins module PyObject *builtins; - // importlib module - PyObject *importlib; - // override for config->use_frozen_modules (for tests) - // (-1: "off", 1: "on", 0: no override) - int override_frozen_modules; PyObject *codec_search_path; PyObject *codec_search_cache; @@ -130,15 +106,11 @@ struct _is { int codecs_initialized; PyConfig config; -#ifdef HAVE_DLOPEN - int dlopenflags; -#endif unsigned long feature_flags; PyObject *dict; /* Stores per-interpreter state */ PyObject *builtins_copy; - PyObject *import_func; // Initialized to _PyEval_EvalFrameDefault(). _PyFrameEvalFunction eval_frame; @@ -205,7 +177,6 @@ struct _is { /* other API */ -extern void _PyInterpreterState_ClearModules(PyInterpreterState *interp); extern void _PyInterpreterState_Clear(PyThreadState *tstate); diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h index 2d431befd74f99..e7a31807205254 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -30,7 +30,6 @@ PyAPI_FUNC(int) _Py_IsLocaleCoercionTarget(const char *ctype_loc); /* Various one-time initializers */ extern void _Py_InitVersion(void); -extern PyStatus _PyImport_Init(void); extern PyStatus _PyFaulthandler_Init(int enable); extern int _PyTraceMalloc_Init(int enable); extern PyObject * _PyBuiltin_Init(PyInterpreterState *interp); @@ -45,7 +44,6 @@ extern int _PyBuiltins_AddExceptions(PyObject * bltinmod); extern PyStatus _Py_HashRandomization_Init(const PyConfig *); extern PyStatus _PyTime_Init(void); -extern PyStatus _PyImportZip_Init(PyThreadState *tstate); extern PyStatus _PyGC_Init(PyInterpreterState *interp); extern PyStatus _PyAtExit_Init(PyInterpreterState *interp); extern int _Py_Deepfreeze_Init(void); @@ -55,8 +53,6 @@ extern int _Py_Deepfreeze_Init(void); extern int _PySignal_Init(int install_signal_handlers); extern void _PySignal_Fini(void); -extern void _PyImport_Fini(void); -extern void _PyImport_Fini2(void); extern void _PyGC_Fini(PyInterpreterState *interp); extern void _Py_HashRandomization_Fini(void); extern void _PyFaulthandler_Fini(void); diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 7046ec8d9adaaf..638b86253879ea 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -152,12 +152,6 @@ extern void _PySignal_AfterFork(void); #endif -PyAPI_FUNC(int) _PyState_AddModule( - PyThreadState *tstate, - PyObject* module, - PyModuleDef* def); - - PyAPI_FUNC(int) _PyOS_InterruptOccurred(PyThreadState *tstate); #define HEAD_LOCK(runtime) \ diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index c6a27d076eae2d..a8d5953ff98b0b 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -97,23 +97,10 @@ extern "C" { ._main_interpreter = _PyInterpreterState_INIT, \ } -#ifdef HAVE_DLOPEN -# include -# if HAVE_DECL_RTLD_NOW -# define _Py_DLOPEN_FLAGS RTLD_NOW -# else -# define _Py_DLOPEN_FLAGS RTLD_LAZY -# endif -# define DLOPENFLAGS_INIT .dlopenflags = _Py_DLOPEN_FLAGS, -#else -# define _Py_DLOPEN_FLAGS 0 -# define DLOPENFLAGS_INIT -#endif - #define _PyInterpreterState_INIT \ { \ .id_refcount = -1, \ - DLOPENFLAGS_INIT \ + .imports = IMPORTS_INIT, \ .ceval = { \ .recursion_limit = Py_DEFAULT_RECURSION_LIMIT, \ }, \ diff --git a/Include/internal/pycore_sysmodule.h b/Include/internal/pycore_sysmodule.h index 10d092cdc30a2c..b4b1febafa4479 100644 --- a/Include/internal/pycore_sysmodule.h +++ b/Include/internal/pycore_sysmodule.h @@ -20,6 +20,9 @@ extern void _PySys_ClearAuditHooks(PyThreadState *tstate); PyAPI_FUNC(int) _PySys_SetAttr(PyObject *, PyObject *); +extern int _PySys_ClearAttrString(PyInterpreterState *interp, + const char *name, int verbose); + #ifdef __cplusplus } #endif diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py index 31dce21587e2ca..c85ab92307de78 100644 --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -387,7 +387,7 @@ def check_with_reinit_reloaded(module, lookedup, initialized, check_basic_reloaded(mod, lookedup, initialized, init_count, before, reloaded) - # Currently _PyState_AddModule() always replaces the cached module. + # Currently PyState_AddModule() always replaces the cached module. self.assertIs(basic.look_up_self(), mod) self.assertEqual(basic.initialized_count(), expected_init_count) diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index e77c1c8409880d..7e50fbda2c07cb 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -864,7 +864,6 @@ def test_windows_feature_macros(self): "_PyObject_GC_Resize", "_PyObject_New", "_PyObject_NewVar", - "_PyState_AddModule", "_PyThreadState_Init", "_PyThreadState_Prealloc", "_PyWeakref_CallableProxyType", diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index 21ff9616133445..c04a3a228caf56 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -1684,9 +1684,6 @@ [function._PyObject_NewVar] added = '3.2' abi_only = true -[function._PyState_AddModule] - added = '3.2' - abi_only = true [function._PyThreadState_Init] added = '3.2' abi_only = true diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 24190e320ee6d6..a0be19a3ca8ac8 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -42,10 +42,9 @@ PyModuleDef_Init(PyModuleDef* def) { assert(PyModuleDef_Type.tp_flags & Py_TPFLAGS_READY); if (def->m_base.m_index == 0) { - _PyRuntime.imports.last_module_index++; Py_SET_REFCNT(def, 1); Py_SET_TYPE(def, &PyModuleDef_Type); - def->m_base.m_index = _PyRuntime.imports.last_module_index; + def->m_base.m_index = _PyImport_GetNextModuleIndex(); } return (PyObject*)def; } @@ -209,24 +208,7 @@ _PyModule_CreateInitialized(PyModuleDef* module, int module_api_version) "module %s: PyModule_Create is incompatible with m_slots", name); return NULL; } - /* Make sure name is fully qualified. - - This is a bit of a hack: when the shared library is loaded, - the module name is "package.module", but the module calls - PyModule_Create*() with just "module" for the name. The shared - library loader squirrels away the true name of the module in - _Py_PackageContext, and PyModule_Create*() will substitute this - (if the name actually matches). - */ -#define _Py_PackageContext (_PyRuntime.imports.pkgcontext) - if (_Py_PackageContext != NULL) { - const char *p = strrchr(_Py_PackageContext, '.'); - if (p != NULL && strcmp(module->m_name, p+1) == 0) { - name = _Py_PackageContext; - _Py_PackageContext = NULL; - } - } -#undef _Py_PackageContext + name = _PyImport_ResolveNameWithPackageContext(name); if ((m = (PyModuleObject*)PyModule_New(name)) == NULL) return NULL; @@ -710,8 +692,7 @@ static PyObject * module_repr(PyModuleObject *m) { PyInterpreterState *interp = _PyInterpreterState_GET(); - - return PyObject_CallMethod(interp->importlib, "_module_repr", "O", m); + return _PyImport_ImportlibModuleRepr(interp, (PyObject *)m); } /* Check if the "_initializing" attribute of the module spec is set to true. diff --git a/PC/python3dll.c b/PC/python3dll.c index e300819365756e..79f09037282f54 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -34,7 +34,6 @@ EXPORT_FUNC(_PyObject_GC_NewVar) EXPORT_FUNC(_PyObject_GC_Resize) EXPORT_FUNC(_PyObject_New) EXPORT_FUNC(_PyObject_NewVar) -EXPORT_FUNC(_PyState_AddModule) EXPORT_FUNC(_PyThreadState_Init) EXPORT_FUNC(_PyThreadState_Prealloc) EXPORT_FUNC(Py_AddPendingCall) diff --git a/Python/_warnings.c b/Python/_warnings.c index e78f21644f372b..d510381c365b66 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -214,7 +214,7 @@ get_warnings_attr(PyInterpreterState *interp, PyObject *attr, int try_import) gone, then we can't even use PyImport_GetModule without triggering an interpreter abort. */ - if (!interp->modules) { + if (!_PyImport_GetModules(interp)) { return NULL; } warnings_module = PyImport_GetModule(&_Py_ID(warnings)); @@ -1050,7 +1050,6 @@ warnings_warn_impl(PyObject *module, PyObject *message, PyObject *category, static PyObject * get_source_line(PyInterpreterState *interp, PyObject *module_globals, int lineno) { - PyObject *external; PyObject *loader; PyObject *module_name; PyObject *get_source; @@ -1059,13 +1058,7 @@ get_source_line(PyInterpreterState *interp, PyObject *module_globals, int lineno PyObject *source_line; /* stolen from import.c */ - external = PyObject_GetAttrString(interp->importlib, "_bootstrap_external"); - if (external == NULL) { - return NULL; - } - - loader = PyObject_CallMethod(external, "_bless_my_loader", "O", module_globals, NULL); - Py_DECREF(external); + loader = _PyImport_BlessMyLoader(interp, module_globals); if (loader == NULL) { return NULL; } diff --git a/Python/ceval.c b/Python/ceval.c index 611d62b0eba9af..09fd2f29266c87 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2688,7 +2688,7 @@ import_name(PyThreadState *tstate, _PyInterpreterFrame *frame, } PyObject *locals = frame->f_locals; /* Fast path for not overloaded __import__. */ - if (import_func == tstate->interp->import_func) { + if (_PyImport_IsDefaultImportFunc(tstate->interp, import_func)) { int ilevel = _PyLong_AsInt(level); if (ilevel == -1 && _PyErr_Occurred(tstate)) { return NULL; diff --git a/Python/dynload_shlib.c b/Python/dynload_shlib.c index 3c5fd83df584d5..6761bba457983b 100644 --- a/Python/dynload_shlib.c +++ b/Python/dynload_shlib.c @@ -75,7 +75,7 @@ _PyImport_FindSharedFuncptr(const char *prefix, return NULL; } - dlopenflags = _PyInterpreterState_GET()->dlopenflags; + dlopenflags = _PyImport_GetDLOpenFlags(_PyInterpreterState_GET()); handle = dlopen(pathname, dlopenflags); diff --git a/Python/import.c b/Python/import.c index 63ed2443657b29..ae27aaf56848d6 100644 --- a/Python/import.c +++ b/Python/import.c @@ -4,7 +4,7 @@ #include "pycore_import.h" // _PyImport_BootstrapImp() #include "pycore_initconfig.h" // _PyStatus_OK() -#include "pycore_interp.h" // _PyInterpreterState_ClearModules() +#include "pycore_interp.h" // struct _import_runtime_state #include "pycore_namespace.h" // _PyNamespace_Type #include "pycore_pyerrors.h" // _PyErr_SetString() #include "pycore_pyhash.h" // _Py_KeyedHash() @@ -24,8 +24,18 @@ extern "C" { #endif -/* Forward references */ -static PyObject *import_add_module(PyThreadState *tstate, PyObject *name); + +/*[clinic input] +module _imp +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=9c332475d8686284]*/ + +#include "clinic/import.c.h" + + +/*******************************/ +/* process-global import state */ +/*******************************/ /* This table is defined in config.c: */ extern struct _inittab _PyImport_Inittab[]; @@ -37,67 +47,51 @@ struct _inittab *PyImport_Inittab = _PyImport_Inittab; // we track the pointer here so we can deallocate it during finalization. static struct _inittab *inittab_copy = NULL; -/*[clinic input] -module _imp -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=9c332475d8686284]*/ - -#include "clinic/import.c.h" - -/* Initialize things */ -PyStatus -_PyImportZip_Init(PyThreadState *tstate) -{ - PyObject *path_hooks; - int err = 0; +/*******************************/ +/* runtime-global import state */ +/*******************************/ - path_hooks = PySys_GetObject("path_hooks"); - if (path_hooks == NULL) { - _PyErr_SetString(tstate, PyExc_RuntimeError, - "unable to get sys.path_hooks"); - goto error; - } +#define INITTAB _PyRuntime.imports.inittab +#define LAST_MODULE_INDEX _PyRuntime.imports.last_module_index +#define EXTENSIONS _PyRuntime.imports.extensions - int verbose = _PyInterpreterState_GetConfig(tstate->interp)->verbose; - if (verbose) { - PySys_WriteStderr("# installing zipimport hook\n"); - } +#define import_lock _PyRuntime.imports.lock.mutex +#define import_lock_thread _PyRuntime.imports.lock.thread +#define import_lock_level _PyRuntime.imports.lock.level - PyObject *zipimporter = _PyImport_GetModuleAttrString("zipimport", "zipimporter"); - if (zipimporter == NULL) { - _PyErr_Clear(tstate); /* No zipimporter object -- okay */ - if (verbose) { - PySys_WriteStderr("# can't import zipimport.zipimporter\n"); - } - } - else { - /* sys.path_hooks.insert(0, zipimporter) */ - err = PyList_Insert(path_hooks, 0, zipimporter); - Py_DECREF(zipimporter); - if (err < 0) { - goto error; - } - if (verbose) { - PySys_WriteStderr("# installed zipimport hook\n"); - } - } +#define FIND_AND_LOAD _PyRuntime.imports.find_and_load +#define PKGCONTEXT (_PyRuntime.imports.pkgcontext) + + +/*******************************/ +/* interpreter import state */ +/*******************************/ + +#define MODULES(interp) \ + (interp)->imports.modules +#define MODULES_BY_INDEX(interp) \ + (interp)->imports.modules_by_index +#define IMPORTLIB(interp) \ + (interp)->imports.importlib +#define OVERRIDE_FROZEN_MODULES(interp) \ + (interp)->imports.override_frozen_modules +#ifdef HAVE_DLOPEN +# define DLOPENFLAGS(interp) \ + (interp)->imports.dlopenflags +#endif +#define IMPORT_FUNC(interp) \ + (interp)->imports.import_func - return _PyStatus_OK(); - error: - PyErr_Print(); - return _PyStatus_ERR("initializing zipimport failed"); -} +/*******************/ +/* the import lock */ +/*******************/ /* Locking primitives to prevent parallel imports of the same module in different threads to return with a partially loaded module. These calls are serialized by the global interpreter lock. */ -#define import_lock _PyRuntime.imports.lock.mutex -#define import_lock_thread _PyRuntime.imports.lock.thread -#define import_lock_level _PyRuntime.imports.lock.level - void _PyImport_AcquireLock(void) { @@ -170,154 +164,45 @@ _PyImport_ReInitLock(void) } #endif -/*[clinic input] -_imp.lock_held - -Return True if the import lock is currently held, else False. - -On platforms without threads, return False. -[clinic start generated code]*/ - -static PyObject * -_imp_lock_held_impl(PyObject *module) -/*[clinic end generated code: output=8b89384b5e1963fc input=9b088f9b217d9bdf]*/ -{ - return PyBool_FromLong(import_lock_thread != PYTHREAD_INVALID_THREAD_ID); -} - -/*[clinic input] -_imp.acquire_lock - -Acquires the interpreter's import lock for the current thread. - -This lock should be used by import hooks to ensure thread-safety when importing -modules. On platforms without threads, this function does nothing. -[clinic start generated code]*/ - -static PyObject * -_imp_acquire_lock_impl(PyObject *module) -/*[clinic end generated code: output=1aff58cb0ee1b026 input=4a2d4381866d5fdc]*/ -{ - _PyImport_AcquireLock(); - Py_RETURN_NONE; -} - -/*[clinic input] -_imp.release_lock - -Release the interpreter's import lock. -On platforms without threads, this function does nothing. -[clinic start generated code]*/ +/***************/ +/* sys.modules */ +/***************/ -static PyObject * -_imp_release_lock_impl(PyObject *module) -/*[clinic end generated code: output=7faab6d0be178b0a input=934fb11516dd778b]*/ +PyObject * +_PyImport_InitModules(PyInterpreterState *interp) { - if (_PyImport_ReleaseLock() < 0) { - PyErr_SetString(PyExc_RuntimeError, - "not holding the import lock"); + assert(MODULES(interp) == NULL); + MODULES(interp) = PyDict_New(); + if (MODULES(interp) == NULL) { return NULL; } - Py_RETURN_NONE; -} - -PyStatus -_PyImport_Init(void) -{ - if (_PyRuntime.imports.inittab != NULL) { - return _PyStatus_ERR("global import state already initialized"); - } - PyStatus status = _PyStatus_OK(); - - size_t size; - for (size = 0; PyImport_Inittab[size].name != NULL; size++) - ; - size++; - - /* Force default raw memory allocator to get a known allocator to be able - to release the memory in _PyImport_Fini() */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - /* Make the copy. */ - struct _inittab *copied = PyMem_RawMalloc(size * sizeof(struct _inittab)); - if (copied == NULL) { - status = PyStatus_NoMemory(); - goto done; - } - memcpy(copied, PyImport_Inittab, size * sizeof(struct _inittab)); - _PyRuntime.imports.inittab = copied; - -done: - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - return status; + return MODULES(interp); } -static inline void _extensions_cache_clear(void); - -void -_PyImport_Fini(void) +PyObject * +_PyImport_GetModules(PyInterpreterState *interp) { - _extensions_cache_clear(); - if (import_lock != NULL) { - PyThread_free_lock(import_lock); - import_lock = NULL; - } - - /* Use the same memory allocator as _PyImport_Init(). */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - /* Free memory allocated by _PyImport_Init() */ - struct _inittab *inittab = _PyRuntime.imports.inittab; - _PyRuntime.imports.inittab = NULL; - PyMem_RawFree(inittab); - - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + return MODULES(interp); } void -_PyImport_Fini2(void) +_PyImport_ClearModules(PyInterpreterState *interp) { - /* Use the same memory allocator than PyImport_ExtendInittab(). */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - // Reset PyImport_Inittab - PyImport_Inittab = _PyImport_Inittab; - - /* Free memory allocated by PyImport_ExtendInittab() */ - PyMem_RawFree(inittab_copy); - inittab_copy = NULL; - - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + Py_SETREF(MODULES(interp), NULL); } -/* Helper for sys */ - PyObject * PyImport_GetModuleDict(void) { PyInterpreterState *interp = _PyInterpreterState_GET(); - if (interp->modules == NULL) { + if (MODULES(interp) == NULL) { Py_FatalError("interpreter has no modules dictionary"); } - return interp->modules; -} - -/* In some corner cases it is important to be sure that the import - machinery has been initialized (or not cleaned up yet). For - example, see issue #4236 and PyModule_Create2(). */ - -int -_PyImport_IsInitialized(PyInterpreterState *interp) -{ - if (interp->modules == NULL) - return 0; - return 1; + return MODULES(interp); } +// This is only kept around for extensions that use _Py_IDENTIFIER. PyObject * _PyImport_GetModuleId(_Py_Identifier *nameid) { @@ -332,7 +217,7 @@ int _PyImport_SetModule(PyObject *name, PyObject *m) { PyInterpreterState *interp = _PyInterpreterState_GET(); - PyObject *modules = interp->modules; + PyObject *modules = MODULES(interp); return PyObject_SetItem(modules, name, m); } @@ -340,14 +225,14 @@ int _PyImport_SetModuleString(const char *name, PyObject *m) { PyInterpreterState *interp = _PyInterpreterState_GET(); - PyObject *modules = interp->modules; + PyObject *modules = MODULES(interp); return PyMapping_SetItemString(modules, name, m); } static PyObject * import_get_module(PyThreadState *tstate, PyObject *name) { - PyObject *modules = tstate->interp->modules; + PyObject *modules = MODULES(tstate->interp); if (modules == NULL) { _PyErr_SetString(tstate, PyExc_RuntimeError, "unable to get sys.modules"); @@ -370,7 +255,6 @@ import_get_module(PyThreadState *tstate, PyObject *name) return m; } - static int import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *name) { @@ -387,7 +271,7 @@ import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *n if (busy) { /* Wait until module is done importing. */ PyObject *value = _PyObject_CallMethodOneArg( - interp->importlib, &_Py_ID(_lock_unlock_module), name); + IMPORTLIB(interp), &_Py_ID(_lock_unlock_module), name); if (value == NULL) { return -1; } @@ -396,47 +280,381 @@ import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *n return 0; } +static void remove_importlib_frames(PyThreadState *tstate); -/* Helper for pythonrun.c -- return magic number and tag. */ - -long -PyImport_GetMagicNumber(void) +PyObject * +PyImport_GetModule(PyObject *name) { - long res; - PyInterpreterState *interp = _PyInterpreterState_GET(); - PyObject *external, *pyc_magic; + PyThreadState *tstate = _PyThreadState_GET(); + PyObject *mod; - external = PyObject_GetAttrString(interp->importlib, "_bootstrap_external"); - if (external == NULL) - return -1; - pyc_magic = PyObject_GetAttrString(external, "_RAW_MAGIC_NUMBER"); - Py_DECREF(external); - if (pyc_magic == NULL) - return -1; - res = PyLong_AsLong(pyc_magic); - Py_DECREF(pyc_magic); - return res; + mod = import_get_module(tstate, name); + if (mod != NULL && mod != Py_None) { + if (import_ensure_initialized(tstate->interp, mod, name) < 0) { + Py_DECREF(mod); + remove_importlib_frames(tstate); + return NULL; + } + } + return mod; } +/* Get the module object corresponding to a module name. + First check the modules dictionary if there's one there, + if not, create a new one and insert it in the modules dictionary. */ -extern const char * _PySys_ImplCacheTag; - -const char * -PyImport_GetMagicTag(void) +static PyObject * +import_add_module(PyThreadState *tstate, PyObject *name) { - return _PySys_ImplCacheTag; -} + PyObject *modules = MODULES(tstate->interp); + if (modules == NULL) { + _PyErr_SetString(tstate, PyExc_RuntimeError, + "no import module dictionary"); + return NULL; + } + PyObject *m; + if (PyDict_CheckExact(modules)) { + m = Py_XNewRef(PyDict_GetItemWithError(modules, name)); + } + else { + m = PyObject_GetItem(modules, name); + // For backward-compatibility we copy the behavior + // of PyDict_GetItemWithError(). + if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { + _PyErr_Clear(tstate); + } + } + if (_PyErr_Occurred(tstate)) { + return NULL; + } + if (m != NULL && PyModule_Check(m)) { + return m; + } + Py_XDECREF(m); + m = PyModule_NewObject(name); + if (m == NULL) + return NULL; + if (PyObject_SetItem(modules, name, m) != 0) { + Py_DECREF(m); + return NULL; + } -/* -We support a number of kinds of single-phase init builtin/extension modules: + return m; +} -* "basic" - * no module state (PyModuleDef.m_size == -1) - * does not support repeated init (we use PyModuleDef.m_base.m_copy) - * may have process-global state - * the module's def is cached in _PyRuntime.imports.extensions, - by (name, filename) +PyObject * +PyImport_AddModuleObject(PyObject *name) +{ + PyThreadState *tstate = _PyThreadState_GET(); + PyObject *mod = import_add_module(tstate, name); + if (mod) { + PyObject *ref = PyWeakref_NewRef(mod, NULL); + Py_DECREF(mod); + if (ref == NULL) { + return NULL; + } + mod = PyWeakref_GetObject(ref); + Py_DECREF(ref); + } + return mod; /* borrowed reference */ +} + + +PyObject * +PyImport_AddModule(const char *name) +{ + PyObject *nameobj = PyUnicode_FromString(name); + if (nameobj == NULL) { + return NULL; + } + PyObject *module = PyImport_AddModuleObject(nameobj); + Py_DECREF(nameobj); + return module; +} + + +/* Remove name from sys.modules, if it's there. + * Can be called with an exception raised. + * If fail to remove name a new exception will be chained with the old + * exception, otherwise the old exception is preserved. + */ +static void +remove_module(PyThreadState *tstate, PyObject *name) +{ + PyObject *type, *value, *traceback; + _PyErr_Fetch(tstate, &type, &value, &traceback); + + PyObject *modules = MODULES(tstate->interp); + if (PyDict_CheckExact(modules)) { + PyObject *mod = _PyDict_Pop(modules, name, Py_None); + Py_XDECREF(mod); + } + else if (PyMapping_DelItem(modules, name) < 0) { + if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { + _PyErr_Clear(tstate); + } + } + + _PyErr_ChainExceptions(type, value, traceback); +} + + +/************************************/ +/* per-interpreter modules-by-index */ +/************************************/ + +Py_ssize_t +_PyImport_GetNextModuleIndex(void) +{ + LAST_MODULE_INDEX++; + return LAST_MODULE_INDEX; +} + +static const char * +_modules_by_index_check(PyInterpreterState *interp, Py_ssize_t index) +{ + if (index == 0) { + return "invalid module index"; + } + if (MODULES_BY_INDEX(interp) == NULL) { + return "Interpreters module-list not accessible."; + } + if (index > PyList_GET_SIZE(MODULES_BY_INDEX(interp))) { + return "Module index out of bounds."; + } + return NULL; +} + +static PyObject * +_modules_by_index_get(PyInterpreterState *interp, PyModuleDef *def) +{ + Py_ssize_t index = def->m_base.m_index; + if (_modules_by_index_check(interp, index) != NULL) { + return NULL; + } + PyObject *res = PyList_GET_ITEM(MODULES_BY_INDEX(interp), index); + return res==Py_None ? NULL : res; +} + +static int +_modules_by_index_set(PyInterpreterState *interp, + PyModuleDef *def, PyObject *module) +{ + assert(def != NULL); + assert(def->m_slots == NULL); + assert(def->m_base.m_index > 0); + + if (MODULES_BY_INDEX(interp) == NULL) { + MODULES_BY_INDEX(interp) = PyList_New(0); + if (MODULES_BY_INDEX(interp) == NULL) { + return -1; + } + } + + Py_ssize_t index = def->m_base.m_index; + while (PyList_GET_SIZE(MODULES_BY_INDEX(interp)) <= index) { + if (PyList_Append(MODULES_BY_INDEX(interp), Py_None) < 0) { + return -1; + } + } + + return PyList_SetItem(MODULES_BY_INDEX(interp), index, Py_NewRef(module)); +} + +static int +_modules_by_index_clear(PyInterpreterState *interp, PyModuleDef *def) +{ + Py_ssize_t index = def->m_base.m_index; + const char *err = _modules_by_index_check(interp, index); + if (err != NULL) { + Py_FatalError(err); + return -1; + } + return PyList_SetItem(MODULES_BY_INDEX(interp), index, Py_NewRef(Py_None)); +} + + +PyObject* +PyState_FindModule(PyModuleDef* module) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (module->m_slots) { + return NULL; + } + return _modules_by_index_get(interp, module); +} + +int +PyState_AddModule(PyObject* module, PyModuleDef* def) +{ + if (!def) { + Py_FatalError("module definition is NULL"); + return -1; + } + + PyThreadState *tstate = _PyThreadState_GET(); + if (def->m_slots) { + _PyErr_SetString(tstate, + PyExc_SystemError, + "PyState_AddModule called on module with slots"); + return -1; + } + + PyInterpreterState *interp = tstate->interp; + Py_ssize_t index = def->m_base.m_index; + if (MODULES_BY_INDEX(interp) && + index < PyList_GET_SIZE(MODULES_BY_INDEX(interp)) && + module == PyList_GET_ITEM(MODULES_BY_INDEX(interp), index)) + { + _Py_FatalErrorFormat(__func__, "module %p already added", module); + return -1; + } + + return _modules_by_index_set(interp, def, module); +} + +int +PyState_RemoveModule(PyModuleDef* def) +{ + PyThreadState *tstate = _PyThreadState_GET(); + if (def->m_slots) { + _PyErr_SetString(tstate, + PyExc_SystemError, + "PyState_RemoveModule called on module with slots"); + return -1; + } + return _modules_by_index_clear(tstate->interp, def); +} + + +// Used by finalize_modules() +void +_PyImport_ClearModulesByIndex(PyInterpreterState *interp) +{ + if (!MODULES_BY_INDEX(interp)) { + return; + } + + Py_ssize_t i; + for (i = 0; i < PyList_GET_SIZE(MODULES_BY_INDEX(interp)); i++) { + PyObject *m = PyList_GET_ITEM(MODULES_BY_INDEX(interp), i); + if (PyModule_Check(m)) { + /* cleanup the saved copy of module dicts */ + PyModuleDef *md = PyModule_GetDef(m); + if (md) { + Py_CLEAR(md->m_base.m_copy); + } + } + } + + /* Setting modules_by_index to NULL could be dangerous, so we + clear the list instead. */ + if (PyList_SetSlice(MODULES_BY_INDEX(interp), + 0, PyList_GET_SIZE(MODULES_BY_INDEX(interp)), + NULL)) { + PyErr_WriteUnraisable(MODULES_BY_INDEX(interp)); + } +} + + +/*********************/ +/* extension modules */ +/*********************/ + +/* Make sure name is fully qualified. + + This is a bit of a hack: when the shared library is loaded, + the module name is "package.module", but the module calls + PyModule_Create*() with just "module" for the name. The shared + library loader squirrels away the true name of the module in + _PyRuntime.imports.pkgcontext, and PyModule_Create*() will + substitute this (if the name actually matches). +*/ +const char * +_PyImport_ResolveNameWithPackageContext(const char *name) +{ + if (PKGCONTEXT != NULL) { + const char *p = strrchr(PKGCONTEXT, '.'); + if (p != NULL && strcmp(name, p+1) == 0) { + name = PKGCONTEXT; + PKGCONTEXT = NULL; + } + } + return name; +} + +const char * +_PyImport_SwapPackageContext(const char *newcontext) +{ + const char *oldcontext = PKGCONTEXT; + PKGCONTEXT = newcontext; + return oldcontext; +} + +#ifdef HAVE_DLOPEN +int +_PyImport_GetDLOpenFlags(PyInterpreterState *interp) +{ + return DLOPENFLAGS(interp); +} + +void +_PyImport_SetDLOpenFlags(PyInterpreterState *interp, int new_val) +{ + DLOPENFLAGS(interp) = new_val; +} +#endif // HAVE_DLOPEN + + +/* Common implementation for _imp.exec_dynamic and _imp.exec_builtin */ +static int +exec_builtin_or_dynamic(PyObject *mod) { + PyModuleDef *def; + void *state; + + if (!PyModule_Check(mod)) { + return 0; + } + + def = PyModule_GetDef(mod); + if (def == NULL) { + return 0; + } + + state = PyModule_GetState(mod); + if (state) { + /* Already initialized; skip reload */ + return 0; + } + + return PyModule_ExecDef(mod, def); +} + + +/*******************/ + +#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) +#include +EM_JS(PyObject*, _PyImport_InitFunc_TrampolineCall, (PyModInitFunction func), { + return wasmTable.get(func)(); +}); +#endif // __EMSCRIPTEN__ && PY_CALL_TRAMPOLINE + + +/*****************************/ +/* single-phase init modules */ +/*****************************/ + +/* +We support a number of kinds of single-phase init builtin/extension modules: + +* "basic" + * no module state (PyModuleDef.m_size == -1) + * does not support repeated init (we use PyModuleDef.m_base.m_copy) + * may have process-global state + * the module's def is cached in _PyRuntime.imports.extensions, + by (name, filename) * "reinit" * no module state (PyModuleDef.m_size == 0) * supports repeated init (m_copy is never used) @@ -512,7 +730,7 @@ gets even messier. static PyModuleDef * _extensions_cache_get(PyObject *filename, PyObject *name) { - PyObject *extensions = _PyRuntime.imports.extensions; + PyObject *extensions = EXTENSIONS; if (extensions == NULL) { return NULL; } @@ -528,13 +746,13 @@ _extensions_cache_get(PyObject *filename, PyObject *name) static int _extensions_cache_set(PyObject *filename, PyObject *name, PyModuleDef *def) { - PyObject *extensions = _PyRuntime.imports.extensions; + PyObject *extensions = EXTENSIONS; if (extensions == NULL) { extensions = PyDict_New(); if (extensions == NULL) { return -1; } - _PyRuntime.imports.extensions = extensions; + EXTENSIONS = extensions; } PyObject *key = PyTuple_Pack(2, filename, name); if (key == NULL) { @@ -551,7 +769,7 @@ _extensions_cache_set(PyObject *filename, PyObject *name, PyModuleDef *def) static void _extensions_cache_clear(void) { - Py_CLEAR(_PyRuntime.imports.extensions); + Py_CLEAR(EXTENSIONS); } static int @@ -569,7 +787,7 @@ fix_up_extension(PyObject *mod, PyObject *name, PyObject *filename) } PyThreadState *tstate = _PyThreadState_GET(); - if (_PyState_AddModule(tstate, mod, def) < 0) { + if (_modules_by_index_set(tstate->interp, def, mod) < 0) { return -1; } @@ -616,40 +834,19 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, return 0; } -int -_PyImport_FixupBuiltin(PyObject *mod, const char *name, PyObject *modules) -{ - int res = -1; - PyObject *nameobj; - nameobj = PyUnicode_InternFromString(name); - if (nameobj == NULL) { - return -1; - } - if (PyObject_SetItem(modules, nameobj, mod) < 0) { - goto finally; - } - if (fix_up_extension(mod, nameobj, nameobj) < 0) { - PyMapping_DelItem(modules, nameobj); - goto finally; - } - res = 0; - -finally: - Py_DECREF(nameobj); - return res; -} static PyObject * import_find_extension(PyThreadState *tstate, PyObject *name, PyObject *filename) { + /* Only single-phase init modules will be in the cache. */ PyModuleDef *def = _extensions_cache_get(filename, name); if (def == NULL) { return NULL; } PyObject *mod, *mdict; - PyObject *modules = tstate->interp->modules; + PyObject *modules = MODULES(tstate->interp); if (def->m_size == -1) { /* Module does not support repeated initialization */ @@ -679,7 +876,7 @@ import_find_extension(PyThreadState *tstate, PyObject *name, return NULL; } } - if (_PyState_AddModule(tstate, mod, def) < 0) { + if (_modules_by_index_set(tstate->interp, def, mod) < 0) { PyMapping_DelItem(modules, name); Py_DECREF(mod); return NULL; @@ -694,107 +891,268 @@ import_find_extension(PyThreadState *tstate, PyObject *name, } -/* Get the module object corresponding to a module name. - First check the modules dictionary if there's one there, - if not, create a new one and insert it in the modules dictionary. */ +/*******************/ +/* builtin modules */ +/*******************/ -static PyObject * -import_add_module(PyThreadState *tstate, PyObject *name) +int +_PyImport_FixupBuiltin(PyObject *mod, const char *name, PyObject *modules) { - PyObject *modules = tstate->interp->modules; - if (modules == NULL) { - _PyErr_SetString(tstate, PyExc_RuntimeError, - "no import module dictionary"); - return NULL; + int res = -1; + PyObject *nameobj; + nameobj = PyUnicode_InternFromString(name); + if (nameobj == NULL) { + return -1; } - - PyObject *m; - if (PyDict_CheckExact(modules)) { - m = Py_XNewRef(PyDict_GetItemWithError(modules, name)); + if (PyObject_SetItem(modules, nameobj, mod) < 0) { + goto finally; } - else { - m = PyObject_GetItem(modules, name); - // For backward-compatibility we copy the behavior - // of PyDict_GetItemWithError(). - if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { - _PyErr_Clear(tstate); - } + if (fix_up_extension(mod, nameobj, nameobj) < 0) { + PyMapping_DelItem(modules, nameobj); + goto finally; } - if (_PyErr_Occurred(tstate)) { - return NULL; + res = 0; + +finally: + Py_DECREF(nameobj); + return res; +} + +/* Helper to test for built-in module */ + +static int +is_builtin(PyObject *name) +{ + int i; + struct _inittab *inittab = INITTAB; + for (i = 0; inittab[i].name != NULL; i++) { + if (_PyUnicode_EqualToASCIIString(name, inittab[i].name)) { + if (inittab[i].initfunc == NULL) + return -1; + else + return 1; + } } - if (m != NULL && PyModule_Check(m)) { - return m; + return 0; +} + +static PyObject* +create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) +{ + PyObject *mod = import_find_extension(tstate, name, name); + if (mod || _PyErr_Occurred(tstate)) { + return mod; } - Py_XDECREF(m); - m = PyModule_NewObject(name); - if (m == NULL) - return NULL; - if (PyObject_SetItem(modules, name, m) != 0) { - Py_DECREF(m); - return NULL; + + PyObject *modules = MODULES(tstate->interp); + for (struct _inittab *p = INITTAB; p->name != NULL; p++) { + if (_PyUnicode_EqualToASCIIString(name, p->name)) { + if (p->initfunc == NULL) { + /* Cannot re-init internal module ("sys" or "builtins") */ + mod = PyImport_AddModuleObject(name); + return Py_XNewRef(mod); + } + mod = _PyImport_InitFunc_TrampolineCall(*p->initfunc); + if (mod == NULL) { + return NULL; + } + + if (PyObject_TypeCheck(mod, &PyModuleDef_Type)) { + return PyModule_FromDefAndSpec((PyModuleDef*)mod, spec); + } + else { + /* Remember pointer to module init function. */ + PyModuleDef *def = PyModule_GetDef(mod); + if (def == NULL) { + return NULL; + } + + def->m_base.m_init = p->initfunc; + if (_PyImport_FixupExtensionObject(mod, name, name, + modules) < 0) { + return NULL; + } + return mod; + } + } } - return m; + // not found + Py_RETURN_NONE; } -PyObject * -PyImport_AddModuleObject(PyObject *name) + +/*****************************/ +/* the builtin modules table */ +/*****************************/ + +/* API for embedding applications that want to add their own entries + to the table of built-in modules. This should normally be called + *before* Py_Initialize(). When the table resize fails, -1 is + returned and the existing table is unchanged. + + After a similar function by Just van Rossum. */ + +int +PyImport_ExtendInittab(struct _inittab *newtab) { - PyThreadState *tstate = _PyThreadState_GET(); - PyObject *mod = import_add_module(tstate, name); - if (mod) { - PyObject *ref = PyWeakref_NewRef(mod, NULL); - Py_DECREF(mod); - if (ref == NULL) { - return NULL; - } - mod = PyWeakref_GetObject(ref); - Py_DECREF(ref); + struct _inittab *p; + size_t i, n; + int res = 0; + + if (INITTAB != NULL) { + Py_FatalError("PyImport_ExtendInittab() may not be called after Py_Initialize()"); } - return mod; /* borrowed reference */ + + /* Count the number of entries in both tables */ + for (n = 0; newtab[n].name != NULL; n++) + ; + if (n == 0) + return 0; /* Nothing to do */ + for (i = 0; PyImport_Inittab[i].name != NULL; i++) + ; + + /* Force default raw memory allocator to get a known allocator to be able + to release the memory in _PyImport_Fini2() */ + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + /* Allocate new memory for the combined table */ + p = NULL; + if (i + n <= SIZE_MAX / sizeof(struct _inittab) - 1) { + size_t size = sizeof(struct _inittab) * (i + n + 1); + p = PyMem_RawRealloc(inittab_copy, size); + } + if (p == NULL) { + res = -1; + goto done; + } + + /* Copy the tables into the new memory at the first call + to PyImport_ExtendInittab(). */ + if (inittab_copy != PyImport_Inittab) { + memcpy(p, PyImport_Inittab, (i+1) * sizeof(struct _inittab)); + } + memcpy(p + i, newtab, (n + 1) * sizeof(struct _inittab)); + PyImport_Inittab = inittab_copy = p; + +done: + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + return res; } +/* Shorthand to add a single entry given a name and a function */ -PyObject * -PyImport_AddModule(const char *name) +int +PyImport_AppendInittab(const char *name, PyObject* (*initfunc)(void)) { - PyObject *nameobj = PyUnicode_FromString(name); - if (nameobj == NULL) { - return NULL; + struct _inittab newtab[2]; + + if (INITTAB != NULL) { + Py_FatalError("PyImport_AppendInittab() may not be called after Py_Initialize()"); } - PyObject *module = PyImport_AddModuleObject(nameobj); - Py_DECREF(nameobj); - return module; + + memset(newtab, '\0', sizeof newtab); + + newtab[0].name = name; + newtab[0].initfunc = initfunc; + + return PyImport_ExtendInittab(newtab); } -/* Remove name from sys.modules, if it's there. - * Can be called with an exception raised. - * If fail to remove name a new exception will be chained with the old - * exception, otherwise the old exception is preserved. - */ +/* the internal table */ + +static int +init_builtin_modules_table(void) +{ + size_t size; + for (size = 0; PyImport_Inittab[size].name != NULL; size++) + ; + size++; + + /* Make the copy. */ + struct _inittab *copied = PyMem_RawMalloc(size * sizeof(struct _inittab)); + if (copied == NULL) { + return -1; + } + memcpy(copied, PyImport_Inittab, size * sizeof(struct _inittab)); + INITTAB = copied; + return 0; +} + static void -remove_module(PyThreadState *tstate, PyObject *name) +fini_builtin_modules_table(void) { - PyObject *type, *value, *traceback; - _PyErr_Fetch(tstate, &type, &value, &traceback); + struct _inittab *inittab = INITTAB; + INITTAB = NULL; + PyMem_RawFree(inittab); +} - PyObject *modules = tstate->interp->modules; - if (PyDict_CheckExact(modules)) { - PyObject *mod = _PyDict_Pop(modules, name, Py_None); - Py_XDECREF(mod); +PyObject * +_PyImport_GetBuiltinModuleNames(void) +{ + PyObject *list = PyList_New(0); + if (list == NULL) { + return NULL; } - else if (PyMapping_DelItem(modules, name) < 0) { - if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { - _PyErr_Clear(tstate); + struct _inittab *inittab = INITTAB; + for (Py_ssize_t i = 0; inittab[i].name != NULL; i++) { + PyObject *name = PyUnicode_FromString(inittab[i].name); + if (name == NULL) { + Py_DECREF(list); + return NULL; } + if (PyList_Append(list, name) < 0) { + Py_DECREF(name); + Py_DECREF(list); + return NULL; + } + Py_DECREF(name); } + return list; +} - _PyErr_ChainExceptions(type, value, traceback); + +/********************/ +/* the magic number */ +/********************/ + +/* Helper for pythonrun.c -- return magic number and tag. */ + +long +PyImport_GetMagicNumber(void) +{ + long res; + PyInterpreterState *interp = _PyInterpreterState_GET(); + PyObject *external, *pyc_magic; + + external = PyObject_GetAttrString(IMPORTLIB(interp), "_bootstrap_external"); + if (external == NULL) + return -1; + pyc_magic = PyObject_GetAttrString(external, "_RAW_MAGIC_NUMBER"); + Py_DECREF(external); + if (pyc_magic == NULL) + return -1; + res = PyLong_AsLong(pyc_magic); + Py_DECREF(pyc_magic); + return res; +} + + +extern const char * _PySys_ImplCacheTag; + +const char * +PyImport_GetMagicTag(void) +{ + return _PySys_ImplCacheTag; } +/*********************************/ +/* a Python module's code object */ +/*********************************/ + /* Execute a code object in a module and return the module object * WITH INCREMENTED REFERENCE COUNT. If an error occurs, name is * removed from sys.modules, to avoid leaving damaged module objects @@ -851,7 +1209,7 @@ PyImport_ExecCodeModuleWithPathnames(const char *name, PyObject *co, Py_FatalError("no current interpreter"); } - external= PyObject_GetAttrString(interp->importlib, + external= PyObject_GetAttrString(IMPORTLIB(interp), "_bootstrap_external"); if (external != NULL) { pathobj = _PyObject_CallMethodOneArg( @@ -936,7 +1294,7 @@ PyImport_ExecCodeModuleObject(PyObject *name, PyObject *co, PyObject *pathname, if (pathname == NULL) { pathname = ((PyCodeObject *)co)->co_filename; } - external = PyObject_GetAttrString(tstate->interp->importlib, + external = PyObject_GetAttrString(IMPORTLIB(tstate->interp), "_bootstrap_external"); if (external == NULL) { Py_DECREF(d); @@ -989,204 +1347,10 @@ update_compiled_module(PyCodeObject *co, PyObject *newname) Py_DECREF(oldname); } -/*[clinic input] -_imp._fix_co_filename - - code: object(type="PyCodeObject *", subclass_of="&PyCode_Type") - Code object to change. - - path: unicode - File path to use. - / - -Changes code.co_filename to specify the passed-in file path. -[clinic start generated code]*/ - -static PyObject * -_imp__fix_co_filename_impl(PyObject *module, PyCodeObject *code, - PyObject *path) -/*[clinic end generated code: output=1d002f100235587d input=895ba50e78b82f05]*/ - -{ - update_compiled_module(code, path); - - Py_RETURN_NONE; -} - - -/* Helper to test for built-in module */ - -static int -is_builtin(PyObject *name) -{ - int i; - struct _inittab *inittab = _PyRuntime.imports.inittab; - for (i = 0; inittab[i].name != NULL; i++) { - if (_PyUnicode_EqualToASCIIString(name, inittab[i].name)) { - if (inittab[i].initfunc == NULL) - return -1; - else - return 1; - } - } - return 0; -} - - -/* Return a finder object for a sys.path/pkg.__path__ item 'p', - possibly by fetching it from the path_importer_cache dict. If it - wasn't yet cached, traverse path_hooks until a hook is found - that can handle the path item. Return None if no hook could; - this tells our caller that the path based finder could not find - a finder for this path item. Cache the result in - path_importer_cache. */ - -static PyObject * -get_path_importer(PyThreadState *tstate, PyObject *path_importer_cache, - PyObject *path_hooks, PyObject *p) -{ - PyObject *importer; - Py_ssize_t j, nhooks; - - /* These conditions are the caller's responsibility: */ - assert(PyList_Check(path_hooks)); - assert(PyDict_Check(path_importer_cache)); - - nhooks = PyList_Size(path_hooks); - if (nhooks < 0) - return NULL; /* Shouldn't happen */ - - importer = PyDict_GetItemWithError(path_importer_cache, p); - if (importer != NULL || _PyErr_Occurred(tstate)) { - return Py_XNewRef(importer); - } - - /* set path_importer_cache[p] to None to avoid recursion */ - if (PyDict_SetItem(path_importer_cache, p, Py_None) != 0) - return NULL; - - for (j = 0; j < nhooks; j++) { - PyObject *hook = PyList_GetItem(path_hooks, j); - if (hook == NULL) - return NULL; - importer = PyObject_CallOneArg(hook, p); - if (importer != NULL) - break; - - if (!_PyErr_ExceptionMatches(tstate, PyExc_ImportError)) { - return NULL; - } - _PyErr_Clear(tstate); - } - if (importer == NULL) { - Py_RETURN_NONE; - } - if (PyDict_SetItem(path_importer_cache, p, importer) < 0) { - Py_DECREF(importer); - return NULL; - } - return importer; -} - -PyObject * -PyImport_GetImporter(PyObject *path) -{ - PyThreadState *tstate = _PyThreadState_GET(); - PyObject *path_importer_cache = PySys_GetObject("path_importer_cache"); - PyObject *path_hooks = PySys_GetObject("path_hooks"); - if (path_importer_cache == NULL || path_hooks == NULL) { - return NULL; - } - return get_path_importer(tstate, path_importer_cache, path_hooks, path); -} - -#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) -#include -EM_JS(PyObject*, _PyImport_InitFunc_TrampolineCall, (PyModInitFunction func), { - return wasmTable.get(func)(); -}); -#endif // __EMSCRIPTEN__ && PY_CALL_TRAMPOLINE - -static PyObject* -create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) -{ - PyObject *mod = import_find_extension(tstate, name, name); - if (mod || _PyErr_Occurred(tstate)) { - return mod; - } - - PyObject *modules = tstate->interp->modules; - for (struct _inittab *p = _PyRuntime.imports.inittab; p->name != NULL; p++) { - if (_PyUnicode_EqualToASCIIString(name, p->name)) { - if (p->initfunc == NULL) { - /* Cannot re-init internal module ("sys" or "builtins") */ - mod = PyImport_AddModuleObject(name); - return Py_XNewRef(mod); - } - mod = _PyImport_InitFunc_TrampolineCall(*p->initfunc); - if (mod == NULL) { - return NULL; - } - - if (PyObject_TypeCheck(mod, &PyModuleDef_Type)) { - return PyModule_FromDefAndSpec((PyModuleDef*)mod, spec); - } - else { - /* Remember pointer to module init function. */ - PyModuleDef *def = PyModule_GetDef(mod); - if (def == NULL) { - return NULL; - } - - def->m_base.m_init = p->initfunc; - if (_PyImport_FixupExtensionObject(mod, name, name, - modules) < 0) { - return NULL; - } - return mod; - } - } - } - - // not found - Py_RETURN_NONE; -} - - - -/*[clinic input] -_imp.create_builtin - - spec: object - / - -Create an extension module. -[clinic start generated code]*/ - -static PyObject * -_imp_create_builtin(PyObject *module, PyObject *spec) -/*[clinic end generated code: output=ace7ff22271e6f39 input=37f966f890384e47]*/ -{ - PyThreadState *tstate = _PyThreadState_GET(); - - PyObject *name = PyObject_GetAttrString(spec, "name"); - if (name == NULL) { - return NULL; - } - - if (!PyUnicode_Check(name)) { - PyErr_Format(PyExc_TypeError, - "name must be string, not %.200s", - Py_TYPE(name)->tp_name); - Py_DECREF(name); - return NULL; - } - - PyObject *mod = create_builtin(tstate, name, spec); - Py_DECREF(name); - return mod; -} +/******************/ +/* frozen modules */ +/******************/ /* Return true if the name is an alias. In that case, "alias" is set to the original module name. If it is an alias but the original @@ -1210,14 +1374,11 @@ resolve_module_alias(const char *name, const struct _module_alias *aliases, } } - -/* Frozen modules */ - static bool use_frozen(void) { PyInterpreterState *interp = _PyInterpreterState_GET(); - int override = interp->override_frozen_modules; + int override = OVERRIDE_FROZEN_MODULES(interp); if (override > 0) { return true; } @@ -1587,54 +1748,315 @@ PyImport_ImportFrozenModule(const char *name) } -/* Import a module, either built-in, frozen, or external, and return - its module object WITH INCREMENTED REFERENCE COUNT */ +/*************/ +/* importlib */ +/*************/ -PyObject * -PyImport_ImportModule(const char *name) +/* Import the _imp extension by calling manually _imp.create_builtin() and + _imp.exec_builtin() since importlib is not initialized yet. Initializing + importlib requires the _imp module: this function fix the bootstrap issue. + */ +static PyObject* +bootstrap_imp(PyThreadState *tstate) { - PyObject *pname; - PyObject *result; - - pname = PyUnicode_FromString(name); - if (pname == NULL) + PyObject *name = PyUnicode_FromString("_imp"); + if (name == NULL) { return NULL; - result = PyImport_Import(pname); - Py_DECREF(pname); - return result; -} + } + // Mock a ModuleSpec object just good enough for PyModule_FromDefAndSpec(): + // an object with just a name attribute. + // + // _imp.__spec__ is overridden by importlib._bootstrap._instal() anyway. + PyObject *attrs = Py_BuildValue("{sO}", "name", name); + if (attrs == NULL) { + goto error; + } + PyObject *spec = _PyNamespace_New(attrs); + Py_DECREF(attrs); + if (spec == NULL) { + goto error; + } -/* Import a module without blocking - * - * At first it tries to fetch the module from sys.modules. If the module was - * never loaded before it loads it with PyImport_ImportModule() unless another - * thread holds the import lock. In the latter case the function raises an - * ImportError instead of blocking. - * - * Returns the module object with incremented ref count. - */ -PyObject * -PyImport_ImportModuleNoBlock(const char *name) -{ - return PyImport_ImportModule(name); + // Create the _imp module from its definition. + PyObject *mod = create_builtin(tstate, name, spec); + Py_CLEAR(name); + Py_DECREF(spec); + if (mod == NULL) { + goto error; + } + assert(mod != Py_None); // not found + + // Execute the _imp module: call imp_module_exec(). + if (exec_builtin_or_dynamic(mod) < 0) { + Py_DECREF(mod); + goto error; + } + return mod; + +error: + Py_XDECREF(name); + return NULL; } +/* Global initializations. Can be undone by Py_FinalizeEx(). Don't + call this twice without an intervening Py_FinalizeEx() call. When + initializations fail, a fatal error is issued and the function does + not return. On return, the first thread and interpreter state have + been created. -/* Remove importlib frames from the traceback, - * except in Verbose mode. */ -static void -remove_importlib_frames(PyThreadState *tstate) + Locking: you must hold the interpreter lock while calling this. + (If the lock has not yet been initialized, that's equivalent to + having the lock, but you cannot use multiple threads.) + +*/ +static int +init_importlib(PyThreadState *tstate, PyObject *sysmod) { - const char *importlib_filename = ""; - const char *external_filename = ""; - const char *remove_frames = "_call_with_frames_removed"; - int always_trim = 0; - int in_importlib = 0; - PyObject *exception, *value, *base_tb, *tb; - PyObject **prev_link, **outer_link = NULL; + assert(!_PyErr_Occurred(tstate)); - /* Synopsis: if it's an ImportError, we trim all importlib chunks + PyInterpreterState *interp = tstate->interp; + int verbose = _PyInterpreterState_GetConfig(interp)->verbose; + + // Import _importlib through its frozen version, _frozen_importlib. + if (verbose) { + PySys_FormatStderr("import _frozen_importlib # frozen\n"); + } + if (PyImport_ImportFrozenModule("_frozen_importlib") <= 0) { + return -1; + } + PyObject *importlib = PyImport_AddModule("_frozen_importlib"); // borrowed + if (importlib == NULL) { + return -1; + } + IMPORTLIB(interp) = Py_NewRef(importlib); + + // Import the _imp module + if (verbose) { + PySys_FormatStderr("import _imp # builtin\n"); + } + PyObject *imp_mod = bootstrap_imp(tstate); + if (imp_mod == NULL) { + return -1; + } + if (_PyImport_SetModuleString("_imp", imp_mod) < 0) { + Py_DECREF(imp_mod); + return -1; + } + + // Install importlib as the implementation of import + PyObject *value = PyObject_CallMethod(importlib, "_install", + "OO", sysmod, imp_mod); + Py_DECREF(imp_mod); + if (value == NULL) { + return -1; + } + Py_DECREF(value); + + assert(!_PyErr_Occurred(tstate)); + return 0; +} + + +static int +init_importlib_external(PyInterpreterState *interp) +{ + PyObject *value; + value = PyObject_CallMethod(IMPORTLIB(interp), + "_install_external_importers", ""); + if (value == NULL) { + return -1; + } + Py_DECREF(value); + return 0; +} + +PyObject * +_PyImport_GetImportlibLoader(PyInterpreterState *interp, + const char *loader_name) +{ + return PyObject_GetAttrString(IMPORTLIB(interp), loader_name); +} + +PyObject * +_PyImport_GetImportlibExternalLoader(PyInterpreterState *interp, + const char *loader_name) +{ + PyObject *bootstrap = PyObject_GetAttrString(IMPORTLIB(interp), + "_bootstrap_external"); + if (bootstrap == NULL) { + return NULL; + } + + PyObject *loader_type = PyObject_GetAttrString(bootstrap, loader_name); + Py_DECREF(bootstrap); + return loader_type; +} + +PyObject * +_PyImport_BlessMyLoader(PyInterpreterState *interp, PyObject *module_globals) +{ + PyObject *external = PyObject_GetAttrString(IMPORTLIB(interp), + "_bootstrap_external"); + if (external == NULL) { + return NULL; + } + + PyObject *loader = PyObject_CallMethod(external, "_bless_my_loader", + "O", module_globals, NULL); + Py_DECREF(external); + return loader; +} + +PyObject * +_PyImport_ImportlibModuleRepr(PyInterpreterState *interp, PyObject *m) +{ + return PyObject_CallMethod(IMPORTLIB(interp), "_module_repr", "O", m); +} + + +/*******************/ + +/* Return a finder object for a sys.path/pkg.__path__ item 'p', + possibly by fetching it from the path_importer_cache dict. If it + wasn't yet cached, traverse path_hooks until a hook is found + that can handle the path item. Return None if no hook could; + this tells our caller that the path based finder could not find + a finder for this path item. Cache the result in + path_importer_cache. */ + +static PyObject * +get_path_importer(PyThreadState *tstate, PyObject *path_importer_cache, + PyObject *path_hooks, PyObject *p) +{ + PyObject *importer; + Py_ssize_t j, nhooks; + + /* These conditions are the caller's responsibility: */ + assert(PyList_Check(path_hooks)); + assert(PyDict_Check(path_importer_cache)); + + nhooks = PyList_Size(path_hooks); + if (nhooks < 0) + return NULL; /* Shouldn't happen */ + + importer = PyDict_GetItemWithError(path_importer_cache, p); + if (importer != NULL || _PyErr_Occurred(tstate)) { + return Py_XNewRef(importer); + } + + /* set path_importer_cache[p] to None to avoid recursion */ + if (PyDict_SetItem(path_importer_cache, p, Py_None) != 0) + return NULL; + + for (j = 0; j < nhooks; j++) { + PyObject *hook = PyList_GetItem(path_hooks, j); + if (hook == NULL) + return NULL; + importer = PyObject_CallOneArg(hook, p); + if (importer != NULL) + break; + + if (!_PyErr_ExceptionMatches(tstate, PyExc_ImportError)) { + return NULL; + } + _PyErr_Clear(tstate); + } + if (importer == NULL) { + Py_RETURN_NONE; + } + if (PyDict_SetItem(path_importer_cache, p, importer) < 0) { + Py_DECREF(importer); + return NULL; + } + return importer; +} + +PyObject * +PyImport_GetImporter(PyObject *path) +{ + PyThreadState *tstate = _PyThreadState_GET(); + PyObject *path_importer_cache = PySys_GetObject("path_importer_cache"); + PyObject *path_hooks = PySys_GetObject("path_hooks"); + if (path_importer_cache == NULL || path_hooks == NULL) { + return NULL; + } + return get_path_importer(tstate, path_importer_cache, path_hooks, path); +} + + +/*********************/ +/* importing modules */ +/*********************/ + +int +_PyImport_InitDefaultImportFunc(PyInterpreterState *interp) +{ + // Get the __import__ function + PyObject *import_func = _PyDict_GetItemStringWithError(interp->builtins, + "__import__"); + if (import_func == NULL) { + return -1; + } + IMPORT_FUNC(interp) = Py_NewRef(import_func); + return 0; +} + +int +_PyImport_IsDefaultImportFunc(PyInterpreterState *interp, PyObject *func) +{ + return func == IMPORT_FUNC(interp); +} + + +/* Import a module, either built-in, frozen, or external, and return + its module object WITH INCREMENTED REFERENCE COUNT */ + +PyObject * +PyImport_ImportModule(const char *name) +{ + PyObject *pname; + PyObject *result; + + pname = PyUnicode_FromString(name); + if (pname == NULL) + return NULL; + result = PyImport_Import(pname); + Py_DECREF(pname); + return result; +} + + +/* Import a module without blocking + * + * At first it tries to fetch the module from sys.modules. If the module was + * never loaded before it loads it with PyImport_ImportModule() unless another + * thread holds the import lock. In the latter case the function raises an + * ImportError instead of blocking. + * + * Returns the module object with incremented ref count. + */ +PyObject * +PyImport_ImportModuleNoBlock(const char *name) +{ + return PyImport_ImportModule(name); +} + + +/* Remove importlib frames from the traceback, + * except in Verbose mode. */ +static void +remove_importlib_frames(PyThreadState *tstate) +{ + const char *importlib_filename = ""; + const char *external_filename = ""; + const char *remove_frames = "_call_with_frames_removed"; + int always_trim = 0; + int in_importlib = 0; + PyObject *exception, *value, *base_tb, *tb; + PyObject **prev_link, **outer_link = NULL; + + /* Synopsis: if it's an ImportError, we trim all importlib chunks from the traceback. We always trim chunks which end with a call to "_call_with_frames_removed". */ @@ -1851,8 +2273,8 @@ import_find_and_load(PyThreadState *tstate, PyObject *abs_name) PyObject *mod = NULL; PyInterpreterState *interp = tstate->interp; int import_time = _PyInterpreterState_GetConfig(interp)->import_time; -#define import_level _PyRuntime.imports.find_and_load.import_level -#define accumulated _PyRuntime.imports.find_and_load.accumulated +#define import_level FIND_AND_LOAD.import_level +#define accumulated FIND_AND_LOAD.accumulated _PyTime_t t1 = 0, accumulated_copy = accumulated; @@ -1873,7 +2295,7 @@ import_find_and_load(PyThreadState *tstate, PyObject *abs_name) * _PyDict_GetItemIdWithError(). */ if (import_time) { -#define header _PyRuntime.imports.find_and_load.header +#define header FIND_AND_LOAD.header if (header) { fputs("import time: self [us] | cumulative | imported package\n", stderr); @@ -1889,8 +2311,8 @@ import_find_and_load(PyThreadState *tstate, PyObject *abs_name) if (PyDTrace_IMPORT_FIND_LOAD_START_ENABLED()) PyDTrace_IMPORT_FIND_LOAD_START(PyUnicode_AsUTF8(abs_name)); - mod = PyObject_CallMethodObjArgs(interp->importlib, &_Py_ID(_find_and_load), - abs_name, interp->import_func, NULL); + mod = PyObject_CallMethodObjArgs(IMPORTLIB(interp), &_Py_ID(_find_and_load), + abs_name, IMPORT_FUNC(interp), NULL); if (PyDTrace_IMPORT_FIND_LOAD_DONE_ENABLED()) PyDTrace_IMPORT_FIND_LOAD_DONE(PyUnicode_AsUTF8(abs_name), @@ -1913,23 +2335,6 @@ import_find_and_load(PyThreadState *tstate, PyObject *abs_name) #undef accumulated } -PyObject * -PyImport_GetModule(PyObject *name) -{ - PyThreadState *tstate = _PyThreadState_GET(); - PyObject *mod; - - mod = import_get_module(tstate, name); - if (mod != NULL && mod != Py_None) { - if (import_ensure_initialized(tstate->interp, mod, name) < 0) { - Py_DECREF(mod); - remove_importlib_frames(tstate); - return NULL; - } - } - return mod; -} - PyObject * PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, PyObject *locals, PyObject *fromlist, @@ -2059,8 +2464,8 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, if (path) { Py_DECREF(path); final_mod = PyObject_CallMethodObjArgs( - interp->importlib, &_Py_ID(_handle_fromlist), - mod, fromlist, interp->import_func, NULL); + IMPORTLIB(interp), &_Py_ID(_handle_fromlist), + mod, fromlist, IMPORT_FUNC(interp), NULL); } else { final_mod = Py_NewRef(mod); @@ -2129,72 +2534,429 @@ PyImport_ReloadModule(PyObject *m) PyObject * PyImport_Import(PyObject *module_name) { - PyThreadState *tstate = _PyThreadState_GET(); - PyObject *globals = NULL; - PyObject *import = NULL; - PyObject *builtins = NULL; - PyObject *r = NULL; + PyThreadState *tstate = _PyThreadState_GET(); + PyObject *globals = NULL; + PyObject *import = NULL; + PyObject *builtins = NULL; + PyObject *r = NULL; + + PyObject *from_list = PyList_New(0); + if (from_list == NULL) { + goto err; + } + + /* Get the builtins from current globals */ + globals = PyEval_GetGlobals(); + if (globals != NULL) { + Py_INCREF(globals); + builtins = PyObject_GetItem(globals, &_Py_ID(__builtins__)); + if (builtins == NULL) + goto err; + } + else { + /* No globals -- use standard builtins, and fake globals */ + builtins = PyImport_ImportModuleLevel("builtins", + NULL, NULL, NULL, 0); + if (builtins == NULL) { + goto err; + } + globals = Py_BuildValue("{OO}", &_Py_ID(__builtins__), builtins); + if (globals == NULL) + goto err; + } + + /* Get the __import__ function from the builtins */ + if (PyDict_Check(builtins)) { + import = PyObject_GetItem(builtins, &_Py_ID(__import__)); + if (import == NULL) { + _PyErr_SetObject(tstate, PyExc_KeyError, &_Py_ID(__import__)); + } + } + else + import = PyObject_GetAttr(builtins, &_Py_ID(__import__)); + if (import == NULL) + goto err; + + /* Call the __import__ function with the proper argument list + Always use absolute import here. + Calling for side-effect of import. */ + r = PyObject_CallFunction(import, "OOOOi", module_name, globals, + globals, from_list, 0, NULL); + if (r == NULL) + goto err; + Py_DECREF(r); + + r = import_get_module(tstate, module_name); + if (r == NULL && !_PyErr_Occurred(tstate)) { + _PyErr_SetObject(tstate, PyExc_KeyError, module_name); + } + + err: + Py_XDECREF(globals); + Py_XDECREF(builtins); + Py_XDECREF(import); + Py_XDECREF(from_list); + + return r; +} + + +/*********************/ +/* runtime lifecycle */ +/*********************/ + +PyStatus +_PyImport_Init(void) +{ + if (INITTAB != NULL) { + return _PyStatus_ERR("global import state already initialized"); + } + + PyStatus status = _PyStatus_OK(); + + /* Force default raw memory allocator to get a known allocator to be able + to release the memory in _PyImport_Fini() */ + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + if (init_builtin_modules_table() != 0) { + status = PyStatus_NoMemory(); + goto done; + } + +done: + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + return status; +} + +void +_PyImport_Fini(void) +{ + /* Destroy the database used by _PyImport_{Fixup,Find}Extension */ + _extensions_cache_clear(); + if (import_lock != NULL) { + PyThread_free_lock(import_lock); + import_lock = NULL; + } + + /* Use the same memory allocator as _PyImport_Init(). */ + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + /* Free memory allocated by _PyImport_Init() */ + fini_builtin_modules_table(); + + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); +} + +void +_PyImport_Fini2(void) +{ + /* Use the same memory allocator than PyImport_ExtendInittab(). */ + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + // Reset PyImport_Inittab + PyImport_Inittab = _PyImport_Inittab; + + /* Free memory allocated by PyImport_ExtendInittab() */ + PyMem_RawFree(inittab_copy); + inittab_copy = NULL; + + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); +} + + +/*************************/ +/* interpreter lifecycle */ +/*************************/ + +PyStatus +_PyImport_InitCore(PyThreadState *tstate, PyObject *sysmod, int importlib) +{ + // XXX Initialize here: interp->modules and interp->import_func. + // XXX Initialize here: sys.modules and sys.meta_path. + + if (importlib) { + /* This call sets up builtin and frozen import support */ + if (init_importlib(tstate, sysmod) < 0) { + return _PyStatus_ERR("failed to initialize importlib"); + } + } + + return _PyStatus_OK(); +} + +/* In some corner cases it is important to be sure that the import + machinery has been initialized (or not cleaned up yet). For + example, see issue #4236 and PyModule_Create2(). */ + +int +_PyImport_IsInitialized(PyInterpreterState *interp) +{ + if (MODULES(interp) == NULL) + return 0; + return 1; +} + +/* Clear the direct per-interpreter import state, if not cleared already. */ +void +_PyImport_ClearCore(PyInterpreterState *interp) +{ + /* interp->modules should have been cleaned up and cleared already + by _PyImport_FiniCore(). */ + Py_CLEAR(MODULES(interp)); + Py_CLEAR(MODULES_BY_INDEX(interp)); + Py_CLEAR(IMPORTLIB(interp)); + Py_CLEAR(IMPORT_FUNC(interp)); +} + +void +_PyImport_FiniCore(PyInterpreterState *interp) +{ + int verbose = _PyInterpreterState_GetConfig(interp)->verbose; + + if (_PySys_ClearAttrString(interp, "meta_path", verbose) < 0) { + PyErr_WriteUnraisable(NULL); + } + + // XXX Pull in most of finalize_modules() in pylifecycle.c. + + if (_PySys_ClearAttrString(interp, "modules", verbose) < 0) { + PyErr_WriteUnraisable(NULL); + } + + _PyImport_ClearCore(interp); +} + +// XXX Add something like _PyImport_Disable() for use early in interp fini? + + +/* "external" imports */ + +static int +init_zipimport(PyThreadState *tstate, int verbose) +{ + PyObject *path_hooks = PySys_GetObject("path_hooks"); + if (path_hooks == NULL) { + _PyErr_SetString(tstate, PyExc_RuntimeError, + "unable to get sys.path_hooks"); + return -1; + } + + if (verbose) { + PySys_WriteStderr("# installing zipimport hook\n"); + } + + PyObject *zipimporter = _PyImport_GetModuleAttrString("zipimport", "zipimporter"); + if (zipimporter == NULL) { + _PyErr_Clear(tstate); /* No zipimporter object -- okay */ + if (verbose) { + PySys_WriteStderr("# can't import zipimport.zipimporter\n"); + } + } + else { + /* sys.path_hooks.insert(0, zipimporter) */ + int err = PyList_Insert(path_hooks, 0, zipimporter); + Py_DECREF(zipimporter); + if (err < 0) { + return -1; + } + if (verbose) { + PySys_WriteStderr("# installed zipimport hook\n"); + } + } + + return 0; +} + +PyStatus +_PyImport_InitExternal(PyThreadState *tstate) +{ + int verbose = _PyInterpreterState_GetConfig(tstate->interp)->verbose; + + // XXX Initialize here: sys.path_hooks and sys.path_importer_cache. + + if (init_importlib_external(tstate->interp) != 0) { + _PyErr_Print(tstate); + return _PyStatus_ERR("external importer setup failed"); + } + + if (init_zipimport(tstate, verbose) != 0) { + PyErr_Print(); + return _PyStatus_ERR("initializing zipimport failed"); + } + + return _PyStatus_OK(); +} + +void +_PyImport_FiniExternal(PyInterpreterState *interp) +{ + int verbose = _PyInterpreterState_GetConfig(interp)->verbose; + + // XXX Uninstall importlib metapath importers here? + + if (_PySys_ClearAttrString(interp, "path_importer_cache", verbose) < 0) { + PyErr_WriteUnraisable(NULL); + } + if (_PySys_ClearAttrString(interp, "path_hooks", verbose) < 0) { + PyErr_WriteUnraisable(NULL); + } +} + + +/******************/ +/* module helpers */ +/******************/ + +PyObject * +_PyImport_GetModuleAttr(PyObject *modname, PyObject *attrname) +{ + PyObject *mod = PyImport_Import(modname); + if (mod == NULL) { + return NULL; + } + PyObject *result = PyObject_GetAttr(mod, attrname); + Py_DECREF(mod); + return result; +} + +PyObject * +_PyImport_GetModuleAttrString(const char *modname, const char *attrname) +{ + PyObject *pmodname = PyUnicode_FromString(modname); + if (pmodname == NULL) { + return NULL; + } + PyObject *pattrname = PyUnicode_FromString(attrname); + if (pattrname == NULL) { + Py_DECREF(pmodname); + return NULL; + } + PyObject *result = _PyImport_GetModuleAttr(pmodname, pattrname); + Py_DECREF(pattrname); + Py_DECREF(pmodname); + return result; +} + + +/**************/ +/* the module */ +/**************/ + +/*[clinic input] +_imp.lock_held + +Return True if the import lock is currently held, else False. + +On platforms without threads, return False. +[clinic start generated code]*/ + +static PyObject * +_imp_lock_held_impl(PyObject *module) +/*[clinic end generated code: output=8b89384b5e1963fc input=9b088f9b217d9bdf]*/ +{ + return PyBool_FromLong(import_lock_thread != PYTHREAD_INVALID_THREAD_ID); +} + +/*[clinic input] +_imp.acquire_lock + +Acquires the interpreter's import lock for the current thread. + +This lock should be used by import hooks to ensure thread-safety when importing +modules. On platforms without threads, this function does nothing. +[clinic start generated code]*/ + +static PyObject * +_imp_acquire_lock_impl(PyObject *module) +/*[clinic end generated code: output=1aff58cb0ee1b026 input=4a2d4381866d5fdc]*/ +{ + _PyImport_AcquireLock(); + Py_RETURN_NONE; +} + +/*[clinic input] +_imp.release_lock + +Release the interpreter's import lock. + +On platforms without threads, this function does nothing. +[clinic start generated code]*/ + +static PyObject * +_imp_release_lock_impl(PyObject *module) +/*[clinic end generated code: output=7faab6d0be178b0a input=934fb11516dd778b]*/ +{ + if (_PyImport_ReleaseLock() < 0) { + PyErr_SetString(PyExc_RuntimeError, + "not holding the import lock"); + return NULL; + } + Py_RETURN_NONE; +} + + +/*[clinic input] +_imp._fix_co_filename + + code: object(type="PyCodeObject *", subclass_of="&PyCode_Type") + Code object to change. + + path: unicode + File path to use. + / + +Changes code.co_filename to specify the passed-in file path. +[clinic start generated code]*/ + +static PyObject * +_imp__fix_co_filename_impl(PyObject *module, PyCodeObject *code, + PyObject *path) +/*[clinic end generated code: output=1d002f100235587d input=895ba50e78b82f05]*/ + +{ + update_compiled_module(code, path); - PyObject *from_list = PyList_New(0); - if (from_list == NULL) { - goto err; - } + Py_RETURN_NONE; +} - /* Get the builtins from current globals */ - globals = PyEval_GetGlobals(); - if (globals != NULL) { - Py_INCREF(globals); - builtins = PyObject_GetItem(globals, &_Py_ID(__builtins__)); - if (builtins == NULL) - goto err; - } - else { - /* No globals -- use standard builtins, and fake globals */ - builtins = PyImport_ImportModuleLevel("builtins", - NULL, NULL, NULL, 0); - if (builtins == NULL) { - goto err; - } - globals = Py_BuildValue("{OO}", &_Py_ID(__builtins__), builtins); - if (globals == NULL) - goto err; - } - /* Get the __import__ function from the builtins */ - if (PyDict_Check(builtins)) { - import = PyObject_GetItem(builtins, &_Py_ID(__import__)); - if (import == NULL) { - _PyErr_SetObject(tstate, PyExc_KeyError, &_Py_ID(__import__)); - } - } - else - import = PyObject_GetAttr(builtins, &_Py_ID(__import__)); - if (import == NULL) - goto err; +/*[clinic input] +_imp.create_builtin - /* Call the __import__ function with the proper argument list - Always use absolute import here. - Calling for side-effect of import. */ - r = PyObject_CallFunction(import, "OOOOi", module_name, globals, - globals, from_list, 0, NULL); - if (r == NULL) - goto err; - Py_DECREF(r); + spec: object + / - r = import_get_module(tstate, module_name); - if (r == NULL && !_PyErr_Occurred(tstate)) { - _PyErr_SetObject(tstate, PyExc_KeyError, module_name); +Create an extension module. +[clinic start generated code]*/ + +static PyObject * +_imp_create_builtin(PyObject *module, PyObject *spec) +/*[clinic end generated code: output=ace7ff22271e6f39 input=37f966f890384e47]*/ +{ + PyThreadState *tstate = _PyThreadState_GET(); + + PyObject *name = PyObject_GetAttrString(spec, "name"); + if (name == NULL) { + return NULL; } - err: - Py_XDECREF(globals); - Py_XDECREF(builtins); - Py_XDECREF(import); - Py_XDECREF(from_list); + if (!PyUnicode_Check(name)) { + PyErr_Format(PyExc_TypeError, + "name must be string, not %.200s", + Py_TYPE(name)->tp_name); + Py_DECREF(name); + return NULL; + } - return r; + PyObject *mod = create_builtin(tstate, name, spec); + Py_DECREF(name); + return mod; } + /*[clinic input] _imp.extension_suffixes @@ -2459,34 +3221,10 @@ _imp__override_frozen_modules_for_tests_impl(PyObject *module, int override) /*[clinic end generated code: output=36d5cb1594160811 input=8f1f95a3ef21aec3]*/ { PyInterpreterState *interp = _PyInterpreterState_GET(); - interp->override_frozen_modules = override; + OVERRIDE_FROZEN_MODULES(interp) = override; Py_RETURN_NONE; } -/* Common implementation for _imp.exec_dynamic and _imp.exec_builtin */ -static int -exec_builtin_or_dynamic(PyObject *mod) { - PyModuleDef *def; - void *state; - - if (!PyModule_Check(mod)) { - return 0; - } - - def = PyModule_GetDef(mod); - if (def == NULL) { - return 0; - } - - state = PyModule_GetState(mod); - if (state) { - /* Already initialized; skip reload */ - return 0; - } - - return PyModule_ExecDef(mod, def); -} - #ifdef HAVE_DYNAMIC_LOADING /*[clinic input] @@ -2674,158 +3412,6 @@ PyInit__imp(void) } -// Import the _imp extension by calling manually _imp.create_builtin() and -// _imp.exec_builtin() since importlib is not initialized yet. Initializing -// importlib requires the _imp module: this function fix the bootstrap issue. -PyObject* -_PyImport_BootstrapImp(PyThreadState *tstate) -{ - PyObject *name = PyUnicode_FromString("_imp"); - if (name == NULL) { - return NULL; - } - - // Mock a ModuleSpec object just good enough for PyModule_FromDefAndSpec(): - // an object with just a name attribute. - // - // _imp.__spec__ is overridden by importlib._bootstrap._instal() anyway. - PyObject *attrs = Py_BuildValue("{sO}", "name", name); - if (attrs == NULL) { - goto error; - } - PyObject *spec = _PyNamespace_New(attrs); - Py_DECREF(attrs); - if (spec == NULL) { - goto error; - } - - // Create the _imp module from its definition. - PyObject *mod = create_builtin(tstate, name, spec); - Py_CLEAR(name); - Py_DECREF(spec); - if (mod == NULL) { - goto error; - } - assert(mod != Py_None); // not found - - // Execute the _imp module: call imp_module_exec(). - if (exec_builtin_or_dynamic(mod) < 0) { - Py_DECREF(mod); - goto error; - } - return mod; - -error: - Py_XDECREF(name); - return NULL; -} - - -/* API for embedding applications that want to add their own entries - to the table of built-in modules. This should normally be called - *before* Py_Initialize(). When the table resize fails, -1 is - returned and the existing table is unchanged. - - After a similar function by Just van Rossum. */ - -int -PyImport_ExtendInittab(struct _inittab *newtab) -{ - struct _inittab *p; - size_t i, n; - int res = 0; - - if (_PyRuntime.imports.inittab != NULL) { - Py_FatalError("PyImport_ExtendInittab() may not be called after Py_Initialize()"); - } - - /* Count the number of entries in both tables */ - for (n = 0; newtab[n].name != NULL; n++) - ; - if (n == 0) - return 0; /* Nothing to do */ - for (i = 0; PyImport_Inittab[i].name != NULL; i++) - ; - - /* Force default raw memory allocator to get a known allocator to be able - to release the memory in _PyImport_Fini2() */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - /* Allocate new memory for the combined table */ - p = NULL; - if (i + n <= SIZE_MAX / sizeof(struct _inittab) - 1) { - size_t size = sizeof(struct _inittab) * (i + n + 1); - p = PyMem_RawRealloc(inittab_copy, size); - } - if (p == NULL) { - res = -1; - goto done; - } - - /* Copy the tables into the new memory at the first call - to PyImport_ExtendInittab(). */ - if (inittab_copy != PyImport_Inittab) { - memcpy(p, PyImport_Inittab, (i+1) * sizeof(struct _inittab)); - } - memcpy(p + i, newtab, (n + 1) * sizeof(struct _inittab)); - PyImport_Inittab = inittab_copy = p; - -done: - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - return res; -} - -/* Shorthand to add a single entry given a name and a function */ - -int -PyImport_AppendInittab(const char *name, PyObject* (*initfunc)(void)) -{ - struct _inittab newtab[2]; - - if (_PyRuntime.imports.inittab != NULL) { - Py_FatalError("PyImport_AppendInittab() may not be called after Py_Initialize()"); - } - - memset(newtab, '\0', sizeof newtab); - - newtab[0].name = name; - newtab[0].initfunc = initfunc; - - return PyImport_ExtendInittab(newtab); -} - - -PyObject * -_PyImport_GetModuleAttr(PyObject *modname, PyObject *attrname) -{ - PyObject *mod = PyImport_Import(modname); - if (mod == NULL) { - return NULL; - } - PyObject *result = PyObject_GetAttr(mod, attrname); - Py_DECREF(mod); - return result; -} - -PyObject * -_PyImport_GetModuleAttrString(const char *modname, const char *attrname) -{ - PyObject *pmodname = PyUnicode_FromString(modname); - if (pmodname == NULL) { - return NULL; - } - PyObject *pattrname = PyUnicode_FromString(attrname); - if (pattrname == NULL) { - Py_DECREF(pmodname); - return NULL; - } - PyObject *result = _PyImport_GetModuleAttr(pmodname, pattrname); - Py_DECREF(pattrname); - Py_DECREF(pmodname); - return result; -} - #ifdef __cplusplus } #endif diff --git a/Python/importdl.c b/Python/importdl.c index 91fa06f49c2897..6dafb4541486e9 100644 --- a/Python/importdl.c +++ b/Python/importdl.c @@ -99,7 +99,7 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) #endif PyObject *name_unicode = NULL, *name = NULL, *path = NULL, *m = NULL; const char *name_buf, *hook_prefix; - const char *oldcontext; + const char *oldcontext, *newcontext; dl_funcptr exportfunc; PyModuleDef *def; PyModInitFunction p0; @@ -113,6 +113,10 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) "spec.name must be a string"); goto error; } + newcontext = PyUnicode_AsUTF8(name_unicode); + if (newcontext == NULL) { + goto error; + } name = get_encoded_name(name_unicode, &hook_prefix); if (name == NULL) { @@ -160,16 +164,9 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) p0 = (PyModInitFunction)exportfunc; /* Package context is needed for single-phase init */ -#define _Py_PackageContext (_PyRuntime.imports.pkgcontext) - oldcontext = _Py_PackageContext; - _Py_PackageContext = PyUnicode_AsUTF8(name_unicode); - if (_Py_PackageContext == NULL) { - _Py_PackageContext = oldcontext; - goto error; - } + oldcontext = _PyImport_SwapPackageContext(newcontext); m = _PyImport_InitFunc_TrampolineCall(p0); - _Py_PackageContext = oldcontext; -#undef _Py_PackageContext + _PyImport_SwapPackageContext(oldcontext); if (m == NULL) { if (!PyErr_Occurred()) { diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 045a2996e8988b..281035dafa9577 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -156,79 +156,6 @@ Py_IsInitialized(void) } -/* Global initializations. Can be undone by Py_FinalizeEx(). Don't - call this twice without an intervening Py_FinalizeEx() call. When - initializations fail, a fatal error is issued and the function does - not return. On return, the first thread and interpreter state have - been created. - - Locking: you must hold the interpreter lock while calling this. - (If the lock has not yet been initialized, that's equivalent to - having the lock, but you cannot use multiple threads.) - -*/ -static int -init_importlib(PyThreadState *tstate, PyObject *sysmod) -{ - assert(!_PyErr_Occurred(tstate)); - - PyInterpreterState *interp = tstate->interp; - int verbose = _PyInterpreterState_GetConfig(interp)->verbose; - - // Import _importlib through its frozen version, _frozen_importlib. - if (verbose) { - PySys_FormatStderr("import _frozen_importlib # frozen\n"); - } - if (PyImport_ImportFrozenModule("_frozen_importlib") <= 0) { - return -1; - } - PyObject *importlib = PyImport_AddModule("_frozen_importlib"); // borrowed - if (importlib == NULL) { - return -1; - } - interp->importlib = Py_NewRef(importlib); - - // Import the _imp module - if (verbose) { - PySys_FormatStderr("import _imp # builtin\n"); - } - PyObject *imp_mod = _PyImport_BootstrapImp(tstate); - if (imp_mod == NULL) { - return -1; - } - if (_PyImport_SetModuleString("_imp", imp_mod) < 0) { - Py_DECREF(imp_mod); - return -1; - } - - // Install importlib as the implementation of import - PyObject *value = PyObject_CallMethod(importlib, "_install", - "OO", sysmod, imp_mod); - Py_DECREF(imp_mod); - if (value == NULL) { - return -1; - } - Py_DECREF(value); - - assert(!_PyErr_Occurred(tstate)); - return 0; -} - - -static PyStatus -init_importlib_external(PyThreadState *tstate) -{ - PyObject *value; - value = PyObject_CallMethod(tstate->interp->importlib, - "_install_external_importers", ""); - if (value == NULL) { - _PyErr_Print(tstate); - return _PyStatus_ERR("external importer setup failed"); - } - Py_DECREF(value); - return _PyImportZip_Init(tstate); -} - /* Helper functions to better handle the legacy C locale * * The legacy C locale assumes ASCII as the default text encoding, which @@ -814,7 +741,8 @@ pycore_init_builtins(PyThreadState *tstate) goto error; } - if (_PyImport_FixupBuiltin(bimod, "builtins", interp->modules) < 0) { + PyObject *modules = _PyImport_GetModules(interp); + if (_PyImport_FixupBuiltin(bimod, "builtins", modules) < 0) { goto error; } @@ -850,13 +778,9 @@ pycore_init_builtins(PyThreadState *tstate) } Py_DECREF(bimod); - // Get the __import__ function - PyObject *import_func = _PyDict_GetItemStringWithError(interp->builtins, - "__import__"); - if (import_func == NULL) { + if (_PyImport_InitDefaultImportFunc(interp) < 0) { goto error; } - interp->import_func = Py_NewRef(import_func); assert(!_PyErr_Occurred(tstate)); return _PyStatus_OK(); @@ -918,11 +842,10 @@ pycore_interp_init(PyThreadState *tstate) } const PyConfig *config = _PyInterpreterState_GetConfig(interp); - if (config->_install_importlib) { - /* This call sets up builtin and frozen import support */ - if (init_importlib(tstate, sysmod) < 0) { - return _PyStatus_ERR("failed to initialize importlib"); - } + + status = _PyImport_InitCore(tstate, sysmod, config->_install_importlib); + if (_PyStatus_EXCEPTION(status)) { + goto done; } done: @@ -1172,7 +1095,7 @@ init_interp_main(PyThreadState *tstate) return _PyStatus_ERR("failed to update the Python config"); } - status = init_importlib_external(tstate); + status = _PyImport_InitExternal(tstate); if (_PyStatus_EXCEPTION(status)) { return status; } @@ -1379,8 +1302,11 @@ finalize_modules_delete_special(PyThreadState *tstate, int verbose) static const char * const sys_deletes[] = { "path", "argv", "ps1", "ps2", "last_type", "last_value", "last_traceback", - "path_hooks", "path_importer_cache", "meta_path", "__interactivehook__", + // path_hooks and path_importer_cache are cleared + // by _PyImport_FiniExternal(). + // XXX Clear meta_path in _PyImport_FiniCore(). + "meta_path", NULL }; @@ -1401,10 +1327,7 @@ finalize_modules_delete_special(PyThreadState *tstate, int verbose) const char * const *p; for (p = sys_deletes; *p != NULL; p++) { - if (verbose) { - PySys_WriteStderr("# clear sys.%s\n", *p); - } - if (PyDict_SetItemString(interp->sysdict, *p, Py_None) < 0) { + if (_PySys_ClearAttrString(interp, *p, verbose) < 0) { PyErr_WriteUnraisable(NULL); } } @@ -1576,11 +1499,12 @@ finalize_clear_sys_builtins_dict(PyInterpreterState *interp, int verbose) /* Clear modules, as good as we can */ +// XXX Move most of this to import.c. static void finalize_modules(PyThreadState *tstate) { PyInterpreterState *interp = tstate->interp; - PyObject *modules = interp->modules; + PyObject *modules = _PyImport_GetModules(interp); if (modules == NULL) { // Already done return; @@ -1645,12 +1569,12 @@ finalize_modules(PyThreadState *tstate) // clear PyInterpreterState.modules_by_index and // clear PyModuleDef.m_base.m_copy (of extensions not using the multi-phase // initialization API) - _PyInterpreterState_ClearModules(interp); + _PyImport_ClearModulesByIndex(interp); // Clear and delete the modules directory. Actual modules will // still be there only if imported during the execution of some // destructor. - Py_SETREF(interp->modules, NULL); + _PyImport_ClearModules(interp); // Collect garbage once more _PyGC_CollectNoFail(tstate); @@ -1861,6 +1785,8 @@ Py_FinalizeEx(void) runtime->initialized = 0; runtime->core_initialized = 0; + // XXX Call something like _PyImport_Disable() here? + /* Destroy the state of all threads of the interpreter, except of the current thread. In practice, only daemon threads should still be alive, except if wait_for_thread_shutdown() has been cancelled by CTRL+C. @@ -1910,6 +1836,7 @@ Py_FinalizeEx(void) PyGC_Collect(); /* Destroy all modules */ + _PyImport_FiniExternal(tstate->interp); finalize_modules(tstate); /* Print debug stats if any */ @@ -1943,7 +1870,9 @@ Py_FinalizeEx(void) so it is possible to use tracemalloc in objects destructor. */ _PyTraceMalloc_Fini(); - /* Destroy the database used by _PyImport_{Fixup,Find}Extension */ + /* Finalize any remaining import state */ + // XXX Move these up to where finalize_modules() is currently. + _PyImport_FiniCore(tstate->interp); _PyImport_Fini(); /* unload faulthandler module */ @@ -2183,7 +2112,11 @@ Py_EndInterpreter(PyThreadState *tstate) Py_FatalError("not the last thread"); } + // XXX Call something like _PyImport_Disable() here? + + _PyImport_FiniExternal(tstate->interp); finalize_modules(tstate); + _PyImport_FiniCore(tstate->interp); finalize_interp_clear(tstate); finalize_interp_delete(tstate->interp); @@ -2232,8 +2165,8 @@ add_main_module(PyInterpreterState *interp) if (PyErr_Occurred()) { return _PyStatus_ERR("Failed to test __main__.__loader__"); } - PyObject *loader = PyObject_GetAttrString(interp->importlib, - "BuiltinImporter"); + PyObject *loader = _PyImport_GetImportlibLoader(interp, + "BuiltinImporter"); if (loader == NULL) { return _PyStatus_ERR("Failed to retrieve BuiltinImporter"); } @@ -2739,7 +2672,7 @@ _Py_DumpExtensionModules(int fd, PyInterpreterState *interp) if (interp == NULL) { return; } - PyObject *modules = interp->modules; + PyObject *modules = _PyImport_GetModules(interp); if (modules == NULL || !PyDict_Check(modules)) { return; } diff --git a/Python/pystate.c b/Python/pystate.c index 1261092d1435fa..4770caaed0a363 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -772,11 +772,13 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) Py_CLEAR(interp->codec_search_path); Py_CLEAR(interp->codec_search_cache); Py_CLEAR(interp->codec_error_registry); - Py_CLEAR(interp->modules); - Py_CLEAR(interp->modules_by_index); + + assert(interp->imports.modules == NULL); + assert(interp->imports.modules_by_index == NULL); + assert(interp->imports.importlib == NULL); + assert(interp->imports.import_func == NULL); + Py_CLEAR(interp->builtins_copy); - Py_CLEAR(interp->importlib); - Py_CLEAR(interp->import_func); Py_CLEAR(interp->dict); #ifdef HAVE_FORK Py_CLEAR(interp->before_forkers); @@ -836,6 +838,7 @@ PyInterpreterState_Clear(PyInterpreterState *interp) // garbage. It can be different than the current Python thread state // of 'interp'. PyThreadState *current_tstate = current_fast_get(interp->runtime); + _PyImport_ClearCore(interp); interpreter_clear(interp, current_tstate); } @@ -843,6 +846,7 @@ PyInterpreterState_Clear(PyInterpreterState *interp) void _PyInterpreterState_Clear(PyThreadState *tstate) { + _PyImport_ClearCore(tstate->interp); interpreter_clear(tstate->interp, tstate); } @@ -945,36 +949,6 @@ _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime) #endif -// Used by finalize_modules() -void -_PyInterpreterState_ClearModules(PyInterpreterState *interp) -{ - if (!interp->modules_by_index) { - return; - } - - Py_ssize_t i; - for (i = 0; i < PyList_GET_SIZE(interp->modules_by_index); i++) { - PyObject *m = PyList_GET_ITEM(interp->modules_by_index, i); - if (PyModule_Check(m)) { - /* cleanup the saved copy of module dicts */ - PyModuleDef *md = PyModule_GetDef(m); - if (md) { - Py_CLEAR(md->m_base.m_copy); - } - } - } - - /* Setting modules_by_index to NULL could be dangerous, so we - clear the list instead. */ - if (PyList_SetSlice(interp->modules_by_index, - 0, PyList_GET_SIZE(interp->modules_by_index), - NULL)) { - PyErr_WriteUnraisable(interp->modules_by_index); - } -} - - //---------- // accessors //---------- @@ -1058,11 +1032,12 @@ _PyInterpreterState_RequireIDRef(PyInterpreterState *interp, int required) PyObject * _PyInterpreterState_GetMainModule(PyInterpreterState *interp) { - if (interp->modules == NULL) { + PyObject *modules = _PyImport_GetModules(interp); + if (modules == NULL) { PyErr_SetString(PyExc_RuntimeError, "interpreter not initialized"); return NULL; } - return PyMapping_GetItemString(interp->modules, "__main__"); + return PyMapping_GetItemString(modules, "__main__"); } PyObject * @@ -1922,110 +1897,6 @@ _PyThread_CurrentExceptions(void) } -/****************/ -/* module state */ -/****************/ - -PyObject* -PyState_FindModule(PyModuleDef* module) -{ - Py_ssize_t index = module->m_base.m_index; - PyInterpreterState *state = _PyInterpreterState_GET(); - PyObject *res; - if (module->m_slots) { - return NULL; - } - if (index == 0) - return NULL; - if (state->modules_by_index == NULL) - return NULL; - if (index >= PyList_GET_SIZE(state->modules_by_index)) - return NULL; - res = PyList_GET_ITEM(state->modules_by_index, index); - return res==Py_None ? NULL : res; -} - -int -_PyState_AddModule(PyThreadState *tstate, PyObject* module, PyModuleDef* def) -{ - if (!def) { - assert(_PyErr_Occurred(tstate)); - return -1; - } - if (def->m_slots) { - _PyErr_SetString(tstate, - PyExc_SystemError, - "PyState_AddModule called on module with slots"); - return -1; - } - - PyInterpreterState *interp = tstate->interp; - if (!interp->modules_by_index) { - interp->modules_by_index = PyList_New(0); - if (!interp->modules_by_index) { - return -1; - } - } - - while (PyList_GET_SIZE(interp->modules_by_index) <= def->m_base.m_index) { - if (PyList_Append(interp->modules_by_index, Py_None) < 0) { - return -1; - } - } - - return PyList_SetItem(interp->modules_by_index, - def->m_base.m_index, Py_NewRef(module)); -} - -int -PyState_AddModule(PyObject* module, PyModuleDef* def) -{ - if (!def) { - Py_FatalError("module definition is NULL"); - return -1; - } - - PyThreadState *tstate = current_fast_get(&_PyRuntime); - PyInterpreterState *interp = tstate->interp; - Py_ssize_t index = def->m_base.m_index; - if (interp->modules_by_index && - index < PyList_GET_SIZE(interp->modules_by_index) && - module == PyList_GET_ITEM(interp->modules_by_index, index)) - { - _Py_FatalErrorFormat(__func__, "module %p already added", module); - return -1; - } - return _PyState_AddModule(tstate, module, def); -} - -int -PyState_RemoveModule(PyModuleDef* def) -{ - PyThreadState *tstate = current_fast_get(&_PyRuntime); - PyInterpreterState *interp = tstate->interp; - - if (def->m_slots) { - _PyErr_SetString(tstate, - PyExc_SystemError, - "PyState_RemoveModule called on module with slots"); - return -1; - } - - Py_ssize_t index = def->m_base.m_index; - if (index == 0) { - Py_FatalError("invalid module index"); - } - if (interp->modules_by_index == NULL) { - Py_FatalError("Interpreters module-list not accessible."); - } - if (index > PyList_GET_SIZE(interp->modules_by_index)) { - Py_FatalError("Module index out of bounds."); - } - - return PyList_SetItem(interp->modules_by_index, index, Py_NewRef(Py_None)); -} - - /***********************************/ /* Python "auto thread state" API. */ /***********************************/ diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 6a4d593768690a..ce993ea8796cb7 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -350,14 +350,8 @@ static int set_main_loader(PyObject *d, PyObject *filename, const char *loader_name) { PyInterpreterState *interp = _PyInterpreterState_GET(); - PyObject *bootstrap = PyObject_GetAttrString(interp->importlib, - "_bootstrap_external"); - if (bootstrap == NULL) { - return -1; - } - - PyObject *loader_type = PyObject_GetAttrString(bootstrap, loader_name); - Py_DECREF(bootstrap); + PyObject *loader_type = _PyImport_GetImportlibExternalLoader(interp, + loader_name); if (loader_type == NULL) { return -1; } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 6e81ef92b67f70..b69b803560924c 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -142,6 +142,20 @@ PySys_SetObject(const char *name, PyObject *v) return sys_set_object_str(interp, name, v); } +int +_PySys_ClearAttrString(PyInterpreterState *interp, + const char *name, int verbose) +{ + if (verbose) { + PySys_WriteStderr("# clear sys.%s\n", name); + } + /* To play it safe, we set the attr to None instead of deleting it. */ + if (PyDict_SetItemString(interp->sysdict, name, Py_None) < 0) { + return -1; + } + return 0; +} + static int should_audit(PyInterpreterState *interp) @@ -1650,7 +1664,7 @@ sys_setdlopenflags_impl(PyObject *module, int new_val) /*[clinic end generated code: output=ec918b7fe0a37281 input=4c838211e857a77f]*/ { PyInterpreterState *interp = _PyInterpreterState_GET(); - interp->dlopenflags = new_val; + _PyImport_SetDLOpenFlags(interp, new_val); Py_RETURN_NONE; } @@ -1668,7 +1682,8 @@ sys_getdlopenflags_impl(PyObject *module) /*[clinic end generated code: output=e92cd1bc5005da6e input=dc4ea0899c53b4b6]*/ { PyInterpreterState *interp = _PyInterpreterState_GET(); - return PyLong_FromLong(interp->dlopenflags); + return PyLong_FromLong( + _PyImport_GetDLOpenFlags(interp)); } #endif /* HAVE_DLOPEN */ @@ -2279,22 +2294,10 @@ static PyMethodDef sys_methods[] = { static PyObject * list_builtin_module_names(void) { - PyObject *list = PyList_New(0); + PyObject *list = _PyImport_GetBuiltinModuleNames(); if (list == NULL) { return NULL; } - struct _inittab *inittab = _PyRuntime.imports.inittab; - for (Py_ssize_t i = 0; inittab[i].name != NULL; i++) { - PyObject *name = PyUnicode_FromString(inittab[i].name); - if (name == NULL) { - goto error; - } - if (PyList_Append(list, name) < 0) { - Py_DECREF(name); - goto error; - } - Py_DECREF(name); - } if (PyList_Sort(list) != 0) { goto error; } @@ -3411,11 +3414,10 @@ _PySys_Create(PyThreadState *tstate, PyObject **sysmod_p) PyInterpreterState *interp = tstate->interp; - PyObject *modules = PyDict_New(); + PyObject *modules = _PyImport_InitModules(interp); if (modules == NULL) { goto error; } - interp->modules = modules; PyObject *sysmod = _PyModule_CreateInitialized(&sysmodule, PYTHON_API_VERSION); if (sysmod == NULL) { @@ -3428,7 +3430,7 @@ _PySys_Create(PyThreadState *tstate, PyObject **sysmod_p) } interp->sysdict = Py_NewRef(sysdict); - if (PyDict_SetItemString(sysdict, "modules", interp->modules) < 0) { + if (PyDict_SetItemString(sysdict, "modules", modules) < 0) { goto error; } @@ -3442,7 +3444,7 @@ _PySys_Create(PyThreadState *tstate, PyObject **sysmod_p) return status; } - if (_PyImport_FixupBuiltin(sysmod, "sys", interp->modules) < 0) { + if (_PyImport_FixupBuiltin(sysmod, "sys", modules) < 0) { goto error; } From b365d88465d9228ce4e9e0be20b88e9e4056ad88 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 15 Feb 2023 16:05:07 -0700 Subject: [PATCH 180/225] gh-101758: Add a Test For Single-Phase Init Modules in Multiple Interpreters (gh-101920) The test verifies the behavior of single-phase init modules when loaded in multiple interpreters. https://github.com/python/cpython/issues/101758 --- Include/internal/pycore_import.h | 3 ++ Lib/test/test_imp.py | 73 ++++++++++++++++++++++++++++++ Modules/_testinternalcapi.c | 15 +++++++ Modules/_testsinglephase.c | 49 +++++++++++++++++++- Python/import.c | 76 +++++++++++++++++++++++++++++++- 5 files changed, 212 insertions(+), 4 deletions(-) diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index da766253ef6b9c..6ee7356b41c021 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -153,6 +153,9 @@ PyAPI_DATA(const struct _frozen *) _PyImport_FrozenStdlib; PyAPI_DATA(const struct _frozen *) _PyImport_FrozenTest; extern const struct _module_alias * _PyImport_FrozenAliases; +// for testing +PyAPI_FUNC(int) _PyImport_ClearExtension(PyObject *name, PyObject *filename); + #ifdef __cplusplus } #endif diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py index c85ab92307de78..e81eb6f0a86fe8 100644 --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -10,10 +10,13 @@ from test.support import os_helper from test.support import script_helper from test.support import warnings_helper +import textwrap import unittest import warnings imp = warnings_helper.import_deprecated('imp') import _imp +import _testinternalcapi +import _xxsubinterpreters as _interpreters OS_PATH_NAME = os.path.__name__ @@ -251,6 +254,71 @@ def test_issue16421_multiple_modules_in_one_dll(self): with self.assertRaises(ImportError): imp.load_dynamic('nonexistent', pathname) + @requires_load_dynamic + def test_singlephase_multiple_interpreters(self): + # Currently, for every single-phrase init module loaded + # in multiple interpreters, those interpreters share a + # PyModuleDef for that object, which can be a problem. + + # This single-phase module has global state, which is shared + # by the interpreters. + import _testsinglephase + name = _testsinglephase.__name__ + filename = _testsinglephase.__file__ + + del sys.modules[name] + _testsinglephase._clear_globals() + _testinternalcapi.clear_extension(name, filename) + init_count = _testsinglephase.initialized_count() + assert init_count == -1, (init_count,) + + def clean_up(): + _testsinglephase._clear_globals() + _testinternalcapi.clear_extension(name, filename) + self.addCleanup(clean_up) + + interp1 = _interpreters.create(isolated=False) + self.addCleanup(_interpreters.destroy, interp1) + interp2 = _interpreters.create(isolated=False) + self.addCleanup(_interpreters.destroy, interp2) + + script = textwrap.dedent(f''' + import _testsinglephase + + expected = %d + init_count = _testsinglephase.initialized_count() + if init_count != expected: + raise Exception(init_count) + + lookedup = _testsinglephase.look_up_self() + if lookedup is not _testsinglephase: + raise Exception((_testsinglephase, lookedup)) + + # Attrs set in the module init func are in m_copy. + _initialized = _testsinglephase._initialized + initialized = _testsinglephase.initialized() + if _initialized != initialized: + raise Exception((_initialized, initialized)) + + # Attrs set after loading are not in m_copy. + if hasattr(_testsinglephase, 'spam'): + raise Exception(_testsinglephase.spam) + _testsinglephase.spam = expected + ''') + + # Use an interpreter that gets destroyed right away. + ret = support.run_in_subinterp(script % 1) + self.assertEqual(ret, 0) + + # The module's init func gets run again. + # The module's globals did not get destroyed. + _interpreters.run_string(interp1, script % 2) + + # The module's init func is not run again. + # The second interpreter copies the module's m_copy. + # However, globals are still shared. + _interpreters.run_string(interp2, script % 2) + @requires_load_dynamic def test_singlephase_variants(self): '''Exercise the most meaningful variants described in Python/import.c.''' @@ -260,6 +328,11 @@ def test_singlephase_variants(self): fileobj, pathname, _ = imp.find_module(basename) fileobj.close() + def clean_up(): + import _testsinglephase + _testsinglephase._clear_globals() + self.addCleanup(clean_up) + modules = {} def load(name): assert name not in modules diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index ba57719d92096b..632fac2de0c419 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -671,6 +671,20 @@ get_interp_settings(PyObject *self, PyObject *args) } +static PyObject * +clear_extension(PyObject *self, PyObject *args) +{ + PyObject *name = NULL, *filename = NULL; + if (!PyArg_ParseTuple(args, "OO:clear_extension", &name, &filename)) { + return NULL; + } + if (_PyImport_ClearExtension(name, filename) < 0) { + return NULL; + } + Py_RETURN_NONE; +} + + static PyMethodDef module_functions[] = { {"get_configs", get_configs, METH_NOARGS}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, @@ -692,6 +706,7 @@ static PyMethodDef module_functions[] = { _TESTINTERNALCAPI_COMPILER_CODEGEN_METHODDEF _TESTINTERNALCAPI_OPTIMIZE_CFG_METHODDEF {"get_interp_settings", get_interp_settings, METH_VARARGS, NULL}, + {"clear_extension", clear_extension, METH_VARARGS, NULL}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/_testsinglephase.c b/Modules/_testsinglephase.c index 9e8dd647ee761a..565221c887e5ae 100644 --- a/Modules/_testsinglephase.c +++ b/Modules/_testsinglephase.c @@ -17,12 +17,27 @@ typedef struct { PyObject *str_const; } module_state; + /* Process-global state is only used by _testsinglephase since it's the only one that does not support re-init. */ static struct { int initialized_count; module_state module; -} global_state = { .initialized_count = -1 }; +} global_state = { + +#define NOT_INITIALIZED -1 + .initialized_count = NOT_INITIALIZED, +}; + +static void clear_state(module_state *state); + +static void +clear_global_state(void) +{ + clear_state(&global_state.module); + global_state.initialized_count = NOT_INITIALIZED; +} + static inline module_state * get_module_state(PyObject *module) @@ -106,6 +121,7 @@ init_state(module_state *state) return -1; } + static int init_module(PyObject *module, module_state *state) { @@ -118,6 +134,16 @@ init_module(PyObject *module, module_state *state) if (PyModule_AddObjectRef(module, "str_const", state->str_const) != 0) { return -1; } + + double d = _PyTime_AsSecondsDouble(state->initialized); + PyObject *initialized = PyFloat_FromDouble(d); + if (initialized == NULL) { + return -1; + } + if (PyModule_AddObjectRef(module, "_initialized", initialized) != 0) { + return -1; + } + return 0; } @@ -198,10 +224,28 @@ basic_initialized_count(PyObject *self, PyObject *Py_UNUSED(ignored)) } #define INITIALIZED_COUNT_METHODDEF \ - {"initialized_count", basic_initialized_count, METH_VARARGS, \ + {"initialized_count", basic_initialized_count, METH_NOARGS, \ basic_initialized_count_doc} +PyDoc_STRVAR(basic__clear_globals_doc, +"_clear_globals()\n\ +\n\ +Free all global state and set it to uninitialized."); + +static PyObject * +basic__clear_globals(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + assert(PyModule_GetDef(self)->m_size == -1); + clear_global_state(); + Py_RETURN_NONE; +} + +#define _CLEAR_GLOBALS_METHODDEF \ + {"_clear_globals", basic__clear_globals, METH_NOARGS, \ + basic__clear_globals_doc} + + /*********************************************/ /* the _testsinglephase module (and aliases) */ /*********************************************/ @@ -223,6 +267,7 @@ static PyMethodDef TestMethods_Basic[] = { SUM_METHODDEF, INITIALIZED_METHODDEF, INITIALIZED_COUNT_METHODDEF, + _CLEAR_GLOBALS_METHODDEF, {NULL, NULL} /* sentinel */ }; diff --git a/Python/import.c b/Python/import.c index ae27aaf56848d6..87981668a30505 100644 --- a/Python/import.c +++ b/Python/import.c @@ -632,6 +632,28 @@ exec_builtin_or_dynamic(PyObject *mod) { } +static int clear_singlephase_extension(PyInterpreterState *interp, + PyObject *name, PyObject *filename); + +// Currently, this is only used for testing. +// (See _testinternalcapi.clear_extension().) +int +_PyImport_ClearExtension(PyObject *name, PyObject *filename) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + + /* Clearing a module's C globals is up to the module. */ + if (clear_singlephase_extension(interp, name, filename) < 0) { + return -1; + } + + // In the future we'll probably also make sure the extension's + // file handle (and DL handle) is closed (requires saving it). + + return 0; +} + + /*******************/ #if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) @@ -766,8 +788,30 @@ _extensions_cache_set(PyObject *filename, PyObject *name, PyModuleDef *def) return 0; } +static int +_extensions_cache_delete(PyObject *filename, PyObject *name) +{ + PyObject *extensions = EXTENSIONS; + if (extensions == NULL) { + return 0; + } + PyObject *key = PyTuple_Pack(2, filename, name); + if (key == NULL) { + return -1; + } + if (PyDict_DelItem(extensions, key) < 0) { + if (!PyErr_ExceptionMatches(PyExc_KeyError)) { + Py_DECREF(key); + return -1; + } + PyErr_Clear(); + } + Py_DECREF(key); + return 0; +} + static void -_extensions_cache_clear(void) +_extensions_cache_clear_all(void) { Py_CLEAR(EXTENSIONS); } @@ -890,6 +934,34 @@ import_find_extension(PyThreadState *tstate, PyObject *name, return mod; } +static int +clear_singlephase_extension(PyInterpreterState *interp, + PyObject *name, PyObject *filename) +{ + PyModuleDef *def = _extensions_cache_get(filename, name); + if (def == NULL) { + if (PyErr_Occurred()) { + return -1; + } + return 0; + } + + /* Clear data set when the module was initially loaded. */ + def->m_base.m_init = NULL; + Py_CLEAR(def->m_base.m_copy); + // We leave m_index alone since there's no reason to reset it. + + /* Clear the PyState_*Module() cache entry. */ + if (_modules_by_index_check(interp, def->m_base.m_index) == NULL) { + if (_modules_by_index_clear(interp, def) < 0) { + return -1; + } + } + + /* Clear the cached module def. */ + return _extensions_cache_delete(filename, name); +} + /*******************/ /* builtin modules */ @@ -2633,7 +2705,7 @@ void _PyImport_Fini(void) { /* Destroy the database used by _PyImport_{Fixup,Find}Extension */ - _extensions_cache_clear(); + _extensions_cache_clear_all(); if (import_lock != NULL) { PyThread_free_lock(import_lock); import_lock = NULL; From 3dea4ba6c1b9237893d23574f931f33c940b74e8 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 15 Feb 2023 17:54:05 -0700 Subject: [PATCH 181/225] gh-101758: Fix the wasm Buildbots (gh-101943) They were broken by gh-101920. https://github.com/python/cpython/issues/101758 --- Lib/test/test_imp.py | 12 +++++++++++- Python/pystate.c | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py index e81eb6f0a86fe8..5997ffad8e1232 100644 --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -16,12 +16,21 @@ imp = warnings_helper.import_deprecated('imp') import _imp import _testinternalcapi -import _xxsubinterpreters as _interpreters +try: + import _xxsubinterpreters as _interpreters +except ModuleNotFoundError: + _interpreters = None OS_PATH_NAME = os.path.__name__ +def requires_subinterpreters(meth): + """Decorator to skip a test if subinterpreters are not supported.""" + return unittest.skipIf(_interpreters is None, + 'subinterpreters required')(meth) + + def requires_load_dynamic(meth): """Decorator to skip a test if not running under CPython or lacking imp.load_dynamic().""" @@ -254,6 +263,7 @@ def test_issue16421_multiple_modules_in_one_dll(self): with self.assertRaises(ImportError): imp.load_dynamic('nonexistent', pathname) + @requires_subinterpreters @requires_load_dynamic def test_singlephase_multiple_interpreters(self): # Currently, for every single-phrase init module loaded diff --git a/Python/pystate.c b/Python/pystate.c index 4770caaed0a363..32b17fd19e348f 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -197,6 +197,7 @@ gilstate_tss_clear(_PyRuntimeState *runtime) } +#ifndef NDEBUG static inline int tstate_is_alive(PyThreadState *tstate); static inline int @@ -204,6 +205,7 @@ tstate_is_bound(PyThreadState *tstate) { return tstate->_status.bound && !tstate->_status.unbound; } +#endif // !NDEBUG static void bind_gilstate_tstate(PyThreadState *); static void unbind_gilstate_tstate(PyThreadState *); @@ -1119,6 +1121,7 @@ _PyInterpreterState_LookUpID(int64_t requested_id) /* the per-thread runtime state */ /********************************/ +#ifndef NDEBUG static inline int tstate_is_alive(PyThreadState *tstate) { @@ -1127,6 +1130,7 @@ tstate_is_alive(PyThreadState *tstate) !tstate->_status.cleared && !tstate->_status.finalizing); } +#endif //---------- From 89ac665891dec1988bedec2ce9b2c4d016502a49 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 15 Feb 2023 18:16:00 -0700 Subject: [PATCH 182/225] gh-98627: Add an Optional Check for Extension Module Subinterpreter Compatibility (gh-99040) Enforcing (optionally) the restriction set by PEP 489 makes sense. Furthermore, this sets the stage for a potential restriction related to a per-interpreter GIL. This change includes the following: * add tests for extension module subinterpreter compatibility * add _PyInterpreterConfig.check_multi_interp_extensions * add Py_RTFLAGS_MULTI_INTERP_EXTENSIONS * add _PyImport_CheckSubinterpIncompatibleExtensionAllowed() * fail iff the module does not implement multi-phase init and the current interpreter is configured to check https://github.com/python/cpython/issues/98627 --- Include/cpython/initconfig.h | 3 + Include/cpython/pystate.h | 3 + Include/internal/pycore_import.h | 5 + Lib/test/support/import_helper.py | 18 ++ Lib/test/test_capi/check_config.py | 77 ++++++ Lib/test/test_capi/test_misc.py | 98 +++++++- Lib/test/test_embed.py | 4 +- Lib/test/test_import/__init__.py | 220 +++++++++++++++++- Lib/test/test_threading.py | 1 + ...2-11-02-20-23-47.gh-issue-98627.VJkdRM.rst | 5 + Modules/_testcapimodule.c | 12 +- Python/clinic/import.c.h | 33 ++- Python/import.c | 88 ++++++- Python/importdl.c | 5 + Python/pylifecycle.c | 4 + 15 files changed, 557 insertions(+), 19 deletions(-) create mode 100644 Lib/test/test_capi/check_config.py create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-11-02-20-23-47.gh-issue-98627.VJkdRM.rst diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h index 6ce42b4c09502f..a070fa9ff3a038 100644 --- a/Include/cpython/initconfig.h +++ b/Include/cpython/initconfig.h @@ -248,6 +248,7 @@ typedef struct { int allow_exec; int allow_threads; int allow_daemon_threads; + int check_multi_interp_extensions; } _PyInterpreterConfig; #define _PyInterpreterConfig_INIT \ @@ -256,6 +257,7 @@ typedef struct { .allow_exec = 0, \ .allow_threads = 1, \ .allow_daemon_threads = 0, \ + .check_multi_interp_extensions = 1, \ } #define _PyInterpreterConfig_LEGACY_INIT \ @@ -264,6 +266,7 @@ typedef struct { .allow_exec = 1, \ .allow_threads = 1, \ .allow_daemon_threads = 1, \ + .check_multi_interp_extensions = 0, \ } /* --- Helper functions --------------------------------------- */ diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index be1fcb61fa2244..3efb241e8237e7 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -11,6 +11,9 @@ is available in a given context. For example, forking the process might not be allowed in the current interpreter (i.e. os.fork() would fail). */ +/* Set if import should check a module for subinterpreter support. */ +#define Py_RTFLAGS_MULTI_INTERP_EXTENSIONS (1UL << 8) + /* Set if threads are allowed. */ #define Py_RTFLAGS_THREADS (1UL << 10) diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index 6ee7356b41c021..b7ffe01c0c0e20 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -64,6 +64,7 @@ struct _import_state { /* override for config->use_frozen_modules (for tests) (-1: "off", 1: "on", 0: no override) */ int override_frozen_modules; + int override_multi_interp_extensions_check; #ifdef HAVE_DLOPEN int dlopenflags; #endif @@ -153,6 +154,10 @@ PyAPI_DATA(const struct _frozen *) _PyImport_FrozenStdlib; PyAPI_DATA(const struct _frozen *) _PyImport_FrozenTest; extern const struct _module_alias * _PyImport_FrozenAliases; +PyAPI_FUNC(int) _PyImport_CheckSubinterpIncompatibleExtensionAllowed( + const char *name); + + // for testing PyAPI_FUNC(int) _PyImport_ClearExtension(PyObject *name, PyObject *filename); diff --git a/Lib/test/support/import_helper.py b/Lib/test/support/import_helper.py index 63a8a7952db7a6..772c0987c2ebef 100644 --- a/Lib/test/support/import_helper.py +++ b/Lib/test/support/import_helper.py @@ -105,6 +105,24 @@ def frozen_modules(enabled=True): _imp._override_frozen_modules_for_tests(0) +@contextlib.contextmanager +def multi_interp_extensions_check(enabled=True): + """Force legacy modules to be allowed in subinterpreters (or not). + + ("legacy" == single-phase init) + + This only applies to modules that haven't been imported yet. + It overrides the PyInterpreterConfig.check_multi_interp_extensions + setting (see support.run_in_subinterp_with_config() and + _xxsubinterpreters.create()). + """ + old = _imp._override_multi_interp_extensions_check(1 if enabled else -1) + try: + yield + finally: + _imp._override_multi_interp_extensions_check(old) + + def import_fresh_module(name, fresh=(), blocked=(), *, deprecated=False, usefrozen=False, diff --git a/Lib/test/test_capi/check_config.py b/Lib/test/test_capi/check_config.py new file mode 100644 index 00000000000000..aaedd82f39af50 --- /dev/null +++ b/Lib/test/test_capi/check_config.py @@ -0,0 +1,77 @@ +# This script is used by test_misc. + +import _imp +import _testinternalcapi +import json +import os +import sys + + +def import_singlephase(): + assert '_testsinglephase' not in sys.modules + try: + import _testsinglephase + except ImportError: + sys.modules.pop('_testsinglephase') + return False + else: + del sys.modules['_testsinglephase'] + return True + + +def check_singlephase(override): + # Check using the default setting. + settings_initial = _testinternalcapi.get_interp_settings() + allowed_initial = import_singlephase() + assert(_testinternalcapi.get_interp_settings() == settings_initial) + + # Apply the override and check. + override_initial = _imp._override_multi_interp_extensions_check(override) + settings_after = _testinternalcapi.get_interp_settings() + allowed_after = import_singlephase() + + # Apply the override again and check. + noop = {} + override_after = _imp._override_multi_interp_extensions_check(override) + settings_noop = _testinternalcapi.get_interp_settings() + if settings_noop != settings_after: + noop['settings_noop'] = settings_noop + allowed_noop = import_singlephase() + if allowed_noop != allowed_after: + noop['allowed_noop'] = allowed_noop + + # Restore the original setting and check. + override_noop = _imp._override_multi_interp_extensions_check(override_initial) + if override_noop != override_after: + noop['override_noop'] = override_noop + settings_restored = _testinternalcapi.get_interp_settings() + allowed_restored = import_singlephase() + + # Restore the original setting again. + override_restored = _imp._override_multi_interp_extensions_check(override_initial) + assert(_testinternalcapi.get_interp_settings() == settings_restored) + + return dict({ + 'requested': override, + 'override__initial': override_initial, + 'override_after': override_after, + 'override_restored': override_restored, + 'settings__initial': settings_initial, + 'settings_after': settings_after, + 'settings_restored': settings_restored, + 'allowed__initial': allowed_initial, + 'allowed_after': allowed_after, + 'allowed_restored': allowed_restored, + }, **noop) + + +def run_singlephase_check(override, outfd): + with os.fdopen(outfd, 'w') as outfile: + sys.stdout = outfile + sys.stderr = outfile + try: + results = check_singlephase(override) + json.dump(results, outfile) + finally: + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 7612cddb1f6576..f26b4723d1e68b 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -31,6 +31,10 @@ import _testmultiphase except ImportError: _testmultiphase = None +try: + import _testsinglephase +except ImportError: + _testsinglephase = None # Skip this test if the _testcapi module isn't available. _testcapi = import_helper.import_module('_testcapi') @@ -1297,17 +1301,20 @@ def test_configured_settings(self): """ import json + EXTENSIONS = 1<<8 THREADS = 1<<10 DAEMON_THREADS = 1<<11 FORK = 1<<15 EXEC = 1<<16 - features = ['fork', 'exec', 'threads', 'daemon_threads'] + features = ['fork', 'exec', 'threads', 'daemon_threads', 'extensions'] kwlist = [f'allow_{n}' for n in features] + kwlist[-1] = 'check_multi_interp_extensions' for config, expected in { - (True, True, True, True): FORK | EXEC | THREADS | DAEMON_THREADS, - (False, False, False, False): 0, - (False, False, True, False): THREADS, + (True, True, True, True, True): + FORK | EXEC | THREADS | DAEMON_THREADS | EXTENSIONS, + (False, False, False, False, False): 0, + (False, False, True, False, True): THREADS | EXTENSIONS, }.items(): kwargs = dict(zip(kwlist, config)) expected = { @@ -1322,12 +1329,93 @@ def test_configured_settings(self): json.dump(settings, stdin) ''') with os.fdopen(r) as stdout: - support.run_in_subinterp_with_config(script, **kwargs) + ret = support.run_in_subinterp_with_config(script, **kwargs) + self.assertEqual(ret, 0) out = stdout.read() settings = json.loads(out) self.assertEqual(settings, expected) + @unittest.skipIf(_testsinglephase is None, "test requires _testsinglephase module") + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def test_overridden_setting_extensions_subinterp_check(self): + """ + PyInterpreterConfig.check_multi_interp_extensions can be overridden + with PyInterpreterState.override_multi_interp_extensions_check. + This verifies that the override works but does not modify + the underlying setting. + """ + import json + + EXTENSIONS = 1<<8 + THREADS = 1<<10 + DAEMON_THREADS = 1<<11 + FORK = 1<<15 + EXEC = 1<<16 + BASE_FLAGS = FORK | EXEC | THREADS | DAEMON_THREADS + base_kwargs = { + 'allow_fork': True, + 'allow_exec': True, + 'allow_threads': True, + 'allow_daemon_threads': True, + } + + def check(enabled, override): + kwargs = dict( + base_kwargs, + check_multi_interp_extensions=enabled, + ) + flags = BASE_FLAGS | EXTENSIONS if enabled else BASE_FLAGS + settings = { + 'feature_flags': flags, + } + + expected = { + 'requested': override, + 'override__initial': 0, + 'override_after': override, + 'override_restored': 0, + # The override should not affect the config or settings. + 'settings__initial': settings, + 'settings_after': settings, + 'settings_restored': settings, + # These are the most likely values to be wrong. + 'allowed__initial': not enabled, + 'allowed_after': not ((override > 0) if override else enabled), + 'allowed_restored': not enabled, + } + + r, w = os.pipe() + script = textwrap.dedent(f''' + from test.test_capi.check_config import run_singlephase_check + run_singlephase_check({override}, {w}) + ''') + with os.fdopen(r) as stdout: + ret = support.run_in_subinterp_with_config(script, **kwargs) + self.assertEqual(ret, 0) + out = stdout.read() + results = json.loads(out) + + self.assertEqual(results, expected) + + self.maxDiff = None + + # setting: check disabled + with self.subTest('config: check disabled; override: disabled'): + check(False, -1) + with self.subTest('config: check disabled; override: use config'): + check(False, 0) + with self.subTest('config: check disabled; override: enabled'): + check(False, 1) + + # setting: check enabled + with self.subTest('config: check enabled; override: disabled'): + check(True, -1) + with self.subTest('config: check enabled; override: use config'): + check(True, 0) + with self.subTest('config: check enabled; override: enabled'): + check(True, 1) + def test_mutate_exception(self): """ Exceptions saved in global module state get shared between diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 4d422da5b99f44..e56d0db8627e91 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -1656,13 +1656,15 @@ def test_init_use_frozen_modules(self): api=API_PYTHON, env=env) def test_init_main_interpreter_settings(self): + EXTENSIONS = 1<<8 THREADS = 1<<10 DAEMON_THREADS = 1<<11 FORK = 1<<15 EXEC = 1<<16 expected = { # All optional features should be enabled. - 'feature_flags': FORK | EXEC | THREADS | DAEMON_THREADS, + 'feature_flags': + FORK | EXEC | THREADS | DAEMON_THREADS, } out, err = self.run_embedded_interpreter( 'test_init_main_interpreter_settings', diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 1e4429ed7efe13..96815b3f758a5b 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -21,7 +21,7 @@ from test.support import os_helper from test.support import ( STDLIB_DIR, swap_attr, swap_item, cpython_only, is_emscripten, - is_wasi) + is_wasi, run_in_subinterp_with_config) from test.support.import_helper import ( forget, make_legacy_pyc, unlink, unload, DirsOnSysPath, CleanImport) from test.support.os_helper import ( @@ -30,6 +30,14 @@ from test.support import threading_helper from test.test_importlib.util import uncache from types import ModuleType +try: + import _testsinglephase +except ImportError: + _testsinglephase = None +try: + import _testmultiphase +except ImportError: + _testmultiphase = None skip_if_dont_write_bytecode = unittest.skipIf( @@ -1392,6 +1400,216 @@ def test_unwritable_module(self): unwritable.x = 42 +class SubinterpImportTests(unittest.TestCase): + + RUN_KWARGS = dict( + allow_fork=False, + allow_exec=False, + allow_threads=True, + allow_daemon_threads=False, + ) + + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def pipe(self): + r, w = os.pipe() + self.addCleanup(os.close, r) + self.addCleanup(os.close, w) + if hasattr(os, 'set_blocking'): + os.set_blocking(r, False) + return (r, w) + + def import_script(self, name, fd, check_override=None): + override_text = '' + if check_override is not None: + override_text = f''' + import _imp + _imp._override_multi_interp_extensions_check({check_override}) + ''' + return textwrap.dedent(f''' + import os, sys + {override_text} + try: + import {name} + except ImportError as exc: + text = 'ImportError: ' + str(exc) + else: + text = 'okay' + os.write({fd}, text.encode('utf-8')) + ''') + + def run_shared(self, name, *, + check_singlephase_setting=False, + check_singlephase_override=None, + ): + """ + Try importing the named module in a subinterpreter. + + The subinterpreter will be in the current process. + The module will have already been imported in the main interpreter. + Thus, for extension/builtin modules, the module definition will + have been loaded already and cached globally. + + "check_singlephase_setting" determines whether or not + the interpreter will be configured to check for modules + that are not compatible with use in multiple interpreters. + + This should always return "okay" for all modules if the + setting is False (with no override). + """ + __import__(name) + + kwargs = dict( + **self.RUN_KWARGS, + check_multi_interp_extensions=check_singlephase_setting, + ) + + r, w = self.pipe() + script = self.import_script(name, w, check_singlephase_override) + + ret = run_in_subinterp_with_config(script, **kwargs) + self.assertEqual(ret, 0) + return os.read(r, 100) + + def check_compatible_shared(self, name, *, strict=False): + # Verify that the named module may be imported in a subinterpreter. + # (See run_shared() for more info.) + out = self.run_shared(name, check_singlephase_setting=strict) + self.assertEqual(out, b'okay') + + def check_incompatible_shared(self, name): + # Differences from check_compatible_shared(): + # * verify that import fails + # * "strict" is always True + out = self.run_shared(name, check_singlephase_setting=True) + self.assertEqual( + out.decode('utf-8'), + f'ImportError: module {name} does not support loading in subinterpreters', + ) + + def check_compatible_isolated(self, name, *, strict=False): + # Differences from check_compatible_shared(): + # * subinterpreter in a new process + # * module has never been imported before in that process + # * this tests importing the module for the first time + _, out, err = script_helper.assert_python_ok('-c', textwrap.dedent(f''' + import _testcapi, sys + assert ( + {name!r} in sys.builtin_module_names or + {name!r} not in sys.modules + ), repr({name!r}) + ret = _testcapi.run_in_subinterp_with_config( + {self.import_script(name, "sys.stdout.fileno()")!r}, + **{self.RUN_KWARGS}, + check_multi_interp_extensions={strict}, + ) + assert ret == 0, ret + ''')) + self.assertEqual(err, b'') + self.assertEqual(out, b'okay') + + def check_incompatible_isolated(self, name): + # Differences from check_compatible_isolated(): + # * verify that import fails + # * "strict" is always True + _, out, err = script_helper.assert_python_ok('-c', textwrap.dedent(f''' + import _testcapi, sys + assert {name!r} not in sys.modules, {name!r} + ret = _testcapi.run_in_subinterp_with_config( + {self.import_script(name, "sys.stdout.fileno()")!r}, + **{self.RUN_KWARGS}, + check_multi_interp_extensions=True, + ) + assert ret == 0, ret + ''')) + self.assertEqual(err, b'') + self.assertEqual( + out.decode('utf-8'), + f'ImportError: module {name} does not support loading in subinterpreters', + ) + + def test_builtin_compat(self): + module = 'sys' + with self.subTest(f'{module}: not strict'): + self.check_compatible_shared(module, strict=False) + with self.subTest(f'{module}: strict, shared'): + self.check_compatible_shared(module, strict=True) + + @cpython_only + def test_frozen_compat(self): + module = '_frozen_importlib' + if __import__(module).__spec__.origin != 'frozen': + raise unittest.SkipTest(f'{module} is unexpectedly not frozen') + with self.subTest(f'{module}: not strict'): + self.check_compatible_shared(module, strict=False) + with self.subTest(f'{module}: strict, shared'): + self.check_compatible_shared(module, strict=True) + + @unittest.skipIf(_testsinglephase is None, "test requires _testsinglephase module") + def test_single_init_extension_compat(self): + module = '_testsinglephase' + with self.subTest(f'{module}: not strict'): + self.check_compatible_shared(module, strict=False) + with self.subTest(f'{module}: strict, shared'): + self.check_incompatible_shared(module) + with self.subTest(f'{module}: strict, isolated'): + self.check_incompatible_isolated(module) + + @unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module") + def test_multi_init_extension_compat(self): + module = '_testmultiphase' + with self.subTest(f'{module}: not strict'): + self.check_compatible_shared(module, strict=False) + with self.subTest(f'{module}: strict, shared'): + self.check_compatible_shared(module, strict=True) + with self.subTest(f'{module}: strict, isolated'): + self.check_compatible_isolated(module, strict=True) + + def test_python_compat(self): + module = 'threading' + if __import__(module).__spec__.origin == 'frozen': + raise unittest.SkipTest(f'{module} is unexpectedly frozen') + with self.subTest(f'{module}: not strict'): + self.check_compatible_shared(module, strict=False) + with self.subTest(f'{module}: strict, shared'): + self.check_compatible_shared(module, strict=True) + with self.subTest(f'{module}: strict, isolated'): + self.check_compatible_isolated(module, strict=True) + + @unittest.skipIf(_testsinglephase is None, "test requires _testsinglephase module") + def test_singlephase_check_with_setting_and_override(self): + module = '_testsinglephase' + + def check_compatible(setting, override): + out = self.run_shared( + module, + check_singlephase_setting=setting, + check_singlephase_override=override, + ) + self.assertEqual(out, b'okay') + + def check_incompatible(setting, override): + out = self.run_shared( + module, + check_singlephase_setting=setting, + check_singlephase_override=override, + ) + self.assertNotEqual(out, b'okay') + + with self.subTest('config: check enabled; override: enabled'): + check_incompatible(True, 1) + with self.subTest('config: check enabled; override: use config'): + check_incompatible(True, 0) + with self.subTest('config: check enabled; override: disabled'): + check_compatible(True, -1) + + with self.subTest('config: check disabled; override: enabled'): + check_incompatible(False, 1) + with self.subTest('config: check disabled; override: use config'): + check_compatible(False, 0) + with self.subTest('config: check disabled; override: disabled'): + check_compatible(False, -1) + + if __name__ == '__main__': # Test needs to be a package, so we can do relative imports. unittest.main() diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 31bf46311a80dc..7fea2d38673eff 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1347,6 +1347,7 @@ def func(): allow_exec=True, allow_threads={allowed}, allow_daemon_threads={daemon_allowed}, + check_multi_interp_extensions=False, ) """) with test.support.SuppressCrashReport(): diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-11-02-20-23-47.gh-issue-98627.VJkdRM.rst b/Misc/NEWS.d/next/Core and Builtins/2022-11-02-20-23-47.gh-issue-98627.VJkdRM.rst new file mode 100644 index 00000000000000..3d2d6f6eb0c41f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-11-02-20-23-47.gh-issue-98627.VJkdRM.rst @@ -0,0 +1,5 @@ +When an interpreter is configured to check (and only then), importing an +extension module will now fail when the extension does not support multiple +interpreters (i.e. doesn't implement PEP 489 multi-phase init). This does +not apply to the main interpreter, nor to subinterpreters created with +``Py_NewInterpreter()``. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 5610a7689136f6..0d8d1d73fb2390 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1618,6 +1618,7 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs) int allow_exec = -1; int allow_threads = -1; int allow_daemon_threads = -1; + int check_multi_interp_extensions = -1; int r; PyThreadState *substate, *mainstate; /* only initialise 'cflags.cf_flags' to test backwards compatibility */ @@ -1628,11 +1629,13 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs) "allow_exec", "allow_threads", "allow_daemon_threads", + "check_multi_interp_extensions", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, - "s$pppp:run_in_subinterp_with_config", kwlist, + "s$ppppp:run_in_subinterp_with_config", kwlist, &code, &allow_fork, &allow_exec, - &allow_threads, &allow_daemon_threads)) { + &allow_threads, &allow_daemon_threads, + &check_multi_interp_extensions)) { return NULL; } if (allow_fork < 0) { @@ -1651,6 +1654,10 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs) PyErr_SetString(PyExc_ValueError, "missing allow_daemon_threads"); return NULL; } + if (check_multi_interp_extensions < 0) { + PyErr_SetString(PyExc_ValueError, "missing check_multi_interp_extensions"); + return NULL; + } mainstate = PyThreadState_Get(); @@ -1661,6 +1668,7 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs) .allow_exec = allow_exec, .allow_threads = allow_threads, .allow_daemon_threads = allow_daemon_threads, + .check_multi_interp_extensions = check_multi_interp_extensions, }; substate = _Py_NewInterpreterFromConfig(&config); if (substate == NULL) { diff --git a/Python/clinic/import.c.h b/Python/clinic/import.c.h index 819fb1c75c15c3..cb74be6a422124 100644 --- a/Python/clinic/import.c.h +++ b/Python/clinic/import.c.h @@ -442,6 +442,37 @@ _imp__override_frozen_modules_for_tests(PyObject *module, PyObject *arg) return return_value; } +PyDoc_STRVAR(_imp__override_multi_interp_extensions_check__doc__, +"_override_multi_interp_extensions_check($module, override, /)\n" +"--\n" +"\n" +"(internal-only) Override PyInterpreterConfig.check_multi_interp_extensions.\n" +"\n" +"(-1: \"never\", 1: \"always\", 0: no override)"); + +#define _IMP__OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK_METHODDEF \ + {"_override_multi_interp_extensions_check", (PyCFunction)_imp__override_multi_interp_extensions_check, METH_O, _imp__override_multi_interp_extensions_check__doc__}, + +static PyObject * +_imp__override_multi_interp_extensions_check_impl(PyObject *module, + int override); + +static PyObject * +_imp__override_multi_interp_extensions_check(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + int override; + + override = _PyLong_AsInt(arg); + if (override == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = _imp__override_multi_interp_extensions_check_impl(module, override); + +exit: + return return_value; +} + #if defined(HAVE_DYNAMIC_LOADING) PyDoc_STRVAR(_imp_create_dynamic__doc__, @@ -617,4 +648,4 @@ _imp_source_hash(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb #ifndef _IMP_EXEC_DYNAMIC_METHODDEF #define _IMP_EXEC_DYNAMIC_METHODDEF #endif /* !defined(_IMP_EXEC_DYNAMIC_METHODDEF) */ -/*[clinic end generated code: output=806352838c3f7008 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b18d46e0036eff49 input=a9049054013a1b77]*/ diff --git a/Python/import.c b/Python/import.c index 87981668a30505..ec126f28b85816 100644 --- a/Python/import.c +++ b/Python/import.c @@ -74,6 +74,8 @@ static struct _inittab *inittab_copy = NULL; (interp)->imports.modules_by_index #define IMPORTLIB(interp) \ (interp)->imports.importlib +#define OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK(interp) \ + (interp)->imports.override_multi_interp_extensions_check #define OVERRIDE_FROZEN_MODULES(interp) \ (interp)->imports.override_frozen_modules #ifdef HAVE_DLOPEN @@ -816,6 +818,38 @@ _extensions_cache_clear_all(void) Py_CLEAR(EXTENSIONS); } + +static bool +check_multi_interp_extensions(PyInterpreterState *interp) +{ + int override = OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK(interp); + if (override < 0) { + return false; + } + else if (override > 0) { + return true; + } + else if (_PyInterpreterState_HasFeature( + interp, Py_RTFLAGS_MULTI_INTERP_EXTENSIONS)) { + return true; + } + return false; +} + +int +_PyImport_CheckSubinterpIncompatibleExtensionAllowed(const char *name) +{ + PyInterpreterState *interp = _PyInterpreterState_Get(); + if (check_multi_interp_extensions(interp)) { + assert(!_Py_IsMainInterpreter(interp)); + PyErr_Format(PyExc_ImportError, + "module %s does not support loading in subinterpreters", + name); + return -1; + } + return 0; +} + static int fix_up_extension(PyObject *mod, PyObject *name, PyObject *filename) { @@ -3297,6 +3331,34 @@ _imp__override_frozen_modules_for_tests_impl(PyObject *module, int override) Py_RETURN_NONE; } +/*[clinic input] +_imp._override_multi_interp_extensions_check + + override: int + / + +(internal-only) Override PyInterpreterConfig.check_multi_interp_extensions. + +(-1: "never", 1: "always", 0: no override) +[clinic start generated code]*/ + +static PyObject * +_imp__override_multi_interp_extensions_check_impl(PyObject *module, + int override) +/*[clinic end generated code: output=3ff043af52bbf280 input=e086a2ea181f92ae]*/ +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (_Py_IsMainInterpreter(interp)) { + PyErr_SetString(PyExc_RuntimeError, + "_imp._override_multi_interp_extensions_check() " + "cannot be used in the main interpreter"); + return NULL; + } + int oldvalue = OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK(interp); + OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK(interp) = override; + return PyLong_FromLong(oldvalue); +} + #ifdef HAVE_DYNAMIC_LOADING /*[clinic input] @@ -3329,18 +3391,23 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) PyThreadState *tstate = _PyThreadState_GET(); mod = import_find_extension(tstate, name, path); - if (mod != NULL || PyErr_Occurred()) { - Py_DECREF(name); - Py_DECREF(path); - return mod; + if (mod != NULL) { + const char *name_buf = PyUnicode_AsUTF8(name); + assert(name_buf != NULL); + if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) { + Py_DECREF(mod); + mod = NULL; + } + goto finally; + } + else if (PyErr_Occurred()) { + goto finally; } if (file != NULL) { fp = _Py_fopen_obj(path, "r"); if (fp == NULL) { - Py_DECREF(name); - Py_DECREF(path); - return NULL; + goto finally; } } else @@ -3348,10 +3415,12 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) mod = _PyImport_LoadDynamicModuleWithSpec(spec, fp); - Py_DECREF(name); - Py_DECREF(path); if (fp) fclose(fp); + +finally: + Py_DECREF(name); + Py_DECREF(path); return mod; } @@ -3436,6 +3505,7 @@ static PyMethodDef imp_methods[] = { _IMP_IS_FROZEN_METHODDEF _IMP__FROZEN_MODULE_NAMES_METHODDEF _IMP__OVERRIDE_FROZEN_MODULES_FOR_TESTS_METHODDEF + _IMP__OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK_METHODDEF _IMP_CREATE_DYNAMIC_METHODDEF _IMP_EXEC_DYNAMIC_METHODDEF _IMP_EXEC_BUILTIN_METHODDEF diff --git a/Python/importdl.c b/Python/importdl.c index 6dafb4541486e9..3a3a30ddbdcdb5 100644 --- a/Python/importdl.c +++ b/Python/importdl.c @@ -3,6 +3,7 @@ #include "Python.h" #include "pycore_call.h" +#include "pycore_import.h" #include "pycore_pystate.h" #include "pycore_runtime.h" @@ -203,6 +204,10 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) /* Fall back to single-phase init mechanism */ + if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) { + goto error; + } + if (hook_prefix == nonascii_prefix) { /* don't allow legacy init for non-ASCII module names */ PyErr_Format( diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 281035dafa9577..e80dd30c89dfd0 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -565,6 +565,10 @@ init_interp_settings(PyInterpreterState *interp, const _PyInterpreterConfig *con if (config->allow_daemon_threads) { interp->feature_flags |= Py_RTFLAGS_DAEMON_THREADS; } + + if (config->check_multi_interp_extensions) { + interp->feature_flags |= Py_RTFLAGS_MULTI_INTERP_EXTENSIONS; + } } From 0b13575e74ff3321364a3389eda6b4e92792afe1 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Wed, 15 Feb 2023 22:08:20 -0800 Subject: [PATCH 183/225] gh-99108: Refactor _sha256 & _sha512 into _sha2. (#101924) This merges their code. They're backed by the same single HACL* static library, having them be a single module simplifies maintenance. This should unbreak the wasm enscripten builds that currently fail due to linking in --whole-archive mode and the HACL* library appearing twice. Long unnoticed error fixed: _sha512.SHA384Type was doubly assigned and was actually SHA512Type. Nobody depends on those internal names. Also rename LIBHACL_ make vars to LIBHACL_SHA2_ in preperation for other future HACL things. --- Lib/hashlib.py | 12 +- Lib/test/test_hashlib.py | 22 +- Makefile.pre.in | 15 +- ...3-02-15-01-54-06.gh-issue-99108.rjTSic.rst | 3 + Modules/Setup | 3 +- Modules/Setup.stdlib.in | 3 +- Modules/clinic/sha256module.c.h | 225 ----- Modules/clinic/sha2module.c.h | 440 ++++++++++ Modules/clinic/sha512module.c.h | 225 ----- Modules/sha256module.c | 465 ---------- Modules/sha2module.c | 805 ++++++++++++++++++ Modules/sha512module.c | 456 ---------- PC/config.c | 6 +- PCbuild/pythoncore.vcxproj | 3 +- PCbuild/pythoncore.vcxproj.filters | 5 +- Python/stdlib_module_names.h | 3 +- configure | 96 +-- configure.ac | 16 +- 18 files changed, 1310 insertions(+), 1493 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-15-01-54-06.gh-issue-99108.rjTSic.rst delete mode 100644 Modules/clinic/sha256module.c.h create mode 100644 Modules/clinic/sha2module.c.h delete mode 100644 Modules/clinic/sha512module.c.h delete mode 100644 Modules/sha256module.c create mode 100644 Modules/sha2module.c delete mode 100644 Modules/sha512module.c diff --git a/Lib/hashlib.py b/Lib/hashlib.py index 21b5e912f3c771..1b16441cb60ba7 100644 --- a/Lib/hashlib.py +++ b/Lib/hashlib.py @@ -92,13 +92,13 @@ def __get_builtin_constructor(name): import _md5 cache['MD5'] = cache['md5'] = _md5.md5 elif name in {'SHA256', 'sha256', 'SHA224', 'sha224'}: - import _sha256 - cache['SHA224'] = cache['sha224'] = _sha256.sha224 - cache['SHA256'] = cache['sha256'] = _sha256.sha256 + import _sha2 + cache['SHA224'] = cache['sha224'] = _sha2.sha224 + cache['SHA256'] = cache['sha256'] = _sha2.sha256 elif name in {'SHA512', 'sha512', 'SHA384', 'sha384'}: - import _sha512 - cache['SHA384'] = cache['sha384'] = _sha512.sha384 - cache['SHA512'] = cache['sha512'] = _sha512.sha512 + import _sha2 + cache['SHA384'] = cache['sha384'] = _sha2.sha384 + cache['SHA512'] = cache['sha512'] = _sha2.sha512 elif name in {'blake2b', 'blake2s'}: import _blake2 cache['blake2b'] = _blake2.blake2b diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index 9c92b4e9c280dc..5ead8857943592 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -1,6 +1,4 @@ -# Test hashlib module -# -# $Id$ +# Test the hashlib module. # # Copyright (C) 2005-2010 Gregory P. Smith (greg@krypto.org) # Licensed to PSF under a Contributor Agreement. @@ -28,7 +26,6 @@ from http.client import HTTPException -# default builtin hash module default_builtin_hashes = {'md5', 'sha1', 'sha256', 'sha512', 'sha3', 'blake2'} # --with-builtin-hashlib-hashes override builtin_hashes = sysconfig.get_config_var("PY_BUILTIN_HASHLIB_HASHES") @@ -66,6 +63,7 @@ def get_fips_mode(): requires_blake2 = unittest.skipUnless(_blake2, 'requires _blake2') # bpo-46913: Don't test the _sha3 extension on a Python UBSAN build +# TODO(gh-99108): Revisit this after _sha3 uses HACL*. SKIP_SHA3 = support.check_sanitizer(ub=True) requires_sha3 = unittest.skipUnless(not SKIP_SHA3, 'requires _sha3') @@ -107,7 +105,7 @@ class HashLibTestCase(unittest.TestCase): shakes = {'shake_128', 'shake_256'} - # Issue #14693: fallback modules are always compiled under POSIX + # gh-58898: Fallback modules are always compiled under POSIX. _warn_on_extension_import = (os.name == 'posix' or support.Py_DEBUG) def _conditional_import_module(self, module_name): @@ -116,7 +114,7 @@ def _conditional_import_module(self, module_name): return importlib.import_module(module_name) except ModuleNotFoundError as error: if self._warn_on_extension_import and module_name in builtin_hashes: - warnings.warn('Did a C extension fail to compile? %s' % error) + warnings.warn(f'Did a C extension fail to compile? {error}') return None def __init__(self, *args, **kwargs): @@ -147,7 +145,7 @@ def _test_algorithm_via_hashlib_new(data=None, _alg=algorithm, **kwargs): _hashlib = self._conditional_import_module('_hashlib') self._hashlib = _hashlib if _hashlib: - # These two algorithms should always be present when this module + # These algorithms should always be present when this module # is compiled. If not, something was compiled wrong. self.assertTrue(hasattr(_hashlib, 'openssl_md5')) self.assertTrue(hasattr(_hashlib, 'openssl_sha1')) @@ -172,12 +170,10 @@ def add_builtin_constructor(name): _sha1 = self._conditional_import_module('_sha1') if _sha1: add_builtin_constructor('sha1') - _sha256 = self._conditional_import_module('_sha256') - if _sha256: + _sha2 = self._conditional_import_module('_sha2') + if _sha2: add_builtin_constructor('sha224') add_builtin_constructor('sha256') - _sha512 = self._conditional_import_module('_sha512') - if _sha512: add_builtin_constructor('sha384') add_builtin_constructor('sha512') if _blake2: @@ -460,9 +456,9 @@ def check_blocksize_name(self, name, block_size=0, digest_size=0, self.assertEqual(len(m.hexdigest()), 2*digest_size) self.assertEqual(m.name, name) # split for sha3_512 / _sha3.sha3 object - self.assertIn(name.split("_")[0], repr(m)) + self.assertIn(name.split("_")[0], repr(m).lower()) - def test_blocksize_name(self): + def test_blocksize_and_name(self): self.check_blocksize_name('md5', 64, 16) self.check_blocksize_name('sha1', 64, 20) self.check_blocksize_name('sha224', 64, 28) diff --git a/Makefile.pre.in b/Makefile.pre.in index ce3fed3d648536..490483a712014c 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -207,7 +207,7 @@ ENSUREPIP= @ENSUREPIP@ # Internal static libraries LIBMPDEC_A= Modules/_decimal/libmpdec/libmpdec.a LIBEXPAT_A= Modules/expat/libexpat.a -LIBHACL_A= Modules/_hacl/libHacl_Streaming_SHA2.a +LIBHACL_SHA2_A= Modules/_hacl/libHacl_Streaming_SHA2.a # Module state, compiler flags and linker flags # Empty CFLAGS and LDFLAGS are omitted. @@ -575,10 +575,10 @@ LIBEXPAT_HEADERS= \ ########################################################################## # hashlib's HACL* library -LIBHACL_OBJS= \ +LIBHACL_SHA2_OBJS= \ Modules/_hacl/Hacl_Streaming_SHA2.o -LIBHACL_HEADERS= \ +LIBHACL_SHA2_HEADERS= \ Modules/_hacl/Hacl_Streaming_SHA2.h \ Modules/_hacl/include/krml/FStar_UInt128_Verified.h \ Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h \ @@ -912,12 +912,12 @@ $(LIBEXPAT_A): $(LIBEXPAT_OBJS) # Build HACL* static libraries for hashlib: libHacl_Streaming_SHA2.a LIBHACL_CFLAGS=-I$(srcdir)/Modules/_hacl/include -D_BSD_SOURCE -D_DEFAULT_SOURCE $(PY_STDMODULE_CFLAGS) $(CCSHARED) -Modules/_hacl/Hacl_Streaming_SHA2.o: $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.c $(LIBHACL_HEADERS) +Modules/_hacl/Hacl_Streaming_SHA2.o: $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.c $(LIBHACL_SHA2_HEADERS) $(CC) -c $(LIBHACL_CFLAGS) -o $@ $(srcdir)/Modules/_hacl/Hacl_Streaming_SHA2.c -$(LIBHACL_A): $(LIBHACL_OBJS) +$(LIBHACL_SHA2_A): $(LIBHACL_SHA2_OBJS) -rm -f $@ - $(AR) $(ARFLAGS) $@ $(LIBHACL_OBJS) + $(AR) $(ARFLAGS) $@ $(LIBHACL_SHA2_OBJS) # create relative links from build/lib.platform/egg.so to Modules/egg.so # pybuilddir.txt is created too late. We cannot use it in Makefile @@ -2635,9 +2635,8 @@ MODULE__HASHLIB_DEPS=$(srcdir)/Modules/hashlib.h MODULE__IO_DEPS=$(srcdir)/Modules/_io/_iomodule.h MODULE__MD5_DEPS=$(srcdir)/Modules/hashlib.h MODULE__SHA1_DEPS=$(srcdir)/Modules/hashlib.h -MODULE__SHA256_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_HEADERS) $(LIBHACL_A) +MODULE__SHA2_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_SHA2_HEADERS) $(LIBHACL_SHA2_A) MODULE__SHA3_DEPS=$(srcdir)/Modules/_sha3/sha3.c $(srcdir)/Modules/_sha3/sha3.h $(srcdir)/Modules/hashlib.h -MODULE__SHA512_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_HEADERS) $(LIBHACL_A) MODULE__SOCKET_DEPS=$(srcdir)/Modules/socketmodule.h $(srcdir)/Modules/addrinfo.h $(srcdir)/Modules/getaddrinfo.c $(srcdir)/Modules/getnameinfo.c MODULE__SSL_DEPS=$(srcdir)/Modules/_ssl.h $(srcdir)/Modules/_ssl/cert.c $(srcdir)/Modules/_ssl/debughelpers.c $(srcdir)/Modules/_ssl/misc.c $(srcdir)/Modules/_ssl_data.h $(srcdir)/Modules/_ssl_data_111.h $(srcdir)/Modules/_ssl_data_300.h $(srcdir)/Modules/socketmodule.h MODULE__TESTCAPI_DEPS=$(srcdir)/Modules/_testcapi/testcapi_long.h $(srcdir)/Modules/_testcapi/parts.h diff --git a/Misc/NEWS.d/next/Library/2023-02-15-01-54-06.gh-issue-99108.rjTSic.rst b/Misc/NEWS.d/next/Library/2023-02-15-01-54-06.gh-issue-99108.rjTSic.rst new file mode 100644 index 00000000000000..1612c89c0ea6be --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-15-01-54-06.gh-issue-99108.rjTSic.rst @@ -0,0 +1,3 @@ +The built-in extension modules for :mod:`hashlib` SHA2 algorithms, used when +OpenSSL does not provide them, now live in a single internal ``_sha2`` module +instead of separate ``_sha256`` and ``_sha512`` modules. diff --git a/Modules/Setup b/Modules/Setup index 428be0a1bf8fa1..1d5183bc2df118 100644 --- a/Modules/Setup +++ b/Modules/Setup @@ -165,8 +165,7 @@ PYTHONPATH=$(COREPYTHONPATH) #_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c #_md5 md5module.c #_sha1 sha1module.c -#_sha256 sha256module.c -#_sha512 sha512module.c +#_sha2 sha2module.c -I$(srcdir)/Modules/_hacl/include Modules/_hacl/libHacl_Streaming_SHA2.a #_sha3 _sha3/sha3module.c # text encodings and unicode diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 22bcb423db233f..8f5e14a4e80e22 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -79,8 +79,7 @@ # hashing builtins, can be disabled with --without-builtin-hashlib-hashes @MODULE__MD5_TRUE@_md5 md5module.c @MODULE__SHA1_TRUE@_sha1 sha1module.c -@MODULE__SHA256_TRUE@_sha256 sha256module.c -I$(srcdir)/Modules/_hacl/include Modules/_hacl/libHacl_Streaming_SHA2.a -@MODULE__SHA512_TRUE@_sha512 sha512module.c -I$(srcdir)/Modules/_hacl/include Modules/_hacl/libHacl_Streaming_SHA2.a +@MODULE__SHA2_TRUE@_sha2 sha2module.c -I$(srcdir)/Modules/_hacl/include Modules/_hacl/libHacl_Streaming_SHA2.a @MODULE__SHA3_TRUE@_sha3 _sha3/sha3module.c @MODULE__BLAKE2_TRUE@_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c diff --git a/Modules/clinic/sha256module.c.h b/Modules/clinic/sha256module.c.h deleted file mode 100644 index 10d09fac695fc4..00000000000000 --- a/Modules/clinic/sha256module.c.h +++ /dev/null @@ -1,225 +0,0 @@ -/*[clinic input] -preserve -[clinic start generated code]*/ - -#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) -# include "pycore_gc.h" // PyGC_Head -# include "pycore_runtime.h" // _Py_ID() -#endif - - -PyDoc_STRVAR(SHA256Type_copy__doc__, -"copy($self, /)\n" -"--\n" -"\n" -"Return a copy of the hash object."); - -#define SHA256TYPE_COPY_METHODDEF \ - {"copy", _PyCFunction_CAST(SHA256Type_copy), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, SHA256Type_copy__doc__}, - -static PyObject * -SHA256Type_copy_impl(SHAobject *self, PyTypeObject *cls); - -static PyObject * -SHA256Type_copy(SHAobject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -{ - if (nargs) { - PyErr_SetString(PyExc_TypeError, "copy() takes no arguments"); - return NULL; - } - return SHA256Type_copy_impl(self, cls); -} - -PyDoc_STRVAR(SHA256Type_digest__doc__, -"digest($self, /)\n" -"--\n" -"\n" -"Return the digest value as a bytes object."); - -#define SHA256TYPE_DIGEST_METHODDEF \ - {"digest", (PyCFunction)SHA256Type_digest, METH_NOARGS, SHA256Type_digest__doc__}, - -static PyObject * -SHA256Type_digest_impl(SHAobject *self); - -static PyObject * -SHA256Type_digest(SHAobject *self, PyObject *Py_UNUSED(ignored)) -{ - return SHA256Type_digest_impl(self); -} - -PyDoc_STRVAR(SHA256Type_hexdigest__doc__, -"hexdigest($self, /)\n" -"--\n" -"\n" -"Return the digest value as a string of hexadecimal digits."); - -#define SHA256TYPE_HEXDIGEST_METHODDEF \ - {"hexdigest", (PyCFunction)SHA256Type_hexdigest, METH_NOARGS, SHA256Type_hexdigest__doc__}, - -static PyObject * -SHA256Type_hexdigest_impl(SHAobject *self); - -static PyObject * -SHA256Type_hexdigest(SHAobject *self, PyObject *Py_UNUSED(ignored)) -{ - return SHA256Type_hexdigest_impl(self); -} - -PyDoc_STRVAR(SHA256Type_update__doc__, -"update($self, obj, /)\n" -"--\n" -"\n" -"Update this hash object\'s state with the provided string."); - -#define SHA256TYPE_UPDATE_METHODDEF \ - {"update", (PyCFunction)SHA256Type_update, METH_O, SHA256Type_update__doc__}, - -PyDoc_STRVAR(_sha256_sha256__doc__, -"sha256($module, /, string=b\'\', *, usedforsecurity=True)\n" -"--\n" -"\n" -"Return a new SHA-256 hash object; optionally initialized with a string."); - -#define _SHA256_SHA256_METHODDEF \ - {"sha256", _PyCFunction_CAST(_sha256_sha256), METH_FASTCALL|METH_KEYWORDS, _sha256_sha256__doc__}, - -static PyObject * -_sha256_sha256_impl(PyObject *module, PyObject *string, int usedforsecurity); - -static PyObject * -_sha256_sha256(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -{ - PyObject *return_value = NULL; - #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - - #define NUM_KEYWORDS 2 - static struct { - PyGC_Head _this_is_not_used; - PyObject_VAR_HEAD - PyObject *ob_item[NUM_KEYWORDS]; - } _kwtuple = { - .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), }, - }; - #undef NUM_KEYWORDS - #define KWTUPLE (&_kwtuple.ob_base.ob_base) - - #else // !Py_BUILD_CORE - # define KWTUPLE NULL - #endif // !Py_BUILD_CORE - - static const char * const _keywords[] = {"string", "usedforsecurity", NULL}; - static _PyArg_Parser _parser = { - .keywords = _keywords, - .fname = "sha256", - .kwtuple = KWTUPLE, - }; - #undef KWTUPLE - PyObject *argsbuf[2]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *string = NULL; - int usedforsecurity = 1; - - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); - if (!args) { - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - if (args[0]) { - string = args[0]; - if (!--noptargs) { - goto skip_optional_pos; - } - } -skip_optional_pos: - if (!noptargs) { - goto skip_optional_kwonly; - } - usedforsecurity = PyObject_IsTrue(args[1]); - if (usedforsecurity < 0) { - goto exit; - } -skip_optional_kwonly: - return_value = _sha256_sha256_impl(module, string, usedforsecurity); - -exit: - return return_value; -} - -PyDoc_STRVAR(_sha256_sha224__doc__, -"sha224($module, /, string=b\'\', *, usedforsecurity=True)\n" -"--\n" -"\n" -"Return a new SHA-224 hash object; optionally initialized with a string."); - -#define _SHA256_SHA224_METHODDEF \ - {"sha224", _PyCFunction_CAST(_sha256_sha224), METH_FASTCALL|METH_KEYWORDS, _sha256_sha224__doc__}, - -static PyObject * -_sha256_sha224_impl(PyObject *module, PyObject *string, int usedforsecurity); - -static PyObject * -_sha256_sha224(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -{ - PyObject *return_value = NULL; - #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - - #define NUM_KEYWORDS 2 - static struct { - PyGC_Head _this_is_not_used; - PyObject_VAR_HEAD - PyObject *ob_item[NUM_KEYWORDS]; - } _kwtuple = { - .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), }, - }; - #undef NUM_KEYWORDS - #define KWTUPLE (&_kwtuple.ob_base.ob_base) - - #else // !Py_BUILD_CORE - # define KWTUPLE NULL - #endif // !Py_BUILD_CORE - - static const char * const _keywords[] = {"string", "usedforsecurity", NULL}; - static _PyArg_Parser _parser = { - .keywords = _keywords, - .fname = "sha224", - .kwtuple = KWTUPLE, - }; - #undef KWTUPLE - PyObject *argsbuf[2]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *string = NULL; - int usedforsecurity = 1; - - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); - if (!args) { - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - if (args[0]) { - string = args[0]; - if (!--noptargs) { - goto skip_optional_pos; - } - } -skip_optional_pos: - if (!noptargs) { - goto skip_optional_kwonly; - } - usedforsecurity = PyObject_IsTrue(args[1]); - if (usedforsecurity < 0) { - goto exit; - } -skip_optional_kwonly: - return_value = _sha256_sha224_impl(module, string, usedforsecurity); - -exit: - return return_value; -} -/*[clinic end generated code: output=ae926f7ec85e7c97 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/sha2module.c.h b/Modules/clinic/sha2module.c.h new file mode 100644 index 00000000000000..8f855ca345e47a --- /dev/null +++ b/Modules/clinic/sha2module.c.h @@ -0,0 +1,440 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +# include "pycore_gc.h" // PyGC_Head +# include "pycore_runtime.h" // _Py_ID() +#endif + + +PyDoc_STRVAR(SHA256Type_copy__doc__, +"copy($self, /)\n" +"--\n" +"\n" +"Return a copy of the hash object."); + +#define SHA256TYPE_COPY_METHODDEF \ + {"copy", _PyCFunction_CAST(SHA256Type_copy), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, SHA256Type_copy__doc__}, + +static PyObject * +SHA256Type_copy_impl(SHA256object *self, PyTypeObject *cls); + +static PyObject * +SHA256Type_copy(SHA256object *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs) { + PyErr_SetString(PyExc_TypeError, "copy() takes no arguments"); + return NULL; + } + return SHA256Type_copy_impl(self, cls); +} + +PyDoc_STRVAR(SHA512Type_copy__doc__, +"copy($self, /)\n" +"--\n" +"\n" +"Return a copy of the hash object."); + +#define SHA512TYPE_COPY_METHODDEF \ + {"copy", _PyCFunction_CAST(SHA512Type_copy), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, SHA512Type_copy__doc__}, + +static PyObject * +SHA512Type_copy_impl(SHA512object *self, PyTypeObject *cls); + +static PyObject * +SHA512Type_copy(SHA512object *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs) { + PyErr_SetString(PyExc_TypeError, "copy() takes no arguments"); + return NULL; + } + return SHA512Type_copy_impl(self, cls); +} + +PyDoc_STRVAR(SHA256Type_digest__doc__, +"digest($self, /)\n" +"--\n" +"\n" +"Return the digest value as a bytes object."); + +#define SHA256TYPE_DIGEST_METHODDEF \ + {"digest", (PyCFunction)SHA256Type_digest, METH_NOARGS, SHA256Type_digest__doc__}, + +static PyObject * +SHA256Type_digest_impl(SHA256object *self); + +static PyObject * +SHA256Type_digest(SHA256object *self, PyObject *Py_UNUSED(ignored)) +{ + return SHA256Type_digest_impl(self); +} + +PyDoc_STRVAR(SHA512Type_digest__doc__, +"digest($self, /)\n" +"--\n" +"\n" +"Return the digest value as a bytes object."); + +#define SHA512TYPE_DIGEST_METHODDEF \ + {"digest", (PyCFunction)SHA512Type_digest, METH_NOARGS, SHA512Type_digest__doc__}, + +static PyObject * +SHA512Type_digest_impl(SHA512object *self); + +static PyObject * +SHA512Type_digest(SHA512object *self, PyObject *Py_UNUSED(ignored)) +{ + return SHA512Type_digest_impl(self); +} + +PyDoc_STRVAR(SHA256Type_hexdigest__doc__, +"hexdigest($self, /)\n" +"--\n" +"\n" +"Return the digest value as a string of hexadecimal digits."); + +#define SHA256TYPE_HEXDIGEST_METHODDEF \ + {"hexdigest", (PyCFunction)SHA256Type_hexdigest, METH_NOARGS, SHA256Type_hexdigest__doc__}, + +static PyObject * +SHA256Type_hexdigest_impl(SHA256object *self); + +static PyObject * +SHA256Type_hexdigest(SHA256object *self, PyObject *Py_UNUSED(ignored)) +{ + return SHA256Type_hexdigest_impl(self); +} + +PyDoc_STRVAR(SHA512Type_hexdigest__doc__, +"hexdigest($self, /)\n" +"--\n" +"\n" +"Return the digest value as a string of hexadecimal digits."); + +#define SHA512TYPE_HEXDIGEST_METHODDEF \ + {"hexdigest", (PyCFunction)SHA512Type_hexdigest, METH_NOARGS, SHA512Type_hexdigest__doc__}, + +static PyObject * +SHA512Type_hexdigest_impl(SHA512object *self); + +static PyObject * +SHA512Type_hexdigest(SHA512object *self, PyObject *Py_UNUSED(ignored)) +{ + return SHA512Type_hexdigest_impl(self); +} + +PyDoc_STRVAR(SHA256Type_update__doc__, +"update($self, obj, /)\n" +"--\n" +"\n" +"Update this hash object\'s state with the provided string."); + +#define SHA256TYPE_UPDATE_METHODDEF \ + {"update", (PyCFunction)SHA256Type_update, METH_O, SHA256Type_update__doc__}, + +PyDoc_STRVAR(SHA512Type_update__doc__, +"update($self, obj, /)\n" +"--\n" +"\n" +"Update this hash object\'s state with the provided string."); + +#define SHA512TYPE_UPDATE_METHODDEF \ + {"update", (PyCFunction)SHA512Type_update, METH_O, SHA512Type_update__doc__}, + +PyDoc_STRVAR(_sha2_sha256__doc__, +"sha256($module, /, string=b\'\', *, usedforsecurity=True)\n" +"--\n" +"\n" +"Return a new SHA-256 hash object; optionally initialized with a string."); + +#define _SHA2_SHA256_METHODDEF \ + {"sha256", _PyCFunction_CAST(_sha2_sha256), METH_FASTCALL|METH_KEYWORDS, _sha2_sha256__doc__}, + +static PyObject * +_sha2_sha256_impl(PyObject *module, PyObject *string, int usedforsecurity); + +static PyObject * +_sha2_sha256(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"string", "usedforsecurity", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "sha256", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *string = NULL; + int usedforsecurity = 1; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + string = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + usedforsecurity = PyObject_IsTrue(args[1]); + if (usedforsecurity < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _sha2_sha256_impl(module, string, usedforsecurity); + +exit: + return return_value; +} + +PyDoc_STRVAR(_sha2_sha224__doc__, +"sha224($module, /, string=b\'\', *, usedforsecurity=True)\n" +"--\n" +"\n" +"Return a new SHA-224 hash object; optionally initialized with a string."); + +#define _SHA2_SHA224_METHODDEF \ + {"sha224", _PyCFunction_CAST(_sha2_sha224), METH_FASTCALL|METH_KEYWORDS, _sha2_sha224__doc__}, + +static PyObject * +_sha2_sha224_impl(PyObject *module, PyObject *string, int usedforsecurity); + +static PyObject * +_sha2_sha224(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"string", "usedforsecurity", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "sha224", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *string = NULL; + int usedforsecurity = 1; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + string = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + usedforsecurity = PyObject_IsTrue(args[1]); + if (usedforsecurity < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _sha2_sha224_impl(module, string, usedforsecurity); + +exit: + return return_value; +} + +PyDoc_STRVAR(_sha2_sha512__doc__, +"sha512($module, /, string=b\'\', *, usedforsecurity=True)\n" +"--\n" +"\n" +"Return a new SHA-512 hash object; optionally initialized with a string."); + +#define _SHA2_SHA512_METHODDEF \ + {"sha512", _PyCFunction_CAST(_sha2_sha512), METH_FASTCALL|METH_KEYWORDS, _sha2_sha512__doc__}, + +static PyObject * +_sha2_sha512_impl(PyObject *module, PyObject *string, int usedforsecurity); + +static PyObject * +_sha2_sha512(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"string", "usedforsecurity", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "sha512", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *string = NULL; + int usedforsecurity = 1; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + string = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + usedforsecurity = PyObject_IsTrue(args[1]); + if (usedforsecurity < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _sha2_sha512_impl(module, string, usedforsecurity); + +exit: + return return_value; +} + +PyDoc_STRVAR(_sha2_sha384__doc__, +"sha384($module, /, string=b\'\', *, usedforsecurity=True)\n" +"--\n" +"\n" +"Return a new SHA-384 hash object; optionally initialized with a string."); + +#define _SHA2_SHA384_METHODDEF \ + {"sha384", _PyCFunction_CAST(_sha2_sha384), METH_FASTCALL|METH_KEYWORDS, _sha2_sha384__doc__}, + +static PyObject * +_sha2_sha384_impl(PyObject *module, PyObject *string, int usedforsecurity); + +static PyObject * +_sha2_sha384(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"string", "usedforsecurity", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "sha384", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *string = NULL; + int usedforsecurity = 1; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + string = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + usedforsecurity = PyObject_IsTrue(args[1]); + if (usedforsecurity < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _sha2_sha384_impl(module, string, usedforsecurity); + +exit: + return return_value; +} +/*[clinic end generated code: output=f81dacb48f3fee72 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/sha512module.c.h b/Modules/clinic/sha512module.c.h deleted file mode 100644 index f8d326363c398e..00000000000000 --- a/Modules/clinic/sha512module.c.h +++ /dev/null @@ -1,225 +0,0 @@ -/*[clinic input] -preserve -[clinic start generated code]*/ - -#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) -# include "pycore_gc.h" // PyGC_Head -# include "pycore_runtime.h" // _Py_ID() -#endif - - -PyDoc_STRVAR(SHA512Type_copy__doc__, -"copy($self, /)\n" -"--\n" -"\n" -"Return a copy of the hash object."); - -#define SHA512TYPE_COPY_METHODDEF \ - {"copy", _PyCFunction_CAST(SHA512Type_copy), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, SHA512Type_copy__doc__}, - -static PyObject * -SHA512Type_copy_impl(SHAobject *self, PyTypeObject *cls); - -static PyObject * -SHA512Type_copy(SHAobject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -{ - if (nargs) { - PyErr_SetString(PyExc_TypeError, "copy() takes no arguments"); - return NULL; - } - return SHA512Type_copy_impl(self, cls); -} - -PyDoc_STRVAR(SHA512Type_digest__doc__, -"digest($self, /)\n" -"--\n" -"\n" -"Return the digest value as a bytes object."); - -#define SHA512TYPE_DIGEST_METHODDEF \ - {"digest", (PyCFunction)SHA512Type_digest, METH_NOARGS, SHA512Type_digest__doc__}, - -static PyObject * -SHA512Type_digest_impl(SHAobject *self); - -static PyObject * -SHA512Type_digest(SHAobject *self, PyObject *Py_UNUSED(ignored)) -{ - return SHA512Type_digest_impl(self); -} - -PyDoc_STRVAR(SHA512Type_hexdigest__doc__, -"hexdigest($self, /)\n" -"--\n" -"\n" -"Return the digest value as a string of hexadecimal digits."); - -#define SHA512TYPE_HEXDIGEST_METHODDEF \ - {"hexdigest", (PyCFunction)SHA512Type_hexdigest, METH_NOARGS, SHA512Type_hexdigest__doc__}, - -static PyObject * -SHA512Type_hexdigest_impl(SHAobject *self); - -static PyObject * -SHA512Type_hexdigest(SHAobject *self, PyObject *Py_UNUSED(ignored)) -{ - return SHA512Type_hexdigest_impl(self); -} - -PyDoc_STRVAR(SHA512Type_update__doc__, -"update($self, obj, /)\n" -"--\n" -"\n" -"Update this hash object\'s state with the provided string."); - -#define SHA512TYPE_UPDATE_METHODDEF \ - {"update", (PyCFunction)SHA512Type_update, METH_O, SHA512Type_update__doc__}, - -PyDoc_STRVAR(_sha512_sha512__doc__, -"sha512($module, /, string=b\'\', *, usedforsecurity=True)\n" -"--\n" -"\n" -"Return a new SHA-512 hash object; optionally initialized with a string."); - -#define _SHA512_SHA512_METHODDEF \ - {"sha512", _PyCFunction_CAST(_sha512_sha512), METH_FASTCALL|METH_KEYWORDS, _sha512_sha512__doc__}, - -static PyObject * -_sha512_sha512_impl(PyObject *module, PyObject *string, int usedforsecurity); - -static PyObject * -_sha512_sha512(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -{ - PyObject *return_value = NULL; - #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - - #define NUM_KEYWORDS 2 - static struct { - PyGC_Head _this_is_not_used; - PyObject_VAR_HEAD - PyObject *ob_item[NUM_KEYWORDS]; - } _kwtuple = { - .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), }, - }; - #undef NUM_KEYWORDS - #define KWTUPLE (&_kwtuple.ob_base.ob_base) - - #else // !Py_BUILD_CORE - # define KWTUPLE NULL - #endif // !Py_BUILD_CORE - - static const char * const _keywords[] = {"string", "usedforsecurity", NULL}; - static _PyArg_Parser _parser = { - .keywords = _keywords, - .fname = "sha512", - .kwtuple = KWTUPLE, - }; - #undef KWTUPLE - PyObject *argsbuf[2]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *string = NULL; - int usedforsecurity = 1; - - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); - if (!args) { - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - if (args[0]) { - string = args[0]; - if (!--noptargs) { - goto skip_optional_pos; - } - } -skip_optional_pos: - if (!noptargs) { - goto skip_optional_kwonly; - } - usedforsecurity = PyObject_IsTrue(args[1]); - if (usedforsecurity < 0) { - goto exit; - } -skip_optional_kwonly: - return_value = _sha512_sha512_impl(module, string, usedforsecurity); - -exit: - return return_value; -} - -PyDoc_STRVAR(_sha512_sha384__doc__, -"sha384($module, /, string=b\'\', *, usedforsecurity=True)\n" -"--\n" -"\n" -"Return a new SHA-384 hash object; optionally initialized with a string."); - -#define _SHA512_SHA384_METHODDEF \ - {"sha384", _PyCFunction_CAST(_sha512_sha384), METH_FASTCALL|METH_KEYWORDS, _sha512_sha384__doc__}, - -static PyObject * -_sha512_sha384_impl(PyObject *module, PyObject *string, int usedforsecurity); - -static PyObject * -_sha512_sha384(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -{ - PyObject *return_value = NULL; - #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - - #define NUM_KEYWORDS 2 - static struct { - PyGC_Head _this_is_not_used; - PyObject_VAR_HEAD - PyObject *ob_item[NUM_KEYWORDS]; - } _kwtuple = { - .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), }, - }; - #undef NUM_KEYWORDS - #define KWTUPLE (&_kwtuple.ob_base.ob_base) - - #else // !Py_BUILD_CORE - # define KWTUPLE NULL - #endif // !Py_BUILD_CORE - - static const char * const _keywords[] = {"string", "usedforsecurity", NULL}; - static _PyArg_Parser _parser = { - .keywords = _keywords, - .fname = "sha384", - .kwtuple = KWTUPLE, - }; - #undef KWTUPLE - PyObject *argsbuf[2]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *string = NULL; - int usedforsecurity = 1; - - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); - if (!args) { - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - if (args[0]) { - string = args[0]; - if (!--noptargs) { - goto skip_optional_pos; - } - } -skip_optional_pos: - if (!noptargs) { - goto skip_optional_kwonly; - } - usedforsecurity = PyObject_IsTrue(args[1]); - if (usedforsecurity < 0) { - goto exit; - } -skip_optional_kwonly: - return_value = _sha512_sha384_impl(module, string, usedforsecurity); - -exit: - return return_value; -} -/*[clinic end generated code: output=dd168f3f21097afe input=a9049054013a1b77]*/ diff --git a/Modules/sha256module.c b/Modules/sha256module.c deleted file mode 100644 index 301c9837bb6720..00000000000000 --- a/Modules/sha256module.c +++ /dev/null @@ -1,465 +0,0 @@ -/* SHA256 module */ - -/* This module provides an interface to NIST's SHA-256 and SHA-224 Algorithms */ - -/* See below for information about the original code this module was - based upon. Additional work performed by: - - Andrew Kuchling (amk@amk.ca) - Greg Stein (gstein@lyra.org) - Trevor Perrin (trevp@trevp.net) - Jonathan Protzenko (jonathan@protzenko.fr) - - Copyright (C) 2005-2007 Gregory P. Smith (greg@krypto.org) - Licensed to PSF under a Contributor Agreement. - -*/ - -/* SHA objects */ -#ifndef Py_BUILD_CORE_BUILTIN -# define Py_BUILD_CORE_MODULE 1 -#endif - -#include "Python.h" -#include "pycore_bitutils.h" // _Py_bswap32() -#include "pycore_strhex.h" // _Py_strhex() -#include "structmember.h" // PyMemberDef -#include "hashlib.h" - -/*[clinic input] -module _sha256 -class SHA256Type "SHAobject *" "&PyType_Type" -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=71a39174d4f0a744]*/ - - -/* The SHA block size and maximum message digest sizes, in bytes */ - -#define SHA_BLOCKSIZE 64 -#define SHA_DIGESTSIZE 32 - -/* The SHA2-224 and SHA2-256 implementations defer to the HACL* verified - * library. */ - -#include "_hacl/Hacl_Streaming_SHA2.h" - -typedef struct { - PyObject_HEAD - // Even though one could conceivably perform run-type checks to tell apart a - // sha224_type from a sha256_type (and thus deduce the digest size), we must - // keep this field because it's exposed as a member field on the underlying - // python object. - // TODO: could we transform this into a getter and get rid of the redundant - // field? - int digestsize; - Hacl_Streaming_SHA2_state_sha2_256 *state; -} SHAobject; - -#include "clinic/sha256module.c.h" - -/* We shall use run-time type information in the remainder of this module to - * tell apart SHA2-224 and SHA2-256 */ -typedef struct { - PyTypeObject* sha224_type; - PyTypeObject* sha256_type; -} _sha256_state; - -static inline _sha256_state* -_sha256_get_state(PyObject *module) -{ - void *state = PyModule_GetState(module); - assert(state != NULL); - return (_sha256_state *)state; -} - -static void SHAcopy(SHAobject *src, SHAobject *dest) -{ - dest->digestsize = src->digestsize; - dest->state = Hacl_Streaming_SHA2_copy_256(src->state); -} - -static SHAobject * -newSHA224object(_sha256_state *state) -{ - SHAobject *sha = (SHAobject *)PyObject_GC_New(SHAobject, - state->sha224_type); - PyObject_GC_Track(sha); - return sha; -} - -static SHAobject * -newSHA256object(_sha256_state *state) -{ - SHAobject *sha = (SHAobject *)PyObject_GC_New(SHAobject, - state->sha256_type); - PyObject_GC_Track(sha); - return sha; -} - -/* Internal methods for a hash object */ -static int -SHA_traverse(PyObject *ptr, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(ptr)); - return 0; -} - -static void -SHA_dealloc(SHAobject *ptr) -{ - Hacl_Streaming_SHA2_free_256(ptr->state); - PyTypeObject *tp = Py_TYPE(ptr); - PyObject_GC_UnTrack(ptr); - PyObject_GC_Del(ptr); - Py_DECREF(tp); -} - -/* HACL* takes a uint32_t for the length of its parameter, but Py_ssize_t can be - * 64 bits. */ -static void update_256(Hacl_Streaming_SHA2_state_sha2_256 *state, uint8_t *buf, Py_ssize_t len) { - /* Note: we explicitly ignore the error code on the basis that it would take > - * 1 billion years to overflow the maximum admissible length for SHA2-256 - * (namely, 2^61-1 bytes). */ - while (len > UINT32_MAX) { - Hacl_Streaming_SHA2_update_256(state, buf, UINT32_MAX); - len -= UINT32_MAX; - buf += UINT32_MAX; - } - /* Cast to uint32_t is safe: upon exiting the loop, len <= UINT32_MAX, and - * therefore fits in a uint32_t */ - Hacl_Streaming_SHA2_update_256(state, buf, (uint32_t) len); -} - - -/* External methods for a hash object */ - -/*[clinic input] -SHA256Type.copy - - cls:defining_class - -Return a copy of the hash object. -[clinic start generated code]*/ - -static PyObject * -SHA256Type_copy_impl(SHAobject *self, PyTypeObject *cls) -/*[clinic end generated code: output=9273f92c382be12f input=3137146fcb88e212]*/ -{ - SHAobject *newobj; - _sha256_state *state = PyType_GetModuleState(cls); - if (Py_IS_TYPE(self, state->sha256_type)) { - if ( (newobj = newSHA256object(state)) == NULL) { - return NULL; - } - } else { - if ( (newobj = newSHA224object(state))==NULL) { - return NULL; - } - } - - SHAcopy(self, newobj); - return (PyObject *)newobj; -} - -/*[clinic input] -SHA256Type.digest - -Return the digest value as a bytes object. -[clinic start generated code]*/ - -static PyObject * -SHA256Type_digest_impl(SHAobject *self) -/*[clinic end generated code: output=46616a5e909fbc3d input=f1f4cfea5cbde35c]*/ -{ - uint8_t digest[SHA_DIGESTSIZE]; - // HACL performs copies under the hood so that self->state remains valid - // after this call. - Hacl_Streaming_SHA2_finish_256(self->state, digest); - return PyBytes_FromStringAndSize((const char *)digest, self->digestsize); -} - -/*[clinic input] -SHA256Type.hexdigest - -Return the digest value as a string of hexadecimal digits. -[clinic start generated code]*/ - -static PyObject * -SHA256Type_hexdigest_impl(SHAobject *self) -/*[clinic end generated code: output=725f8a7041ae97f3 input=0cc4c714693010d1]*/ -{ - uint8_t digest[SHA_DIGESTSIZE]; - Hacl_Streaming_SHA2_finish_256(self->state, digest); - return _Py_strhex((const char *)digest, self->digestsize); -} - -/*[clinic input] -SHA256Type.update - - obj: object - / - -Update this hash object's state with the provided string. -[clinic start generated code]*/ - -static PyObject * -SHA256Type_update(SHAobject *self, PyObject *obj) -/*[clinic end generated code: output=0967fb2860c66af7 input=b2d449d5b30f0f5a]*/ -{ - Py_buffer buf; - - GET_BUFFER_VIEW_OR_ERROUT(obj, &buf); - - update_256(self->state, buf.buf, buf.len); - - PyBuffer_Release(&buf); - Py_RETURN_NONE; -} - -static PyMethodDef SHA_methods[] = { - SHA256TYPE_COPY_METHODDEF - SHA256TYPE_DIGEST_METHODDEF - SHA256TYPE_HEXDIGEST_METHODDEF - SHA256TYPE_UPDATE_METHODDEF - {NULL, NULL} /* sentinel */ -}; - -static PyObject * -SHA256_get_block_size(PyObject *self, void *closure) -{ - return PyLong_FromLong(SHA_BLOCKSIZE); -} - -static PyObject * -SHA256_get_name(SHAobject *self, void *closure) -{ - if (self->digestsize == 28) { - return PyUnicode_FromStringAndSize("sha224", 6); - } - return PyUnicode_FromStringAndSize("sha256", 6); -} - -static PyGetSetDef SHA_getseters[] = { - {"block_size", - (getter)SHA256_get_block_size, NULL, - NULL, - NULL}, - {"name", - (getter)SHA256_get_name, NULL, - NULL, - NULL}, - {NULL} /* Sentinel */ -}; - -static PyMemberDef SHA_members[] = { - {"digest_size", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL}, - {NULL} /* Sentinel */ -}; - -static PyType_Slot sha256_types_slots[] = { - {Py_tp_dealloc, SHA_dealloc}, - {Py_tp_methods, SHA_methods}, - {Py_tp_members, SHA_members}, - {Py_tp_getset, SHA_getseters}, - {Py_tp_traverse, SHA_traverse}, - {0,0} -}; - -static PyType_Spec sha224_type_spec = { - .name = "_sha256.sha224", - .basicsize = sizeof(SHAobject), - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | - Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), - .slots = sha256_types_slots -}; - -static PyType_Spec sha256_type_spec = { - .name = "_sha256.sha256", - .basicsize = sizeof(SHAobject), - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | - Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), - .slots = sha256_types_slots -}; - -/* The single module-level function: new() */ - -/*[clinic input] -_sha256.sha256 - - string: object(c_default="NULL") = b'' - * - usedforsecurity: bool = True - -Return a new SHA-256 hash object; optionally initialized with a string. -[clinic start generated code]*/ - -static PyObject * -_sha256_sha256_impl(PyObject *module, PyObject *string, int usedforsecurity) -/*[clinic end generated code: output=a1de327e8e1185cf input=9be86301aeb14ea5]*/ -{ - Py_buffer buf; - - if (string) { - GET_BUFFER_VIEW_OR_ERROUT(string, &buf); - } - - _sha256_state *state = PyModule_GetState(module); - - SHAobject *new; - if ((new = newSHA256object(state)) == NULL) { - if (string) { - PyBuffer_Release(&buf); - } - return NULL; - } - - new->state = Hacl_Streaming_SHA2_create_in_256(); - new->digestsize = 32; - - if (PyErr_Occurred()) { - Py_DECREF(new); - if (string) { - PyBuffer_Release(&buf); - } - return NULL; - } - if (string) { - update_256(new->state, buf.buf, buf.len); - PyBuffer_Release(&buf); - } - - return (PyObject *)new; -} - -/*[clinic input] -_sha256.sha224 - - string: object(c_default="NULL") = b'' - * - usedforsecurity: bool = True - -Return a new SHA-224 hash object; optionally initialized with a string. -[clinic start generated code]*/ - -static PyObject * -_sha256_sha224_impl(PyObject *module, PyObject *string, int usedforsecurity) -/*[clinic end generated code: output=08be6b36569bc69c input=9fcfb46e460860ac]*/ -{ - Py_buffer buf; - if (string) { - GET_BUFFER_VIEW_OR_ERROUT(string, &buf); - } - - _sha256_state *state = PyModule_GetState(module); - SHAobject *new; - if ((new = newSHA224object(state)) == NULL) { - if (string) { - PyBuffer_Release(&buf); - } - return NULL; - } - - new->state = Hacl_Streaming_SHA2_create_in_224(); - new->digestsize = 28; - - if (PyErr_Occurred()) { - Py_DECREF(new); - if (string) { - PyBuffer_Release(&buf); - } - return NULL; - } - if (string) { - update_256(new->state, buf.buf, buf.len); - PyBuffer_Release(&buf); - } - - return (PyObject *)new; -} - - -/* List of functions exported by this module */ - -static struct PyMethodDef SHA_functions[] = { - _SHA256_SHA256_METHODDEF - _SHA256_SHA224_METHODDEF - {NULL, NULL} /* Sentinel */ -}; - -static int -_sha256_traverse(PyObject *module, visitproc visit, void *arg) -{ - _sha256_state *state = _sha256_get_state(module); - Py_VISIT(state->sha224_type); - Py_VISIT(state->sha256_type); - return 0; -} - -static int -_sha256_clear(PyObject *module) -{ - _sha256_state *state = _sha256_get_state(module); - Py_CLEAR(state->sha224_type); - Py_CLEAR(state->sha256_type); - return 0; -} - -static void -_sha256_free(void *module) -{ - _sha256_clear((PyObject *)module); -} - -static int sha256_exec(PyObject *module) -{ - _sha256_state *state = _sha256_get_state(module); - - state->sha224_type = (PyTypeObject *)PyType_FromModuleAndSpec( - module, &sha224_type_spec, NULL); - - if (state->sha224_type == NULL) { - return -1; - } - - state->sha256_type = (PyTypeObject *)PyType_FromModuleAndSpec( - module, &sha256_type_spec, NULL); - - if (state->sha256_type == NULL) { - return -1; - } - - Py_INCREF((PyObject *)state->sha224_type); - if (PyModule_AddObject(module, "SHA224Type", (PyObject *)state->sha224_type) < 0) { - Py_DECREF((PyObject *)state->sha224_type); - return -1; - } - Py_INCREF((PyObject *)state->sha256_type); - if (PyModule_AddObject(module, "SHA256Type", (PyObject *)state->sha256_type) < 0) { - Py_DECREF((PyObject *)state->sha256_type); - return -1; - } - return 0; -} - -static PyModuleDef_Slot _sha256_slots[] = { - {Py_mod_exec, sha256_exec}, - {0, NULL} -}; - -static struct PyModuleDef _sha256module = { - PyModuleDef_HEAD_INIT, - .m_name = "_sha256", - .m_size = sizeof(_sha256_state), - .m_methods = SHA_functions, - .m_slots = _sha256_slots, - .m_traverse = _sha256_traverse, - .m_clear = _sha256_clear, - .m_free = _sha256_free -}; - -/* Initialize this module. */ -PyMODINIT_FUNC -PyInit__sha256(void) -{ - return PyModuleDef_Init(&_sha256module); -} diff --git a/Modules/sha2module.c b/Modules/sha2module.c new file mode 100644 index 00000000000000..9999f255cd578a --- /dev/null +++ b/Modules/sha2module.c @@ -0,0 +1,805 @@ +/* SHA2 module */ + +/* This provides an interface to NIST's SHA2 224, 256, 384, & 512 Algorithms */ + +/* See below for information about the original code this module was + based upon. Additional work performed by: + + Andrew Kuchling (amk@amk.ca) + Greg Stein (gstein@lyra.org) + Trevor Perrin (trevp@trevp.net) + Jonathan Protzenko (jonathan@protzenko.fr) + + Copyright (C) 2005-2007 Gregory P. Smith (greg@krypto.org) + Licensed to PSF under a Contributor Agreement. + +*/ + +/* SHA objects */ +#ifndef Py_BUILD_CORE_BUILTIN +# define Py_BUILD_CORE_MODULE 1 +#endif + +#include "Python.h" +#include "pycore_bitutils.h" // _Py_bswap32() +#include "pycore_moduleobject.h" // _PyModule_GetState() +#include "pycore_strhex.h" // _Py_strhex() +#include "structmember.h" // PyMemberDef +#include "hashlib.h" + +/*[clinic input] +module _sha2 +class SHA256Type "SHA256object *" "&PyType_Type" +class SHA512Type "SHA512object *" "&PyType_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=b5315a7b611c9afc]*/ + + +/* The SHA block sizes and maximum message digest sizes, in bytes */ + +#define SHA256_BLOCKSIZE 64 +#define SHA256_DIGESTSIZE 32 +#define SHA512_BLOCKSIZE 128 +#define SHA512_DIGESTSIZE 64 + +/* Our SHA2 implementations defer to the HACL* verified library. */ + +#include "_hacl/Hacl_Streaming_SHA2.h" + +// TODO: Get rid of int digestsize in favor of Hacl state info? + +typedef struct { + PyObject_HEAD + int digestsize; + Hacl_Streaming_SHA2_state_sha2_256 *state; +} SHA256object; + +typedef struct { + PyObject_HEAD + int digestsize; + Hacl_Streaming_SHA2_state_sha2_512 *state; +} SHA512object; + +#include "clinic/sha2module.c.h" + +/* We shall use run-time type information in the remainder of this module to + * tell apart SHA2-224 and SHA2-256 */ +typedef struct { + PyTypeObject* sha224_type; + PyTypeObject* sha256_type; + PyTypeObject* sha384_type; + PyTypeObject* sha512_type; +} sha2_state; + +static inline sha2_state* +sha2_get_state(PyObject *module) +{ + void *state = _PyModule_GetState(module); + assert(state != NULL); + return (sha2_state *)state; +} + +static void SHA256copy(SHA256object *src, SHA256object *dest) +{ + dest->digestsize = src->digestsize; + dest->state = Hacl_Streaming_SHA2_copy_256(src->state); +} + +static void SHA512copy(SHA512object *src, SHA512object *dest) +{ + dest->digestsize = src->digestsize; + dest->state = Hacl_Streaming_SHA2_copy_512(src->state); +} + +static SHA256object * +newSHA224object(sha2_state *state) +{ + SHA256object *sha = (SHA256object *)PyObject_GC_New( + SHA256object, state->sha224_type); + if (!sha) { + return NULL; + } + PyObject_GC_Track(sha); + return sha; +} + +static SHA256object * +newSHA256object(sha2_state *state) +{ + SHA256object *sha = (SHA256object *)PyObject_GC_New( + SHA256object, state->sha256_type); + if (!sha) { + return NULL; + } + PyObject_GC_Track(sha); + return sha; +} + +static SHA512object * +newSHA384object(sha2_state *state) +{ + SHA512object *sha = (SHA512object *)PyObject_GC_New( + SHA512object, state->sha384_type); + if (!sha) { + return NULL; + } + PyObject_GC_Track(sha); + return sha; +} + +static SHA512object * +newSHA512object(sha2_state *state) +{ + SHA512object *sha = (SHA512object *)PyObject_GC_New( + SHA512object, state->sha512_type); + if (!sha) { + return NULL; + } + PyObject_GC_Track(sha); + return sha; +} + +/* Internal methods for our hash objects. */ + +static int +SHA2_traverse(PyObject *ptr, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(ptr)); + return 0; +} + +static void +SHA256_dealloc(SHA256object *ptr) +{ + Hacl_Streaming_SHA2_free_256(ptr->state); + PyTypeObject *tp = Py_TYPE(ptr); + PyObject_GC_UnTrack(ptr); + PyObject_GC_Del(ptr); + Py_DECREF(tp); +} + +static void +SHA512_dealloc(SHA512object *ptr) +{ + Hacl_Streaming_SHA2_free_512(ptr->state); + PyTypeObject *tp = Py_TYPE(ptr); + PyObject_GC_UnTrack(ptr); + PyObject_GC_Del(ptr); + Py_DECREF(tp); +} + +/* HACL* takes a uint32_t for the length of its parameter, but Py_ssize_t can be + * 64 bits so we loop in <4gig chunks when needed. */ + +static void update_256(Hacl_Streaming_SHA2_state_sha2_256 *state, uint8_t *buf, Py_ssize_t len) { + /* Note: we explicitly ignore the error code on the basis that it would take > + * 1 billion years to overflow the maximum admissible length for SHA2-256 + * (namely, 2^61-1 bytes). */ +#if PY_SSIZE_T_MAX > UINT32_MAX + while (len > UINT32_MAX) { + Hacl_Streaming_SHA2_update_256(state, buf, UINT32_MAX); + len -= UINT32_MAX; + buf += UINT32_MAX; + } +#endif + /* Cast to uint32_t is safe: len <= UINT32_MAX at this point. */ + Hacl_Streaming_SHA2_update_256(state, buf, (uint32_t) len); +} + +static void update_512(Hacl_Streaming_SHA2_state_sha2_512 *state, uint8_t *buf, Py_ssize_t len) { + /* Note: we explicitly ignore the error code on the basis that it would take > + * 1 billion years to overflow the maximum admissible length for this API + * (namely, 2^64-1 bytes). */ +#if PY_SSIZE_T_MAX > UINT32_MAX + while (len > UINT32_MAX) { + Hacl_Streaming_SHA2_update_512(state, buf, UINT32_MAX); + len -= UINT32_MAX; + buf += UINT32_MAX; + } +#endif + /* Cast to uint32_t is safe: len <= UINT32_MAX at this point. */ + Hacl_Streaming_SHA2_update_512(state, buf, (uint32_t) len); +} + + +/* External methods for our hash objects */ + +/*[clinic input] +SHA256Type.copy + + cls:defining_class + +Return a copy of the hash object. +[clinic start generated code]*/ + +static PyObject * +SHA256Type_copy_impl(SHA256object *self, PyTypeObject *cls) +/*[clinic end generated code: output=fabd515577805cd3 input=3137146fcb88e212]*/ +{ + SHA256object *newobj; + sha2_state *state = PyType_GetModuleState(cls); + if (Py_IS_TYPE(self, state->sha256_type)) { + if ((newobj = newSHA256object(state)) == NULL) { + return NULL; + } + } else { + if ((newobj = newSHA224object(state)) == NULL) { + return NULL; + } + } + + SHA256copy(self, newobj); + return (PyObject *)newobj; +} + +/*[clinic input] +SHA512Type.copy + + cls: defining_class + +Return a copy of the hash object. +[clinic start generated code]*/ + +static PyObject * +SHA512Type_copy_impl(SHA512object *self, PyTypeObject *cls) +/*[clinic end generated code: output=66d2a8ef20de8302 input=f673a18f66527c90]*/ +{ + SHA512object *newobj; + sha2_state *state = PyType_GetModuleState(cls); + + if (Py_IS_TYPE((PyObject*)self, state->sha512_type)) { + if ((newobj = newSHA512object(state)) == NULL) { + return NULL; + } + } + else { + if ((newobj = newSHA384object(state)) == NULL) { + return NULL; + } + } + + SHA512copy(self, newobj); + return (PyObject *)newobj; +} + +/*[clinic input] +SHA256Type.digest + +Return the digest value as a bytes object. +[clinic start generated code]*/ + +static PyObject * +SHA256Type_digest_impl(SHA256object *self) +/*[clinic end generated code: output=3a2e3997a98ee792 input=f1f4cfea5cbde35c]*/ +{ + uint8_t digest[SHA256_DIGESTSIZE]; + assert(self->digestsize <= SHA256_DIGESTSIZE); + // HACL* performs copies under the hood so that self->state remains valid + // after this call. + Hacl_Streaming_SHA2_finish_256(self->state, digest); + return PyBytes_FromStringAndSize((const char *)digest, self->digestsize); +} + +/*[clinic input] +SHA512Type.digest + +Return the digest value as a bytes object. +[clinic start generated code]*/ + +static PyObject * +SHA512Type_digest_impl(SHA512object *self) +/*[clinic end generated code: output=dd8c6320070458e0 input=f6470dd359071f4b]*/ +{ + uint8_t digest[SHA512_DIGESTSIZE]; + assert(self->digestsize <= SHA512_DIGESTSIZE); + // HACL* performs copies under the hood so that self->state remains valid + // after this call. + Hacl_Streaming_SHA2_finish_512(self->state, digest); + return PyBytes_FromStringAndSize((const char *)digest, self->digestsize); +} + +/*[clinic input] +SHA256Type.hexdigest + +Return the digest value as a string of hexadecimal digits. +[clinic start generated code]*/ + +static PyObject * +SHA256Type_hexdigest_impl(SHA256object *self) +/*[clinic end generated code: output=96cb68996a780ab3 input=0cc4c714693010d1]*/ +{ + uint8_t digest[SHA256_DIGESTSIZE]; + assert(self->digestsize <= SHA256_DIGESTSIZE); + Hacl_Streaming_SHA2_finish_256(self->state, digest); + return _Py_strhex((const char *)digest, self->digestsize); +} + +/*[clinic input] +SHA512Type.hexdigest + +Return the digest value as a string of hexadecimal digits. +[clinic start generated code]*/ + +static PyObject * +SHA512Type_hexdigest_impl(SHA512object *self) +/*[clinic end generated code: output=cbd6f844aba1fe7c input=498b877b25cbe0a2]*/ +{ + uint8_t digest[SHA512_DIGESTSIZE]; + assert(self->digestsize <= SHA512_DIGESTSIZE); + Hacl_Streaming_SHA2_finish_512(self->state, digest); + return _Py_strhex((const char *)digest, self->digestsize); +} + +/*[clinic input] +SHA256Type.update + + obj: object + / + +Update this hash object's state with the provided string. +[clinic start generated code]*/ + +static PyObject * +SHA256Type_update(SHA256object *self, PyObject *obj) +/*[clinic end generated code: output=1b240f965ddbd8c6 input=b2d449d5b30f0f5a]*/ +{ + Py_buffer buf; + + GET_BUFFER_VIEW_OR_ERROUT(obj, &buf); + + update_256(self->state, buf.buf, buf.len); + + PyBuffer_Release(&buf); + Py_RETURN_NONE; +} + +/*[clinic input] +SHA512Type.update + + obj: object + / + +Update this hash object's state with the provided string. +[clinic start generated code]*/ + +static PyObject * +SHA512Type_update(SHA512object *self, PyObject *obj) +/*[clinic end generated code: output=745f51057a985884 input=ded2b46656566283]*/ +{ + Py_buffer buf; + + GET_BUFFER_VIEW_OR_ERROUT(obj, &buf); + + update_512(self->state, buf.buf, buf.len); + + PyBuffer_Release(&buf); + Py_RETURN_NONE; +} + +static PyMethodDef SHA256_methods[] = { + SHA256TYPE_COPY_METHODDEF + SHA256TYPE_DIGEST_METHODDEF + SHA256TYPE_HEXDIGEST_METHODDEF + SHA256TYPE_UPDATE_METHODDEF + {NULL, NULL} /* sentinel */ +}; + +static PyMethodDef SHA512_methods[] = { + SHA512TYPE_COPY_METHODDEF + SHA512TYPE_DIGEST_METHODDEF + SHA512TYPE_HEXDIGEST_METHODDEF + SHA512TYPE_UPDATE_METHODDEF + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +SHA256_get_block_size(PyObject *self, void *closure) +{ + return PyLong_FromLong(SHA256_BLOCKSIZE); +} + +static PyObject * +SHA512_get_block_size(PyObject *self, void *closure) +{ + return PyLong_FromLong(SHA512_BLOCKSIZE); +} + +static PyObject * +SHA256_get_digest_size(SHA256object *self, void *closure) +{ + return PyLong_FromLong(self->digestsize); +} + +static PyObject * +SHA512_get_digest_size(SHA512object *self, void *closure) +{ + return PyLong_FromLong(self->digestsize); +} + +static PyObject * +SHA256_get_name(SHA256object *self, void *closure) +{ + if (self->digestsize == 28) { + return PyUnicode_FromStringAndSize("sha224", 6); + } + return PyUnicode_FromStringAndSize("sha256", 6); +} + +static PyObject * +SHA512_get_name(SHA512object *self, void *closure) +{ + if (self->digestsize == 64) { + return PyUnicode_FromStringAndSize("sha512", 6); + } + return PyUnicode_FromStringAndSize("sha384", 6); +} + +static PyGetSetDef SHA256_getseters[] = { + {"block_size", + (getter)SHA256_get_block_size, NULL, + NULL, + NULL}, + {"name", + (getter)SHA256_get_name, NULL, + NULL, + NULL}, + {"digest_size", + (getter)SHA256_get_digest_size, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; + +static PyGetSetDef SHA512_getseters[] = { + {"block_size", + (getter)SHA512_get_block_size, NULL, + NULL, + NULL}, + {"name", + (getter)SHA512_get_name, NULL, + NULL, + NULL}, + {"digest_size", + (getter)SHA512_get_digest_size, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; + +static PyType_Slot sha256_types_slots[] = { + {Py_tp_dealloc, SHA256_dealloc}, + {Py_tp_methods, SHA256_methods}, + {Py_tp_getset, SHA256_getseters}, + {Py_tp_traverse, SHA2_traverse}, + {0,0} +}; + +static PyType_Slot sha512_type_slots[] = { + {Py_tp_dealloc, SHA512_dealloc}, + {Py_tp_methods, SHA512_methods}, + {Py_tp_getset, SHA512_getseters}, + {Py_tp_traverse, SHA2_traverse}, + {0,0} +}; + +// Using PyType_GetModuleState() on these types is safe since they +// cannot be subclassed: they don't have the Py_TPFLAGS_BASETYPE flag. +static PyType_Spec sha224_type_spec = { + .name = "_sha2.SHA224Type", + .basicsize = sizeof(SHA256object), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), + .slots = sha256_types_slots +}; + +static PyType_Spec sha256_type_spec = { + .name = "_sha2.SHA256Type", + .basicsize = sizeof(SHA256object), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), + .slots = sha256_types_slots +}; + +static PyType_Spec sha384_type_spec = { + .name = "_sha2.SHA384Type", + .basicsize = sizeof(SHA512object), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), + .slots = sha512_type_slots +}; + +static PyType_Spec sha512_type_spec = { + .name = "_sha2.SHA512Type", + .basicsize = sizeof(SHA512object), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), + .slots = sha512_type_slots +}; + +/* The module-level constructors. */ + +/*[clinic input] +_sha2.sha256 + + string: object(c_default="NULL") = b'' + * + usedforsecurity: bool = True + +Return a new SHA-256 hash object; optionally initialized with a string. +[clinic start generated code]*/ + +static PyObject * +_sha2_sha256_impl(PyObject *module, PyObject *string, int usedforsecurity) +/*[clinic end generated code: output=243c9dd289931f87 input=6249da1de607280a]*/ +{ + Py_buffer buf; + + if (string) { + GET_BUFFER_VIEW_OR_ERROUT(string, &buf); + } + + sha2_state *state = sha2_get_state(module); + + SHA256object *new; + if ((new = newSHA256object(state)) == NULL) { + if (string) { + PyBuffer_Release(&buf); + } + return NULL; + } + + new->state = Hacl_Streaming_SHA2_create_in_256(); + new->digestsize = 32; + + if (PyErr_Occurred()) { + Py_DECREF(new); + if (string) { + PyBuffer_Release(&buf); + } + return NULL; + } + if (string) { + update_256(new->state, buf.buf, buf.len); + PyBuffer_Release(&buf); + } + + return (PyObject *)new; +} + +/*[clinic input] +_sha2.sha224 + + string: object(c_default="NULL") = b'' + * + usedforsecurity: bool = True + +Return a new SHA-224 hash object; optionally initialized with a string. +[clinic start generated code]*/ + +static PyObject * +_sha2_sha224_impl(PyObject *module, PyObject *string, int usedforsecurity) +/*[clinic end generated code: output=68191f232e4a3843 input=c42bcba47fd7d2b7]*/ +{ + Py_buffer buf; + if (string) { + GET_BUFFER_VIEW_OR_ERROUT(string, &buf); + } + + sha2_state *state = sha2_get_state(module); + SHA256object *new; + if ((new = newSHA224object(state)) == NULL) { + if (string) { + PyBuffer_Release(&buf); + } + return NULL; + } + + new->state = Hacl_Streaming_SHA2_create_in_224(); + new->digestsize = 28; + + if (PyErr_Occurred()) { + Py_DECREF(new); + if (string) { + PyBuffer_Release(&buf); + } + return NULL; + } + if (string) { + update_256(new->state, buf.buf, buf.len); + PyBuffer_Release(&buf); + } + + return (PyObject *)new; +} + +/*[clinic input] +_sha2.sha512 + + string: object(c_default="NULL") = b'' + * + usedforsecurity: bool = True + +Return a new SHA-512 hash object; optionally initialized with a string. +[clinic start generated code]*/ + +static PyObject * +_sha2_sha512_impl(PyObject *module, PyObject *string, int usedforsecurity) +/*[clinic end generated code: output=d55c8996eca214d7 input=0576ae2a6ebfad25]*/ +{ + SHA512object *new; + Py_buffer buf; + + sha2_state *state = sha2_get_state(module); + + if (string) + GET_BUFFER_VIEW_OR_ERROUT(string, &buf); + + if ((new = newSHA512object(state)) == NULL) { + if (string) + PyBuffer_Release(&buf); + return NULL; + } + + new->state = Hacl_Streaming_SHA2_create_in_512(); + new->digestsize = 64; + + if (PyErr_Occurred()) { + Py_DECREF(new); + if (string) + PyBuffer_Release(&buf); + return NULL; + } + if (string) { + update_512(new->state, buf.buf, buf.len); + PyBuffer_Release(&buf); + } + + return (PyObject *)new; +} + +/*[clinic input] +_sha2.sha384 + + string: object(c_default="NULL") = b'' + * + usedforsecurity: bool = True + +Return a new SHA-384 hash object; optionally initialized with a string. +[clinic start generated code]*/ + +static PyObject * +_sha2_sha384_impl(PyObject *module, PyObject *string, int usedforsecurity) +/*[clinic end generated code: output=b29a0d81d51d1368 input=4e9199d8de0d2f9b]*/ +{ + SHA512object *new; + Py_buffer buf; + + sha2_state *state = sha2_get_state(module); + + if (string) + GET_BUFFER_VIEW_OR_ERROUT(string, &buf); + + if ((new = newSHA384object(state)) == NULL) { + if (string) + PyBuffer_Release(&buf); + return NULL; + } + + new->state = Hacl_Streaming_SHA2_create_in_384(); + new->digestsize = 48; + + if (PyErr_Occurred()) { + Py_DECREF(new); + if (string) + PyBuffer_Release(&buf); + return NULL; + } + if (string) { + update_512(new->state, buf.buf, buf.len); + PyBuffer_Release(&buf); + } + + return (PyObject *)new; +} + +/* List of functions exported by this module */ + +static struct PyMethodDef SHA2_functions[] = { + _SHA2_SHA256_METHODDEF + _SHA2_SHA224_METHODDEF + _SHA2_SHA512_METHODDEF + _SHA2_SHA384_METHODDEF + {NULL, NULL} /* Sentinel */ +}; + +static int +_sha2_traverse(PyObject *module, visitproc visit, void *arg) +{ + sha2_state *state = sha2_get_state(module); + Py_VISIT(state->sha224_type); + Py_VISIT(state->sha256_type); + Py_VISIT(state->sha384_type); + Py_VISIT(state->sha512_type); + return 0; +} + +static int +_sha2_clear(PyObject *module) +{ + sha2_state *state = sha2_get_state(module); + Py_CLEAR(state->sha224_type); + Py_CLEAR(state->sha256_type); + Py_CLEAR(state->sha384_type); + Py_CLEAR(state->sha512_type); + return 0; +} + +static void +_sha2_free(void *module) +{ + _sha2_clear((PyObject *)module); +} + +/* Initialize this module. */ +static int sha2_exec(PyObject *module) +{ + sha2_state *state = sha2_get_state(module); + + state->sha224_type = (PyTypeObject *)PyType_FromModuleAndSpec( + module, &sha224_type_spec, NULL); + if (state->sha224_type == NULL) { + return -1; + } + state->sha256_type = (PyTypeObject *)PyType_FromModuleAndSpec( + module, &sha256_type_spec, NULL); + if (state->sha256_type == NULL) { + return -1; + } + state->sha384_type = (PyTypeObject *)PyType_FromModuleAndSpec( + module, &sha384_type_spec, NULL); + if (state->sha384_type == NULL) { + return -1; + } + state->sha512_type = (PyTypeObject *)PyType_FromModuleAndSpec( + module, &sha512_type_spec, NULL); + if (state->sha512_type == NULL) { + return -1; + } + + if (PyModule_AddType(module, state->sha224_type) < 0) { + return -1; + } + if (PyModule_AddType(module, state->sha256_type) < 0) { + return -1; + } + if (PyModule_AddType(module, state->sha384_type) < 0) { + return -1; + } + if (PyModule_AddType(module, state->sha512_type) < 0) { + return -1; + } + + return 0; +} + +static PyModuleDef_Slot _sha2_slots[] = { + {Py_mod_exec, sha2_exec}, + {0, NULL} +}; + +static struct PyModuleDef _sha2module = { + PyModuleDef_HEAD_INIT, + .m_name = "_sha2", + .m_size = sizeof(sha2_state), + .m_methods = SHA2_functions, + .m_slots = _sha2_slots, + .m_traverse = _sha2_traverse, + .m_clear = _sha2_clear, + .m_free = _sha2_free +}; + +PyMODINIT_FUNC +PyInit__sha2(void) +{ + return PyModuleDef_Init(&_sha2module); +} diff --git a/Modules/sha512module.c b/Modules/sha512module.c deleted file mode 100644 index d7dfed4e5db03a..00000000000000 --- a/Modules/sha512module.c +++ /dev/null @@ -1,456 +0,0 @@ -/* SHA512 module */ - -/* This module provides an interface to NIST's SHA-512 and SHA-384 Algorithms */ - -/* See below for information about the original code this module was - based upon. Additional work performed by: - - Andrew Kuchling (amk@amk.ca) - Greg Stein (gstein@lyra.org) - Trevor Perrin (trevp@trevp.net) - Jonathan Protzenko (jonathan@protzenko.fr) - - Copyright (C) 2005-2007 Gregory P. Smith (greg@krypto.org) - Licensed to PSF under a Contributor Agreement. - -*/ - -/* SHA objects */ -#ifndef Py_BUILD_CORE_BUILTIN -# define Py_BUILD_CORE_MODULE 1 -#endif - -#include "Python.h" -#include "pycore_bitutils.h" // _Py_bswap64() -#include "pycore_strhex.h" // _Py_strhex() -#include "structmember.h" // PyMemberDef -#include "hashlib.h" - -/*[clinic input] -module _sha512 -class SHA512Type "SHAobject *" "&PyType_Type" -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=81a3ccde92bcfe8d]*/ - - -/* The SHA block size and message digest sizes, in bytes */ - -#define SHA_BLOCKSIZE 128 -#define SHA_DIGESTSIZE 64 - -/* The SHA2-384 and SHA2-512 implementations defer to the HACL* verified - * library. */ - -#include "_hacl/Hacl_Streaming_SHA2.h" - -typedef struct { - PyObject_HEAD - int digestsize; - Hacl_Streaming_SHA2_state_sha2_512 *state; -} SHAobject; - -#include "clinic/sha512module.c.h" - - -static void SHAcopy(SHAobject *src, SHAobject *dest) -{ - dest->digestsize = src->digestsize; - dest->state = Hacl_Streaming_SHA2_copy_512(src->state); -} - -typedef struct { - PyTypeObject* sha384_type; - PyTypeObject* sha512_type; -} SHA512State; - -static inline SHA512State* -sha512_get_state(PyObject *module) -{ - void *state = PyModule_GetState(module); - assert(state != NULL); - return (SHA512State *)state; -} - -static SHAobject * -newSHA384object(SHA512State *st) -{ - SHAobject *sha = (SHAobject *)PyObject_GC_New(SHAobject, st->sha384_type); - PyObject_GC_Track(sha); - return sha; -} - -static SHAobject * -newSHA512object(SHA512State *st) -{ - SHAobject *sha = (SHAobject *)PyObject_GC_New(SHAobject, st->sha512_type); - PyObject_GC_Track(sha); - return sha; -} - -/* Internal methods for a hash object */ -static int -SHA_traverse(PyObject *ptr, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(ptr)); - return 0; -} - -static void -SHA512_dealloc(SHAobject *ptr) -{ - Hacl_Streaming_SHA2_free_512(ptr->state); - PyTypeObject *tp = Py_TYPE(ptr); - PyObject_GC_UnTrack(ptr); - PyObject_GC_Del(ptr); - Py_DECREF(tp); -} - -/* HACL* takes a uint32_t for the length of its parameter, but Py_ssize_t can be - * 64 bits. */ -static void update_512(Hacl_Streaming_SHA2_state_sha2_512 *state, uint8_t *buf, Py_ssize_t len) { - /* Note: we explicitly ignore the error code on the basis that it would take > - * 1 billion years to overflow the maximum admissible length for this API - * (namely, 2^64-1 bytes). */ - while (len > UINT32_MAX) { - Hacl_Streaming_SHA2_update_512(state, buf, UINT32_MAX); - len -= UINT32_MAX; - buf += UINT32_MAX; - } - /* Cast to uint32_t is safe: upon exiting the loop, len <= UINT32_MAX, and - * therefore fits in a uint32_t */ - Hacl_Streaming_SHA2_update_512(state, buf, (uint32_t) len); -} - - -/* External methods for a hash object */ - -/*[clinic input] -SHA512Type.copy - - cls: defining_class - -Return a copy of the hash object. -[clinic start generated code]*/ - -static PyObject * -SHA512Type_copy_impl(SHAobject *self, PyTypeObject *cls) -/*[clinic end generated code: output=85ea5b47837a08e6 input=f673a18f66527c90]*/ -{ - SHAobject *newobj; - SHA512State *st = PyType_GetModuleState(cls); - - if (Py_IS_TYPE((PyObject*)self, st->sha512_type)) { - if ( (newobj = newSHA512object(st))==NULL) { - return NULL; - } - } - else { - if ( (newobj = newSHA384object(st))==NULL) { - return NULL; - } - } - - SHAcopy(self, newobj); - return (PyObject *)newobj; -} - -/*[clinic input] -SHA512Type.digest - -Return the digest value as a bytes object. -[clinic start generated code]*/ - -static PyObject * -SHA512Type_digest_impl(SHAobject *self) -/*[clinic end generated code: output=1080bbeeef7dde1b input=f6470dd359071f4b]*/ -{ - uint8_t digest[SHA_DIGESTSIZE]; - // HACL performs copies under the hood so that self->state remains valid - // after this call. - Hacl_Streaming_SHA2_finish_512(self->state, digest); - return PyBytes_FromStringAndSize((const char *)digest, self->digestsize); -} - -/*[clinic input] -SHA512Type.hexdigest - -Return the digest value as a string of hexadecimal digits. -[clinic start generated code]*/ - -static PyObject * -SHA512Type_hexdigest_impl(SHAobject *self) -/*[clinic end generated code: output=7373305b8601e18b input=498b877b25cbe0a2]*/ -{ - uint8_t digest[SHA_DIGESTSIZE]; - Hacl_Streaming_SHA2_finish_512(self->state, digest); - return _Py_strhex((const char *)digest, self->digestsize); -} - -/*[clinic input] -SHA512Type.update - - obj: object - / - -Update this hash object's state with the provided string. -[clinic start generated code]*/ - -static PyObject * -SHA512Type_update(SHAobject *self, PyObject *obj) -/*[clinic end generated code: output=1cf333e73995a79e input=ded2b46656566283]*/ -{ - Py_buffer buf; - - GET_BUFFER_VIEW_OR_ERROUT(obj, &buf); - - update_512(self->state, buf.buf, buf.len); - - PyBuffer_Release(&buf); - Py_RETURN_NONE; -} - -static PyMethodDef SHA_methods[] = { - SHA512TYPE_COPY_METHODDEF - SHA512TYPE_DIGEST_METHODDEF - SHA512TYPE_HEXDIGEST_METHODDEF - SHA512TYPE_UPDATE_METHODDEF - {NULL, NULL} /* sentinel */ -}; - -static PyObject * -SHA512_get_block_size(PyObject *self, void *closure) -{ - return PyLong_FromLong(SHA_BLOCKSIZE); -} - -static PyObject * -SHA512_get_name(PyObject *self, void *closure) -{ - if (((SHAobject *)self)->digestsize == 64) - return PyUnicode_FromStringAndSize("sha512", 6); - else - return PyUnicode_FromStringAndSize("sha384", 6); -} - -static PyGetSetDef SHA_getseters[] = { - {"block_size", - (getter)SHA512_get_block_size, NULL, - NULL, - NULL}, - {"name", - (getter)SHA512_get_name, NULL, - NULL, - NULL}, - {NULL} /* Sentinel */ -}; - -static PyMemberDef SHA_members[] = { - {"digest_size", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL}, - {NULL} /* Sentinel */ -}; - -static PyType_Slot sha512_sha384_type_slots[] = { - {Py_tp_dealloc, SHA512_dealloc}, - {Py_tp_methods, SHA_methods}, - {Py_tp_members, SHA_members}, - {Py_tp_getset, SHA_getseters}, - {Py_tp_traverse, SHA_traverse}, - {0,0} -}; - -static PyType_Spec sha512_sha384_type_spec = { - .name = "_sha512.sha384", - .basicsize = sizeof(SHAobject), - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | - Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), - .slots = sha512_sha384_type_slots -}; - -// Using PyType_GetModuleState() on this type is safe since -// it cannot be subclassed: it does not have the Py_TPFLAGS_BASETYPE flag. -static PyType_Spec sha512_sha512_type_spec = { - .name = "_sha512.sha512", - .basicsize = sizeof(SHAobject), - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | - Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC), - .slots = sha512_sha384_type_slots -}; - -/* The single module-level function: new() */ - -/*[clinic input] -_sha512.sha512 - - string: object(c_default="NULL") = b'' - * - usedforsecurity: bool = True - -Return a new SHA-512 hash object; optionally initialized with a string. -[clinic start generated code]*/ - -static PyObject * -_sha512_sha512_impl(PyObject *module, PyObject *string, int usedforsecurity) -/*[clinic end generated code: output=a8d9e5f9e6a0831c input=23b4daebc2ebb9c9]*/ -{ - SHAobject *new; - Py_buffer buf; - - SHA512State *st = sha512_get_state(module); - - if (string) - GET_BUFFER_VIEW_OR_ERROUT(string, &buf); - - if ((new = newSHA512object(st)) == NULL) { - if (string) - PyBuffer_Release(&buf); - return NULL; - } - - new->state = Hacl_Streaming_SHA2_create_in_512(); - new->digestsize = 64; - - if (PyErr_Occurred()) { - Py_DECREF(new); - if (string) - PyBuffer_Release(&buf); - return NULL; - } - if (string) { - update_512(new->state, buf.buf, buf.len); - PyBuffer_Release(&buf); - } - - return (PyObject *)new; -} - -/*[clinic input] -_sha512.sha384 - - string: object(c_default="NULL") = b'' - * - usedforsecurity: bool = True - -Return a new SHA-384 hash object; optionally initialized with a string. -[clinic start generated code]*/ - -static PyObject * -_sha512_sha384_impl(PyObject *module, PyObject *string, int usedforsecurity) -/*[clinic end generated code: output=da7d594a08027ac3 input=59ef72f039a6b431]*/ -{ - SHAobject *new; - Py_buffer buf; - - SHA512State *st = sha512_get_state(module); - - if (string) - GET_BUFFER_VIEW_OR_ERROUT(string, &buf); - - if ((new = newSHA384object(st)) == NULL) { - if (string) - PyBuffer_Release(&buf); - return NULL; - } - - new->state = Hacl_Streaming_SHA2_create_in_384(); - new->digestsize = 48; - - if (PyErr_Occurred()) { - Py_DECREF(new); - if (string) - PyBuffer_Release(&buf); - return NULL; - } - if (string) { - update_512(new->state, buf.buf, buf.len); - PyBuffer_Release(&buf); - } - - return (PyObject *)new; -} - - -/* List of functions exported by this module */ - -static struct PyMethodDef SHA_functions[] = { - _SHA512_SHA512_METHODDEF - _SHA512_SHA384_METHODDEF - {NULL, NULL} /* Sentinel */ -}; - -static int -_sha512_traverse(PyObject *module, visitproc visit, void *arg) -{ - SHA512State *state = sha512_get_state(module); - Py_VISIT(state->sha384_type); - Py_VISIT(state->sha512_type); - return 0; -} - -static int -_sha512_clear(PyObject *module) -{ - SHA512State *state = sha512_get_state(module); - Py_CLEAR(state->sha384_type); - Py_CLEAR(state->sha512_type); - return 0; -} - -static void -_sha512_free(void *module) -{ - _sha512_clear((PyObject *)module); -} - - -/* Initialize this module. */ -static int -_sha512_exec(PyObject *m) -{ - SHA512State* st = sha512_get_state(m); - - st->sha384_type = (PyTypeObject *)PyType_FromModuleAndSpec( - m, &sha512_sha384_type_spec, NULL); - - st->sha512_type = (PyTypeObject *)PyType_FromModuleAndSpec( - m, &sha512_sha512_type_spec, NULL); - - if (st->sha384_type == NULL || st->sha512_type == NULL) { - return -1; - } - - Py_INCREF(st->sha384_type); - if (PyModule_AddObject(m, "SHA384Type", (PyObject *)st->sha384_type) < 0) { - Py_DECREF(st->sha384_type); - return -1; - } - - Py_INCREF(st->sha512_type); - if (PyModule_AddObject(m, "SHA384Type", (PyObject *)st->sha512_type) < 0) { - Py_DECREF(st->sha512_type); - return -1; - } - - return 0; -} - -static PyModuleDef_Slot _sha512_slots[] = { - {Py_mod_exec, _sha512_exec}, - {0, NULL} -}; - -static struct PyModuleDef _sha512module = { - PyModuleDef_HEAD_INIT, - .m_name = "_sha512", - .m_size = sizeof(SHA512State), - .m_methods = SHA_functions, - .m_slots = _sha512_slots, - .m_traverse = _sha512_traverse, - .m_clear = _sha512_clear, - .m_free = _sha512_free -}; - -PyMODINIT_FUNC -PyInit__sha512(void) -{ - return PyModuleDef_Init(&_sha512module); -} diff --git a/PC/config.c b/PC/config.c index cdb5db23c4ae49..b1481d79e6508d 100644 --- a/PC/config.c +++ b/PC/config.c @@ -20,8 +20,7 @@ extern PyObject* PyInit_nt(void); extern PyObject* PyInit__operator(void); extern PyObject* PyInit__signal(void); extern PyObject* PyInit__sha1(void); -extern PyObject* PyInit__sha256(void); -extern PyObject* PyInit__sha512(void); +extern PyObject* PyInit__sha2(void); extern PyObject* PyInit__sha3(void); extern PyObject* PyInit__statistics(void); extern PyObject* PyInit__typing(void); @@ -98,8 +97,7 @@ struct _inittab _PyImport_Inittab[] = { {"_signal", PyInit__signal}, {"_md5", PyInit__md5}, {"_sha1", PyInit__sha1}, - {"_sha256", PyInit__sha256}, - {"_sha512", PyInit__sha512}, + {"_sha2", PyInit__sha2}, {"_sha3", PyInit__sha3}, {"_blake2", PyInit__blake2}, {"time", PyInit_time}, diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index e8e9ff01e306bc..222963bc42d17c 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -408,8 +408,7 @@ - - + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 4820db6f2c32dc..efb96222043ac2 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -869,10 +869,7 @@ Modules - - Modules - - + Modules diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h index 4e7dfb14d19dec..e9f0061a59d3ba 100644 --- a/Python/stdlib_module_names.h +++ b/Python/stdlib_module_names.h @@ -63,9 +63,8 @@ static const char* _Py_stdlib_module_names[] = { "_random", "_scproxy", "_sha1", -"_sha256", +"_sha2", "_sha3", -"_sha512", "_signal", "_sitebuiltins", "_socket", diff --git a/configure b/configure index c00a1e1d2ec986..7c4254f3cb176f 100755 --- a/configure +++ b/configure @@ -686,10 +686,8 @@ MODULE__BLAKE2_FALSE MODULE__BLAKE2_TRUE MODULE__SHA3_FALSE MODULE__SHA3_TRUE -MODULE__SHA512_FALSE -MODULE__SHA512_TRUE -MODULE__SHA256_FALSE -MODULE__SHA256_TRUE +MODULE__SHA2_FALSE +MODULE__SHA2_TRUE MODULE__SHA1_FALSE MODULE__SHA1_TRUE MODULE__MD5_FALSE @@ -1891,9 +1889,9 @@ Optional Packages: leave OpenSSL's defaults untouched, STRING: use a custom string, python and STRING also set TLS 1.2 as minimum TLS version - --with-builtin-hashlib-hashes=md5,sha1,sha256,sha512,sha3,blake2 - builtin hash modules, md5, sha1, sha256, sha512, - sha3 (with shake), blake2 + --with-builtin-hashlib-hashes=md5,sha1,sha2,sha3,blake2 + builtin hash modules, md5, sha1, sha2, sha3 (with + shake), blake2 Some influential environment variables: PKG_CONFIG path to pkg-config utility @@ -25346,7 +25344,7 @@ fi # builtin hash modules -default_hashlib_hashes="md5,sha1,sha256,sha512,sha3,blake2" +default_hashlib_hashes="md5,sha1,sha2,sha3,blake2" $as_echo "#define PY_BUILTIN_HASHLIB_HASHES /**/" >>confdefs.h @@ -25386,10 +25384,8 @@ for builtin_hash in $with_builtin_hashlib_hashes; do with_builtin_md5=yes ;; #( sha1) : with_builtin_sha1=yes ;; #( - sha256) : - with_builtin_sha256=yes ;; #( - sha512) : - with_builtin_sha512=yes ;; #( + sha2) : + with_builtin_sha2=yes ;; #( sha3) : with_builtin_sha3=yes ;; #( blake2) : @@ -26898,72 +26894,38 @@ fi $as_echo "$py_cv_module__sha1" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module _sha256" >&5 -$as_echo_n "checking for stdlib extension module _sha256... " >&6; } - if test "$py_cv_module__sha256" != "n/a"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module _sha2" >&5 +$as_echo_n "checking for stdlib extension module _sha2... " >&6; } + if test "$py_cv_module__sha2" != "n/a"; then : - if test "$with_builtin_sha256" = yes; then : + if test "$with_builtin_sha2" = yes; then : if true; then : - py_cv_module__sha256=yes + py_cv_module__sha2=yes else - py_cv_module__sha256=missing + py_cv_module__sha2=missing fi else - py_cv_module__sha256=disabled + py_cv_module__sha2=disabled fi fi - as_fn_append MODULE_BLOCK "MODULE__SHA256_STATE=$py_cv_module__sha256$as_nl" - if test "x$py_cv_module__sha256" = xyes; then : + as_fn_append MODULE_BLOCK "MODULE__SHA2_STATE=$py_cv_module__sha2$as_nl" + if test "x$py_cv_module__sha2" = xyes; then : - as_fn_append MODULE_BLOCK "MODULE__SHA256_CFLAGS=-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE$as_nl" + as_fn_append MODULE_BLOCK "MODULE__SHA2_CFLAGS=-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE$as_nl" fi - if test "$py_cv_module__sha256" = yes; then - MODULE__SHA256_TRUE= - MODULE__SHA256_FALSE='#' + if test "$py_cv_module__sha2" = yes; then + MODULE__SHA2_TRUE= + MODULE__SHA2_FALSE='#' else - MODULE__SHA256_TRUE='#' - MODULE__SHA256_FALSE= + MODULE__SHA2_TRUE='#' + MODULE__SHA2_FALSE= fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $py_cv_module__sha256" >&5 -$as_echo "$py_cv_module__sha256" >&6; } - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module _sha512" >&5 -$as_echo_n "checking for stdlib extension module _sha512... " >&6; } - if test "$py_cv_module__sha512" != "n/a"; then : - - if test "$with_builtin_sha512" = yes; then : - if true; then : - py_cv_module__sha512=yes -else - py_cv_module__sha512=missing -fi -else - py_cv_module__sha512=disabled -fi - -fi - as_fn_append MODULE_BLOCK "MODULE__SHA512_STATE=$py_cv_module__sha512$as_nl" - if test "x$py_cv_module__sha512" = xyes; then : - - as_fn_append MODULE_BLOCK "MODULE__SHA512_CFLAGS=-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE$as_nl" - - -fi - if test "$py_cv_module__sha512" = yes; then - MODULE__SHA512_TRUE= - MODULE__SHA512_FALSE='#' -else - MODULE__SHA512_TRUE='#' - MODULE__SHA512_FALSE= -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $py_cv_module__sha512" >&5 -$as_echo "$py_cv_module__sha512" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $py_cv_module__sha2" >&5 +$as_echo "$py_cv_module__sha2" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module _sha3" >&5 @@ -28337,12 +28299,8 @@ if test -z "${MODULE__SHA1_TRUE}" && test -z "${MODULE__SHA1_FALSE}"; then as_fn_error $? "conditional \"MODULE__SHA1\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi -if test -z "${MODULE__SHA256_TRUE}" && test -z "${MODULE__SHA256_FALSE}"; then - as_fn_error $? "conditional \"MODULE__SHA256\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi -if test -z "${MODULE__SHA512_TRUE}" && test -z "${MODULE__SHA512_FALSE}"; then - as_fn_error $? "conditional \"MODULE__SHA512\" was never defined. +if test -z "${MODULE__SHA2_TRUE}" && test -z "${MODULE__SHA2_FALSE}"; then + as_fn_error $? "conditional \"MODULE__SHA2\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${MODULE__SHA3_TRUE}" && test -z "${MODULE__SHA3_FALSE}"; then diff --git a/configure.ac b/configure.ac index 92a05c011026f2..370bbe07c57634 100644 --- a/configure.ac +++ b/configure.ac @@ -6928,14 +6928,14 @@ AC_DEFINE(PY_SSL_DEFAULT_CIPHERS, 1) ]) # builtin hash modules -default_hashlib_hashes="md5,sha1,sha256,sha512,sha3,blake2" +default_hashlib_hashes="md5,sha1,sha2,sha3,blake2" AC_DEFINE([PY_BUILTIN_HASHLIB_HASHES], [], [enabled builtin hash modules] ) AC_MSG_CHECKING(for --with-builtin-hashlib-hashes) AC_ARG_WITH(builtin-hashlib-hashes, - AS_HELP_STRING([--with-builtin-hashlib-hashes=md5,sha1,sha256,sha512,sha3,blake2], + AS_HELP_STRING([--with-builtin-hashlib-hashes=md5,sha1,sha2,sha3,blake2], [builtin hash modules, - md5, sha1, sha256, sha512, sha3 (with shake), blake2]), + md5, sha1, sha2, sha3 (with shake), blake2]), [ AS_CASE([$with_builtin_hashlib_hashes], [yes], [with_builtin_hashlib_hashes=$default_hashlib_hashes], @@ -6952,8 +6952,7 @@ for builtin_hash in $with_builtin_hashlib_hashes; do AS_CASE($builtin_hash, [md5], [with_builtin_md5=yes], [sha1], [with_builtin_sha1=yes], - [sha256], [with_builtin_sha256=yes], - [sha512], [with_builtin_sha512=yes], + [sha2], [with_builtin_sha2=yes], [sha3], [with_builtin_sha3=yes], [blake2], [with_builtin_blake2=yes] ) @@ -7197,11 +7196,8 @@ dnl By default we always compile these even when OpenSSL is available dnl (issue #14693). The modules are small. PY_STDLIB_MOD([_md5], [test "$with_builtin_md5" = yes]) PY_STDLIB_MOD([_sha1], [test "$with_builtin_sha1" = yes]) -PY_STDLIB_MOD([_sha256], - [test "$with_builtin_sha256" = yes], [], - [-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE]) -PY_STDLIB_MOD([_sha512], - [test "$with_builtin_sha512" = yes], [], +PY_STDLIB_MOD([_sha2], + [test "$with_builtin_sha2" = yes], [], [-I\$(srcdir)/Modules/_hacl/include -I\$(srcdir)/Modules/_hacl/internal -D_BSD_SOURCE -D_DEFAULT_SOURCE]) PY_STDLIB_MOD([_sha3], [test "$with_builtin_sha3" = yes]) PY_STDLIB_MOD([_blake2], From df7ccf6138b1a2ce0b82ff06aa3497ca4d38c90d Mon Sep 17 00:00:00 2001 From: penguin_wwy <940375606@qq.com> Date: Thu, 16 Feb 2023 19:31:41 +0800 Subject: [PATCH 184/225] gh-101928: fix crash in compiler on multi-line lambda in function call (#101933) --- Lib/test/test_compile.py | 11 +++++++++++ Python/compile.c | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 90b067bcf30912..a77742c0cfa6fc 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -1155,6 +1155,17 @@ def test_if_expression_expression_empty_block(self): with self.subTest(expr=expr): compile(expr, "", "exec") + def test_multi_line_lambda_as_argument(self): + # See gh-101928 + compile(""" +def foo(param, lambda_exp): + pass + +foo(param=0, + lambda_exp=lambda: + 1) + """, "", "exec") + @requires_debug_ranges() class TestSourcePositions(unittest.TestCase): diff --git a/Python/compile.c b/Python/compile.c index 0534b536e3d12e..c3b344c7af2a7f 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -9085,8 +9085,8 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) Py_DECREF(cnt); break; case RETURN_VALUE: - INSTR_SET_OP1(inst, RETURN_CONST, oparg); - INSTR_SET_OP0(&bb->b_instr[i + 1], NOP); + INSTR_SET_OP0(inst, NOP); + INSTR_SET_OP1(&bb->b_instr[++i], RETURN_CONST, oparg); break; } break; From 36b139af638cdeb671cb6b8b0315b254148688f7 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Thu, 16 Feb 2023 12:31:59 +0000 Subject: [PATCH 185/225] gh-101951: use textwrap.dedent in compiler tests to make them more readable (GH-101950) Fixes #101951. Automerge-Triggered-By: GH:iritkatriel --- Lib/test/test_compile.py | 288 ++++++++++++++++++++------------------- 1 file changed, 145 insertions(+), 143 deletions(-) diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index a77742c0cfa6fc..fe775779c50f50 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -115,24 +115,24 @@ def test_extended_arg(self): repeat = 2000 longexpr = 'x = x or ' + '-x' * repeat g = {} - code = ''' -def f(x): - %s - %s - %s - %s - %s - %s - %s - %s - %s - %s - # the expressions above have no effect, x == argument - while x: - x -= 1 - # EXTENDED_ARG/JUMP_ABSOLUTE here - return x -''' % ((longexpr,)*10) + code = textwrap.dedent(''' + def f(x): + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + # the expressions above have no effect, x == argument + while x: + x -= 1 + # EXTENDED_ARG/JUMP_ABSOLUTE here + return x + ''' % ((longexpr,)*10)) exec(code, g) self.assertEqual(g['f'](5), 0) @@ -148,10 +148,11 @@ def test_float_literals(self): def test_indentation(self): # testing compile() of indented block w/o trailing newline" - s = """ -if 1: - if 2: - pass""" + s = textwrap.dedent(""" + if 1: + if 2: + pass + """) compile(s, "", "exec") # This test is probably specific to CPython and may not generalize @@ -1157,14 +1158,15 @@ def test_if_expression_expression_empty_block(self): def test_multi_line_lambda_as_argument(self): # See gh-101928 - compile(""" -def foo(param, lambda_exp): - pass + code = textwrap.dedent(""" + def foo(param, lambda_exp): + pass -foo(param=0, - lambda_exp=lambda: - 1) - """, "", "exec") + foo(param=0, + lambda_exp=lambda: + 1) + """) + compile(code, "", "exec") @requires_debug_ranges() @@ -1252,24 +1254,24 @@ def test_compiles_to_extended_op_arg(self): column=2, end_column=9, occurrence=2) def test_multiline_expression(self): - snippet = """\ -f( - 1, 2, 3, 4 -) -""" + snippet = textwrap.dedent("""\ + f( + 1, 2, 3, 4 + ) + """) compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'CALL', line=1, end_line=3, column=0, end_column=1) @requires_specialization def test_multiline_boolean_expression(self): - snippet = """\ -if (a or - (b and not c) or - not ( - d > 0)): - x = 42 -""" + snippet = textwrap.dedent("""\ + if (a or + (b and not c) or + not ( + d > 0)): + x = 42 + """) compiled_code, _ = self.check_positions_against_ast(snippet) # jump if a is true: self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_TRUE', @@ -1288,11 +1290,11 @@ def test_multiline_boolean_expression(self): line=4, end_line=4, column=8, end_column=13, occurrence=2) def test_multiline_assert(self): - snippet = """\ -assert (a > 0 and - bb > 0 and - ccc == 4), "error msg" -""" + snippet = textwrap.dedent("""\ + assert (a > 0 and + bb > 0 and + ccc == 4), "error msg" + """) compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'LOAD_ASSERTION_ERROR', line=1, end_line=3, column=0, end_column=30, occurrence=1) @@ -1305,14 +1307,14 @@ def test_multiline_assert(self): line=1, end_line=3, column=0, end_column=30, occurrence=1) def test_multiline_generator_expression(self): - snippet = """\ -((x, - 2*x) - for x - in [1,2,3] if (x > 0 - and x < 100 - and x != 50)) -""" + snippet = textwrap.dedent("""\ + ((x, + 2*x) + for x + in [1,2,3] if (x > 0 + and x < 100 + and x != 50)) + """) compiled_code, _ = self.check_positions_against_ast(snippet) compiled_code = compiled_code.co_consts[0] self.assertIsInstance(compiled_code, types.CodeType) @@ -1324,14 +1326,14 @@ def test_multiline_generator_expression(self): line=1, end_line=6, column=0, end_column=32, occurrence=1) def test_multiline_async_generator_expression(self): - snippet = """\ -((x, - 2*x) - async for x - in [1,2,3] if (x > 0 - and x < 100 - and x != 50)) -""" + snippet = textwrap.dedent("""\ + ((x, + 2*x) + async for x + in [1,2,3] if (x > 0 + and x < 100 + and x != 50)) + """) compiled_code, _ = self.check_positions_against_ast(snippet) compiled_code = compiled_code.co_consts[0] self.assertIsInstance(compiled_code, types.CodeType) @@ -1341,14 +1343,14 @@ def test_multiline_async_generator_expression(self): line=1, end_line=6, column=0, end_column=32, occurrence=1) def test_multiline_list_comprehension(self): - snippet = """\ -[(x, - 2*x) - for x - in [1,2,3] if (x > 0 - and x < 100 - and x != 50)] -""" + snippet = textwrap.dedent("""\ + [(x, + 2*x) + for x + in [1,2,3] if (x > 0 + and x < 100 + and x != 50)] + """) compiled_code, _ = self.check_positions_against_ast(snippet) compiled_code = compiled_code.co_consts[0] self.assertIsInstance(compiled_code, types.CodeType) @@ -1360,15 +1362,15 @@ def test_multiline_list_comprehension(self): line=1, end_line=6, column=0, end_column=32, occurrence=1) def test_multiline_async_list_comprehension(self): - snippet = """\ -async def f(): - [(x, - 2*x) - async for x - in [1,2,3] if (x > 0 - and x < 100 - and x != 50)] -""" + snippet = textwrap.dedent("""\ + async def f(): + [(x, + 2*x) + async for x + in [1,2,3] if (x > 0 + and x < 100 + and x != 50)] + """) compiled_code, _ = self.check_positions_against_ast(snippet) g = {} eval(compiled_code, g) @@ -1382,14 +1384,14 @@ async def f(): line=2, end_line=7, column=4, end_column=36, occurrence=1) def test_multiline_set_comprehension(self): - snippet = """\ -{(x, - 2*x) - for x - in [1,2,3] if (x > 0 - and x < 100 - and x != 50)} -""" + snippet = textwrap.dedent("""\ + {(x, + 2*x) + for x + in [1,2,3] if (x > 0 + and x < 100 + and x != 50)} + """) compiled_code, _ = self.check_positions_against_ast(snippet) compiled_code = compiled_code.co_consts[0] self.assertIsInstance(compiled_code, types.CodeType) @@ -1401,15 +1403,15 @@ def test_multiline_set_comprehension(self): line=1, end_line=6, column=0, end_column=32, occurrence=1) def test_multiline_async_set_comprehension(self): - snippet = """\ -async def f(): - {(x, - 2*x) - async for x - in [1,2,3] if (x > 0 - and x < 100 - and x != 50)} -""" + snippet = textwrap.dedent("""\ + async def f(): + {(x, + 2*x) + async for x + in [1,2,3] if (x > 0 + and x < 100 + and x != 50)} + """) compiled_code, _ = self.check_positions_against_ast(snippet) g = {} eval(compiled_code, g) @@ -1423,14 +1425,14 @@ async def f(): line=2, end_line=7, column=4, end_column=36, occurrence=1) def test_multiline_dict_comprehension(self): - snippet = """\ -{x: - 2*x - for x - in [1,2,3] if (x > 0 - and x < 100 - and x != 50)} -""" + snippet = textwrap.dedent("""\ + {x: + 2*x + for x + in [1,2,3] if (x > 0 + and x < 100 + and x != 50)} + """) compiled_code, _ = self.check_positions_against_ast(snippet) compiled_code = compiled_code.co_consts[0] self.assertIsInstance(compiled_code, types.CodeType) @@ -1442,15 +1444,15 @@ def test_multiline_dict_comprehension(self): line=1, end_line=6, column=0, end_column=32, occurrence=1) def test_multiline_async_dict_comprehension(self): - snippet = """\ -async def f(): - {x: - 2*x - async for x - in [1,2,3] if (x > 0 - and x < 100 - and x != 50)} -""" + snippet = textwrap.dedent("""\ + async def f(): + {x: + 2*x + async for x + in [1,2,3] if (x > 0 + and x < 100 + and x != 50)} + """) compiled_code, _ = self.check_positions_against_ast(snippet) g = {} eval(compiled_code, g) @@ -1464,11 +1466,11 @@ async def f(): line=2, end_line=7, column=4, end_column=36, occurrence=1) def test_matchcase_sequence(self): - snippet = """\ -match x: - case a, b: - pass -""" + snippet = textwrap.dedent("""\ + match x: + case a, b: + pass + """) compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_SEQUENCE', line=2, end_line=2, column=9, end_column=13, occurrence=1) @@ -1480,11 +1482,11 @@ def test_matchcase_sequence(self): line=2, end_line=2, column=9, end_column=13, occurrence=2) def test_matchcase_sequence_wildcard(self): - snippet = """\ -match x: - case a, *b, c: - pass -""" + snippet = textwrap.dedent("""\ + match x: + case a, *b, c: + pass + """) compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_SEQUENCE', line=2, end_line=2, column=9, end_column=17, occurrence=1) @@ -1498,11 +1500,11 @@ def test_matchcase_sequence_wildcard(self): line=2, end_line=2, column=9, end_column=17, occurrence=3) def test_matchcase_mapping(self): - snippet = """\ -match x: - case {"a" : a, "b": b}: - pass -""" + snippet = textwrap.dedent("""\ + match x: + case {"a" : a, "b": b}: + pass + """) compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_MAPPING', line=2, end_line=2, column=9, end_column=26, occurrence=1) @@ -1514,11 +1516,11 @@ def test_matchcase_mapping(self): line=2, end_line=2, column=9, end_column=26, occurrence=2) def test_matchcase_mapping_wildcard(self): - snippet = """\ -match x: - case {"a" : a, "b": b, **c}: - pass -""" + snippet = textwrap.dedent("""\ + match x: + case {"a" : a, "b": b, **c}: + pass + """) compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_MAPPING', line=2, end_line=2, column=9, end_column=31, occurrence=1) @@ -1530,11 +1532,11 @@ def test_matchcase_mapping_wildcard(self): line=2, end_line=2, column=9, end_column=31, occurrence=2) def test_matchcase_class(self): - snippet = """\ -match x: - case C(a, b): - pass -""" + snippet = textwrap.dedent("""\ + match x: + case C(a, b): + pass + """) compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_CLASS', line=2, end_line=2, column=9, end_column=16, occurrence=1) @@ -1546,11 +1548,11 @@ def test_matchcase_class(self): line=2, end_line=2, column=9, end_column=16, occurrence=2) def test_matchcase_or(self): - snippet = """\ -match x: - case C(1) | C(2): - pass -""" + snippet = textwrap.dedent("""\ + match x: + case C(1) | C(2): + pass + """) compiled_code, _ = self.check_positions_against_ast(snippet) self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_CLASS', line=2, end_line=2, column=9, end_column=13, occurrence=1) From 739c026f4488bd2e37d500a2c3d948aaf929b641 Mon Sep 17 00:00:00 2001 From: Rayyan Ansari Date: Thu, 16 Feb 2023 14:52:24 +0000 Subject: [PATCH 186/225] gh-101881: Support (non-)blocking read/write functions on Windows pipes (GH-101882) * fileutils: handle non-blocking pipe IO on Windows Handle erroring operations on non-blocking pipes by reading the _doserrno code. Limit writes on non-blocking pipes that are too large. * Support blocking functions on Windows Use the GetNamedPipeHandleState and SetNamedPipeHandleState Win32 API functions to add support for os.get_blocking and os.set_blocking. --- Doc/library/os.rst | 12 ++- Include/internal/pycore_fileutils.h | 4 +- Lib/test/test_os.py | 1 + ...-02-13-18-05-49.gh-issue-101881._TnHzN.rst | 1 + ...-02-15-11-08-10.gh-issue-101881.fScr3m.rst | 1 + Modules/clinic/posixmodule.c.h | 18 +--- Modules/posixmodule.c | 2 - Python/fileutils.c | 93 ++++++++++++++++++- 8 files changed, 107 insertions(+), 25 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-02-13-18-05-49.gh-issue-101881._TnHzN.rst create mode 100644 Misc/NEWS.d/next/Windows/2023-02-15-11-08-10.gh-issue-101881.fScr3m.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index fb091176767f7a..85924d0e48366b 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1091,13 +1091,17 @@ as internal buffering of data. See also :func:`set_blocking` and :meth:`socket.socket.setblocking`. - .. availability:: Unix. + .. availability:: Unix, Windows. The function is limited on Emscripten and WASI, see :ref:`wasm-availability` for more information. + On Windows, this function is limited to pipes. + .. versionadded:: 3.5 + .. versionchanged:: 3.12 + Added support for pipes on Windows. .. function:: isatty(fd, /) @@ -1565,13 +1569,17 @@ or `the MSDN `_ on Windo See also :func:`get_blocking` and :meth:`socket.socket.setblocking`. - .. availability:: Unix. + .. availability:: Unix, Windows. The function is limited on Emscripten and WASI, see :ref:`wasm-availability` for more information. + On Windows, this function is limited to pipes. + .. versionadded:: 3.5 + .. versionchanged:: 3.12 + Added support for pipes on Windows. .. data:: SF_NODISKIO SF_MNOWAIT diff --git a/Include/internal/pycore_fileutils.h b/Include/internal/pycore_fileutils.h index ac89c43d569c07..f8e2bf22590888 100644 --- a/Include/internal/pycore_fileutils.h +++ b/Include/internal/pycore_fileutils.h @@ -160,11 +160,11 @@ PyAPI_FUNC(int) _Py_set_inheritable_async_safe(int fd, int inheritable, PyAPI_FUNC(int) _Py_dup(int fd); -#ifndef MS_WINDOWS PyAPI_FUNC(int) _Py_get_blocking(int fd); PyAPI_FUNC(int) _Py_set_blocking(int fd, int blocking); -#else /* MS_WINDOWS */ + +#ifdef MS_WINDOWS PyAPI_FUNC(void*) _Py_get_osfhandle_noraise(int fd); PyAPI_FUNC(void*) _Py_get_osfhandle(int fd); diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 387d2581c06fc6..deea207bfdadd9 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -4099,6 +4099,7 @@ def test_path_t_converter_and_custom_class(self): @unittest.skipUnless(hasattr(os, 'get_blocking'), 'needs os.get_blocking() and os.set_blocking()') @unittest.skipIf(support.is_emscripten, "Cannot unset blocking flag") +@unittest.skipIf(sys.platform == 'win32', 'Windows only supports blocking on pipes') class BlockingTests(unittest.TestCase): def test_blocking(self): fd = os.open(__file__, os.O_RDONLY) diff --git a/Misc/NEWS.d/next/Windows/2023-02-13-18-05-49.gh-issue-101881._TnHzN.rst b/Misc/NEWS.d/next/Windows/2023-02-13-18-05-49.gh-issue-101881._TnHzN.rst new file mode 100644 index 00000000000000..ba58dd4f5cb450 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-02-13-18-05-49.gh-issue-101881._TnHzN.rst @@ -0,0 +1 @@ +Add support for the os.get_blocking() and os.set_blocking() functions on Windows. diff --git a/Misc/NEWS.d/next/Windows/2023-02-15-11-08-10.gh-issue-101881.fScr3m.rst b/Misc/NEWS.d/next/Windows/2023-02-15-11-08-10.gh-issue-101881.fScr3m.rst new file mode 100644 index 00000000000000..099b2c1c07a665 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-02-15-11-08-10.gh-issue-101881.fScr3m.rst @@ -0,0 +1 @@ +Handle read and write operations on non-blocking pipes properly on Windows. diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 5e04507ddd6917..dcd25c28370c93 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -10402,8 +10402,6 @@ os_set_handle_inheritable(PyObject *module, PyObject *const *args, Py_ssize_t na #endif /* defined(MS_WINDOWS) */ -#if !defined(MS_WINDOWS) - PyDoc_STRVAR(os_get_blocking__doc__, "get_blocking($module, fd, /)\n" "--\n" @@ -10439,10 +10437,6 @@ os_get_blocking(PyObject *module, PyObject *arg) return return_value; } -#endif /* !defined(MS_WINDOWS) */ - -#if !defined(MS_WINDOWS) - PyDoc_STRVAR(os_set_blocking__doc__, "set_blocking($module, fd, blocking, /)\n" "--\n" @@ -10482,8 +10476,6 @@ os_set_blocking(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } -#endif /* !defined(MS_WINDOWS) */ - PyDoc_STRVAR(os_DirEntry_is_symlink__doc__, "is_symlink($self, /)\n" "--\n" @@ -11789,14 +11781,6 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #define OS_SET_HANDLE_INHERITABLE_METHODDEF #endif /* !defined(OS_SET_HANDLE_INHERITABLE_METHODDEF) */ -#ifndef OS_GET_BLOCKING_METHODDEF - #define OS_GET_BLOCKING_METHODDEF -#endif /* !defined(OS_GET_BLOCKING_METHODDEF) */ - -#ifndef OS_SET_BLOCKING_METHODDEF - #define OS_SET_BLOCKING_METHODDEF -#endif /* !defined(OS_SET_BLOCKING_METHODDEF) */ - #ifndef OS_GETRANDOM_METHODDEF #define OS_GETRANDOM_METHODDEF #endif /* !defined(OS_GETRANDOM_METHODDEF) */ @@ -11812,4 +11796,4 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF #define OS_WAITSTATUS_TO_EXITCODE_METHODDEF #endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */ -/*[clinic end generated code: output=a3f76228b549e8ec input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1b0eb6a76b1a0e28 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index d9e93473aeadaa..524dc7eb1ccc97 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -13930,7 +13930,6 @@ os_set_handle_inheritable_impl(PyObject *module, intptr_t handle, } #endif /* MS_WINDOWS */ -#ifndef MS_WINDOWS /*[clinic input] os.get_blocking -> bool fd: int @@ -13978,7 +13977,6 @@ os_set_blocking_impl(PyObject *module, int fd, int blocking) return NULL; Py_RETURN_NONE; } -#endif /* !MS_WINDOWS */ /*[clinic input] diff --git a/Python/fileutils.c b/Python/fileutils.c index 22b2257a56d0ec..897c2f9f4ea160 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -1750,7 +1750,15 @@ _Py_read(int fd, void *buf, size_t count) Py_BEGIN_ALLOW_THREADS errno = 0; #ifdef MS_WINDOWS + _doserrno = 0; n = read(fd, buf, (int)count); + // read() on a non-blocking empty pipe fails with EINVAL, which is + // mapped from the Windows error code ERROR_NO_DATA. + if (n < 0 && errno == EINVAL) { + if (_doserrno == ERROR_NO_DATA) { + errno = EAGAIN; + } + } #else n = read(fd, buf, count); #endif @@ -1804,6 +1812,7 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held) } } } + #endif if (count > _PY_WRITE_MAX) { count = _PY_WRITE_MAX; @@ -1814,7 +1823,18 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held) Py_BEGIN_ALLOW_THREADS errno = 0; #ifdef MS_WINDOWS - n = write(fd, buf, (int)count); + // write() on a non-blocking pipe fails with ENOSPC on Windows if + // the pipe lacks available space for the entire buffer. + int c = (int)count; + do { + _doserrno = 0; + n = write(fd, buf, c); + if (n >= 0 || errno != ENOSPC || _doserrno != 0) { + break; + } + errno = EAGAIN; + c /= 2; + } while (c > 0); #else n = write(fd, buf, count); #endif @@ -1829,7 +1849,18 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held) do { errno = 0; #ifdef MS_WINDOWS - n = write(fd, buf, (int)count); + // write() on a non-blocking pipe fails with ENOSPC on Windows if + // the pipe lacks available space for the entire buffer. + int c = (int)count; + do { + _doserrno = 0; + n = write(fd, buf, c); + if (n >= 0 || errno != ENOSPC || _doserrno != 0) { + break; + } + errno = EAGAIN; + c /= 2; + } while (c > 0); #else n = write(fd, buf, count); #endif @@ -2450,6 +2481,64 @@ _Py_set_blocking(int fd, int blocking) return -1; } #else /* MS_WINDOWS */ +int +_Py_get_blocking(int fd) +{ + HANDLE handle; + DWORD mode; + BOOL success; + + handle = _Py_get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) { + return -1; + } + + Py_BEGIN_ALLOW_THREADS + success = GetNamedPipeHandleStateW(handle, &mode, + NULL, NULL, NULL, NULL, 0); + Py_END_ALLOW_THREADS + + if (!success) { + PyErr_SetFromWindowsErr(0); + return -1; + } + + return !(mode & PIPE_NOWAIT); +} + +int +_Py_set_blocking(int fd, int blocking) +{ + HANDLE handle; + DWORD mode; + BOOL success; + + handle = _Py_get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) { + return -1; + } + + Py_BEGIN_ALLOW_THREADS + success = GetNamedPipeHandleStateW(handle, &mode, + NULL, NULL, NULL, NULL, 0); + if (success) { + if (blocking) { + mode &= ~PIPE_NOWAIT; + } + else { + mode |= PIPE_NOWAIT; + } + success = SetNamedPipeHandleState(handle, &mode, NULL, NULL); + } + Py_END_ALLOW_THREADS + + if (!success) { + PyErr_SetFromWindowsErr(0); + return -1; + } + return 0; +} + void* _Py_get_osfhandle_noraise(int fd) { From 924a3bfa28578802eb9ca77a66fb5d4762a62f14 Mon Sep 17 00:00:00 2001 From: sblondon Date: Thu, 16 Feb 2023 16:13:21 +0100 Subject: [PATCH 187/225] gh-93573: Replace wrong example domains in configparser doc (GH-93574) * Replace bitbucket.org domain by forge.example * Update example to python.org * Use explicitly invalid domain topsecret.server.com domain is not controled by PSF. It's replaced by invalid topsecret.server.example domain. It follows RFC 2606, which advise .example as TLD for documentation. --- Doc/library/configparser.rst | 46 ++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/Doc/library/configparser.rst b/Doc/library/configparser.rst index a925a3dd4fb9c2..a7f75fd6e84f4c 100644 --- a/Doc/library/configparser.rst +++ b/Doc/library/configparser.rst @@ -69,10 +69,10 @@ Let's take a very basic configuration file that looks like this: CompressionLevel = 9 ForwardX11 = yes - [bitbucket.org] + [forge.example] User = hg - [topsecret.server.com] + [topsecret.server.example] Port = 50022 ForwardX11 = no @@ -89,10 +89,10 @@ creating the above configuration file programmatically. >>> config['DEFAULT'] = {'ServerAliveInterval': '45', ... 'Compression': 'yes', ... 'CompressionLevel': '9'} - >>> config['bitbucket.org'] = {} - >>> config['bitbucket.org']['User'] = 'hg' - >>> config['topsecret.server.com'] = {} - >>> topsecret = config['topsecret.server.com'] + >>> config['forge.example'] = {} + >>> config['forge.example']['User'] = 'hg' + >>> config['topsecret.server.example'] = {} + >>> topsecret = config['topsecret.server.example'] >>> topsecret['Port'] = '50022' # mutates the parser >>> topsecret['ForwardX11'] = 'no' # same here >>> config['DEFAULT']['ForwardX11'] = 'yes' @@ -115,28 +115,28 @@ back and explore the data it holds. >>> config.read('example.ini') ['example.ini'] >>> config.sections() - ['bitbucket.org', 'topsecret.server.com'] - >>> 'bitbucket.org' in config + ['forge.example', 'topsecret.server.example'] + >>> 'forge.example' in config True - >>> 'bytebong.com' in config + >>> 'python.org' in config False - >>> config['bitbucket.org']['User'] + >>> config['forge.example']['User'] 'hg' >>> config['DEFAULT']['Compression'] 'yes' - >>> topsecret = config['topsecret.server.com'] + >>> topsecret = config['topsecret.server.example'] >>> topsecret['ForwardX11'] 'no' >>> topsecret['Port'] '50022' - >>> for key in config['bitbucket.org']: # doctest: +SKIP + >>> for key in config['forge.example']: # doctest: +SKIP ... print(key) user compressionlevel serveraliveinterval compression forwardx11 - >>> config['bitbucket.org']['ForwardX11'] + >>> config['forge.example']['ForwardX11'] 'yes' As we can see above, the API is pretty straightforward. The only bit of magic @@ -154,15 +154,15 @@ configuration while the previously existing keys are retained. >>> another_config = configparser.ConfigParser() >>> another_config.read('example.ini') ['example.ini'] - >>> another_config['topsecret.server.com']['Port'] + >>> another_config['topsecret.server.example']['Port'] '50022' - >>> another_config.read_string("[topsecret.server.com]\nPort=48484") - >>> another_config['topsecret.server.com']['Port'] + >>> another_config.read_string("[topsecret.server.example]\nPort=48484") + >>> another_config['topsecret.server.example']['Port'] '48484' - >>> another_config.read_dict({"topsecret.server.com": {"Port": 21212}}) - >>> another_config['topsecret.server.com']['Port'] + >>> another_config.read_dict({"topsecret.server.example": {"Port": 21212}}) + >>> another_config['topsecret.server.example']['Port'] '21212' - >>> another_config['topsecret.server.com']['ForwardX11'] + >>> another_config['topsecret.server.example']['ForwardX11'] 'no' This behaviour is equivalent to a :meth:`ConfigParser.read` call with several @@ -195,9 +195,9 @@ recognizes Boolean values from ``'yes'``/``'no'``, ``'on'``/``'off'``, >>> topsecret.getboolean('ForwardX11') False - >>> config['bitbucket.org'].getboolean('ForwardX11') + >>> config['forge.example'].getboolean('ForwardX11') True - >>> config.getboolean('bitbucket.org', 'Compression') + >>> config.getboolean('forge.example', 'Compression') True Apart from :meth:`~ConfigParser.getboolean`, config parsers also @@ -224,7 +224,7 @@ provide fallback values: Please note that default values have precedence over fallback values. For instance, in our example the ``'CompressionLevel'`` key was specified only in the ``'DEFAULT'`` section. If we try to get it from -the section ``'topsecret.server.com'``, we will always get the default, +the section ``'topsecret.server.example'``, we will always get the default, even if we specify a fallback: .. doctest:: @@ -239,7 +239,7 @@ the ``fallback`` keyword-only argument: .. doctest:: - >>> config.get('bitbucket.org', 'monster', + >>> config.get('forge.example', 'monster', ... fallback='No such things as monsters') 'No such things as monsters' From 68bd8c5e2efab64ff9d38a214775164182179431 Mon Sep 17 00:00:00 2001 From: Eclips4 <80244920+Eclips4@users.noreply.github.com> Date: Thu, 16 Feb 2023 20:46:43 +0300 Subject: [PATCH 188/225] gh-101952: Fix possible segfault in `BUILD_SET` opcode (#101958) --- .../2023-02-16-16-57-23.gh-issue-101952.Zo1dlq.rst | 1 + Python/bytecodes.c | 2 ++ Python/generated_cases.c.h | 2 ++ 3 files changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-16-16-57-23.gh-issue-101952.Zo1dlq.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-16-16-57-23.gh-issue-101952.Zo1dlq.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-16-16-57-23.gh-issue-101952.Zo1dlq.rst new file mode 100644 index 00000000000000..3902c988c8bf9f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-16-16-57-23.gh-issue-101952.Zo1dlq.rst @@ -0,0 +1 @@ +Fix possible segfault in ``BUILD_SET`` opcode, when new set created. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index d5d5034cbfbf74..84747f1758e06c 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1302,6 +1302,8 @@ dummy_func( inst(BUILD_SET, (values[oparg] -- set)) { set = PySet_New(NULL); + if (set == NULL) + goto error; int err = 0; for (int i = 0; i < oparg; i++) { PyObject *item = values[i]; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 8b8a7161ad898e..730dfb7426acbf 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1649,6 +1649,8 @@ PyObject **values = &PEEK(oparg); PyObject *set; set = PySet_New(NULL); + if (set == NULL) + goto error; int err = 0; for (int i = 0; i < oparg; i++) { PyObject *item = values[i]; From 226484e47599a93f5bf033ac47198e68ff401432 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Thu, 16 Feb 2023 12:57:59 -0500 Subject: [PATCH 189/225] gh-99942: correct the pkg-config/python-config flags for cygwin/android --- .../next/Build/2023-01-12-00-49-16.gh-issue-99942.DUR8b4.rst | 2 ++ configure | 2 +- configure.ac | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2023-01-12-00-49-16.gh-issue-99942.DUR8b4.rst diff --git a/Misc/NEWS.d/next/Build/2023-01-12-00-49-16.gh-issue-99942.DUR8b4.rst b/Misc/NEWS.d/next/Build/2023-01-12-00-49-16.gh-issue-99942.DUR8b4.rst new file mode 100644 index 00000000000000..5b692c3cc458c5 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-01-12-00-49-16.gh-issue-99942.DUR8b4.rst @@ -0,0 +1,2 @@ +On Android, in a static build, python-config in embed mode no longer +incorrectly reports a library to link to. diff --git a/configure b/configure index 7c4254f3cb176f..17dc62fb63de3b 100755 --- a/configure +++ b/configure @@ -21496,7 +21496,7 @@ $as_echo "$LDVERSION" >&6; } # On Android and Cygwin the shared libraries must be linked with libpython. -if test -n "$ANDROID_API_LEVEL" -o "$MACHDEP" = "cygwin"; then +if test "$PY_ENABLE_SHARED" = "1" && ( test -n "$ANDROID_API_LEVEL" || test "$MACHDEP" = "cygwin"); then LIBPYTHON="-lpython${VERSION}${ABIFLAGS}" else LIBPYTHON='' diff --git a/configure.ac b/configure.ac index 370bbe07c57634..bc288b86cfa590 100644 --- a/configure.ac +++ b/configure.ac @@ -5759,7 +5759,7 @@ AC_MSG_RESULT($LDVERSION) # On Android and Cygwin the shared libraries must be linked with libpython. AC_SUBST(LIBPYTHON) -if test -n "$ANDROID_API_LEVEL" -o "$MACHDEP" = "cygwin"; then +if test "$PY_ENABLE_SHARED" = "1" && ( test -n "$ANDROID_API_LEVEL" || test "$MACHDEP" = "cygwin"); then LIBPYTHON="-lpython${VERSION}${ABIFLAGS}" else LIBPYTHON='' From a5024a261a75dafa4fb6613298dcb64a9603d9c7 Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Fri, 17 Feb 2023 00:18:21 +0530 Subject: [PATCH 190/225] GH-96764: rewrite `asyncio.wait_for` to use `asyncio.timeout` (#98518) Changes `asyncio.wait_for` to use `asyncio.timeout` as its underlying implementation. --- Lib/asyncio/tasks.py | 77 ++++------- Lib/test/test_asyncio/test_futures2.py | 7 +- Lib/test/test_asyncio/test_waitfor.py | 127 ++++++++++++++---- ...2-10-22-09-26-43.gh-issue-96764.Dh9Y5L.rst | 1 + 4 files changed, 133 insertions(+), 79 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-10-22-09-26-43.gh-issue-96764.Dh9Y5L.rst diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index e78719de216fd0..a2e06d5ef72f42 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -24,6 +24,7 @@ from . import events from . import exceptions from . import futures +from . import timeouts from .coroutines import _is_coroutine # Helper to generate new task names @@ -437,65 +438,44 @@ async def wait_for(fut, timeout): If the wait is cancelled, the task is also cancelled. + If the task supresses the cancellation and returns a value instead, + that value is returned. + This function is a coroutine. """ - loop = events.get_running_loop() + # The special case for timeout <= 0 is for the following case: + # + # async def test_waitfor(): + # func_started = False + # + # async def func(): + # nonlocal func_started + # func_started = True + # + # try: + # await asyncio.wait_for(func(), 0) + # except asyncio.TimeoutError: + # assert not func_started + # else: + # assert False + # + # asyncio.run(test_waitfor()) - if timeout is None: - return await fut - if timeout <= 0: - fut = ensure_future(fut, loop=loop) + if timeout is not None and timeout <= 0: + fut = ensure_future(fut) if fut.done(): return fut.result() - await _cancel_and_wait(fut, loop=loop) + await _cancel_and_wait(fut) try: return fut.result() except exceptions.CancelledError as exc: - raise exceptions.TimeoutError() from exc - - waiter = loop.create_future() - timeout_handle = loop.call_later(timeout, _release_waiter, waiter) - cb = functools.partial(_release_waiter, waiter) - - fut = ensure_future(fut, loop=loop) - fut.add_done_callback(cb) - - try: - # wait until the future completes or the timeout - try: - await waiter - except exceptions.CancelledError: - if fut.done(): - return fut.result() - else: - fut.remove_done_callback(cb) - # We must ensure that the task is not running - # after wait_for() returns. - # See https://bugs.python.org/issue32751 - await _cancel_and_wait(fut, loop=loop) - raise - - if fut.done(): - return fut.result() - else: - fut.remove_done_callback(cb) - # We must ensure that the task is not running - # after wait_for() returns. - # See https://bugs.python.org/issue32751 - await _cancel_and_wait(fut, loop=loop) - # In case task cancellation failed with some - # exception, we should re-raise it - # See https://bugs.python.org/issue40607 - try: - return fut.result() - except exceptions.CancelledError as exc: - raise exceptions.TimeoutError() from exc - finally: - timeout_handle.cancel() + raise TimeoutError from exc + async with timeouts.timeout(timeout): + return await fut async def _wait(fs, timeout, return_when, loop): """Internal helper for wait(). @@ -541,9 +521,10 @@ def _on_completion(f): return done, pending -async def _cancel_and_wait(fut, loop): +async def _cancel_and_wait(fut): """Cancel the *fut* future or task and wait until it completes.""" + loop = events.get_running_loop() waiter = loop.create_future() cb = functools.partial(_release_waiter, waiter) fut.add_done_callback(cb) diff --git a/Lib/test/test_asyncio/test_futures2.py b/Lib/test/test_asyncio/test_futures2.py index 9e7a5775a70383..b7cfffb76bd8f1 100644 --- a/Lib/test/test_asyncio/test_futures2.py +++ b/Lib/test/test_asyncio/test_futures2.py @@ -86,10 +86,9 @@ async def test_recursive_repr_for_pending_tasks(self): async def func(): return asyncio.all_tasks() - # The repr() call should not raise RecursiveError at first. - # The check for returned string is not very reliable but - # exact comparison for the whole string is even weaker. - self.assertIn('...', repr(await asyncio.wait_for(func(), timeout=10))) + # The repr() call should not raise RecursionError at first. + waiter = await asyncio.wait_for(asyncio.Task(func()),timeout=10) + self.assertIn('...', repr(waiter)) if __name__ == '__main__': diff --git a/Lib/test/test_asyncio/test_waitfor.py b/Lib/test/test_asyncio/test_waitfor.py index 45498fa097f6bc..ed80540b2b3852 100644 --- a/Lib/test/test_asyncio/test_waitfor.py +++ b/Lib/test/test_asyncio/test_waitfor.py @@ -237,33 +237,6 @@ async def inner(): with self.assertRaises(FooException): await foo() - async def test_wait_for_self_cancellation(self): - async def inner(): - try: - await asyncio.sleep(0.3) - except asyncio.CancelledError: - try: - await asyncio.sleep(0.3) - except asyncio.CancelledError: - await asyncio.sleep(0.3) - - return 42 - - inner_task = asyncio.create_task(inner()) - - wait = asyncio.wait_for(inner_task, timeout=0.1) - - # Test that wait_for itself is properly cancellable - # even when the initial task holds up the initial cancellation. - task = asyncio.create_task(wait) - await asyncio.sleep(0.2) - task.cancel() - - with self.assertRaises(asyncio.CancelledError): - await task - - self.assertEqual(await inner_task, 42) - async def _test_cancel_wait_for(self, timeout): loop = asyncio.get_running_loop() @@ -289,6 +262,106 @@ async def test_cancel_blocking_wait_for(self): async def test_cancel_wait_for(self): await self._test_cancel_wait_for(60.0) + async def test_wait_for_cancel_suppressed(self): + # GH-86296: Supressing CancelledError is discouraged + # but if a task subpresses CancelledError and returns a value, + # `wait_for` should return the value instead of raising CancelledError. + # This is the same behavior as `asyncio.timeout`. + + async def return_42(): + try: + await asyncio.sleep(10) + except asyncio.CancelledError: + return 42 + + res = await asyncio.wait_for(return_42(), timeout=0.1) + self.assertEqual(res, 42) + + + async def test_wait_for_issue86296(self): + # GH-86296: The task should get cancelled and not run to completion. + # inner completes in one cycle of the event loop so it + # completes before the task is cancelled. + + async def inner(): + return 'done' + + inner_task = asyncio.create_task(inner()) + reached_end = False + + async def wait_for_coro(): + await asyncio.wait_for(inner_task, timeout=100) + await asyncio.sleep(1) + nonlocal reached_end + reached_end = True + + task = asyncio.create_task(wait_for_coro()) + self.assertFalse(task.done()) + # Run the task + await asyncio.sleep(0) + task.cancel() + with self.assertRaises(asyncio.CancelledError): + await task + self.assertTrue(inner_task.done()) + self.assertEqual(await inner_task, 'done') + self.assertFalse(reached_end) + + +class WaitForShieldTests(unittest.IsolatedAsyncioTestCase): + + async def test_zero_timeout(self): + # `asyncio.shield` creates a new task which wraps the passed in + # awaitable and shields it from cancellation so with timeout=0 + # the task returned by `asyncio.shield` aka shielded_task gets + # cancelled immediately and the task wrapped by it is scheduled + # to run. + + async def coro(): + await asyncio.sleep(0.01) + return 'done' + + task = asyncio.create_task(coro()) + with self.assertRaises(asyncio.TimeoutError): + shielded_task = asyncio.shield(task) + await asyncio.wait_for(shielded_task, timeout=0) + + # Task is running in background + self.assertFalse(task.done()) + self.assertFalse(task.cancelled()) + self.assertTrue(shielded_task.cancelled()) + + # Wait for the task to complete + await asyncio.sleep(0.1) + self.assertTrue(task.done()) + + + async def test_none_timeout(self): + # With timeout=None the timeout is disabled so it + # runs till completion. + async def coro(): + await asyncio.sleep(0.1) + return 'done' + + task = asyncio.create_task(coro()) + await asyncio.wait_for(asyncio.shield(task), timeout=None) + + self.assertTrue(task.done()) + self.assertEqual(await task, "done") + + async def test_shielded_timeout(self): + # shield prevents the task from being cancelled. + async def coro(): + await asyncio.sleep(0.1) + return 'done' + + task = asyncio.create_task(coro()) + with self.assertRaises(asyncio.TimeoutError): + await asyncio.wait_for(asyncio.shield(task), timeout=0.01) + + self.assertFalse(task.done()) + self.assertFalse(task.cancelled()) + self.assertEqual(await task, "done") + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2022-10-22-09-26-43.gh-issue-96764.Dh9Y5L.rst b/Misc/NEWS.d/next/Library/2022-10-22-09-26-43.gh-issue-96764.Dh9Y5L.rst new file mode 100644 index 00000000000000..a0174291cbc311 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-10-22-09-26-43.gh-issue-96764.Dh9Y5L.rst @@ -0,0 +1 @@ +:func:`asyncio.wait_for` now uses :func:`asyncio.timeout` as its underlying implementation. Patch by Kumar Aditya. From 4d8959b73ac194ca9a2f623dcb5c23680f7d8536 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 16 Feb 2023 14:05:31 -0700 Subject: [PATCH 191/225] gh-101758: Add _PyState_AddModule() Back for the Stable ABI (gh-101956) We're adding the function back, only for the stable ABI symbol and not as any form of API. I had removed it yesterday. This undocumented "private" function was added with the implementation for PEP 3121 (3.0, 2007) for internal use and later moved out of the limited API (3.6, 2016) and then into the internal API (3.9, 2019). I removed it completely yesterday, including from the stable ABI manifest (where it was added because the symbol happened to be exported). It's unlikely that anyone is using _PyState_AddModule(), especially any stable ABI extensions built against 3.2-3.5, but we're playing it safe. https://github.com/python/cpython/issues/101758 --- Include/internal/pycore_pystate.h | 6 ++++++ Lib/test/test_stable_abi_ctypes.py | 1 + Misc/stable_abi.toml | 3 +++ PC/python3dll.c | 1 + Python/import.c | 20 ++++++++++++++++++++ 5 files changed, 31 insertions(+) diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 638b86253879ea..7046ec8d9adaaf 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -152,6 +152,12 @@ extern void _PySignal_AfterFork(void); #endif +PyAPI_FUNC(int) _PyState_AddModule( + PyThreadState *tstate, + PyObject* module, + PyModuleDef* def); + + PyAPI_FUNC(int) _PyOS_InterruptOccurred(PyThreadState *tstate); #define HEAD_LOCK(runtime) \ diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index 7e50fbda2c07cb..e77c1c8409880d 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -864,6 +864,7 @@ def test_windows_feature_macros(self): "_PyObject_GC_Resize", "_PyObject_New", "_PyObject_NewVar", + "_PyState_AddModule", "_PyThreadState_Init", "_PyThreadState_Prealloc", "_PyWeakref_CallableProxyType", diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index c04a3a228caf56..21ff9616133445 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -1684,6 +1684,9 @@ [function._PyObject_NewVar] added = '3.2' abi_only = true +[function._PyState_AddModule] + added = '3.2' + abi_only = true [function._PyThreadState_Init] added = '3.2' abi_only = true diff --git a/PC/python3dll.c b/PC/python3dll.c index 79f09037282f54..e300819365756e 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -34,6 +34,7 @@ EXPORT_FUNC(_PyObject_GC_NewVar) EXPORT_FUNC(_PyObject_GC_Resize) EXPORT_FUNC(_PyObject_New) EXPORT_FUNC(_PyObject_NewVar) +EXPORT_FUNC(_PyState_AddModule) EXPORT_FUNC(_PyThreadState_Init) EXPORT_FUNC(_PyThreadState_Prealloc) EXPORT_FUNC(Py_AddPendingCall) diff --git a/Python/import.c b/Python/import.c index ec126f28b85816..fabf03b1c5d698 100644 --- a/Python/import.c +++ b/Python/import.c @@ -487,6 +487,26 @@ PyState_FindModule(PyModuleDef* module) return _modules_by_index_get(interp, module); } +/* _PyState_AddModule() has been completely removed from the C-API + (and was removed from the limited API in 3.6). However, we're + playing it safe and keeping it around for any stable ABI extensions + built against 3.2-3.5. */ +int +_PyState_AddModule(PyThreadState *tstate, PyObject* module, PyModuleDef* def) +{ + if (!def) { + assert(_PyErr_Occurred(tstate)); + return -1; + } + if (def->m_slots) { + _PyErr_SetString(tstate, + PyExc_SystemError, + "PyState_AddModule called on module with slots"); + return -1; + } + return _modules_by_index_set(tstate->interp, def, module); +} + int PyState_AddModule(PyObject* module, PyModuleDef* def) { From 984f8ab018f847fe8d66768af962f69ec0e81849 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 16 Feb 2023 17:21:22 -0700 Subject: [PATCH 192/225] gh-101758: Fix Refleak-Related Failures in test_singlephase_variants (gh-101969) gh-101891 is causing failures under `$> ./python -m test test_imp -R 3:3`. Furthermore, with that fixed, "test_singlephase_variants" is leaking references. This change addresses the first part, but skips the leaking tests until we can follow up with a fix. https://github.com/python/cpython/issues/101758 --- Lib/test/test_imp.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py index 5997ffad8e1232..2292bb20939599 100644 --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -263,6 +263,7 @@ def test_issue16421_multiple_modules_in_one_dll(self): with self.assertRaises(ImportError): imp.load_dynamic('nonexistent', pathname) + @unittest.skip('known refleak (temporarily skipping)') @requires_subinterpreters @requires_load_dynamic def test_singlephase_multiple_interpreters(self): @@ -329,9 +330,10 @@ def clean_up(): # However, globals are still shared. _interpreters.run_string(interp2, script % 2) + @unittest.skip('known refleak (temporarily skipping)') @requires_load_dynamic def test_singlephase_variants(self): - '''Exercise the most meaningful variants described in Python/import.c.''' + # Exercise the most meaningful variants described in Python/import.c. self.maxDiff = None basename = '_testsinglephase' @@ -343,6 +345,11 @@ def clean_up(): _testsinglephase._clear_globals() self.addCleanup(clean_up) + def add_ext_cleanup(name): + def clean_up(): + _testinternalcapi.clear_extension(name, pathname) + self.addCleanup(clean_up) + modules = {} def load(name): assert name not in modules @@ -440,6 +447,7 @@ def check_with_reinit_reloaded(module, lookedup, initialized, # Check the "basic" module. name = basename + add_ext_cleanup(name) expected_init_count = 1 with self.subTest(name): mod = load(name) @@ -457,6 +465,7 @@ def check_with_reinit_reloaded(module, lookedup, initialized, # Check its indirect variants. name = f'{basename}_basic_wrapper' + add_ext_cleanup(name) expected_init_count += 1 with self.subTest(name): mod = load(name) @@ -480,6 +489,7 @@ def check_with_reinit_reloaded(module, lookedup, initialized, # Check its direct variant. name = f'{basename}_basic_copy' + add_ext_cleanup(name) expected_init_count += 1 with self.subTest(name): mod = load(name) @@ -500,6 +510,7 @@ def check_with_reinit_reloaded(module, lookedup, initialized, # Check the non-basic variant that has no state. name = f'{basename}_with_reinit' + add_ext_cleanup(name) with self.subTest(name): mod = load(name) lookedup, initialized, cached = check_common(name, mod) @@ -518,6 +529,7 @@ def check_with_reinit_reloaded(module, lookedup, initialized, # Check the basic variant that has state. name = f'{basename}_with_state' + add_ext_cleanup(name) with self.subTest(name): mod = load(name) lookedup, initialized, cached = check_common(name, mod) From a3bb7fbe7eecfae6bf7b2f0912f9b2b12fac8db1 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Fri, 17 Feb 2023 12:43:07 +0400 Subject: [PATCH 193/225] gh-101973: Fix parameter reference for PyModule_FromDefAndSpec (#101976) --- Doc/c-api/module.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index e2ba157b32c7d9..c0351c8a6c72aa 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -388,7 +388,7 @@ objects dynamically. Note that both ``PyModule_FromDefAndSpec`` and .. c:function:: PyObject * PyModule_FromDefAndSpec(PyModuleDef *def, PyObject *spec) - Create a new module object, given the definition in *module* and the + Create a new module object, given the definition in *def* and the ModuleSpec *spec*. This behaves like :c:func:`PyModule_FromDefAndSpec2` with *module_api_version* set to :const:`PYTHON_API_VERSION`. @@ -396,7 +396,7 @@ objects dynamically. Note that both ``PyModule_FromDefAndSpec`` and .. c:function:: PyObject * PyModule_FromDefAndSpec2(PyModuleDef *def, PyObject *spec, int module_api_version) - Create a new module object, given the definition in *module* and the + Create a new module object, given the definition in *def* and the ModuleSpec *spec*, assuming the API version *module_api_version*. If that version does not match the version of the running interpreter, a :exc:`RuntimeWarning` is emitted. From 3c0a31cbfd1258bd96153a007dd44a96f2947dbf Mon Sep 17 00:00:00 2001 From: Yeojin Kim Date: Fri, 17 Feb 2023 17:47:02 +0900 Subject: [PATCH 194/225] Docs: fix typos in PyFunction_WatchCallback docs and in 3.12 NEWS (GH-101980) - possitibility => possibility - disaallowed => disallowed --- Doc/c-api/function.rst | 2 +- Misc/NEWS.d/3.12.0a2.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/function.rst b/Doc/c-api/function.rst index 3cce18bdde3057..bc7569d0add97d 100644 --- a/Doc/c-api/function.rst +++ b/Doc/c-api/function.rst @@ -169,7 +169,7 @@ There are a few functions specific to Python functions. before the modification to *func* takes place, so the prior state of *func* can be inspected. The runtime is permitted to optimize away the creation of function objects when possible. In such cases no event will be emitted. - Although this creates the possitibility of an observable difference of + Although this creates the possibility of an observable difference of runtime behavior depending on optimization decisions, it does not change the semantics of the Python code being executed. diff --git a/Misc/NEWS.d/3.12.0a2.rst b/Misc/NEWS.d/3.12.0a2.rst index 318f3f71f11546..41ad8cd22b5d89 100644 --- a/Misc/NEWS.d/3.12.0a2.rst +++ b/Misc/NEWS.d/3.12.0a2.rst @@ -1060,7 +1060,7 @@ Add ``getbufferproc`` and ``releasebufferproc`` to the stable API. Some configurable capabilities of sub-interpreters have changed. They always allow subprocesses (:mod:`subprocess`) now, whereas before subprocesses -could be optionally disaallowed for a sub-interpreter. Instead +could be optionally disallowed for a sub-interpreter. Instead :func:`os.exec` can now be disallowed. Disallowing daemon threads is now supported. Disallowing all threads is still allowed, but is never done by default. Note that the optional restrictions are only available through From 775f8819e319127f9bfb046773b74bcc62c68b6a Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Fri, 17 Feb 2023 19:14:07 +0900 Subject: [PATCH 195/225] gh-101766: Fix refleak for _BlockingOnManager resources (gh-101942) --- Lib/importlib/_bootstrap.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index bebe7e15cbce67..1ef7b6adb04434 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -85,6 +85,11 @@ def __enter__(self): def __exit__(self, *args, **kwargs): """Remove self.lock from this thread's _blocking_on list.""" self.blocked_on.remove(self.lock) + if len(self.blocked_on) == 0: + # gh-101766: glboal cache should be cleaned-up + # if there is no more _blocking_on for this thread. + del _blocking_on[self.thread_id] + del self.blocked_on class _DeadlockError(RuntimeError): From d401b20630965c0e1d2a5a0d60d5fc51aa5a8d80 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Fri, 17 Feb 2023 14:05:38 +0000 Subject: [PATCH 196/225] gh-101360: Fix anchor matching in pathlib.PureWindowsPath.match() (GH-101363) Use `fnmatch` to match path and pattern anchors, just as we do for other path parts. This allows patterns such as `'*:/Users/*'` to be matched. --- Lib/pathlib.py | 5 ----- Lib/test/test_ntpath.py | 4 ++++ Lib/test/test_pathlib.py | 9 ++++++--- .../2023-01-27-02-53-50.gh-issue-101360.bPB7SL.rst | 3 +++ 4 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-01-27-02-53-50.gh-issue-101360.bPB7SL.rst diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 17659bcd3e2d7f..d7994a331d5eac 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -647,15 +647,10 @@ def match(self, path_pattern): drv, root, pat_parts = self._parse_parts((path_pattern,)) if not pat_parts: raise ValueError("empty pattern") - elif drv and drv != self._flavour.normcase(self._drv): - return False - elif root and root != self._root: - return False parts = self._parts_normcase if drv or root: if len(pat_parts) != len(parts): return False - pat_parts = pat_parts[1:] elif len(pat_parts) > len(parts): return False for part, pat in zip(reversed(parts), reversed(pat_parts)): diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index b32900697874b1..08c8a7a1f94b95 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -200,6 +200,10 @@ def test_splitroot(self): tester('ntpath.splitroot("//x")', ("//x", "", "")) # non-empty server & missing share tester('ntpath.splitroot("//x/")', ("//x/", "", "")) # non-empty server & empty share + # gh-101363: match GetFullPathNameW() drive letter parsing behaviour + tester('ntpath.splitroot(" :/foo")', (" :", "/", "foo")) + tester('ntpath.splitroot("/:/foo")', ("", "/", ":/foo")) + def test_split(self): tester('ntpath.split("c:\\foo\\bar")', ('c:\\foo', 'bar')) tester('ntpath.split("\\\\conky\\mountpoint\\foo\\bar")', diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index a596795b44f0fa..b8683796d9600d 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -852,8 +852,7 @@ def test_as_uri(self): def test_match_common(self): P = self.cls # Absolute patterns. - self.assertTrue(P('c:/b.py').match('/*.py')) - self.assertTrue(P('c:/b.py').match('c:*.py')) + self.assertTrue(P('c:/b.py').match('*:/*.py')) self.assertTrue(P('c:/b.py').match('c:/*.py')) self.assertFalse(P('d:/b.py').match('c:/*.py')) # wrong drive self.assertFalse(P('b.py').match('/*.py')) @@ -864,7 +863,7 @@ def test_match_common(self): self.assertFalse(P('/b.py').match('c:*.py')) self.assertFalse(P('/b.py').match('c:/*.py')) # UNC patterns. - self.assertTrue(P('//some/share/a.py').match('/*.py')) + self.assertTrue(P('//some/share/a.py').match('//*/*/*.py')) self.assertTrue(P('//some/share/a.py').match('//some/share/*.py')) self.assertFalse(P('//other/share/a.py').match('//some/share/*.py')) self.assertFalse(P('//some/share/a/b.py').match('//some/share/*.py')) @@ -872,6 +871,10 @@ def test_match_common(self): self.assertTrue(P('B.py').match('b.PY')) self.assertTrue(P('c:/a/B.Py').match('C:/A/*.pY')) self.assertTrue(P('//Some/Share/B.Py').match('//somE/sharE/*.pY')) + # Path anchor doesn't match pattern anchor + self.assertFalse(P('c:/b.py').match('/*.py')) # 'c:/' vs '/' + self.assertFalse(P('c:/b.py').match('c:*.py')) # 'c:/' vs 'c:' + self.assertFalse(P('//some/share/a.py').match('/*.py')) # '//some/share/' vs '/' def test_ordering_common(self): # Case-insensitivity. diff --git a/Misc/NEWS.d/next/Library/2023-01-27-02-53-50.gh-issue-101360.bPB7SL.rst b/Misc/NEWS.d/next/Library/2023-01-27-02-53-50.gh-issue-101360.bPB7SL.rst new file mode 100644 index 00000000000000..4cfb136c5db853 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-27-02-53-50.gh-issue-101360.bPB7SL.rst @@ -0,0 +1,3 @@ +Fix anchor matching in :meth:`pathlib.PureWindowsPath.match`. Path and +pattern anchors are now matched with :mod:`fnmatch`, just like other path +parts. This allows patterns such as ``"*:/Users/*"`` to be matched. From 072011b3c38f871cdc3ab62630ea2234d09456d1 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Fri, 17 Feb 2023 14:08:14 +0000 Subject: [PATCH 197/225] gh-100809: Fix handling of drive-relative paths in pathlib.Path.absolute() (GH-100812) Resolving the drive independently uses the OS API, which ensures it starts from the current directory on that drive. --- Lib/pathlib.py | 7 +++- Lib/test/support/os_helper.py | 35 +++++++++++++++++++ Lib/test/test_pathlib.py | 20 +++++++++++ ...-01-06-21-14-41.gh-issue-100809.I697UT.rst | 3 ++ 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-01-06-21-14-41.gh-issue-100809.I697UT.rst diff --git a/Lib/pathlib.py b/Lib/pathlib.py index d7994a331d5eac..dde573592fddce 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -816,7 +816,12 @@ def absolute(self): """ if self.is_absolute(): return self - return self._from_parts([os.getcwd()] + self._parts) + elif self._drv: + # There is a CWD on each drive-letter drive. + cwd = self._flavour.abspath(self._drv) + else: + cwd = os.getcwd() + return self._from_parts([cwd] + self._parts) def resolve(self, strict=False): """ diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py index 2d4356a1191b1e..821a4b1ffd5077 100644 --- a/Lib/test/support/os_helper.py +++ b/Lib/test/support/os_helper.py @@ -4,6 +4,7 @@ import os import re import stat +import string import sys import time import unittest @@ -716,3 +717,37 @@ def __exit__(self, *ignore_exc): else: self._environ[k] = v os.environ = self._environ + + +try: + import ctypes + kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) + + ERROR_FILE_NOT_FOUND = 2 + DDD_REMOVE_DEFINITION = 2 + DDD_EXACT_MATCH_ON_REMOVE = 4 + DDD_NO_BROADCAST_SYSTEM = 8 +except (ImportError, AttributeError): + def subst_drive(path): + raise unittest.SkipTest('ctypes or kernel32 is not available') +else: + @contextlib.contextmanager + def subst_drive(path): + """Temporarily yield a substitute drive for a given path.""" + for c in reversed(string.ascii_uppercase): + drive = f'{c}:' + if (not kernel32.QueryDosDeviceW(drive, None, 0) and + ctypes.get_last_error() == ERROR_FILE_NOT_FOUND): + break + else: + raise unittest.SkipTest('no available logical drive') + if not kernel32.DefineDosDeviceW( + DDD_NO_BROADCAST_SYSTEM, drive, path): + raise ctypes.WinError(ctypes.get_last_error()) + try: + yield drive + finally: + if not kernel32.DefineDosDeviceW( + DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE, + drive, path): + raise ctypes.WinError(ctypes.get_last_error()) diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index b8683796d9600d..4de91d52c6d10c 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -2973,6 +2973,26 @@ def test_absolute(self): self.assertEqual(str(P('a', 'b', 'c').absolute()), os.path.join(share, 'a', 'b', 'c')) + drive = os.path.splitdrive(BASE)[0] + with os_helper.change_cwd(BASE): + # Relative path with root + self.assertEqual(str(P('\\').absolute()), drive + '\\') + self.assertEqual(str(P('\\foo').absolute()), drive + '\\foo') + + # Relative path on current drive + self.assertEqual(str(P(drive).absolute()), BASE) + self.assertEqual(str(P(drive + 'foo').absolute()), os.path.join(BASE, 'foo')) + + with os_helper.subst_drive(BASE) as other_drive: + # Set the working directory on the substitute drive + saved_cwd = os.getcwd() + other_cwd = f'{other_drive}\\dirA' + os.chdir(other_cwd) + os.chdir(saved_cwd) + + # Relative path on another drive + self.assertEqual(str(P(other_drive).absolute()), other_cwd) + self.assertEqual(str(P(other_drive + 'foo').absolute()), other_cwd + '\\foo') def test_glob(self): P = self.cls diff --git a/Misc/NEWS.d/next/Library/2023-01-06-21-14-41.gh-issue-100809.I697UT.rst b/Misc/NEWS.d/next/Library/2023-01-06-21-14-41.gh-issue-100809.I697UT.rst new file mode 100644 index 00000000000000..54082de88ccf4a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-06-21-14-41.gh-issue-100809.I697UT.rst @@ -0,0 +1,3 @@ +Fix handling of drive-relative paths (like 'C:' and 'C:foo') in +:meth:`pathlib.Path.absolute`. This method now uses the OS API +to retrieve the correct current working directory for the drive. From f482ade4c7887c49dfd8bba3be76f839e562608d Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sat, 18 Feb 2023 00:18:47 +0900 Subject: [PATCH 198/225] gh-101766: Fix refleak for _BlockingOnManager resources from test suite level (gh-101988) --- Lib/importlib/_bootstrap.py | 5 ----- Lib/test/test_importlib/test_locks.py | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 1ef7b6adb04434..bebe7e15cbce67 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -85,11 +85,6 @@ def __enter__(self): def __exit__(self, *args, **kwargs): """Remove self.lock from this thread's _blocking_on list.""" self.blocked_on.remove(self.lock) - if len(self.blocked_on) == 0: - # gh-101766: glboal cache should be cleaned-up - # if there is no more _blocking_on for this thread. - del _blocking_on[self.thread_id] - del self.blocked_on class _DeadlockError(RuntimeError): diff --git a/Lib/test/test_importlib/test_locks.py b/Lib/test/test_importlib/test_locks.py index 56d73c496e6bbb..ba9cf51c261d52 100644 --- a/Lib/test/test_importlib/test_locks.py +++ b/Lib/test/test_importlib/test_locks.py @@ -33,6 +33,11 @@ class ModuleLockAsRLockTests: test_repr = None test_locked_repr = None + def tearDown(self): + for splitinit in init.values(): + splitinit._bootstrap._blocking_on.clear() + + LOCK_TYPES = {kind: splitinit._bootstrap._ModuleLock for kind, splitinit in init.items()} From a1723caabfcdca5d675c4cb04554fb04c7edf601 Mon Sep 17 00:00:00 2001 From: Dustin Rodrigues Date: Fri, 17 Feb 2023 14:30:29 -0500 Subject: [PATCH 199/225] gh-101992: update plistlib examples to be runnable (#101994) * gh-101992: update plistlib examples to be runnable * Update Doc/library/plistlib.rst --------- Co-authored-by: Terry Jan Reedy --- Doc/library/plistlib.rst | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Doc/library/plistlib.rst b/Doc/library/plistlib.rst index 5ded9661f08014..7aad15ec91a0ac 100644 --- a/Doc/library/plistlib.rst +++ b/Doc/library/plistlib.rst @@ -159,6 +159,9 @@ Examples Generating a plist:: + import datetime + import plistlib + pl = dict( aString = "Doodah", aList = ["A", "B", 12, 32.1, [1, 2, 3]], @@ -172,13 +175,19 @@ Generating a plist:: ), someData = b"", someMoreData = b"" * 10, - aDate = datetime.datetime.fromtimestamp(time.mktime(time.gmtime())), + aDate = datetime.datetime.now() ) - with open(fileName, 'wb') as fp: - dump(pl, fp) + print(plistlib.dumps(pl).decode()) Parsing a plist:: - with open(fileName, 'rb') as fp: - pl = load(fp) - print(pl["aKey"]) + import plistlib + + plist = b""" + + foo + bar + + """ + pl = plistlib.loads(plist) + print(pl["foo"]) From 77d95c83733722ada35eb1ef89ae5b84a51ddd32 Mon Sep 17 00:00:00 2001 From: Jan Gosmann Date: Fri, 17 Feb 2023 22:01:26 +0100 Subject: [PATCH 200/225] gh-100226: Clarify StreamReader.read behavior (#101807) --- Doc/library/asyncio-stream.rst | 12 ++++++++++-- Lib/asyncio/streams.py | 17 +++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst index 3b3c68ab6ef625..bbac1c32b5695f 100644 --- a/Doc/library/asyncio-stream.rst +++ b/Doc/library/asyncio-stream.rst @@ -206,12 +206,20 @@ StreamReader .. coroutinemethod:: read(n=-1) - Read up to *n* bytes. If *n* is not provided, or set to ``-1``, - read until EOF and return all read bytes. + Read up to *n* bytes from the stream. + If *n* is not provided or set to ``-1``, + read until EOF, then return all read :class:`bytes`. If EOF was received and the internal buffer is empty, return an empty ``bytes`` object. + If *n* is ``0``, return an empty ``bytes`` object immediately. + + If *n* is positive, return at most *n* available ``bytes`` + as soon as at least 1 byte is available in the internal buffer. + If EOF is received before any byte is read, return an empty + ``bytes`` object. + .. coroutinemethod:: readline() Read one line, where "line" is a sequence of bytes diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py index 7d13e961bd2de4..bf15f517e50dce 100644 --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -649,16 +649,17 @@ async def readuntil(self, separator=b'\n'): async def read(self, n=-1): """Read up to `n` bytes from the stream. - If n is not provided, or set to -1, read until EOF and return all read - bytes. If the EOF was received and the internal buffer is empty, return - an empty bytes object. + If `n` is not provided or set to -1, + read until EOF, then return all read bytes. + If EOF was received and the internal buffer is empty, + return an empty bytes object. - If n is zero, return empty bytes object immediately. + If `n` is 0, return an empty bytes object immediately. - If n is positive, this function try to read `n` bytes, and may return - less or equal bytes than requested, but at least one byte. If EOF was - received before any byte is read, this function returns empty byte - object. + If `n` is positive, return at most `n` available bytes + as soon as at least 1 byte is available in the internal buffer. + If EOF is received before any byte is read, return an empty + bytes object. Returned value is not limited with limit, configured at stream creation. From 7f1c72175600b21c1c840e8988cc6e6b4b244582 Mon Sep 17 00:00:00 2001 From: Owain Davies <116417456+OTheDev@users.noreply.github.com> Date: Sat, 18 Feb 2023 04:36:47 +0700 Subject: [PATCH 201/225] gh-101739: [Enum] update docs - default boundary for Flag is CONFORM (GH-101746) --- Doc/library/enum.rst | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 13591a1bdc7347..24b6dbfe37cd38 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -696,10 +696,9 @@ Data Types .. attribute:: STRICT - Out-of-range values cause a :exc:`ValueError` to be raised. This is the - default for :class:`Flag`:: + Out-of-range values cause a :exc:`ValueError` to be raised:: - >>> from enum import Flag, STRICT + >>> from enum import Flag, STRICT, auto >>> class StrictFlag(Flag, boundary=STRICT): ... RED = auto() ... GREEN = auto() @@ -715,9 +714,9 @@ Data Types .. attribute:: CONFORM Out-of-range values have invalid values removed, leaving a valid *Flag* - value:: + value. This is the default for :class:`Flag`:: - >>> from enum import Flag, CONFORM + >>> from enum import Flag, CONFORM, auto >>> class ConformFlag(Flag, boundary=CONFORM): ... RED = auto() ... GREEN = auto() @@ -731,7 +730,7 @@ Data Types Out-of-range values lose their *Flag* membership and revert to :class:`int`. This is the default for :class:`IntFlag`:: - >>> from enum import Flag, EJECT + >>> from enum import Flag, EJECT, auto >>> class EjectFlag(Flag, boundary=EJECT): ... RED = auto() ... GREEN = auto() @@ -742,10 +741,10 @@ Data Types .. attribute:: KEEP - Out-of-range values are kept, and the *Flag* membership is kept. This is - used for some stdlib flags: + Out-of-range values are kept, and the *Flag* membership is kept. This is + used for some stdlib flags:: - >>> from enum import Flag, KEEP + >>> from enum import Flag, KEEP, auto >>> class KeepFlag(Flag, boundary=KEEP): ... RED = auto() ... GREEN = auto() From 89413bbccb9261b72190e275eefe4b0d49671477 Mon Sep 17 00:00:00 2001 From: Eclips4 <80244920+Eclips4@users.noreply.github.com> Date: Sat, 18 Feb 2023 03:52:23 +0300 Subject: [PATCH 202/225] gh-101967: add a missing error check (#101968) --- .../2023-02-16-23-19-01.gh-issue-101967.Kqr1dz.rst | 1 + Python/ceval.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-16-23-19-01.gh-issue-101967.Kqr1dz.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-16-23-19-01.gh-issue-101967.Kqr1dz.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-16-23-19-01.gh-issue-101967.Kqr1dz.rst new file mode 100644 index 00000000000000..6e681f910f5359 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-16-23-19-01.gh-issue-101967.Kqr1dz.rst @@ -0,0 +1 @@ +Fix possible segfault in ``positional_only_passed_as_keyword`` function, when new list created. diff --git a/Python/ceval.c b/Python/ceval.c index 09fd2f29266c87..308ef52259df3d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1255,7 +1255,9 @@ positional_only_passed_as_keyword(PyThreadState *tstate, PyCodeObject *co, { int posonly_conflicts = 0; PyObject* posonly_names = PyList_New(0); - + if (posonly_names == NULL) { + goto fail; + } for(int k=0; k < co->co_posonlyargcount; k++){ PyObject* posonly_name = PyTuple_GET_ITEM(co->co_localsplusnames, k); From af446bbb76f64e67831444a0ceee6863a1527088 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sat, 18 Feb 2023 18:46:33 +0300 Subject: [PATCH 203/225] gh-101536: [docs] Improve attributes of `urllib.error.HTTPError` (#101612) * gh-101536: [docs] Improve attributes of `urllib.error.HTTPError` * Address review --- Doc/library/urllib.error.rst | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Doc/library/urllib.error.rst b/Doc/library/urllib.error.rst index f7d47ed76aca18..3adbdd26132273 100644 --- a/Doc/library/urllib.error.rst +++ b/Doc/library/urllib.error.rst @@ -31,7 +31,7 @@ The following exceptions are raised by :mod:`urllib.error` as appropriate: of :exc:`IOError`. -.. exception:: HTTPError +.. exception:: HTTPError(url, code, msg, hdrs, fp) Though being an exception (a subclass of :exc:`URLError`), an :exc:`HTTPError` can also function as a non-exceptional file-like return @@ -39,6 +39,11 @@ The following exceptions are raised by :mod:`urllib.error` as appropriate: is useful when handling exotic HTTP errors, such as requests for authentication. + .. attribute:: url + + Contains the request URL. + An alias for *filename* attribute. + .. attribute:: code An HTTP status code as defined in :rfc:`2616`. This numeric value corresponds @@ -48,14 +53,20 @@ The following exceptions are raised by :mod:`urllib.error` as appropriate: .. attribute:: reason This is usually a string explaining the reason for this error. + An alias for *msg* attribute. .. attribute:: headers The HTTP response headers for the HTTP request that caused the :exc:`HTTPError`. + An alias for *hdrs* attribute. .. versionadded:: 3.4 + .. attribute:: fp + + A file-like object where the HTTP error body can be read from. + .. exception:: ContentTooShortError(msg, content) This exception is raised when the :func:`~urllib.request.urlretrieve` From 128379b8cdb88a6d3d7fed24df082c9a654b3fb8 Mon Sep 17 00:00:00 2001 From: Nicko van Someren Date: Sat, 18 Feb 2023 11:44:41 -0700 Subject: [PATCH 204/225] bpo-46978: Correct docstrings for in-place builtin operators (#31802) --- .../Core and Builtins/2022-03-10-21-48-05.bpo-46978.f5QFfw.rst | 1 + Objects/typeobject.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-03-10-21-48-05.bpo-46978.f5QFfw.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-03-10-21-48-05.bpo-46978.f5QFfw.rst b/Misc/NEWS.d/next/Core and Builtins/2022-03-10-21-48-05.bpo-46978.f5QFfw.rst new file mode 100644 index 00000000000000..72291d042a0394 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-03-10-21-48-05.bpo-46978.f5QFfw.rst @@ -0,0 +1 @@ +Fixed docstrings for in-place operators of built-in types. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index bf6ccdb77a90f0..f2e8092aa37eec 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -8564,7 +8564,7 @@ an all-zero entry. #NAME "($self, /)\n--\n\n" DOC) #define IBSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \ ETSLOT(NAME, as_number.SLOT, FUNCTION, WRAPPER, \ - #NAME "($self, value, /)\n--\n\nReturn self" DOC "value.") + #NAME "($self, value, /)\n--\n\nCompute self " DOC " value.") #define BINSLOT(NAME, SLOT, FUNCTION, DOC) \ ETSLOT(NAME, as_number.SLOT, FUNCTION, wrap_binaryfunc_l, \ #NAME "($self, value, /)\n--\n\nReturn self" DOC "value.") From 5170caf3059fdacc92d7370eecb9fe4f0c5a1c76 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 18 Feb 2023 16:29:22 -0500 Subject: [PATCH 205/225] gh-97930: Apply changes from importlib_resources 5.12. (GH-102010) --- Lib/importlib/resources/_adapters.py | 4 +- Lib/importlib/resources/_itertools.py | 69 ++++++------ Lib/importlib/resources/readers.py | 36 +++++-- Lib/test/test_importlib/resources/_path.py | 18 ++-- .../subdirectory/subsubdir/resource.txt | 1 + .../resources/test_compatibilty_files.py | 6 +- .../test_importlib/resources/test_custom.py | 46 ++++++++ .../test_importlib/resources/test_files.py | 4 +- .../test_importlib/resources/test_open.py | 14 ++- .../test_importlib/resources/test_path.py | 15 ++- .../test_importlib/resources/test_read.py | 12 ++- .../test_importlib/resources/test_reader.py | 11 ++ .../test_importlib/resources/test_resource.py | 100 ++++++++---------- Lib/test/test_importlib/resources/util.py | 28 +++-- ...3-02-17-19-00-58.gh-issue-97930.C_nQjb.rst | 4 + 15 files changed, 241 insertions(+), 127 deletions(-) create mode 100644 Lib/test/test_importlib/resources/data02/subdirectory/subsubdir/resource.txt create mode 100644 Lib/test/test_importlib/resources/test_custom.py create mode 100644 Misc/NEWS.d/next/Library/2023-02-17-19-00-58.gh-issue-97930.C_nQjb.rst diff --git a/Lib/importlib/resources/_adapters.py b/Lib/importlib/resources/_adapters.py index f22f6bc509a3d7..50688fbb666658 100644 --- a/Lib/importlib/resources/_adapters.py +++ b/Lib/importlib/resources/_adapters.py @@ -34,9 +34,7 @@ def _io_wrapper(file, mode='r', *args, **kwargs): return TextIOWrapper(file, *args, **kwargs) elif mode == 'rb': return file - raise ValueError( - f"Invalid mode value '{mode}', only 'r' and 'rb' are supported" - ) + raise ValueError(f"Invalid mode value '{mode}', only 'r' and 'rb' are supported") class CompatibilityFiles: diff --git a/Lib/importlib/resources/_itertools.py b/Lib/importlib/resources/_itertools.py index cce05582ffc6fe..7b775ef5ae893f 100644 --- a/Lib/importlib/resources/_itertools.py +++ b/Lib/importlib/resources/_itertools.py @@ -1,35 +1,38 @@ -from itertools import filterfalse +# from more_itertools 9.0 +def only(iterable, default=None, too_long=None): + """If *iterable* has only one item, return it. + If it has zero items, return *default*. + If it has more than one item, raise the exception given by *too_long*, + which is ``ValueError`` by default. + >>> only([], default='missing') + 'missing' + >>> only([1]) + 1 + >>> only([1, 2]) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: Expected exactly one item in iterable, but got 1, 2, + and perhaps more.' + >>> only([1, 2], too_long=TypeError) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + TypeError + Note that :func:`only` attempts to advance *iterable* twice to ensure there + is only one item. See :func:`spy` or :func:`peekable` to check + iterable contents less destructively. + """ + it = iter(iterable) + first_value = next(it, default) -from typing import ( - Callable, - Iterable, - Iterator, - Optional, - Set, - TypeVar, - Union, -) - -# Type and type variable definitions -_T = TypeVar('_T') -_U = TypeVar('_U') - - -def unique_everseen( - iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = None -) -> Iterator[_T]: - "List unique elements, preserving order. Remember all elements ever seen." - # unique_everseen('AAAABBBCCDAABBB') --> A B C D - # unique_everseen('ABBCcAD', str.lower) --> A B C D - seen: Set[Union[_T, _U]] = set() - seen_add = seen.add - if key is None: - for element in filterfalse(seen.__contains__, iterable): - seen_add(element) - yield element + try: + second_value = next(it) + except StopIteration: + pass else: - for element in iterable: - k = key(element) - if k not in seen: - seen_add(k) - yield element + msg = ( + 'Expected exactly one item in iterable, but got {!r}, {!r}, ' + 'and perhaps more.'.format(first_value, second_value) + ) + raise too_long or ValueError(msg) + + return first_value diff --git a/Lib/importlib/resources/readers.py b/Lib/importlib/resources/readers.py index 80cb320dd8bda0..c3cdf769cbecb0 100644 --- a/Lib/importlib/resources/readers.py +++ b/Lib/importlib/resources/readers.py @@ -1,11 +1,12 @@ import collections -import operator +import itertools import pathlib +import operator import zipfile from . import abc -from ._itertools import unique_everseen +from ._itertools import only def remove_duplicates(items): @@ -41,8 +42,10 @@ def open_resource(self, resource): raise FileNotFoundError(exc.args[0]) def is_resource(self, path): - # workaround for `zipfile.Path.is_file` returning true - # for non-existent paths. + """ + Workaround for `zipfile.Path.is_file` returning true + for non-existent paths. + """ target = self.files().joinpath(path) return target.is_file() and target.exists() @@ -67,8 +70,10 @@ def __init__(self, *paths): raise NotADirectoryError('MultiplexedPath only supports directories') def iterdir(self): - files = (file for path in self._paths for file in path.iterdir()) - return unique_everseen(files, key=operator.attrgetter('name')) + children = (child for path in self._paths for child in path.iterdir()) + by_name = operator.attrgetter('name') + groups = itertools.groupby(sorted(children, key=by_name), key=by_name) + return map(self._follow, (locs for name, locs in groups)) def read_bytes(self): raise FileNotFoundError(f'{self} is not a file') @@ -90,6 +95,25 @@ def joinpath(self, *descendants): # Just return something that will not exist. return self._paths[0].joinpath(*descendants) + @classmethod + def _follow(cls, children): + """ + Construct a MultiplexedPath if needed. + + If children contains a sole element, return it. + Otherwise, return a MultiplexedPath of the items. + Unless one of the items is not a Directory, then return the first. + """ + subdirs, one_dir, one_file = itertools.tee(children, 3) + + try: + return only(one_dir) + except ValueError: + try: + return cls(*subdirs) + except NotADirectoryError: + return next(one_file) + def open(self, *args, **kwargs): raise FileNotFoundError(f'{self} is not a file') diff --git a/Lib/test/test_importlib/resources/_path.py b/Lib/test/test_importlib/resources/_path.py index c630e4d3d3f352..1f97c96146960d 100644 --- a/Lib/test/test_importlib/resources/_path.py +++ b/Lib/test/test_importlib/resources/_path.py @@ -1,12 +1,16 @@ import pathlib import functools +from typing import Dict, Union + #### -# from jaraco.path 3.4 +# from jaraco.path 3.4.1 + +FilesSpec = Dict[str, Union[str, bytes, 'FilesSpec']] # type: ignore -def build(spec, prefix=pathlib.Path()): +def build(spec: FilesSpec, prefix=pathlib.Path()): """ Build a set of files/directories, as described by the spec. @@ -23,15 +27,17 @@ def build(spec, prefix=pathlib.Path()): ... "baz.py": "# Some code", ... } ... } - >>> tmpdir = getfixture('tmpdir') - >>> build(spec, tmpdir) + >>> target = getfixture('tmp_path') + >>> build(spec, target) + >>> target.joinpath('foo/baz.py').read_text(encoding='utf-8') + '# Some code' """ for name, contents in spec.items(): create(contents, pathlib.Path(prefix) / name) @functools.singledispatch -def create(content, path): +def create(content: Union[str, bytes, FilesSpec], path): path.mkdir(exist_ok=True) build(content, prefix=path) # type: ignore @@ -43,7 +49,7 @@ def _(content: bytes, path): @create.register def _(content: str, path): - path.write_text(content) + path.write_text(content, encoding='utf-8') # end from jaraco.path diff --git a/Lib/test/test_importlib/resources/data02/subdirectory/subsubdir/resource.txt b/Lib/test/test_importlib/resources/data02/subdirectory/subsubdir/resource.txt new file mode 100644 index 00000000000000..48f587a2d0ac53 --- /dev/null +++ b/Lib/test/test_importlib/resources/data02/subdirectory/subsubdir/resource.txt @@ -0,0 +1 @@ +a resource \ No newline at end of file diff --git a/Lib/test/test_importlib/resources/test_compatibilty_files.py b/Lib/test/test_importlib/resources/test_compatibilty_files.py index 6fa18a24973f64..bcf608d9e2cbdf 100644 --- a/Lib/test/test_importlib/resources/test_compatibilty_files.py +++ b/Lib/test/test_importlib/resources/test_compatibilty_files.py @@ -64,11 +64,13 @@ def test_orphan_path_name(self): def test_spec_path_open(self): self.assertEqual(self.files.read_bytes(), b'Hello, world!') - self.assertEqual(self.files.read_text(), 'Hello, world!') + self.assertEqual(self.files.read_text(encoding='utf-8'), 'Hello, world!') def test_child_path_open(self): self.assertEqual((self.files / 'a').read_bytes(), b'Hello, world!') - self.assertEqual((self.files / 'a').read_text(), 'Hello, world!') + self.assertEqual( + (self.files / 'a').read_text(encoding='utf-8'), 'Hello, world!' + ) def test_orphan_path_open(self): with self.assertRaises(FileNotFoundError): diff --git a/Lib/test/test_importlib/resources/test_custom.py b/Lib/test/test_importlib/resources/test_custom.py new file mode 100644 index 00000000000000..73127209a2761b --- /dev/null +++ b/Lib/test/test_importlib/resources/test_custom.py @@ -0,0 +1,46 @@ +import unittest +import contextlib +import pathlib + +from test.support import os_helper + +from importlib import resources +from importlib.resources.abc import TraversableResources, ResourceReader +from . import util + + +class SimpleLoader: + """ + A simple loader that only implements a resource reader. + """ + + def __init__(self, reader: ResourceReader): + self.reader = reader + + def get_resource_reader(self, package): + return self.reader + + +class MagicResources(TraversableResources): + """ + Magically returns the resources at path. + """ + + def __init__(self, path: pathlib.Path): + self.path = path + + def files(self): + return self.path + + +class CustomTraversableResourcesTests(unittest.TestCase): + def setUp(self): + self.fixtures = contextlib.ExitStack() + self.addCleanup(self.fixtures.close) + + def test_custom_loader(self): + temp_dir = self.fixtures.enter_context(os_helper.temp_dir()) + loader = SimpleLoader(MagicResources(temp_dir)) + pkg = util.create_package_from_loader(loader) + files = resources.files(pkg) + assert files is temp_dir diff --git a/Lib/test/test_importlib/resources/test_files.py b/Lib/test/test_importlib/resources/test_files.py index fe813ae7d08881..1450cfb310926a 100644 --- a/Lib/test/test_importlib/resources/test_files.py +++ b/Lib/test/test_importlib/resources/test_files.py @@ -85,7 +85,7 @@ def test_module_resources(self): _path.build(spec, self.site_dir) import mod - actual = resources.files(mod).joinpath('res.txt').read_text() + actual = resources.files(mod).joinpath('res.txt').read_text(encoding='utf-8') assert actual == spec['res.txt'] @@ -99,7 +99,7 @@ def test_implicit_files(self): '__init__.py': textwrap.dedent( """ import importlib.resources as res - val = res.files().joinpath('res.txt').read_text() + val = res.files().joinpath('res.txt').read_text(encoding='utf-8') """ ), 'res.txt': 'resources are the best', diff --git a/Lib/test/test_importlib/resources/test_open.py b/Lib/test/test_importlib/resources/test_open.py index 0554c41ba67d0e..86becb4bfaad37 100644 --- a/Lib/test/test_importlib/resources/test_open.py +++ b/Lib/test/test_importlib/resources/test_open.py @@ -15,7 +15,7 @@ def execute(self, package, path): class CommonTextTests(util.CommonTests, unittest.TestCase): def execute(self, package, path): target = resources.files(package).joinpath(path) - with target.open(): + with target.open(encoding='utf-8'): pass @@ -28,7 +28,7 @@ def test_open_binary(self): def test_open_text_default_encoding(self): target = resources.files(self.data) / 'utf-8.file' - with target.open() as fp: + with target.open(encoding='utf-8') as fp: result = fp.read() self.assertEqual(result, 'Hello, UTF-8 world!\n') @@ -39,7 +39,9 @@ def test_open_text_given_encoding(self): self.assertEqual(result, 'Hello, UTF-16 world!\n') def test_open_text_with_errors(self): - # Raises UnicodeError without the 'errors' argument. + """ + Raises UnicodeError without the 'errors' argument. + """ target = resources.files(self.data) / 'utf-16.file' with target.open(encoding='utf-8', errors='strict') as fp: self.assertRaises(UnicodeError, fp.read) @@ -54,11 +56,13 @@ def test_open_text_with_errors(self): def test_open_binary_FileNotFoundError(self): target = resources.files(self.data) / 'does-not-exist' - self.assertRaises(FileNotFoundError, target.open, 'rb') + with self.assertRaises(FileNotFoundError): + target.open('rb') def test_open_text_FileNotFoundError(self): target = resources.files(self.data) / 'does-not-exist' - self.assertRaises(FileNotFoundError, target.open) + with self.assertRaises(FileNotFoundError): + target.open(encoding='utf-8') class OpenDiskTests(OpenTests, unittest.TestCase): diff --git a/Lib/test/test_importlib/resources/test_path.py b/Lib/test/test_importlib/resources/test_path.py index adcf75feea78ec..34a6bdd2d58b91 100644 --- a/Lib/test/test_importlib/resources/test_path.py +++ b/Lib/test/test_importlib/resources/test_path.py @@ -14,9 +14,12 @@ def execute(self, package, path): class PathTests: def test_reading(self): - # Path should be readable. - # Test also implicitly verifies the returned object is a pathlib.Path - # instance. + """ + Path should be readable. + + Test also implicitly verifies the returned object is a pathlib.Path + instance. + """ target = resources.files(self.data) / 'utf-8.file' with resources.as_file(target) as path: self.assertTrue(path.name.endswith("utf-8.file"), repr(path)) @@ -51,8 +54,10 @@ def setUp(self): class PathZipTests(PathTests, util.ZipSetup, unittest.TestCase): def test_remove_in_context_manager(self): - # It is not an error if the file that was temporarily stashed on the - # file system is removed inside the `with` stanza. + """ + It is not an error if the file that was temporarily stashed on the + file system is removed inside the `with` stanza. + """ target = resources.files(self.data) / 'utf-8.file' with resources.as_file(target) as path: path.unlink() diff --git a/Lib/test/test_importlib/resources/test_read.py b/Lib/test/test_importlib/resources/test_read.py index 0ca8ee9d02856b..088982681e8b0c 100644 --- a/Lib/test/test_importlib/resources/test_read.py +++ b/Lib/test/test_importlib/resources/test_read.py @@ -12,7 +12,7 @@ def execute(self, package, path): class CommonTextTests(util.CommonTests, unittest.TestCase): def execute(self, package, path): - resources.files(package).joinpath(path).read_text() + resources.files(package).joinpath(path).read_text(encoding='utf-8') class ReadTests: @@ -21,7 +21,11 @@ def test_read_bytes(self): self.assertEqual(result, b'\0\1\2\3') def test_read_text_default_encoding(self): - result = resources.files(self.data).joinpath('utf-8.file').read_text() + result = ( + resources.files(self.data) + .joinpath('utf-8.file') + .read_text(encoding='utf-8') + ) self.assertEqual(result, 'Hello, UTF-8 world!\n') def test_read_text_given_encoding(self): @@ -33,7 +37,9 @@ def test_read_text_given_encoding(self): self.assertEqual(result, 'Hello, UTF-16 world!\n') def test_read_text_with_errors(self): - # Raises UnicodeError without the 'errors' argument. + """ + Raises UnicodeError without the 'errors' argument. + """ target = resources.files(self.data) / 'utf-16.file' self.assertRaises(UnicodeError, target.read_text, encoding='utf-8') result = target.read_text(encoding='utf-8', errors='ignore') diff --git a/Lib/test/test_importlib/resources/test_reader.py b/Lib/test/test_importlib/resources/test_reader.py index 4fd9e6bbe4281c..8670f72a334585 100644 --- a/Lib/test/test_importlib/resources/test_reader.py +++ b/Lib/test/test_importlib/resources/test_reader.py @@ -81,6 +81,17 @@ def test_join_path_compound(self): path = MultiplexedPath(self.folder) assert not path.joinpath('imaginary/foo.py').exists() + def test_join_path_common_subdir(self): + prefix = os.path.abspath(os.path.join(__file__, '..')) + data01 = os.path.join(prefix, 'data01') + data02 = os.path.join(prefix, 'data02') + path = MultiplexedPath(data01, data02) + self.assertIsInstance(path.joinpath('subdirectory'), MultiplexedPath) + self.assertEqual( + str(path.joinpath('subdirectory', 'subsubdir'))[len(prefix) + 1 :], + os.path.join('data02', 'subdirectory', 'subsubdir'), + ) + def test_repr(self): self.assertEqual( repr(MultiplexedPath(self.folder)), diff --git a/Lib/test/test_importlib/resources/test_resource.py b/Lib/test/test_importlib/resources/test_resource.py index f7e3abbdc805a7..6f75cf57f03d02 100644 --- a/Lib/test/test_importlib/resources/test_resource.py +++ b/Lib/test/test_importlib/resources/test_resource.py @@ -1,3 +1,4 @@ +import contextlib import sys import unittest import uuid @@ -7,7 +8,7 @@ from . import zipdata01, zipdata02 from . import util from importlib import resources, import_module -from test.support import import_helper +from test.support import import_helper, os_helper from test.support.os_helper import unlink @@ -69,10 +70,12 @@ def test_resource_missing(self): class ResourceCornerCaseTests(unittest.TestCase): def test_package_has_no_reader_fallback(self): - # Test odd ball packages which: + """ + Test odd ball packages which: # 1. Do not have a ResourceReader as a loader # 2. Are not on the file system # 3. Are not in a zip file + """ module = util.create_package( file=data01, path=data01.__file__, contents=['A', 'B', 'C'] ) @@ -138,82 +141,71 @@ def test_unrelated_contents(self): ) +@contextlib.contextmanager +def zip_on_path(dir): + data_path = pathlib.Path(zipdata01.__file__) + source_zip_path = data_path.parent.joinpath('ziptestdata.zip') + zip_path = pathlib.Path(dir) / f'{uuid.uuid4()}.zip' + zip_path.write_bytes(source_zip_path.read_bytes()) + sys.path.append(str(zip_path)) + import_module('ziptestdata') + + try: + yield + finally: + with contextlib.suppress(ValueError): + sys.path.remove(str(zip_path)) + + with contextlib.suppress(KeyError): + del sys.path_importer_cache[str(zip_path)] + del sys.modules['ziptestdata'] + + with contextlib.suppress(OSError): + unlink(zip_path) + + class DeletingZipsTest(unittest.TestCase): """Having accessed resources in a zip file should not keep an open reference to the zip. """ - ZIP_MODULE = zipdata01 - def setUp(self): + self.fixtures = contextlib.ExitStack() + self.addCleanup(self.fixtures.close) + modules = import_helper.modules_setup() self.addCleanup(import_helper.modules_cleanup, *modules) - data_path = pathlib.Path(self.ZIP_MODULE.__file__) - data_dir = data_path.parent - self.source_zip_path = data_dir / 'ziptestdata.zip' - self.zip_path = pathlib.Path(f'{uuid.uuid4()}.zip').absolute() - self.zip_path.write_bytes(self.source_zip_path.read_bytes()) - sys.path.append(str(self.zip_path)) - self.data = import_module('ziptestdata') - - def tearDown(self): - try: - sys.path.remove(str(self.zip_path)) - except ValueError: - pass - - try: - del sys.path_importer_cache[str(self.zip_path)] - del sys.modules[self.data.__name__] - except KeyError: - pass - - try: - unlink(self.zip_path) - except OSError: - # If the test fails, this will probably fail too - pass + temp_dir = self.fixtures.enter_context(os_helper.temp_dir()) + self.fixtures.enter_context(zip_on_path(temp_dir)) def test_iterdir_does_not_keep_open(self): - c = [item.name for item in resources.files('ziptestdata').iterdir()] - self.zip_path.unlink() - del c + [item.name for item in resources.files('ziptestdata').iterdir()] def test_is_file_does_not_keep_open(self): - c = resources.files('ziptestdata').joinpath('binary.file').is_file() - self.zip_path.unlink() - del c + resources.files('ziptestdata').joinpath('binary.file').is_file() def test_is_file_failure_does_not_keep_open(self): - c = resources.files('ziptestdata').joinpath('not-present').is_file() - self.zip_path.unlink() - del c + resources.files('ziptestdata').joinpath('not-present').is_file() @unittest.skip("Desired but not supported.") def test_as_file_does_not_keep_open(self): # pragma: no cover - c = resources.as_file(resources.files('ziptestdata') / 'binary.file') - self.zip_path.unlink() - del c + resources.as_file(resources.files('ziptestdata') / 'binary.file') def test_entered_path_does_not_keep_open(self): - # This is what certifi does on import to make its bundle - # available for the process duration. - c = resources.as_file( - resources.files('ziptestdata') / 'binary.file' - ).__enter__() - self.zip_path.unlink() - del c + """ + Mimic what certifi does on import to make its bundle + available for the process duration. + """ + resources.as_file(resources.files('ziptestdata') / 'binary.file').__enter__() def test_read_binary_does_not_keep_open(self): - c = resources.files('ziptestdata').joinpath('binary.file').read_bytes() - self.zip_path.unlink() - del c + resources.files('ziptestdata').joinpath('binary.file').read_bytes() def test_read_text_does_not_keep_open(self): - c = resources.files('ziptestdata').joinpath('utf-8.file').read_text() - self.zip_path.unlink() - del c + resources.files('ziptestdata').joinpath('utf-8.file').read_text( + encoding='utf-8' + ) class ResourceFromNamespaceTest01(unittest.TestCase): diff --git a/Lib/test/test_importlib/resources/util.py b/Lib/test/test_importlib/resources/util.py index 1e72b91ff6b31f..dbe6ee81476699 100644 --- a/Lib/test/test_importlib/resources/util.py +++ b/Lib/test/test_importlib/resources/util.py @@ -80,32 +80,44 @@ def execute(self, package, path): """ def test_package_name(self): - # Passing in the package name should succeed. + """ + Passing in the package name should succeed. + """ self.execute(data01.__name__, 'utf-8.file') def test_package_object(self): - # Passing in the package itself should succeed. + """ + Passing in the package itself should succeed. + """ self.execute(data01, 'utf-8.file') def test_string_path(self): - # Passing in a string for the path should succeed. + """ + Passing in a string for the path should succeed. + """ path = 'utf-8.file' self.execute(data01, path) def test_pathlib_path(self): - # Passing in a pathlib.PurePath object for the path should succeed. + """ + Passing in a pathlib.PurePath object for the path should succeed. + """ path = pathlib.PurePath('utf-8.file') self.execute(data01, path) def test_importing_module_as_side_effect(self): - # The anchor package can already be imported. + """ + The anchor package can already be imported. + """ del sys.modules[data01.__name__] self.execute(data01.__name__, 'utf-8.file') def test_missing_path(self): - # Attempting to open or read or request the path for a - # non-existent path should succeed if open_resource - # can return a viable data stream. + """ + Attempting to open or read or request the path for a + non-existent path should succeed if open_resource + can return a viable data stream. + """ bytes_data = io.BytesIO(b'Hello, world!') package = create_package(file=bytes_data, path=FileNotFoundError()) self.execute(package, 'utf-8.file') diff --git a/Misc/NEWS.d/next/Library/2023-02-17-19-00-58.gh-issue-97930.C_nQjb.rst b/Misc/NEWS.d/next/Library/2023-02-17-19-00-58.gh-issue-97930.C_nQjb.rst new file mode 100644 index 00000000000000..967e13f752bcd1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-17-19-00-58.gh-issue-97930.C_nQjb.rst @@ -0,0 +1,4 @@ +Apply changes from `importlib_resources 5.12 +`_, +including fix for ``MultiplexedPath`` to support directories in multiple +namespaces (python/importlib_resources#265). From 61f1e67c6fcbf80eb9be2b75f7d62954e28c89e6 Mon Sep 17 00:00:00 2001 From: Furkan Onder Date: Sun, 19 Feb 2023 00:22:02 +0000 Subject: [PATCH 206/225] GH-84783: Make the slice object hashable (GH-101264) --- Doc/library/functions.rst | 3 ++ Lib/test/test_capi/test_misc.py | 7 +--- Lib/test/test_doctest.py | 2 +- Lib/test/test_slice.py | 12 ++++-- ...3-02-11-23-14-06.gh-issue-84783._P5sMa.rst | 1 + Objects/sliceobject.c | 38 ++++++++++++++++++- Objects/tupleobject.c | 2 +- 7 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-11-23-14-06.gh-issue-84783._P5sMa.rst diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 658d6768457d16..3ff28849025153 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1635,6 +1635,9 @@ are always available. They are listed here in alphabetical order. example: ``a[start:stop:step]`` or ``a[start:stop, i]``. See :func:`itertools.islice` for an alternate version that returns an iterator. + .. versionchanged:: 3.12 + Slice objects are now :term:`hashable` (provided :attr:`~slice.start`, + :attr:`~slice.stop`, and :attr:`~slice.step` are hashable). .. function:: sorted(iterable, /, *, key=None, reverse=False) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index f26b4723d1e68b..ad099c61463b66 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -419,11 +419,6 @@ def __setitem__(self, index, value): with self.assertRaises(TypeError): _testcapi.sequence_set_slice(None, 1, 3, 'xy') - mapping = {1: 'a', 2: 'b', 3: 'c'} - with self.assertRaises(TypeError): - _testcapi.sequence_set_slice(mapping, 1, 3, 'xy') - self.assertEqual(mapping, {1: 'a', 2: 'b', 3: 'c'}) - def test_sequence_del_slice(self): # Correct case: data = [1, 2, 3, 4, 5] @@ -459,7 +454,7 @@ def __delitem__(self, index): _testcapi.sequence_del_slice(None, 1, 3) mapping = {1: 'a', 2: 'b', 3: 'c'} - with self.assertRaises(TypeError): + with self.assertRaises(KeyError): _testcapi.sequence_del_slice(mapping, 1, 3) self.assertEqual(mapping, {1: 'a', 2: 'b', 3: 'c'}) diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index 65e215f1cdda4a..3491d4cdb1c18b 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -707,7 +707,7 @@ def non_Python_modules(): r""" >>> import builtins >>> tests = doctest.DocTestFinder().find(builtins) - >>> 825 < len(tests) < 845 # approximate number of objects with docstrings + >>> 830 < len(tests) < 850 # approximate number of objects with docstrings True >>> real_tests = [t for t in tests if len(t.examples) > 0] >>> len(real_tests) # objects that actually have doctests diff --git a/Lib/test/test_slice.py b/Lib/test/test_slice.py index 03fde3275e1475..c35a2293f790a2 100644 --- a/Lib/test/test_slice.py +++ b/Lib/test/test_slice.py @@ -80,10 +80,16 @@ def test_repr(self): self.assertEqual(repr(slice(1, 2, 3)), "slice(1, 2, 3)") def test_hash(self): - # Verify clearing of SF bug #800796 - self.assertRaises(TypeError, hash, slice(5)) + self.assertEqual(hash(slice(5)), slice(5).__hash__()) + self.assertEqual(hash(slice(1, 2)), slice(1, 2).__hash__()) + self.assertEqual(hash(slice(1, 2, 3)), slice(1, 2, 3).__hash__()) + self.assertNotEqual(slice(5), slice(6)) + + with self.assertRaises(TypeError): + hash(slice(1, 2, [])) + with self.assertRaises(TypeError): - slice(5).__hash__() + hash(slice(4, {})) def test_cmp(self): s1 = slice(1, 2, 3) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-11-23-14-06.gh-issue-84783._P5sMa.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-11-23-14-06.gh-issue-84783._P5sMa.rst new file mode 100644 index 00000000000000..e1c851a0825a7f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-11-23-14-06.gh-issue-84783._P5sMa.rst @@ -0,0 +1 @@ +Make the slice object hashable. diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c index 5694bd9c661fa5..5d2e6ad522bcf2 100644 --- a/Objects/sliceobject.c +++ b/Objects/sliceobject.c @@ -628,6 +628,42 @@ slice_traverse(PySliceObject *v, visitproc visit, void *arg) return 0; } +/* code based on tuplehash() of Objects/tupleobject.c */ +#if SIZEOF_PY_UHASH_T > 4 +#define _PyHASH_XXPRIME_1 ((Py_uhash_t)11400714785074694791ULL) +#define _PyHASH_XXPRIME_2 ((Py_uhash_t)14029467366897019727ULL) +#define _PyHASH_XXPRIME_5 ((Py_uhash_t)2870177450012600261ULL) +#define _PyHASH_XXROTATE(x) ((x << 31) | (x >> 33)) /* Rotate left 31 bits */ +#else +#define _PyHASH_XXPRIME_1 ((Py_uhash_t)2654435761UL) +#define _PyHASH_XXPRIME_2 ((Py_uhash_t)2246822519UL) +#define _PyHASH_XXPRIME_5 ((Py_uhash_t)374761393UL) +#define _PyHASH_XXROTATE(x) ((x << 13) | (x >> 19)) /* Rotate left 13 bits */ +#endif + +static Py_hash_t +slicehash(PySliceObject *v) +{ + Py_uhash_t acc = _PyHASH_XXPRIME_5; +#define _PyHASH_SLICE_PART(com) { \ + Py_uhash_t lane = PyObject_Hash(v->com); \ + if(lane == (Py_uhash_t)-1) { \ + return -1; \ + } \ + acc += lane * _PyHASH_XXPRIME_2; \ + acc = _PyHASH_XXROTATE(acc); \ + acc *= _PyHASH_XXPRIME_1; \ +} + _PyHASH_SLICE_PART(start); + _PyHASH_SLICE_PART(stop); + _PyHASH_SLICE_PART(step); +#undef _PyHASH_SLICE_PART + if(acc == (Py_uhash_t)-1) { + return 1546275796; + } + return acc; +} + PyTypeObject PySlice_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "slice", /* Name of this type */ @@ -642,7 +678,7 @@ PyTypeObject PySlice_Type = { 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ - PyObject_HashNotImplemented, /* tp_hash */ + (hashfunc)slicehash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index e1b9953226c0d7..7d6d0e1bea249e 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -288,7 +288,7 @@ tuplerepr(PyTupleObject *v) /* Hash for tuples. This is a slightly simplified version of the xxHash non-cryptographic hash: - - we do not use any parallellism, there is only 1 accumulator. + - we do not use any parallelism, there is only 1 accumulator. - we drop the final mixing since this is just a permutation of the output space: it does not help against collisions. - at the end, we mangle the length with a single constant. From 36b670908b3546f46283aae4dbf311e53289f3d1 Mon Sep 17 00:00:00 2001 From: Reza Rastak Date: Sat, 18 Feb 2023 19:55:43 -0500 Subject: [PATCH 207/225] Fix incorrectly documented attribute in csv docs (#101250) --- Doc/library/csv.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/csv.rst b/Doc/library/csv.rst index 41f11505aa1030..f1776554d8b9f2 100644 --- a/Doc/library/csv.rst +++ b/Doc/library/csv.rst @@ -458,7 +458,7 @@ Reader objects have the following public attributes: DictReader objects have the following public attribute: -.. attribute:: csvreader.fieldnames +.. attribute:: DictReader.fieldnames If not passed as a parameter when creating the object, this attribute is initialized upon first access or when the first record is read from the From 6aab56f3c2ee331116eba242d2fcdca592577328 Mon Sep 17 00:00:00 2001 From: Patricio Paez Date: Sat, 18 Feb 2023 19:06:03 -0600 Subject: [PATCH 208/225] gh-99735: Use required=True in argparse subparsers example (#100927) --- Doc/library/argparse.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index dbaa5d0d9b995b..34b4c61649b99f 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -1867,7 +1867,7 @@ Sub-commands ... >>> # create the top-level parser >>> parser = argparse.ArgumentParser() - >>> subparsers = parser.add_subparsers() + >>> subparsers = parser.add_subparsers(required=True) >>> >>> # create the parser for the "foo" command >>> parser_foo = subparsers.add_parser('foo') From 072935951f7cd44b40ee37fe561478b2e431c2fb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 18 Feb 2023 21:32:50 -0500 Subject: [PATCH 209/225] gh-97930: Also include subdirectory in makefile. (#102030) --- Makefile.pre.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.pre.in b/Makefile.pre.in index 490483a712014c..b28c6067a535b8 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2075,6 +2075,8 @@ TESTSUBDIRS= idlelib/idle_test \ test/test_importlib/resources/data01/subdirectory \ test/test_importlib/resources/data02 \ test/test_importlib/resources/data02/one \ + test/test_importlib/resources/data02/subdirectory \ + test/test_importlib/resources/data02/subdirectory/subsubdir \ test/test_importlib/resources/data02/two \ test/test_importlib/resources/data03 \ test/test_importlib/resources/data03/namespace \ From 9a07eff628c1cd88b7cdda88a8fd0db3fe7ea552 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Amiel Date: Sun, 19 Feb 2023 11:18:12 +0100 Subject: [PATCH 210/225] gh-100210: Correct the comment link for unescaping HTML (#100212) gh-100210: correct the comment link for unescaping HTML --- Lib/html/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/html/__init__.py b/Lib/html/__init__.py index da0a0a3ce70eed..1543460ca33b0a 100644 --- a/Lib/html/__init__.py +++ b/Lib/html/__init__.py @@ -25,7 +25,7 @@ def escape(s, quote=True): return s -# see http://www.w3.org/TR/html5/syntax.html#tokenizing-character-references +# see https://html.spec.whatwg.org/multipage/parsing.html#numeric-character-reference-end-state _invalid_charrefs = { 0x00: '\ufffd', # REPLACEMENT CHARACTER From 71f614ef2a3d66213b9cae807cbbc1ed03741221 Mon Sep 17 00:00:00 2001 From: Owain Davies <116417456+OTheDev@users.noreply.github.com> Date: Sun, 19 Feb 2023 22:00:59 +0700 Subject: [PATCH 211/225] Add missing 'is' to `cmath.log()` docstring (#102049) Fix missing 'is' in cmath.log() docstring --- Modules/clinic/cmathmodule.c.h | 4 ++-- Modules/cmathmodule.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/clinic/cmathmodule.c.h b/Modules/clinic/cmathmodule.c.h index b1da9452c61db8..941448e76e80de 100644 --- a/Modules/clinic/cmathmodule.c.h +++ b/Modules/clinic/cmathmodule.c.h @@ -644,7 +644,7 @@ PyDoc_STRVAR(cmath_log__doc__, "\n" "log(z[, base]) -> the logarithm of z to the given base.\n" "\n" -"If the base not specified, returns the natural logarithm (base e) of z."); +"If the base is not specified, returns the natural logarithm (base e) of z."); #define CMATH_LOG_METHODDEF \ {"log", _PyCFunction_CAST(cmath_log), METH_FASTCALL, cmath_log__doc__}, @@ -982,4 +982,4 @@ cmath_isclose(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec exit: return return_value; } -/*[clinic end generated code: output=0146c656e67f5d5f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=87f609786ef270cd input=a9049054013a1b77]*/ diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 2038ac26e65857..53e34061d53773 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -957,12 +957,12 @@ cmath.log log(z[, base]) -> the logarithm of z to the given base. -If the base not specified, returns the natural logarithm (base e) of z. +If the base is not specified, returns the natural logarithm (base e) of z. [clinic start generated code]*/ static PyObject * cmath_log_impl(PyObject *module, Py_complex x, PyObject *y_obj) -/*[clinic end generated code: output=4effdb7d258e0d94 input=230ed3a71ecd000a]*/ +/*[clinic end generated code: output=4effdb7d258e0d94 input=e1f81d4fcfd26497]*/ { Py_complex y; From 32df540635cacce1053ee0ef98ee23f3f6a43c02 Mon Sep 17 00:00:00 2001 From: neuralstring <107343209+neuralstring@users.noreply.github.com> Date: Sun, 19 Feb 2023 16:39:03 +0000 Subject: [PATCH 212/225] gh-100425: Update tutorial docs related to sum() accuracy (FH-101854) --- Doc/tutorial/stdlib2.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/tutorial/stdlib2.rst b/Doc/tutorial/stdlib2.rst index 0c101c1f207235..33f311db3a24d2 100644 --- a/Doc/tutorial/stdlib2.rst +++ b/Doc/tutorial/stdlib2.rst @@ -394,7 +394,7 @@ point:: >>> sum([Decimal('0.1')]*10) == Decimal('1.0') True - >>> sum([0.1]*10) == 1.0 + >>> 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 == 1.0 False The :mod:`decimal` module provides arithmetic with as much precision as needed:: From b513c46d998344dc07eb6d510782c2e23d2b859e Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 19 Feb 2023 19:15:44 +0000 Subject: [PATCH 213/225] gh-85417: Clarify behaviour on branch cuts in cmath module (#102046) This PR updates the cmath module documentation to reflect the reality that Python is almost always (and as far as I can tell, that "almost" can be omitted) running on a machine whose C double supports signed zeros. * Removes misleading references to functions being continuous from above / below / the left / the right at branch cuts * Expands the note on branch cuts at the top of the module documentation to explain the double-sided sign-of-zero-based behaviour --- Doc/library/cmath.rst | 66 +++++++++++-------- ...3-02-19-10-33-01.gh-issue-85417.kYO8u3.rst | 1 + 2 files changed, 39 insertions(+), 28 deletions(-) create mode 100644 Misc/NEWS.d/next/Documentation/2023-02-19-10-33-01.gh-issue-85417.kYO8u3.rst diff --git a/Doc/library/cmath.rst b/Doc/library/cmath.rst index 28cd96b0e12da9..5ed7a09b3e9db2 100644 --- a/Doc/library/cmath.rst +++ b/Doc/library/cmath.rst @@ -15,11 +15,27 @@ the function is then applied to the result of the conversion. .. note:: - On platforms with hardware and system-level support for signed - zeros, functions involving branch cuts are continuous on *both* - sides of the branch cut: the sign of the zero distinguishes one - side of the branch cut from the other. On platforms that do not - support signed zeros the continuity is as specified below. + For functions involving branch cuts, we have the problem of deciding how to + define those functions on the cut itself. Following Kahan's "Branch cuts for + complex elementary functions" paper, as well as Annex G of C99 and later C + standards, we use the sign of zero to distinguish one side of the branch cut + from the other: for a branch cut along (a portion of) the real axis we look + at the sign of the imaginary part, while for a branch cut along the + imaginary axis we look at the sign of the real part. + + For example, the :func:`cmath.sqrt` function has a branch cut along the + negative real axis. An argument of ``complex(-2.0, -0.0)`` is treated as + though it lies *below* the branch cut, and so gives a result on the negative + imaginary axis:: + + >>> cmath.sqrt(complex(-2.0, -0.0)) + -1.4142135623730951j + + But an argument of ``complex(-2.0, 0.0)`` is treated as though it lies above + the branch cut:: + + >>> cmath.sqrt(complex(-2.0, 0.0)) + 1.4142135623730951j Conversions to and from polar coordinates @@ -44,14 +60,11 @@ rectangular coordinates to polar coordinates and back. .. function:: phase(x) - Return the phase of *x* (also known as the *argument* of *x*), as a - float. ``phase(x)`` is equivalent to ``math.atan2(x.imag, - x.real)``. The result lies in the range [-\ *π*, *π*], and the branch - cut for this operation lies along the negative real axis, - continuous from above. On systems with support for signed zeros - (which includes most systems in current use), this means that the - sign of the result is the same as the sign of ``x.imag``, even when - ``x.imag`` is zero:: + Return the phase of *x* (also known as the *argument* of *x*), as a float. + ``phase(x)`` is equivalent to ``math.atan2(x.imag, x.real)``. The result + lies in the range [-\ *π*, *π*], and the branch cut for this operation lies + along the negative real axis. The sign of the result is the same as the + sign of ``x.imag``, even when ``x.imag`` is zero:: >>> phase(complex(-1.0, 0.0)) 3.141592653589793 @@ -92,8 +105,8 @@ Power and logarithmic functions .. function:: log(x[, base]) Returns the logarithm of *x* to the given *base*. If the *base* is not - specified, returns the natural logarithm of *x*. There is one branch cut, from 0 - along the negative real axis to -∞, continuous from above. + specified, returns the natural logarithm of *x*. There is one branch cut, + from 0 along the negative real axis to -∞. .. function:: log10(x) @@ -112,9 +125,9 @@ Trigonometric functions .. function:: acos(x) - Return the arc cosine of *x*. There are two branch cuts: One extends right from - 1 along the real axis to ∞, continuous from below. The other extends left from - -1 along the real axis to -∞, continuous from above. + Return the arc cosine of *x*. There are two branch cuts: One extends right + from 1 along the real axis to ∞. The other extends left from -1 along the + real axis to -∞. .. function:: asin(x) @@ -125,9 +138,8 @@ Trigonometric functions .. function:: atan(x) Return the arc tangent of *x*. There are two branch cuts: One extends from - ``1j`` along the imaginary axis to ``∞j``, continuous from the right. The - other extends from ``-1j`` along the imaginary axis to ``-∞j``, continuous - from the left. + ``1j`` along the imaginary axis to ``∞j``. The other extends from ``-1j`` + along the imaginary axis to ``-∞j``. .. function:: cos(x) @@ -151,23 +163,21 @@ Hyperbolic functions .. function:: acosh(x) Return the inverse hyperbolic cosine of *x*. There is one branch cut, - extending left from 1 along the real axis to -∞, continuous from above. + extending left from 1 along the real axis to -∞. .. function:: asinh(x) Return the inverse hyperbolic sine of *x*. There are two branch cuts: - One extends from ``1j`` along the imaginary axis to ``∞j``, - continuous from the right. The other extends from ``-1j`` along - the imaginary axis to ``-∞j``, continuous from the left. + One extends from ``1j`` along the imaginary axis to ``∞j``. The other + extends from ``-1j`` along the imaginary axis to ``-∞j``. .. function:: atanh(x) Return the inverse hyperbolic tangent of *x*. There are two branch cuts: One - extends from ``1`` along the real axis to ``∞``, continuous from below. The - other extends from ``-1`` along the real axis to ``-∞``, continuous from - above. + extends from ``1`` along the real axis to ``∞``. The other extends from + ``-1`` along the real axis to ``-∞``. .. function:: cosh(x) diff --git a/Misc/NEWS.d/next/Documentation/2023-02-19-10-33-01.gh-issue-85417.kYO8u3.rst b/Misc/NEWS.d/next/Documentation/2023-02-19-10-33-01.gh-issue-85417.kYO8u3.rst new file mode 100644 index 00000000000000..a5532df14795d2 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-02-19-10-33-01.gh-issue-85417.kYO8u3.rst @@ -0,0 +1 @@ +Update :mod:`cmath` documentation to clarify behaviour on branch cuts. From 3b264df470c82d77f5b01c6f9d1d7173d1cb9597 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 19 Feb 2023 13:21:37 -0600 Subject: [PATCH 214/225] Misc improvements to the float tutorial (GH-102052) --- Doc/tutorial/floatingpoint.rst | 139 +++++++++++++++++++++++++-------- 1 file changed, 106 insertions(+), 33 deletions(-) diff --git a/Doc/tutorial/floatingpoint.rst b/Doc/tutorial/floatingpoint.rst index cedade6e336608..306b1eba3c45b8 100644 --- a/Doc/tutorial/floatingpoint.rst +++ b/Doc/tutorial/floatingpoint.rst @@ -1,6 +1,7 @@ .. testsetup:: import math + from fractions import Fraction .. _tut-fp-issues: @@ -9,12 +10,13 @@ Floating Point Arithmetic: Issues and Limitations ************************************************** .. sectionauthor:: Tim Peters +.. sectionauthor:: Raymond Hettinger Floating-point numbers are represented in computer hardware as base 2 (binary) -fractions. For example, the **decimal** fraction ``0.125`` -has value 1/10 + 2/100 + 5/1000, and in the same way the **binary** fraction ``0.001`` -has value 0/2 + 0/4 + 1/8. These two fractions have identical values, the only +fractions. For example, the **decimal** fraction ``0.625`` +has value 6/10 + 2/100 + 5/1000, and in the same way the **binary** fraction ``0.101`` +has value 1/2 + 0/4 + 1/8. These two fractions have identical values, the only real difference being that the first is written in base 10 fractional notation, and the second in base 2. @@ -57,13 +59,15 @@ Many users are not aware of the approximation because of the way values are displayed. Python only prints a decimal approximation to the true decimal value of the binary approximation stored by the machine. On most machines, if Python were to print the true decimal value of the binary approximation stored -for 0.1, it would have to display :: +for 0.1, it would have to display:: >>> 0.1 0.1000000000000000055511151231257827021181583404541015625 That is more digits than most people find useful, so Python keeps the number -of digits manageable by displaying a rounded value instead :: +of digits manageable by displaying a rounded value instead: + +.. doctest:: >>> 1 / 10 0.1 @@ -90,7 +94,10 @@ thing in all languages that support your hardware's floating-point arithmetic (although some languages may not *display* the difference by default, or in all output modes). -For more pleasant output, you may wish to use string formatting to produce a limited number of significant digits:: +For more pleasant output, you may wish to use string formatting to produce a +limited number of significant digits: + +.. doctest:: >>> format(math.pi, '.12g') # give 12 significant digits '3.14159265359' @@ -101,33 +108,49 @@ For more pleasant output, you may wish to use string formatting to produce a lim >>> repr(math.pi) '3.141592653589793' - It's important to realize that this is, in a real sense, an illusion: you're simply rounding the *display* of the true machine value. One illusion may beget another. For example, since 0.1 is not exactly 1/10, -summing three values of 0.1 may not yield exactly 0.3, either:: +summing three values of 0.1 may not yield exactly 0.3, either: + +.. doctest:: - >>> .1 + .1 + .1 == .3 + >>> 0.1 + 0.1 + 0.1 == 0.3 False Also, since the 0.1 cannot get any closer to the exact value of 1/10 and 0.3 cannot get any closer to the exact value of 3/10, then pre-rounding with -:func:`round` function cannot help:: +:func:`round` function cannot help: - >>> round(.1, 1) + round(.1, 1) + round(.1, 1) == round(.3, 1) +.. doctest:: + + >>> round(0.1, 1) + round(0.1, 1) + round(0.1, 1) == round(0.3, 1) False Though the numbers cannot be made closer to their intended exact values, -the :func:`round` function can be useful for post-rounding so that results -with inexact values become comparable to one another:: +the :func:`math.isclose` function can be useful for comparing inexact values: - >>> round(.1 + .1 + .1, 10) == round(.3, 10) - True +.. doctest:: + + >>> math.isclose(0.1 + 0.1 + 0.1, 0.3) + True + +Alternatively, the :func:`round` function can be used to compare rough +approximations:: + +.. doctest:: + + >>> round(math.pi, ndigits=2) == round(22 / 7, ndigits=2) + True Binary floating-point arithmetic holds many surprises like this. The problem with "0.1" is explained in precise detail below, in the "Representation Error" -section. See `The Perils of Floating Point `_ +section. See `Examples of Floating Point Problems +`_ for +a pleasant summary of how binary floating point works and the kinds of +problems commonly encountered in practice. Also see +`The Perils of Floating Point `_ for a more complete account of other common surprises. As that says near the end, "there are no easy answers." Still, don't be unduly @@ -158,26 +181,34 @@ statistical operations supplied by the SciPy project. See . Python provides tools that may help on those rare occasions when you really *do* want to know the exact value of a float. The :meth:`float.as_integer_ratio` method expresses the value of a float as a -fraction:: +fraction: + +.. doctest:: >>> x = 3.14159 >>> x.as_integer_ratio() (3537115888337719, 1125899906842624) Since the ratio is exact, it can be used to losslessly recreate the -original value:: +original value: + +.. doctest:: >>> x == 3537115888337719 / 1125899906842624 True The :meth:`float.hex` method expresses a float in hexadecimal (base -16), again giving the exact value stored by your computer:: +16), again giving the exact value stored by your computer: + +.. doctest:: >>> x.hex() '0x1.921f9f01b866ep+1' This precise hexadecimal representation can be used to reconstruct -the float value exactly:: +the float value exactly: + +.. doctest:: >>> x == float.fromhex('0x1.921f9f01b866ep+1') True @@ -186,17 +217,43 @@ Since the representation is exact, it is useful for reliably porting values across different versions of Python (platform independence) and exchanging data with other languages that support the same format (such as Java and C99). -Another helpful tool is the :func:`math.fsum` function which helps mitigate -loss-of-precision during summation. It tracks "lost digits" as values are -added onto a running total. That can make a difference in overall accuracy -so that the errors do not accumulate to the point where they affect the -final total: +Another helpful tool is the :func:`sum` function which helps mitigate +loss-of-precision during summation. It uses extended precision for +intermediate rounding steps as values are added onto a running total. +That can make a difference in overall accuracy so that the errors do not +accumulate to the point where they affect the final total: + +.. doctest:: >>> 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 == 1.0 False - >>> math.fsum([0.1] * 10) == 1.0 + >>> sum([0.1] * 10) == 1.0 True +The :func:`math.fsum()` goes further and tracks all of the "lost digits" +as values are added onto a running total so that the result has only a +single rounding. This is slower than :func:`sum` but will be more +accurate in uncommon cases where large magnitude inputs mostly cancel +each other out leaving a final sum near zero: + +.. doctest:: + + >>> arr = [-0.10430216751806065, -266310978.67179024, 143401161448607.16, + ... -143401161400469.7, 266262841.31058735, -0.003244936839808227] + >>> float(sum(map(Fraction, arr))) # Exact summation with single rounding + 8.042173697819788e-13 + >>> math.fsum(arr) # Single rounding + 8.042173697819788e-13 + >>> sum(arr) # Multiple roundings in extended precision + 8.042178034628478e-13 + >>> total = 0.0 + >>> for x in arr: + ... total += x # Multiple roundings in standard precision + ... + >>> total # Straight addition has no correct digits! + -0.0051575902860057365 + + .. _tut-fp-error: Representation Error @@ -225,20 +282,28 @@ as :: J ~= 2**N / 10 and recalling that *J* has exactly 53 bits (is ``>= 2**52`` but ``< 2**53``), -the best value for *N* is 56:: +the best value for *N* is 56: + +.. doctest:: >>> 2**52 <= 2**56 // 10 < 2**53 True That is, 56 is the only value for *N* that leaves *J* with exactly 53 bits. The -best possible value for *J* is then that quotient rounded:: +best possible value for *J* is then that quotient rounded: + +.. doctest:: >>> q, r = divmod(2**56, 10) >>> r 6 Since the remainder is more than half of 10, the best approximation is obtained -by rounding up:: +by rounding up: + +.. doctest:: + + >>> q+1 7205759403792794 @@ -256,13 +321,17 @@ if we had not rounded up, the quotient would have been a little bit smaller than 1/10. But in no case can it be *exactly* 1/10! So the computer never "sees" 1/10: what it sees is the exact fraction given -above, the best 754 double approximation it can get:: +above, the best 754 double approximation it can get: + +.. doctest:: >>> 0.1 * 2 ** 55 3602879701896397.0 If we multiply that fraction by 10\*\*55, we can see the value out to -55 decimal digits:: +55 decimal digits: + +.. doctest:: >>> 3602879701896397 * 10 ** 55 // 2 ** 55 1000000000000000055511151231257827021181583404541015625 @@ -270,13 +339,17 @@ If we multiply that fraction by 10\*\*55, we can see the value out to meaning that the exact number stored in the computer is equal to the decimal value 0.1000000000000000055511151231257827021181583404541015625. Instead of displaying the full decimal value, many languages (including -older versions of Python), round the result to 17 significant digits:: +older versions of Python), round the result to 17 significant digits: + +.. doctest:: >>> format(0.1, '.17f') '0.10000000000000001' The :mod:`fractions` and :mod:`decimal` modules make these calculations -easy:: +easy: + +.. doctest:: >>> from decimal import Decimal >>> from fractions import Fraction From 60bbed7f174e481d3fc69984430a4667951678b3 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 19 Feb 2023 21:22:29 +0100 Subject: [PATCH 215/225] gh-101578: Amend PyErr_{Set,Get}RaisedException docs (#101962) Co-authored-by: C.A.M. Gerlach --- Doc/c-api/exceptions.rst | 49 ++++++++++++++-------------------------- Doc/data/refcounts.dat | 3 +++ 2 files changed, 20 insertions(+), 32 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index de9b15edd6859a..e735f8db402363 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -402,51 +402,36 @@ Querying the error indicator .. c:function:: PyObject *PyErr_GetRaisedException(void) - Returns the exception currently being raised, clearing the exception at - the same time. Do not confuse this with the exception currently being - handled which can be accessed with :c:func:`PyErr_GetHandledException`. + Return the exception currently being raised, clearing the error indicator at + the same time. - .. note:: + This function is used by code that needs to catch exceptions, + or code that needs to save and restore the error indicator temporarily. - This function is normally only used by code that needs to catch exceptions or - by code that needs to save and restore the error indicator temporarily, e.g.:: + For example:: - { - PyObject *exc = PyErr_GetRaisedException(); + { + PyObject *exc = PyErr_GetRaisedException(); - /* ... code that might produce other errors ... */ + /* ... code that might produce other errors ... */ - PyErr_SetRaisedException(exc); - } + PyErr_SetRaisedException(exc); + } + + .. seealso:: :c:func:`PyErr_GetHandledException`, + to save the exception currently being handled. .. versionadded:: 3.12 .. c:function:: void PyErr_SetRaisedException(PyObject *exc) - Sets the exception currently being raised ``exc``. - If the exception is already set, it is cleared first. - - ``exc`` must be a valid exception. - (Violating this rules will cause subtle problems later.) - This call consumes a reference to the ``exc`` object: you must own a - reference to that object before the call and after the call you no longer own - that reference. - (If you don't understand this, don't use this function. I warned you.) + Set *exc* as the exception currently being raised, + clearing the existing exception if one is set. - .. note:: - - This function is normally only used by code that needs to save and restore the - error indicator temporarily. Use :c:func:`PyErr_GetRaisedException` to save - the current exception, e.g.:: - - { - PyObject *exc = PyErr_GetRaisedException(); - - /* ... code that might produce other errors ... */ + .. warning:: - PyErr_SetRaisedException(exc); - } + This call steals a reference to *exc*, which must be a valid exception. .. versionadded:: 3.12 diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index 349c4dd5be3d81..ed2958f8cd2205 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -606,6 +606,9 @@ PyErr_GetExcInfo:PyObject**:ptype:+1: PyErr_GetExcInfo:PyObject**:pvalue:+1: PyErr_GetExcInfo:PyObject**:ptraceback:+1: +PyErr_GetRaisedException:PyObject*::+1: +PyErr_SetRaisedException:::: + PyErr_GivenExceptionMatches:int::: PyErr_GivenExceptionMatches:PyObject*:given:0: PyErr_GivenExceptionMatches:PyObject*:exc:0: From b1b375e2670a58fc37cb4c2629ed73b045159918 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 20 Feb 2023 01:16:11 +0000 Subject: [PATCH 216/225] gh-97786: Fix compiler warnings in pytime.c (#101826) Fixes compiler warnings in pytime.c. --- ...3-02-11-13-23-29.gh-issue-97786.QjvQ1B.rst | 2 ++ Python/pytime.c | 35 +++++++++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-11-13-23-29.gh-issue-97786.QjvQ1B.rst diff --git a/Misc/NEWS.d/next/Library/2023-02-11-13-23-29.gh-issue-97786.QjvQ1B.rst b/Misc/NEWS.d/next/Library/2023-02-11-13-23-29.gh-issue-97786.QjvQ1B.rst new file mode 100644 index 00000000000000..df194b67590d67 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-11-13-23-29.gh-issue-97786.QjvQ1B.rst @@ -0,0 +1,2 @@ +Fix potential undefined behaviour in corner cases of floating-point-to-time +conversions. diff --git a/Python/pytime.c b/Python/pytime.c index 01c07da074757e..acd1842056af43 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -1,5 +1,4 @@ #include "Python.h" -#include "pycore_pymath.h" // _Py_InIntegralTypeRange() #ifdef MS_WINDOWS # include // struct timeval #endif @@ -41,6 +40,14 @@ # error "unsupported time_t size" #endif +#if PY_TIME_T_MAX + PY_TIME_T_MIN != -1 +# error "time_t is not a two's complement integer type" +#endif + +#if _PyTime_MIN + _PyTime_MAX != -1 +# error "_PyTime_t is not a two's complement integer type" +#endif + static void pytime_time_t_overflow(void) @@ -294,7 +301,21 @@ pytime_double_to_denominator(double d, time_t *sec, long *numerator, } assert(0.0 <= floatpart && floatpart < denominator); - if (!_Py_InIntegralTypeRange(time_t, intpart)) { + /* + Conversion of an out-of-range value to time_t gives undefined behaviour + (C99 §6.3.1.4p1), so we must guard against it. However, checking that + `intpart` is in range is delicate: the obvious expression `intpart <= + PY_TIME_T_MAX` will first convert the value `PY_TIME_T_MAX` to a double, + potentially changing its value and leading to us failing to catch some + UB-inducing values. The code below works correctly under the mild + assumption that time_t is a two's complement integer type with no trap + representation, and that `PY_TIME_T_MIN` is within the representable + range of a C double. + + Note: we want the `if` condition below to be true for NaNs; therefore, + resist any temptation to simplify by applying De Morgan's laws. + */ + if (!((double)PY_TIME_T_MIN <= intpart && intpart < -(double)PY_TIME_T_MIN)) { pytime_time_t_overflow(); return -1; } @@ -349,7 +370,8 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round) d = pytime_round(d, round); (void)modf(d, &intpart); - if (!_Py_InIntegralTypeRange(time_t, intpart)) { + /* See comments in pytime_double_to_denominator */ + if (!((double)PY_TIME_T_MIN <= intpart && intpart < -(double)PY_TIME_T_MIN)) { pytime_time_t_overflow(); return -1; } @@ -515,8 +537,9 @@ pytime_from_double(_PyTime_t *tp, double value, _PyTime_round_t round, d *= (double)unit_to_ns; d = pytime_round(d, round); - if (!_Py_InIntegralTypeRange(_PyTime_t, d)) { - pytime_overflow(); + /* See comments in pytime_double_to_denominator */ + if (!((double)_PyTime_MIN <= d && d < -(double)_PyTime_MIN)) { + pytime_time_t_overflow(); return -1; } _PyTime_t ns = (_PyTime_t)d; @@ -910,7 +933,7 @@ py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) info->monotonic = 0; info->adjustable = 1; if (clock_getres(CLOCK_REALTIME, &res) == 0) { - info->resolution = res.tv_sec + res.tv_nsec * 1e-9; + info->resolution = (double)res.tv_sec + (double)res.tv_nsec * 1e-9; } else { info->resolution = 1e-9; From 27136310414965a3ea7f835e416cf74b91cefb48 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 20 Feb 2023 14:07:25 +0100 Subject: [PATCH 217/225] gh-101981: Build macOS as recommended by the devguide (GH-102070) Automerge-Triggered-By: GH:erlend-aasland --- .github/workflows/build.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 97ea2d94598e2c..acc8d936774af5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -157,12 +157,19 @@ jobs: PYTHONSTRICTEXTENSIONBUILD: 1 steps: - uses: actions/checkout@v3 - - name: Prepare homebrew environment variables + - name: Install Homebrew dependencies + run: brew install pkg-config openssl@1.1 xz gdbm tcl-tk + - name: Prepare Homebrew environment variables run: | - echo "LDFLAGS=-L$(brew --prefix tcl-tk)/lib" >> $GITHUB_ENV + echo "CFLAGS=-I$(brew --prefix gdbm)/include -I$(brew --prefix xz)/include" >> $GITHUB_ENV + echo "LDFLAGS=-L$(brew --prefix gdbm)/lib -I$(brew --prefix xz)/lib" >> $GITHUB_ENV echo "PKG_CONFIG_PATH=$(brew --prefix openssl@1.1)/lib/pkgconfig:$(brew --prefix tcl-tk)/lib/pkgconfig" >> $GITHUB_ENV - name: Configure CPython - run: ./configure --with-pydebug --prefix=/opt/python-dev + run: | + ./configure \ + --with-pydebug \ + --prefix=/opt/python-dev \ + --with-openssl="$(brew --prefix openssl@1.1)" - name: Build CPython run: make -j4 - name: Display build info From c00faf79438cc7f0d98af2679c695f747e4369a3 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 20 Feb 2023 14:46:20 +0100 Subject: [PATCH 218/225] gh-101819: Adapt _io types to heap types, batch 1 (GH-101949) Adapt StringIO, TextIOWrapper, FileIO, Buffered*, and BytesIO types. Automerge-Triggered-By: GH:erlend-aasland --- Lib/test/test_io.py | 91 +++++- Modules/_io/_iomodule.c | 99 +++--- Modules/_io/_iomodule.h | 47 ++- Modules/_io/bufferedio.c | 341 +++++++------------- Modules/_io/bytesio.c | 77 ++--- Modules/_io/clinic/bufferedio.c.h | 4 +- Modules/_io/fileio.c | 90 ++---- Modules/_io/stringio.c | 85 +++-- Modules/_io/textio.c | 99 +++--- Tools/c-analyzer/cpython/globals-to-fix.tsv | 8 - 10 files changed, 463 insertions(+), 478 deletions(-) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index c5f2e5060a546d..9bcc92a40c61df 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -1042,6 +1042,95 @@ def close(self): support.gc_collect() self.assertIsNone(wr(), wr) +@support.cpython_only +class TestIOCTypes(unittest.TestCase): + def setUp(self): + _io = import_helper.import_module("_io") + self.types = [ + _io.BufferedRWPair, + _io.BufferedRandom, + _io.BufferedReader, + _io.BufferedWriter, + _io.BytesIO, + _io.FileIO, + _io.IncrementalNewlineDecoder, + _io.StringIO, + _io.TextIOWrapper, + _io._BufferedIOBase, + _io._BytesIOBuffer, + _io._IOBase, + _io._RawIOBase, + _io._TextIOBase, + ] + if sys.platform == "win32": + self.types.append(_io._WindowsConsoleIO) + self._io = _io + + def test_immutable_types(self): + for tp in self.types: + with self.subTest(tp=tp): + with self.assertRaisesRegex(TypeError, "immutable"): + tp.foo = "bar" + + def test_class_hierarchy(self): + def check_subs(types, base): + for tp in types: + with self.subTest(tp=tp, base=base): + self.assertTrue(issubclass(tp, base)) + + def recursive_check(d): + for k, v in d.items(): + if isinstance(v, dict): + recursive_check(v) + elif isinstance(v, set): + check_subs(v, k) + else: + self.fail("corrupt test dataset") + + _io = self._io + hierarchy = { + _io._IOBase: { + _io._BufferedIOBase: { + _io.BufferedRWPair, + _io.BufferedRandom, + _io.BufferedReader, + _io.BufferedWriter, + _io.BytesIO, + }, + _io._RawIOBase: { + _io.FileIO, + }, + _io._TextIOBase: { + _io.StringIO, + _io.TextIOWrapper, + }, + }, + } + if sys.platform == "win32": + hierarchy[_io._IOBase][_io._RawIOBase].add(_io._WindowsConsoleIO) + + recursive_check(hierarchy) + + def test_subclassing(self): + _io = self._io + dataset = {k: True for k in self.types} + dataset[_io._BytesIOBuffer] = False + + for tp, is_basetype in dataset.items(): + with self.subTest(tp=tp, is_basetype=is_basetype): + name = f"{tp.__name__}_subclass" + bases = (tp,) + if is_basetype: + _ = type(name, bases, {}) + else: + msg = "not an acceptable base type" + with self.assertRaisesRegex(TypeError, msg): + _ = type(name, bases, {}) + + def test_disallow_instantiation(self): + _io = self._io + support.check_disallow_instantiation(self, _io._BytesIOBuffer) + class PyIOTest(IOTest): pass @@ -4671,7 +4760,7 @@ def load_tests(loader, tests, pattern): CIncrementalNewlineDecoderTest, PyIncrementalNewlineDecoderTest, CTextIOWrapperTest, PyTextIOWrapperTest, CMiscIOTest, PyMiscIOTest, - CSignalsTest, PySignalsTest, + CSignalsTest, PySignalsTest, TestIOCTypes, ) # Put the namespaces of the IO module we are testing and some useful mock diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index 811b1d221a0122..55b6535eb34b66 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -10,7 +10,6 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" #include "_iomodule.h" -#include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_pystate.h" // _PyInterpreterState_GET() #ifdef HAVE_SYS_TYPES_H @@ -315,8 +314,9 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode, } /* Create the Raw file stream */ + _PyIO_State *state = get_io_state(module); { - PyObject *RawIO_class = (PyObject *)&PyFileIO_Type; + PyObject *RawIO_class = (PyObject *)state->PyFileIO_Type; #ifdef MS_WINDOWS const PyConfig *config = _Py_GetConfig(); if (!config->legacy_windows_stdio && _PyIO_get_console_type(path_or_fd) != '\0') { @@ -390,12 +390,15 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode, { PyObject *Buffered_class; - if (updating) - Buffered_class = (PyObject *)&PyBufferedRandom_Type; - else if (creating || writing || appending) - Buffered_class = (PyObject *)&PyBufferedWriter_Type; - else if (reading) - Buffered_class = (PyObject *)&PyBufferedReader_Type; + if (updating) { + Buffered_class = (PyObject *)state->PyBufferedRandom_Type; + } + else if (creating || writing || appending) { + Buffered_class = (PyObject *)state->PyBufferedWriter_Type; + } + else if (reading) { + Buffered_class = (PyObject *)state->PyBufferedReader_Type; + } else { PyErr_Format(PyExc_ValueError, "unknown mode: '%s'", mode); @@ -417,7 +420,7 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode, } /* wraps into a TextIOWrapper */ - wrapper = PyObject_CallFunction((PyObject *)&PyTextIOWrapper_Type, + wrapper = PyObject_CallFunction((PyObject *)state->PyTextIOWrapper_Type, "OsssO", buffer, encoding, errors, newline, @@ -558,14 +561,6 @@ PyNumber_AsOff_t(PyObject *item, PyObject *err) return result; } -static inline _PyIO_State* -get_io_state(PyObject *module) -{ - void *state = _PyModule_GetState(module); - assert(state != NULL); - return (_PyIO_State *)state; -} - _PyIO_State * _PyIO_get_module_state(void) { @@ -587,6 +582,15 @@ iomodule_traverse(PyObject *mod, visitproc visit, void *arg) { return 0; Py_VISIT(state->locale_module); Py_VISIT(state->unsupported_operation); + + Py_VISIT(state->PyBufferedRWPair_Type); + Py_VISIT(state->PyBufferedRandom_Type); + Py_VISIT(state->PyBufferedReader_Type); + Py_VISIT(state->PyBufferedWriter_Type); + Py_VISIT(state->PyBytesIO_Type); + Py_VISIT(state->PyFileIO_Type); + Py_VISIT(state->PyStringIO_Type); + Py_VISIT(state->PyTextIOWrapper_Type); return 0; } @@ -599,6 +603,15 @@ iomodule_clear(PyObject *mod) { if (state->locale_module != NULL) Py_CLEAR(state->locale_module); Py_CLEAR(state->unsupported_operation); + + Py_CLEAR(state->PyBufferedRWPair_Type); + Py_CLEAR(state->PyBufferedRandom_Type); + Py_CLEAR(state->PyBufferedReader_Type); + Py_CLEAR(state->PyBufferedWriter_Type); + Py_CLEAR(state->PyBytesIO_Type); + Py_CLEAR(state->PyFileIO_Type); + Py_CLEAR(state->PyStringIO_Type); + Py_CLEAR(state->PyTextIOWrapper_Type); return 0; } @@ -612,7 +625,9 @@ iomodule_free(PyObject *mod) { * Module definition */ +#define clinic_state() (get_io_state(module)) #include "clinic/_iomodule.c.h" +#undef clinic_state static PyMethodDef module_methods[] = { _IO_OPEN_METHODDEF @@ -644,23 +659,11 @@ static PyTypeObject* static_types[] = { &PyRawIOBase_Type, &PyTextIOBase_Type, - // PyBufferedIOBase_Type(PyIOBase_Type) subclasses - &PyBytesIO_Type, - &PyBufferedReader_Type, - &PyBufferedWriter_Type, - &PyBufferedRWPair_Type, - &PyBufferedRandom_Type, - // PyRawIOBase_Type(PyIOBase_Type) subclasses - &PyFileIO_Type, &_PyBytesIOBuffer_Type, #ifdef MS_WINDOWS &PyWindowsConsoleIO_Type, #endif - - // PyTextIOBase_Type(PyIOBase_Type) subclasses - &PyStringIO_Type, - &PyTextIOWrapper_Type, }; @@ -673,6 +676,17 @@ _PyIO_Fini(void) } } +#define ADD_TYPE(module, type, spec, base) \ +do { \ + type = (PyTypeObject *)PyType_FromModuleAndSpec(module, spec, \ + (PyObject *)base); \ + if (type == NULL) { \ + goto fail; \ + } \ + if (PyModule_AddType(module, type) < 0) { \ + goto fail; \ + } \ +} while (0) PyMODINIT_FUNC PyInit__io(void) @@ -705,17 +719,9 @@ PyInit__io(void) } // Set type base classes - PyFileIO_Type.tp_base = &PyRawIOBase_Type; - PyBytesIO_Type.tp_base = &PyBufferedIOBase_Type; - PyStringIO_Type.tp_base = &PyTextIOBase_Type; #ifdef MS_WINDOWS PyWindowsConsoleIO_Type.tp_base = &PyRawIOBase_Type; #endif - PyBufferedReader_Type.tp_base = &PyBufferedIOBase_Type; - PyBufferedWriter_Type.tp_base = &PyBufferedIOBase_Type; - PyBufferedRWPair_Type.tp_base = &PyBufferedIOBase_Type; - PyBufferedRandom_Type.tp_base = &PyBufferedIOBase_Type; - PyTextIOWrapper_Type.tp_base = &PyTextIOBase_Type; // Add types for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) { @@ -725,6 +731,25 @@ PyInit__io(void) } } + // PyBufferedIOBase_Type(PyIOBase_Type) subclasses + ADD_TYPE(m, state->PyBytesIO_Type, &bytesio_spec, &PyBufferedIOBase_Type); + ADD_TYPE(m, state->PyBufferedWriter_Type, &bufferedwriter_spec, + &PyBufferedIOBase_Type); + ADD_TYPE(m, state->PyBufferedReader_Type, &bufferedreader_spec, + &PyBufferedIOBase_Type); + ADD_TYPE(m, state->PyBufferedRWPair_Type, &bufferedrwpair_spec, + &PyBufferedIOBase_Type); + ADD_TYPE(m, state->PyBufferedRandom_Type, &bufferedrandom_spec, + &PyBufferedIOBase_Type); + + // PyRawIOBase_Type(PyIOBase_Type) subclasses + ADD_TYPE(m, state->PyFileIO_Type, &fileio_spec, &PyRawIOBase_Type); + + // PyTextIOBase_Type(PyIOBase_Type) subclasses + ADD_TYPE(m, state->PyStringIO_Type, &stringio_spec, &PyTextIOBase_Type); + ADD_TYPE(m, state->PyTextIOWrapper_Type, &textiowrapper_spec, + &PyTextIOBase_Type); + state->initialized = 1; return m; diff --git a/Modules/_io/_iomodule.h b/Modules/_io/_iomodule.h index 7617cb8fb70e43..02daef9e85677e 100644 --- a/Modules/_io/_iomodule.h +++ b/Modules/_io/_iomodule.h @@ -4,6 +4,9 @@ #include "exports.h" +#include "pycore_moduleobject.h" // _PyModule_GetState() +#include "structmember.h" + /* ABCs */ extern PyTypeObject PyIOBase_Type; extern PyTypeObject PyRawIOBase_Type; @@ -11,16 +14,18 @@ extern PyTypeObject PyBufferedIOBase_Type; extern PyTypeObject PyTextIOBase_Type; /* Concrete classes */ -extern PyTypeObject PyFileIO_Type; -extern PyTypeObject PyBytesIO_Type; -extern PyTypeObject PyStringIO_Type; -extern PyTypeObject PyBufferedReader_Type; -extern PyTypeObject PyBufferedWriter_Type; -extern PyTypeObject PyBufferedRWPair_Type; -extern PyTypeObject PyBufferedRandom_Type; -extern PyTypeObject PyTextIOWrapper_Type; extern PyTypeObject PyIncrementalNewlineDecoder_Type; +/* Type specs */ +extern PyType_Spec bufferedrandom_spec; +extern PyType_Spec bufferedreader_spec; +extern PyType_Spec bufferedrwpair_spec; +extern PyType_Spec bufferedwriter_spec; +extern PyType_Spec bytesio_spec; +extern PyType_Spec fileio_spec; +extern PyType_Spec stringio_spec; +extern PyType_Spec textiowrapper_spec; + #ifdef MS_WINDOWS extern PyTypeObject PyWindowsConsoleIO_Type; #endif /* MS_WINDOWS */ @@ -140,11 +145,37 @@ typedef struct { PyObject *locale_module; PyObject *unsupported_operation; + + /* Types */ + PyTypeObject *PyBufferedRWPair_Type; + PyTypeObject *PyBufferedRandom_Type; + PyTypeObject *PyBufferedReader_Type; + PyTypeObject *PyBufferedWriter_Type; + PyTypeObject *PyBytesIO_Type; + PyTypeObject *PyFileIO_Type; + PyTypeObject *PyStringIO_Type; + PyTypeObject *PyTextIOWrapper_Type; } _PyIO_State; #define IO_MOD_STATE(mod) ((_PyIO_State *)PyModule_GetState(mod)) #define IO_STATE() _PyIO_get_module_state() +static inline _PyIO_State * +get_io_state(PyObject *module) +{ + void *state = _PyModule_GetState(module); + assert(state != NULL); + return (_PyIO_State *)state; +} + +static inline _PyIO_State * +find_io_state_by_def(PyTypeObject *type) +{ + PyObject *mod = PyType_GetModuleByDef(type, &_PyIO_Module); + assert(mod != NULL); + return get_io_state(mod); +} + extern _PyIO_State *_PyIO_get_module_state(void); #ifdef MS_WINDOWS diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index ba8969f0bcd100..56491f097100c0 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -18,12 +18,12 @@ module _io class _io._BufferedIOBase "PyObject *" "&PyBufferedIOBase_Type" class _io._Buffered "buffered *" "&PyBufferedIOBase_Type" -class _io.BufferedReader "buffered *" "&PyBufferedReader_Type" -class _io.BufferedWriter "buffered *" "&PyBufferedWriter_Type" -class _io.BufferedRWPair "rwpair *" "&PyBufferedRWPair_Type" -class _io.BufferedRandom "buffered *" "&PyBufferedRandom_Type" +class _io.BufferedReader "buffered *" "clinic_state()->PyBufferedReader_Type" +class _io.BufferedWriter "buffered *" "clinic_state()->PyBufferedWriter_Type" +class _io.BufferedRWPair "rwpair *" "clinic_state()->PyBufferedRWPair_Type" +class _io.BufferedRandom "buffered *" "clinic_state()->PyBufferedRandom_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=59460b9c5639984d]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=abd685b9d94b9888]*/ /* * BufferedIOBase class, inherits from IOBase. @@ -366,6 +366,7 @@ _enter_buffered_busy(buffered *self) static void buffered_dealloc(buffered *self) { + PyTypeObject *tp = Py_TYPE(self); self->finalizing = 1; if (_PyIOBase_finalize((PyObject *) self) < 0) return; @@ -383,7 +384,8 @@ buffered_dealloc(buffered *self) self->lock = NULL; } Py_CLEAR(self->dict); - Py_TYPE(self)->tp_free((PyObject *)self); + tp->tp_free((PyObject *)self); + Py_DECREF(tp); } static PyObject * @@ -399,6 +401,7 @@ buffered_sizeof(buffered *self, PyObject *Py_UNUSED(ignored)) static int buffered_traverse(buffered *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->raw); Py_VISIT(self->dict); return 0; @@ -1328,9 +1331,11 @@ buffered_iternext(buffered *self) CHECK_INITIALIZED(self); + _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); tp = Py_TYPE(self); - if (tp == &PyBufferedReader_Type || - tp == &PyBufferedRandom_Type) { + if (Py_IS_TYPE(tp, state->PyBufferedReader_Type) || + Py_IS_TYPE(tp, state->PyBufferedRandom_Type)) + { /* Skip method call overhead for speed */ line = _buffered_readline(self, -1); } @@ -1428,8 +1433,11 @@ _io_BufferedReader___init___impl(buffered *self, PyObject *raw, return -1; _bufferedreader_reset_buf(self); - self->fast_closed_checks = (Py_IS_TYPE(self, &PyBufferedReader_Type) && - Py_IS_TYPE(raw, &PyFileIO_Type)); + _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); + self->fast_closed_checks = ( + Py_IS_TYPE(self, state->PyBufferedReader_Type) && + Py_IS_TYPE(raw, state->PyFileIO_Type) + ); self->ok = 1; return 0; @@ -1783,8 +1791,11 @@ _io_BufferedWriter___init___impl(buffered *self, PyObject *raw, _bufferedwriter_reset_buf(self); self->pos = 0; - self->fast_closed_checks = (Py_IS_TYPE(self, &PyBufferedWriter_Type) && - Py_IS_TYPE(raw, &PyFileIO_Type)); + _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); + self->fast_closed_checks = ( + Py_IS_TYPE(self, state->PyBufferedWriter_Type) && + Py_IS_TYPE(raw, state->PyFileIO_Type) + ); self->ok = 1; return 0; @@ -2090,13 +2101,16 @@ _io_BufferedRWPair___init___impl(rwpair *self, PyObject *reader, if (_PyIOBase_check_writable(writer, Py_True) == NULL) return -1; + _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); self->reader = (buffered *) PyObject_CallFunction( - (PyObject *) &PyBufferedReader_Type, "On", reader, buffer_size); + (PyObject *)state->PyBufferedReader_Type, + "On", reader, buffer_size); if (self->reader == NULL) return -1; self->writer = (buffered *) PyObject_CallFunction( - (PyObject *) &PyBufferedWriter_Type, "On", writer, buffer_size); + (PyObject *)state->PyBufferedWriter_Type, + "On", writer, buffer_size); if (self->writer == NULL) { Py_CLEAR(self->reader); return -1; @@ -2108,6 +2122,7 @@ _io_BufferedRWPair___init___impl(rwpair *self, PyObject *reader, static int bufferedrwpair_traverse(rwpair *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->dict); return 0; } @@ -2124,13 +2139,15 @@ bufferedrwpair_clear(rwpair *self) static void bufferedrwpair_dealloc(rwpair *self) { + PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *)self); Py_CLEAR(self->reader); Py_CLEAR(self->writer); Py_CLEAR(self->dict); - Py_TYPE(self)->tp_free((PyObject *) self); + tp->tp_free((PyObject *) self); + Py_DECREF(tp); } static PyObject * @@ -2295,14 +2312,17 @@ _io_BufferedRandom___init___impl(buffered *self, PyObject *raw, _bufferedwriter_reset_buf(self); self->pos = 0; - self->fast_closed_checks = (Py_IS_TYPE(self, &PyBufferedRandom_Type) && - Py_IS_TYPE(raw, &PyFileIO_Type)); + _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); + self->fast_closed_checks = (Py_IS_TYPE(self, state->PyBufferedRandom_Type) && + Py_IS_TYPE(raw, state->PyFileIO_Type)); self->ok = 1; return 0; } +#define clinic_state() (find_io_state_by_def(Py_TYPE(self))) #include "clinic/bufferedio.c.h" +#undef clinic_state static PyMethodDef bufferediobase_methods[] = { @@ -2394,6 +2414,8 @@ static PyMethodDef bufferedreader_methods[] = { static PyMemberDef bufferedreader_members[] = { {"raw", T_OBJECT, offsetof(buffered, raw), READONLY}, {"_finalizing", T_BOOL, offsetof(buffered, finalizing), 0}, + {"__weaklistoffset__", T_PYSSIZET, offsetof(buffered, weakreflist), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(buffered, dict), READONLY}, {NULL} }; @@ -2405,58 +2427,27 @@ static PyGetSetDef bufferedreader_getset[] = { }; -PyTypeObject PyBufferedReader_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io.BufferedReader", /*tp_name*/ - sizeof(buffered), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)buffered_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - (reprfunc)buffered_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - _io_BufferedReader___init____doc__, /* tp_doc */ - (traverseproc)buffered_traverse, /* tp_traverse */ - (inquiry)buffered_clear, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(buffered, weakreflist), /*tp_weaklistoffset*/ - 0, /* tp_iter */ - (iternextfunc)buffered_iternext, /* tp_iternext */ - bufferedreader_methods, /* tp_methods */ - bufferedreader_members, /* tp_members */ - bufferedreader_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - offsetof(buffered, dict), /* tp_dictoffset */ - _io_BufferedReader___init__, /* tp_init */ - 0, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - 0, /* tp_finalize */ +static PyType_Slot bufferedreader_slots[] = { + {Py_tp_dealloc, buffered_dealloc}, + {Py_tp_repr, buffered_repr}, + {Py_tp_doc, (void *)_io_BufferedReader___init____doc__}, + {Py_tp_traverse, buffered_traverse}, + {Py_tp_clear, buffered_clear}, + {Py_tp_iternext, buffered_iternext}, + {Py_tp_methods, bufferedreader_methods}, + {Py_tp_members, bufferedreader_members}, + {Py_tp_getset, bufferedreader_getset}, + {Py_tp_init, _io_BufferedReader___init__}, + {0, NULL}, }; +PyType_Spec bufferedreader_spec = { + .name = "_io.BufferedReader", + .basicsize = sizeof(buffered), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = bufferedreader_slots, +}; static PyMethodDef bufferedwriter_methods[] = { /* BufferedIOMixin methods */ @@ -2480,6 +2471,8 @@ static PyMethodDef bufferedwriter_methods[] = { static PyMemberDef bufferedwriter_members[] = { {"raw", T_OBJECT, offsetof(buffered, raw), READONLY}, {"_finalizing", T_BOOL, offsetof(buffered, finalizing), 0}, + {"__weaklistoffset__", T_PYSSIZET, offsetof(buffered, weakreflist), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(buffered, dict), READONLY}, {NULL} }; @@ -2491,58 +2484,26 @@ static PyGetSetDef bufferedwriter_getset[] = { }; -PyTypeObject PyBufferedWriter_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io.BufferedWriter", /*tp_name*/ - sizeof(buffered), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)buffered_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - (reprfunc)buffered_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - _io_BufferedWriter___init____doc__, /* tp_doc */ - (traverseproc)buffered_traverse, /* tp_traverse */ - (inquiry)buffered_clear, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(buffered, weakreflist), /*tp_weaklistoffset*/ - 0, /* tp_iter */ - 0, /* tp_iternext */ - bufferedwriter_methods, /* tp_methods */ - bufferedwriter_members, /* tp_members */ - bufferedwriter_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - offsetof(buffered, dict), /* tp_dictoffset */ - _io_BufferedWriter___init__, /* tp_init */ - 0, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - 0, /* tp_finalize */ +static PyType_Slot bufferedwriter_slots[] = { + {Py_tp_dealloc, buffered_dealloc}, + {Py_tp_repr, buffered_repr}, + {Py_tp_doc, (void *)_io_BufferedWriter___init____doc__}, + {Py_tp_traverse, buffered_traverse}, + {Py_tp_clear, buffered_clear}, + {Py_tp_methods, bufferedwriter_methods}, + {Py_tp_members, bufferedwriter_members}, + {Py_tp_getset, bufferedwriter_getset}, + {Py_tp_init, _io_BufferedWriter___init__}, + {0, NULL}, }; +PyType_Spec bufferedwriter_spec = { + .name = "_io.BufferedWriter", + .basicsize = sizeof(buffered), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = bufferedwriter_slots, +}; static PyMethodDef bufferedrwpair_methods[] = { {"read", (PyCFunction)bufferedrwpair_read, METH_VARARGS}, @@ -2563,61 +2524,35 @@ static PyMethodDef bufferedrwpair_methods[] = { {NULL, NULL} }; +static PyMemberDef bufferedrwpair_members[] = { + {"__weaklistoffset__", T_PYSSIZET, offsetof(rwpair, weakreflist), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(rwpair, dict), READONLY}, + {NULL} +}; + static PyGetSetDef bufferedrwpair_getset[] = { {"closed", (getter)bufferedrwpair_closed_get, NULL, NULL}, {NULL} }; -PyTypeObject PyBufferedRWPair_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io.BufferedRWPair", /*tp_name*/ - sizeof(rwpair), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)bufferedrwpair_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - _io_BufferedRWPair___init____doc__, /* tp_doc */ - (traverseproc)bufferedrwpair_traverse, /* tp_traverse */ - (inquiry)bufferedrwpair_clear, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(rwpair, weakreflist), /*tp_weaklistoffset*/ - 0, /* tp_iter */ - 0, /* tp_iternext */ - bufferedrwpair_methods, /* tp_methods */ - 0, /* tp_members */ - bufferedrwpair_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - offsetof(rwpair, dict), /* tp_dictoffset */ - _io_BufferedRWPair___init__, /* tp_init */ - 0, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - 0, /* tp_finalize */ +static PyType_Slot bufferedrwpair_slots[] = { + {Py_tp_dealloc, bufferedrwpair_dealloc}, + {Py_tp_doc, (void *)_io_BufferedRWPair___init____doc__}, + {Py_tp_traverse, bufferedrwpair_traverse}, + {Py_tp_clear, bufferedrwpair_clear}, + {Py_tp_methods, bufferedrwpair_methods}, + {Py_tp_members, bufferedrwpair_members}, + {Py_tp_getset, bufferedrwpair_getset}, + {Py_tp_init, _io_BufferedRWPair___init__}, + {0, NULL}, +}; + +PyType_Spec bufferedrwpair_spec = { + .name = "_io.BufferedRWPair", + .basicsize = sizeof(rwpair), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = bufferedrwpair_slots, }; @@ -2651,6 +2586,8 @@ static PyMethodDef bufferedrandom_methods[] = { static PyMemberDef bufferedrandom_members[] = { {"raw", T_OBJECT, offsetof(buffered, raw), READONLY}, {"_finalizing", T_BOOL, offsetof(buffered, finalizing), 0}, + {"__weaklistoffset__", T_PYSSIZET, offsetof(buffered, weakreflist), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(buffered, dict), READONLY}, {NULL} }; @@ -2662,54 +2599,24 @@ static PyGetSetDef bufferedrandom_getset[] = { }; -PyTypeObject PyBufferedRandom_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io.BufferedRandom", /*tp_name*/ - sizeof(buffered), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)buffered_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - (reprfunc)buffered_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - _io_BufferedRandom___init____doc__, /* tp_doc */ - (traverseproc)buffered_traverse, /* tp_traverse */ - (inquiry)buffered_clear, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(buffered, weakreflist), /*tp_weaklistoffset*/ - 0, /* tp_iter */ - (iternextfunc)buffered_iternext, /* tp_iternext */ - bufferedrandom_methods, /* tp_methods */ - bufferedrandom_members, /* tp_members */ - bufferedrandom_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /*tp_dict*/ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - offsetof(buffered, dict), /*tp_dictoffset*/ - _io_BufferedRandom___init__, /* tp_init */ - 0, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - 0, /* tp_finalize */ +static PyType_Slot bufferedrandom_slots[] = { + {Py_tp_dealloc, buffered_dealloc}, + {Py_tp_repr, buffered_repr}, + {Py_tp_doc, (void *)_io_BufferedRandom___init____doc__}, + {Py_tp_traverse, buffered_traverse}, + {Py_tp_clear, buffered_clear}, + {Py_tp_iternext, buffered_iternext}, + {Py_tp_methods, bufferedrandom_methods}, + {Py_tp_members, bufferedrandom_members}, + {Py_tp_getset, bufferedrandom_getset}, + {Py_tp_init, _io_BufferedRandom___init__}, + {0, NULL}, +}; + +PyType_Spec bufferedrandom_spec = { + .name = "_io.BufferedRandom", + .basicsize = sizeof(buffered), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = bufferedrandom_slots, }; diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c index 6698c60355fcc5..7e9d28b3b9655c 100644 --- a/Modules/_io/bytesio.c +++ b/Modules/_io/bytesio.c @@ -5,9 +5,9 @@ /*[clinic input] module _io -class _io.BytesIO "bytesio *" "&PyBytesIO_Type" +class _io.BytesIO "bytesio *" "clinic_state()->PyBytesIO_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=7f50ec034f5c0b26]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=48ede2f330f847c3]*/ typedef struct { PyObject_HEAD @@ -881,6 +881,7 @@ bytesio_setstate(bytesio *self, PyObject *state) static void bytesio_dealloc(bytesio *self) { + PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); if (self->exports > 0) { PyErr_SetString(PyExc_SystemError, @@ -891,7 +892,8 @@ bytesio_dealloc(bytesio *self) Py_CLEAR(self->dict); if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) self); - Py_TYPE(self)->tp_free(self); + tp->tp_free(self); + Py_DECREF(tp); } static PyObject * @@ -971,6 +973,7 @@ bytesio_sizeof(bytesio *self, void *unused) static int bytesio_traverse(bytesio *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->dict); return 0; } @@ -983,7 +986,9 @@ bytesio_clear(bytesio *self) } +#define clinic_state() (find_io_state_by_def(Py_TYPE(self))) #include "clinic/bytesio.c.h" +#undef clinic_state static PyGetSetDef bytesio_getsetlist[] = { {"closed", (getter)bytesio_get_closed, NULL, @@ -1016,48 +1021,34 @@ static struct PyMethodDef bytesio_methods[] = { {NULL, NULL} /* sentinel */ }; -PyTypeObject PyBytesIO_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io.BytesIO", /*tp_name*/ - sizeof(bytesio), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)bytesio_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | - Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - _io_BytesIO___init____doc__, /*tp_doc*/ - (traverseproc)bytesio_traverse, /*tp_traverse*/ - (inquiry)bytesio_clear, /*tp_clear*/ - 0, /*tp_richcompare*/ - offsetof(bytesio, weakreflist), /*tp_weaklistoffset*/ - PyObject_SelfIter, /*tp_iter*/ - (iternextfunc)bytesio_iternext, /*tp_iternext*/ - bytesio_methods, /*tp_methods*/ - 0, /*tp_members*/ - bytesio_getsetlist, /*tp_getset*/ - 0, /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - offsetof(bytesio, dict), /*tp_dictoffset*/ - _io_BytesIO___init__, /*tp_init*/ - 0, /*tp_alloc*/ - bytesio_new, /*tp_new*/ +static PyMemberDef bytesio_members[] = { + {"__weaklistoffset__", T_PYSSIZET, offsetof(bytesio, weakreflist), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(bytesio, dict), READONLY}, + {NULL} }; +static PyType_Slot bytesio_slots[] = { + {Py_tp_dealloc, bytesio_dealloc}, + {Py_tp_doc, (void *)_io_BytesIO___init____doc__}, + {Py_tp_traverse, bytesio_traverse}, + {Py_tp_clear, bytesio_clear}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, bytesio_iternext}, + {Py_tp_methods, bytesio_methods}, + {Py_tp_members, bytesio_members}, + {Py_tp_getset, bytesio_getsetlist}, + {Py_tp_init, _io_BytesIO___init__}, + {Py_tp_new, bytesio_new}, + {0, NULL}, +}; + +PyType_Spec bytesio_spec = { + .name = "_io.BytesIO", + .basicsize = sizeof(bytesio), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = bytesio_slots, +}; /* * Implementation of the small intermediate object used by getbuffer(). diff --git a/Modules/_io/clinic/bufferedio.c.h b/Modules/_io/clinic/bufferedio.c.h index 38ea756879c122..d44321bb8b960e 100644 --- a/Modules/_io/clinic/bufferedio.c.h +++ b/Modules/_io/clinic/bufferedio.c.h @@ -601,7 +601,7 @@ static int _io_BufferedRWPair___init__(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; - PyTypeObject *base_tp = &PyBufferedRWPair_Type; + PyTypeObject *base_tp = clinic_state()->PyBufferedRWPair_Type; PyObject *reader; PyObject *writer; Py_ssize_t buffer_size = DEFAULT_BUFFER_SIZE; @@ -714,4 +714,4 @@ _io_BufferedRandom___init__(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=953f1577e96e8d86 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8412b10c04259bb8 input=a9049054013a1b77]*/ diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index d1a183cedac53a..f424fb8439d7a8 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -51,9 +51,9 @@ /*[clinic input] module _io -class _io.FileIO "fileio *" "&PyFileIO_Type" +class _io.FileIO "fileio *" "clinic_state()->PyFileIO_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=1c77708b41fda70c]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ac25ec278f4d6703]*/ typedef struct { PyObject_HEAD @@ -70,9 +70,7 @@ typedef struct { PyObject *dict; } fileio; -PyTypeObject PyFileIO_Type; - -#define PyFileIO_Check(op) (PyObject_TypeCheck((op), &PyFileIO_Type)) +#define PyFileIO_Check(state, op) (PyObject_TypeCheck((op), state->PyFileIO_Type)) /* Forward declarations */ static PyObject* portable_lseek(fileio *self, PyObject *posobj, int whence, bool suppress_pipe_error); @@ -242,7 +240,10 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode, int fstat_result; int async_err = 0; - assert(PyFileIO_Check(self)); +#ifdef Py_DEBUG + _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); + assert(PyFileIO_Check(state, self)); +#endif if (self->fd >= 0) { if (self->closefd) { /* Have to close the existing file first. */ @@ -503,6 +504,7 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode, static int fileio_traverse(fileio *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->dict); return 0; } @@ -517,6 +519,7 @@ fileio_clear(fileio *self) static void fileio_dealloc(fileio *self) { + PyTypeObject *tp = Py_TYPE(self); self->finalizing = 1; if (_PyIOBase_finalize((PyObject *) self) < 0) return; @@ -524,7 +527,8 @@ fileio_dealloc(fileio *self) if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) self); Py_CLEAR(self->dict); - Py_TYPE(self)->tp_free((PyObject *)self); + tp->tp_free((PyObject *)self); + Py_DECREF(tp); } static PyObject * @@ -1177,57 +1181,29 @@ static PyGetSetDef fileio_getsetlist[] = { static PyMemberDef fileio_members[] = { {"_blksize", T_UINT, offsetof(fileio, blksize), 0}, {"_finalizing", T_BOOL, offsetof(fileio, finalizing), 0}, + {"__weaklistoffset__", T_PYSSIZET, offsetof(fileio, weakreflist), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(fileio, dict), READONLY}, {NULL} }; -PyTypeObject PyFileIO_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io.FileIO", - sizeof(fileio), - 0, - (destructor)fileio_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)fileio_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - _io_FileIO___init____doc__, /* tp_doc */ - (traverseproc)fileio_traverse, /* tp_traverse */ - (inquiry)fileio_clear, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(fileio, weakreflist), /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - fileio_methods, /* tp_methods */ - fileio_members, /* tp_members */ - fileio_getsetlist, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - offsetof(fileio, dict), /* tp_dictoffset */ - _io_FileIO___init__, /* tp_init */ - PyType_GenericAlloc, /* tp_alloc */ - fileio_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - 0, /* tp_finalize */ +static PyType_Slot fileio_slots[] = { + {Py_tp_dealloc, fileio_dealloc}, + {Py_tp_repr, fileio_repr}, + {Py_tp_doc, (void *)_io_FileIO___init____doc__}, + {Py_tp_traverse, fileio_traverse}, + {Py_tp_clear, fileio_clear}, + {Py_tp_methods, fileio_methods}, + {Py_tp_members, fileio_members}, + {Py_tp_getset, fileio_getsetlist}, + {Py_tp_init, _io_FileIO___init__}, + {Py_tp_new, fileio_new}, + {0, NULL}, +}; + +PyType_Spec fileio_spec = { + .name = "_io.FileIO", + .basicsize = sizeof(fileio), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = fileio_slots, }; diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index ae6c3125a2d9da..54c050f0be4688 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -13,9 +13,9 @@ /*[clinic input] module _io -class _io.StringIO "stringio *" "&PyStringIO_Type" +class _io.StringIO "stringio *" "clinic_state()->PyStringIO_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=c17bc0f42165cd7d]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=2693eada0658d470]*/ typedef struct { PyObject_HEAD @@ -43,6 +43,7 @@ typedef struct { PyObject *dict; PyObject *weakreflist; + _PyIO_State *module_state; } stringio; static int _io_StringIO___init__(PyObject *self, PyObject *args, PyObject *kwargs); @@ -401,7 +402,7 @@ stringio_iternext(stringio *self) CHECK_CLOSED(self); ENSURE_REALIZED(self); - if (Py_IS_TYPE(self, &PyStringIO_Type)) { + if (Py_IS_TYPE(self, self->module_state->PyStringIO_Type)) { /* Skip method call overhead for speed */ line = _stringio_readline(self, -1); } @@ -581,6 +582,7 @@ _io_StringIO_close_impl(stringio *self) static int stringio_traverse(stringio *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->dict); return 0; } @@ -595,6 +597,7 @@ stringio_clear(stringio *self) static void stringio_dealloc(stringio *self) { + PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); self->ok = 0; if (self->buf) { @@ -606,9 +609,11 @@ stringio_dealloc(stringio *self) Py_CLEAR(self->writenl); Py_CLEAR(self->decoder); Py_CLEAR(self->dict); - if (self->weakreflist != NULL) + if (self->weakreflist != NULL) { PyObject_ClearWeakRefs((PyObject *) self); - Py_TYPE(self)->tp_free(self); + } + tp->tp_free(self); + Py_DECREF(tp); } static PyObject * @@ -745,7 +750,7 @@ _io_StringIO___init___impl(stringio *self, PyObject *value, self->state = STATE_ACCUMULATING; } self->pos = 0; - + self->module_state = find_io_state_by_def(Py_TYPE(self)); self->closed = 0; self->ok = 1; return 0; @@ -963,7 +968,9 @@ stringio_newlines(stringio *self, void *context) return PyObject_GetAttr(self->decoder, &_Py_ID(newlines)); } +#define clinic_state() (find_io_state_by_def(Py_TYPE(self))) #include "clinic/stringio.c.h" +#undef clinic_state static struct PyMethodDef stringio_methods[] = { _IO_STRINGIO_CLOSE_METHODDEF @@ -997,44 +1004,30 @@ static PyGetSetDef stringio_getset[] = { {NULL} }; -PyTypeObject PyStringIO_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io.StringIO", /*tp_name*/ - sizeof(stringio), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)stringio_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - _io_StringIO___init____doc__, /*tp_doc*/ - (traverseproc)stringio_traverse, /*tp_traverse*/ - (inquiry)stringio_clear, /*tp_clear*/ - 0, /*tp_richcompare*/ - offsetof(stringio, weakreflist), /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - (iternextfunc)stringio_iternext, /*tp_iternext*/ - stringio_methods, /*tp_methods*/ - 0, /*tp_members*/ - stringio_getset, /*tp_getset*/ - 0, /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - offsetof(stringio, dict), /*tp_dictoffset*/ - _io_StringIO___init__, /*tp_init*/ - 0, /*tp_alloc*/ - stringio_new, /*tp_new*/ +static struct PyMemberDef stringio_members[] = { + {"__weaklistoffset__", T_PYSSIZET, offsetof(stringio, weakreflist), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(stringio, dict), READONLY}, + {NULL}, +}; + +static PyType_Slot stringio_slots[] = { + {Py_tp_dealloc, stringio_dealloc}, + {Py_tp_doc, (void *)_io_StringIO___init____doc__}, + {Py_tp_traverse, stringio_traverse}, + {Py_tp_clear, stringio_clear}, + {Py_tp_iternext, stringio_iternext}, + {Py_tp_methods, stringio_methods}, + {Py_tp_members, stringio_members}, + {Py_tp_getset, stringio_getset}, + {Py_tp_init, _io_StringIO___init__}, + {Py_tp_new, stringio_new}, + {0, NULL}, +}; + +PyType_Spec stringio_spec = { + .name = "_io.StringIO", + .basicsize = sizeof(stringio), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = stringio_slots, }; diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index ea2ea32c336954..fbf0bf46840374 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -19,9 +19,9 @@ /*[clinic input] module _io class _io.IncrementalNewlineDecoder "nldecoder_object *" "&PyIncrementalNewlineDecoder_Type" -class _io.TextIOWrapper "textio *" "&TextIOWrapper_Type" +class _io.TextIOWrapper "textio *" "clinic_state()->TextIOWrapper_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ed072384f8aada2c]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=d3f032e90f74c8f2]*/ /* TextIOBase */ @@ -682,6 +682,8 @@ typedef struct PyObject *weakreflist; PyObject *dict; + + _PyIO_State *state; } textio; static void @@ -1175,15 +1177,16 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer, /* Finished sorting out the codec details */ Py_CLEAR(codec_info); - if (Py_IS_TYPE(buffer, &PyBufferedReader_Type) || - Py_IS_TYPE(buffer, &PyBufferedWriter_Type) || - Py_IS_TYPE(buffer, &PyBufferedRandom_Type)) + _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); + if (Py_IS_TYPE(buffer, state->PyBufferedReader_Type) || + Py_IS_TYPE(buffer, state->PyBufferedWriter_Type) || + Py_IS_TYPE(buffer, state->PyBufferedRandom_Type)) { if (_PyObject_LookupAttr(buffer, &_Py_ID(raw), &raw) < 0) goto error; /* Cache the raw FileIO object to speed up 'closed' checks */ if (raw != NULL) { - if (Py_IS_TYPE(raw, &PyFileIO_Type)) + if (Py_IS_TYPE(raw, state->PyFileIO_Type)) self->raw = raw; else Py_DECREF(raw); @@ -1211,6 +1214,7 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer, goto error; } + self->state = state; self->ok = 1; return 0; @@ -1387,6 +1391,7 @@ textiowrapper_clear(textio *self) static void textiowrapper_dealloc(textio *self) { + PyTypeObject *tp = Py_TYPE(self); self->finalizing = 1; if (_PyIOBase_finalize((PyObject *) self) < 0) return; @@ -1395,12 +1400,14 @@ textiowrapper_dealloc(textio *self) if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *)self); textiowrapper_clear(self); - Py_TYPE(self)->tp_free((PyObject *)self); + tp->tp_free((PyObject *)self); + Py_DECREF(tp); } static int textiowrapper_traverse(textio *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->buffer); Py_VISIT(self->encoding); Py_VISIT(self->encoder); @@ -1424,7 +1431,7 @@ textiowrapper_closed_get(textio *self, void *context); do { \ int r; \ PyObject *_res; \ - if (Py_IS_TYPE(self, &PyTextIOWrapper_Type)) { \ + if (Py_IS_TYPE(self, self->state->PyTextIOWrapper_Type)) { \ if (self->raw != NULL) \ r = _PyFileIO_closed(self->raw); \ else { \ @@ -3053,7 +3060,7 @@ textiowrapper_iternext(textio *self) CHECK_ATTACHED(self); self->telling = 0; - if (Py_IS_TYPE(self, &PyTextIOWrapper_Type)) { + if (Py_IS_TYPE(self, self->state->PyTextIOWrapper_Type)) { /* Skip method call overhead for speed */ line = _textiowrapper_readline(self, -1); } @@ -3145,7 +3152,9 @@ textiowrapper_chunk_size_set(textio *self, PyObject *arg, void *context) return 0; } +#define clinic_state() (find_io_state_by_def(Py_TYPE(self))) #include "clinic/textio.c.h" +#undef clinic_state static PyMethodDef incrementalnewlinedecoder_methods[] = { _IO_INCREMENTALNEWLINEDECODER_DECODE_METHODDEF @@ -3229,6 +3238,8 @@ static PyMemberDef textiowrapper_members[] = { {"line_buffering", T_BOOL, offsetof(textio, line_buffering), READONLY}, {"write_through", T_BOOL, offsetof(textio, write_through), READONLY}, {"_finalizing", T_BOOL, offsetof(textio, finalizing), 0}, + {"__weaklistoffset__", T_PYSSIZET, offsetof(textio, weakreflist), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(textio, dict), READONLY}, {NULL} }; @@ -3244,54 +3255,24 @@ static PyGetSetDef textiowrapper_getset[] = { {NULL} }; -PyTypeObject PyTextIOWrapper_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_io.TextIOWrapper", /*tp_name*/ - sizeof(textio), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)textiowrapper_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tps_etattr*/ - 0, /*tp_as_async*/ - (reprfunc)textiowrapper_repr,/*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - _io_TextIOWrapper___init____doc__, /* tp_doc */ - (traverseproc)textiowrapper_traverse, /* tp_traverse */ - (inquiry)textiowrapper_clear, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(textio, weakreflist), /*tp_weaklistoffset*/ - 0, /* tp_iter */ - (iternextfunc)textiowrapper_iternext, /* tp_iternext */ - textiowrapper_methods, /* tp_methods */ - textiowrapper_members, /* tp_members */ - textiowrapper_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - offsetof(textio, dict), /*tp_dictoffset*/ - _io_TextIOWrapper___init__, /* tp_init */ - 0, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ - 0, /* tp_finalize */ +PyType_Slot textiowrapper_slots[] = { + {Py_tp_dealloc, textiowrapper_dealloc}, + {Py_tp_repr, textiowrapper_repr}, + {Py_tp_doc, (void *)_io_TextIOWrapper___init____doc__}, + {Py_tp_traverse, textiowrapper_traverse}, + {Py_tp_clear, textiowrapper_clear}, + {Py_tp_iternext, textiowrapper_iternext}, + {Py_tp_methods, textiowrapper_methods}, + {Py_tp_members, textiowrapper_members}, + {Py_tp_getset, textiowrapper_getset}, + {Py_tp_init, _io_TextIOWrapper___init__}, + {0, NULL}, +}; + +PyType_Spec textiowrapper_spec = { + .name = "_io.TextIOWrapper", + .basicsize = sizeof(textio), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = textiowrapper_slots, }; diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 6011b1604508af..2e28c50c6ff69a 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -317,19 +317,11 @@ Modules/_collectionsmodule.c - dequeiter_type - Modules/_collectionsmodule.c - dequereviter_type - Modules/_collectionsmodule.c - tuplegetter_type - Modules/_io/bufferedio.c - PyBufferedIOBase_Type - -Modules/_io/bufferedio.c - PyBufferedRWPair_Type - -Modules/_io/bufferedio.c - PyBufferedRandom_Type - -Modules/_io/bufferedio.c - PyBufferedReader_Type - -Modules/_io/bufferedio.c - PyBufferedWriter_Type - -Modules/_io/bytesio.c - PyBytesIO_Type - Modules/_io/bytesio.c - _PyBytesIOBuffer_Type - -Modules/_io/fileio.c - PyFileIO_Type - Modules/_io/iobase.c - PyIOBase_Type - Modules/_io/iobase.c - PyRawIOBase_Type - -Modules/_io/stringio.c - PyStringIO_Type - Modules/_io/textio.c - PyIncrementalNewlineDecoder_Type - Modules/_io/textio.c - PyTextIOBase_Type - -Modules/_io/textio.c - PyTextIOWrapper_Type - Modules/_io/winconsoleio.c - PyWindowsConsoleIO_Type - Modules/_testcapi/vectorcall.c - MethodDescriptorBase_Type - Modules/_testcapi/vectorcall.c - MethodDescriptorDerived_Type - From a99eb5cd9947629a6745a4ad99cb07af1c287b5d Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 20 Feb 2023 14:56:48 +0000 Subject: [PATCH 219/225] gh-101907: Stop using `_Py_OPCODE` and `_Py_OPARG` macros (GH-101912) * gh-101907: Removes use of non-standard C++ extension from Include/cpython/code.h * Make cases_generator correct on Windows --- Include/cpython/code.h | 28 ++- ...-02-14-15-53-01.gh-issue-101907.HgF1N2.rst | 1 + Objects/codeobject.c | 20 +- Objects/frameobject.c | 24 +-- Objects/genobject.c | 10 +- Objects/typeobject.c | 4 +- Python/bytecodes.c | 34 ++-- Python/ceval.c | 12 +- Python/ceval_macros.h | 18 +- Python/compile.c | 20 +- Python/generated_cases.c.h | 34 ++-- Python/specialize.c | 172 +++++++++--------- Tools/cases_generator/generate_cases.py | 7 +- 13 files changed, 200 insertions(+), 184 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2023-02-14-15-53-01.gh-issue-101907.HgF1N2.rst diff --git a/Include/cpython/code.h b/Include/cpython/code.h index 0cf49f06c87732..fba9296aedc99d 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -19,21 +19,35 @@ extern "C" { typedef union { uint16_t cache; struct { - uint8_t opcode; - uint8_t oparg; - }; + uint8_t code; + uint8_t arg; + } op; } _Py_CODEUNIT; -#define _Py_OPCODE(word) ((word).opcode) -#define _Py_OPARG(word) ((word).oparg) + +/* These macros only remain defined for compatibility. */ +#define _Py_OPCODE(word) ((word).op.code) +#define _Py_OPARG(word) ((word).op.arg) + +static inline _Py_CODEUNIT +_py_make_codeunit(uint8_t opcode, uint8_t oparg) +{ + // No designated initialisers because of C++ compat + _Py_CODEUNIT word; + word.op.code = opcode; + word.op.arg = oparg; + return word; +} static inline void _py_set_opcode(_Py_CODEUNIT *word, uint8_t opcode) { - word->opcode = opcode; + word->op.code = opcode; } -#define _Py_SET_OPCODE(word, opcode) _py_set_opocde(&(word), opcode) +#define _Py_MAKE_CODEUNIT(opcode, oparg) _py_make_codeunit((opcode), (oparg)) +#define _Py_SET_OPCODE(word, opcode) _py_set_opcode(&(word), (opcode)) + typedef struct { PyObject *_co_code; diff --git a/Misc/NEWS.d/next/C API/2023-02-14-15-53-01.gh-issue-101907.HgF1N2.rst b/Misc/NEWS.d/next/C API/2023-02-14-15-53-01.gh-issue-101907.HgF1N2.rst new file mode 100644 index 00000000000000..cfc0d72cdbca00 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-02-14-15-53-01.gh-issue-101907.HgF1N2.rst @@ -0,0 +1 @@ +Removes use of non-standard C++ extension in public header files. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index ab31b6582cdaae..a03b14edea8d4c 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -413,7 +413,7 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) PyBytes_GET_SIZE(con->code)); int entry_point = 0; while (entry_point < Py_SIZE(co) && - _Py_OPCODE(_PyCode_CODE(co)[entry_point]) != RESUME) { + _PyCode_CODE(co)[entry_point].op.code != RESUME) { entry_point++; } co->_co_firsttraceable = entry_point; @@ -1505,12 +1505,12 @@ deopt_code(_Py_CODEUNIT *instructions, Py_ssize_t len) { for (int i = 0; i < len; i++) { _Py_CODEUNIT instruction = instructions[i]; - int opcode = _PyOpcode_Deopt[_Py_OPCODE(instruction)]; + int opcode = _PyOpcode_Deopt[instruction.op.code]; int caches = _PyOpcode_Caches[opcode]; - instructions[i].opcode = opcode; + instructions[i].op.code = opcode; while (caches--) { - instructions[++i].opcode = CACHE; - instructions[i].oparg = 0; + instructions[++i].op.code = CACHE; + instructions[i].op.arg = 0; } } } @@ -1763,13 +1763,13 @@ code_richcompare(PyObject *self, PyObject *other, int op) for (int i = 0; i < Py_SIZE(co); i++) { _Py_CODEUNIT co_instr = _PyCode_CODE(co)[i]; _Py_CODEUNIT cp_instr = _PyCode_CODE(cp)[i]; - co_instr.opcode = _PyOpcode_Deopt[_Py_OPCODE(co_instr)]; - cp_instr.opcode =_PyOpcode_Deopt[_Py_OPCODE(cp_instr)]; + co_instr.op.code = _PyOpcode_Deopt[co_instr.op.code]; + cp_instr.op.code = _PyOpcode_Deopt[cp_instr.op.code]; eq = co_instr.cache == cp_instr.cache; if (!eq) { goto unequal; } - i += _PyOpcode_Caches[_Py_OPCODE(co_instr)]; + i += _PyOpcode_Caches[co_instr.op.code]; } /* compare constants */ @@ -1848,9 +1848,9 @@ code_hash(PyCodeObject *co) SCRAMBLE_IN(co->co_firstlineno); SCRAMBLE_IN(Py_SIZE(co)); for (int i = 0; i < Py_SIZE(co); i++) { - int deop = _PyOpcode_Deopt[_Py_OPCODE(_PyCode_CODE(co)[i])]; + int deop = _PyOpcode_Deopt[_PyCode_CODE(co)[i].op.code]; SCRAMBLE_IN(deop); - SCRAMBLE_IN(_Py_OPARG(_PyCode_CODE(co)[i])); + SCRAMBLE_IN(_PyCode_CODE(co)[i].op.arg); i += _PyOpcode_Caches[deop]; } if ((Py_hash_t)uhash == -1) { diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 581ed2d214c4d9..34143c9a40b293 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -111,13 +111,13 @@ static unsigned int get_arg(const _Py_CODEUNIT *codestr, Py_ssize_t i) { _Py_CODEUNIT word; - unsigned int oparg = _Py_OPARG(codestr[i]); - if (i >= 1 && _Py_OPCODE(word = codestr[i-1]) == EXTENDED_ARG) { - oparg |= _Py_OPARG(word) << 8; - if (i >= 2 && _Py_OPCODE(word = codestr[i-2]) == EXTENDED_ARG) { - oparg |= _Py_OPARG(word) << 16; - if (i >= 3 && _Py_OPCODE(word = codestr[i-3]) == EXTENDED_ARG) { - oparg |= _Py_OPARG(word) << 24; + unsigned int oparg = codestr[i].op.arg; + if (i >= 1 && (word = codestr[i-1]).op.code == EXTENDED_ARG) { + oparg |= word.op.arg << 8; + if (i >= 2 && (word = codestr[i-2]).op.code == EXTENDED_ARG) { + oparg |= word.op.arg << 16; + if (i >= 3 && (word = codestr[i-3]).op.code == EXTENDED_ARG) { + oparg |= word.op.arg << 24; } } } @@ -304,7 +304,7 @@ mark_stacks(PyCodeObject *code_obj, int len) if (next_stack == UNINITIALIZED) { continue; } - opcode = _Py_OPCODE(code[i]); + opcode = code[i].op.code; switch (opcode) { case JUMP_IF_FALSE_OR_POP: case JUMP_IF_TRUE_OR_POP: @@ -610,7 +610,7 @@ _PyFrame_GetState(PyFrameObject *frame) if (_PyInterpreterFrame_LASTI(frame->f_frame) < 0) { return FRAME_CREATED; } - switch (_Py_OPCODE(*frame->f_frame->prev_instr)) + switch (frame->f_frame->prev_instr->op.code) { case COPY_FREE_VARS: case MAKE_CELL: @@ -1092,8 +1092,8 @@ _PyFrame_OpAlreadyRan(_PyInterpreterFrame *frame, int opcode, int oparg) for (_Py_CODEUNIT *instruction = _PyCode_CODE(frame->f_code); instruction < frame->prev_instr; instruction++) { - int check_opcode = _PyOpcode_Deopt[_Py_OPCODE(*instruction)]; - check_oparg |= _Py_OPARG(*instruction); + int check_opcode = _PyOpcode_Deopt[instruction->op.code]; + check_oparg |= instruction->op.arg; if (check_opcode == opcode && check_oparg == oparg) { return 1; } @@ -1117,7 +1117,7 @@ frame_init_get_vars(_PyInterpreterFrame *frame) // here: PyCodeObject *co = frame->f_code; int lasti = _PyInterpreterFrame_LASTI(frame); - if (!(lasti < 0 && _Py_OPCODE(_PyCode_CODE(co)[0]) == COPY_FREE_VARS + if (!(lasti < 0 && _PyCode_CODE(co)->op.code == COPY_FREE_VARS && PyFunction_Check(frame->f_funcobj))) { /* Free vars are initialized */ diff --git a/Objects/genobject.c b/Objects/genobject.c index 35246653c45348..aec64ca7004e11 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -332,11 +332,11 @@ _PyGen_yf(PyGenObject *gen) /* Return immediately if the frame didn't start yet. SEND always come after LOAD_CONST: a code object should not start with SEND */ - assert(_Py_OPCODE(_PyCode_CODE(gen->gi_code)[0]) != SEND); + assert(_PyCode_CODE(gen->gi_code)[0].op.code != SEND); return NULL; } _Py_CODEUNIT next = frame->prev_instr[1]; - if (_Py_OPCODE(next) != RESUME || _Py_OPARG(next) < 2) + if (next.op.code != RESUME || next.op.arg < 2) { /* Not in a yield from */ return NULL; @@ -371,9 +371,9 @@ gen_close(PyGenObject *gen, PyObject *args) _PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe; /* It is possible for the previous instruction to not be a * YIELD_VALUE if the debugger has changed the lineno. */ - if (err == 0 && frame->prev_instr->opcode == YIELD_VALUE) { - assert(frame->prev_instr[1].opcode == RESUME); - int exception_handler_depth = frame->prev_instr->oparg; + if (err == 0 && frame->prev_instr[0].op.code == YIELD_VALUE) { + assert(frame->prev_instr[1].op.code == RESUME); + int exception_handler_depth = frame->prev_instr[0].op.code; assert(exception_handler_depth > 0); /* We can safely ignore the outermost try block * as it automatically generated to handle diff --git a/Objects/typeobject.c b/Objects/typeobject.c index f2e8092aa37eec..2d1220a0695036 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -9507,8 +9507,8 @@ super_init_without_args(_PyInterpreterFrame *cframe, PyCodeObject *co, if (_PyInterpreterFrame_LASTI(cframe) >= 0) { // MAKE_CELL and COPY_FREE_VARS have no quickened forms, so no need // to use _PyOpcode_Deopt here: - assert(_Py_OPCODE(_PyCode_CODE(co)[0]) == MAKE_CELL || - _Py_OPCODE(_PyCode_CODE(co)[0]) == COPY_FREE_VARS); + assert(_PyCode_CODE(co)[0].op.code == MAKE_CELL || + _PyCode_CODE(co)[0].op.code == COPY_FREE_VARS); assert(PyCell_Check(firstarg)); firstarg = PyCell_GET(firstarg); } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 84747f1758e06c..c5959f2f994fdc 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -246,9 +246,9 @@ dummy_func( DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP); _Py_CODEUNIT true_next = next_instr[INLINE_CACHE_ENTRIES_BINARY_OP]; - assert(_Py_OPCODE(true_next) == STORE_FAST || - _Py_OPCODE(true_next) == STORE_FAST__LOAD_FAST); - PyObject **target_local = &GETLOCAL(_Py_OPARG(true_next)); + assert(true_next.op.code == STORE_FAST || + true_next.op.code == STORE_FAST__LOAD_FAST); + PyObject **target_local = &GETLOCAL(true_next.op.arg); DEOPT_IF(*target_local != left, BINARY_OP); STAT_INC(BINARY_OP, hit); /* Handle `left = left + right` or `left += right` for str. @@ -1748,10 +1748,10 @@ dummy_func( Py_DECREF(left); Py_DECREF(right); ERROR_IF(cond == NULL, error); - assert(_Py_OPCODE(next_instr[1]) == POP_JUMP_IF_FALSE || - _Py_OPCODE(next_instr[1]) == POP_JUMP_IF_TRUE); - bool jump_on_true = _Py_OPCODE(next_instr[1]) == POP_JUMP_IF_TRUE; - int offset = _Py_OPARG(next_instr[1]); + assert(next_instr[1].op.code == POP_JUMP_IF_FALSE || + next_instr[1].op.code == POP_JUMP_IF_TRUE); + bool jump_on_true = next_instr[1].op.code == POP_JUMP_IF_TRUE; + int offset = next_instr[1].op.arg; int err = PyObject_IsTrue(cond); Py_DECREF(cond); if (err < 0) { @@ -1774,7 +1774,7 @@ dummy_func( _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); if (sign_ish & oparg) { - int offset = _Py_OPARG(next_instr[1]); + int offset = next_instr[1].op.arg; JUMPBY(offset); } } @@ -1795,7 +1795,7 @@ dummy_func( _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); if (sign_ish & oparg) { - int offset = _Py_OPARG(next_instr[1]); + int offset = next_instr[1].op.arg; JUMPBY(offset); } } @@ -1814,7 +1814,7 @@ dummy_func( assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); if ((res + COMPARISON_NOT_EQUALS) & oparg) { - int offset = _Py_OPARG(next_instr[1]); + int offset = next_instr[1].op.arg; JUMPBY(offset); } } @@ -2122,7 +2122,7 @@ dummy_func( _PyErr_Clear(tstate); } /* iterator ended normally */ - assert(_Py_OPCODE(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg]) == END_FOR); + assert(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg].op.code == END_FOR); Py_DECREF(iter); STACK_SHRINK(1); /* Jump forward oparg, then skip following END_FOR instruction */ @@ -2186,7 +2186,7 @@ dummy_func( DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER]; - assert(_PyOpcode_Deopt[_Py_OPCODE(next)] == STORE_FAST); + assert(_PyOpcode_Deopt[next.op.code] == STORE_FAST); if (r->len <= 0) { STACK_SHRINK(1); Py_DECREF(r); @@ -2197,7 +2197,7 @@ dummy_func( long value = r->start; r->start = value + r->step; r->len--; - if (_PyLong_AssignValue(&GETLOCAL(_Py_OPARG(next)), value) < 0) { + if (_PyLong_AssignValue(&GETLOCAL(next.op.arg), value) < 0) { goto error; } // The STORE_FAST is already done. @@ -2220,7 +2220,7 @@ dummy_func( gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg); - assert(_Py_OPCODE(*next_instr) == END_FOR); + assert(next_instr->op.code == END_FOR); DISPATCH_INLINED(gen_frame); } @@ -2809,7 +2809,7 @@ dummy_func( STACK_SHRINK(3); // CALL + POP_TOP JUMPBY(INLINE_CACHE_ENTRIES_CALL + 1); - assert(_Py_OPCODE(next_instr[-1]) == POP_TOP); + assert(next_instr[-1].op.code == POP_TOP); DISPATCH(); } @@ -3118,8 +3118,8 @@ dummy_func( inst(EXTENDED_ARG, (--)) { assert(oparg); assert(cframe.use_tracing == 0); - opcode = _Py_OPCODE(*next_instr); - oparg = oparg << 8 | _Py_OPARG(*next_instr); + opcode = next_instr->op.code; + oparg = oparg << 8 | next_instr->op.arg; PRE_DISPATCH_GOTO(); DISPATCH_GOTO(); } diff --git a/Python/ceval.c b/Python/ceval.c index 308ef52259df3d..b85231a1df8a95 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -132,8 +132,8 @@ lltrace_instruction(_PyInterpreterFrame *frame, objects enters the interpreter recursively. It is also slow. So you might want to comment it out. */ dump_stack(frame, stack_pointer); - int oparg = _Py_OPARG(*next_instr); - int opcode = _Py_OPCODE(*next_instr); + int oparg = next_instr->op.arg; + int opcode = next_instr->op.code; const char *opname = _PyOpcode_OpName[opcode]; assert(opname != NULL); int offset = (int)(next_instr - _PyCode_CODE(frame->f_code)); @@ -920,8 +920,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int // CPython hasn't ever traced the instruction after an EXTENDED_ARG. // Inline the EXTENDED_ARG here, so we can avoid branching there: INSTRUCTION_START(EXTENDED_ARG); - opcode = _Py_OPCODE(*next_instr); - oparg = oparg << 8 | _Py_OPARG(*next_instr); + opcode = next_instr->op.code; + oparg = oparg << 8 | next_instr->op.arg; // Make sure the next instruction isn't a RESUME, since that needs // to trace properly (and shouldn't have an EXTENDED_ARG, anyways): assert(opcode != RESUME); @@ -946,7 +946,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif /* Tell C compilers not to hold the opcode variable in the loop. next_instr points the current instruction without TARGET(). */ - opcode = _Py_OPCODE(*next_instr); + opcode = next_instr->op.code; _PyErr_Format(tstate, PyExc_SystemError, "%U:%d: unknown opcode %d", frame->f_code->co_filename, @@ -2196,7 +2196,7 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj, (_PyInterpreterFrame_LASTI(frame) < instr_prev && // SEND has no quickened forms, so no need to use _PyOpcode_Deopt // here: - _Py_OPCODE(*frame->prev_instr) != SEND); + frame->prev_instr->op.code != SEND); if (trace) { result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None); } diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 691bf8e1caae95..ac1fec77daca8a 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -100,7 +100,7 @@ #define DISPATCH_SAME_OPARG() \ { \ - opcode = _Py_OPCODE(*next_instr); \ + opcode = next_instr->op.code; \ PRE_DISPATCH_GOTO(); \ opcode |= cframe.use_tracing OR_DTRACE_LINE; \ DISPATCH_GOTO(); \ @@ -143,8 +143,8 @@ GETITEM(PyObject *v, Py_ssize_t i) { #define INSTR_OFFSET() ((int)(next_instr - _PyCode_CODE(frame->f_code))) #define NEXTOPARG() do { \ _Py_CODEUNIT word = *next_instr; \ - opcode = _Py_OPCODE(word); \ - oparg = _Py_OPARG(word); \ + opcode = word.op.code; \ + oparg = word.op.arg; \ } while (0) #define JUMPTO(x) (next_instr = _PyCode_CODE(frame->f_code) + (x)) #define JUMPBY(x) (next_instr += (x)) @@ -180,14 +180,14 @@ GETITEM(PyObject *v, Py_ssize_t i) { #if USE_COMPUTED_GOTOS #define PREDICT(op) if (0) goto PREDICT_ID(op) #else -#define PREDICT(op) \ +#define PREDICT(next_op) \ do { \ _Py_CODEUNIT word = *next_instr; \ - opcode = _Py_OPCODE(word) | cframe.use_tracing OR_DTRACE_LINE; \ - if (opcode == op) { \ - oparg = _Py_OPARG(word); \ - INSTRUCTION_START(op); \ - goto PREDICT_ID(op); \ + opcode = word.op.code | cframe.use_tracing OR_DTRACE_LINE; \ + if (opcode == next_op) { \ + oparg = word.op.arg; \ + INSTRUCTION_START(next_op); \ + goto PREDICT_ID(next_op); \ } \ } while(0) #endif diff --git a/Python/compile.c b/Python/compile.c index c3b344c7af2a7f..3f620beb0d0205 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -274,31 +274,31 @@ write_instr(_Py_CODEUNIT *codestr, struct instr *instruction, int ilen) int caches = _PyOpcode_Caches[opcode]; switch (ilen - caches) { case 4: - codestr->opcode = EXTENDED_ARG; - codestr->oparg = (oparg >> 24) & 0xFF; + codestr->op.code = EXTENDED_ARG; + codestr->op.arg = (oparg >> 24) & 0xFF; codestr++; /* fall through */ case 3: - codestr->opcode = EXTENDED_ARG; - codestr->oparg = (oparg >> 16) & 0xFF; + codestr->op.code = EXTENDED_ARG; + codestr->op.arg = (oparg >> 16) & 0xFF; codestr++; /* fall through */ case 2: - codestr->opcode = EXTENDED_ARG; - codestr->oparg = (oparg >> 8) & 0xFF; + codestr->op.code = EXTENDED_ARG; + codestr->op.arg = (oparg >> 8) & 0xFF; codestr++; /* fall through */ case 1: - codestr->opcode = opcode; - codestr->oparg = oparg & 0xFF; + codestr->op.code = opcode; + codestr->op.arg = oparg & 0xFF; codestr++; break; default: Py_UNREACHABLE(); } while (caches--) { - codestr->opcode = CACHE; - codestr->oparg = 0; + codestr->op.code = CACHE; + codestr->op.arg = 0; codestr++; } } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 730dfb7426acbf..487e63d855d14a 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -339,9 +339,9 @@ DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP); _Py_CODEUNIT true_next = next_instr[INLINE_CACHE_ENTRIES_BINARY_OP]; - assert(_Py_OPCODE(true_next) == STORE_FAST || - _Py_OPCODE(true_next) == STORE_FAST__LOAD_FAST); - PyObject **target_local = &GETLOCAL(_Py_OPARG(true_next)); + assert(true_next.op.code == STORE_FAST || + true_next.op.code == STORE_FAST__LOAD_FAST); + PyObject **target_local = &GETLOCAL(true_next.op.arg); DEOPT_IF(*target_local != left, BINARY_OP); STAT_INC(BINARY_OP, hit); /* Handle `left = left + right` or `left += right` for str. @@ -2199,10 +2199,10 @@ Py_DECREF(left); Py_DECREF(right); if (cond == NULL) goto pop_2_error; - assert(_Py_OPCODE(next_instr[1]) == POP_JUMP_IF_FALSE || - _Py_OPCODE(next_instr[1]) == POP_JUMP_IF_TRUE); - bool jump_on_true = _Py_OPCODE(next_instr[1]) == POP_JUMP_IF_TRUE; - int offset = _Py_OPARG(next_instr[1]); + assert(next_instr[1].op.code == POP_JUMP_IF_FALSE || + next_instr[1].op.code == POP_JUMP_IF_TRUE); + bool jump_on_true = next_instr[1].op.code == POP_JUMP_IF_TRUE; + int offset = next_instr[1].op.arg; int err = PyObject_IsTrue(cond); Py_DECREF(cond); if (err < 0) { @@ -2230,7 +2230,7 @@ _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); if (sign_ish & oparg) { - int offset = _Py_OPARG(next_instr[1]); + int offset = next_instr[1].op.arg; JUMPBY(offset); } STACK_SHRINK(2); @@ -2255,7 +2255,7 @@ _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); if (sign_ish & oparg) { - int offset = _Py_OPARG(next_instr[1]); + int offset = next_instr[1].op.arg; JUMPBY(offset); } STACK_SHRINK(2); @@ -2278,7 +2278,7 @@ assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); if ((res + COMPARISON_NOT_EQUALS) & oparg) { - int offset = _Py_OPARG(next_instr[1]); + int offset = next_instr[1].op.arg; JUMPBY(offset); } STACK_SHRINK(2); @@ -2682,7 +2682,7 @@ _PyErr_Clear(tstate); } /* iterator ended normally */ - assert(_Py_OPCODE(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg]) == END_FOR); + assert(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg].op.code == END_FOR); Py_DECREF(iter); STACK_SHRINK(1); /* Jump forward oparg, then skip following END_FOR instruction */ @@ -2761,7 +2761,7 @@ DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER]; - assert(_PyOpcode_Deopt[_Py_OPCODE(next)] == STORE_FAST); + assert(_PyOpcode_Deopt[next.op.code] == STORE_FAST); if (r->len <= 0) { STACK_SHRINK(1); Py_DECREF(r); @@ -2772,7 +2772,7 @@ long value = r->start; r->start = value + r->step; r->len--; - if (_PyLong_AssignValue(&GETLOCAL(_Py_OPARG(next)), value) < 0) { + if (_PyLong_AssignValue(&GETLOCAL(next.op.arg), value) < 0) { goto error; } // The STORE_FAST is already done. @@ -2795,7 +2795,7 @@ gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg); - assert(_Py_OPCODE(*next_instr) == END_FOR); + assert(next_instr->op.code == END_FOR); DISPATCH_INLINED(gen_frame); } @@ -3516,7 +3516,7 @@ STACK_SHRINK(3); // CALL + POP_TOP JUMPBY(INLINE_CACHE_ENTRIES_CALL + 1); - assert(_Py_OPCODE(next_instr[-1]) == POP_TOP); + assert(next_instr[-1].op.code == POP_TOP); DISPATCH(); } @@ -3903,8 +3903,8 @@ TARGET(EXTENDED_ARG) { assert(oparg); assert(cframe.use_tracing == 0); - opcode = _Py_OPCODE(*next_instr); - oparg = oparg << 8 | _Py_OPARG(*next_instr); + opcode = next_instr->op.code; + oparg = oparg << 8 | next_instr->op.arg; PRE_DISPATCH_GOTO(); DISPATCH_GOTO(); } diff --git a/Python/specialize.c b/Python/specialize.c index 4ede3122d38046..c9555f8ad4dccb 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -282,7 +282,7 @@ _PyCode_Quicken(PyCodeObject *code) _Py_CODEUNIT *instructions = _PyCode_CODE(code); for (int i = 0; i < Py_SIZE(code); i++) { int previous_opcode = opcode; - opcode = _PyOpcode_Deopt[_Py_OPCODE(instructions[i])]; + opcode = _PyOpcode_Deopt[instructions[i].op.code]; int caches = _PyOpcode_Caches[opcode]; if (caches) { instructions[i + 1].cache = adaptive_counter_warmup(); @@ -291,31 +291,31 @@ _PyCode_Quicken(PyCodeObject *code) } switch (previous_opcode << 8 | opcode) { case LOAD_CONST << 8 | LOAD_FAST: - instructions[i - 1].opcode = LOAD_CONST__LOAD_FAST; + instructions[i - 1].op.code = LOAD_CONST__LOAD_FAST; break; case LOAD_FAST << 8 | LOAD_CONST: - instructions[i - 1].opcode = LOAD_FAST__LOAD_CONST; + instructions[i - 1].op.code = LOAD_FAST__LOAD_CONST; break; case LOAD_FAST << 8 | LOAD_FAST: - instructions[i - 1].opcode = LOAD_FAST__LOAD_FAST; + instructions[i - 1].op.code = LOAD_FAST__LOAD_FAST; break; case STORE_FAST << 8 | LOAD_FAST: - instructions[i - 1].opcode = STORE_FAST__LOAD_FAST; + instructions[i - 1].op.code = STORE_FAST__LOAD_FAST; break; case STORE_FAST << 8 | STORE_FAST: - instructions[i - 1].opcode = STORE_FAST__STORE_FAST; + instructions[i - 1].op.code = STORE_FAST__STORE_FAST; break; case COMPARE_OP << 8 | POP_JUMP_IF_TRUE: case COMPARE_OP << 8 | POP_JUMP_IF_FALSE: { - int oparg = instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].oparg; + int oparg = instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].op.arg; assert((oparg >> 4) <= Py_GE); int mask = compare_masks[oparg >> 4]; if (opcode == POP_JUMP_IF_FALSE) { mask = mask ^ 0xf; } - instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].opcode = COMPARE_AND_BRANCH; - instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].oparg = (oparg & 0xf0) | mask; + instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].op.code = COMPARE_AND_BRANCH; + instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].op.arg = (oparg & 0xf0) | mask; break; } } @@ -519,7 +519,7 @@ specialize_module_load_attr( } write_u32(cache->version, keys_version); cache->index = (uint16_t)index; - _py_set_opcode(instr, LOAD_ATTR_MODULE); + instr->op.code = LOAD_ATTR_MODULE; return 0; } @@ -674,7 +674,7 @@ specialize_dict_access( } write_u32(cache->version, type->tp_version_tag); cache->index = (uint16_t)index; - _py_set_opcode(instr, values_op); + instr->op.code = values_op; } else { PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); @@ -694,7 +694,7 @@ specialize_dict_access( } cache->index = (uint16_t)index; write_u32(cache->version, type->tp_version_tag); - _py_set_opcode(instr, hint_op); + instr->op.code = hint_op; } return 1; } @@ -739,7 +739,7 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) goto fail; case METHOD: { - int oparg = _Py_OPARG(*instr); + int oparg = instr->op.arg; if (oparg & 1) { if (specialize_attr_loadmethod(owner, instr, name, descr, kind)) { goto success; @@ -775,7 +775,7 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) write_u32(lm_cache->type_version, type->tp_version_tag); /* borrowed */ write_obj(lm_cache->descr, fget); - _py_set_opcode(instr, LOAD_ATTR_PROPERTY); + instr->op.code = LOAD_ATTR_PROPERTY; goto success; } case OBJECT_SLOT: @@ -799,7 +799,7 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) assert(offset > 0); cache->index = (uint16_t)offset; write_u32(cache->version, type->tp_version_tag); - _py_set_opcode(instr, LOAD_ATTR_SLOT); + instr->op.code = LOAD_ATTR_SLOT; goto success; } case DUNDER_CLASS: @@ -808,7 +808,7 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) assert(offset == (uint16_t)offset); cache->index = (uint16_t)offset; write_u32(cache->version, type->tp_version_tag); - _py_set_opcode(instr, LOAD_ATTR_SLOT); + instr->op.code = LOAD_ATTR_SLOT; goto success; } case OTHER_SLOT: @@ -836,7 +836,7 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) /* borrowed */ write_obj(lm_cache->descr, descr); write_u32(lm_cache->type_version, type->tp_version_tag); - _py_set_opcode(instr, LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN); + instr->op.code = LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN; goto success; } case BUILTIN_CLASSMETHOD: @@ -867,7 +867,7 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) fail: STAT_INC(LOAD_ATTR, failure); assert(!PyErr_Occurred()); - _py_set_opcode(instr, LOAD_ATTR); + instr->op.code = LOAD_ATTR; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -927,7 +927,7 @@ _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) assert(offset > 0); cache->index = (uint16_t)offset; write_u32(cache->version, type->tp_version_tag); - _py_set_opcode(instr, STORE_ATTR_SLOT); + instr->op.code = STORE_ATTR_SLOT; goto success; } case DUNDER_CLASS: @@ -963,7 +963,7 @@ _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) fail: STAT_INC(STORE_ATTR, failure); assert(!PyErr_Occurred()); - _py_set_opcode(instr, STORE_ATTR); + instr->op.code = STORE_ATTR; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -1027,7 +1027,7 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr, case NON_DESCRIPTOR: write_u32(cache->type_version, ((PyTypeObject *)owner)->tp_version_tag); write_obj(cache->descr, descr); - _py_set_opcode(instr, LOAD_ATTR_CLASS); + instr->op.code = LOAD_ATTR_CLASS; return 0; #ifdef Py_STATS case ABSENT: @@ -1069,7 +1069,7 @@ PyObject *descr, DescriptorClassification kind) return 0; } write_u32(cache->keys_version, keys_version); - _py_set_opcode(instr, LOAD_ATTR_METHOD_WITH_VALUES); + instr->op.code = LOAD_ATTR_METHOD_WITH_VALUES; } else { Py_ssize_t dictoffset = owner_cls->tp_dictoffset; @@ -1078,7 +1078,7 @@ PyObject *descr, DescriptorClassification kind) return 0; } if (dictoffset == 0) { - _py_set_opcode(instr, LOAD_ATTR_METHOD_NO_DICT); + instr->op.code = LOAD_ATTR_METHOD_NO_DICT; } else { PyObject *dict = *(PyObject **) ((char *)owner + dictoffset); @@ -1088,7 +1088,7 @@ PyObject *descr, DescriptorClassification kind) } assert(owner_cls->tp_dictoffset > 0); assert(owner_cls->tp_dictoffset <= INT16_MAX); - _py_set_opcode(instr, LOAD_ATTR_METHOD_LAZY_DICT); + instr->op.code = LOAD_ATTR_METHOD_LAZY_DICT; } } /* `descr` is borrowed. This is safe for methods (even inherited ones from @@ -1146,7 +1146,7 @@ _Py_Specialize_LoadGlobal( } cache->index = (uint16_t)index; write_u32(cache->module_keys_version, keys_version); - _py_set_opcode(instr, LOAD_GLOBAL_MODULE); + instr->op.code = LOAD_GLOBAL_MODULE; goto success; } if (!PyDict_CheckExact(builtins)) { @@ -1184,12 +1184,12 @@ _Py_Specialize_LoadGlobal( cache->index = (uint16_t)index; write_u32(cache->module_keys_version, globals_version); cache->builtin_keys_version = (uint16_t)builtins_version; - _py_set_opcode(instr, LOAD_GLOBAL_BUILTIN); + instr->op.code = LOAD_GLOBAL_BUILTIN; goto success; fail: STAT_INC(LOAD_GLOBAL, failure); assert(!PyErr_Occurred()); - _py_set_opcode(instr, LOAD_GLOBAL); + instr->op.code = LOAD_GLOBAL; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -1295,7 +1295,7 @@ _Py_Specialize_BinarySubscr( if (container_type == &PyList_Type) { if (PyLong_CheckExact(sub)) { if (Py_SIZE(sub) == 0 || Py_SIZE(sub) == 1) { - _py_set_opcode(instr, BINARY_SUBSCR_LIST_INT); + instr->op.code = BINARY_SUBSCR_LIST_INT; goto success; } SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OUT_OF_RANGE); @@ -1308,7 +1308,7 @@ _Py_Specialize_BinarySubscr( if (container_type == &PyTuple_Type) { if (PyLong_CheckExact(sub)) { if (Py_SIZE(sub) == 0 || Py_SIZE(sub) == 1) { - _py_set_opcode(instr, BINARY_SUBSCR_TUPLE_INT); + instr->op.code = BINARY_SUBSCR_TUPLE_INT; goto success; } SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OUT_OF_RANGE); @@ -1319,7 +1319,7 @@ _Py_Specialize_BinarySubscr( goto fail; } if (container_type == &PyDict_Type) { - _py_set_opcode(instr, BINARY_SUBSCR_DICT); + instr->op.code = BINARY_SUBSCR_DICT; goto success; } PyTypeObject *cls = Py_TYPE(container); @@ -1350,7 +1350,7 @@ _Py_Specialize_BinarySubscr( } cache->func_version = version; ((PyHeapTypeObject *)container_type)->_spec_cache.getitem = descriptor; - _py_set_opcode(instr, BINARY_SUBSCR_GETITEM); + instr->op.code = BINARY_SUBSCR_GETITEM; goto success; } SPECIALIZATION_FAIL(BINARY_SUBSCR, @@ -1358,7 +1358,7 @@ _Py_Specialize_BinarySubscr( fail: STAT_INC(BINARY_SUBSCR, failure); assert(!PyErr_Occurred()); - _py_set_opcode(instr, BINARY_SUBSCR); + instr->op.code = BINARY_SUBSCR; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -1378,7 +1378,7 @@ _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *ins if ((Py_SIZE(sub) == 0 || Py_SIZE(sub) == 1) && ((PyLongObject *)sub)->long_value.ob_digit[0] < (size_t)PyList_GET_SIZE(container)) { - _py_set_opcode(instr, STORE_SUBSCR_LIST_INT); + instr->op.code = STORE_SUBSCR_LIST_INT; goto success; } else { @@ -1396,8 +1396,8 @@ _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *ins } } if (container_type == &PyDict_Type) { - _py_set_opcode(instr, STORE_SUBSCR_DICT); - goto success; + instr->op.code = STORE_SUBSCR_DICT; + goto success; } #ifdef Py_STATS PyMappingMethods *as_mapping = container_type->tp_as_mapping; @@ -1463,7 +1463,7 @@ _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *ins fail: STAT_INC(STORE_SUBSCR, failure); assert(!PyErr_Occurred()); - _py_set_opcode(instr, STORE_SUBSCR); + instr->op.code = STORE_SUBSCR; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -1482,23 +1482,23 @@ specialize_class_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs, return -1; } if (tp->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) { - int oparg = _Py_OPARG(*instr); + int oparg = instr->op.arg; if (nargs == 1 && kwnames == NULL && oparg == 1) { if (tp == &PyUnicode_Type) { - _py_set_opcode(instr, CALL_NO_KW_STR_1); + instr->op.code = CALL_NO_KW_STR_1; return 0; } else if (tp == &PyType_Type) { - _py_set_opcode(instr, CALL_NO_KW_TYPE_1); + instr->op.code = CALL_NO_KW_TYPE_1; return 0; } else if (tp == &PyTuple_Type) { - _py_set_opcode(instr, CALL_NO_KW_TUPLE_1); + instr->op.code = CALL_NO_KW_TUPLE_1; return 0; } } if (tp->tp_vectorcall != NULL) { - _py_set_opcode(instr, CALL_BUILTIN_CLASS); + instr->op.code = CALL_BUILTIN_CLASS; return 0; } SPECIALIZATION_FAIL(CALL, tp == &PyUnicode_Type ? @@ -1573,7 +1573,7 @@ specialize_method_descriptor(PyMethodDescrObject *descr, _Py_CODEUNIT *instr, SPECIALIZATION_FAIL(CALL, SPEC_FAIL_WRONG_NUMBER_ARGUMENTS); return -1; } - _py_set_opcode(instr, CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS); + instr->op.code = CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS; return 0; } case METH_O: { @@ -1584,21 +1584,21 @@ specialize_method_descriptor(PyMethodDescrObject *descr, _Py_CODEUNIT *instr, PyInterpreterState *interp = _PyInterpreterState_GET(); PyObject *list_append = interp->callable_cache.list_append; _Py_CODEUNIT next = instr[INLINE_CACHE_ENTRIES_CALL + 1]; - bool pop = (_Py_OPCODE(next) == POP_TOP); - int oparg = _Py_OPARG(*instr); + bool pop = (next.op.code == POP_TOP); + int oparg = instr->op.arg; if ((PyObject *)descr == list_append && oparg == 1 && pop) { - _py_set_opcode(instr, CALL_NO_KW_LIST_APPEND); + instr->op.code = CALL_NO_KW_LIST_APPEND; return 0; } - _py_set_opcode(instr, CALL_NO_KW_METHOD_DESCRIPTOR_O); + instr->op.code = CALL_NO_KW_METHOD_DESCRIPTOR_O; return 0; } case METH_FASTCALL: { - _py_set_opcode(instr, CALL_NO_KW_METHOD_DESCRIPTOR_FAST); + instr->op.code = CALL_NO_KW_METHOD_DESCRIPTOR_FAST; return 0; } case METH_FASTCALL | METH_KEYWORDS: { - _py_set_opcode(instr, CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS); + instr->op.code = CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS; return 0; } } @@ -1649,14 +1649,14 @@ specialize_py_call(PyFunctionObject *func, _Py_CODEUNIT *instr, int nargs, write_u32(cache->func_version, version); cache->min_args = min_args; if (argcount == nargs) { - _py_set_opcode(instr, bound_method ? CALL_BOUND_METHOD_EXACT_ARGS : CALL_PY_EXACT_ARGS); + instr->op.code = bound_method ? CALL_BOUND_METHOD_EXACT_ARGS : CALL_PY_EXACT_ARGS; } else if (bound_method) { SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_BOUND_METHOD); return -1; } else { - _py_set_opcode(instr, CALL_PY_WITH_DEFAULTS); + instr->op.code = CALL_PY_WITH_DEFAULTS; } return 0; } @@ -1683,10 +1683,10 @@ specialize_c_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs, /* len(o) */ PyInterpreterState *interp = _PyInterpreterState_GET(); if (callable == interp->callable_cache.len) { - _py_set_opcode(instr, CALL_NO_KW_LEN); + instr->op.code = CALL_NO_KW_LEN; return 0; } - _py_set_opcode(instr, CALL_NO_KW_BUILTIN_O); + instr->op.code = CALL_NO_KW_BUILTIN_O; return 0; } case METH_FASTCALL: { @@ -1698,15 +1698,15 @@ specialize_c_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs, /* isinstance(o1, o2) */ PyInterpreterState *interp = _PyInterpreterState_GET(); if (callable == interp->callable_cache.isinstance) { - _py_set_opcode(instr, CALL_NO_KW_ISINSTANCE); + instr->op.code = CALL_NO_KW_ISINSTANCE; return 0; } } - _py_set_opcode(instr, CALL_NO_KW_BUILTIN_FAST); + instr->op.code = CALL_NO_KW_BUILTIN_FAST; return 0; } case METH_FASTCALL | METH_KEYWORDS: { - _py_set_opcode(instr, CALL_BUILTIN_FAST_WITH_KEYWORDS); + instr->op.code = CALL_BUILTIN_FAST_WITH_KEYWORDS; return 0; } default: @@ -1785,7 +1785,7 @@ _Py_Specialize_Call(PyObject *callable, _Py_CODEUNIT *instr, int nargs, if (fail) { STAT_INC(CALL, failure); assert(!PyErr_Occurred()); - _py_set_opcode(instr, CALL); + instr->op.code = CALL; cache->counter = adaptive_counter_backoff(cache->counter); } else { @@ -1880,21 +1880,21 @@ _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, } if (PyUnicode_CheckExact(lhs)) { _Py_CODEUNIT next = instr[INLINE_CACHE_ENTRIES_BINARY_OP + 1]; - bool to_store = (_Py_OPCODE(next) == STORE_FAST || - _Py_OPCODE(next) == STORE_FAST__LOAD_FAST); - if (to_store && locals[_Py_OPARG(next)] == lhs) { - _py_set_opcode(instr, BINARY_OP_INPLACE_ADD_UNICODE); + bool to_store = (next.op.code == STORE_FAST || + next.op.code == STORE_FAST__LOAD_FAST); + if (to_store && locals[next.op.arg] == lhs) { + instr->op.code = BINARY_OP_INPLACE_ADD_UNICODE; goto success; } - _py_set_opcode(instr, BINARY_OP_ADD_UNICODE); + instr->op.code = BINARY_OP_ADD_UNICODE; goto success; } if (PyLong_CheckExact(lhs)) { - _py_set_opcode(instr, BINARY_OP_ADD_INT); + instr->op.code = BINARY_OP_ADD_INT; goto success; } if (PyFloat_CheckExact(lhs)) { - _py_set_opcode(instr, BINARY_OP_ADD_FLOAT); + instr->op.code = BINARY_OP_ADD_FLOAT; goto success; } break; @@ -1904,11 +1904,11 @@ _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, break; } if (PyLong_CheckExact(lhs)) { - _py_set_opcode(instr, BINARY_OP_MULTIPLY_INT); + instr->op.code = BINARY_OP_MULTIPLY_INT; goto success; } if (PyFloat_CheckExact(lhs)) { - _py_set_opcode(instr, BINARY_OP_MULTIPLY_FLOAT); + instr->op.code = BINARY_OP_MULTIPLY_FLOAT; goto success; } break; @@ -1918,18 +1918,18 @@ _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, break; } if (PyLong_CheckExact(lhs)) { - _py_set_opcode(instr, BINARY_OP_SUBTRACT_INT); + instr->op.code = BINARY_OP_SUBTRACT_INT; goto success; } if (PyFloat_CheckExact(lhs)) { - _py_set_opcode(instr, BINARY_OP_SUBTRACT_FLOAT); + instr->op.code = BINARY_OP_SUBTRACT_FLOAT; goto success; } break; } SPECIALIZATION_FAIL(BINARY_OP, binary_op_fail_kind(oparg, lhs, rhs)); STAT_INC(BINARY_OP, failure); - _py_set_opcode(instr, BINARY_OP); + instr->op.code = BINARY_OP; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -1981,7 +1981,7 @@ _Py_Specialize_CompareAndBranch(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *inst assert(_PyOpcode_Caches[COMPARE_AND_BRANCH] == INLINE_CACHE_ENTRIES_COMPARE_OP); _PyCompareOpCache *cache = (_PyCompareOpCache *)(instr + 1); #ifndef NDEBUG - int next_opcode = _Py_OPCODE(instr[INLINE_CACHE_ENTRIES_COMPARE_OP + 1]); + int next_opcode = instr[INLINE_CACHE_ENTRIES_COMPARE_OP + 1].op.code; assert(next_opcode == POP_JUMP_IF_FALSE || next_opcode == POP_JUMP_IF_TRUE); #endif if (Py_TYPE(lhs) != Py_TYPE(rhs)) { @@ -1989,12 +1989,12 @@ _Py_Specialize_CompareAndBranch(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *inst goto failure; } if (PyFloat_CheckExact(lhs)) { - _py_set_opcode(instr, COMPARE_AND_BRANCH_FLOAT); + instr->op.code = COMPARE_AND_BRANCH_FLOAT; goto success; } if (PyLong_CheckExact(lhs)) { if (Py_ABS(Py_SIZE(lhs)) <= 1 && Py_ABS(Py_SIZE(rhs)) <= 1) { - _py_set_opcode(instr, COMPARE_AND_BRANCH_INT); + instr->op.code = COMPARE_AND_BRANCH_INT; goto success; } else { @@ -2009,14 +2009,14 @@ _Py_Specialize_CompareAndBranch(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *inst goto failure; } else { - _py_set_opcode(instr, COMPARE_AND_BRANCH_STR); + instr->op.code = COMPARE_AND_BRANCH_STR; goto success; } } SPECIALIZATION_FAIL(COMPARE_AND_BRANCH, compare_op_fail_kind(lhs, rhs)); failure: STAT_INC(COMPARE_AND_BRANCH, failure); - _py_set_opcode(instr, COMPARE_AND_BRANCH); + instr->op.code = COMPARE_AND_BRANCH; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -2051,10 +2051,10 @@ _Py_Specialize_UnpackSequence(PyObject *seq, _Py_CODEUNIT *instr, int oparg) goto failure; } if (PyTuple_GET_SIZE(seq) == 2) { - _py_set_opcode(instr, UNPACK_SEQUENCE_TWO_TUPLE); + instr->op.code = UNPACK_SEQUENCE_TWO_TUPLE; goto success; } - _py_set_opcode(instr, UNPACK_SEQUENCE_TUPLE); + instr->op.code = UNPACK_SEQUENCE_TUPLE; goto success; } if (PyList_CheckExact(seq)) { @@ -2062,13 +2062,13 @@ _Py_Specialize_UnpackSequence(PyObject *seq, _Py_CODEUNIT *instr, int oparg) SPECIALIZATION_FAIL(UNPACK_SEQUENCE, SPEC_FAIL_EXPECTED_ERROR); goto failure; } - _py_set_opcode(instr, UNPACK_SEQUENCE_LIST); + instr->op.code = UNPACK_SEQUENCE_LIST; goto success; } SPECIALIZATION_FAIL(UNPACK_SEQUENCE, unpack_sequence_fail_kind(seq)); failure: STAT_INC(UNPACK_SEQUENCE, failure); - _py_set_opcode(instr, UNPACK_SEQUENCE); + instr->op.code = UNPACK_SEQUENCE; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -2156,28 +2156,28 @@ _Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg) _PyForIterCache *cache = (_PyForIterCache *)(instr + 1); PyTypeObject *tp = Py_TYPE(iter); _Py_CODEUNIT next = instr[1+INLINE_CACHE_ENTRIES_FOR_ITER]; - int next_op = _PyOpcode_Deopt[_Py_OPCODE(next)]; + int next_op = _PyOpcode_Deopt[next.op.code]; if (tp == &PyListIter_Type) { - _py_set_opcode(instr, FOR_ITER_LIST); + instr->op.code = FOR_ITER_LIST; goto success; } else if (tp == &PyTupleIter_Type) { - _py_set_opcode(instr, FOR_ITER_TUPLE); + instr->op.code = FOR_ITER_TUPLE; goto success; } else if (tp == &PyRangeIter_Type && next_op == STORE_FAST) { - _py_set_opcode(instr, FOR_ITER_RANGE); + instr->op.code = FOR_ITER_RANGE; goto success; } else if (tp == &PyGen_Type && oparg <= SHRT_MAX) { - assert(_Py_OPCODE(instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1]) == END_FOR); - _py_set_opcode(instr, FOR_ITER_GEN); + assert(instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1].op.code == END_FOR); + instr->op.code = FOR_ITER_GEN; goto success; } SPECIALIZATION_FAIL(FOR_ITER, _PySpecialization_ClassifyIterator(iter)); STAT_INC(FOR_ITER, failure); - _py_set_opcode(instr, FOR_ITER); + instr->op.code = FOR_ITER; cache->counter = adaptive_counter_backoff(cache->counter); return; success: @@ -2193,13 +2193,13 @@ _Py_Specialize_Send(PyObject *receiver, _Py_CODEUNIT *instr) _PySendCache *cache = (_PySendCache *)(instr + 1); PyTypeObject *tp = Py_TYPE(receiver); if (tp == &PyGen_Type || tp == &PyCoro_Type) { - _py_set_opcode(instr, SEND_GEN); + instr->op.code = SEND_GEN; goto success; } SPECIALIZATION_FAIL(SEND, _PySpecialization_ClassifyIterator(receiver)); STAT_INC(SEND, failure); - _py_set_opcode(instr, SEND); + instr->op.code = SEND; cache->counter = adaptive_counter_backoff(cache->counter); return; success: diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index aa8e14075c8738..c7f52b55a5eb99 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -8,6 +8,7 @@ import contextlib import dataclasses import os +import posixpath import re import sys import typing @@ -17,7 +18,7 @@ HERE = os.path.dirname(__file__) ROOT = os.path.join(HERE, "../..") -THIS = os.path.relpath(__file__, ROOT) +THIS = os.path.relpath(__file__, ROOT).replace(os.path.sep, posixpath.sep) DEFAULT_INPUT = os.path.relpath(os.path.join(ROOT, "Python/bytecodes.c")) DEFAULT_OUTPUT = os.path.relpath(os.path.join(ROOT, "Python/generated_cases.c.h")) @@ -930,7 +931,7 @@ def write_metadata(self) -> None: with open(self.metadata_filename, "w") as f: # Write provenance header f.write(f"// This file is generated by {THIS} --metadata\n") - f.write(f"// from {os.path.relpath(self.filename, ROOT)}\n") + f.write(f"// from {os.path.relpath(self.filename, ROOT).replace(os.path.sep, posixpath.sep)}\n") f.write(f"// Do not edit!\n") # Create formatter; the rest of the code uses this @@ -1009,7 +1010,7 @@ def write_instructions(self) -> None: with open(self.output_filename, "w") as f: # Write provenance header f.write(f"// This file is generated by {THIS}\n") - f.write(f"// from {os.path.relpath(self.filename, ROOT)}\n") + f.write(f"// from {os.path.relpath(self.filename, ROOT).replace(os.path.sep, posixpath.sep)}\n") f.write(f"// Do not edit!\n") # Create formatter; the rest of the code uses this From ed01addb59a554804995303ad3e7bf0c6067737b Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Tue, 21 Feb 2023 00:20:18 +0900 Subject: [PATCH 220/225] gh-101981: Apply HOMEBREW related environment variables (gh-102074) --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index acc8d936774af5..eec11e25a7c7f6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -154,6 +154,9 @@ jobs: needs: check_source if: needs.check_source.outputs.run_tests == 'true' env: + HOMEBREW_NO_ANALYTICS: 1 + HOMEBREW_NO_AUTO_UPDATE: 1 + HOMEBREW_NO_INSTALL_CLEANUP: 1 PYTHONSTRICTEXTENSIONBUILD: 1 steps: - uses: actions/checkout@v3 From 59e86caca812fc993c5eb7dc8ccd1508ffccba86 Mon Sep 17 00:00:00 2001 From: Tim Hatch Date: Mon, 20 Feb 2023 09:07:03 -0800 Subject: [PATCH 221/225] gh-88233: zipfile: handle extras after a zip64 extra (GH-96161) Previously, any data _after_ the zip64 extra would be removed. With many new tests. Fixes #88233 Automerge-Triggered-By: GH:jaraco --- Lib/test/test_zipfile/test_core.py | 62 +++++++++++++++++++ Lib/zipfile/__init__.py | 2 + ...2-09-05-12-17-34.gh-issue-88233.gff9qJ.rst | 2 + 3 files changed, 66 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2022-09-05-12-17-34.gh-issue-88233.gff9qJ.rst diff --git a/Lib/test/test_zipfile/test_core.py b/Lib/test/test_zipfile/test_core.py index cf41d0e8cb8d53..e23f5c2a8556f2 100644 --- a/Lib/test/test_zipfile/test_core.py +++ b/Lib/test/test_zipfile/test_core.py @@ -3010,5 +3010,67 @@ def test_cli_with_metadata_encoding_extract(self): self.assertIn(name, listing) +class StripExtraTests(unittest.TestCase): + # Note: all of the "z" characters are technically invalid, but up + # to 3 bytes at the end of the extra will be passed through as they + # are too short to encode a valid extra. + + ZIP64_EXTRA = 1 + + def test_no_data(self): + s = struct.Struct(" Date: Mon, 20 Feb 2023 19:21:10 +0000 Subject: [PATCH 222/225] GH-99818: improve the documentation for zipfile.Path and Traversable (GH-101589) Automerge-Triggered-By: GH:FFY00 --- Doc/library/importlib.resources.abc.rst | 5 ++++- Doc/library/zipfile.rst | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Doc/library/importlib.resources.abc.rst b/Doc/library/importlib.resources.abc.rst index 7747e89a833c02..2d0f137ffc7996 100644 --- a/Doc/library/importlib.resources.abc.rst +++ b/Doc/library/importlib.resources.abc.rst @@ -89,9 +89,12 @@ .. class:: Traversable - An object with a subset of pathlib.Path methods suitable for + An object with a subset of :class:`pathlib.Path` methods suitable for traversing directories and opening files. + For a representation of the object on the file-system, use + :meth:`importlib.resources.as_file`. + .. versionadded:: 3.9 .. deprecated-removed:: 3.12 3.14 diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst index 1c516723f04a33..0195abc3a992c1 100644 --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -55,8 +55,9 @@ The module defines the following items: .. class:: Path :noindex: - A pathlib-compatible wrapper for zip files. See section - :ref:`path-objects` for details. + Class that implements a subset of the interface provided by + :class:`pathlib.Path`, including the full + :class:`importlib.resources.abc.Traversable` interface. .. versionadded:: 3.8 From 36854bbb240e417c0df6f0014924fcc899388186 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 20 Feb 2023 16:01:58 -0500 Subject: [PATCH 223/225] gh-101566: Sync with zipp 3.14. (GH-102018) --- Lib/test/test_zipfile/_context.py | 30 ++++ Lib/test/test_zipfile/_func_timeout_compat.py | 8 + Lib/test/test_zipfile/_itertools.py | 29 ++++ Lib/test/test_zipfile/test_path.py | 137 +++++++++++------- Lib/zipfile/_path.py | 63 +++++++- ...-02-17-20-24-15.gh-issue-101566.FjgWBt.rst | 4 + 6 files changed, 215 insertions(+), 56 deletions(-) create mode 100644 Lib/test/test_zipfile/_context.py create mode 100644 Lib/test/test_zipfile/_func_timeout_compat.py create mode 100644 Misc/NEWS.d/next/Library/2023-02-17-20-24-15.gh-issue-101566.FjgWBt.rst diff --git a/Lib/test/test_zipfile/_context.py b/Lib/test/test_zipfile/_context.py new file mode 100644 index 00000000000000..348798aa08d452 --- /dev/null +++ b/Lib/test/test_zipfile/_context.py @@ -0,0 +1,30 @@ +import contextlib +import time + + +class DeadlineExceeded(Exception): + pass + + +class TimedContext(contextlib.ContextDecorator): + """ + A context that will raise DeadlineExceeded if the + max duration is reached during the execution. + + >>> TimedContext(1)(time.sleep)(.1) + >>> TimedContext(0)(time.sleep)(.1) + Traceback (most recent call last): + ... + tests._context.DeadlineExceeded: (..., 0) + """ + + def __init__(self, max_duration: int): + self.max_duration = max_duration + + def __enter__(self): + self.start = time.monotonic() + + def __exit__(self, *err): + duration = time.monotonic() - self.start + if duration > self.max_duration: + raise DeadlineExceeded(duration, self.max_duration) diff --git a/Lib/test/test_zipfile/_func_timeout_compat.py b/Lib/test/test_zipfile/_func_timeout_compat.py new file mode 100644 index 00000000000000..b1f2b2698a8538 --- /dev/null +++ b/Lib/test/test_zipfile/_func_timeout_compat.py @@ -0,0 +1,8 @@ +try: + from func_timeout import func_set_timeout as set_timeout +except ImportError: # pragma: no cover + # provide a fallback that doesn't actually time out + from ._context import TimedContext as set_timeout + + +__all__ = ['set_timeout'] diff --git a/Lib/test/test_zipfile/_itertools.py b/Lib/test/test_zipfile/_itertools.py index 559f3f111b88a3..74f01fe5ba3de2 100644 --- a/Lib/test/test_zipfile/_itertools.py +++ b/Lib/test/test_zipfile/_itertools.py @@ -1,3 +1,32 @@ +import itertools + + +# from jaraco.itertools 6.3.0 +class Counter: + """ + Wrap an iterable in an object that stores the count of items + that pass through it. + + >>> items = Counter(range(20)) + >>> items.count + 0 + >>> values = list(items) + >>> items.count + 20 + """ + + def __init__(self, i): + self.count = 0 + self.iter = zip(itertools.count(1), i) + + def __iter__(self): + return self + + def __next__(self): + self.count, result = next(self.iter) + return result + + # from more_itertools v8.13.0 def always_iterable(obj, base_type=(str, bytes)): if obj is None: diff --git a/Lib/test/test_zipfile/test_path.py b/Lib/test/test_zipfile/test_path.py index 3086fd2080a97d..92fda690b2fc50 100644 --- a/Lib/test/test_zipfile/test_path.py +++ b/Lib/test/test_zipfile/test_path.py @@ -4,36 +4,25 @@ import pathlib import pickle import string -from test.support.script_helper import assert_python_ok +import sys import unittest import zipfile -from ._test_params import parameterize, Invoked from ._functools import compose +from ._itertools import Counter +from ._test_params import parameterize, Invoked +from ._func_timeout_compat import set_timeout from test.support.os_helper import temp_dir -# Poor man's technique to consume a (smallish) iterable. -consume = tuple - - -# from jaraco.itertools 5.0 class jaraco: class itertools: - class Counter: - def __init__(self, i): - self.count = 0 - self._orig_iter = iter(i) + Counter = Counter - def __iter__(self): - return self - def __next__(self): - result = next(self._orig_iter) - self.count += 1 - return result +consume = tuple def add_dirs(zf): @@ -161,10 +150,10 @@ def test_open_encoding_utf16(self): u16 = path.joinpath("16.txt") with u16.open('r', "utf-16") as strm: data = strm.read() - self.assertEqual(data, "This was utf-16") + assert data == "This was utf-16" with u16.open(encoding="utf-16") as strm: data = strm.read() - self.assertEqual(data, "This was utf-16") + assert data == "This was utf-16" def test_open_encoding_errors(self): in_memory_file = io.BytesIO() @@ -177,9 +166,9 @@ def test_open_encoding_errors(self): # encoding= as a positional argument for gh-101144. data = u16.read_text("utf-8", errors="ignore") - self.assertEqual(data, "invalid utf-8: .") + assert data == "invalid utf-8: ." with u16.open("r", "utf-8", errors="surrogateescape") as f: - self.assertEqual(f.read(), "invalid utf-8: \udcff\udcff.") + assert f.read() == "invalid utf-8: \udcff\udcff." # encoding= both positional and keyword is an error; gh-101144. with self.assertRaisesRegex(TypeError, "encoding"): @@ -191,24 +180,21 @@ def test_open_encoding_errors(self): with self.assertRaises(UnicodeDecodeError): f.read() - def test_encoding_warnings(self): + @unittest.skipIf( + not getattr(sys.flags, 'warn_default_encoding', 0), + "Requires warn_default_encoding", + ) + @pass_alpharep + def test_encoding_warnings(self, alpharep): """EncodingWarning must blame the read_text and open calls.""" - code = '''\ -import io, zipfile -with zipfile.ZipFile(io.BytesIO(), "w") as zf: - zf.filename = '' - zf.writestr("path/file.txt", b"Spanish Inquisition") - root = zipfile.Path(zf) - (path,) = root.iterdir() - file_path = path.joinpath("file.txt") - unused = file_path.read_text() # should warn - file_path.open("r").close() # should warn -''' - proc = assert_python_ok('-X', 'warn_default_encoding', '-c', code) - warnings = proc.err.splitlines() - self.assertEqual(len(warnings), 2, proc.err) - self.assertRegex(warnings[0], rb"^:8: EncodingWarning:") - self.assertRegex(warnings[1], rb"^:9: EncodingWarning:") + assert sys.flags.warn_default_encoding + root = zipfile.Path(alpharep) + with self.assertWarns(EncodingWarning) as wc: + root.joinpath("a.txt").read_text() + assert __file__ == wc.filename + with self.assertWarns(EncodingWarning) as wc: + root.joinpath("a.txt").open("r").close() + assert __file__ == wc.filename def test_open_write(self): """ @@ -250,7 +236,8 @@ def test_read(self, alpharep): root = zipfile.Path(alpharep) a, b, g = root.iterdir() assert a.read_text(encoding="utf-8") == "content of a" - a.read_text("utf-8") # No positional arg TypeError per gh-101144. + # Also check positional encoding arg (gh-101144). + assert a.read_text("utf-8") == "content of a" assert a.read_bytes() == b"content of a" @pass_alpharep @@ -275,19 +262,6 @@ def test_traverse_truediv(self, alpharep): e = root / "b" / "d" / "e.txt" assert e.read_text(encoding="utf-8") == "content of e" - @pass_alpharep - def test_traverse_simplediv(self, alpharep): - """ - Disable the __future__.division when testing traversal. - """ - code = compile( - source="zipfile.Path(alpharep) / 'a'", - filename="(test)", - mode="eval", - dont_inherit=True, - ) - eval(code) - @pass_alpharep def test_pathlike_construction(self, alpharep): """ @@ -356,7 +330,7 @@ def test_joinpath_constant_time(self): # Check the file iterated all items assert entries.count == self.HUGE_ZIPFILE_NUM_ENTRIES - # @func_timeout.func_set_timeout(3) + @set_timeout(3) def test_implied_dirs_performance(self): data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)] zipfile.CompleteDirs._implied_dirs(data) @@ -472,6 +446,52 @@ def test_root_unnamed(self, alpharep): assert sub.name == "b" assert sub.parent + @pass_alpharep + def test_match_and_glob(self, alpharep): + root = zipfile.Path(alpharep) + assert not root.match("*.txt") + + assert list(root.glob("b/c.*")) == [zipfile.Path(alpharep, "b/c.txt")] + + files = root.glob("**/*.txt") + assert all(each.match("*.txt") for each in files) + + assert list(root.glob("**/*.txt")) == list(root.rglob("*.txt")) + + def test_glob_empty(self): + root = zipfile.Path(zipfile.ZipFile(io.BytesIO(), 'w')) + with self.assertRaises(ValueError): + root.glob('') + + @pass_alpharep + def test_eq_hash(self, alpharep): + root = zipfile.Path(alpharep) + assert root == zipfile.Path(alpharep) + + assert root != (root / "a.txt") + assert (root / "a.txt") == (root / "a.txt") + + root = zipfile.Path(alpharep) + assert root in {root} + + @pass_alpharep + def test_is_symlink(self, alpharep): + """ + See python/cpython#82102 for symlink support beyond this object. + """ + + root = zipfile.Path(alpharep) + assert not root.is_symlink() + + @pass_alpharep + def test_relative_to(self, alpharep): + root = zipfile.Path(alpharep) + relative = root.joinpath("b", "c.txt").relative_to(root / "b") + assert str(relative) == "c.txt" + + relative = root.joinpath("b", "d", "e.txt").relative_to(root / "b") + assert str(relative) == "d/e.txt" + @pass_alpharep def test_inheritance(self, alpharep): cls = type('PathChild', (zipfile.Path,), {}) @@ -493,3 +513,14 @@ def test_pickle(self, alpharep, path_type, subpath): restored_1 = pickle.loads(saved_1) first, *rest = restored_1.iterdir() assert first.read_text().startswith('content of ') + + @pass_alpharep + def test_extract_orig_with_implied_dirs(self, alpharep): + """ + A zip file wrapped in a Path should extract even with implied dirs. + """ + source_path = self.zipfile_ondisk(alpharep) + zf = zipfile.ZipFile(source_path) + # wrap the zipfile for its side effect + zipfile.Path(zf) + zf.extractall(source_path.parent) diff --git a/Lib/zipfile/_path.py b/Lib/zipfile/_path.py index 7c7a6a0e2c0d32..c2c804e96d6b6c 100644 --- a/Lib/zipfile/_path.py +++ b/Lib/zipfile/_path.py @@ -4,6 +4,8 @@ import itertools import contextlib import pathlib +import re +import fnmatch __all__ = ['Path'] @@ -93,7 +95,7 @@ def _implied_dirs(names): return _dedupe(_difference(as_dirs, names)) def namelist(self): - names = super(CompleteDirs, self).namelist() + names = super().namelist() return names + list(self._implied_dirs(names)) def _name_set(self): @@ -109,6 +111,17 @@ def resolve_dir(self, name): dir_match = name not in names and dirname in names return dirname if dir_match else name + def getinfo(self, name): + """ + Supplement getinfo for implied dirs. + """ + try: + return super().getinfo(name) + except KeyError: + if not name.endswith('/') or name not in self._name_set(): + raise + return zipfile.ZipInfo(filename=name) + @classmethod def make(cls, source): """ @@ -138,13 +151,13 @@ class FastLookup(CompleteDirs): def namelist(self): with contextlib.suppress(AttributeError): return self.__names - self.__names = super(FastLookup, self).namelist() + self.__names = super().namelist() return self.__names def _name_set(self): with contextlib.suppress(AttributeError): return self.__lookup - self.__lookup = super(FastLookup, self)._name_set() + self.__lookup = super()._name_set() return self.__lookup @@ -246,6 +259,18 @@ def __init__(self, root, at=""): self.root = FastLookup.make(root) self.at = at + def __eq__(self, other): + """ + >>> Path(zipfile.ZipFile(io.BytesIO(), 'w')) == 'foo' + False + """ + if self.__class__ is not other.__class__: + return NotImplemented + return (self.root, self.at) == (other.root, other.at) + + def __hash__(self): + return hash((self.root, self.at)) + def open(self, mode='r', *args, pwd=None, **kwargs): """ Open this entry as text or binary following the semantics @@ -316,6 +341,38 @@ def iterdir(self): subs = map(self._next, self.root.namelist()) return filter(self._is_child, subs) + def match(self, path_pattern): + return pathlib.Path(self.at).match(path_pattern) + + def is_symlink(self): + """ + Return whether this path is a symlink. Always false (python/cpython#82102). + """ + return False + + def _descendants(self): + for child in self.iterdir(): + yield child + if child.is_dir(): + yield from child._descendants() + + def glob(self, pattern): + if not pattern: + raise ValueError(f"Unacceptable pattern: {pattern!r}") + + matches = re.compile(fnmatch.translate(pattern)).fullmatch + return ( + child + for child in self._descendants() + if matches(str(child.relative_to(self))) + ) + + def rglob(self, pattern): + return self.glob(f'**/{pattern}') + + def relative_to(self, other, *extra): + return posixpath.relpath(str(self), str(other.joinpath(*extra))) + def __str__(self): return posixpath.join(self.root.filename, self.at) diff --git a/Misc/NEWS.d/next/Library/2023-02-17-20-24-15.gh-issue-101566.FjgWBt.rst b/Misc/NEWS.d/next/Library/2023-02-17-20-24-15.gh-issue-101566.FjgWBt.rst new file mode 100644 index 00000000000000..5fc1a0288a82dc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-17-20-24-15.gh-issue-101566.FjgWBt.rst @@ -0,0 +1,4 @@ +In zipfile, sync Path with `zipp 3.14 +`_, including +fix for extractall on the underlying zipfile after being wrapped in +``Path``. From 4d3bc89a3f54c4f09756a9b644b3912bf54191a7 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Mon, 20 Feb 2023 21:54:19 +0000 Subject: [PATCH 224/225] gh-102011: use sys.exception() instead of sys.exc_info() in docs where possible (#102012) --- Doc/library/exceptions.rst | 2 +- Doc/library/traceback.rst | 21 ++++++++++---------- Doc/library/types.rst | 2 +- Doc/library/wsgiref.rst | 2 +- Doc/reference/compound_stmts.rst | 33 +++++++++++++++----------------- Doc/reference/datamodel.rst | 1 + 6 files changed, 29 insertions(+), 32 deletions(-) diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index 1217b817b4e843..4a57e9c8799336 100644 --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -123,7 +123,7 @@ The following exceptions are used mostly as base classes for other exceptions. try: ... except SomeException: - tb = sys.exc_info()[2] + tb = sys.exception().__traceback__ raise OtherException(...).with_traceback(tb) .. method:: add_note(note) diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst index 69818baf184d7c..561c85290463ef 100644 --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -16,9 +16,8 @@ interpreter. .. index:: object: traceback -The module uses traceback objects --- this is the object type that is stored in -the :data:`sys.last_traceback` variable and returned as the third item from -:func:`sys.exc_info`. +The module uses traceback objects --- these are objects of type :class:`types.TracebackType`, +which are assigned to the ``__traceback__`` field of :class:`BaseException` instances. .. seealso:: @@ -81,7 +80,7 @@ The module defines the following functions: .. function:: print_exc(limit=None, file=None, chain=True) - This is a shorthand for ``print_exception(*sys.exc_info(), limit, file, + This is a shorthand for ``print_exception(sys.exception(), limit, file, chain)``. @@ -444,11 +443,11 @@ exception and traceback: try: lumberjack() except IndexError: - exc_type, exc_value, exc_traceback = sys.exc_info() + exc = sys.exception() print("*** print_tb:") - traceback.print_tb(exc_traceback, limit=1, file=sys.stdout) + traceback.print_tb(exc.__traceback__, limit=1, file=sys.stdout) print("*** print_exception:") - traceback.print_exception(exc_value, limit=2, file=sys.stdout) + traceback.print_exception(exc, limit=2, file=sys.stdout) print("*** print_exc:") traceback.print_exc(limit=2, file=sys.stdout) print("*** format_exc, first and last line:") @@ -456,12 +455,12 @@ exception and traceback: print(formatted_lines[0]) print(formatted_lines[-1]) print("*** format_exception:") - print(repr(traceback.format_exception(exc_value))) + print(repr(traceback.format_exception(exc))) print("*** extract_tb:") - print(repr(traceback.extract_tb(exc_traceback))) + print(repr(traceback.extract_tb(exc.__traceback__))) print("*** format_tb:") - print(repr(traceback.format_tb(exc_traceback))) - print("*** tb_lineno:", exc_traceback.tb_lineno) + print(repr(traceback.format_tb(exc.__traceback__))) + print("*** tb_lineno:", exc.__traceback__.tb_lineno) The output for the example would look similar to this: diff --git a/Doc/library/types.rst b/Doc/library/types.rst index cce0ad960edf97..415413c4cd9747 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -320,7 +320,7 @@ Standard names are defined for the following types: .. class:: TracebackType(tb_next, tb_frame, tb_lasti, tb_lineno) - The type of traceback objects such as found in ``sys.exc_info()[2]``. + The type of traceback objects such as found in ``sys.exception().__traceback__``. See :ref:`the language reference ` for details of the available attributes and operations, and guidance on creating tracebacks diff --git a/Doc/library/wsgiref.rst b/Doc/library/wsgiref.rst index 75dea466335163..39a4c1ba142338 100644 --- a/Doc/library/wsgiref.rst +++ b/Doc/library/wsgiref.rst @@ -674,7 +674,7 @@ input, output, and error streams. This method is a WSGI application to generate an error page for the user. It is only invoked if an error occurs before headers are sent to the client. - This method can access the current error information using ``sys.exc_info()``, + This method can access the current error using ``sys.exception()``, and should pass that information to *start_response* when calling it (as described in the "Error Handling" section of :pep:`3333`). diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index d5cb2899c231b4..52bb0b22b042f6 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -301,31 +301,28 @@ keeping all locals in that frame alive until the next garbage collection occurs. object: traceback Before an :keyword:`!except` clause's suite is executed, -details about the exception are -stored in the :mod:`sys` module and can be accessed via :func:`sys.exc_info`. -:func:`sys.exc_info` returns a 3-tuple consisting of the exception class, the -exception instance and a traceback object (see section :ref:`types`) identifying -the point in the program where the exception occurred. The details about the -exception accessed via :func:`sys.exc_info` are restored to their previous values -when leaving an exception handler:: - - >>> print(sys.exc_info()) - (None, None, None) +the exception is stored in the :mod:`sys` module, where it can be accessed +from within the body of the :keyword:`!except` clause by calling +:func:`sys.exception`. When leaving an exception handler, the exception +stored in the :mod:`sys` module is reset to its previous value:: + + >>> print(sys.exception()) + None >>> try: ... raise TypeError ... except: - ... print(sys.exc_info()) + ... print(repr(sys.exception())) ... try: ... raise ValueError ... except: - ... print(sys.exc_info()) - ... print(sys.exc_info()) + ... print(repr(sys.exception())) + ... print(repr(sys.exception())) ... - (, TypeError(), ) - (, ValueError(), ) - (, TypeError(), ) - >>> print(sys.exc_info()) - (None, None, None) + TypeError() + ValueError() + TypeError() + >>> print(sys.exception()) + None .. index:: diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index e4e471e50079ae..f447bbb1216d52 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1122,6 +1122,7 @@ Internal types single: exc_info (in module sys) single: last_traceback (in module sys) single: sys.exc_info + single: sys.exception single: sys.last_traceback Traceback objects represent a stack trace of an exception. A traceback object From 022b44f2546c44183e4df7b67e3e64502fae9143 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Mon, 20 Feb 2023 22:16:09 +0000 Subject: [PATCH 225/225] gh-102056: Fix a few bugs in error handling of exception printing code (#102078) --- Lib/test/test_threading.py | 31 +++++++++++++++++++ ...-02-20-15-18-33.gh-issue-102056.uHKuwH.rst | 1 + Python/pythonrun.c | 23 +++++++++----- 3 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-20-15-18-33.gh-issue-102056.uHKuwH.rst diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 7fea2d38673eff..a39a267b403d83 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1523,6 +1523,37 @@ def run(): self.assertEqual(out, b'') self.assertNotIn("Unhandled exception", err.decode()) + def test_print_exception_gh_102056(self): + # This used to crash. See gh-102056. + script = r"""if True: + import time + import threading + import _thread + + def f(): + try: + f() + except RecursionError: + f() + + def g(): + try: + raise ValueError() + except* ValueError: + f() + + def h(): + time.sleep(1) + _thread.interrupt_main() + + t = threading.Thread(target=h) + t.start() + g() + t.join() + """ + + assert_python_failure("-c", script) + def test_bare_raise_in_brand_new_thread(self): def bare_raise(): raise diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-20-15-18-33.gh-issue-102056.uHKuwH.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-20-15-18-33.gh-issue-102056.uHKuwH.rst new file mode 100644 index 00000000000000..78cd525b365fe5 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-20-15-18-33.gh-issue-102056.uHKuwH.rst @@ -0,0 +1 @@ +Fix error handling bugs in interpreter's exception printing code, which could cause a crash on infinite recursion. diff --git a/Python/pythonrun.c b/Python/pythonrun.c index ce993ea8796cb7..34a44dd92847ba 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1246,8 +1246,7 @@ print_chained(struct exception_print_context* ctx, PyObject *value, const char * message, const char *tag) { PyObject *f = ctx->file; - - if (_Py_EnterRecursiveCall(" in print_chained") < 0) { + if (_Py_EnterRecursiveCall(" in print_chained")) { return -1; } bool need_close = ctx->need_close; @@ -1374,7 +1373,9 @@ print_exception_group(struct exception_print_context *ctx, PyObject *value) if (ctx->exception_group_depth == 0) { ctx->exception_group_depth += 1; } - print_exception(ctx, value); + if (print_exception(ctx, value) < 0) { + return -1; + } PyObject *excs = ((PyBaseExceptionGroupObject *)value)->excs; assert(excs && PyTuple_Check(excs)); @@ -1424,7 +1425,7 @@ print_exception_group(struct exception_print_context *ctx, PyObject *value) PyObject *exc = PyTuple_GET_ITEM(excs, i); if (!truncated) { - if (_Py_EnterRecursiveCall(" in print_exception_group") != 0) { + if (_Py_EnterRecursiveCall(" in print_exception_group")) { return -1; } int res = print_exception_recursive(ctx, exc); @@ -1477,22 +1478,30 @@ print_exception_group(struct exception_print_context *ctx, PyObject *value) static int print_exception_recursive(struct exception_print_context *ctx, PyObject *value) { + if (_Py_EnterRecursiveCall(" in print_exception_recursive")) { + return -1; + } if (ctx->seen != NULL) { /* Exception chaining */ if (print_exception_cause_and_context(ctx, value) < 0) { - return -1; + goto error; } } if (!_PyBaseExceptionGroup_Check(value)) { if (print_exception(ctx, value) < 0) { - return -1; + goto error; } } else if (print_exception_group(ctx, value) < 0) { - return -1; + goto error; } assert(!PyErr_Occurred()); + + _Py_LeaveRecursiveCall(); return 0; +error: + _Py_LeaveRecursiveCall(); + return -1; } #define PyErr_MAX_GROUP_WIDTH 15

    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

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