Python 是一种出色的编程语言,而且还有更多优点。然而,其最弱点之一是包装。这是社会上众所周知的事实。多年来,安装、导入、使用和创建包已经有所改进,但它仍然无法与 Go 和 Rust 等较新的语言相提并论,后者可以从 Python 和其他更成熟的语言的斗争中学到很多东西。
在本教程中,您将学习构建和共享自己的包所需的一切。有关 Python 包的一般背景信息,请阅读如何使用 Python 包。
打包项目
打包项目是一个过程,通过这个过程,您可以获取一组一致的 Python 模块和可能的其他文件,并将它们放入可以轻松使用的结构中。您必须考虑多种因素,例如对其他包的依赖关系、内部结构(子包)、版本控制、目标受众以及包的形式(源代码和/或二进制文件)。
示例
让我们从一个简单的例子开始。 conman 包是用于管理配置的包。它支持多种文件格式以及使用 etcd
的分布式配置。
包的内容通常存储在单个目录中(尽管将子包拆分到多个目录中很常见),有时(如本例所示)存储在其自己的 git 存储库中。
根目录包含各种配置文件(setup.py是必需的,也是最重要的一个),包代码本身通常位于一个子目录中,其名称最好是包的名称一个测试目录。这是 conman
的样子:
> tree
.
├── LICENSE
├── MANIFEST.in
├── README.md
├── conman
│ ├── __init__.py
│ ├── __pycache__
│ ├── conman_base.py
│ ├── conman_etcd.py
│ └── conman_file.py
├── requirements.txt
├── setup.cfg
├── setup.py
├── test-requirements.txt
├── tests
│ ├── __pycache__
│ ├── conman_etcd_test.py
│ ├── conman_file_test.py
│ └── etcd_test_util.py
└── tox.ini
登录后复制
让我们快速浏览一下 setup.py 文件。它从 setuptools 包中导入两个函数:setup()
和 find_packages()
。然后它调用 setup()
函数并使用 find_packages()
作为参数之一。
from setuptools import setup, find_packages
setup(name='conman',
version='0.3',
url='https://github.com/the-gigi/conman',
license='MIT',
author='Gigi Sayfan',
author_email='the.gigi@gmail.com',
description='Manage configuration files',
packages=find_packages(exclude=['tests']),
long_description=open('README.md').read(),
zip_safe=False,
setup_requires=['nose>=1.0'],
test_suite='nose.collector')
登录后复制
这很正常。虽然 setup.py 文件是一个常规的 Python 文件,您可以在其中做任何您想做的事情,但它的主要工作是使用适当的参数调用 setup()
函数参数,因为在安装包时它将由各种工具以标准方式调用。我将在下一节中详细介绍。
配置文件
除了setup.py之外,还有一些其他可选配置文件可以显示在此处并用于各种目的。
setup.py
setup()
函数采用大量命名参数来控制包安装的许多方面以及运行各种命令。许多参数指定将包上传到存储库时用于搜索和过滤的元数据。
-
name
:你的包的名称(以及它将如何在 PyPI 上列出) -
version
:这对于维护正确的依赖管理至关重要 -
url
:包的 URL,通常是 GitHub 或 readthedocs URL -
packages
:需要包含的子包列表;find_packages()
在这里提供帮助 -
setup_requires
:在这里指定依赖项 -
test_suite
:测试时运行哪个工具
long_description
在此设置为 README.md 文件的内容,这是拥有单一事实来源的最佳实践。
setup.cfg
setup.py
文件还提供命令行界面来运行各种命令。例如,要运行单元测试,您可以键入: python setup.py test
running test
running egg_info
writing conman.egg-info/PKG-INFO
writing top-level names to conman.egg-info/top_level.txt
writing dependency_links to conman.egg-info/dependency_links.txt
reading manifest file 'conman.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
writing manifest file 'conman.egg-info/SOURCES.txt'
running build_ext
test_add_bad_key (conman_etcd_test.ConManEtcdTest) ... ok
test_add_good_key (conman_etcd_test.ConManEtcdTest) ... ok
test_dictionary_access (conman_etcd_test.ConManEtcdTest) ... ok
test_initialization (conman_etcd_test.ConManEtcdTest) ... ok
test_refresh (conman_etcd_test.ConManEtcdTest) ... ok
test_add_config_file_from_env_var (conman_file_test.ConmanFileTest) ... ok
test_add_config_file_simple_guess_file_type (conman_file_test.ConmanFileTest) ... ok
test_add_config_file_simple_unknown_wrong_file_type (conman_file_test.ConmanFileTest) ... ok
test_add_config_file_simple_with_file_type (conman_file_test.ConmanFileTest) ... ok
test_add_config_file_simple_wrong_file_type (conman_file_test.ConmanFileTest) ... ok
test_add_config_file_with_base_dir (conman_file_test.ConmanFileTest) ... ok
test_dictionary_access (conman_file_test.ConmanFileTest) ... ok
test_guess_file_type (conman_file_test.ConmanFileTest) ... ok
test_init_no_files (conman_file_test.ConmanFileTest) ... ok
test_init_some_bad_files (conman_file_test.ConmanFileTest) ... ok
test_init_some_good_files (conman_file_test.ConmanFileTest) ... ok
----------------------------------------------------------------------
Ran 16 tests in 0.160s
OK
登录后复制
setup.cfg
is 是一个 ini 格式文件,其中可能包含传递给 setup.py
的命令的选项默认值。在这里, setup.cfg
包含 nosetests
(我们的测试运行程序)的一些选项:
[nosetests]
verbose=1
nocapture=1
登录后复制
MANIFEST.in
此文件包含不属于内部包目录的一部分,但您仍想包含的文件。这些通常是自述文件、许可证文件等。一个重要的文件是 requirements.txt。 pip 使用此文件来安装其他所需的软件包。
这是 conman 的 MANIFEST.in 文件:
include LICENSE
include README.md
include requirements.txt
登录后复制
依赖项
您可以在 setup.py 的 install_requires
部分和 requirements.txt 文件中指定依赖项。 Pip 将自动从 install_requires
安装依赖项,但不会从 requirements.txt 文件安装。要安装这些要求,您必须在运行 pip 时明确指定它: pip install -r requests.txt
。
install_requires
选项旨在指定主要版本级别的最低和更抽象的要求。 requirements.txt 文件用于更具体的要求,通常包含固定的次要版本。
这是conman的需求文件。您可以看到所有版本都已固定,这意味着如果其中一个软件包升级并引入破坏 conman 的更改,它可能会受到负面影响。
PyYAML==3.11
python-etcd==0.4.3
urllib3==1.7
pyOpenSSL==0.15.1
psutil==4.0.0
six==1.7.3
登录后复制
固定让您可预测且安心。如果许多人在不同时间安装您的软件包,这一点尤其重要。如果没有固定,每个人都会根据安装时间获得不同的依赖版本组合。固定的缺点是,如果您不跟上依赖项的开发,您可能会陷入某些依赖项的旧的、性能不佳甚至易受攻击的版本。
我最初在2014年写了conman,并没有太关注它。现在,在本教程中,我升级了所有内容,并且几乎每个依赖项都进行了一些重大改进。
发行版
您可以创建源发行版或二进制发行版。我将涵盖两者。
源分布
使用以下命令创建源发行版:python setup.py sdist
。这是 conman 的输出:
> python setup.py sdist
running sdist
running egg_info
writing conman.egg-info/PKG-INFO
writing top-level names to conman.egg-info/top_level.txt
writing dependency_links to conman.egg-info/dependency_links.txt
reading manifest file 'conman.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
writing manifest file 'conman.egg-info/SOURCES.txt'
warning: sdist: standard file not found: should have one of README, README.rst, README.txt
running check
creating conman-0.3
creating conman-0.3/conman
creating conman-0.3/conman.egg-info
making hard links in conman-0.3...
hard linking LICENSE -> conman-0.3
hard linking MANIFEST.in -> conman-0.3
hard linking README.md -> conman-0.3
hard linking requirements.txt -> conman-0.3
hard linking setup.cfg -> conman-0.3
hard linking setup.py -> conman-0.3
hard linking conman/__init__.py -> conman-0.3/conman
hard linking conman/conman_base.py -> conman-0.3/conman
hard linking conman/conman_etcd.py -> conman-0.3/conman
hard linking conman/conman_file.py -> conman-0.3/conman
hard linking conman.egg-info/PKG-INFO -> conman-0.3/conman.egg-info
hard linking conman.egg-info/SOURCES.txt -> conman-0.3/conman.egg-info
hard linking conman.egg-info/dependency_links.txt -> conman-0.3/conman.egg-info
hard linking conman.egg-info/not-zip-safe -> conman-0.3/conman.egg-info
hard linking conman.egg-info/top_level.txt -> conman-0.3/conman.egg-info
copying setup.cfg -> conman-0.3
Writing conman-0.3/setup.cfg
creating dist
Creating tar archive
removing 'conman-0.3' (and everything under it)
登录后复制
如您所见,我收到一条关于缺少带有标准前缀之一的 README 文件的警告,因为我喜欢 Markdown,所以我有一个 README.md 。除此之外,还包括所有包源文件和附加文件。然后,在conman.egg-info目录中创建了一堆元数据。最后,创建一个名为 conman-0.3.tar.gz 的压缩 tar 存档,并将其放入 dist 子目录中。
安装这个包需要一个构建步骤(即使它是纯Python)。您可以正常使用 pip 安装它,只需传递包的路径即可。例如:
pip install dist/conman-0.3.tar.gz
Processing ./dist/conman-0.3.tar.gz
Installing collected packages: conman
Running setup.py install for conman ... done
Successfully installed conman-0.3
登录后复制
Conman 已安装到站点包中,可以像任何其他包一样导入:
import conman
conman.__file__
'/Users/gigi/.virtualenvs/conman/lib/python2.7/site-packages/conman/__init__.pyc'
登录后复制
轮子
Wheels 是一种相对较新的方式来打包 Python 代码和可选的 C 扩展。它们取代了鸡蛋的形式。轮子有几种类型:纯Python轮子、平台轮子、通用轮子。纯 Python 轮子是像 conman 这样的包,没有任何 C 扩展代码。
平台轮子确实有 C 扩展代码。通用轮子是纯 Python 轮子,兼容具有相同代码库的 Python 2 和 Python 3(它们甚至不需要 2 到 3)。
如果您有一个纯 Python 包,并且希望您的包同时支持 Python 2 和 Python 3(变得越来越重要),那么您可以构建一个通用构建,而不是为 Python 2 构建一个轮子,为 Python 构建一个轮子3.
Python 3 是当前受到积极支持的 Python 版本,不断更新、改进和社区支持;建议对所有新项目和迁移使用 Python 3。
如果你的包有C扩展代码,你必须为每个平台构建一个平台轮。轮子的巨大好处,特别是对于带有 C 扩展的软件包来说,是不需要在目标机器上提供编译器和支持库。该轮子已经包含一个内置包。所以你知道它不会构建失败,而且安装速度要快得多,因为它实际上只是一个副本。使用 Numpy 和 Pandas 等科学库的人可以真正体会到这一点,因为安装此类软件包过去需要很长时间,并且如果缺少某些库或编译器配置不正确,则可能会失败。
构建纯轮子或平台轮子的命令是:python setup.py bdist_wheel
。
Setuptools——提供 setup()
函数的引擎——将自动检测是否需要纯轮子或平台轮子。
running bdist_wheel
running build
running build_py
creating build
creating build/lib
creating build/lib/conman
copying conman/__init__.py -> build/lib/conman
copying conman/conman_base.py -> build/lib/conman
copying conman/conman_etcd.py -> build/lib/conman
copying conman/conman_file.py -> build/lib/conman
installing to build/bdist.macosx-10.9-x86_64/wheel
running install
running install_lib
creating build/bdist.macosx-10.9-x86_64
creating build/bdist.macosx-10.9-x86_64/wheel
creating build/bdist.macosx-10.9-x86_64/wheel/conman
copying build/lib/conman/__init__.py -> build/bdist.macosx-10.9-x86_64/wheel/conman
copying build/lib/conman/conman_base.py -> build/bdist.macosx-10.9-x86_64/wheel/conman
copying build/lib/conman/conman_etcd.py -> build/bdist.macosx-10.9-x86_64/wheel/conman
copying build/lib/conman/conman_file.py -> build/bdist.macosx-10.9-x86_64/wheel/conman
running install_egg_info
running egg_info
creating conman.egg-info
writing conman.egg-info/PKG-INFO
writing top-level names to conman.egg-info/top_level.txt
writing dependency_links to conman.egg-info/dependency_links.txt
writing manifest file 'conman.egg-info/SOURCES.txt'
reading manifest file 'conman.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
writing manifest file 'conman.egg-info/SOURCES.txt'
Copying conman.egg-info to build/bdist.macosx-10.9-x86_64/wheel/conman-0.3-py2.7.egg-info
running install_scripts
creating build/bdist.macosx-10.9-x86_64/wheel/conman-0.3.dist-info/WHEEL
登录后复制
查看dist
目录,可以看到创建了一个纯Python的轮子:
ls -la dist
dist/
total 32
-rw-r--r-- 1 gigi staff 5.5K Feb 29 07:57 conman-0.3-py2-none-any.whl
-rw-r--r-- 1 gigi staff 4.4K Feb 28 23:33 conman-0.3.tar.gz
登录后复制
名称 conman-0.3-py2-none-any.whl
有几个组件:
- 包名称
- 软件包版本
- Python版本
- 平台版本,最后是“whl”扩展。
要构建通用包,您只需添加 --universal
,如 python setup.py bdist_wheel --universal
。
生成的轮子名为 conman-0.3-py2.py3-none-any.whl
。
请注意,如果您创建通用包,您有责任确保您的代码实际上可以在 Python 2 和 Python 3 下运行。
使用 Toml
Toml(Tom's Obvious Minimal Language)是一种易于使用的配置文件格式,用于配置软件应用程序。尽管 setup.py 仍然被广泛使用和支持,PEP518 建议使用 pyproject.toml 文件而不是 setup.py 进行打包和分发。
让我们使用pyproject.toml创建一个名为 apex
的简单 Python 包,用于检查电池状态并在电池充满时显示一条消息。
创建一个名为 apex 的目录结构。在 apex 内,添加一个 pyproject.toml 和一个 README.md 文件。
.apex
├── pyproject.toml
└── README.md
登录后复制
在根 apex 目录中,创建一个名为 apex 的子目录,其中包含一个 __init__.py 文件以使其成为一个包和一个 battery.py 文件。您的项目目录现在应如下所示:
.
├── apex
│ ├── battery.py
│ └── __init__.py
├── pyproject.toml
└── README.md
登录后复制
使用 pip 安装 psutil 包。
pip install psutil
登录后复制
Psutil(进程和系统实用程序)是一个用于 Python 中进程和系统监控的跨平台库。 Psutil 可以检索有关系统上运行的进程的信息,例如状态、CPU 使用情况和内存使用情况。它还可以操纵系统进程。
在battery.py文件中,添加检查电池是否已充满的代码。
import psutil
def check_battery():
battery = psutil.sensors_battery()
plugged = battery.power_plugged
percent = battery.percent
if percent == 100 and plugged:
print "Battery is full. Unplug your Charger"
else:
print "Battery is not yet full."
if __name__ == "__main__":
message = check_battery()
print(message)
登录后复制
pyproject.toml 文件包含包的元数据,包括:
构建系统
名称
版本
项目依赖项.
打开pyproject.toml并指定 setuptools
作为构建系统。
[build-system]
requires = ["setuptools","wheel"]
build-backend ="setuptools.build_meta"
登录后复制
指定包的名称、版本号和项目依赖项列表。现在 pyproject.toml 文件如下所示:
[build-system]
requires = ["setuptools","wheel"]
build-backend ="setuptools.build_meta"
[project]
name="apex"
version ="1.0.0"
[project-dependencies]
psutil ="5.9.5"
登录后复制
最后,在README.md文件中添加以下内容。
# apex
**apex** is a Python package that allows you to check the battery status of your laptop and display a message when the battery is full.
## Features
- Retrieve battery status information
- Display a message when the battery is full
- Cross-platform support (Windows, macOS, Linux, etc.)
## Installation
Install **apex** using `pip`:
```shell
pip install apex
登录后复制
使用 build
命令构建包。
python -m build
登录后复制
您应该看到下面的输出,该输出已被我截断。
creating build/bdist.linux-x86_64/wheel/apex-1.0.0.dist-info/WHEEL
creating '/home/vaati/Desktop/apex/dist/.tmp-d8_lqaaj/apex-1.0.0-py3-none-any.whl' and adding 'build/bdist.linux-x86_64/wheel' to it
adding 'apex/__init__.py'
adding 'apex/battery.py'
adding 'apex-1.0.0.dist-info/METADATA'
adding 'apex-1.0.0.dist-info/WHEEL'
adding 'apex-1.0.0.dist-info/top_level.txt'
adding 'apex-1.0.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built apex-1.0.0.tar.gz and apex-1.0.0-py3-none-any.whl
登录后复制
构建会创建一个 dist 文件夹,其中包含包的分发文件:
- apex-1.0.0.tar.gz:apex 包的源发行版。它包含安装该软件包所需的源代码。
- apex-1.0.0-py3-none-any.whl:apex 包的二进制发行版。您可以使用二进制发行版直接从源代码安装它。
构建过程还会生成一个 apex.egg-info 文件夹,其中包含有关包的元数据。
.
├── apex
│ ├── battery.py
│ └── __init__.py
├── apex.egg-info
│ ├── dependency_links.txt
│ ├── PKG-INFO
│ ├── SOURCES.txt
│ └── top_level.txt
├── dist
│ ├── apex-1.0.0-py3-none-any.whl
│ └── apex-1.0.0.tar.gz
├── pyproject.toml
└── README.md
登录后复制
结论
编写自己的 Python 包需要处理大量工具,指定大量元数据,并仔细考虑您的依赖项和目标受众。但回报是巨大的。
如果您编写有用的代码并正确打包它,人们将能够轻松安装它并从中受益。
以上就是创建您自己的 Python 包的详细内容,更多请关注每日运维网(www.mryunwei.com)其它相关文章!