CTypes,从包装器中引发 python 风格的异常



在使用可调用对象修补原始 python 类型的__rshift__运算符时,修补使用包装器:

def _patch_rshift(py_class, func):
assert isinstance(func, FunctionType)
py_type_name = 'tp_as_number'
py_type_method = 'nb_rshift'
py_obj = PyTypeObject.from_address(id(py_class))
type_ptr = getattr(py_obj, py_type_name)
if not type_ptr:
tp_as_obj = PyNumberMethods()
FUNC_INDIRECTION_REFS[(py_class, '__rshift__')] = tp_as_obj
tp_as_new_ptr = ctypes.cast(ctypes.addressof(tp_as_obj),
ctypes.POINTER(PyNumberMethods))
setattr(py_obj, py_type_name, tp_as_new_ptr)
type_head = type_ptr[0]
c_func_t = binary_func_p
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except BaseException as be:
# Need to raise a python style error here:
wrapper.exc_info = sys.exc_info()
return False
c_func = c_func_t(wrapper)
C_FUNC_CALLBACK_REFS[(py_class, '__rshift__')] = c_func
setattr(type_head, py_type_method, c_func)

现在的挑战是,一旦在内部捕获异常,wrapper就像任何普通的python异常一样在这里引发异常。

像这样提高:

@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except BaseException as be:
raise

或者根本不抓:

@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)

收益 率:

Windows fatal exception: access violation
Process finished with exit code -1073741819 (0xC0000005)

所需的行为是简单地重新引发捕获的异常,但如果可能,请使用 python 样式的输出。

相关答案依赖于以 Windows 为中心的平台来实现所需的结果,这不合适,不保留原始异常,或者没有实现所需的 python 异常样式行为:

从 ctypes windll 获取错误消息

Ctypes 捕获异常

更新:

经过更多的挖掘,似乎在这种方法中的任何位置提出都会触发段错误。对于如何解决它,没有比这更明智的了。

不是解决方案,而是解决方法:

手动收集错误信息(使用tracebackinspect(,打印到sys.stderr,然后返回指向 CTypes 错误单例的指针(如在 forbiddenfruit 中所做的那样(可防止段错误发生。

然后,输出将是:

  • 自定义错误代码
  • 正如人们从未修补的原语中所期望的那样 Python 错误代码

(为简洁起见,此处使用的某些方法不包括在内,因为它们不会为解决方案增加任何价值,我选择了pytest样式的错误格式。

def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except BaseException as be:
# Traceback will not help use here, assemble a custom error message:
from custom_inspect_utils import get_calling_expression_recursively
import sys
calling_file, calling_expression = get_calling_expression_recursively()
indentation = " "*(len(calling_expression)-len(calling_expression.lstrip()))
print(f"n{calling_file}n>{calling_expression}E{indentation}{type(be).__name__}: {be}", file=sys.stderr)
print(f"ntAs this package patches primitive types, the python RTE also raised:", file=sys.stderr)
return ErrorSingleton

例如,在修补list以实现__rshift__之后,期望CustomException的表达式[1,2,3] >> wrong_target现在将首先输出

source_file.py:330 (method_name)
>    [1,2,3] >> wrong_target
E    CustomException: Message.

后跟一个TypeError

TypeError: unsupported operand type(s) for >>: 'list' and 'WrongType'

最新更新