从ctypes派生的Monkey Patching类不起作用 [英] Monkey Patching class derived from ctypes.Union doesn't work
问题描述
我试图猴子修补派生自Python ctypes Union的类,但我无法这样做-出现奇怪的错误,有时还会出现段错误。从ctypes Structure派生出来时,同样的事情效果很好。
I am trying to "monkey patch" a class derived from Python ctypes "Union", but I am unable to do so - getting weird errors and sometimes seg-faults. The same thing works quite well when deriving from ctypes "Structure".
我将其范围缩小到下面要发布的最简单的测试用例。我正在使用Python 3.6.4。我想知道我做错了什么(还是ctypes Union实现有问题?)。请参阅下面的代码和相应的输出。
I have narrowed this to down to the simplest possible test case which I am posting below. I am using Python 3.6.4. I am wondering if I am doing something wrong (or is there a problem with the ctypes "Union" implementation?). Please see the code below and the corresponding output.
import ctypes
def display(self):
""" A new kind of display """
return f'Print Type #2: ( {self.y1}, {self.y2} )'
class MyStruct(ctypes.Structure):
_fields_ = [
('y1', ctypes.c_uint32),
('y2', ctypes.c_uint32)
]
def __str__(self):
return f'Print Type #1: [ {self.y1}, {self.y2} ]'
class MyUnion(ctypes.Union):
_fields_ = [
('y1', ctypes.c_uint32),
('y2', ctypes.c_uint32)
]
def __str__(self):
return f'Print Type #1: [ {self.y1}, {self.y2} ]'
if __name__ == '__main__':
a = MyStruct()
a.y1 = 10
a.y2 = 20
print('Using Structure:')
print('----------------')
print(a)
print('Original :', MyStruct.__str__)
# monkey patch __str__ with a different function.
MyStruct.__str__ = display
print('Patched :', MyStruct.__str__)
print('Patched (dict) :', MyStruct.__dict__['__str__'])
print(a)
a = MyUnion()
a.y1 = 10
a.y2 = 20
print('Using Union:')
print('------------')
print(a)
print('Original :', MyUnion.__str__)
# monkey patch __str__ with a different function.
MyUnion.__str__ = display
print('Patched :', MyUnion.__str__)
print('Patched (dict) :', MyUnion.__dict__['__str__'])
print(a)
这是运行程序时的输出。
Here's the output when I run the program.
Using Structure:
----------------
Print Type #1: [ 10, 20 ]
Original : <function MyStruct.__str__ at 0x7fdf89d02e18>
Patched : <function display at 0x7fdf8b0ebe18>
Patched (dict) : <function display at 0x7fdf8b0ebe18>
Print Type #2: ( 10, 20 )
Using Union:
------------
Print Type #1: [ 20, 20 ]
Original : <function MyUnion.__str__ at 0x7fdf89d02f28>
Patched : <function MyUnion.__str__ at 0x7fdf89d02f28>
Patched (dict) : <function display at 0x7fdf8b0ebe18>
Traceback (most recent call last):
File "ctypes_bug.py", line 54, in <module>
print(a)
TypeError: 'managedbuffer' object is not callable
显然,当相应的Python对象从 Structure派生时,我能够修补 __ str __
,但是我无法修补 __ str__
当相应的Python对象是从 Union派生的。
Clearly, I am able to "patch" __str__
when the corresponding Python object was derived from "Structure", but I am unable to "patch" __str__
when the corresponding Python object was derived from "Union".
有趣的是, MyUnion .__ dict __ [__ str __]
和 MyUnion .__ str __
显示了不同的结果-也很奇怪。
Interestingly, MyUnion.__dict__[__str__]
and MyUnion.__str__
shows different results - which is weird as well.
是否有东西我在这里做错了吗?我非常感谢您的帮助或见识!
Is there something I am doing wrong here? I highly appreciate any help or insight!
推荐答案
我认为这里有一个实际的CPython错误。 __ setattr __
实现对于结构类型的类型对象使用 PyType_Type.tp_setattro
:
I think there's an actual CPython bug here. The __setattr__
implementation for type objects for struct types uses PyType_Type.tp_setattro
:
static int
PyCStructType_setattro(PyObject *self, PyObject *key, PyObject *value)
{
/* XXX Should we disallow deleting _fields_? */
if (-1 == PyType_Type.tp_setattro(self, key, value))
return -1;
if (value && PyUnicode_Check(key) &&
_PyUnicode_EqualToASCIIString(key, "_fields_"))
return PyCStructUnionType_update_stgdict(self, value, 1);
return 0;
}
但用于联合类型的类型对象使用 PyObject_GenericSetAttr
:
static int
UnionType_setattro(PyObject *self, PyObject *key, PyObject *value)
{
/* XXX Should we disallow deleting _fields_? */
if (-1 == PyObject_GenericSetAttr(self, key, value))
return -1;
if (PyUnicode_Check(key) &&
_PyUnicode_EqualToASCIIString(key, "_fields_"))
return PyCStructUnionType_update_stgdict(self, value, 0);
return 0;
}
使用 PyType_Type.tp_setattro
对于更新类型插槽和使内部类型属性缓存。 PyObject_GenericSetAttr
不知道应该做这两种事情,由于僵尸缓存的属性而导致潜在的内存损坏。看起来相同的错误已在2008年初已修复,用于但是他们忘了处理工会。
The use of PyType_Type.tp_setattro
is necessary to update type slots and invalidate the internal type attribute cache. PyObject_GenericSetAttr
doesn't know it should do either of those things, causing potential memory corruption due to "zombie" cached attributes. It looks like the same bug was fixed in early 2008 for structs, but they forgot to handle unions.
这篇关于从ctypes派生的Monkey Patching类不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!