I would like to register a Lock acquisition in Python. In my context, I cannot inherit from _thread.LockType
and override its acquire
method.
Hence, I'm trying to decorate _thread.LockType.acquire
with my own function.
Thanks to this post Extension method for python built-in types, I've managed to bypass the mappingproxy
that is _thread.LockType.__dict__
with the following code:
import ctypes
from ctypes import pythonapi as api
from ctypes import py_object
from _thread import LockType
class PyObject(ctypes.Structure):
"""
To quote https://docs.python.org/3/c-api/structures.html#c.PyObject,
"all object type are extensions of this type".
It contains at least two members:
- 'ob_refcnt' of type Py_ssize_t which is the reference counter
- 'ob_type' of type PyTypeObject which is the type of the Python Object
Its members are represented by a ctypes.Structure, whose member _fields_ is
a list of (field_name: str, field_type: PyTypeObject)
"""
pass
"""
From https://docs.python.org/3/c-api/intro.html#c.Py_ssize_t,
'a signed integral type such as sizeof(Py_ssize_t) == sizeof(size_t) in C'
"""
Py_ssize_t = ctypes.sizeof(ctypes.c_voidp)
Py_ssize_t = ctypes.c_int64 if ctypes.sizeof(ctypes.c_int64) == Py_ssize_t else ctypes.c_int32
PyObject._fields_ = [
('ob_refcnt', Py_ssize_t),
('ob_type', ctypes.POINTER(PyObject))
]
class SlotsPointer(PyObject):
_fields_ = [('dict', ctypes.POINTER(PyObject))]
mapping_proxy = getattr(LockType, '__dict__')
proxy_pointer = Dict.from_address(id(mapping_proxy))
namespace = {}
api.PyDict_SetItem(py_object(namespace), py_object(LockType.__name__), proxy_pointer.dict)
lck_dict = namespace[LockType.__name__]
lck_dict['acquire'] = my_acquire_decorator # a decorator whose signature matches with acquire's
This chunk of code works perfectly as long as I never, NEVER, use _thread.LockType.acquire
explicitly.
Let's say that I need to save the original acquire function (in order to use it in my decorator), with the really exotic statement original_acquire = LockType.acquire
.
Then the decorator is completely discarded and never called, as if the line lck_dict['acquire'] = my_acquire_decorator
was never there.
Any idea as to why ? I'm not even sure it is due to references of the original being kept around since a call to sys.getrefcount(LockType.acquire)
suffices to ignore the change.
I tried to
- Disable the garbage collector automatic collection
- Save the original
- Artificially clear its references counter
- Register the decorator
- Increment the original's ref counter
- Enable back the gc
It wasn't the wisest choice, as the Segfault told me.