6. Distutils 示例 — Python 文档

来自菜鸟教程
Python/docs/3.9/distutils/examples
跳转至:导航、​搜索

6. Distutils 示例

笔记

本文档仅保留到 https://setuptools.readthedocs.io/en/latest/setuptools.html 上的 setuptools 文档独立涵盖当前包含在此处的所有相关信息之前。


本章提供了许多基本示例来帮助开始使用 distutils。 有关使用 distutils 的其他信息可以在 Distutils Cookbook 中找到。

也可以看看

Distutils 食谱
展示如何更好地控制 distutils 的食谱集。


6.1. 纯 Python 发行版(按模块)

如果您只是分发几个模块,特别是如果它们不在特定包中,则可以使用安装脚本中的 py_modules 选项单独指定它们。

在最简单的情况下,您需要担心两个文件:一个安装脚本和您分发的单个模块,在本例中为 foo.py

<root>/
        setup.py
        foo.py

(在本节的所有图表中,' 将引用分发根目录。)描述这种情况的最小安装脚本是:

from distutils.core import setup
setup(name='foo',
      version='1.0',
      py_modules=['foo'],
      )

请注意,发行版的名称是用 name 选项独立指定的,并且没有规定它必须与发行版中唯一模块的名称相同(尽管这可能是一个很好的约定跟随)。 但是,分发名称用于生成文件名,因此您应该坚持使用字母、数字、下划线和连字符。

由于 py_modules 是一个列表,您当然可以指定多个模块,例如。 如果您分发模块 foobar,您的设置可能如下所示:

<root>/
        setup.py
        foo.py
        bar.py

安装脚本可能是

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      py_modules=['foo', 'bar'],
      )

您可以将模块源文件放到另一个目录中,但是如果您有足够的模块来执行此操作,那么按包指定模块可能比单独列出它们更容易。


6.2. 纯 Python 发行版(按包)

如果您有多个模块要分发,特别是如果它们位于多个包中,则指定整个包而不是单个模块可能更容易。 即使您的模块不在包中,这也有效; 你可以告诉 Distutils 从根包处理模块,这与任何其他包的工作方式相同(除了你不必有 __init__.py 文件)。

上一个例子中的设置脚本也可以写成

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      packages=[''],
      )

(空字符串代表根包。)

如果这两个文件被移动到一个子目录中,但保留在根包中,例如:

<root>/
        setup.py
        src/      foo.py
                  bar.py

那么你仍然会指定根包,但你必须告诉 Distutils 根包中的源文件所在的位置:

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      package_dir={'': 'src'},
      packages=[''],
      )

但是,更常见的是,您希望在同一个包(或子包)中分发多个模块。 例如,如果 foobar 模块属于包 foobar,则布局源树的一种方法是

<root>/
        setup.py
        foobar/
                 __init__.py
                 foo.py
                 bar.py

这实际上是 Distutils 所期望的默认布局,并且在您的安装脚本中需要最少的工作来描述:

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      packages=['foobar'],
      )

如果您想将模块放在未以其包命名的目录中,则需要再次使用 package_dir 选项。 例如,如果 src 目录包含 foobar 包中的模块:

<root>/
        setup.py
        src/
                 __init__.py
                 foo.py
                 bar.py

一个合适的安装脚本是

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      package_dir={'foobar': 'src'},
      packages=['foobar'],
      )

或者,您可以将主包中的模块直接放在分发根目录中:

<root>/
        setup.py
        __init__.py
        foo.py
        bar.py

在这种情况下,您的安装脚本将是

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      package_dir={'foobar': ''},
      packages=['foobar'],
      )

(空字符串也代表当前目录。)

如果您有子包,它们必须在 packages 中明确列出,但 package_dir 中的任何条目都会自动扩展到子包。 (换句话说,Distutils 不会 扫描你的源代码树,试图通过查找 __init__.py 文件来找出对应于 Python 包的目录。)因此,如果默认布局增长了子包:

<root>/
        setup.py
        foobar/
                 __init__.py
                 foo.py
                 bar.py
                 subfoo/
                           __init__.py
                           blah.py

那么相应的安装脚本将是

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      packages=['foobar', 'foobar.subfoo'],
      )

6.3. 单扩展模块

使用 ext_modules 选项指定扩展模块。 package_dir 对找到扩展源文件的位置没有影响; 它只影响纯 Python 模块的源代码。 最简单的情况,单个 C 源文件中的单个扩展模块是:

<root>/
        setup.py
        foo.c

如果 foo 扩展属于根包,则此安装脚本可能是

from distutils.core import setup
from distutils.extension import Extension
setup(name='foobar',
      version='1.0',
      ext_modules=[Extension('foo', ['foo.c'])],
      )

如果扩展实际上属于一个包,比如 foopkg,那么

使用完全相同的源代码树布局,只需更改扩展名,即可将此扩展放入 foopkg 包中:

from distutils.core import setup
from distutils.extension import Extension
setup(name='foobar',
      version='1.0',
      ext_modules=[Extension('foopkg.foo', ['foo.c'])],
      )

6.4. 检查包裹

check 命令允许您验证您的包元数据是否满足构建发行版的最低要求。

要运行它,只需使用您的 setup.py 脚本调用它。 如果缺少某些内容,check 将显示警告。

让我们以一个简单的脚本为例:

from distutils.core import setup

setup(name='foobar')

运行 check 命令将显示一些警告:

$ python setup.py check
running check
warning: check: missing required meta-data: version, url
warning: check: missing meta-data: either (author and author_email) or
         (maintainer and maintainer_email) should be supplied

如果您在 long_description 字段中使用 reStructuredText 语法并且安装了 docutils,则可以使用 restructuredtext 命令检查语法是否正确] 选项。

例如,如果 setup.py 脚本更改如下:

from distutils.core import setup

desc = """\
My description
==============

This is the description of the ``foobar`` package.
"""

setup(name='foobar', version='1', author='tarek',
    author_email='tarek@ziade.org',
    url='http://example.com', long_description=desc)

如果长描述被破坏,check 将能够使用 docutils 解析器检测到它:

$ python setup.py check --restructuredtext
running check
warning: check: Title underline too short. (line 2)
warning: check: Could not finish the parsing.

6.5. 读取元数据

distutils.core.setup() 函数提供了一个命令行界面,允许您通过给定项目的 setup.py 脚本查询项目的元数据字段:

$ python setup.py --name
distribute

此调用通过运行 distutils.core.setup() 函数读取 name 元数据。 但是,当使用 Distutils 创建源或二进制分发时,元数据字段写入名为 PKG-INFO 的静态文件中。 当在 Python 中安装基于 Distutils 的项目时,PKG-INFO 文件与 NAME-VERSION-pyX.X.egg-info 下的分发模块和包一起复制,其中 NAME 是项目的名称, VERSION 元数据中定义的版本,pyX.X Python 的主要和次要版本,如 2.73.2

您可以使用 distutils.dist.DistributionMetadata 类及其 read_pkg_file() 方法读回此静态文件:

>>> from distutils.dist import DistributionMetadata
>>> metadata = DistributionMetadata()
>>> metadata.read_pkg_file(open('distribute-0.6.8-py2.7.egg-info'))
>>> metadata.name
'distribute'
>>> metadata.version
'0.6.8'
>>> metadata.description
'Easily download, build, install, upgrade, and uninstall Python packages'

请注意,还可以使用元数据文件路径实例化该类以加载其值:

>>> pkg_info_path = 'distribute-0.6.8-py2.7.egg-info'
>>> DistributionMetadata(pkg_info_path).name
'distribute'