13. Python打包工具- setuptools

文章目录

源码地址:https://github.com/pypa/setuptools

Setuptools 是Python开发中用于打包、安装和分发软件的核心工具。它扩展了 Distutils ,增加了元数据定义和依赖处理等高级功能,并引入了 .EGG 格式和相关的打包工具。Setuptools 简化了软件包的安装过程,支持依赖管理,使得创建自包含的发行版成为可能。此外,它与 easy_install 命令、虚拟环境工具如 pipvirtualenv 良好集成,对于Python生态系统的繁荣至关重要。

安装方法

使用如下命令可以安装最新的版本:

复制代码
uv add --upgrade setuptools[core]

然而,大多数时候,在创建新的Python包时,建议使用名为 build 的命令行工具。此工具将自动下载 setuptools 和项目需要用到的其他构建时依赖项。您只需要在项目根目录下的 pyproject.toml 文件中指定它们。如下:

复制代码
uv add --upgrade build

完成后,将运行使用如下的命令行:

复制代码
python -m build

每个python包都必须提供一个 pyproject.toml 文件并指定它要使用的后端(构建系统)。然后,可以使用任何提供象 build sdist 功能的工具生成该发行版。

配置概览

基础配置

在创建了一个 Python 项目之后,在项目根目录下必须包含一个 pyproject.toml 文件,并在该文件下的 build-system 节如下代码所示指定他要使用的后端。

复制代码
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

build-system 声明了构建系统依赖项是什么,以及将实际使用哪个库进行打包。

⚠️ 注意

包维护者可能会将 setuptools[core]作为一个个必要条件尝试使用;但最好避免这样做,因为 extra 目前被认为是内部实现细节且将来可能会消失,Setuptools 团队将不再支持对声明了这个 extra 的包所产生的问题的兼容性。供应商提供的包会满足最常见的独立构建场景中的依赖关系。

本文档不再在 requires 列表中列出 wheel,但现在许多项目仍然列出。不建议这样做,因为后端不再需要 wheel 包,显式列出它会导致源代码发行版构建时会把它构建进去。如果你需要在构建期间显式地访问 wheel,你应该只在 require 中包含wheel(例如,如果你的项目需要导入 wheelsetup.py脚本)。

除了指定构建系统之外,您还需要添加一些包信息,例如元数据、内容、依赖项等。这可以在pyproject.toml 文件中配置,或者通过在单独的setup.cfgsetup.py文件配置。

下面的例子演示了一个最小配置(假设项目依赖于 requests importlib-metadata才能运行):

  • pyproject.toml 中配置:

    复制代码
    [project]
    name = "mypackage"
    version = "0.0.1"
    dependencies = [
        "requests",
        'importlib-metadata; python_version<"3.10"',
    ]
  • 或在 setup.cfg 中配置

    复制代码
    [metadata]
    name = mypackage
    version = 0.0.1
    
    [options]
    install_requires =
        requests
        importlib-metadata; python_version<"3.10"
  • 或在 setup.py 中配置

    复制代码
    from setuptools import setup
    
    setup(
        name='mypackage',
        version='0.0.1',
        install_requires=[
            'requests',
            'importlib-metadata; python_version<"3.10"',
        ],
    )

最后,你需要组织你的Python代码,使其准备好分发成如下所示的内容(带有#标记的可选文件):

复制代码
mypackage
├── pyproject.toml  # and/or setup.cfg/setup.py (depending on the configuration method)
|   # README.rst or README.md (a nice description of your package)
|   # LICENCE (properly chosen license information, e.g. MIT, BSD-3, GPL-3, MPL-2, etc...)
└── mypackage
    ├── __init__.py
    └── ... (other Python files)

如果已经安装 build 工具,运行以下命令:

复制代码
python -m build

现在您已经准备好了您的发行版(例如,dist 目录中的 tar.gz 文件和 .whl 文件),您可以将其上传到 PyPI !

当然,在将项目发布到PyPI之前,您需要添加更多信息,以帮助人们查找或了解您的项目。也许到那时您的项目已经发展到包含一些依赖项,也许还包括一些数据文件和脚本。

包发现

对于遵循简单目录结构的项目,setuptools 应该能够自动检测所有包和命名空间。然而,复杂的项目可能包括非必须分发的额外文件夹和支持文件,这些资源可能会混淆 setuptools 自动发现算法。

因此,setuptools 提供了一种方便的方法来定制应该分发哪些包以及应该在哪个目录中找到它们,如下面的示例所示:

  • pyproject.toml 中配置

    复制代码
    # ...
    [tool.setuptools.packages]
    find = {}  # 使用默认参数查找项目目录
    
    # 或者
    [tool.setuptools.packages.find]
    # 以下所有配置都是可选的:
    where = ["src"]  # 默认是  ["."]
    include = ["mypackage*"]  # 默认是  ["*"]
    exclude = ["mypackage.tests*"]  # 默认是 空
    namespaces = false  # true by default
  • 或在 setup.cfg 中配置

    复制代码
    [options]
    packages = find: # 如果想使用命名空间,使用 `find_namespace:`
    
    [options.packages.find]  # (上面设置使用命名空间,则将 `find` 写成 `find_namespace`)
    # 本节中以下所有参数都是可选的:
    where=src  #  默认是 '.'
    include=mypackage*  # 默认是 '*'
    exclude=mypackage.tests*  # 默认是空
  • 或在 setup.py 中配置

    复制代码
    from setuptools import setup, find_packages  # or find_namespace_packages
    
    setup(
        # ...
        packages=find_packages(
            # 下面所有配置选项都是可选的:
            where='src',  # 默认是 '.' 
            include=['mypackage*'],  # 默认是 ['*']
            exclude=['mypackage.tests'],  # 默认是空
        ),
        # ...
    )

    当您传递上述信息以及其他必要信息时,setuptools 遍历 where(默认为 .)中指定的目录,并加入与 include 模式(默认为 *)匹配的包,然后删除与 exclude 匹配的包(默认为空),然后返回Python包列表。

配置自动执行脚本

Setuptools 支持在安装时自动创建脚本,如果在入口点(entry_points)中指定这些脚本,这些脚本将在包中被执行。比如通过 pip 来执行:它允许您像运行 pip install 一样运行这些命令,而不必输入 python -m pip install

下面的配置示例展示了如何完成此操作:

  • pyproject.toml 中配置

    复制代码
    [project.scripts]
    cli-name = "mypkg.mymodule:some_func"
  • 或在 setup.cfg 中配置

    复制代码
    [options.entry_points]
    console_scripts =
        cli-name = mypkg.mymodule:some_func
  • 或在 setup.py 中配置

    复制代码
    setup(
        # ...
        entry_points={
            'console_scripts': [
                'cli-name = mypkg.mymodule:some_func',
            ]
        }
    )

    安装此项目时,将创建一个 cli-name 可执行项。cli-name 在用户调用时调用 mypkg/mymodule.py 中的 some_func 函数。另外,您还可以使用入口点在安装包和插件之间发布组件。

    依赖管理

使用 setuptools 构建的包可以指定需要自动安装的依赖项。下面的例子展示了如何配置这种依赖:

  • pyproject.toml 中配置

    复制代码
    [project]
    # ...
    dependencies = [
        "docutils",
        "requests <= 0.4",
    ]
    # ...
  • 或在 setup.cfg 中配置

    复制代码
    [options]
    install_requires =
        docutils
        requests <= 0.4
  • 或在 setup.py 中配置

    复制代码
    setup(
        # ...
        install_requires=["docutils", "requests <= 0.4"],
        # ...
    )

每个依赖都由一个字符串表示,该字符串可以包含版本(如操作符<><=>===!= 之一,后跟一个版本标识符),和(或)条件环境表示(如 sys_platform == "win32")。

在安装项目时,所有未安装的依赖项将会查找定位、下载、构建(如果需要)和安装。当然,这是一个简化的场景。您还可以指定一组额外的依赖项,这些依赖项不是包工作所严格要求的,但它们将提供额外的功能。

包含数据文件

Setuptools 提供了三种方法来指定要包含在包中的数据文件。对于最简单的使用,您可以简单地使用 include_package_data 关键字:

  • pyproject.toml 中配置

    复制代码
    [tool.setuptools]
    include-package-data = true
    # This is already the default behaviour if you are using
    # pyproject.toml to configure your build.
    # You can deactivate that with `include-package-data = false`
  • 或在 setup.cfg 中配置

    复制代码
    [options]
    include_package_data = True
  • 或在 setup.py 中配置

    复制代码
    setup(
        # ...
        include_package_data=True,
        # ...
    )

    启用安装数据文件后,setuptools 将在项目中查找所能找到的数据文件并安装。数据文件必须通过 MANIFEST.in 指定或者通过 Revision Control System plugin 自动添加的资源。

开发模式

setuptools 允许你安装一个包,而不需要将任何文件复制到你的环境目录(例如 site-packages 目录)。这允许您修改源代码并使更改生效,而无需重新构建和重新安装。以下示例展示如何做到这一点:

复制代码
uv add --editable .

配置元数据

以下所列是 setuptools.setup() 接受的关键字,用来指示 Python 发行版的构建过程,或者通过放在项目根目录下的 setup.py 脚本添加元数据。所有这些都是可选的;您不必手动配置这些参数,除非您需要相关的 setuptools 特性。

setup() 提供的元数据和配置是对 setup.cfgpyproject.toml 的信息重新设置并且可能覆盖掉原值。如果没有指定,可能有一些重要的元数据(如 nameversion)会被设定为无意义的值。

强烈建议用户通过 setup.cfgpyproject.toml 使用声明性配置。只有当他们需要利用需要脚本的特殊行为(比如构建C扩展)时才依赖 setup.py

参数名 类型 说明
name str 指定包的名称。
version str 指定包的版本号。
description str 在单行中描述包。
long_description str 提供更长的包描述。
long_description_content_type str 指定用于long_description(例如 text/markdown)的内容类型
author str 指定包的作者。
author_email str 指定包作者的电子邮件地址。
maintainer str 如果当前维护者与作者不同,则指定其名称。注意,如果指定了维护者,setuptools 将把它作为 PKG-INFO 中的作者使用。`
maintainer_email str 如果当前维护者与作者不同,则指定其电子邮件地址。
url str 指定包主页的URL。
download_url str 指定下载包的URL。
packages list[str] 指定 setuptools 要操作的包列表。
py_modules list[str] 指定 setuptools 要操作的模块列表。
scripts list[str] 指定要构建和安装的独立脚本文件列表。
ext_package str 指定此包提供的扩展的基本包名。
ext_modules list[str] 提供要构建的Python扩展列表。列表的每一项都是 setuptools.Extension 的实例
classifiers list[str] 描述包的类别的列表。
distclass Distribution 要使用的Distribution 的子类
script_name str 指定 setup.py 脚本的名称。默认为 sys.argv[0]
script_args list[str] 定义要提供给 setup 脚本的参数列表。
options dict 为安装脚本提供默认选项的字典。
license str 指定包的 license
license_files list[str] 应该包含的与证书相关文件的全局通配符列表。如果 license_filelicense_files 都未指定,则默认为 LICEN[CS]E*copy *NOTICE*AUTHORS*
keywords strlist[str] 提供描述性元数据的字符串列表或逗号分隔的字符串。
platforms strlist[str] 字符串或逗号分隔字符串。
cmdclass dict 一个字典,提供命令名称与 Command 子类的映射。
data_files str 指定要安装的数据文件的 (directory, files)序列,directory 是一个strfiles是一个 str 序列。序列中的每个 (directory, files) 对指定安装目录和要在其中安装的文件。
package_dir str 一个将包名(最终用户将导入它们)映射到目录路径(实际存在于项目的代码树中)的字典。
obsoletes list[str] 描述此包之前的包的字符串列表,适用于当包修改过名称
provides list[str] 指定可以为哪些模块提供依赖
include_package_data bool 安装包是否包含的数据文件
exclude_package_data dict 排除的安装包中的数据文件
package_data dict[str, list] 将包名称映射到全局匹配模式列表的字典。以获取完整的描述和示例
zip_safe bool 指定项目是否可以安全地从 zip 文件安装和运行。如果没有提供这个参数,bdist_egg 命令将不得不在每次构建一个 egg 时分析项目的所有内容,以找出可能存在的问题。
install_requires str, list[str] 指定在安装此发行版时需要安装的其他发行版名称或者名称列表。
entry_points dict 将入口点组名称映射到定义入口点的字符串或字符串列表的字典。入口点用于支持动态发现项目提供的服务或插件。
extras_require str 字典将 extras(项目的可选功能)的名称映射到字符串或字符串列表,指定必须安装哪些其他发行版才能支持这些功能。
python_requires str 对应于Python版本的版本说明符(如PEP 440中定义的)
setup_requires str, list[str] 指定需要哪些其他发行版才能运行安装脚本。setuptools 将在处理其余的安装脚本或命令之前尝试获取这些信息。如果您在构建过程中使用 distutils 扩展,则需要此参数;
dependency_links list[str] 在满足依赖关系时命名要搜索的 url。如果需要安装由 setup_requiretests_require 指定的包,将使用这些链接。它们也将被写入到 egg 的元数据中,以便在安装期间由支持它们的工具使用。
namespace_packages list[str] 命名项目的 namespace packages。名称空间包是一个可以跨多个项目发行版分割的包。
test_suite str unittest.TestCase 子类民(或包含一个或多个此类子类的包或模块,或此类子类的方法),或者命名一个不带参数调用并返回 unittest.TestSuite 的函数。
tests_require str, list[str] 如果项目的测试除了安装所需的包之外还需要一个或多个附加包,则可以使用此选项来指定它们。它指定运行包的测试需要提供哪些其他发行版。当您运行 test 命令时,setuptools 将尝试获取这些信息。
test_loader str 如果您希望使用与 setuptools 通常使用的不同的方式来查找要运行的测试,您可以在此参数中指定模块名称和类名称。类必须是无参并可实例化的,并且它的实例必须支持 loadTestsFromNames()方法,该方法定义在Python unittest模块的 TestLoader 类中。
eager_resources list[str] 如果需要,或者项目中包含的任何C扩展被导入,则应该一起提取命名资源。只有当项目作为 zip 文件安装,并且需要将列出的所有资源作为一个单元提取到文件系统中时,该参数才有效。
project_urls map URL 名称到超链接的任意映射,比简单的 urldownload_url 选项提供更多可扩展的文档,说明在哪里可以找到各种资源。

入口点 entry_points

入口点是一种元数据,可以在安装时由包公开。它们是Python生态系统中非常有用的功能,在以下两种情况下特别方便:

  1. 这个包想要提供在终端上运行的命令。这个功能被称为控制台脚本。该命令还可以打开 GUI,在这种情况下,它被称为 GUI 脚本。一个控制台脚本的示例是由 pip 包提供的脚本,它允许您在终端中运行像 pip install 这样的命令。

  2. 一个包希望能够通过插件来定制它的功能。例如,测试框架 pytest 允许通过 pytest11 入口点进行定制,语法高亮显示工具pyements 允许使用入口点 pygments.styles 指定额外的样式。

相关推荐
小鸡吃米…2 小时前
Python 中的多层继承
开发语言·python
deng-c-f2 小时前
Linux C/C++ 学习日记(53):原子操作(二):实现shared_ptr
开发语言·c++·学习
中國移动丶移不动2 小时前
Python MySQL 数据库操作完整示例
数据库·python·mysql
落叶,听雪2 小时前
AI建站推荐
大数据·人工智能·python
wanghowie2 小时前
01.07 Java基础篇|函数式编程与语言新特性总览
java·开发语言·面试
ZAz_2 小时前
DAY 45 预训练模型
python
Cricyta Sevina2 小时前
Java IO 基础理论知识笔记
java·开发语言·笔记
MyBFuture2 小时前
C#接口与抽象类:关键区别详解
开发语言·c#·visual studio
呆萌很3 小时前
python 项目迁移
python