Skip to content

Commit 713244e

Browse files
committed
gh-155358: Convert sys structseq types to heap types
Convert the following structseq types from static (built-in) types to heap types: * AsyncGenHooksType * Hash_InfoType * WindowsVersionType * FlagsType * VersionInfoType So these types are no longer shared between sub-interpreters. Cleanup also the EmscriptenInfoType type. Changes: * Replace _PyStructSequence_InitBuiltin() with PyStructSequence_NewType() or _PyStructSequence_NewType(). * Add PyInterpreterState.sys_state structure. * Rename sys.get_asyncgen_hooks() type from "asyncgen_hooks" to "sys.asyncgen_hooks". * Add test.support.check_immutable_type().
1 parent 115400b commit 713244e

8 files changed

Lines changed: 115 additions & 82 deletions

File tree

Include/internal/pycore_interp_structs.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,17 @@ typedef _Py_CODEUNIT *(*_PyJitEntryFuncPtr)(struct _PyExecutorObject *exec, _PyI
823823

824824
#define _PyInterpreterGuard_GUARDS_NOT_ALLOWED UINTPTR_MAX
825825

826+
typedef struct {
827+
PyTypeObject *async_gen_hooks_type;
828+
PyTypeObject *flags_type;
829+
#if defined(MS_WINDOWS)
830+
PyTypeObject *windows_version_type;
831+
#endif
832+
#ifdef __EMSCRIPTEN__
833+
PyTypeObject *emscripten_info_type;
834+
#endif
835+
} _PySys_State;
836+
826837
/* PyInterpreterState holds the global state for one of the runtime's
827838
interpreters. Typically the initial (main) interpreter is the only one.
828839
@@ -899,6 +910,7 @@ struct _is {
899910

900911
// Dictionary of the sys module
901912
PyObject *sysdict;
913+
_PySys_State sys_state;
902914

903915
// Dictionary of the builtins module
904916
PyObject *builtins;

Lib/test/support/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3464,3 +3464,9 @@ def skip_on_low_desktop_heap_memory_subprocess(returncode):
34643464
if returncode == STATUS_DLL_INIT_FAILED:
34653465
raise unittest.SkipTest('gh-150436: DLL init failed, likely because '
34663466
'of low desktop heap memory')
3467+
3468+
3469+
def check_immutable_type(testcase, type):
3470+
regex = r'cannot set .* attribute of immutable type'
3471+
with testcase.assertRaisesRegex(TypeError, regex):
3472+
setattr(type, 'custom_attr', 123)

Lib/test/test_decimal.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import unittest
3333
import numbers
3434
import locale
35+
from test import support
3536
from test.support import (is_resource_enabled,
3637
requires_IEEE_754, requires_docstrings,
3738
check_disallow_instantiation)
@@ -5806,8 +5807,7 @@ def test_c_immutable_types(self):
58065807
)
58075808
for tp in types:
58085809
with self.subTest(tp=tp):
5809-
with self.assertRaisesRegex(TypeError, "immutable"):
5810-
tp.foo = 1
5810+
support.check_immutable_type(self, tp)
58115811

58125812
def test_c_disallow_instantiation(self):
58135813
ContextManager = type(C.localcontext())

Lib/test/test_itertools.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1541,8 +1541,7 @@ def test_immutable_types(self):
15411541
)
15421542
for tp in dataset:
15431543
with self.subTest(tp=tp):
1544-
with self.assertRaisesRegex(TypeError, "immutable"):
1545-
tp.foobar = 1
1544+
support.check_immutable_type(self, tp)
15461545

15471546

15481547
class TestExamples(unittest.TestCase):

Lib/test/test_sys.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ def test_getwindowsversion(self):
425425
self.assertEqual(v[2], v.build)
426426
self.assertEqual(v[3], v.platform)
427427
self.assertEqual(v[4], v.service_pack)
428+
support.check_immutable_type(self, type(v))
428429

429430
# This is how platform.py calls it. Make sure tuple
430431
# still has 5 elements
@@ -690,6 +691,7 @@ def test_attributes(self):
690691
self.assertEqual(algo, 0)
691692
self.assertGreaterEqual(sys.hash_info.cutoff, 0)
692693
self.assertLess(sys.hash_info.cutoff, 8)
694+
support.check_immutable_type(self, type(sys.hash_info))
693695

694696
self.assertIsInstance(sys.maxsize, int)
695697
self.assertIsInstance(sys.maxunicode, int)
@@ -893,11 +895,13 @@ def assert_raise_on_new_sys_type(self, sys_attr):
893895
# sys.flags, sys.version_info, and sys.getwindowsversion.
894896
support.check_disallow_instantiation(self, type(sys_attr), sys_attr)
895897

896-
def test_sys_flags_no_instantiation(self):
898+
def test_sys_flags_type(self):
897899
self.assert_raise_on_new_sys_type(sys.flags)
900+
support.check_immutable_type(self, type(sys.flags))
898901

899-
def test_sys_version_info_no_instantiation(self):
902+
def test_sys_version_info_type(self):
900903
self.assert_raise_on_new_sys_type(sys.version_info)
904+
support.check_immutable_type(self, type(sys.version_info))
901905

902906
def test_sys_getwindowsversion_no_instantiation(self):
903907
# Skip if not being run on Windows.
@@ -1954,6 +1958,7 @@ def test_asyncgen_hooks(self):
19541958
cur = sys.get_asyncgen_hooks()
19551959
self.assertIsNone(cur.firstiter)
19561960
self.assertIsNone(cur.finalizer)
1961+
support.check_immutable_type(self, type(cur))
19571962

19581963
# gh-118473
19591964
with self.assertRaises(TypeError):
@@ -1997,6 +2002,7 @@ def write(self, s):
19972002
self.assertEqual(out, b"")
19982003
self.assertEqual(err, b"")
19992004

2005+
20002006
@test.support.support_remote_exec_only
20012007
@test.support.cpython_only
20022008
class TestRemoteExec(unittest.TestCase):

Lib/test/test_xml_etree_c.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,7 @@ def test_immutable_types(self):
194194
)
195195
for tp in dataset:
196196
with self.subTest(tp=tp):
197-
with self.assertRaisesRegex(TypeError, "immutable"):
198-
tp.foo = 1
197+
support.check_immutable_type(self, tp)
199198

200199
@support.cpython_only
201200
def test_disallow_instantiation(self):

Python/pylifecycle.c

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2072,7 +2072,6 @@ finalize_interp_types(PyInterpreterState *interp)
20722072
{
20732073
_PyTypes_FiniExtTypes(interp);
20742074
_PyUnicode_FiniTypes(interp);
2075-
_PySys_FiniTypes(interp);
20762075
_PyXI_FiniTypes(interp);
20772076
_PyExc_Fini(interp);
20782077
_PyFloat_FiniType(interp);
@@ -2112,14 +2111,16 @@ finalize_interp_types(PyInterpreterState *interp)
21122111
static void
21132112
finalize_interp_clear(PyThreadState *tstate)
21142113
{
2115-
int is_main_interp = _Py_IsMainInterpreter(tstate->interp);
2114+
PyInterpreterState *interp = tstate->interp;
2115+
int is_main_interp = _Py_IsMainInterpreter(interp);
21162116

2117-
_PyXI_Fini(tstate->interp);
2118-
_PyExc_ClearExceptionGroupType(tstate->interp);
2119-
_Py_clear_generic_types(tstate->interp);
2120-
_PyTypes_FiniCachedDescriptors(tstate->interp);
2117+
_PyXI_Fini(interp);
2118+
_PyExc_ClearExceptionGroupType(interp);
2119+
_Py_clear_generic_types(interp);
2120+
_PyTypes_FiniCachedDescriptors(interp);
2121+
_PySys_FiniTypes(interp);
21212122

2122-
/* Clear interpreter state and all thread states */
2123+
/* Clear interpreter state and all thread states: last GC collection! */
21232124
_PyInterpreterState_Clear(tstate);
21242125

21252126
/* Clear all loghooks */
@@ -2136,13 +2137,13 @@ finalize_interp_clear(PyThreadState *tstate)
21362137
_PyPerfTrampoline_Fini();
21372138
}
21382139

2139-
finalize_interp_types(tstate->interp);
2140+
finalize_interp_types(interp);
21402141

21412142
/* Finalize dtoa at last so that finalizers calling repr of float doesn't crash */
2142-
_PyDtoa_Fini(tstate->interp);
2143+
_PyDtoa_Fini(interp);
21432144

21442145
/* Free any delayed free requests immediately */
2145-
_PyMem_FiniDelayed(tstate->interp);
2146+
_PyMem_FiniDelayed(interp);
21462147

21472148
/* finalize_interp_types may allocate Python objects so we may need to
21482149
abandon mimalloc segments again */

0 commit comments

Comments
 (0)