intbitset __init__导致SIGSEGV

问题描述 投票:4回答:1

以下代码导致引发分段错误。我不确定为什么......

import numpy as np
from intbitset import intbitset

arr = np.array([1,2,3,4,5])

# This works
intbitset(arr.tolist())
=> intbitset([1, 2, 3, 4, 5])

# This throws SIGSEGV
intbitset([x for x in arr])

[x for x in arr]完美运作并按预期返回列表。

有没有人对此有解释?在输入intbitset ctr之前,列表推导不会被评估到列表中吗?

我已经在Python 3.6.3和2.7.13(需要将zip更改为itertools.izip)上进行了测试。两者都崩溃了。 intbitset版本是2.3.0

python
1个回答
0
投票

这里有一些有趣的东西:

$ gdb python 
...
(gdb) run crash.py
...
Thread 1 "python" received signal SIGSEGV, Segmentation fault.
0x00007ffff745338b in ?? () from /usr/lib/libpython3.6m.so.1.0
(gdb) bt
#0  0x00007ffff745338b in ?? ()
   from /usr/lib/libpython3.6m.so.1.0
#1  0x00007ffff74a245f in ?? ()
   from /usr/lib/libpython3.6m.so.1.0
#2  0x00007ffff73f0565 in PyList_Append ()
   from /usr/lib/libpython3.6m.so.1.0
#3  0x00007ffff73a2580 in ?? ()
   from /usr/lib/libpython3.6m.so.1.0
#4  0x00007ffff7404b55 in _PyCFunction_FastCallDict ()
   from /usr/lib/libpython3.6m.so.1.0
#5  0x00007ffff740e10f in _PyObject_FastCallDict ()
   from /usr/lib/libpython3.6m.so.1.0
#6  0x00007ffff73fc9d0 in PyFile_WriteObject ()
   from /usr/lib/libpython3.6m.so.1.0
#7  0x00007ffff74a3d6a in PyFile_WriteString ()
   from /usr/lib/libpython3.6m.so.1.0
#8  0x00007ffff74b2f9d in PyTraceBack_Print ()
   from /usr/lib/libpython3.6m.so.1.0
#9  0x00007ffff7491154 in ?? ()
   from /usr/lib/libpython3.6m.so.1.0
#10 0x00007ffff7327d14 in ?? ()
   from /usr/lib/libpython3.6m.so.1.0
#11 0x00007ffff749136e in PyErr_Display ()
   from /usr/lib/libpython3.6m.so.1.0
#12 0x00007ffff74c890a in ?? ()
   from /usr/lib/libpython3.6m.so.1.0
#13 0x00007ffff7404ad0 in _PyCFunction_FastCallDict ()
   from /usr/lib/libpython3.6m.so.1.0
#14 0x00007ffff740e10f in _PyObject_FastCallDict ()
   from /usr/lib/libpython3.6m.so.1.0
#15 0x00007ffff7492c5b in PyErr_PrintEx ()
  3.6m.so.1.0
#16 0x00007ffff74939d1 in PyRun_SimpleFileExFlags ()
   from /usr/lib/libpython3.6m.so.1.0
#17 0x00007ffff748970b in Py_Main ()
   from /usr/lib/libpython3.6m.so.1.0
#18 0x0000555555554c39 in main ()

观察对PyErr_DisplayPyTraceBack_Print的调用。看起来Python试图显示错误,但在此过程中崩溃了。确实,这不会崩溃:

try:
    intbitset([x for x in arr])
except Exception as ex:
    print(repr(ex))

相反,它输出以下内容:

ValueError('retrieving integers from rhs is impossible: invalid index to scalar variable.')

here in intbitset.__cinit__提出了这个例外。请注意,__cinit__是一个特殊的Cython函数。

它是针对另一个例外而提出的,来自gen_arrtype_subscript的numpy C代码。它可以通过索引这样的标量来触发:

>>> import numpy as np
>>> arr = np.array([1,2,3,4,5])
>>> arr[0][0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: invalid index to scalar variable.

intbitset触发此异常的原因是this line

  tuple_of_tuples = rhs and hasattr(rhs, '__getitem__') and hasattr(rhs[0], '__getitem__')

实际上,numpy标量(在这种情况下是numpy.int64)确实有__getitem__,如果你称之为它们就不喜欢它。这导致intbitset错误地认为它正在接收sequence made of tuples,这会触发对异常提升__getitem__的调用。

这解释了为什么如果你传递一个生成器intbitset(x for x in arr)它没有崩溃:它没有__getitem__,所以intbitset进入一个不同的代码路径。如果你直接传递intbitset(arr)tuple_of_tuples线会在尝试将arr转换为bool时触发另一个异常:

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

这个例外是numpy的非Pythonic(与列表转换为bool的方式不一致),但这就是它的工作方式。

那么为什么invalid index to scalar异常导致段错误,即使truth value of an array没有?事实上,如果我把raise ValueError()放在它之后,两者都会崩溃,所以很明显,两种情况下都会出现未定义的行为,而且运气有时也不会崩溃。

我的猜测是,intbitset通过从__cinit__提出异常来做出意想不到的事情。在Cython文档中没有明确禁止,所以我不确定如何或什么。

© www.soinside.com 2019 - 2024. All rights reserved.