)]}'
{
  "commit": "524d72b36dad6ece7e389b709d5081976de42630",
  "tree": "466096a85ce59774057eea883cbb7b237c40e885",
  "parents": [
    "e8cead16267e039304fc59766262b6b6d16ad4f2"
  ],
  "author": {
    "name": "Ralf W. Grosse-Kunstleve",
    "email": "rwgkio@gmail.com",
    "time": "Mon Mar 30 10:17:22 2026 +0700"
  },
  "committer": {
    "name": "GitHub",
    "email": "noreply@github.com",
    "time": "Sun Mar 29 20:17:22 2026 -0700"
  },
  "message": "fix: strdup `\"self\"` arg in `def_property_static`, partially revert #6010 (gh-5976) (#6015)\n\n* fix: strdup args added after initialize_generic in def_property_static (gh-5976)\n\n`def_property_static` calls `process_attributes::init` on already-initialized\nfunction records (after `initialize_generic`\u0027s strdup loop has run).\nArgs added at this stage (e.g. \"self\" via `append_self_arg_if_needed`) remain\nas string literals, so `destruct()` would call `free()` on them.\n\nFix by strdup\u0027ing name/descr of any args appended by the late\n`process_attributes::init` call. Root cause introduced by gh-5486.\n\nMade-with: Cursor\n\n* Partially revert gh-6010: remove py_is_finalizing() workarounds\n\nNow that the root cause (free of string literals in def_property_static,\ngh-5976) is fixed in the previous commit, the py_is_finalizing() guards\nintroduced in gh-6010 are no longer needed:\n\n- tp_dealloc_impl: remove early return during finalization (was leaking\n  all function records instead of properly destroying them)\n- destruct(): remove guard around arg.value.dec_ref()\n- common.h: remove py_is_finalizing() helper (no remaining callers)\n\nThe genuine fix from gh-6010 (PyObject_Free + Py_DECREF ordering in\ntp_dealloc_impl) is retained.\n\nMade-with: Cursor\n\n* test: add embedding test for py::enum_ across interpreter restart (gh-5976)\n\npy::enum_ is the primary trigger for gh-5976 because its constructor\ncreates properties via def_property_static / def_property_readonly_static,\nwhich call process_attributes::init on already-initialized function records.\nYet none of the existing embedding tests used py::enum_ at all.\n\nAdd an PYBIND11_EMBEDDED_MODULE with py::enum_ and a test case that imports\nit, finalize/reinitializes the interpreter, and re-imports it. This exercises\nthe def_property_static code path that was fixed in the preceding commit.\n\nNote: on Python 3.14.2 (and likely 3.12+), tp_dealloc_impl is not called\nduring Py_FinalizeEx for function record PyObjects — they simply leak because\ntypes are effectively immortalized. As a result, this test cannot trigger the\noriginal free()-on-string-literal crash on this Python version. However, it\nremains valuable as a regression guard: on Python builds where finalization\ndoes clean up function records (or if CPython changes this behavior), the\ntest would catch the crash. It also verifies that py::enum_ survives\ninterpreter restart correctly, which was previously untested.\n\nMade-with: Cursor\n\n* test: skip enum restart test on Python 3.12 (pre-existing crash)\n\nMade-with: Cursor\n\n* Add test_standalone_enum_module.py, standalone_enum_module.cpp\n\n* Make standalone_enum_module.cpp more similar to #5976 reproducer. Also fix clang-tidy error.\n\n* This crashes when testing locally:\n\n( cd /wrk/forked/pybind11/tests \u0026\u0026 PYTHONPATH\u003d/wrk/bld/pybind11_gcc_v3.14.2_df793163d58_default/lib /wrk/bld/pybind11_gcc_v3.14.2_df793163d58_default/TestVenv/bin/python3 -m pytest test_standalone_enum_module.py )\n\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d test session starts \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\nplatform linux -- Python 3.14.2, pytest-9.0.2, pluggy-1.6.0\ninstalled packages of interest: build\u003d\u003d1.4.2 numpy\u003d\u003d2.4.3 scipy\u003d\u003d1.17.1\nC++ Info: 13.3.0 C++20 __pybind11_internals_v12_system_libstdcpp_gxx_abi_1xxx_use_cxx11_abi_1__ PYBIND11_SIMPLE_GIL_MANAGEMENT\u003dFalse\nrootdir: /wrk/forked/pybind11/tests\nconfigfile: pytest.ini\nplugins: timeout-2.4.0, xdist-3.8.0\ncollected 1 item\n\ntest_standalone_enum_module.py F                                         [100%]\n\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d FAILURES \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n________________________ test_enum_import_exit_no_crash ________________________\n\n    def test_enum_import_exit_no_crash():\n        # Modeled after reproducer under issue #5976\n\u003e       env.check_script_success_in_subprocess(\n            f\"\"\"\n            import sys\n            sys.path.insert(0, {os.path.dirname(env.__file__)!r})\n            import standalone_enum_module as m\n            assert m.SomeEnum.__class__.__name__ \u003d\u003d \"pybind11_type\"\n            \"\"\",\n            rerun\u003d1,\n        )\n\ntest_standalone_enum_module.py:10:\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n\ncode \u003d \u0027import sys\\nsys.path.insert(0, \\\u0027/wrk/forked/pybind11/tests\\\u0027)\\nimport standalone_enum_module as m\\nassert m.SomeEnum.__class__.__name__ \u003d\u003d \"pybind11_type\"\u0027\n\n    def check_script_success_in_subprocess(code: str, *, rerun: int \u003d 8) -\u003e None:\n        \"\"\"Runs the given code in a subprocess.\"\"\"\n        import os\n        import subprocess\n        import sys\n        import textwrap\n\n        if ANDROID or IOS or sys.platform.startswith(\"emscripten\"):\n            pytest.skip(\"Requires subprocess support\")\n\n        code \u003d textwrap.dedent(code).strip()\n        try:\n            for _ in range(rerun):  # run flakily failing test multiple times\n                subprocess.check_output(\n                    [sys.executable, \"-c\", code],\n                    cwd\u003dos.getcwd(),\n                    stderr\u003dsubprocess.STDOUT,\n                    text\u003dTrue,\n                )\n        except subprocess.CalledProcessError as ex:\n\u003e           raise RuntimeError(\n                f\"Subprocess failed with exit code {ex.returncode}.\\n\\n\"\n                f\"Code:\\n\"\n                f\"```python\\n\"\n                f\"{code}\\n\"\n                f\"```\\n\\n\"\n                f\"Output:\\n\"\n                f\"{ex.output}\"\n            ) from None\nE           RuntimeError: Subprocess failed with exit code -6.\nE\nE           Code:\nE           ```python\nE           import sys\nE           sys.path.insert(0, \u0027/wrk/forked/pybind11/tests\u0027)\nE           import standalone_enum_module as m\nE           assert m.SomeEnum.__class__.__name__ \u003d\u003d \"pybind11_type\"\nE           ```\nE\nE           Output:\nE           munmap_chunk(): invalid pointer\n\n_          \u003d 0\ncode       \u003d \u0027import sys\\nsys.path.insert(0, \\\u0027/wrk/forked/pybind11/tests\\\u0027)\\nimport standalone_enum_module as m\\nassert m.SomeEnum.__class__.__name__ \u003d\u003d \"pybind11_type\"\u0027\nos         \u003d \u003cmodule \u0027os\u0027 (frozen)\u003e\nrerun      \u003d 1\nsubprocess \u003d \u003cmodule \u0027subprocess\u0027 from \u0027/wrk/cpython_installs/v3.14.2_df793163d58_default/lib/python3.14/subprocess.py\u0027\u003e\nsys        \u003d \u003cmodule \u0027sys\u0027 (built-in)\u003e\ntextwrap   \u003d \u003cmodule \u0027textwrap\u0027 from \u0027/wrk/cpython_installs/v3.14.2_df793163d58_default/lib/python3.14/textwrap.py\u0027\u003e\n\nenv.py:68: RuntimeError\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d short test summary info \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\nFAILED test_standalone_enum_module.py::test_enum_import_exit_no_crash - Runti...\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d 1 failed in 0.23s \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\nERROR: completed_process.returncode\u003d1\n\n* Add \"Added in PR #6015\" comments, for easy reference back to this PR\n\n* test: use PYBIND11_CATCH2_SKIP_IF for Python 3.12 enum restart skip\n\nReplace #if/#else/#endif preprocessor guard with runtime\nPYBIND11_CATCH2_SKIP_IF so the test is always compiled and\nshows [ SKIPPED ] in output on Python 3.12.\n\nMade-with: Cursor\n\n* fix: suppress MSVC C4127 in PYBIND11_CATCH2_SKIP_IF macro\n\nThe constant condition in PYBIND11_CATCH2_SKIP_IF triggers MSVC\nwarning C4127 (conditional expression is constant), which becomes\na build error under /WX.\n\nMade-with: Cursor",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "d438f9c224b2b6183e87b408aff02fa388733773",
      "old_mode": 33188,
      "old_path": "include/pybind11/detail/common.h",
      "new_id": "ae682f308f99769947299c70d087b987782636df",
      "new_mode": 33188,
      "new_path": "include/pybind11/detail/common.h"
    },
    {
      "type": "modify",
      "old_id": "0d03f4fa60c9e2236182f20655da6dec948bd0a7",
      "old_mode": 33188,
      "old_path": "include/pybind11/pybind11.h",
      "new_id": "f5cf411c6401cac2f4099955daa1d652a60fe93d",
      "new_mode": 33188,
      "new_path": "include/pybind11/pybind11.h"
    },
    {
      "type": "modify",
      "old_id": "9a35052daa343af1796cefb77bb9361284cf3a8b",
      "old_mode": 33188,
      "old_path": "tests/CMakeLists.txt",
      "new_id": "e87b1e93b3ff82dddec841b4ced92a5432572dc7",
      "new_mode": 33188,
      "new_path": "tests/CMakeLists.txt"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "7e917b974ec69eceffffc6d947319913eb83cec5",
      "new_mode": 33188,
      "new_path": "tests/standalone_enum_module.cpp"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "3358b887f75da7d01691407e6c9248dc18442049",
      "new_mode": 33188,
      "new_path": "tests/test_standalone_enum_module.py"
    },
    {
      "type": "modify",
      "old_id": "72ffdb62b68b76b614e82c8c58dc6646f5ecdbc3",
      "old_mode": 33188,
      "old_path": "tests/test_with_catch/catch_skip.h",
      "new_id": "9e2954d3b628de773f54d7c6b76c61ca520b5424",
      "new_mode": 33188,
      "new_path": "tests/test_with_catch/catch_skip.h"
    },
    {
      "type": "modify",
      "old_id": "d227eecddc0b518ecec6a9432bfdb48d5bbd916a",
      "old_mode": 33188,
      "old_path": "tests/test_with_catch/test_interpreter.cpp",
      "new_id": "4103c0f5ff1e574c59628ac5d0680c655cc3eaf9",
      "new_mode": 33188,
      "new_path": "tests/test_with_catch/test_interpreter.cpp"
    }
  ]
}
