44from typing import Any
55
66NOT_FOUND : object = object ()
7+ NULL : object = object ()
78_MAX_FIELD_SEARCH_OFFSET = 50
89
910if sys .maxsize > 2 ** 32 :
@@ -55,12 +56,17 @@ def try_write_readonly_attr(
5556 if offset == - 1 :
5657 return
5758 obj_addr = ctypes .c_void_p .from_buffer (ctypes .py_object (obj )).value
58- new_value_addr = ctypes .c_void_p .from_buffer (ctypes .py_object (new_value )).value
59+ if new_value is NULL :
60+ new_value_addr : int | None = 0
61+ else :
62+ new_value_addr = ctypes .c_void_p .from_buffer (
63+ ctypes .py_object (new_value )
64+ ).value
5965 if obj_addr is None or new_value_addr is None :
6066 return
6167 if prev_value is not None :
6268 ctypes .pythonapi .Py_DecRef (ctypes .py_object (prev_value ))
63- if new_value is not None :
69+ if new_value not in ( None , NULL ) :
6470 ctypes .pythonapi .Py_IncRef (ctypes .py_object (new_value ))
6571 ctypes .cast (
6672 obj_addr + WORD_N_BYTES * offset , ctypes .POINTER (WORD_TYPE )
@@ -108,12 +114,10 @@ def try_patch_attr(
108114 def patch_function (
109115 cls , to_patch_to : Any , to_patch_from : Any , is_method : bool
110116 ) -> None :
111- new_freevars = []
112117 new_closure = []
113118 for freevar , closure_val in zip (
114119 to_patch_from .__code__ .co_freevars or [], to_patch_from .__closure__ or []
115120 ):
116- new_freevars .append (freevar )
117121 if (
118122 callable (closure_val .cell_contents )
119123 and freevar in to_patch_to .__code__ .co_freevars
@@ -125,23 +129,19 @@ def patch_function(
125129 )
126130 else :
127131 new_closure .append (closure_val )
128- code_with_new_freevars = to_patch_from .__code__ .replace (
129- co_freevars = tuple (new_freevars )
130- )
131132 # lambdas may complain if there is more than one freevar
132- cls .try_patch_attr (
133- to_patch_to , code_with_new_freevars , "__code__" , new_is_value = True
134- )
133+ cls .try_patch_attr (to_patch_to , to_patch_from , "__code__" )
135134 offset = - 1
136135 if to_patch_to .__closure__ is None and to_patch_from .__closure__ is not None :
137136 offset = cls .infer_field_offset (to_patch_from , "__closure__" )
138- cls .try_patch_readonly_attr (
139- to_patch_to ,
140- tuple (new_closure ) or None ,
141- "__closure__" ,
142- new_is_value = True ,
143- offset = offset ,
144- )
137+ if to_patch_to .__closure__ is not None or to_patch_from .__closure__ is not None :
138+ cls .try_patch_readonly_attr (
139+ to_patch_to ,
140+ tuple (new_closure ) or NULL ,
141+ "__closure__" ,
142+ new_is_value = True ,
143+ offset = offset ,
144+ )
145145 for attr in ("__defaults__" , "__kwdefaults__" , "__doc__" , "__dict__" ):
146146 cls .try_patch_attr (to_patch_to , to_patch_from , attr )
147147 if is_method :
0 commit comments