@@ -40,6 +40,30 @@ void unsafe_reset_internals_for_single_interpreter() {
4040 py::detail::get_local_internals ();
4141}
4242
43+ py::object &get_dict_type_object () {
44+ PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> storage;
45+ return storage
46+ .call_once_and_store_result (
47+ []() -> py::object { return py::module_::import (" builtins" ).attr (" dict" ); })
48+ .get_stored ();
49+ }
50+
51+ py::object &get_ordered_dict_type_object () {
52+ PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> storage;
53+ return storage
54+ .call_once_and_store_result (
55+ []() -> py::object { return py::module_::import (" collections" ).attr (" OrderedDict" ); })
56+ .get_stored ();
57+ }
58+
59+ py::object &get_default_dict_type_object () {
60+ PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> storage;
61+ return storage
62+ .call_once_and_store_result (
63+ []() -> py::object { return py::module_::import (" collections" ).attr (" defaultdict" ); })
64+ .get_stored ();
65+ }
66+
4367TEST_CASE (" Single Subinterpreter" ) {
4468 unsafe_reset_internals_for_single_interpreter ();
4569
@@ -324,9 +348,15 @@ TEST_CASE("gil_safe_call_once_and_store per-interpreter isolation") {
324348 .get_stored ();
325349 REQUIRE (main_value.cast <py::interpid_t >() == main_interp_id);
326350
351+ py::object dict_type = get_dict_type_object ();
352+ py::object ordered_dict_type = get_ordered_dict_type_object ();
353+ py::object default_dict_type = get_default_dict_type_object ();
354+
327355 py::interpid_t sub_interp_id = -1 ;
328356 py::interpid_t sub_cached_value = -1 ;
329357
358+ bool sub_default_dict_type_destroyed = false ;
359+
330360 // Create a subinterpreter and check that it gets its own storage
331361 {
332362 py::scoped_subinterpreter ssi;
@@ -351,12 +381,38 @@ TEST_CASE("gil_safe_call_once_and_store per-interpreter isolation") {
351381 // This would fail without per-interpreter storage.
352382 REQUIRE (sub_cached_value == sub_interp_id);
353383 REQUIRE (sub_cached_value != main_interp_id);
384+
385+ py::object sub_dict_type = get_dict_type_object ();
386+ py::object sub_ordered_dict_type = get_ordered_dict_type_object ();
387+ py::object sub_default_dict_type = get_default_dict_type_object ();
388+
389+ REQUIRE (sub_dict_type.is (dict_type)); // dict is a static type
390+ REQUIRE (sub_ordered_dict_type.is (ordered_dict_type)); // OrderedDict is a static type
391+ REQUIRE_FALSE (sub_default_dict_type.is (default_dict_type)); // defaultdict is a heap type
392+
393+ (void ) py::weakref (sub_default_dict_type,
394+ py::cpp_function ([&](py::handle weakref) -> void {
395+ sub_default_dict_type_destroyed = true ;
396+ weakref.dec_ref ();
397+ }))
398+ .release ();
354399 }
355400
356401 // Back in main interpreter, verify main's value is unchanged
357402 auto &main_value_after = storage.get_stored ();
358403 REQUIRE (main_value_after.cast <py::interpid_t >() == main_interp_id);
359404
405+ // Verify that the types cached in main are unchanged
406+ py::object dict_type_after = get_dict_type_object ();
407+ py::object ordered_dict_type_after = get_ordered_dict_type_object ();
408+ py::object default_dict_type_after = get_default_dict_type_object ();
409+ REQUIRE (dict_type_after.is (dict_type));
410+ REQUIRE (ordered_dict_type_after.is (ordered_dict_type));
411+ REQUIRE (default_dict_type_after.is (default_dict_type));
412+
413+ // Verify that the subinterpreter's cached default_dict_type was destroyed
414+ REQUIRE (sub_default_dict_type_destroyed);
415+
360416 unsafe_reset_internals_for_single_interpreter ();
361417}
362418
0 commit comments