PyPy 的 ctypes 实现¶
总结¶
术语
- 应用程序级代码 - 用完整 Python 编写的代码
- 解释器级代码 - 用 RPython 编写的代码,编译成其他东西,比如 C,解释器的一部分。
PyPy 的 ctypes 实现,在其当前状态下证明了实现一个模块的可行性,该模块具有与 CPython 的 ctypes 相同的接口和行为。
PyPy 的实现内部使用 libffi,就像 CPython 的 ctypes 一样。在我们的实现中,尽可能多的代码是用完整的 Python 编写的,而不是 RPython。在 CPython 的情况下,等效的操作是尽可能少地用 C 编写代码。我们本质上更倾向于快速实验,而不是担心这个第一个试用实现的速度。这使得我们能够在 2 个月的实际时间内提供一个具有 ctypes 功能大部分的可用实现。
我们重用了 CPython 中的 ctypes
包版本 1.0.2,原封不动。我们实现了 _ctypes
,它是一个 C 模块,在 CPython 中主要用纯 Python 实现,基于一个更底层的扩展模块 _rawffi
。
底层部分:_rawffi
¶
这个 PyPy 扩展模块 (pypy/module/_rawffi) 提供了一个简单的接口来创建 C 对象(数组和结构)并通过 libffi 调用动态库中的函数。在大多数情况下释放对象,并确保引用彼此的对象保持活动,这是更高层的责任。
这个模块使用 libffi 的绑定,这些绑定在 rpython/rlib/libffi.py 中定义。
我们试图使这个模块尽可能小。可以想象,其他实现(例如 Jython)可以通过编写自己的 _rawffi
版本来使用我们的 ctypes 实现。
高级部分¶
重用的 ctypes
包位于 lib_pypy/ctypes
中。实现与 CPython 中 _ctypes
相同接口的 _ctypes
位于 lib_pypy/_ctypes
中。
讨论和限制¶
重新实现 ctypes 功能通常是可能的。PyPy 支持可插拔的垃圾收集器,其中一些是移动收集器,这意味着将 Python 对象中的直接引用传递给外部库的策略是不可行的(除非 GC 支持固定,而现在还没有)。这样做的结果是,有时需要复制而不是共享,这可能会导致一些语义差异。用 _rawffi 本身创建的 C 对象在 GC 堆之外分配,这样就可以将它们传递给外部函数,而无需担心。
将实现移植到解释器级别可能会提高其速度。此外,当前的分层和当前的 _rawffi 接口比严格必要的需要更多对象分配和复制;这也可以改进。
以下是当前实现的限制和缺失功能列表
ctypes.pythonapi
缺失。在之前的版本中,它存在并重定向到 cpyext C API 模拟层,但我们的实现没有对 GIL 做任何明智的处理,并且函数名称多了一个“Py”,例如PyPyInt_FromLong()
。它被移除是因为它没有帮助。- 我们复制 Python 字符串,而不是使用指向原始缓冲区的指针
- 我们没有实现的功能
- 自定义对齐和位域
- 调整大小(
resize()
函数) - 非原生字节序对象
- 接受按值传递结构的回调函数
- ctypes 在其基本类型和用户对基本类型的子类之间存在细微的语义差异
运行应用程序示例¶
pyglet 已知可以运行。我们还尝试了 pygame-ctypes(它不再维护)和 pysqlite-ctypes 的快照,并取得了一些成功。我们只描述如何运行 pyglet 示例。
pyglet¶
我们尝试从其存储库中检出 pyglet,版本为 1984。
从 pyglet 中,以下示例已知可以运行
- opengl.py
- multiple_windows.py
- events.py
- html_label.py
- timer.py
- window_platform_event.py
- fixed_resolution.py
用于运行 ctypes 测试的 pypy-c 翻译也可以用来运行 pyglet 示例。它们可以像这样运行,例如
$ cd pyglet/
$ PYTHONPATH=. ../ctypes-stable/pypy/goal/pypy-c examples/opengl.py
它们通常应该使用 ctrl-c 终止。有关它们应该如何运行的详细信息,请参阅它们的文档字符串。
以下示例由于与 ctypes 无关的原因无法运行
- image_convert.py 需要 PIL
- image_display.py 需要 PIL
- astraea/astraea.py 需要 PIL
我们没有尝试以下示例
- media_player.py 需要 avbin 或至少需要为 .wav 文件设置合适的声卡
- video.py 需要 avbin
- soundscape 需要 avbin