从ctypes派生的Monkey Patching类不起作用 [英] Monkey Patching class derived from ctypes.Union doesn't work

查看:58
本文介绍了从ctypes派生的Monkey Patching类不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图猴子修补派生自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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆