公共 Cython API#
截至 2020 年 4 月,SciPy 中以下模块通过公共 cdef
Cython API 声明公开功能
scipy.linalg.cython_blas
scipy.linalg.cython_lapack
scipy.optimize.cython_optimize
scipy.special.cython_special
这使用 Cython 的声明共享功能,其中共享的 cdef
项在 *.pxd
文件中声明,这些文件与二进制 SciPy 安装中的相应 DLL/SO 文件一起分发。
应用程序二进制接口#
然而,在 SciPy 中使用这些功能需要 SciPy 贡献者在维护应用程序二进制接口 (ABI) 稳定性方面格外注意。这类似于在 C 中开发库,与纯 Python 中的向后兼容性工作方式不同。
与 Python 的主要区别在于,头文件 .pxd
文件中的声明在用户编写的代码编译时使用,但它们也必须与用户代码导入时 SciPy 中可用的声明匹配。
用户代码可以使用一个版本的 SciPy 编译,编译后的二进制文件(使用 .pxd
文件中声明的二进制接口)可以使用系统上安装的不同版本的 SciPy。如果接口不兼容,则会引发异常或发生运行时内存损坏和崩溃。
在导入时,Cython 检查已安装的 SciPy SO/DLL 文件中函数的签名是否与用户在编译期间使用的 .pxd
文件中的签名匹配,如果存在不匹配,则引发 Python 异常。如果 SciPy 代码结构正确(见下文),则仅针对用户代码中实际导入的函数执行此检查。
我们依赖于此功能来提供运行时安全检查,这使得用户更容易通过 Python 异常来检测不兼容的 SciPy 版本,而不是难以追踪的运行时崩溃。
ABI 稳定性目标#
SciPy 旨在以以下意义维护 Cython 代码中的 ABI 稳定性
使用一个版本的 SciPy 编译用户源代码生成的二进制文件与任何其他可以编译源代码的 SciPy 版本兼容。
尝试在运行时使用不兼容版本的 SciPy 将导致在用户模块导入时出现 Python 异常。
尝试在编译时使用不兼容版本的 SciPy 将导致 Cython 错误。
这意味着用户可以使用任何兼容版本的 SciPy 编译二进制文件,而无需关注 ABI,即
ABI 兼容性 = API 兼容性
Cython API 向后/向前兼容性将使用与 Python API 相似的弃用/删除策略进行处理,请参阅 弃用。
在 SciPy 中实现 ABI 稳定性#
以下规则是 SciPy 中 Cython API 开发中保持上述 ABI 稳定性目标所必需的
添加新的
cdef
声明(函数、结构体、类型等)是允许的。删除
cdef
声明是允许的,但应遵循一般的弃用/删除策略。cdef
函数声明可以更改。但是,更改会导致向后不兼容的 API 更改,这会破坏使用更改签名的任何代码,并且应遵循一般的弃用/删除策略。
cdef
其他任何事物的声明(例如struct
、enum
和类型)是最终的。一旦声明在发布的 SciPy 版本中的公共 Cython API 中公开,就不得更改。如果需要更改,则需要通过添加具有不同名称的新声明并删除旧声明来执行。
cdef
类不允许在公共 API 中(待定:cdef
类的向后兼容性需要更多研究,但在我们不确定时不得允许)对于每个公共 API 模块(如
scipy.linalg.cython_blas
),使用单个接口.pxd
声明文件。公共接口声明文件不应包含
cimport
语句。如果包含,Cython 的签名检查将检查所有 cimported 函数,而不仅仅是用户代码使用的函数,因此更改其中一个函数会破坏整个 API。如果数据结构是必要的,在公共 API 中首选不透明结构体。接口声明不应包含任何结构体成员声明。数据结构的分配、释放和属性访问应使用函数完成。
弃用公共 Cython API#
要弃用公共 Cython API 函数,例如
# scipy/something/foo.pxd
cdef public int somefunc()
# scipy/something/foo.pyx
cdef public int somefunc():
return 42
可以在相应的 .pyx
文件末尾添加使用 scipy._lib.deprecation.deprecate_cython_api
函数来执行弃用
# scipy/something/foo.pyx
cdef public int somefunc():
return 42
from scipy._lib.deprecation import deprecate_cython_api
import scipy.something.foo as mod
deprecate_cython_api(mod, "somefunc", new_name="scipy.something.newfunc",
message="Deprecated in Scipy 1.5.0")
del deprecate_cython_api, mod
此后,cimport somefunc
的 Cython 模块将在导入时发出 DeprecationWarning。
没有办法弃用 Cython 数据结构和类型。但是,在 API 中使用它们的任何函数都删除后,它们可以被删除,并且已经经历了弃用周期。
整个 Cython 模块可以像 Python 模块一样弃用,方法是在顶级发出 DeprecationWarning。