CTypes,从包装器引发python样式的异常

时间:2019-09-12 16:42:36

标签: python ctypes

在使用可调用程序修补原始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异常样式行为:

Get error message from ctypes windll

Ctypes catching exception

更新:

再进行一些挖掘之后,似乎在这种方法中的任何地方加注都会触发段错误。没有一个明智的解决方法。

1 个答案:

答案 0 :(得分:0)

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

手动收集错误信息(使用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"\n\tAs this package patches primitive types, the python RTE also raised:", file=sys.stderr)
            return ErrorSingleton

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

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

后跟一个TypeError

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