从ctypes派生的Monkey Patching类不起作用

时间:2018-11-30 19:16:06

标签: python ctypes monkeypatching

我正在尝试“猴子修补”派生自Python ctypes“ Union”的类,但我无法这样做-出现奇怪的错误,有时还会出现段错误。从ctypes“ Structure”派生出来时,同一件事效果很好。

我将范围缩小到了我在下面发布的最简单的测试用例。我正在使用Python 3.6.4。我想知道我做错了什么(还是ctypes“ Union”实现有问题?)。请参见下面的代码和相应的输出。

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)

这是我运行程序时的输出。

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

很明显,当从“ Structure”派生相应的Python对象时,我能够“修补” __str__,但是当从“派生”派生相应的Python对象时,我无法“修补” __str__ “联盟”。

有趣的是,MyUnion.__dict__[__str__]MyUnion.__str__显示了不同的结果-也很奇怪。

我在这里做错什么了吗?我非常感谢您的任何帮助或见识!

1 个答案:

答案 0 :(得分:1)

我认为这里有一个实际的CPython错误。结构类型的类型对象的__setattr__ implementation使用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;
}

但是the one for type objects for union types使用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来更新类型槽并使internal type attribute cache无效。 PyObject_GenericSetAttr不知道应该做任何一件事情,由于“僵尸”缓存的属性而导致潜在的内存损坏。看起来与2008年初的fixed相同,是针对结构的错误,但他们忘记了处理联合。