diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 95133c1338b682..dc636715032969 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -13,9 +13,11 @@ on: - "Lib/test/libregrtest/**" - "Lib/tomllib/**" - "Misc/mypy/**" + - "Tools/build/check_extension_modules.py" - "Tools/build/compute-changes.py" - "Tools/build/deepfreeze.py" - "Tools/build/generate_sbom.py" + - "Tools/build/generate_stdlib_module_names.py" - "Tools/build/generate-build-details.py" - "Tools/build/verify_ensurepip_wheels.py" - "Tools/build/update_file.py" diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index ca4744789f1c23..d8fefa16faf70f 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -842,6 +842,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(call)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(call_exception_handler)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(call_soon)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(callable)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(callback)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(cancel)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(capath)); @@ -877,6 +878,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(command)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(comment_factory)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(compile_mode)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(config)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(consts)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(context)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(contravariant)); @@ -935,6 +937,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(errors)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(event)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(eventmask)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(exc)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(exc_type)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(exc_value)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(excepthook)); @@ -1006,6 +1009,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(identity_hint)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ignore)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(imag)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(implieslink)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(importlib)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(in_fd)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(include_aliases)); @@ -1055,6 +1059,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(kw)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(kw1)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(kw2)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(kwargs)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(kwdefaults)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(label)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last)); @@ -1171,6 +1176,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(pos1)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(pos2)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(posix)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(preserve_exc)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(print_file_and_line)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(priority)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(progress)); @@ -1198,9 +1204,12 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(reload)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(repl)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(replace)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(reqrefs)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(require_ready)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(reserved)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(reset)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(resetids)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(restrict)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(return)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(reverse)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(reversed)); @@ -1227,6 +1236,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(setsigmask)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(setstate)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(shape)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(shared)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(show_cmd)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(signed)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(size)); @@ -1297,6 +1307,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(uid)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(unlink)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(unraisablehook)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(updates)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(uri)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(usedforsecurity)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(value)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 47bb6fcecc1ba6..b9207f46cd729d 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -333,6 +333,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(call) STRUCT_FOR_ID(call_exception_handler) STRUCT_FOR_ID(call_soon) + STRUCT_FOR_ID(callable) STRUCT_FOR_ID(callback) STRUCT_FOR_ID(cancel) STRUCT_FOR_ID(capath) @@ -368,6 +369,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(command) STRUCT_FOR_ID(comment_factory) STRUCT_FOR_ID(compile_mode) + STRUCT_FOR_ID(config) STRUCT_FOR_ID(consts) STRUCT_FOR_ID(context) STRUCT_FOR_ID(contravariant) @@ -426,6 +428,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(errors) STRUCT_FOR_ID(event) STRUCT_FOR_ID(eventmask) + STRUCT_FOR_ID(exc) STRUCT_FOR_ID(exc_type) STRUCT_FOR_ID(exc_value) STRUCT_FOR_ID(excepthook) @@ -497,6 +500,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(identity_hint) STRUCT_FOR_ID(ignore) STRUCT_FOR_ID(imag) + STRUCT_FOR_ID(implieslink) STRUCT_FOR_ID(importlib) STRUCT_FOR_ID(in_fd) STRUCT_FOR_ID(include_aliases) @@ -546,6 +550,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(kw) STRUCT_FOR_ID(kw1) STRUCT_FOR_ID(kw2) + STRUCT_FOR_ID(kwargs) STRUCT_FOR_ID(kwdefaults) STRUCT_FOR_ID(label) STRUCT_FOR_ID(last) @@ -662,6 +667,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(pos1) STRUCT_FOR_ID(pos2) STRUCT_FOR_ID(posix) + STRUCT_FOR_ID(preserve_exc) STRUCT_FOR_ID(print_file_and_line) STRUCT_FOR_ID(priority) STRUCT_FOR_ID(progress) @@ -689,9 +695,12 @@ struct _Py_global_strings { STRUCT_FOR_ID(reload) STRUCT_FOR_ID(repl) STRUCT_FOR_ID(replace) + STRUCT_FOR_ID(reqrefs) + STRUCT_FOR_ID(require_ready) STRUCT_FOR_ID(reserved) STRUCT_FOR_ID(reset) STRUCT_FOR_ID(resetids) + STRUCT_FOR_ID(restrict) STRUCT_FOR_ID(return) STRUCT_FOR_ID(reverse) STRUCT_FOR_ID(reversed) @@ -718,6 +727,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(setsigmask) STRUCT_FOR_ID(setstate) STRUCT_FOR_ID(shape) + STRUCT_FOR_ID(shared) STRUCT_FOR_ID(show_cmd) STRUCT_FOR_ID(signed) STRUCT_FOR_ID(size) @@ -788,6 +798,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(uid) STRUCT_FOR_ID(unlink) STRUCT_FOR_ID(unraisablehook) + STRUCT_FOR_ID(updates) STRUCT_FOR_ID(uri) STRUCT_FOR_ID(usedforsecurity) STRUCT_FOR_ID(value) diff --git a/Include/internal/pycore_magic_number.h b/Include/internal/pycore_magic_number.h index a570ae684379a4..81bfd162c7ea80 100644 --- a/Include/internal/pycore_magic_number.h +++ b/Include/internal/pycore_magic_number.h @@ -277,6 +277,8 @@ Known values: Python 3.14a7 3622 (Store annotations in different class dict keys) Python 3.14a7 3623 (Add BUILD_INTERPOLATION & BUILD_TEMPLATE opcodes) Python 3.14b1 3624 (Don't optimize LOAD_FAST when local is killed by DELETE_FAST) + Python 3.14b3 3625 (Fix handling of opcodes that may leave operands on the stack when optimizing LOAD_FAST) + Python 3.14rc2 3626 (Fix missing exception handlers in logical expression) Python 3.15a0 3650 (Initial version) Python 3.15a1 3651 (Simplify LOAD_CONST) Python 3.15a1 3652 (Virtual iterators) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 70a54f5273e446..254b02be1334bd 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -840,6 +840,7 @@ extern "C" { INIT_ID(call), \ INIT_ID(call_exception_handler), \ INIT_ID(call_soon), \ + INIT_ID(callable), \ INIT_ID(callback), \ INIT_ID(cancel), \ INIT_ID(capath), \ @@ -875,6 +876,7 @@ extern "C" { INIT_ID(command), \ INIT_ID(comment_factory), \ INIT_ID(compile_mode), \ + INIT_ID(config), \ INIT_ID(consts), \ INIT_ID(context), \ INIT_ID(contravariant), \ @@ -933,6 +935,7 @@ extern "C" { INIT_ID(errors), \ INIT_ID(event), \ INIT_ID(eventmask), \ + INIT_ID(exc), \ INIT_ID(exc_type), \ INIT_ID(exc_value), \ INIT_ID(excepthook), \ @@ -1004,6 +1007,7 @@ extern "C" { INIT_ID(identity_hint), \ INIT_ID(ignore), \ INIT_ID(imag), \ + INIT_ID(implieslink), \ INIT_ID(importlib), \ INIT_ID(in_fd), \ INIT_ID(include_aliases), \ @@ -1053,6 +1057,7 @@ extern "C" { INIT_ID(kw), \ INIT_ID(kw1), \ INIT_ID(kw2), \ + INIT_ID(kwargs), \ INIT_ID(kwdefaults), \ INIT_ID(label), \ INIT_ID(last), \ @@ -1169,6 +1174,7 @@ extern "C" { INIT_ID(pos1), \ INIT_ID(pos2), \ INIT_ID(posix), \ + INIT_ID(preserve_exc), \ INIT_ID(print_file_and_line), \ INIT_ID(priority), \ INIT_ID(progress), \ @@ -1196,9 +1202,12 @@ extern "C" { INIT_ID(reload), \ INIT_ID(repl), \ INIT_ID(replace), \ + INIT_ID(reqrefs), \ + INIT_ID(require_ready), \ INIT_ID(reserved), \ INIT_ID(reset), \ INIT_ID(resetids), \ + INIT_ID(restrict), \ INIT_ID(return), \ INIT_ID(reverse), \ INIT_ID(reversed), \ @@ -1225,6 +1234,7 @@ extern "C" { INIT_ID(setsigmask), \ INIT_ID(setstate), \ INIT_ID(shape), \ + INIT_ID(shared), \ INIT_ID(show_cmd), \ INIT_ID(signed), \ INIT_ID(size), \ @@ -1295,6 +1305,7 @@ extern "C" { INIT_ID(uid), \ INIT_ID(unlink), \ INIT_ID(unraisablehook), \ + INIT_ID(updates), \ INIT_ID(uri), \ INIT_ID(usedforsecurity), \ INIT_ID(value), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 89444f4fb83b94..f1f6bc48fdf1a3 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -1120,6 +1120,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(callable); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(callback); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -1260,6 +1264,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(config); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(consts); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -1492,6 +1500,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(exc); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(exc_type); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -1776,6 +1788,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(implieslink); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(importlib); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -1972,6 +1988,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(kwargs); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(kwdefaults); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -2436,6 +2456,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(preserve_exc); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(print_file_and_line); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -2544,6 +2568,14 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(reqrefs); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(require_ready); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(reserved); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -2556,6 +2588,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(restrict); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(return); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -2660,6 +2696,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(shared); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(show_cmd); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -2940,6 +2980,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(updates); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(uri); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 83ea623dce6281..d29f1615f276d2 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1283,6 +1283,10 @@ def _add_slots(cls, is_frozen, weakref_slot, defined_fields): if '__slots__' in cls.__dict__: raise TypeError(f'{cls.__name__} already specifies __slots__') + # gh-102069: Remove existing __weakref__ descriptor. + # gh-135228: Make sure the original class can be garbage collected. + sys._clear_type_descriptors(cls) + # Create a new dict for our new class. cls_dict = dict(cls.__dict__) field_names = tuple(f.name for f in fields(cls)) @@ -1300,12 +1304,6 @@ def _add_slots(cls, is_frozen, weakref_slot, defined_fields): # available in _MARKER. cls_dict.pop(field_name, None) - # Remove __dict__ itself. - cls_dict.pop('__dict__', None) - - # Clear existing `__weakref__` descriptor, it belongs to a previous type: - cls_dict.pop('__weakref__', None) # gh-102069 - # And finally create the class. qualname = getattr(cls, '__qualname__', None) newcls = type(cls)(cls.__name__, cls.__bases__, cls_dict) diff --git a/Lib/test/test__interpreters.py b/Lib/test/test__interpreters.py index a32d5d81d2bf2d..ec5a26bc1c05ad 100644 --- a/Lib/test/test__interpreters.py +++ b/Lib/test/test__interpreters.py @@ -474,19 +474,19 @@ def setUp(self): def test_signatures(self): # See https://github.com/python/cpython/issues/126654 - msg = r'_interpreters.exec\(\) argument 3 must be dict, not int' + msg = r"exec\(\) argument 'shared' must be dict, not int" with self.assertRaisesRegex(TypeError, msg): _interpreters.exec(self.id, 'a', 1) with self.assertRaisesRegex(TypeError, msg): _interpreters.exec(self.id, 'a', shared=1) - msg = r'_interpreters.run_string\(\) argument 3 must be dict, not int' + msg = r"run_string\(\) argument 'shared' must be dict, not int" with self.assertRaisesRegex(TypeError, msg): _interpreters.run_string(self.id, 'a', shared=1) - msg = r'_interpreters.run_func\(\) argument 3 must be dict, not int' + msg = r"run_func\(\) argument 'shared' must be dict, not int" with self.assertRaisesRegex(TypeError, msg): _interpreters.run_func(self.id, lambda: None, shared=1) # See https://github.com/python/cpython/issues/135855 - msg = r'_interpreters.set___main___attrs\(\) argument 2 must be dict, not int' + msg = r"set___main___attrs\(\) argument 'updates' must be dict, not int" with self.assertRaisesRegex(TypeError, msg): _interpreters.set___main___attrs(self.id, 1) diff --git a/Lib/test/test_dataclasses/__init__.py b/Lib/test/test_dataclasses/__init__.py index e98a8f284cec9f..6bf5e5b3e5554b 100644 --- a/Lib/test/test_dataclasses/__init__.py +++ b/Lib/test/test_dataclasses/__init__.py @@ -3804,6 +3804,41 @@ class WithCorrectSuper(CorrectSuper): # that we create internally. self.assertEqual(CorrectSuper.args, ["default", "default"]) + def test_original_class_is_gced(self): + # gh-135228: Make sure when we replace the class with slots=True, the original class + # gets garbage collected. + def make_simple(): + @dataclass(slots=True) + class SlotsTest: + pass + + return SlotsTest + + def make_with_annotations(): + @dataclass(slots=True) + class SlotsTest: + x: int + + return SlotsTest + + def make_with_annotations_and_method(): + @dataclass(slots=True) + class SlotsTest: + x: int + + def method(self) -> int: + return self.x + + return SlotsTest + + for make in (make_simple, make_with_annotations, make_with_annotations_and_method): + with self.subTest(make=make): + C = make() + support.gc_collect() + candidates = [cls for cls in object.__subclasses__() if cls.__name__ == 'SlotsTest' + and cls.__firstlineno__ == make.__code__.co_firstlineno + 1] + self.assertEqual(candidates, [C]) + class TestDescriptors(unittest.TestCase): def test_set_name(self): diff --git a/Misc/NEWS.d/next/Library/2025-07-20-16-56-55.gh-issue-135228.n_XIao.rst b/Misc/NEWS.d/next/Library/2025-07-20-16-56-55.gh-issue-135228.n_XIao.rst new file mode 100644 index 00000000000000..517a37feb37ae6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-07-20-16-56-55.gh-issue-135228.n_XIao.rst @@ -0,0 +1,4 @@ +When :mod:`dataclasses` replaces a class with a slotted dataclass, the +original class can now be garbage collected again. Earlier changes in Python +3.14 caused this class to always remain in existence together with the replacement +class synthesized by :mod:`dataclasses`. diff --git a/Misc/NEWS.d/next/Library/2025-08-11-05-05-08.gh-issue-137630.9lmqyc.rst b/Misc/NEWS.d/next/Library/2025-08-11-05-05-08.gh-issue-137630.9lmqyc.rst new file mode 100644 index 00000000000000..94d836c17834fd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-08-11-05-05-08.gh-issue-137630.9lmqyc.rst @@ -0,0 +1,2 @@ +The :mod:`!_interpreters` module now uses Argument Clinic to parse arguments. +Patch by Adam Turner. diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index faf3b25b68c4eb..c7a3d54a0b9a52 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -20,12 +20,18 @@ #include "_interpreters_common.h" +#include "clinic/_interpretersmodule.c.h" #define MODULE_NAME _interpreters #define MODULE_NAME_STR Py_STRINGIFY(MODULE_NAME) #define MODINIT_FUNC_NAME RESOLVE_MODINIT_FUNC_NAME(MODULE_NAME) +/*[clinic input] +module _interpreters +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=bfd967980a0de892]*/ + static PyInterpreterState * _get_current_interp(void) { @@ -797,12 +803,12 @@ get_summary(PyInterpreterState *interp) } +// Not converted to Argument Clinic because the function uses ``**kwargs``. static PyObject * interp_new_config(PyObject *self, PyObject *args, PyObject *kwds) { const char *name = NULL; - if (!PyArg_ParseTuple(args, "|s:" MODULE_NAME_STR ".new_config", - &name)) + if (!PyArg_ParseTuple(args, "|s:" MODULE_NAME_STR ".new_config", &name)) { return NULL; } @@ -830,7 +836,8 @@ interp_new_config(PyObject *self, PyObject *args, PyObject *kwds) } PyDoc_STRVAR(new_config_doc, -"new_config(name='isolated', /, **overrides) -> type.SimpleNamespace\n\ +"new_config($module, name='isolated', /, **overrides)\n\ +--\n\ \n\ Return a representation of a new PyInterpreterConfig.\n\ \n\ @@ -841,17 +848,28 @@ Any keyword arguments are set on the corresponding config fields,\n\ overriding the initial values."); +/*[clinic input] +_interpreters.create + config as configobj: object(py_default="'isolated'") = NULL + * + reqrefs: bool = False + +Create a new interpreter and return a unique generated ID. + +The caller is responsible for destroying the interpreter before exiting, +typically by using _interpreters.destroy(). This can be managed +automatically by passing "reqrefs=True" and then using _incref() and +_decref() appropriately. + +"config" must be a valid interpreter config or the name of a +predefined config ('isolated' or 'legacy'). The default +is 'isolated'. +[clinic start generated code]*/ + static PyObject * -interp_create(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_create_impl(PyObject *module, PyObject *configobj, int reqrefs) +/*[clinic end generated code: output=c1cc6835b1277c16 input=235ce396a23624d5]*/ { - static char *kwlist[] = {"config", "reqrefs", NULL}; - PyObject *configobj = NULL; - int reqrefs = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O$p:create", kwlist, - &configobj, &reqrefs)) { - return NULL; - } - PyInterpreterConfig config; if (config_from_object(configobj, &config) < 0) { return NULL; @@ -885,34 +903,22 @@ interp_create(PyObject *self, PyObject *args, PyObject *kwds) } -PyDoc_STRVAR(create_doc, -"create([config], *, reqrefs=False) -> ID\n\ -\n\ -Create a new interpreter and return a unique generated ID.\n\ -\n\ -The caller is responsible for destroying the interpreter before exiting,\n\ -typically by using _interpreters.destroy(). This can be managed \n\ -automatically by passing \"reqrefs=True\" and then using _incref() and\n\ -_decref() appropriately.\n\ -\n\ -\"config\" must be a valid interpreter config or the name of a\n\ -predefined config (\"isolated\" or \"legacy\"). The default\n\ -is \"isolated\"."); +/*[clinic input] +_interpreters.destroy + id: object + * + restrict as restricted: bool = False + +Destroy the identified interpreter. +Attempting to destroy the current interpreter raises InterpreterError. +So does an unrecognized ID. +[clinic start generated code]*/ static PyObject * -interp_destroy(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_destroy_impl(PyObject *module, PyObject *id, int restricted) +/*[clinic end generated code: output=0bc20da8700ab4dd input=561bdd6537639d40]*/ { - static char *kwlist[] = {"id", "restrict", NULL}; - PyObject *id; - int restricted = 0; - // XXX Use "L" for id? - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O|$p:destroy", kwlist, &id, &restricted)) - { - return NULL; - } - // Look up the interpreter. int reqready = 0; PyInterpreterState *interp = \ @@ -946,27 +952,19 @@ interp_destroy(PyObject *self, PyObject *args, PyObject *kwds) Py_RETURN_NONE; } -PyDoc_STRVAR(destroy_doc, -"destroy(id, *, restrict=False)\n\ -\n\ -Destroy the identified interpreter.\n\ -\n\ -Attempting to destroy the current interpreter raises InterpreterError.\n\ -So does an unrecognized ID."); +/*[clinic input] +_interpreters.list_all + * + require_ready as reqready: bool = False + +Return a list containing the ID of every existing interpreter. +[clinic start generated code]*/ static PyObject * -interp_list_all(PyObject *self, PyObject *args, PyObject *kwargs) +_interpreters_list_all_impl(PyObject *module, int reqready) +/*[clinic end generated code: output=3f21c1a7c78043c0 input=35bae91c381a2cf9]*/ { - static char *kwlist[] = {"require_ready", NULL}; - int reqready = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, - "|$p:" MODULE_NAME_STR ".list_all", - kwlist, &reqready)) - { - return NULL; - } - PyObject *ids = PyList_New(0); if (ids == NULL) { return NULL; @@ -995,14 +993,16 @@ interp_list_all(PyObject *self, PyObject *args, PyObject *kwargs) return ids; } -PyDoc_STRVAR(list_all_doc, -"list_all() -> [(ID, whence)]\n\ -\n\ -Return a list containing the ID of every existing interpreter."); +/*[clinic input] +_interpreters.get_current + +Return (ID, whence) of the current interpreter. +[clinic start generated code]*/ static PyObject * -interp_get_current(PyObject *self, PyObject *Py_UNUSED(ignored)) +_interpreters_get_current_impl(PyObject *module) +/*[clinic end generated code: output=03161c8fcc0136eb input=37fb2c067c14d543]*/ { PyInterpreterState *interp =_get_current_interp(); if (interp == NULL) { @@ -1012,39 +1012,38 @@ interp_get_current(PyObject *self, PyObject *Py_UNUSED(ignored)) return get_summary(interp); } -PyDoc_STRVAR(get_current_doc, -"get_current() -> (ID, whence)\n\ -\n\ -Return the ID of current interpreter."); +/*[clinic input] +_interpreters.get_main + +Return (ID, whence) of the main interpreter. +[clinic start generated code]*/ static PyObject * -interp_get_main(PyObject *self, PyObject *Py_UNUSED(ignored)) +_interpreters_get_main_impl(PyObject *module) +/*[clinic end generated code: output=9647288aff735557 input=b4ace23ca562146f]*/ { PyInterpreterState *interp = _PyInterpreterState_Main(); assert(_PyInterpreterState_IsReady(interp)); return get_summary(interp); } -PyDoc_STRVAR(get_main_doc, -"get_main() -> (ID, whence)\n\ -\n\ -Return the ID of main interpreter."); +/*[clinic input] +_interpreters.set___main___attrs + id: object + updates: object(subclass_of='&PyDict_Type') + * + restrict as restricted: bool = False + +Bind the given attributes in the interpreter's __main__ module. +[clinic start generated code]*/ static PyObject * -interp_set___main___attrs(PyObject *self, PyObject *args, PyObject *kwargs) +_interpreters_set___main___attrs_impl(PyObject *module, PyObject *id, + PyObject *updates, int restricted) +/*[clinic end generated code: output=f3803010cb452bf0 input=d16ab8d81371f86a]*/ { - static char *kwlist[] = {"id", "updates", "restrict", NULL}; - PyObject *id, *updates; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, - "OO!|$p:" MODULE_NAME_STR ".set___main___attrs", - kwlist, &id, &PyDict_Type, &updates, &restricted)) - { - return NULL; - } - // Look up the interpreter. int reqready = 1; PyInterpreterState *interp = \ @@ -1091,11 +1090,6 @@ interp_set___main___attrs(PyObject *self, PyObject *args, PyObject *kwargs) Py_RETURN_NONE; } -PyDoc_STRVAR(set___main___attrs_doc, -"set___main___attrs(id, ns, *, restrict=False)\n\ -\n\ -Bind the given attributes in the interpreter's __main__ module."); - static PyObject * _handle_script_error(struct run_result *runres) @@ -1109,23 +1103,36 @@ _handle_script_error(struct run_result *runres) return runres->excinfo; } +/*[clinic input] +_interpreters.exec + id: object + code: object + shared: object(subclass_of='&PyDict_Type', c_default='NULL') = {} + * + restrict as restricted: bool = False + +Execute the provided code in the identified interpreter. + +This is equivalent to running the builtin exec() under the target +interpreter, using the __dict__ of its __main__ module as both +globals and locals. + +"code" may be a string containing the text of a Python script. + +Functions (and code objects) are also supported, with some restrictions. +The code/function must not take any arguments or be a closure +(i.e. have cell vars). Methods and other callables are not supported. + +If a function is provided, its code object is used and all its state +is ignored, including its __globals__ dict. +[clinic start generated code]*/ + static PyObject * -interp_exec(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_exec_impl(PyObject *module, PyObject *id, PyObject *code, + PyObject *shared, int restricted) +/*[clinic end generated code: output=492057c4f10dc304 input=5a22c1ed0c5dbcf3]*/ { -#define FUNCNAME MODULE_NAME_STR ".exec" PyThreadState *tstate = _PyThreadState_GET(); - static char *kwlist[] = {"id", "code", "shared", "restrict", NULL}; - PyObject *id, *code; - PyObject *shared = NULL; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "OO|O!$p:" FUNCNAME, kwlist, - &id, &code, &PyDict_Type, &shared, - &restricted)) - { - return NULL; - } - int reqready = 1; PyInterpreterState *interp = \ resolve_interp(id, restricted, reqready, "exec code for"); @@ -1150,43 +1157,29 @@ interp_exec(PyObject *self, PyObject *args, PyObject *kwds) } assert(runres.result == NULL); Py_RETURN_NONE; -#undef FUNCNAME } -PyDoc_STRVAR(exec_doc, -"exec(id, code, shared=None, *, restrict=False)\n\ -\n\ -Execute the provided code in the identified interpreter.\n\ -This is equivalent to running the builtin exec() under the target\n\ -interpreter, using the __dict__ of its __main__ module as both\n\ -globals and locals.\n\ -\n\ -\"code\" may be a string containing the text of a Python script.\n\ -\n\ -Functions (and code objects) are also supported, with some restrictions.\n\ -The code/function must not take any arguments or be a closure\n\ -(i.e. have cell vars). Methods and other callables are not supported.\n\ -\n\ -If a function is provided, its code object is used and all its state\n\ -is ignored, including its __globals__ dict."); +/*[clinic input] +_interpreters.run_string + id: object + script: unicode + shared: object(subclass_of='&PyDict_Type', c_default='NULL') = {} + * + restrict as restricted: bool = False + +Execute the provided string in the identified interpreter. + +(See _interpreters.exec().) +[clinic start generated code]*/ static PyObject * -interp_run_string(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_run_string_impl(PyObject *module, PyObject *id, + PyObject *script, PyObject *shared, + int restricted) +/*[clinic end generated code: output=a30a64fb9ad396a2 input=51ce549b9a8dbe21]*/ { #define FUNCNAME MODULE_NAME_STR ".run_string" PyThreadState *tstate = _PyThreadState_GET(); - static char *kwlist[] = {"id", "script", "shared", "restrict", NULL}; - PyObject *id, *script; - PyObject *shared = NULL; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "OU|O!$p:" FUNCNAME, kwlist, - &id, &script, &PyDict_Type, &shared, - &restricted)) - { - return NULL; - } - int reqready = 1; PyInterpreterState *interp = \ resolve_interp(id, restricted, reqready, "run a string in"); @@ -1217,30 +1210,29 @@ interp_run_string(PyObject *self, PyObject *args, PyObject *kwds) #undef FUNCNAME } -PyDoc_STRVAR(run_string_doc, -"run_string(id, script, shared=None, *, restrict=False)\n\ -\n\ -Execute the provided string in the identified interpreter.\n\ -\n\ -(See " MODULE_NAME_STR ".exec()."); +/*[clinic input] +_interpreters.run_func + id: object + func: object + shared: object(subclass_of='&PyDict_Type', c_default='NULL') = {} + * + restrict as restricted: bool = False + +Execute the body of the provided function in the identified interpreter. + +Code objects are also supported. In both cases, closures and args +are not supported. Methods and other callables are not supported either. + +(See _interpreters.exec().) +[clinic start generated code]*/ static PyObject * -interp_run_func(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_run_func_impl(PyObject *module, PyObject *id, PyObject *func, + PyObject *shared, int restricted) +/*[clinic end generated code: output=131f7202ca4a0c5e input=2d62bb9b9eaf4948]*/ { #define FUNCNAME MODULE_NAME_STR ".run_func" PyThreadState *tstate = _PyThreadState_GET(); - static char *kwlist[] = {"id", "func", "shared", "restrict", NULL}; - PyObject *id, *func; - PyObject *shared = NULL; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "OO|O!$p:" FUNCNAME, kwlist, - &id, &func, &PyDict_Type, &shared, - &restricted)) - { - return NULL; - } - int reqready = 1; PyInterpreterState *interp = \ resolve_interp(id, restricted, reqready, "run a function in"); @@ -1280,37 +1272,28 @@ interp_run_func(PyObject *self, PyObject *args, PyObject *kwds) #undef FUNCNAME } -PyDoc_STRVAR(run_func_doc, -"run_func(id, func, shared=None, *, restrict=False)\n\ -\n\ -Execute the body of the provided function in the identified interpreter.\n\ -Code objects are also supported. In both cases, closures and args\n\ -are not supported. Methods and other callables are not supported either.\n\ -\n\ -(See " MODULE_NAME_STR ".exec()."); +/*[clinic input] +_interpreters.call + id: object + callable: object + args as args_obj: object(subclass_of='&PyTuple_Type', c_default='NULL') = () + kwargs as kwargs_obj: object(subclass_of='&PyDict_Type', c_default='NULL') = {} + * + preserve_exc: bool = False + restrict as restricted: bool = False + +Call the provided object in the identified interpreter. + +Pass the given args and kwargs, if possible. +[clinic start generated code]*/ static PyObject * -interp_call(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_call_impl(PyObject *module, PyObject *id, PyObject *callable, + PyObject *args_obj, PyObject *kwargs_obj, + int preserve_exc, int restricted) +/*[clinic end generated code: output=983ee27b3c43f6ef input=77590fdb3f519d65]*/ { -#define FUNCNAME MODULE_NAME_STR ".call" PyThreadState *tstate = _PyThreadState_GET(); - static char *kwlist[] = {"id", "callable", "args", "kwargs", - "preserve_exc", "restrict", NULL}; - PyObject *id, *callable; - PyObject *args_obj = NULL; - PyObject *kwargs_obj = NULL; - int preserve_exc = 0; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "OO|O!O!$pp:" FUNCNAME, kwlist, - &id, &callable, - &PyTuple_Type, &args_obj, - &PyDict_Type, &kwargs_obj, - &preserve_exc, &restricted)) - { - return NULL; - } - int reqready = 1; PyInterpreterState *interp = \ resolve_interp(id, restricted, reqready, "make a call in"); @@ -1341,26 +1324,20 @@ interp_call(PyObject *self, PyObject *args, PyObject *kwds) _interp_call_clear(&call); _run_result_clear(&runres); return res_and_exc; -#undef FUNCNAME } -PyDoc_STRVAR(call_doc, -"call(id, callable, args=None, kwargs=None, *, restrict=False)\n\ -\n\ -Call the provided object in the identified interpreter.\n\ -Pass the given args and kwargs, if possible."); +/*[clinic input] +_interpreters.is_shareable + obj: object + +Return True if the object's data may be shared between interpreters and False otherwise. +[clinic start generated code]*/ static PyObject * -object_is_shareable(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_is_shareable_impl(PyObject *module, PyObject *obj) +/*[clinic end generated code: output=227856926a22940b input=72b9a36bdf1d2a53]*/ { - static char *kwlist[] = {"obj", NULL}; - PyObject *obj; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O:is_shareable", kwlist, &obj)) { - return NULL; - } - PyThreadState *tstate = _PyThreadState_GET(); if (_PyObject_CheckXIData(tstate, obj) == 0) { Py_RETURN_TRUE; @@ -1369,26 +1346,20 @@ object_is_shareable(PyObject *self, PyObject *args, PyObject *kwds) Py_RETURN_FALSE; } -PyDoc_STRVAR(is_shareable_doc, -"is_shareable(obj) -> bool\n\ -\n\ -Return True if the object's data may be shared between interpreters and\n\ -False otherwise."); +/*[clinic input] +_interpreters.is_running + id: object + * + restrict as restricted: bool = False + +Return whether or not the identified interpreter is running. +[clinic start generated code]*/ static PyObject * -interp_is_running(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_is_running_impl(PyObject *module, PyObject *id, int restricted) +/*[clinic end generated code: output=32a6225d5ded9bdb input=3291578d04231125]*/ { - static char *kwlist[] = {"id", "restrict", NULL}; - PyObject *id; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O|$p:is_running", kwlist, - &id, &restricted)) - { - return NULL; - } - int reqready = 1; PyInterpreterState *interp = \ resolve_interp(id, restricted, reqready, "check if running for"); @@ -1402,24 +1373,21 @@ interp_is_running(PyObject *self, PyObject *args, PyObject *kwds) Py_RETURN_FALSE; } -PyDoc_STRVAR(is_running_doc, -"is_running(id, *, restrict=False) -> bool\n\ -\n\ -Return whether or not the identified interpreter is running."); +/*[clinic input] +_interpreters.get_config + id as idobj: object + * + restrict as restricted: bool = False + +Return a representation of the config used to initialize the interpreter. +[clinic start generated code]*/ static PyObject * -interp_get_config(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_get_config_impl(PyObject *module, PyObject *idobj, + int restricted) +/*[clinic end generated code: output=63f81d35c2fe1387 input=aa38d50f534eb3c5]*/ { - static char *kwlist[] = {"id", "restrict", NULL}; - PyObject *idobj = NULL; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O|$p:get_config", kwlist, - &idobj, &restricted)) - { - return NULL; - } if (idobj == Py_None) { idobj = NULL; } @@ -1445,23 +1413,18 @@ interp_get_config(PyObject *self, PyObject *args, PyObject *kwds) return configobj; } -PyDoc_STRVAR(get_config_doc, -"get_config(id, *, restrict=False) -> types.SimpleNamespace\n\ -\n\ -Return a representation of the config used to initialize the interpreter."); +/*[clinic input] +_interpreters.whence + id: object + +Return an identifier for where the interpreter was created. +[clinic start generated code]*/ static PyObject * -interp_whence(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_whence_impl(PyObject *module, PyObject *id) +/*[clinic end generated code: output=ef2c21ab106c2c20 input=eeede0a2fbfa2968]*/ { - static char *kwlist[] = {"id", NULL}; - PyObject *id; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O:whence", kwlist, &id)) - { - return NULL; - } - PyInterpreterState *interp = look_up_interp(id); if (interp == NULL) { return NULL; @@ -1471,26 +1434,21 @@ interp_whence(PyObject *self, PyObject *args, PyObject *kwds) return PyLong_FromLong(whence); } -PyDoc_STRVAR(whence_doc, -"whence(id) -> int\n\ -\n\ -Return an identifier for where the interpreter was created."); +/*[clinic input] +_interpreters.incref + id: object + * + implieslink: bool = False + restrict as restricted: bool = False + +[clinic start generated code]*/ static PyObject * -interp_incref(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_incref_impl(PyObject *module, PyObject *id, int implieslink, + int restricted) +/*[clinic end generated code: output=eccaa4e03fbe8ee2 input=a0a614748f2e348c]*/ { - static char *kwlist[] = {"id", "implieslink", "restrict", NULL}; - PyObject *id; - int implieslink = 0; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O|$pp:incref", kwlist, - &id, &implieslink, &restricted)) - { - return NULL; - } - int reqready = 1; PyInterpreterState *interp = \ resolve_interp(id, restricted, reqready, "incref"); @@ -1508,18 +1466,18 @@ interp_incref(PyObject *self, PyObject *args, PyObject *kwds) } +/*[clinic input] +_interpreters.decref + id: object + * + restrict as restricted: bool = False + +[clinic start generated code]*/ + static PyObject * -interp_decref(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_decref_impl(PyObject *module, PyObject *id, int restricted) +/*[clinic end generated code: output=5c54db4b22086171 input=c4aa34f09c44e62a]*/ { - static char *kwlist[] = {"id", "restrict", NULL}; - PyObject *id; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O|$p:decref", kwlist, &id, &restricted)) - { - return NULL; - } - int reqready = 1; PyInterpreterState *interp = \ resolve_interp(id, restricted, reqready, "decref"); @@ -1533,18 +1491,20 @@ interp_decref(PyObject *self, PyObject *args, PyObject *kwds) } +/*[clinic input] +_interpreters.capture_exception + exc as exc_arg: object = None + +Return a snapshot of an exception. + +If "exc" is None then the current exception, if any, is used (but not cleared). +The returned snapshot is the same as what _interpreters.exec() returns. +[clinic start generated code]*/ + static PyObject * -capture_exception(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_capture_exception_impl(PyObject *module, PyObject *exc_arg) +/*[clinic end generated code: output=ef3f5393ef9c88a6 input=32045341e979bc9e]*/ { - static char *kwlist[] = {"exc", NULL}; - PyObject *exc_arg = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "|O:capture_exception", kwlist, - &exc_arg)) - { - return NULL; - } - PyObject *exc = exc_arg; if (exc == NULL || exc == Py_None) { exc = PyErr_GetRaisedException(); @@ -1592,58 +1552,33 @@ capture_exception(PyObject *self, PyObject *args, PyObject *kwds) return captured; } -PyDoc_STRVAR(capture_exception_doc, -"capture_exception(exc=None) -> types.SimpleNamespace\n\ -\n\ -Return a snapshot of an exception. If \"exc\" is None\n\ -then the current exception, if any, is used (but not cleared).\n\ -\n\ -The returned snapshot is the same as what _interpreters.exec() returns."); - static PyMethodDef module_functions[] = { {"new_config", _PyCFunction_CAST(interp_new_config), METH_VARARGS | METH_KEYWORDS, new_config_doc}, - {"create", _PyCFunction_CAST(interp_create), - METH_VARARGS | METH_KEYWORDS, create_doc}, - {"destroy", _PyCFunction_CAST(interp_destroy), - METH_VARARGS | METH_KEYWORDS, destroy_doc}, - {"list_all", _PyCFunction_CAST(interp_list_all), - METH_VARARGS | METH_KEYWORDS, list_all_doc}, - {"get_current", interp_get_current, - 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}, - {"get_config", _PyCFunction_CAST(interp_get_config), - METH_VARARGS | METH_KEYWORDS, get_config_doc}, - {"whence", _PyCFunction_CAST(interp_whence), - METH_VARARGS | METH_KEYWORDS, whence_doc}, - {"exec", _PyCFunction_CAST(interp_exec), - METH_VARARGS | METH_KEYWORDS, exec_doc}, - {"call", _PyCFunction_CAST(interp_call), - METH_VARARGS | METH_KEYWORDS, call_doc}, - {"run_string", _PyCFunction_CAST(interp_run_string), - METH_VARARGS | METH_KEYWORDS, run_string_doc}, - {"run_func", _PyCFunction_CAST(interp_run_func), - METH_VARARGS | METH_KEYWORDS, run_func_doc}, - - {"set___main___attrs", _PyCFunction_CAST(interp_set___main___attrs), - METH_VARARGS | METH_KEYWORDS, set___main___attrs_doc}, - - {"incref", _PyCFunction_CAST(interp_incref), - METH_VARARGS | METH_KEYWORDS, NULL}, - {"decref", _PyCFunction_CAST(interp_decref), - METH_VARARGS | METH_KEYWORDS, NULL}, - - {"is_shareable", _PyCFunction_CAST(object_is_shareable), - METH_VARARGS | METH_KEYWORDS, is_shareable_doc}, - - {"capture_exception", _PyCFunction_CAST(capture_exception), - METH_VARARGS | METH_KEYWORDS, capture_exception_doc}, + _INTERPRETERS_CREATE_METHODDEF + _INTERPRETERS_DESTROY_METHODDEF + _INTERPRETERS_LIST_ALL_METHODDEF + _INTERPRETERS_GET_CURRENT_METHODDEF + _INTERPRETERS_GET_MAIN_METHODDEF + + _INTERPRETERS_IS_RUNNING_METHODDEF + _INTERPRETERS_GET_CONFIG_METHODDEF + _INTERPRETERS_WHENCE_METHODDEF + _INTERPRETERS_EXEC_METHODDEF + _INTERPRETERS_CALL_METHODDEF + _INTERPRETERS_RUN_STRING_METHODDEF + _INTERPRETERS_RUN_FUNC_METHODDEF + + _INTERPRETERS_SET___MAIN___ATTRS_METHODDEF + + _INTERPRETERS_INCREF_METHODDEF + _INTERPRETERS_DECREF_METHODDEF + + _INTERPRETERS_IS_SHAREABLE_METHODDEF + + _INTERPRETERS_CAPTURE_EXCEPTION_METHODDEF {NULL, NULL} /* sentinel */ }; diff --git a/Modules/clinic/_interpretersmodule.c.h b/Modules/clinic/_interpretersmodule.c.h new file mode 100644 index 00000000000000..a8f0d3df8908dc --- /dev/null +++ b/Modules/clinic/_interpretersmodule.c.h @@ -0,0 +1,1202 @@ +/*[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 +#include "pycore_modsupport.h" // _PyArg_UnpackKeywords() + +PyDoc_STRVAR(_interpreters_create__doc__, +"create($module, /, config=\'isolated\', *, reqrefs=False)\n" +"--\n" +"\n" +"Create a new interpreter and return a unique generated ID.\n" +"\n" +"The caller is responsible for destroying the interpreter before exiting,\n" +"typically by using _interpreters.destroy(). This can be managed\n" +"automatically by passing \"reqrefs=True\" and then using _incref() and\n" +"_decref() appropriately.\n" +"\n" +"\"config\" must be a valid interpreter config or the name of a\n" +"predefined config (\'isolated\' or \'legacy\'). The default\n" +"is \'isolated\'."); + +#define _INTERPRETERS_CREATE_METHODDEF \ + {"create", _PyCFunction_CAST(_interpreters_create), METH_FASTCALL|METH_KEYWORDS, _interpreters_create__doc__}, + +static PyObject * +_interpreters_create_impl(PyObject *module, PyObject *configobj, int reqrefs); + +static PyObject * +_interpreters_create(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 + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(config), &_Py_ID(reqrefs), }, + }; + #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[] = {"config", "reqrefs", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "create", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *configobj = NULL; + int reqrefs = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + configobj = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + reqrefs = PyObject_IsTrue(args[1]); + if (reqrefs < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_create_impl(module, configobj, reqrefs); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_destroy__doc__, +"destroy($module, /, id, *, restrict=False)\n" +"--\n" +"\n" +"Destroy the identified interpreter.\n" +"\n" +"Attempting to destroy the current interpreter raises InterpreterError.\n" +"So does an unrecognized ID."); + +#define _INTERPRETERS_DESTROY_METHODDEF \ + {"destroy", _PyCFunction_CAST(_interpreters_destroy), METH_FASTCALL|METH_KEYWORDS, _interpreters_destroy__doc__}, + +static PyObject * +_interpreters_destroy_impl(PyObject *module, PyObject *id, int restricted); + +static PyObject * +_interpreters_destroy(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 + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(restrict), }, + }; + #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[] = {"id", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "destroy", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *id; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + if (!noptargs) { + goto skip_optional_kwonly; + } + restricted = PyObject_IsTrue(args[1]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_destroy_impl(module, id, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_list_all__doc__, +"list_all($module, /, *, require_ready=False)\n" +"--\n" +"\n" +"Return a list containing the ID of every existing interpreter."); + +#define _INTERPRETERS_LIST_ALL_METHODDEF \ + {"list_all", _PyCFunction_CAST(_interpreters_list_all), METH_FASTCALL|METH_KEYWORDS, _interpreters_list_all__doc__}, + +static PyObject * +_interpreters_list_all_impl(PyObject *module, int reqready); + +static PyObject * +_interpreters_list_all(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 + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(require_ready), }, + }; + #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[] = {"require_ready", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "list_all", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int reqready = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + reqready = PyObject_IsTrue(args[0]); + if (reqready < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_list_all_impl(module, reqready); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_get_current__doc__, +"get_current($module, /)\n" +"--\n" +"\n" +"Return (ID, whence) of the current interpreter."); + +#define _INTERPRETERS_GET_CURRENT_METHODDEF \ + {"get_current", (PyCFunction)_interpreters_get_current, METH_NOARGS, _interpreters_get_current__doc__}, + +static PyObject * +_interpreters_get_current_impl(PyObject *module); + +static PyObject * +_interpreters_get_current(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return _interpreters_get_current_impl(module); +} + +PyDoc_STRVAR(_interpreters_get_main__doc__, +"get_main($module, /)\n" +"--\n" +"\n" +"Return (ID, whence) of the main interpreter."); + +#define _INTERPRETERS_GET_MAIN_METHODDEF \ + {"get_main", (PyCFunction)_interpreters_get_main, METH_NOARGS, _interpreters_get_main__doc__}, + +static PyObject * +_interpreters_get_main_impl(PyObject *module); + +static PyObject * +_interpreters_get_main(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return _interpreters_get_main_impl(module); +} + +PyDoc_STRVAR(_interpreters_set___main___attrs__doc__, +"set___main___attrs($module, /, id, updates, *, restrict=False)\n" +"--\n" +"\n" +"Bind the given attributes in the interpreter\'s __main__ module."); + +#define _INTERPRETERS_SET___MAIN___ATTRS_METHODDEF \ + {"set___main___attrs", _PyCFunction_CAST(_interpreters_set___main___attrs), METH_FASTCALL|METH_KEYWORDS, _interpreters_set___main___attrs__doc__}, + +static PyObject * +_interpreters_set___main___attrs_impl(PyObject *module, PyObject *id, + PyObject *updates, int restricted); + +static PyObject * +_interpreters_set___main___attrs(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 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(updates), &_Py_ID(restrict), }, + }; + #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[] = {"id", "updates", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "set___main___attrs", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *id; + PyObject *updates; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + if (!PyDict_Check(args[1])) { + _PyArg_BadArgument("set___main___attrs", "argument 'updates'", "dict", args[1]); + goto exit; + } + updates = args[1]; + if (!noptargs) { + goto skip_optional_kwonly; + } + restricted = PyObject_IsTrue(args[2]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_set___main___attrs_impl(module, id, updates, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_exec__doc__, +"exec($module, /, id, code, shared={}, *, restrict=False)\n" +"--\n" +"\n" +"Execute the provided code in the identified interpreter.\n" +"\n" +"This is equivalent to running the builtin exec() under the target\n" +"interpreter, using the __dict__ of its __main__ module as both\n" +"globals and locals.\n" +"\n" +"\"code\" may be a string containing the text of a Python script.\n" +"\n" +"Functions (and code objects) are also supported, with some restrictions.\n" +"The code/function must not take any arguments or be a closure\n" +"(i.e. have cell vars). Methods and other callables are not supported.\n" +"\n" +"If a function is provided, its code object is used and all its state\n" +"is ignored, including its __globals__ dict."); + +#define _INTERPRETERS_EXEC_METHODDEF \ + {"exec", _PyCFunction_CAST(_interpreters_exec), METH_FASTCALL|METH_KEYWORDS, _interpreters_exec__doc__}, + +static PyObject * +_interpreters_exec_impl(PyObject *module, PyObject *id, PyObject *code, + PyObject *shared, int restricted); + +static PyObject * +_interpreters_exec(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 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(code), &_Py_ID(shared), &_Py_ID(restrict), }, + }; + #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[] = {"id", "code", "shared", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "exec", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *id; + PyObject *code; + PyObject *shared = NULL; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + code = args[1]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[2]) { + if (!PyDict_Check(args[2])) { + _PyArg_BadArgument("exec", "argument 'shared'", "dict", args[2]); + goto exit; + } + shared = args[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + restricted = PyObject_IsTrue(args[3]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_exec_impl(module, id, code, shared, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_run_string__doc__, +"run_string($module, /, id, script, shared={}, *, restrict=False)\n" +"--\n" +"\n" +"Execute the provided string in the identified interpreter.\n" +"\n" +"(See _interpreters.exec().)"); + +#define _INTERPRETERS_RUN_STRING_METHODDEF \ + {"run_string", _PyCFunction_CAST(_interpreters_run_string), METH_FASTCALL|METH_KEYWORDS, _interpreters_run_string__doc__}, + +static PyObject * +_interpreters_run_string_impl(PyObject *module, PyObject *id, + PyObject *script, PyObject *shared, + int restricted); + +static PyObject * +_interpreters_run_string(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 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(script), &_Py_ID(shared), &_Py_ID(restrict), }, + }; + #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[] = {"id", "script", "shared", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "run_string", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *id; + PyObject *script; + PyObject *shared = NULL; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + if (!PyUnicode_Check(args[1])) { + _PyArg_BadArgument("run_string", "argument 'script'", "str", args[1]); + goto exit; + } + script = args[1]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[2]) { + if (!PyDict_Check(args[2])) { + _PyArg_BadArgument("run_string", "argument 'shared'", "dict", args[2]); + goto exit; + } + shared = args[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + restricted = PyObject_IsTrue(args[3]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_run_string_impl(module, id, script, shared, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_run_func__doc__, +"run_func($module, /, id, func, shared={}, *, restrict=False)\n" +"--\n" +"\n" +"Execute the body of the provided function in the identified interpreter.\n" +"\n" +"Code objects are also supported. In both cases, closures and args\n" +"are not supported. Methods and other callables are not supported either.\n" +"\n" +"(See _interpreters.exec().)"); + +#define _INTERPRETERS_RUN_FUNC_METHODDEF \ + {"run_func", _PyCFunction_CAST(_interpreters_run_func), METH_FASTCALL|METH_KEYWORDS, _interpreters_run_func__doc__}, + +static PyObject * +_interpreters_run_func_impl(PyObject *module, PyObject *id, PyObject *func, + PyObject *shared, int restricted); + +static PyObject * +_interpreters_run_func(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 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(func), &_Py_ID(shared), &_Py_ID(restrict), }, + }; + #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[] = {"id", "func", "shared", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "run_func", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *id; + PyObject *func; + PyObject *shared = NULL; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + func = args[1]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[2]) { + if (!PyDict_Check(args[2])) { + _PyArg_BadArgument("run_func", "argument 'shared'", "dict", args[2]); + goto exit; + } + shared = args[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + restricted = PyObject_IsTrue(args[3]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_run_func_impl(module, id, func, shared, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_call__doc__, +"call($module, /, id, callable, args=(), kwargs={}, *,\n" +" preserve_exc=False, restrict=False)\n" +"--\n" +"\n" +"Call the provided object in the identified interpreter.\n" +"\n" +"Pass the given args and kwargs, if possible."); + +#define _INTERPRETERS_CALL_METHODDEF \ + {"call", _PyCFunction_CAST(_interpreters_call), METH_FASTCALL|METH_KEYWORDS, _interpreters_call__doc__}, + +static PyObject * +_interpreters_call_impl(PyObject *module, PyObject *id, PyObject *callable, + PyObject *args_obj, PyObject *kwargs_obj, + int preserve_exc, int restricted); + +static PyObject * +_interpreters_call(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 6 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(callable), &_Py_ID(args), &_Py_ID(kwargs), &_Py_ID(preserve_exc), &_Py_ID(restrict), }, + }; + #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[] = {"id", "callable", "args", "kwargs", "preserve_exc", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "call", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[6]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *id; + PyObject *callable; + PyObject *args_obj = NULL; + PyObject *kwargs_obj = NULL; + int preserve_exc = 0; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + callable = args[1]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[2]) { + if (!PyTuple_Check(args[2])) { + _PyArg_BadArgument("call", "argument 'args'", "tuple", args[2]); + goto exit; + } + args_obj = args[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[3]) { + if (!PyDict_Check(args[3])) { + _PyArg_BadArgument("call", "argument 'kwargs'", "dict", args[3]); + goto exit; + } + kwargs_obj = args[3]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[4]) { + preserve_exc = PyObject_IsTrue(args[4]); + if (preserve_exc < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + restricted = PyObject_IsTrue(args[5]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_call_impl(module, id, callable, args_obj, kwargs_obj, preserve_exc, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_is_shareable__doc__, +"is_shareable($module, /, obj)\n" +"--\n" +"\n" +"Return True if the object\'s data may be shared between interpreters and False otherwise."); + +#define _INTERPRETERS_IS_SHAREABLE_METHODDEF \ + {"is_shareable", _PyCFunction_CAST(_interpreters_is_shareable), METH_FASTCALL|METH_KEYWORDS, _interpreters_is_shareable__doc__}, + +static PyObject * +_interpreters_is_shareable_impl(PyObject *module, PyObject *obj); + +static PyObject * +_interpreters_is_shareable(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 + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(obj), }, + }; + #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[] = {"obj", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_shareable", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *obj; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + obj = args[0]; + return_value = _interpreters_is_shareable_impl(module, obj); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_is_running__doc__, +"is_running($module, /, id, *, restrict=False)\n" +"--\n" +"\n" +"Return whether or not the identified interpreter is running."); + +#define _INTERPRETERS_IS_RUNNING_METHODDEF \ + {"is_running", _PyCFunction_CAST(_interpreters_is_running), METH_FASTCALL|METH_KEYWORDS, _interpreters_is_running__doc__}, + +static PyObject * +_interpreters_is_running_impl(PyObject *module, PyObject *id, int restricted); + +static PyObject * +_interpreters_is_running(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 + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(restrict), }, + }; + #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[] = {"id", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_running", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *id; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + if (!noptargs) { + goto skip_optional_kwonly; + } + restricted = PyObject_IsTrue(args[1]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_is_running_impl(module, id, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_get_config__doc__, +"get_config($module, /, id, *, restrict=False)\n" +"--\n" +"\n" +"Return a representation of the config used to initialize the interpreter."); + +#define _INTERPRETERS_GET_CONFIG_METHODDEF \ + {"get_config", _PyCFunction_CAST(_interpreters_get_config), METH_FASTCALL|METH_KEYWORDS, _interpreters_get_config__doc__}, + +static PyObject * +_interpreters_get_config_impl(PyObject *module, PyObject *idobj, + int restricted); + +static PyObject * +_interpreters_get_config(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 + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(restrict), }, + }; + #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[] = {"id", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "get_config", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *idobj; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + idobj = args[0]; + if (!noptargs) { + goto skip_optional_kwonly; + } + restricted = PyObject_IsTrue(args[1]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_get_config_impl(module, idobj, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_whence__doc__, +"whence($module, /, id)\n" +"--\n" +"\n" +"Return an identifier for where the interpreter was created."); + +#define _INTERPRETERS_WHENCE_METHODDEF \ + {"whence", _PyCFunction_CAST(_interpreters_whence), METH_FASTCALL|METH_KEYWORDS, _interpreters_whence__doc__}, + +static PyObject * +_interpreters_whence_impl(PyObject *module, PyObject *id); + +static PyObject * +_interpreters_whence(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 + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), }, + }; + #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[] = {"id", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "whence", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *id; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + return_value = _interpreters_whence_impl(module, id); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_incref__doc__, +"incref($module, /, id, *, implieslink=False, restrict=False)\n" +"--\n" +"\n"); + +#define _INTERPRETERS_INCREF_METHODDEF \ + {"incref", _PyCFunction_CAST(_interpreters_incref), METH_FASTCALL|METH_KEYWORDS, _interpreters_incref__doc__}, + +static PyObject * +_interpreters_incref_impl(PyObject *module, PyObject *id, int implieslink, + int restricted); + +static PyObject * +_interpreters_incref(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 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(implieslink), &_Py_ID(restrict), }, + }; + #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[] = {"id", "implieslink", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "incref", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *id; + int implieslink = 0; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[1]) { + implieslink = PyObject_IsTrue(args[1]); + if (implieslink < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + restricted = PyObject_IsTrue(args[2]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_incref_impl(module, id, implieslink, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_decref__doc__, +"decref($module, /, id, *, restrict=False)\n" +"--\n" +"\n"); + +#define _INTERPRETERS_DECREF_METHODDEF \ + {"decref", _PyCFunction_CAST(_interpreters_decref), METH_FASTCALL|METH_KEYWORDS, _interpreters_decref__doc__}, + +static PyObject * +_interpreters_decref_impl(PyObject *module, PyObject *id, int restricted); + +static PyObject * +_interpreters_decref(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 + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(restrict), }, + }; + #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[] = {"id", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "decref", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *id; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + if (!noptargs) { + goto skip_optional_kwonly; + } + restricted = PyObject_IsTrue(args[1]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_decref_impl(module, id, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_capture_exception__doc__, +"capture_exception($module, /, exc=None)\n" +"--\n" +"\n" +"Return a snapshot of an exception.\n" +"\n" +"If \"exc\" is None then the current exception, if any, is used (but not cleared).\n" +"The returned snapshot is the same as what _interpreters.exec() returns."); + +#define _INTERPRETERS_CAPTURE_EXCEPTION_METHODDEF \ + {"capture_exception", _PyCFunction_CAST(_interpreters_capture_exception), METH_FASTCALL|METH_KEYWORDS, _interpreters_capture_exception__doc__}, + +static PyObject * +_interpreters_capture_exception_impl(PyObject *module, PyObject *exc_arg); + +static PyObject * +_interpreters_capture_exception(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 + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(exc), }, + }; + #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[] = {"exc", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "capture_exception", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *exc_arg = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + exc_arg = args[0]; +skip_optional_pos: + return_value = _interpreters_capture_exception_impl(module, exc_arg); + +exit: + return return_value; +} +/*[clinic end generated code: output=cf3f54caaa2dd6a2 input=a9049054013a1b77]*/ diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index a47e4d11b54441..09ce77fd12608f 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -1793,6 +1793,37 @@ sys__baserepl(PyObject *module, PyObject *Py_UNUSED(ignored)) return sys__baserepl_impl(module); } +PyDoc_STRVAR(sys__clear_type_descriptors__doc__, +"_clear_type_descriptors($module, type, /)\n" +"--\n" +"\n" +"Private function for clearing certain descriptors from a type\'s dictionary.\n" +"\n" +"See gh-135228 for context."); + +#define SYS__CLEAR_TYPE_DESCRIPTORS_METHODDEF \ + {"_clear_type_descriptors", (PyCFunction)sys__clear_type_descriptors, METH_O, sys__clear_type_descriptors__doc__}, + +static PyObject * +sys__clear_type_descriptors_impl(PyObject *module, PyObject *type); + +static PyObject * +sys__clear_type_descriptors(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *type; + + if (!PyObject_TypeCheck(arg, &PyType_Type)) { + _PyArg_BadArgument("_clear_type_descriptors", "argument", (&PyType_Type)->tp_name, arg); + goto exit; + } + type = arg; + return_value = sys__clear_type_descriptors_impl(module, type); + +exit: + return return_value; +} + PyDoc_STRVAR(sys__is_gil_enabled__doc__, "_is_gil_enabled($module, /)\n" "--\n" @@ -1948,4 +1979,4 @@ _jit_is_active(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=449d16326e69dcf6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9052f399f40ca32d input=a9049054013a1b77]*/ diff --git a/Python/sysmodule.c b/Python/sysmodule.c index ae6cf306735939..e4bc27d2ce624c 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2641,6 +2641,47 @@ sys__baserepl_impl(PyObject *module) Py_RETURN_NONE; } +/*[clinic input] +sys._clear_type_descriptors + + type: object(subclass_of='&PyType_Type') + / + +Private function for clearing certain descriptors from a type's dictionary. + +See gh-135228 for context. +[clinic start generated code]*/ + +static PyObject * +sys__clear_type_descriptors_impl(PyObject *module, PyObject *type) +/*[clinic end generated code: output=5ad17851b762b6d9 input=dc536c97fde07251]*/ +{ + PyTypeObject *typeobj = (PyTypeObject *)type; + if (_PyType_HasFeature(typeobj, Py_TPFLAGS_IMMUTABLETYPE)) { + PyErr_SetString(PyExc_TypeError, "argument is immutable"); + return NULL; + } + PyObject *dict = _PyType_GetDict(typeobj); + PyObject *dunder_dict = NULL; + if (PyDict_Pop(dict, &_Py_ID(__dict__), &dunder_dict) < 0) { + return NULL; + } + PyObject *dunder_weakref = NULL; + if (PyDict_Pop(dict, &_Py_ID(__weakref__), &dunder_weakref) < 0) { + PyType_Modified(typeobj); + Py_XDECREF(dunder_dict); + return NULL; + } + PyType_Modified(typeobj); + // We try to hold onto a reference to these until after we call + // PyType_Modified(), in case their deallocation triggers somer user code + // that tries to do something to the type. + Py_XDECREF(dunder_dict); + Py_XDECREF(dunder_weakref); + Py_RETURN_NONE; +} + + /*[clinic input] sys._is_gil_enabled -> bool @@ -2837,6 +2878,7 @@ static PyMethodDef sys_methods[] = { SYS__STATS_DUMP_METHODDEF #endif SYS__GET_CPU_COUNT_CONFIG_METHODDEF + SYS__CLEAR_TYPE_DESCRIPTORS_METHODDEF SYS__IS_GIL_ENABLED_METHODDEF SYS__DUMP_TRACELETS_METHODDEF {NULL, NULL} // sentinel diff --git a/Tools/build/check_extension_modules.py b/Tools/build/check_extension_modules.py index 9815bcfe27d995..668db8df0bd181 100644 --- a/Tools/build/check_extension_modules.py +++ b/Tools/build/check_extension_modules.py @@ -17,9 +17,11 @@ See --help for more information """ + +from __future__ import annotations + import _imp import argparse -import collections import enum import logging import os @@ -29,13 +31,16 @@ import sysconfig import warnings from collections.abc import Iterable -from importlib._bootstrap import _load as bootstrap_load +from importlib._bootstrap import ( # type: ignore[attr-defined] + _load as bootstrap_load, +) from importlib.machinery import ( BuiltinImporter, ExtensionFileLoader, ModuleSpec, ) from importlib.util import spec_from_file_location, spec_from_loader +from typing import NamedTuple SRC_DIR = pathlib.Path(__file__).parent.parent.parent @@ -112,6 +117,7 @@ ) +@enum.unique class ModuleState(enum.Enum): # Makefile state "yes" BUILTIN = "builtin" @@ -123,11 +129,13 @@ class ModuleState(enum.Enum): # disabled by Setup / makesetup rule DISABLED_SETUP = "disabled_setup" - def __bool__(self): + def __bool__(self) -> bool: return self.value in {"builtin", "shared"} -ModuleInfo = collections.namedtuple("ModuleInfo", "name state") +class ModuleInfo(NamedTuple): + name: str + state: ModuleState class ModuleChecker: @@ -135,9 +143,9 @@ class ModuleChecker: setup_files = ( # see end of configure.ac - "Modules/Setup.local", - "Modules/Setup.stdlib", - "Modules/Setup.bootstrap", + pathlib.Path("Modules/Setup.local"), + pathlib.Path("Modules/Setup.stdlib"), + pathlib.Path("Modules/Setup.bootstrap"), SRC_DIR / "Modules/Setup", ) @@ -149,15 +157,15 @@ def __init__(self, cross_compiling: bool = False, strict: bool = False): self.builddir = self.get_builddir() self.modules = self.get_modules() - self.builtin_ok = [] - self.shared_ok = [] - self.failed_on_import = [] - self.missing = [] - self.disabled_configure = [] - self.disabled_setup = [] - self.notavailable = [] + self.builtin_ok: list[ModuleInfo] = [] + self.shared_ok: list[ModuleInfo] = [] + self.failed_on_import: list[ModuleInfo] = [] + self.missing: list[ModuleInfo] = [] + self.disabled_configure: list[ModuleInfo] = [] + self.disabled_setup: list[ModuleInfo] = [] + self.notavailable: list[ModuleInfo] = [] - def check(self): + def check(self) -> None: if not hasattr(_imp, 'create_dynamic'): logger.warning( ('Dynamic extensions not supported ' @@ -189,10 +197,10 @@ def check(self): assert modinfo.state == ModuleState.SHARED self.shared_ok.append(modinfo) - def summary(self, *, verbose: bool = False): + def summary(self, *, verbose: bool = False) -> None: longest = max([len(e.name) for e in self.modules], default=0) - def print_three_column(modinfos: list[ModuleInfo]): + def print_three_column(modinfos: list[ModuleInfo]) -> None: names = [modinfo.name for modinfo in modinfos] names.sort(key=str.lower) # guarantee zip() doesn't drop anything @@ -262,12 +270,12 @@ def print_three_column(modinfos: list[ModuleInfo]): f"{len(self.failed_on_import)} failed on import)" ) - def check_strict_build(self): + def check_strict_build(self) -> None: """Fail if modules are missing and it's a strict build""" if self.strict_extensions_build and (self.failed_on_import or self.missing): raise RuntimeError("Failed to build some stdlib modules") - def list_module_names(self, *, all: bool = False) -> set: + def list_module_names(self, *, all: bool = False) -> set[str]: names = {modinfo.name for modinfo in self.modules} if all: names.update(WINDOWS_MODULES) @@ -280,9 +288,9 @@ def get_builddir(self) -> pathlib.Path: except FileNotFoundError: logger.error("%s must be run from the top build directory", __file__) raise - builddir = pathlib.Path(builddir) - logger.debug("%s: %s", self.pybuilddir_txt, builddir) - return builddir + builddir_path = pathlib.Path(builddir) + logger.debug("%s: %s", self.pybuilddir_txt, builddir_path) + return builddir_path def get_modules(self) -> list[ModuleInfo]: """Get module info from sysconfig and Modules/Setup* files""" @@ -367,7 +375,7 @@ def parse_setup_file(self, setup_file: pathlib.Path) -> Iterable[ModuleInfo]: case ["*disabled*"]: state = ModuleState.DISABLED case ["*noconfig*"]: - state = None + continue case [*items]: if state == ModuleState.DISABLED: # *disabled* can disable multiple modules per line @@ -384,26 +392,33 @@ def parse_setup_file(self, setup_file: pathlib.Path) -> Iterable[ModuleInfo]: def get_spec(self, modinfo: ModuleInfo) -> ModuleSpec: """Get ModuleSpec for builtin or extension module""" if modinfo.state == ModuleState.SHARED: - location = os.fspath(self.get_location(modinfo)) + mod_location = self.get_location(modinfo) + assert mod_location is not None + location = os.fspath(mod_location) loader = ExtensionFileLoader(modinfo.name, location) - return spec_from_file_location(modinfo.name, location, loader=loader) + spec = spec_from_file_location(modinfo.name, location, loader=loader) + assert spec is not None + return spec elif modinfo.state == ModuleState.BUILTIN: - return spec_from_loader(modinfo.name, loader=BuiltinImporter) + spec = spec_from_loader(modinfo.name, loader=BuiltinImporter) + assert spec is not None + return spec else: raise ValueError(modinfo) - def get_location(self, modinfo: ModuleInfo) -> pathlib.Path: + def get_location(self, modinfo: ModuleInfo) -> pathlib.Path | None: """Get shared library location in build directory""" if modinfo.state == ModuleState.SHARED: return self.builddir / f"{modinfo.name}{self.ext_suffix}" else: return None - def _check_file(self, modinfo: ModuleInfo, spec: ModuleSpec): + def _check_file(self, modinfo: ModuleInfo, spec: ModuleSpec) -> None: """Check that the module file is present and not empty""" - if spec.loader is BuiltinImporter: + if spec.loader is BuiltinImporter: # type: ignore[comparison-overlap] return try: + assert spec.origin is not None st = os.stat(spec.origin) except FileNotFoundError: logger.error("%s (%s) is missing", modinfo.name, spec.origin) @@ -411,7 +426,7 @@ def _check_file(self, modinfo: ModuleInfo, spec: ModuleSpec): if not st.st_size: raise ImportError(f"{spec.origin} is an empty file") - def check_module_import(self, modinfo: ModuleInfo): + def check_module_import(self, modinfo: ModuleInfo) -> None: """Attempt to import module and report errors""" spec = self.get_spec(modinfo) self._check_file(modinfo, spec) @@ -430,7 +445,7 @@ def check_module_import(self, modinfo: ModuleInfo): logger.exception("Importing extension '%s' failed!", modinfo.name) raise - def check_module_cross(self, modinfo: ModuleInfo): + def check_module_cross(self, modinfo: ModuleInfo) -> None: """Sanity check for cross compiling""" spec = self.get_spec(modinfo) self._check_file(modinfo, spec) @@ -443,6 +458,7 @@ def rename_module(self, modinfo: ModuleInfo) -> None: failed_name = f"{modinfo.name}_failed{self.ext_suffix}" builddir_path = self.get_location(modinfo) + assert builddir_path is not None if builddir_path.is_symlink(): symlink = builddir_path module_path = builddir_path.resolve().relative_to(os.getcwd()) @@ -466,7 +482,7 @@ def rename_module(self, modinfo: ModuleInfo) -> None: logger.debug("Rename '%s' -> '%s'", module_path, failed_path) -def main(): +def main() -> None: args = parser.parse_args() if args.debug: args.verbose = True diff --git a/Tools/build/generate_stdlib_module_names.py b/Tools/build/generate_stdlib_module_names.py index 88414cdbb37a8d..bda72539640611 100644 --- a/Tools/build/generate_stdlib_module_names.py +++ b/Tools/build/generate_stdlib_module_names.py @@ -1,9 +1,12 @@ # This script lists the names of standard library modules # to update Python/stdlib_module_names.h +from __future__ import annotations + import _imp import os.path import sys import sysconfig +from typing import TextIO from check_extension_modules import ModuleChecker @@ -48,12 +51,12 @@ } # Built-in modules -def list_builtin_modules(names): +def list_builtin_modules(names: set[str]) -> None: names |= set(sys.builtin_module_names) # Pure Python modules (Lib/*.py) -def list_python_modules(names): +def list_python_modules(names: set[str]) -> None: for filename in os.listdir(STDLIB_PATH): if not filename.endswith(".py"): continue @@ -62,7 +65,7 @@ def list_python_modules(names): # Packages in Lib/ -def list_packages(names): +def list_packages(names: set[str]) -> None: for name in os.listdir(STDLIB_PATH): if name in IGNORE: continue @@ -76,16 +79,16 @@ def list_packages(names): # Built-in and extension modules built by Modules/Setup* # includes Windows and macOS extensions. -def list_modules_setup_extensions(names): +def list_modules_setup_extensions(names: set[str]) -> None: checker = ModuleChecker() names.update(checker.list_module_names(all=True)) # List frozen modules of the PyImport_FrozenModules list (Python/frozen.c). # Use the "./Programs/_testembed list_frozen" command. -def list_frozen(names): +def list_frozen(names: set[str]) -> None: submodules = set() - for name in _imp._frozen_module_names(): + for name in _imp._frozen_module_names(): # type: ignore[attr-defined] # To skip __hello__, __hello_alias__ and etc. if name.startswith('__'): continue @@ -101,8 +104,8 @@ def list_frozen(names): raise Exception(f'unexpected frozen submodules: {sorted(submodules)}') -def list_modules(): - names = set() +def list_modules() -> set[str]: + names: set[str] = set() list_builtin_modules(names) list_modules_setup_extensions(names) @@ -127,7 +130,7 @@ def list_modules(): return names -def write_modules(fp, names): +def write_modules(fp: TextIO, names: set[str]) -> None: print(f"// Auto-generated by {SCRIPT_NAME}.", file=fp) print("// List used to create sys.stdlib_module_names.", file=fp) @@ -138,7 +141,7 @@ def write_modules(fp, names): print("};", file=fp) -def main(): +def main() -> None: if not sysconfig.is_python_build(): print(f"ERROR: {sys.executable} is not a Python build", file=sys.stderr) diff --git a/Tools/build/mypy.ini b/Tools/build/mypy.ini index 123dc895f90a1f..2abb21e51f1cf8 100644 --- a/Tools/build/mypy.ini +++ b/Tools/build/mypy.ini @@ -3,10 +3,12 @@ # Please, when adding new files here, also add them to: # .github/workflows/mypy.yml files = + Tools/build/check_extension_modules.py, Tools/build/compute-changes.py, Tools/build/deepfreeze.py, Tools/build/generate-build-details.py, Tools/build/generate_sbom.py, + Tools/build/generate_stdlib_module_names.py, Tools/build/verify_ensurepip_wheels.py, Tools/build/update_file.py, Tools/build/umarshal.py