通过hatchling实现python工程的打包(whl)处理

一直都是在用别人封装的包,一直好奇,自己能不能也打一个包玩玩。

瞎扯几句

不知道脑子想啥呢,突然想看看whl包是怎么打的,就看了一下官方文档,试了一下,竟然成功了!怕自己忘记了,就写下来吧!
自己是做自动化测试的,正好测试工程封装了好多工具类,可以打包处理,后面测试工程的代码就显得少了,更聚焦测试用例了。

基于目前情况,什么聚焦测试用例啊,对我来说都是自己催眠自己,测试工程就我一个人搞,团队就我一个人,代码多少我都不care。纯是瞎折腾!

进入正题

本文就按照官方文档的步骤来,说明一下怎么来把python工程打包成whl文件。算不上是翻译,我这水平还不够。

用到的打包工具是hatch,具体参考 官方文档

环境准备

在开始之前先保证我们的pipbuild是最新版本,以防打包时候出现莫名问题

# windows
py -m pip install --upgrade pip
# unix/macOS
python3 -m pip install --upgrade pip

创建工程

工程的包名要确保唯一性,防止在使用的时候和其他包有冲突,为了保证唯一性,我们先用名字来命名,比如:你的名字是tom,那我们的包名就命名为example_package_tom
具体的工程目录结构如下:

packaging_tutorial/
└── src/
    └── example_package_tom/
        ├── __init__.py
        └── example.py

在这里插入图片描述

一旦创建了该工程,后面命令都会基于该工程目录下执行。也就是packaging_tutorial目录。

其中example.py为了演示包中的方法使用,在该文件中添加如下代码

def add_one(number):
    return number + 1

创建打包文件

为了打包,我们还需要添加一些文件,具体文件名和文件在工程中的位置,如下所示:

packaging_tutorial/
├── LICENSE
├── pyproject.toml
├── README.md
├── src/
│   └── example_package_tom/
│       ├── __init__.py
│       └── example.py
└── tests/

在这里插入图片描述

tests/ 是存放测试文件的地方,可以为空目录
pyproject.toml 是编写打包参数的文件

打开pyproject.toml文件,输入如下信息

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

requires选项是存放打包所需要的依赖包,不需要安装他们,在执行build命令的时候会自动安装。
build-backend 是一个打包选项,意为通过hatchling.build打包

配置元数据

打开pyproject.toml并加入如下内容,根据自己的工程信息更改相应的元数据信息,,重要的事情再说一遍,name属性,也就是包名,尽量保证唯一性
,以免和其他人的包产生冲突。

[project]
name = "example_package_tom"
version = "0.0.1"
authors = [
  { name="Example Author", email="author@example.com" },
]
description = "A small example package"
readme = "README.md"
requires-python = ">=3.7"
classifiers = [
    "Programming Language :: Python :: 3",
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
]

[project.urls]
"Homepage" = "https://github.com/pypa/sampleproject"
"Bug Tracker" = "https://github.com/pypa/sampleproject/issues"

创建README.md文件

打开README.md文件并写入如下内容(可以自定义内容):

# Example Package

This is a simple example package. You can use
[Github-flavored Markdown](https://guides.github.com/features/mastering-markdown/)
to write your content.

创建LICENSE文件

LICENSE文件针对每个上传到python仓库的包来说非常重要。针对license的详细介绍,可以参考链接,此处不做详细介绍。
打开LICENSE文件,并写入如下内容:

Copyright (c) 2018 The Python Packaging Authority

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

有的打包工具会自动引入LICENSE文件,这个要看打包工具的官方文档来了解详细信息。

生成whl文件

终于到这一步了,上面做了那么多,不就为了这一步么!

首先确保我们的build版本是最新的,这个在刚开始我们就提到过了。
进入到pyproject.toml同级目录下,也就是工程根目录下,执行如下命令:

# windows
py -m build
# unlix/macOS
python3 -m build

命令执行后会打印相关的打包信息,根据日志信息,可以看出打包结果会在dist目录下生成两个文件:

dist/
├── example_package_tom-0.0.1-py3-none-any.whl
└── example_package_tom-0.0.1.tar.gz

一个是whl文件,也就是我们想要的包文件

一个是tar.gz文件,就是我们的源码文件

具体的打包日志信息如下:

* Creating venv isolated environment...
* Installing packages in isolated environment... (hatchling)
* Getting build dependencies for sdist...
* Building sdist...
* Building wheel from sdist
* Creating venv isolated environment...
* Installing packages in isolated environment... (hatchling)
* Getting build dependencies for wheel...
* Building wheel...
Successfully built example_package_tom-0.0.1.tar.gz and example_package_tom-0.0.1-py3-none-an
y.whl

此时的工程结构如下:
在这里插入图片描述

测试一下

whl包已经生成了,能不能用呢?给了别人万一不能用,就尴尬了!我们测试一下看看。
通过命令行执行如下命令安装生成的包。

cd dist
pip install example_package_tom-0.0.1-py3-none-any.whl

打印信息如下就代表已经安装成功了

(venv) E:\packaging_tutorial>cd dist

(venv) E:\packaging_tutorial\dist>pip install example_package_tom-0.0.1-py3-none-
any.whl
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Processing e:\packaging_tutorial\dist\example_package_tom-0.0.1-py3-none-any.whl 
Installing collected packages: example-package-tom
Successfully installed example-package-tom-0.0.1

进入到python控制台,执行如下指令:

>>> from example_package_tom import example                           
>>> example.add_one(4)
5   
>>> 

得到以上结果,说明我们的包可以正常使用了。

额外补充

Demo的目的是为了演示,就拿我们上面的例子来说,只是为了演示如何打包,真正落地到工作上,哈哈,差太远了。肯定还有别的事情要做,
比如说,需要依赖别的第三方库,或者说需要开发一个command-line工具传递参数等。

添加依赖库

为了演示依赖库配置,我们就用个简单的例子,利用requests写个Demo。(嗯? 又是Demo?)

安装代码依赖库

首先我们先安装requests库(啥?不是添加依赖么?你这倒是先装上了。),这个库是为了我们编写代码使用,后面测试的时候再卸载掉!

还是基于example.py文件编写程序,写入以下代码(为了演示效果,用了requests库官方的一个例子):

记得在文件头引入requests

# coding:utf-8
import requests
# 此处省略 上面的 add_one(number)方法
def request_get():
    """
    演示requests库的get请求
    :return:
    """
    r = requests.get('https://api.github.com/events')
    print("请求返回的状态码为:{}".format(r.status_code))

配置更新

功能完成了,接下来更新```pyproject.toml``文件,添加依赖库

[project]
dependencies = [
"requests"
]

也可以指定版本号,如以下写法:

[project]
dependencies = [
"requests==2.28.1"
]

还有更多的写法,可以参考文档 dependncy configuration

配置都已经完成了,接下来就是打包了,这个我们已经知道如何做了。还是在工程根目录下执行py -m build

测试

上面我们已经说过了,测试验证的时候我们要把库给卸载的,执行如下命令卸载已经安装的requests

pip uninstall requests

安装我们刚刚打包的文件

cd  dist
pip install example_package_tom-0.0.1-py3-none-any.whl

从安装打印的日志可以看出来,我们安装包的时候,会自动把我们添加的依赖库requests自动装上。

(venv) E:\packaging_tutorial\dist>pip install example_package_tom-0.0.1-py3-none-any.whl
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Processing e:\packaging_tutorial\dist\example_package_tom-0.0.1-py3-none-any.whl
Collecting requests
  Using cached https://pypi.tuna.tsinghua.edu.cn/packages/ca/91/6d9b8ccacd0412c08820f72cebaa4f0c0441b5cda699c90f618b6f8a1b42/requests-2.28.1-py3-none-any.whl (62 kB)
Requirement already satisfied: idna<4,>=2.5 in e:\packaging_tutorial\venv\lib\site-packages (from requests->example-package-tom==0.0.1) (3.4)
Requirement already satisfied: certifi>=2017.4.17 in e:\packaging_tutorial\venv\lib\site-packages (from requests->example-package-tom==0.0.1) (2022.9.24)
Requirement already satisfied: charset-normalizer<3,>=2 in e:\packaging_tutorial\venv\lib\site-packages (from requests->example-package-tom==0.0.1) (2.1.1)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in e:\packaging_tutorial\venv\lib\site-packages (from requests->example-package-tom==0.0.1) (1.26.13)
Installing collected packages: requests, example-package-tom
Successfully installed example-package-tom-0.0.1 requests-2.28.1

安装成功后,进入到python终端,执行以下指令

(venv) E:\packaging_tutorial\dist>python
Python 3.10.5 (tags/v3.10.5:f377153, Jun  6 2022, 16:14:13) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from example_package_tom import example
>>> example.request_get()                   
请求返回的状态码为:200
>>> 

对,要的就是这效果,如果也是有这个输出的话,就验证通过了。

读内部文件

有时候代码中会涉及到一些配置文件,资源文件之类的,需要打包的时候一起打进去,但是打进去之后,可能会发现,执行时候报文件不存在的错误。

下面就简单提一下这块的内容。

创建资源文件

example_package_tom包下创建resources目录,并创建文本文件如demo.txt,内容可以自定义,测试时候把内容打印出来。

此时工程结构如下:

读取资源文件

我们依然是基于example.py文件添加函数,用于读取文件。

# coding:utf-8 
import os.path

# 此处省略其他代码和引入的包信息

def read_txt():
    # 获取当前文件所在目录
    cur_dir = os.path.split(os.path.realpath(__file__))[0]
    # 根据当前路径打开资源文件并读取打印出来
    with open(os.path.join(cur_dir, "./resources/demo.txt"), "r", encoding="utf-8") as f:
        print(f.readlines())

打包验证

又到了打包验证时刻,同样的操作,不同的验证,直接上验证代码吧

>>> from example_package_tom import example
>>> example.read_txt()  
['hello everyone, i am a demo text!']
>>>

到此,读取资源文件部分也算通过了。

CLI工具

平时我们需要使用到一些命令行来辅助完成工作,比如提供一个工具给到别人直接命令行执行测试,不需要提供代码了。这样就很方便。

添加入口函数

既然是命令行工具,我们就需要一个执行命令时候的处理函数,也就是入口函数,同样还是基于example.py文件来实现

example.py文件中添加函数,函数名可以自定义,这里就用的main函数

def main():
    print("Hello EveryOne")

在实际工作应用中,这里的处理会多一些参数解析的工作,用到python的argparse库,这块内容需要了解的话,可以参考python官方文档中的Argparse 教程,此处就不做体现了。

配置更新

入口函数完成之后就要更新pyproject.toml配置文件,加入我们的自定义命令

打开pyproject.toml,添加如下内容

[project.scripts]
cli = "example_package_tom.example:main"

cli可以自定义,随便想起个什么名字都可以,但是也要注意冲突的问题。后面就是我们的入口函数的位置,包名.模块名:方法名的写法。

打包测试

: 哎,这个文章,来来回回打包多少次了,就不能一次性弄好,最后一下测试搞定!
可以这样啊,这不好不容易打的字,再删除掉不是可惜了么

依然是同样的方式打包,安装,直接上测试验证部分。这次不需要进入python控制台了,直接命令行输入cli,也就是我们自定义的命令指令。

(venv) E:\packaging_tutorial\dist>pip install example_package_tom-0.0.1-py3-none-
any.whl
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Processing e:\packaging_tutorial\dist\example_package_tom-0.0.1-py3-none-any.whl
Requirement already satisfied: requests in e:\packaging_tutorial\venv\lib\site-packages (from example-package-tom==0.0.1) (2.28.1)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in e:\packaging_tutorial\venv\lib\site-packages (from requests->example-package-tom==0.0.1) (1.26.13)
Requirement already satisfied: charset-normalizer<3,>=2 in e:\packaging_tutorial\venv\lib\site-packages (from requests->example-package-tom==0.0.1) (2.1.1)
Requirement already satisfied: idna<4,>=2.5 in e:\packaging_tutorial\venv\lib\site-packages (from requests->example-package-tom==0.0.1) (3.4)
Requirement already satisfied: certifi>=2017.4.17 in e:\packaging_tutorial\venv\lib\site-packages (from requests->example-package-tom==0.0.1) (2022.9.24)
Installing collected packages: example-package-tom
Successfully installed example-package-tom-0.0.1

(venv) E:\packaging_tutorial\dist>cli
Hello EveryOne

出现Hello EveryOne就验证通过了,接下来就是传递参数,参数解析之类的工作了。

附录

源码文件

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值