鸿蒙电脑开发 Python-whl 打包
什么是 whl 文件
whl(Wheel)是 Python 的一种打包格式,用于发布和安装 Python 软件包。与传统的.tar.gz 或.zip 文件相比,文件具有更好的性能和易用性,它可以包含预编译的二进制文件、Python 代码、依赖关系和其他必需的资源。.whl 文件允许开发者将包的所有内容打包在一个文件中,使安装过程更加简单和方便。
在鸿蒙电脑上制作 Python whl 分发包的流程。内容包括 whl 文件格式说明、优势及命名规范。详细步骤涵盖环境准备、打包工具安装(wheel, build, pytest)、工程结构搭建、pyproject.toml 配置编写、执行打包命令以及本地安装测试与卸载方法。旨在帮助开发者掌握 Python 二进制包的标准化发布流程。

whl(Wheel)是 Python 的一种打包格式,用于发布和安装 Python 软件包。与传统的.tar.gz 或.zip 文件相比,文件具有更好的性能和易用性,它可以包含预编译的二进制文件、Python 代码、依赖关系和其他必需的资源。.whl 文件允许开发者将包的所有内容打包在一个文件中,使安装过程更加简单和方便。
.whlwhl 格式本质上是一个压缩包,里面包含了 py 文件,以及经过编译的 pyd 文件,可以在不具备编译环境的情况下,选择合适自己的 Python 环境进行安装,目前 wheel被认为是 Python 的二进制包的标准格式。
whl 文件名遵循 PEP 427 规范,标准格式为:
{distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl
以 flask举例:flask-3.0.3-py3-none-any.whl
| 字段 | 含义说明 |
|---|---|
| distribution | 包名(如 flask) |
| version | 版本号(如 3.0.3) |
| build tag | 构建序号(可选,如 1) |
| python tag | Python 版本标签(如 py3 表示 Python 3) |
| abi tag | ABI 标签(如 none 表示无 ABI 依赖) |
| platform tag | 平台标签(如 any 表示跨平台,win_amd64 表示 Windows 64 位) |
这个包告诉我们,这是一个依赖 Python 3可跨平台的无ABI依赖的flask3.0.3版本的 Python 软件分发包
再以 numpy举例:numpy-1.21.0-cp39-cp39-linux_armv7l.whl
这个文件名告诉我们:这是一个依赖 CPython 3.9的仅在 Linux ARMv7架构(如树莓派)且依赖 与 CPython 3.9 兼容的 ABI的numpy1.21.0版本的 Python 软件分发包
这些包可以在pypi 源找到,可以自行研究下 另外也可以参考一些国内源,比较推荐 aliyun 可以直接访问 http://mirrors.aliyun.com/pypi/simple/ + 包名来查看,仍然以 numpy 举例:http://mirrors.aliyun.com/pypi/simple/numpy/ 下包含无数个不同平台,版本等等组合关系的分发软件包

pip 能够确切通过命名来检索适合你的系统的 Python 软件分发包是什么,避免了下载不兼容包的问题。
whl 包前面介绍了命名规范,我们可以简单理解,none-any形态的包是不依赖任何接口或 C 接口的,这次我们先介绍如何制作一个 none-any类型的包,这类包有充分的跨平台属性,不依赖任何 native 接口,仅需要依赖对应的 Python 运行时
在鸿蒙电脑上制作 whl 包,需要具备 Python 环境和基础工具链,建议按照以下环境部署:
6.0.0.115版本,建议升级到该版本以上在终端中执行 python --version、git version、clang --version确保以上环境部署成功,如图所示

接下来需要安装几个 python 的打包工具,在终端中依次执行下面的命令
python -m pip install wheel
看到以下结果证明安装成功,wheel 工具安装到 /storage/Users/currentUser/.local下

python -m pip install build
build 安装结果同理

python -m pip install pytest
pytest 安装结果同理

我们编写一个典型案例 simple_math,包含加减乘除的封装库 工程结构如下:
simple_math/
├── src/ #源码顶层目录
│ └── simple_math/ #但功能模块目录
│ ├── __init__.py
│ ├── add.py #加法
│ ├── sub.py #减法
│ ├── mult.py #乘法
│ └── div.py #除法
├── test/
│ └── test_ops.py #测试程序
├── examples/
│ └── example.py #使用示例程序
├── README.md #说明文档
├── LICENSE #许可证
└── pyproject.toml #TOML 配置文件
分别编写这些文件
def add(a: float, b: float) -> float:
"""
将两个数字相加
Args:
a (float): 第一个数字
b (float): 第二个数字
Returns:
float: 两个数字的和
"""
return float(a + b)
def div(a: float, b: float) -> float:
"""
将第一个数字除以第二个数字
Args:
a (float): 被除数
b (float): 除数
Returns:
float: 两个数字的商
Raises:
ZeroDivisionError: 当除数为零时抛出
"""
if b == 0:
raise ZeroDivisionError("除数不能为零")
return float(a / b)
def sub(a: float, b: float) -> float:
return float(a - b)
def mult(a: float, b: float) -> float:
return float(a * b)
src/simple_math/init.py
""" 简单数学计算包 提供加减乘除基本运算 """
__version__ = "0.1.0"
# 导入所有功能,方便用户直接使用
from .add import add
from .sub import sub
from .mult import mult
from .div import div
import pytest
from simple_math.add import add
from simple_math.sub import sub
from simple_math.mult import mult
from simple_math.div import div
class TestAdd:
"""测试加法功能"""
def test_add(self):
assert add(2, 3) == 5.0
class TestSub:
"""测试减法功能"""
def test_sub(self):
assert sub(5, 3) == 2.0
class TestMult:
"""测试乘法功能"""
def test_mult_positive_numbers(self):
assert mult(2, 3) == 6.0
class TestDiv:
"""测试除法功能"""
def test_div(self):
assert div(6, 3) == 2.0
def test_div_by_zero(self):
with pytest.raises(ZeroDivisionError):
div(5, 0)
#!/bin/env python3
from simple_math import add, sub, mult, div
def main():
"""演示所有功能的使用"""
print("=== math_ops 包使用示例 ===\n")
# 演示数学运算
print("数学运算:")
print(f"2 + 3 = {add(2, 3)}")
print(f"5 - 3 = {sub(5, 3)}")
print(f"2 * 3 = {mult(2, 3)}")
print(f"6 / 3 = {div(6, 3)}")
print("\n" + "="*40 + "\n")
# 演示错误处理
print("错误处理:")
try:
result = div(5, 0)
print(f"5 / 0 = {result}")
except ZeroDivisionError as e:
print(f"错误:{e}")
if __name__ == "__main__":
main()
[build-system]
requires = ["setuptools>=61.0.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "simple-math"
version = "0.1.0"
description = "一个简单的数学计算"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
{name = "your_name", email = "[email protected]"}
]
keywords = ["math", "calculator", "hello-world", "example"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Education",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Education",
"Topic :: Software Development :: Libraries :: Python Modules",
]
dependencies = []
[project.urls]
Homepage = "https://example.com/yourusername/simple-math"
Documentation = "https://example.com/yourusername/simple-math#readme"
Repository = "https://example.com/yourusername/simple-math"
Issues = "https://example.com/yourusername/simple-math"
[tool.setuptools]
package-dir = {"" = "src"}
[tool.setuptools.packages.find]
where = ["src"]
# 配置测试命令
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-v"
这里对配置文件做下拆解
[build-system]
requires = ["setuptools>=61.0.0", "wheel"]
build-backend = "setuptools.build_meta"
作用:定义构建工具和依赖
[project]
name = "simple-math"
version = "0.1.0"
description = "一个简单的数学计算"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
{name = "your_name", email = "[email protected]"}
]
keywords = ["math", "calculator", "hello-world", "example"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Education",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Education",
"Topic :: Software Development :: Libraries :: Python Modules",
]
dependencies = []
[project.urls]
Homepage = "https://example.com/yourusername/simple-math"
Documentation = "https://example.com/yourusername/simple-math#readme"
Repository = "https://example.com/yourusername/simple-math"
Issues = "https://example.com/yourusername/simple-math"
提供项目主页、文档、仓库和问题跟踪链接
[tool.setuptools]
package-dir = {"" = "src"}
[tool.setuptools.packages.find]
where = ["src"]
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-v"
完成上述所有动作就可以进行打包了
# 确保你在项目根目录(有 pyproject.toml 的目录)
cd simple_math
# 使用现代构建打包
python -m build --wheel

此时我们再次查看工程目录,可以看到多了一个 build 目录,包含 dist 和 lib 目录,这部分均为打包文件,生成的 whl 包在 dist 下,名称为 simple_math-0.1.0-py3-none-any.whl,属于我们期望的 none-any类型,另外 egg 也正常产生了


# 安装 wheel 包
python -m pip install dist/simple_math-0.1.0-py3-none-any.whl
# 测试安装是否成功
python -c "import simple_math; print(simple_math.__version__)"
结果如下则证明安装成功,simple-math安装到 ~/.local/lib/python3.12/site-packages/simple_math目录下

也可通过 pip 查询如下
python -m pip list | grep simple-math

# 运行测试
pytest
测试结果如下

通过 python -m pip uninstall <module>可以卸载我们安装的软件包
# 卸载模块
python -m pip uninstall simple_math
可以看到在用户同意后(Y),我们刚安装的 simple_math模块已经移除,在 pip list以及 ~/.local/lib/python3.12/site-packages目录下均无法查询到对应的模块

同样通过测试手法也无法成功


微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online