Python Distutils-SIG:设计提案
Python Distutils-SIG
(前提:请在尝试阅读此设计文档之前阅读建议的接口;它很大程度上是接口文档的续集。)
设计提案
Distutils 的观点
setup.py 只需要导入一个模块 distutils.core
。该模块负责解析 setup.py 的所有命令行参数(即使选项的解释分布在各种 Distutils 命令中,并且可能分布在客户端 setup.py 中)。它还负责从 setup.py 接收控制,并将其适当地传递给 Distutils 命令。最重要的是,distutils.core
定义了 Distribution
类,它是 Distutils 的核心和灵魂。客户端 (setup.py) 的主要作用是为 Distribution
实例提供属性,并且所有 Distutils 命令都在该实例上运行。distutils.core
还定义了 Command
类,该类对于实现 Distutils 命令非常方便。
说到 Distutils 命令:每个命令都实现为一个 Python 模块,例如 build
命令由 distutils.build
模块实现。每个命令模块都需要定义一个类,也以命令命名,例如 distutils.build.Build
。这些命令类将继承自 Command
类,该类(至少)将提供一种处理命令特定选项的方法。(Command
将提供一个构造函数,该构造函数接受 Distribution
类和此命令的可选参数列表,并通过检查 Command
派生实例中的 getopt 样式的选项说明符来解析参数列表。)每个命令类都必须提供一个方法 run
,该方法使用 Distribution
实例中的信息和命令选项来“完成它的工作”。编写良好的命令类会将此任务分成几个定义明确(且有文档记录)的方法,以便客户端 setup.py 可以继承并覆盖 Distutils 命令类的特定行为。这也意味着 Distribution
类必须有一种方法将覆盖的命令类传递给主调度程序。
客户端的观点
正如我上面所说,客户端 (setup.py) 只需要导入 distutils.core
– 所有其他的 Distutils 相关的东西都由这个核心模块处理。但是,客户端需要一种方法将其特定的选项传递到 Distutils 核心(并传递给命令模块)。
有两种可能的方案:一种简短方便(但不太可扩展),另一种有点冗长且笨拙(但更面向对象和可扩展)。没有理由我们不能鱼与熊掌兼得;对于许多不需要大量自定义的模块发行版,方便的接口可以只是完整接口的包装器。
首先,这是一个简单接口的示例,用于具有单个“纯 Python”模块 (mymod.py) 的模块发行版。
from distutils.core import setup setup (name = "mymod", version = "1.2", author = "Greg Ward <gward@cnri.reston.va.us>", description = "A very simple, one-module distribution")请注意,我们没有在任何地方显式列出 mymod.py:Distutils 假设这是一个以其唯一模块 (
mymod
) 命名的单模块发行版。
那些喜欢定义子类的人可能更喜欢用不同的方式表达
from distutils.core import Distribution, setup class MyDistribution (Distribution): name = "mymod" version = "1.2", author = "Greg Ward <gward@cnri.reston.va.us>", description = "A very simple, one-module distribution") setup (distclass = MyDistribution)对于小型发行版来说,这是多余的:我们定义了一个新类,仅仅是为了提供属性值,而
distutils.core.setup
的主要作用是让我们无论如何都能做到这一点。尽管如此,面向对象的纯粹主义者会喜欢这样 – 并且毫无疑问,有时客户端将不得不覆盖行为,而不仅仅是数据,并且面向对象的接口将是必要的。
对于具有大量属性需要自定义的更复杂的模块发行版,像这样分解可能会更容易阅读/维护。考虑一个具有两个纯 Python 模块(mymod
和 my_othermod
)和一个 C 扩展(myext
)的发行版;C 扩展必须与两个辅助 C 文件和一个 C 库链接。哦,对了,这个发行版需要 Python 1.5 和任何版本的 re
模块(忽略一个通常意味着另一个的事实)
from distutils.core import Distribution, setup class MyDistribution (Distribution): name = "mydist", version = "1.3.4", author = "Greg Ward <gward@cnri.reston.va.us>" description = """\ This is an example module distribution. It provides no useful code, but is an interesting example of the Distutils in action.""" # Dependencies requires = { 'python': '1.5', # I like class-based exceptions 're': '*', # and I love Perl-style regexps! ;-) } # Actual files that need to be processed and installed in some form py_modules = ['mymod.py', 'my_othermod.py'], ext_modules = {'myext.c': {'other_c': ['extra1.c', 'extra2.c'], 'c_libraries': ['mylib']} } setup (distclass = MyDistribution)
有几点需要注意
- 我不怕使用深度嵌套的数据结构;如果您正在编写和分发 Python 模块,这应该不是问题!
- 每个属性都有特定的类型(字符串、列表、字典...)
- 具有复杂类型(尤其是字典)的属性将具有众所周知的且有据可查的内部结构,例如
"""ext_modules is a hash mapping names of C source files (each containing a Python extension module) to a nested hash of information about how to build that module. The allowed keys to this nested hash are: - other_c: other C files that must be compiled and linked with the main C file to create the module - c_libraries: C libraries that must be included in the link ... """
毫无疑问,ext_modules
嵌套哈希将具有更多选项,并且毫无疑问其他 Distribution
属性将具有复杂且有文档记录的结构。
最后,所有 Distribution
属性的列表必须是众所周知的并且有据可查!这些似乎分为几个大类。这是一个初步的列表尝试
- 发行版元数据
名称
版本
作者
描述
- 依赖项
需要
- 要处理和安装的文件
py_modules
ext_modules
doc_files
- 构建目录(默认情况下都在 ./blib 下)
build_lib
– 用于放置与平台无关的库文件build_platlib
– 用于放置与平台相关的库文件build_exe
– 用于放置可执行程序(即脚本)build_html
– 用于放置已处理的文档 (HTML)
- 安装目录(默认情况下在
sysconfig.LIBDEST
下)install_lib
install_platlib
install_exe
install_html
再次审视 Distutils 的观点
总而言之,让我们回顾一下用户运行 setup.py 时会发生什么。无论 setup.py 是以简单(调用函数)还是通用(定义子类)的形式编写,都无关紧要,所以我不会将事情分成两个流程。
- setup.py 导入
distutils.core
distutils.core
启动代码解析命令行参数:处理它知道的全局选项,并保存其余的供客户端 (setup.py) 处理;找出每个命令的命令和选项,并将它们全部保存起来以供稍后处理- setup.py 调用
distutils.core.setup
(可能带有distclass
参数,该参数指定Distribution
的子类,可能带有大量其他命名参数,这些参数指定Distribution
实例的各种属性) distutils.core.setup
实例化Distribution
(或客户端提供的子类),并使用其参数(distclass
除外)来覆盖此实例的属性distutils.core.setup
加载命令模块(例如distutils.build
)distutils.core.setup
确定命令类(通常只是以命令命名,例如distutils.build.Build
,但也可能是客户端作为Distribution
实例的属性之一提供的类)并实例化它- 命令类构造函数将
Distribution
实例和 setup.py 命令行上特定于此命令的任何命令行参数作为参数 - 命令类构造函数解析其选项以设置/覆盖一些实例属性
distutils.core.setup
调用命令对象的run
方法- 该方法执行命令应该执行的任何操作:构建模块、处理文档、安装文件等。
distutils.core.setup
确定下一个命令类(如果给出了多个命令),并像以前一样继续执行