From 9e1f15f0b7e354daff8cb8ce9eba2b4f11d48c71 Mon Sep 17 00:00:00 2001 From: Stephen Macke Date: Sat, 30 Aug 2025 21:35:56 -0700 Subject: [PATCH] deduperreload should patch NULL for empty closure rather than None --- .../deduperreload/deduperreload_patching.py | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/IPython/extensions/deduperreload/deduperreload_patching.py b/IPython/extensions/deduperreload/deduperreload_patching.py index a8b53e68e78..36ee103a8e4 100644 --- a/IPython/extensions/deduperreload/deduperreload_patching.py +++ b/IPython/extensions/deduperreload/deduperreload_patching.py @@ -4,6 +4,7 @@ from typing import Any NOT_FOUND: object = object() +NULL: object = object() _MAX_FIELD_SEARCH_OFFSET = 50 if sys.maxsize > 2**32: @@ -55,12 +56,17 @@ def try_write_readonly_attr( if offset == -1: return obj_addr = ctypes.c_void_p.from_buffer(ctypes.py_object(obj)).value - new_value_addr = ctypes.c_void_p.from_buffer(ctypes.py_object(new_value)).value + if new_value is NULL: + new_value_addr: int | None = 0 + else: + new_value_addr = ctypes.c_void_p.from_buffer( + ctypes.py_object(new_value) + ).value if obj_addr is None or new_value_addr is None: return if prev_value is not None: ctypes.pythonapi.Py_DecRef(ctypes.py_object(prev_value)) - if new_value is not None: + if new_value not in (None, NULL): ctypes.pythonapi.Py_IncRef(ctypes.py_object(new_value)) ctypes.cast( obj_addr + WORD_N_BYTES * offset, ctypes.POINTER(WORD_TYPE) @@ -108,12 +114,10 @@ def try_patch_attr( def patch_function( cls, to_patch_to: Any, to_patch_from: Any, is_method: bool ) -> None: - new_freevars = [] new_closure = [] for freevar, closure_val in zip( to_patch_from.__code__.co_freevars or [], to_patch_from.__closure__ or [] ): - new_freevars.append(freevar) if ( callable(closure_val.cell_contents) and freevar in to_patch_to.__code__.co_freevars @@ -125,23 +129,19 @@ def patch_function( ) else: new_closure.append(closure_val) - code_with_new_freevars = to_patch_from.__code__.replace( - co_freevars=tuple(new_freevars) - ) # lambdas may complain if there is more than one freevar - cls.try_patch_attr( - to_patch_to, code_with_new_freevars, "__code__", new_is_value=True - ) + cls.try_patch_attr(to_patch_to, to_patch_from, "__code__") offset = -1 if to_patch_to.__closure__ is None and to_patch_from.__closure__ is not None: offset = cls.infer_field_offset(to_patch_from, "__closure__") - cls.try_patch_readonly_attr( - to_patch_to, - tuple(new_closure) or None, - "__closure__", - new_is_value=True, - offset=offset, - ) + if to_patch_to.__closure__ is not None or to_patch_from.__closure__ is not None: + cls.try_patch_readonly_attr( + to_patch_to, + tuple(new_closure) or NULL, + "__closure__", + new_is_value=True, + offset=offset, + ) for attr in ("__defaults__", "__kwdefaults__", "__doc__", "__dict__"): cls.try_patch_attr(to_patch_to, to_patch_from, attr) if is_method: