numpy.distutils
用户指南#
警告
numpy.distutils
已被弃用,并将在 Python >= 3.12 时移除。更多详情请参阅 numpy.distutils 的状态和迁移建议
SciPy 结构#
目前 SciPy 项目包含两个包
NumPy — 它提供以下包,例如
numpy.distutils - Python distutils 的扩展
numpy.f2py - 将 Fortran/C 代码绑定到 Python 的工具
numpy._core - Numeric 和 numarray 包的未来替代
numpy.lib - 额外的实用函数
numpy.testing - 用于单元测试的 NumPy 风格工具
等等
SciPy — 一个 Python 科学工具集合。
本文档旨在描述如何向 SciPy 添加新工具。
SciPy 包的要求#
SciPy 由 Python 包(称为 SciPy 包)组成,这些包通过 scipy
命名空间对 Python 用户可用。每个 SciPy 包可能包含其他 SciPy 包,依此类推。因此,SciPy 目录树是一个具有任意深度和宽度的包树。任何 SciPy 包都可以依赖 NumPy 包,但对其他 SciPy 包的依赖应保持最小或为零。
一个 SciPy 包除了其源代码外,还包含以下文件和目录
setup.py
— 构建脚本__init__.py
— 包初始化器tests/
— 单元测试目录
它们的内容在下面描述。
The setup.py
文件#
为了向 SciPy 添加一个 Python 包,其构建脚本 (setup.py
) 必须满足特定要求。最重要的要求是包必须定义一个 configuration(parent_package='',top_path=None)
函数,该函数返回一个适合传递给 numpy.distutils.core.setup(..)
的字典。为了简化此字典的构建,numpy.distutils.misc_util
提供了下面描述的 Configuration
类。
SciPy 纯 Python 包示例#
下面是一个纯 SciPy 包的最小 setup.py
文件示例
#!/usr/bin/env python3
def configuration(parent_package='',top_path=None):
from numpy.distutils.misc_util import Configuration
config = Configuration('mypackage',parent_package,top_path)
return config
if __name__ == "__main__":
from numpy.distutils.core import setup
#setup(**configuration(top_path='').todict())
setup(configuration=configuration)
configuration
函数的参数指定父 SciPy 包的名称 (parent_package
) 和主 setup.py
脚本的目录位置 (top_path
)。这些参数以及当前包的名称,应传递给 Configuration
构造函数。
Configuration
构造函数有一个第四个可选参数 package_path
,当包文件位于与 setup.py
文件目录不同的位置时可以使用。
其余的 Configuration
参数都是关键字参数,将用于初始化 Configuration
实例的属性。通常,这些关键字与 setup(..)
函数预期的关键字相同,例如 packages
、ext_modules
、data_files
、include_dirs
、libraries
、headers
、scripts
、package_dir
等。然而,不建议直接指定这些关键字,因为这些关键字参数的内容将不会被处理或检查 SciPy 构建系统的一致性。
最后,Configuration
具有 .todict()
方法,该方法将所有配置数据作为字典返回,适合传递给 setup(..)
函数。
Configuration
实例属性#
除了可以通过关键字参数传递给 Configuration
构造函数的属性外,Configuration
实例(我们称之为 config
)还具有以下属性,这些属性在编写设置脚本时可能很有用
config.name
- 当前包的完整名称。父包的名称可以通过config.name.split('.')
提取。config.local_path
- 当前setup.py
文件所在位置的路径。config.top_path
- 主setup.py
文件所在位置的路径。
Configuration
实例方法#
config.todict()
— 返回适合传递给numpy.distutils.core.setup(..)
函数的配置字典。config.paths(*paths) --- 如果需要,将 ``glob.glob(..)
应用于paths
中的项。修复相对于config.local_path
的paths
项。config.get_subpackage(subpackage_name,subpackage_path=None)
— 返回子包配置列表。子包会在当前目录下以subpackage_name
名称查找,但路径也可以通过可选的subpackage_path
参数指定。如果subpackage_name
指定为None
,则子包名称将取自subpackage_path
的基本名称。用于子包名称的任何*
都将作为通配符展开。config.add_subpackage(subpackage_name,subpackage_path=None)
— 将 SciPy 子包配置添加到当前配置中。参数的含义和用法如上所述,请参阅config.get_subpackage()
方法。config.add_data_files(*files)
— 将files
添加到data_files
列表的开头。如果files
项是元组,则其第一个元素定义数据文件相对于包安装目录的复制位置后缀,第二个元素指定数据文件的路径。默认情况下,数据文件复制到包安装目录下。例如,config.add_data_files('foo.dat', ('fun',['gun.dat','nun/pun.dat','/tmp/sun.dat']), 'bar/car.dat'. '/full/path/to/can.dat', )
将数据文件安装到以下位置
<installation path of config.name package>/ foo.dat fun/ gun.dat pun.dat sun.dat bar/ car.dat can.dat
数据文件的路径可以是一个不带参数并返回数据文件路径的函数——这在构建包时生成数据文件的情况下很有用。(XXX:精确解释此函数何时被调用)
config.add_data_dir(data_path)
— 将目录data_path
递归地添加到data_files
。从data_path
开始的整个目录树将被复制到包安装目录下。如果data_path
是一个元组,则其第一个元素定义数据文件相对于包安装目录的复制位置后缀,第二个元素指定数据目录的路径。默认情况下,数据目录会复制到包安装目录下,以data_path
的基本名称作为子目录。例如,config.add_data_dir('fun') # fun/ contains foo.dat bar/car.dat config.add_data_dir(('sun','fun')) config.add_data_dir(('gun','/full/path/to/fun'))
将数据文件安装到以下位置
<installation path of config.name package>/ fun/ foo.dat bar/ car.dat sun/ foo.dat bar/ car.dat gun/ foo.dat bar/ car.dat
config.add_include_dirs(*paths)
— 将paths
添加到include_dirs
列表的开头。此列表对当前包的所有扩展模块都可见。config.add_headers(*files)
— 将files
添加到headers
列表的开头。默认情况下,头文件将安装在<prefix>/include/pythonX.X/<config.name.replace('.','/')>/
目录下。如果files
项是元组,则其第一个参数指定相对于<prefix>/include/pythonX.X/
路径的安装后缀。这是一个 Python distutils 方法;不鼓励在 NumPy 和 SciPy 中使用它,而推荐使用config.add_data_files(*files)
。config.add_scripts(*files)
— 将files
添加到scripts
列表的开头。脚本将安装在<prefix>/bin/
目录下。config.add_extension(name,sources,**kw)
— 创建并添加一个Extension
实例到ext_modules
列表。第一个参数name
定义了将安装在config.name
包下的扩展模块的名称。第二个参数是源文件列表。add_extension
方法也接受传递给Extension
构造函数的关键字参数。允许的关键字列表如下:include_dirs
、define_macros
、undef_macros
、library_dirs
、libraries
、runtime_library_dirs
、extra_objects
、extra_compile_args
、extra_link_args
、export_symbols
、swig_opts
、depends
、language
、f2py_options
、module_dirs
、extra_info
、extra_f77_compile_args
、extra_f90_compile_args
。请注意,
config.paths
方法适用于所有可能包含路径的列表。extra_info
是一个字典或字典列表,其内容将被附加到关键字参数中。depends
列表包含扩展模块源文件所依赖的文件或目录的路径。如果depends
列表中的任何路径比扩展模块新,则该模块将被重建。源文件列表可能包含具有模式
def <funcname>(ext, build_dir): return <source(s) or None>
的函数(“源生成器”)。如果funcname
返回None
,则不生成任何源文件。如果在处理所有源生成器后Extension
实例没有源文件,则不会构建任何扩展模块。这是有条件地定义扩展模块的推荐方法。源生成器函数由numpy.distutils
的build_src
子命令调用。例如,下面是一个典型的源生成器函数
def generate_source(ext,build_dir): import os from distutils.dep_util import newer target = os.path.join(build_dir,'somesource.c') if newer(target,__file__): # create target file return target
第一个参数包含 Extension 实例,这对于访问其属性(如
depends
、sources
等列表)并在构建过程中修改它们非常有用。第二个参数提供了构建目录的路径,在创建文件到磁盘时必须使用此路径。config.add_library(name, sources, **build_info)
— 将库添加到libraries
列表。允许的关键字参数有depends
、macros
、include_dirs
、extra_compiler_args
、f2py_options
、extra_f77_compile_args
、extra_f90_compile_args
。有关参数的更多信息,请参阅.add_extension()
方法。config.have_f77c()
— 如果 Fortran 77 编译器可用(即:一个简单的 Fortran 77 代码成功编译),则返回 True。config.have_f90c()
— 如果 Fortran 90 编译器可用(即:一个简单的 Fortran 90 代码成功编译),则返回 True。config.get_version()
— 返回当前包的版本字符串,如果无法检测到版本信息则返回None
。此方法扫描文件__version__.py
、<packagename>_version.py
、version.py
、__svn_version__.py
以查找字符串变量version
、__version__
、<packagename>_version
。config.make_svn_version_py()
— 向data_files
列表添加一个数据函数,该函数将生成__svn_version__.py
文件到当前包目录。该文件将在 Python 退出时从源目录中移除。config.get_build_temp_dir()
— 返回一个临时目录的路径。这是应该构建临时文件的地方。config.get_distribution()
— 返回 distutilsDistribution
实例。config.get_config_cmd()
— 返回numpy.distutils
配置命令实例。config.get_info(*names)
—
使用模板转换 .src
文件#
NumPy distutils 支持名为 <somefile>.src 的源文件的自动转换。此功能可用于维护非常相似的代码块,仅需在块之间进行简单更改。在 setup 的构建阶段,如果遇到名为 <somefile>.src 的模板文件,则会从该模板构建一个名为 <somefile> 的新文件,并将其放置在构建目录中以供使用。支持两种形式的模板转换。第一种形式适用于名为 <file>.ext.src 的文件,其中 ext 是已识别的 Fortran 扩展名(f, f90, f95, f77, for, ftn, pyf)。第二种形式用于所有其他情况。
Fortran 文件#
此模板转换器将根据“<…>”中的规则,复制文件中所有名称包含“<…>”的 函数 和 子例程 块。“<…>”中逗号分隔的单词数量决定了块重复的次数。这些单词表示在每个块中应替换重复规则“<…>”的内容。块中的所有重复规则必须包含相同数量的逗号分隔单词,以指示该块应重复的次数。如果重复规则中的单词需要逗号、左箭头或右箭头,则在其前面加上反斜杠 ' '。如果重复规则中的单词匹配 ' \<index>',则它将被替换为相同重复规范中的第 <index> 个单词。重复规则有两种形式:命名形式和简短形式。
命名重复规则#
当块中需要多次使用同一组重复时,命名重复规则很有用。它使用 <rule1=item1, item2, item3,…, itemN> 指定,其中 N 是块应重复的次数。每次重复块时,整个表达式“<…>”将首先被 item1 替换,然后是 item2,依此类推,直到完成 N 次重复。一旦引入命名重复规范,相同的重复规则可以仅通过引用名称(即 <rule1>)在 当前块中 使用。
简短重复规则#
简短重复规则类似于 <item1, item2, item3, …, itemN>。该规则指定整个表达式“<…>”应首先被 item1 替换,然后是 item2,依此类推,直到完成 N 次重复。
预定义名称#
以下预定义的命名重复规则可用
<prefix=s,d,c,z>
<_c=s,d,c,z>
<_t=real, double precision, complex, double complex>
<ftype=real, double precision, complex, double complex>
<ctype=float, double, complex_float, complex_double>
<ftypereal=float, double precision, \0, \1>
<ctypereal=float, double, \0, \1>
其他文件#
非 Fortran 文件使用单独的语法来定义模板块,这些模板块应使用类似于 Fortran 特有重复规则中的命名变量扩展进行重复。
NumPy Distutils 预处理用自定义模板语言编写的 C 源文件(扩展名:.c.src
),以生成 C 代码。@
符号用于包裹宏风格的变量,以实现字符串替换机制,该机制可以描述(例如)一组数据类型。
模板语言块由 /**begin repeat
和 /**end repeat**/
行分隔,这些行也可以使用连续编号的分隔行(如 /**begin repeat1
和 /**end repeat1**/
)进行嵌套。
单独一行的
/**begin repeat
标记了应重复片段的开始。命名变量扩展使用
#name=item1, item2, item3, ..., itemN#
定义,并放置在连续的行上。这些变量在每个重复块中会被替换为相应的词。同一重复块中的所有命名变量必须定义相同数量的词。在指定命名变量的重复规则时,
item*N
是item, item, ..., item
重复 N 次的简写。此外,括号与*N
结合使用可以用于分组多个应重复的项。因此,#name=(item1, item2)*4#
等同于#name=item1, item2, item1, item2, item1, item2, item1, item2#
。单独一行的
*/
标记了变量扩展命名的结束。下一行是使用命名规则重复的第一行。在要重复的块内部,应扩展的变量指定为
@name@
。单独一行的
/**end repeat**/
标记了前一行作为要重复块的最后一行。NumPy C 源代码中的一个循环可能包含一个用于字符串替换的
@TYPE@
变量,它被预处理为多个在其他方面相同的循环,其中包含INT
、LONG
、UINT
、ULONG
等字符串。因此,@TYPE@
风格的语法通过模仿支持泛型类型的语言,减少了代码重复和维护负担。
上述规则在以下模板源示例中可能更清晰
1 /* TIMEDELTA to non-float types */
2
3 /**begin repeat
4 *
5 * #TOTYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
6 * LONGLONG, ULONGLONG, DATETIME,
7 * TIMEDELTA#
8 * #totype = npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint,
9 * npy_long, npy_ulong, npy_longlong, npy_ulonglong,
10 * npy_datetime, npy_timedelta#
11 */
12
13 /**begin repeat1
14 *
15 * #FROMTYPE = TIMEDELTA#
16 * #fromtype = npy_timedelta#
17 */
18 static void
19 @FROMTYPE@_to_@TOTYPE@(void *input, void *output, npy_intp n,
20 void *NPY_UNUSED(aip), void *NPY_UNUSED(aop))
21 {
22 const @fromtype@ *ip = input;
23 @totype@ *op = output;
24
25 while (n--) {
26 *op++ = (@totype@)*ip++;
27 }
28 }
29 /**end repeat1**/
30
31 /**end repeat**/
泛型类型 C 源文件(无论是在 NumPy 本身还是在使用 NumPy Distutils 的任何第三方包中)的预处理由 conv_template.py 执行。这些模块在构建过程中生成的特定类型 C 文件(扩展名:.c
)已准备好编译。这种泛型类型也支持 C 头文件(预处理以生成 .h
文件)。
numpy.distutils.misc_util
中有用的函数#
get_numpy_include_dirs()
— 返回 NumPy 基本包含目录的列表。NumPy 基本包含目录包含诸如numpy/arrayobject.h
、numpy/funcobject.h
等头文件。对于已安装的 NumPy,返回的列表长度为 1,但在构建 NumPy 时,该列表可能包含更多目录,例如numpy/base/setup.py
文件生成并由numpy
头文件使用的config.h
文件的路径。append_path(prefix,path)
— 智能地将path
附加到prefix
。gpaths(paths, local_path='')
— 如有需要,将 glob 应用于路径并前置local_path
。njoin(*path)
— 连接路径名组件 + 将/
分隔的路径转换为os.sep
分隔的路径,并解析路径中的..
、.
。例如:njoin('a',['b','./c'],'..','g') -> os.path.join('a','b','g')
。minrelpath(path)
— 解析path
中的点。rel_path(path, parent_path)
— 返回path
相对于parent_path
的路径。def get_cmd(cmdname,_cache={})
— 返回numpy.distutils
命令实例。all_strings(lst)
has_f_sources(sources)
has_cxx_sources(sources)
filter_sources(sources)
— 返回c_sources, cxx_sources, f_sources, fmodule_sources
get_dependencies(sources)
is_local_src_dir(directory)
get_ext_source_files(ext)
get_script_files(scripts)
get_lib_source_files(lib)
get_data_files(data)
dot_join(*args)
— 使用点连接非零参数。get_frame(level=0)
— 从给定级别的调用堆栈返回帧对象。cyg2win32(path)
mingw32()
— 在使用 mingw32 环境时返回True
。terminal_has_colors()
,red_text(s)
,green_text(s)
,yellow_text(s)
,blue_text(s)
,cyan_text(s)
get_path(mod_name,parent_path=None)
— 返回模块相对于给定 parent_path 的路径。也处理__main__
和__builtin__
模块。allpath(name)
— 在name
中用os.sep
替换/
。cxx_ext_match
,fortran_ext_match
,f90_ext_match
,f90_module_name_match
numpy.distutils.system_info
模块#
get_info(name,notfound_action=0)
combine_paths(*args,**kws)
show_all()
numpy.distutils.cpuinfo
模块#
cpuinfo
numpy.distutils.log
模块#
set_verbosity(v)
numpy.distutils.exec_command
模块#
get_pythonexe()
find_executable(exe, path=None)
exec_command( command, execute_in='', use_shell=None, use_tee=None, **env )
The __init__.py
文件#
典型的 SciPy __init__.py
的头部是
"""
Package docstring, typically with a brief description and function listing.
"""
# import functions into module namespace
from .subpackage import *
...
__all__ = [s for s in dir() if not s.startswith('_')]
from numpy.testing import Tester
test = Tester().test
bench = Tester().bench
NumPy Distutils 中的额外特性#
在 setup.py 脚本中为库指定 config_fc 选项#
可以在 setup.py 脚本中指定 config_fc 选项。例如,使用
config.add_library('library',
sources=[...],
config_fc={'noopt':(__file__,1)})
将编译 library
源文件,但不带优化标志。
建议仅以编译器独立的方式指定 config_fc 选项。
从源文件获取额外的 Fortran 77 编译器选项#
一些旧的 Fortran 代码需要特殊的编译器选项才能正常工作。为了为每个源文件指定编译器选项,numpy.distutils
Fortran 编译器会在源代码的前 20 行中查找以下模式
CF77FLAGS(<fcompiler type>) = <fcompiler f77flags>
在源文件的前 20 行中,并使用 f77flags
用于指定类型的 fcompiler(第一个字符 C
是可选的)。
待办:此功能也可以轻松扩展到 Fortran 90 代码。如果您需要此功能,请告知我们。