微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 ctypes,如何将 Python 'bytes' 值传递给需要无符号字符指针的 C 函数?

如何解决使用 ctypes,如何将 Python 'bytes' 值传递给需要无符号字符指针的 C 函数?

我有一个 C 库,其中有一个函数,其中有一个 (const unsigned char *) 类型参数,我想为此编写一个基于 ctypes 的 Python 绑定。

在 Python 版本中,我希望能够传递一个“字节”值。

直接传递“字节”值不起作用。我得到一个 ctypes.ArgumentError:

ctypes.ArgumentError: argument 6: <class 'TypeError'>: expected LP_c_ubyte instance instead of bytes

我可以通过使用 ctypes.cast() 方法将 'bytes' 值转换为 unsigned char * 来使其工作。这似乎是显而易见的事情,效果很好,但是当我在我的代码上运行 mypy 时,我收到以下错误

source/pydwf/core/api/DigitalCanAPI.py:65: error: Argument 1 to "cast" has incompatible type "bytes"; expected "Union[_CData,_CArgObject,int]"
Found 1 error in 1 file (checked 52 source files)

那么,有没有一种方法可以写出同时满足 Python(所以它可以工作)和 mypy(所以它对演员表很满意?)

这可能是 mypy 中的错误吗?

解决方法

您可以直接构造一个 c_ubyte 数组,也可以通过一个 c_char_p() 实例小心地将其强制转换以消除 mypy 警告:

test.c

#include <stdio.h>

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

// dump data as hexadecimal
API void func(unsigned char* data,size_t length) {
    for(size_t i = 0; i < length; ++i)
        printf("%02x",data[i]);
    printf("\n");
}

test.py

from ctypes import *

dll = CDLL('./test')
dll.func.argtypes = POINTER(c_ubyte),c_size_t
dll.func.restype = None

# demo passing data that might not be nul-terminated
data = b'\x11\x22\x33\x00\x44\x55\x66'
dll.func(cast(c_char_p(data),POINTER(c_ubyte)),len(data))

bdata = (c_ubyte * len(data))(*data)
dll.func(bdata,len(data))

输出:

11223300445566
11223300445566

脚本通过mypy

C:\>mypy test.py
Success: no issues found in 1 source file
,

你的演员表是错误的,因为char != unsigned char。如果您的意思是真正的 unsigned char,那么您就不能使用所有的 char 辅助方法。在定义要签名的字符的设置中,您会遇到奇怪的错误。但是,如果您对自己的实现/c 编译器总是将 char 定义为 unsigned char 感到高兴,那么就根本不用理会 u_byte。只需根据需要将 arg 类型和返回类型定义为 c_char 或 c_char_p。

如果您需要坚持使用 u_byte,那么这是正确的,并且让 mypy 开心(无需演员表)。

import ctypes.util

# or whatever to get some library function
libc_path = ctypes.util.find_library('msvcrt')
libc = ctypes.WinDLL(libc_path)

# snprintf actually takes char,but this should be fine for most setups
snprintf = libc._snprintf
snprintf.argtypes = (
    ctypes.POINTER(ctypes.c_ubyte),ctypes.c_size_t,ctypes.POINTER(ctypes.c_ubyte),ctypes.c_int,# white lie about the number of arguments snprintf takes
)
snprintf.restype = ctypes.c_int


def to_ubytes(bytes_):
    # nul terminate the string just to be safe
    u_bytes = (ctypes.c_ubyte * (len(bytes_) + 1))()
    u_bytes[:-1] = bytes_
    u_bytes[-1] = 0
    return u_bytes

buffer = (ctypes.c_ubyte * 20)()
format_ = to_ubytes(b'hello %i\n')

result = snprintf(buffer,len(buffer),format_,1234)

if result >= 0:
    result_bytes = bytes(buffer[:result])
    print('result is:',result_bytes)
else:
    print('snprintf encountered an error')

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。