21、Python项目的打包与运行

Python项目的打包与运行

在Python开发中,为了简化流程,我们可以不区分应用程序和库,它们唯一的技术区别在于应用程序会附带更多的数据文件和控制台脚本。接下来,我们将详细探讨Python项目的打包、依赖管理、版本控制以及发布等方面的内容。

打包所需的标准文件

当打包Python项目时,需要在Python包旁边准备三个标准文件:
- pyproject.toml :项目构建系统的配置文件。
- setup.py setup.cfg :控制项目打包和元数据的特殊模块。
- requirements.txt :列出项目依赖的文件。

setup.py 文件详解

setup.py 文件在与Python项目交互时起着关键作用。当执行 setup() 函数时,它会生成一个遵循PEP 314格式的静态元数据文件。不过,由于项目作者可能在 setup.py 中包含特定平台的代码,会根据不同平台和Python版本生成不同的元数据文件,所以不能使用静态版本,需要通过 setup() 调用重新生成元数据文件。

在创建 setup.py 文件时,常见的错误是在有第三方依赖时将包导入其中。例如,当 pip 等工具通过运行 setup.py 读取元数据时,可能在列出所有待安装依赖之前就引发导入错误。在 setup.py 文件中,唯一可以直接导入的依赖是 Setuptools ,因为可以假定尝试安装项目的人其环境中可能已经有该依赖。

以下是一些可用于微服务项目的 setup() 参数:
| 参数 | 说明 |
| ---- | ---- |
| name | 包的名称,应简短且为小写 |
| version | 项目的版本,遵循PEP 440定义 |
| url | 项目的URL,可以是其仓库或主页 |
| description | 描述项目的一句话 |
| long_description | 一个reStructuredText或Markdown文档 |
| author author_email | 作者的姓名和电子邮件,可以是一个组织 |
| license | 项目使用的许可证(如MIT、Apache2、GPL等) |
| classifiers | 从固定列表中选取的分类器列表,定义于PEP 301 |
| keywords | 描述项目的标签,发布到Python Package Index (PyPI) 时很有用 |
| packages | 项目包含的包列表,Setuptools可以使用 find_packages() 方法自动填充 |
| entry_points | 一组Setuptools钩子,如控制台脚本 |
| include_package_data | 一个标志,简化非Python文件的包含 |
| zip_safe | 一个标志,防止Setuptools将项目作为ZIP文件安装 |

以下是一个包含这些选项的 setup.py 文件示例:

from setuptools import setup, find_packages

with open("README.rst") as f:
    LONG_DESC = f.read()

setup(
    name="MyProject",
    version="1.0.0",
    url="http://example.com",
    description="This is a cool microservice based on Quart.",
    long_description=LONG_DESC,
    long_description_content_type="text/x-rst",
    author="Simon",
    author_email="simon@example.com",
    license="MIT",
    classifiers=[
        "Development Status :: 3 - Alpha",
        "License :: OSI Approved :: MIT License",
        "Programming Language :: Python :: 3",
    ],
    keywords=["quart", "microservice"],
    packages=find_packages(),
    include_package_data=True,
    zip_safe=False,
    install_requires=["quart"],
)

long_description 选项通常从 README.rst 文件中提取,这样可以避免在函数中处理大段的reStructuredText字符串。Twine项目(https://pypi.org/project/twine/)有一个检查命令,可确保长描述能正确渲染,将此检查添加到持续集成(CI)的标准测试套件中是个不错的主意。

许可证字段是自由格式的,只要人们能识别所使用的许可证即可。建议在 setup.py 文件旁边添加一个 LICENCE 文件,包含该许可证的官方文本。在开源项目中,通常还会包含一个“行为准则”,如Contributor Covenant(https://www.contributor-covenant.org/)。

分类器选项可能是最难编写的,需要从https://pypi.python.org/pypi?%3Aaction=list_classifiers 中选择字符串来对项目进行分类。开发者常用的三个分类器是支持的Python版本列表、许可证(应与 license 选项匹配)和开发状态,它能提示项目的成熟度。

关键词是在将项目发布到Python Package Index时提高项目可见性的好方法。例如,创建Quart微服务时,应使用 “quart” 和 “microservice” 作为关键词。

entry_points 部分是一个类似INI的字符串,定义了通过可调用对象与Python模块交互的方式,最常见的是控制台脚本。添加函数到该部分后,命令行脚本将与Python解释器一起安装,并通过入口点与函数挂钩。

install_requires 列出了所有依赖项,是项目使用的Python项目列表, pip 等工具在安装时会使用该列表。也可以从 requirements.txt 文件中读取依赖项,并从单独的文本文件或JSON文件中读取版本信息。

创建好 setup.py 文件后,可以通过创建本地虚拟环境来测试它。假设已经安装了 virtualenv ,在包含 setup.py 文件的目录中运行以下命令:

$ python3 –m venv ./my-project-venv 
$ source ./my-project-venv/bin/activate 
(my-project-venv) $ 

运行 pip install -e 命令将以可编辑模式安装项目,这种安装方式允许直接在项目的Python模块上工作,模块会通过 site-packages 目录链接到本地Python安装。

pip 调用还会生成一个 MyProject.egg-info 目录,其中包含元数据。以下是 PKG-INFO 文件的示例:

Metadata-Version: 2.1
Name: MyProject
Version: 1.0.0
Summary: This is a cool microservice based on Quart.
Home-page: http://example.com
Author: Simon
Author-email: simon@example.com
License: MIT
Description: long description!
Keywords: quart,microservice
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Description-Content-Type: text/x-rst
requirements.txt 文件

pip 社区中出现的一个标准是使用 requirements.txt 文件,它列出了项目的所有依赖项,还提供了安装可编辑依赖项的扩展语法。以下是一个示例:

arrow 
python-dateutil 
pytz 
requests 
six 
stravalib 
units 

使用该文件已被社区广泛采用,因为它便于记录项目依赖。可以在项目中创建任意数量的需求文件,让用户使用 pip install -r requirements.txt 命令安装其中描述的包。

例如,可以有一个 dev-requirements.txt 文件,包含开发所需的额外工具;还有一个 prod-requirements.txt 文件,包含生产环境特定的依赖项。该格式支持继承,有助于管理需求文件集合。

使用需求文件会重复 setup.py 文件中 install_requires 部分的一些信息。可以读取 requirements.txt 文件并将数据包含在 setup.py 中。一些开发者故意将这两个信息源分开,以区分应用程序和库,使库在依赖项方面更具灵活性,但这也意味着需要保持两个信息源的更新,容易造成混淆。

为避免信息重复,社区中有一些工具可以实现 setup.py 和需求文件之间的同步自动化。例如, pip-tools (https://github.com/jazzband/pip-tools)工具可以通过 pip-compile CLI生成 requirements.txt 文件:

$ pip install pip-tools 
... 
$ pip-compile 
#
# This file is autogenerated by pip-compile
# To update, run:
#
#   pip-compile
#
aiofiles==0.6.0
# via quart
blinker==1.4
# via quart
click==7.1.2
# via quart
h11==0.12.0
# via
#   hypercorn
#   wsproto
…

使用 pip-compile 时,若不提供其他参数,它将检查 setup.py 文件。也可以传递一个未固定版本的文件,如 requirements.in 作为要使用的包列表。

在生产环境中,固定依赖项的版本是个好主意,因为这样可以确保应用程序的可重复性。若不指定安装版本,可能会获取到最新版本,这可能会导致应用程序出现问题。还可以在 requirements.txt 文件中添加依赖项的哈希值,以避免有人上传未更新版本号的包或恶意替换现有版本的包。

$ pip-compile —generate-hashes
#
# This file is autogenerated by pip-compile
# To update, run:
#
#   pip-compile —generate-hashes
#
aiofiles==0.6.0 \
—hash=sha256:bd3019af67f83b739f8e4053c6c0512a7f545b9a8d91aaeab55e6
e0f9d123c27 \
—hash=sha256:e0281b157d3d5d59d803e3f4557dcc9a3dff28a4dd4829a9ff478
adae50ca092
# via quart
blinker==1.4 \
—hash=sha256:471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466d
c00d606f8b6
# via quart
click==7.1.2 \
—hash=sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783
aa94dfb6b1a \
—hash=sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb5003026
3894c38c0dc
# via quart

若不使用 pip-tools pip 有一个内置的 freeze 命令,可生成Python虚拟环境中当前安装的所有版本的列表。但在没有虚拟环境的情况下使用 pip freeze 可能会列出许多其他项目使用的包。

固定依赖项时的一个问题是,当另一个项目有相同的依赖项但版本不同时, pip 会报错并无法满足所有需求。如果正在开发一个库,期望其他人使用并添加到他们自己的依赖列表中,最好指定支持的版本范围,例如 quart>0.13.0,<0.15.0

通常的做法是在 setup.py 文件中不固定依赖项,而在 requirements.txt 文件中固定。这样, pip 可以安装每个包的最新版本,在部署时,特别是在测试或生产环境中,可以通过运行 pip install -r requirements.txt 命令刷新版本。

MANIFEST.in 文件

创建源或二进制发行版时, Setuptools 会自动将所有包模块、数据文件、 setup.py 文件和其他一些文件包含在包存档中,但像 pip 需求文件等不会被包含。要将这些文件添加到发行版中,需要添加一个 MANIFEST.in 文件,其中包含要包含的文件列表。

该文件遵循简单的类似glob的语法,例如:

include requirements.txt 
include README.rst 
include LICENSE 
recursive-include myservice *.ini 
recursive-include docs *.rst *.png *.svg *.css *.html conf.py 
prune docs/build/* 

上述示例中,包含了 requirements.txt README.rst LICENSE 文件,递归包含 myservice 目录下的所有 .ini 文件,递归包含 docs 目录下的多种文件,同时排除 docs/build/ 目录下的所有文件。

版本控制

Python打包工具并不强制要求特定的版本控制模式,但版本字段应能通过 packaging 模块转换为有意义的版本。为了理解版本控制方案,安装程序需要知道如何对版本进行排序和比较。

一些软件使用基于发布日期的版本控制方案,如20210101表示软件于2021年1月1日发布。对于持续部署(CD)场景,由于变化频繁,固定版本号难以管理,基于日期或版本控制哈希的版本可能更适用。

然而,基于日期或提交的版本控制在分支发布时效果不佳。例如,软件行为有重大变化且需要在一段时间内支持旧版本时,使用版本1和2会更清晰,而使用日期可能会导致混淆。

此外,还有发布beta、alpha、候选版本和开发版本的问题。开发者希望能够将版本标记为预发布版本,例如Python在发布新版本前会发布候选版本,如3.10.0rc1或3.10.0rc2。

对于不向社区发布的微服务,使用这些标记通常是不必要的,但当有组织外部的人员使用软件时,这些标记可能会变得有用。

PEP 386和440为Python社区提出了一种版本控制方案,它基于标准的MAJOR.MINOR[.PATCH]方案,并对预发布和后发布版本有特定规则。

语义版本控制(SemVer)(http://semver.org/)方案是社区中出现的另一个标准,在Python之外的许多地方都有使用。如果使用SemVer,只要不使用预发布标记,就与PEP 440和 pip 安装程序兼容。例如,3.6.0rc2在SemVer中表示为3.6.0-rc2。

对于微服务项目或任何Python项目,建议从0.1.0版本开始,以表明项目尚未稳定,早期开发过程中可能会有重大变化,且不保证向后兼容性。当软件成熟后,通常发布1.0.0版本,然后遵循以下规则:
- 当引入与现有API不兼容的更改时,增加MAJOR版本。
- 当添加不破坏现有API的新功能时,增加MINOR版本。
- 仅进行错误修复时,增加PATCH版本。

对于库来说,API是指所有公共且有文档记录的函数和类;对于微服务,需要区分代码API和HTTP API。

需要注意的是,版本号不是小数或计数数字,例如,3.9之后的版本不一定是4.0,3.10及更高版本也是可以接受的,版本号只是一种排序和比较大小的方式。

项目发布

在了解了如何处理版本号后,就可以进行项目发布了。通常,发布项目包括创建源发行版(基本上是项目结构的存档),如果有C扩展,还可以创建二进制发行版。后续可以通过一些命令将项目注册到PyPI等平台。

综上所述,Python项目的打包、依赖管理、版本控制和发布是一个系统的过程,需要综合考虑各个方面的因素,以确保项目的可维护性、可重复性和易用性。

Python项目的打包与运行

项目发布流程总结

项目发布前,我们已经完成了 setup.py requirements.txt MANIFEST.in 文件的创建,也确定了合适的版本号。接下来,我们可以按照以下步骤进行项目发布:
1. 创建虚拟环境并安装依赖
bash $ python3 –m venv ./my-project-venv $ source ./my-project-venv/bin/activate (my-project-venv) $ pip install -e .
2. 生成需求文件(可选)
bash $ pip install pip-tools $ pip-compile
3. 检查长描述(可选) :使用 Twine 项目的检查命令确保长描述能正确渲染。
4. 创建源发行版和二进制发行版(如果有 C 扩展)
bash $ python setup.py sdist bdist_wheel
5. 上传到 PyPI
bash $ twine upload dist/*

依赖管理最佳实践

在依赖管理方面,我们总结了以下最佳实践:
| 场景 | 建议做法 |
| ---- | ---- |
| 开发库 | 在 setup.py 中不固定依赖项版本,指定支持的版本范围,如 quart>0.13.0,<0.15.0 ;在 requirements.txt 中固定版本。 |
| 开发应用 | 在 setup.py 中列出主要依赖项,在 requirements.txt 中固定所有依赖项版本,确保生产环境的可重复性。 |
| 持续集成 | 将依赖项检查和长描述检查添加到 CI 流程中,确保每次提交代码时依赖项和文档的正确性。 |

版本控制的重要性及应用

版本控制在项目开发和维护中起着至关重要的作用,以下是其重要性及应用场景:
- 清晰的版本标识 :通过版本号,开发者和用户可以清晰地了解项目的状态和功能变化。例如,从 0.1.0 到 1.0.0 的转变表示项目从早期开发阶段进入成熟阶段。
- 兼容性管理 :遵循版本控制规则,如 MAJOR、MINOR 和 PATCH 的更新规则,可以确保项目在不同版本之间的兼容性,避免因版本不兼容导致的问题。
- 发布管理 :在发布预发布版本(如 alpha、beta、rc)时,版本号可以帮助社区提前测试新功能,发现潜在问题,确保最终版本的稳定性。

以下是一个简单的 mermaid 流程图,展示了版本控制的基本流程:

graph LR
    A[项目开始] --> B[0.1.0版本]
    B --> C{功能开发}
    C -->|添加新功能| D[增加MINOR版本]
    C -->|修复bug| E[增加PATCH版本]
    C -->|重大不兼容更改| F[增加MAJOR版本]
    D --> C
    E --> C
    F --> C
总结与展望

Python 项目的打包、依赖管理、版本控制和发布是一个复杂但有序的过程。通过合理使用 setup.py requirements.txt MANIFEST.in 文件,以及遵循合适的版本控制方案,我们可以确保项目的可维护性、可重复性和易用性。

在未来的开发中,我们可以进一步探索以下方面:
- 自动化工具的使用 :除了 pip-tools 和 Twine,还有许多其他自动化工具可以帮助我们更高效地完成项目打包和发布,如 Poetry 等。
- 容器化部署 :将 Python 项目打包成 Docker 容器,可以更好地管理依赖项和环境,提高项目的可移植性。
- 持续交付 :结合 CI/CD 工具,实现项目的自动化部署和发布,提高开发效率和质量。

希望本文能为你在 Python 项目开发和发布过程中提供有益的参考,让你能够更加顺利地完成项目的各个阶段。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值