MicroPython/Python 发布第三方库
原文链接:
摘要
文章讲解内容包括第三方库文件说明和组织、开源许可协议选择、通过black模块、Flake8模块和预提交钩子实现自动代码格式规范和静态检查以及Github/PyPi平台公开发布,还有通过mpremote工具实现MicroPython第三方库的一键安装。
往期推荐:
全网最适合入门的面向对象编程教程:00 面向对象设计方法导论
全网最适合入门的面向对象编程教程:01 面向对象编程的基本概念
全网最适合入门的面向对象编程教程:02 类和对象的 Python 实现-使用 Python 创建类
全网最适合入门的面向对象编程教程:03 类和对象的 Python 实现-为自定义类添加属性
全网最适合入门的面向对象编程教程:04 类和对象的Python实现-为自定义类添加方法
全网最适合入门的面向对象编程教程:05 类和对象的Python实现-PyCharm代码标签
全网最适合入门的面向对象编程教程:06 类和对象的Python实现-自定义类的数据封装
全网最适合入门的面向对象编程教程:07 类和对象的Python实现-类型注解
全网最适合入门的面向对象编程教程:08 类和对象的Python实现-@property装饰器
全网最适合入门的面向对象编程教程:09 类和对象的Python实现-类之间的关系
全网最适合入门的面向对象编程教程:10 类和对象的Python实现-类的继承和里氏替换原则
全网最适合入门的面向对象编程教程:11 类和对象的Python实现-子类调用父类方法
全网最适合入门的面向对象编程教程:12 类和对象的Python实现-Python使用logging模块输出程序运行日志
全网最适合入门的面向对象编程教程:13 类和对象的Python实现-可视化阅读代码神器Sourcetrail的安装使用
全网最适合入门的面向对象编程教程:全网最适合入门的面向对象编程教程:14 类和对象的Python实现-类的静态方法和类方法
全网最适合入门的面向对象编程教程:15 类和对象的 Python 实现-__slots__魔法方法
全网最适合入门的面向对象编程教程:16 类和对象的Python实现-多态、方法重写与开闭原则
全网最适合入门的面向对象编程教程:17 类和对象的Python实现-鸭子类型与"file-like object"
全网最适合入门的面向对象编程教程:18 类和对象的Python实现-多重继承与PyQtGraph串口数据绘制曲线图
全网最适合入门的面向对象编程教程:19 类和对象的 Python 实现-使用 PyCharm 自动生成文件注释和函数注释
全网最适合入门的面向对象编程教程:20 类和对象的Python实现-组合关系的实现与CSV文件保存
全网最适合入门的面向对象编程教程:21 类和对象的Python实现-多文件的组织:模块module和包package
全网最适合入门的面向对象编程教程:22 类和对象的Python实现-异常和语法错误
全网最适合入门的面向对象编程教程:23 类和对象的Python实现-抛出异常
全网最适合入门的面向对象编程教程:24 类和对象的Python实现-异常的捕获与处理
全网最适合入门的面向对象编程教程:25 类和对象的Python实现-Python判断输入数据类型
全网最适合入门的面向对象编程教程:26 类和对象的Python实现-上下文管理器和with语句
全网最适合入门的面向对象编程教程:27 类和对象的Python实现-Python中异常层级与自定义异常类的实现
全网最适合入门的面向对象编程教程:28 类和对象的Python实现-Python编程原则、哲学和规范大汇总
全网最适合入门的面向对象编程教程:29 类和对象的Python实现-断言与防御性编程和help函数的使用
全网最适合入门的面向对象编程教程:30 Python的内置数据类型-object根类
全网最适合入门的面向对象编程教程:31 Python的内置数据类型-对象Object和类型Type
全网最适合入门的面向对象编程教程:32 Python的内置数据类型-类Class和实例Instance
全网最适合入门的面向对象编程教程:33 Python的内置数据类型-对象Object和类型Type的关系
全网最适合入门的面向对象编程教程:34 Python的内置数据类型-Python常用复合数据类型:元组和命名元组
全网最适合入门的面向对象编程教程:35 Python的内置数据类型-文档字符串和__doc__属性
全网最适合入门的面向对象编程教程:36 Python的内置数据类型-字典
全网最适合入门的面向对象编程教程:37 Python常用复合数据类型-列表和列表推导式
全网最适合入门的面向对象编程教程:38 Python常用复合数据类型-使用列表实现堆栈、队列和双端队列
全网最适合入门的面向对象编程教程:39 Python常用复合数据类型-集合
全网最适合入门的面向对象编程教程:40 Python常用复合数据类型-枚举和enum模块的使用
全网最适合入门的面向对象编程教程:41 Python常用复合数据类型-队列(FIFO、LIFO、优先级队列、双端队列和环形队列)
全网最适合入门的面向对象编程教程:42 Python常用复合数据类型-collections容器数据类型
全网最适合入门的面向对象编程教程:43 Python常用复合数据类型-扩展内置数据类型
全网最适合入门的面向对象编程教程:44 Python内置函数与魔法方法-重写内置类型的魔法方法
全网最适合入门的面向对象编程教程:45 Python实现常见数据结构-链表、树、哈希表、图和堆
全网最适合入门的面向对象编程教程:46 Python函数方法与接口-函数与事件驱动框架
全网最适合入门的面向对象编程教程:47 Python函数方法与接口-回调函数Callback
全网最适合入门的面向对象编程教程:48 Python函数方法与接口-位置参数、默认参数、可变参数和关键字参数
全网最适合入门的面向对象编程教程:49 Python函数方法与接口-函数与方法的区别和lamda匿名函数
全网最适合入门的面向对象编程教程:50 Python函数方法与接口-接口和抽象基类
全网最适合入门的面向对象编程教程:51 Python函数方法与接口-使用Zope实现接口
全网最适合入门的面向对象编程教程:52 Python函数方法与接口-Protocol协议与接口
全网最适合入门的面向对象编程教程:53 Python字符串与序列化-字符串与字符编码
全网最适合入门的面向对象编程教程:54 Python字符串与序列化-字符串格式化与format方法
全网最适合入门的面向对象编程教程:55 Python字符串与序列化-字节序列类型和可变字节字符串
全网最适合入门的面向对象编程教程:56 Python字符串与序列化-正则表达式和re模块应用
全网最适合入门的面向对象编程教程:57 Python字符串与序列化-序列化与反序列化
全网最适合入门的面向对象编程教程:58 Python字符串与序列化-序列化Web对象的定义与实现
全网最适合入门的面向对象编程教程:59 Python并行与并发-并行与并发和线程与进程
更多精彩内容可看:
给你的 Python 加加速:一文速通 Python 并行计算
一个MicroPython的开源项目集锦:awesome-micropython,包含各个方面的Micropython工具库
Avnet ZUBoard 1CG开发板---深度学习新选择
1.前言

在 MicroPython 生态发展的早期,开发者共享或使用第三方库时,往往需要手动下载多个文件并复制到项目目录中,这种"复制粘贴"的方式不仅繁琐,还容易引发版本混乱、依赖缺失等问题,同时我们可以看到许多代码文档注释并不完善,上手往往难以使用,经常是看着 API 不知道入口参数是什么类型,如下所示:

随着 MicroPython 社区工具的成熟(如 mpremote
、upip
),如今遵循规范流程发布第三方库已成为学习 MicroPython 开发内容中必不可少的一环,通过编写规范的 package.json 项目依赖文件、完善的 README.md 文件、遵循 PEP8 规范的代码,我们可以让别人更容易使用我们编写的 MicroPython 库文件。
例如,我们可以使用 mpremote
工具直接实现我们编写第三方模块及其依赖库的一键下载:

可以看到,规范的库文件使得用户无需手动管理文件路径,依赖解析和更新均可由工具链自动完成,大幅降低了使用门槛,并且如果我们的 Github 仓库中该库有更新内容,还是可以直接通过一行命令完成库文件的安装。
本文将以我们编写的 MicrPython 串口舵机库为例,讲解如何规范的发布 MicroPython 第三方库文件。
其中代码链接如下:
https://github.com/leezisheng/freakstudio-micropython-libraries/tree/main/serial_servo
由于 MicroPython-lib 发布比较麻烦,我们将整个库文件发布到了 PyPi 上面:
https://pypi.org/project/serial-servo/
如果你想学习 Python 第三方库发布,这篇文章对你也非常合适。
本文适合有一定 Python 语言基础,了解 Python 虚拟环境配置、面向对象编程语法、之前实际进行 MicroPython 开发并且有一定版本管理意识但对于开源发布并不了解的入门者进行查看,其本文主要内容包括:
- 第三方库需要的文件说明及文件夹组织
- 使用 Pre-commit hook 自动调用 black 和 Flake8 模块,进行代码格式规范与静态检查
- PyPi 平台注册和公开发布
2.MicroPython 第三方库文件说明及文件夹组织
2.1 什么是发布库文件
在 MicroPython 中,发布库文件指的是将已经开发好的功能模块或工具库以标准化的方式分享给其他开发者使用,通过发布我们编写的库文件,可以让其他人能够快速地集成并使用这些库,而不必从头开始编写相同的功能;发布库文件通常包含功能实现、配置文件、示例代码以及文档,按照一定的规范组织,使得其他用户可以通过简单的安装和引用过程来使用这些功能模块。
对于 MicroPython 来说,发布库文件不仅仅是一个普通的 .py
文件,它还应该包括项目的元数据、文档、示例和许可证等,方便其他开发者快速了解和使用,以下是我们串口舵机库文件的组织内容:

可以看到,包括功能实现文件(.py 文件)、README.md 文件、开源许可证、示例主程序和项目依赖文件等。
这里说一句题外话,在 GitHub 上公开发布一个 MicroPython 库并不意味着在正式意义上将其"发布"到某个软件包管理平台(例如 PyPI),不过这个过程被通常称为"发布"或"公开发布",因为你将你的库开放给其他开发者使用,并且可以通过 GitHub 来进行版本管理、问题追踪和社区协作。
发布一个 MicroPython 库文件的过程包括:
- 将功能模块和工具类封装成一个易于安装和使用的形式。
- 确保库的结构和文档完善,以便其他开发者能够理解并使用。
- 在适当的地方选择合适的许可证,允许他人修改和分享代码。
一般来说,MicroPython/Python 会发布到以下这些平台上:

具体区别如下所示:
PyPI | MicroPython-lib | GitHub/GitLab | |
---|---|---|---|
特点 | - PyPI 是 Python 生态中最主流的包管理平台,支持 pip 安装。- 发布到 PyPI 的库可以被全球 Python 开发者轻松访问。- MicroPython 的库如果依赖特定硬件功能(如 GPIO、I2C 等),可能无法直接在 PyPI 上运行。 |
- MicroPython 官方维护了一个库索引(MicroPython Libraries),收录了大量针对 MicroPython 优化的库。- 这些库通常经过 MicroPython 核心团队的审核,兼容性和性能较好。- 适合发布与硬件交互、资源占用低的库。- 发布流程需要通过 GitHub 提交 Pull Request,并经过社区审核。 | - GitHub/GitLab 是开源社区最常用的代码托管平台,支持版本管理和协作开发。- 可以通过 git clone 或直接下载源码的方式分发库。- 适合发布实验性、未成熟的库,或者需要频繁更新的库。- 需要用户手动下载和安装,不如 PyPI 或 MicroPython 官方库索引方便,但是如果库的配置依赖文件书写无误并且符合 mpremote 要求的包结构。 |
适用对象 | - 纯 Python 库- 兼容 MicroPython 的库 | MicroPython 的库 | - 纯 Python 库- MicroPython 的库 |
库的安装方式 | 通过 pip install 或 conda install 。 |
通过 mpremote 、GitHub 下载或手动复制到设备。 |
均支持 |
这里,为了兼顾讲解 Python 和 MicroPython 两个语言第三方库的发布,我们的发布平台设定为 PyPI,但注意,我们无法通过 pip 工具安装 MicroPython 库,这个串口舵机库也无法在 PC 端的 Python 平台使用。
由于 MicroPython-lib 平台上发布比较繁琐,例如需要包含 unittest
框架编写的测试文件,这里我们不进行讲解,感兴趣同学可以在学习完成本文后,自行研究如下内容:
2.2 库文件组织
一个 Python/MicroPython 第三方库文件夹结构如下:
bash
my_micropython_library/
│
├── my_module.py # 库的主要模块或功能文件
├── __init__.py # 可选,表示这是一个包
├── README.md # 项目文档,描述库的用途、安装、示例等
├── LICENSE # 开源许可证文件,声明使用协议
├── requirements.txt # 可选,列出依赖的其他库
├── examples/ # 存放示例代码的文件夹
│ ├── example1.py
│ └── example2.py
└── library.properties # 可选,库的元数据文件
其中包括:
- 功能实现文件(.py文件): 这些是库的核心文件,包含了库的主要功能。根据库的复杂性,这些文件可能只有一个或者多个。例如,可能有一个用于与硬件交互的模块(比如控制一个传感器、屏幕或电机等)。
- README.md文件: 该文件描述了库的用途、安装方法、使用示例等内容,是项目的文档入口。
- LICENSE文件: 用于声明库的开源许可证,明确你希望其他人如何使用、修改、分发这个库。常见的开源许可证有 MIT 、GPL 、Apache 2.0 等。
- 示例代码(examples 文件夹): 提供一些简单的示例代码,帮助用户快速上手使用该库。例如,可以通过一个小的脚本展示如何初始化库、如何调用库中的方法、如何处理异常等。
- init.py 文件(可选):** 如果你将多个功能模块组织成一个包结构,可以添加
init.py
文件,它使得目录成为一个 Python 包,允许通过import
语句引用库中的模块。 - 项目依赖文件: 在 MicroPython 中,如果你想使得库能通过工具(如
mpremote
)进行一键下载,你需要提供一个package.json
、manifest.py
或者requirements.txt
的元数据文件,这个文件包含库的名称、版本、依赖关系等信息,其中 Python 包使用requirements.txt
文件,而 MicroPython 使用package.json
文件或manifest.py
文件进行描述。
这里,可以查看我们的库文件夹组织:

其中,serial_servo 文件夹是库的根目录文件夹,文件夹内部的 serial_servo 文件夹包含库的主要功能实现,其中包含 __init__.py
文件和 serial_servo.py
文件:
__init__.py
文件:使得该目录成为一个 Python 包,以便通过import
引用。serial_servo.py
:库的核心代码实现文件,通常包含实际功能和类的定义。
在项目依赖文件中,我们使用两个文件:
- package.json文件:此文件通常用于描述项目的依赖项和元数据,MicroPython 中使用。
- requirements.txt和setup.py文件:列出 Python 项目所依赖的第三方库,Python 中使用。
这里,如果你只需要发布 MiroPython 第三方库在自己的 Github 仓库中,只需要 package.json
文件即可;如果你需要发布 Python 第三方库在 PyPi 平台上,那么你需要 requirements.txt
和 setup.py
文件即可;如果你想要发布到 MicroPython-lib 平台上,那么你需要使用 manifest.py
作为项目依赖文件。
2.3 库文件说明
2.3.1 库实现代码
2.3.1.1 核心代码实现文件
这里,我们需要在库的根目录文件夹下命名一个同名子文件夹,库的主要功能实现代码就放到该子文件夹中,拿我们的库文件举例,serial_servo 文件夹是库的根目录文件夹,文件夹内部的 serial_servo 文件夹包含库的主要功能实现,其中包含 __init__.py
文件和 serial_servo.py
文件:

这里,serial_servo.py
文件为库的核心代码实现文件:

我们提供的第三方库应当将相关功能组织在一个类中,便于管理和扩展,类可以封装内部状态(如硬件配置、传感器数据等),来避免全局变量的滥用,关于 Python 面向对象编程相关知识,可以查看我们之前的教程:
在封装为类时,需要注意以下几点,包括清晰的函数命名、注释和文档字符串、错误处理和类型注解。
我们需要确保每个方法的命名符合功能,例如 move_servo_immediate
明确表示立即移动舵机,calculate_checksum
清晰表达了计算校验和的功能,示例代码如下:
bash
def calculate_checksum(self, data: list[int]) -> int:
"""
计算校验和。
校验和通过对数据包进行求和并取反得到(低八位数据),确保数据的完整性和正确性。
Args:
data (list[int]): 数据包(不包括校验和本身)。
Returns:
int: 计算出的校验和,值范围为 0~255。
===================================================
Calculate checksum to ensure data integrity and correctness.
The checksum is obtained by summing the data packet and taking the one's complement (low eight bits of data).
Args:
data (list[int]): Data packet (excluding checksum itself).
Returns:
int: Calculated checksum, value range 0~255.
"""
checksum = ~(sum(data) & 0xFF) & 0xFF
return checksum
并且可以看到在每个函数和方法上方,我们都添加详细的文档字符串(docstring),来说明每个方法的目的、输入参数和返回值。
同样的,我们也使用了类型注解在代码中为变量、函数参数和返回值指定类型,从而帮助其他开发者可以轻松理解函数的输入和输出类型:
bash
def build_packet(self, servo_id: int, cmd: int, params: list[int]) -> bytearray:
return bytearray(packet)
def send_command(self, servo_id: int, cmd: int, params: list[int] = []) -> None:
self.uart.write(packet)
def receive_command(self, expected_cmd: int, expected_data_len: int) -> list:
return params
同时,需要注意在方法中使用适当的错误处理,例如在 build_packet
方法中检查 servo_id
是否在有效范围内,如果不符合要求则抛出异常:
bash
def build_packet(self, servo_id: int, cmd: int, params: list[int]) -> bytearray:
"""
构建舵机指令包。
根据舵机ID、指令命令和参数生成一个完整的数据包,并附加校验和。
Args:
servo_id (int): 舵机ID,范围0~253,其中254为广播ID,表示所有舵机。
cmd (int): 指令命令字节。
params (list[int]): 参数列表。
Returns:
bytearray: 构建的舵机控制指令包。
Raises:
ValueError: 如果舵机ID不在 0~254 范围内,则抛出异常。
===================================================
Build servo control command packet, including checksum.
Args:
servo_id (int): Servo ID, range 0~253, where 254 is broadcast ID, indicating all servos.
cmd (int): Command byte.
params (list[int]): Parameter list (default empty).
Returns:
bytearray: Built servo control command packet, including checksum.
Raises:
ValueError: If the servo ID is not in the range 0~254, an exception will be raised.
"""
# 对传入参数进行检查
# 检查舵机ID是否在 0~254 范围内
if servo_id < 0 or servo_id > 254:
raise ValueError("Servo ID must be in range 0~254.")
# 数据包长度(指令+参数+校验和)
length = 3 + len(params)
# 构建数据包:帧头 + ID + 数据长度 + 命令 + 参数 + 校验和
packet = [0x55, 0x55, servo_id, length, cmd] + params
# 计算校验和(从ID到最后一个参数)
checksum = self.calculate_checksum(packet[2:])
# 增加校验和
packet.append(checksum)
return bytearray(packet)
关于错误处理和类型注解知识点可以查看以下两个链接文档:
在类的文档字符串中,我们也应解释类的整体作用、功能以及使用方法:
bash
# 串口舵机自定义类
class SerialServo:
"""
串口舵机控制类,用于生成和发送控制指令。
该类通过UART串口与舵机进行通信,支持构建控制指令包、计算校验和以及发送指令。
支持可调的波特率和不同舵机的控制。
Attributes:
uart (machine.UART): 用于与舵机通信的UART实例。
Class Variables:
- 指令及其参数长度或返回数据长度的定义。
- 各种舵机控制指令的定义,包括写入命令和读取命令。
- 舵机工作模式的定义。
- LED报警故障类型的定义。
Methods:
calculate_checksum(data: list[int]) -> int:
计算校验和,确保数据的完整性和正确性。
build_packet(servo_id: int, cmd: int, params: list[int]) -> bytearray:
构建舵机指令包。
send_command(servo_id: int, cmd: int, params: list[int] = []) -> None:
发送控制指令到舵机。
receive_command(expected_cmd: int, expected_data_len: int) -> list:
接收并处理舵机返回的指令数据包。
move_servo_immediate(servo_id: int, angle: float, time_ms: int) -> None:
立即控制舵机转动到指定角度。
get_servo_move_immediate(servo_id: int) -> tuple:
获取舵机的预设角度和时间。
2.3.1.2 __init__.py
标识文件
__init.py__
文件是一个特殊的 Python 文件,用于将一个目录标识为一个 Python 包,如果我们的模块中有多个文件,可以在模块文件同级目录下放置一个 __init.py__
文件,将该文件夹标识为一个包,使得该目录下的所有 Python 文件都被视为一个包的一部分,可以通过 import
语句进行导入和使用。
例如,包中有多个模块(如 module1.py
, module2.py
),init.py
文件可以通过 import
语句导入这些模块,使得用户能够直接通过包名来访问包内的模块:
bash
from mypackage import module1
并且,如果希望在包级别提供某些接口或属性,可以在 __init.py__
文件中定义:
bash
# mypackage/__init__.py
from .module1 import MyClass
这样,用户可以直接通过 from mypackage import MyClass
来访问 module1.py
中的 MyClass
类。
在我们提供的串口舵机类的 __init.py__
文件中:
bash
# 导入 SerialServo 类并将其暴露给包用户
from .serial_servo import SerialServo
# 通过 __all__ 确保只暴露 SerialServo 类
__all__ = ["SerialServo"]
# 定义版本号
__version__ = "1.0.0"
我们在包级别提供了 SerialServo
类,同时简单定义了版本号。
关于 Python 中模块和包的基本知识,可以查看如下文档:
2.3.2 README 文件
在任何一个开源库中,README.md
文件都是一个非常重要的文件,它不仅提供了对项目的基本介绍,而且帮助其他开发者快速理解如何使用、贡献和安装该库,可以说 README.md
文件是项目的"门户"。

README.md
文件是一个文本文件,通常会以 Markdown 格式编写,包含以下内容:
bash
**项目名称**:简要说明项目的名称。
**项目简介**:说明项目的目的、功能或使用场景,提供一些背景信息,帮助用户理解项目的用途。
**安装指南**:提供如何安装和配置该项目的步骤,包括系统要求、依赖项和安装命令等。
**使用说明**:提供使用项目的基本步骤、命令行或API接口的示例代码、配置文件示例等。
**功能特性**:列出项目的主要功能和特性,让用户了解项目的能力。
**贡献指南**:如果该项目是开源的,应该提供如何贡献代码、提交bug报告或反馈的说明。
**许可证**:说明该项目使用的许可证类型(如MIT, GPL等)。
**开发和测试**:说明如何进行本地开发、测试项目和提交修改。
**文档链接**:如果项目有更详细的文档或外部资源链接,提供这些信息。
**示例代码**:提供项目的基本使用示例或代码片段,帮助用户快速入门。
**联系方式**:如果用户有问题或建议,可以如何联系开发者或团队。
**致谢**:感谢对项目有帮助的贡献者或项目所依赖的其他开源项目。
我们的 README 文件中,包括了以下内容:






具体可以查看:
我们这边习惯于使用 PyCharm 中 MD 插件来编写 README.md
文件,在编写 md 文件之前,需要了解一些简单 md 语法,推荐大家看如下教程:
2.3.3 开源许可证
许可证(License) 类似于一种法律协议,对于任何一个开源项目来说都非常重要,它明确规定了一个软件项目的使用、修改、分发等方面的权限和限制:

- **明确使用权限:**许可证为使用者明确了可以如何合法使用第三方库,例如,是否可以商业化使用、是否可以修改源代码、是否需要公开修改后的版本等。
- **保护开发者权益:**通过许可证,开发者可以规定自己对代码的控制权限。开发者可以选择开源协议(如 MIT、GPL 等)来授权别人使用、修改和分发其代码,同时也可以设置一些限制,避免代码被不当使用或滥用。
- **避免法律纠纷:**如果没有明确的许可证声明,其他开发者使用你的代码时,可能会产生版权和知识产权的纠纷。明确的许可证条款可以帮助减少这些问题,防止因使用不当产生的法律纠纷。
- **支持商业化:**对于一些公司或团队来说,选择适合的许可证非常重要。如果项目使用了允许商业化的开源许可证(如 MIT、Apache 2.0),那么公司可以合法地将这些代码集成到他们的产品中。而如果项目使用的是需要公开源代码的许可证(如 GPL),则可能影响其商业化路径。
许可证有多种,根据使用自由度、是否允许商业使用、修改与分发的要求、保护隐私和法律责任的不同而各有侧重,例如某些许可证(如 AGPL)关注于保护用户隐私,尤其在网络应用中,要求修改后的代码也必须公开,避免开发者利用软件收集用户数据并保持私密。
以下是一张广为流传的许可证选择分类图(来源于知乎大佬的分享):

以下是三种常用的许可证:
- MIT 许可证(Massachusetts Institute of Technology License):MIT 许可证是一种非常宽松的许可证,允许用户自由使用、复制、修改和分发软件。唯一的要求是,在软件的所有副本中必须包括原始的版权声明和许可证声明。MIT 许可证通常用于不需要对源代码进行修改或对修改后的代码做限制的开源项目。
- GPL 许可证(GNU General Public License):GPL 是由自由软件基金会(FSF)发布的开源许可证,要求使用 GPL 许可证的代码必须保持开放源代码,并且所有派生的作品也必须采用 GPL 许可证。最重要的要求是,如果你修改或分发了该代码,必须公开源代码,并且任何使用该代码的软件也必须保持开源。
- CC BY-NC 许可证(Creative Commons Attribution-NonCommercial License):这是由创意共享组织(Creative Commons)发布的许可证,允许用户复制、分发、展示和改编作品,但前提是不得用于商业目的。此外,使用该作品的人必须给予作者署名。
这里,我们使用 知识共享署名-非商业性使用 4.0 国际版 (CC BY-NC 4.0) 许可协议,在 README.md 文件中写明,可以查看我们的 LICENSE:

bash
Creative Commons Attribution-NonCommercial 4.0 International License
Copyright 2025 Lee Qingshui / Freak
This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License.
You may not use the material for commercial purposes.
You must give appropriate credit, provide a link to the license, and indicate if changes were made.
You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
To view a copy of this license, visit:
https://creativecommons.org/licenses/by-nc/4.0/
2.3.4 项目依赖和安装配置文件
2.3.4.1 Python
在纯 Python 中,我们使用 requirements.txt
文件和 setup.py
文件对发布库的依赖库及其版本、项目的元数据以及如何安装和分发项目进行说明:
requirements.txt
是一个常见的配置文件,列出项目所依赖的所有 Python 包及其版本要求:
bash
setuptools~=70.0.0
machine
time
以上是我们的 requirements.txt
,可以看到每行列出一个依赖项,并指定了版本。
setup.py
是 Python 项目的传统安装配置文件,在我们将包发布到 Python 包索引(PyPI)时是必不可少的,它提供项目信息(如名称、版本、作者等)以及依赖项和安装方式。
bash
from setuptools import setup, find_packages
# 读取 README 文件内容
with open("README.md", "r", encoding="utf-8") as fh:
long_description = fh.read()
setup(
name="serial_servo",
version="1.0.5",
description="A MicroPython library to control servo motors via UART",
author="leeqingshui",
author_email="1069653183@qq.com",
url="https://github.com/leezisheng/freakstudio-micropython-libraries",
packages=find_packages(where="serial_servo"),
long_description=long_description,
long_description_content_type="text/markdown",
install_requires=[
# 仅使用MicroPython内置模块
],
classifiers=[
"Programming Language :: Python :: 3", # 支持 Python 3
"Programming Language :: Python :: 3.8", # 支持 Python 3.8
"License :: Other/Proprietary License", # 使用 CC BY-NC 4.0 许可证
"Operating System :: OS Independent", # 操作系统无关
],
# Python版本要求(适应MicroPython,v1.23.0版本支持的Python版本)
python_requires='>=3.12',
# 如果有MicroPython相关的依赖,可以在这里添加
extras_require={
# MicroPython的依赖
'micropython': ['machine', 'time'],
},
)
在 setup.py 文件中,setup 是一个 函数,它来自 setuptools 库,其参数是一个字典,包含项目的各种元数据和配置项,即项目的基本信息、需要的 Python 最低版本要求和特定的依赖,常见参数如下:
- name : 项目的名称,例如
"serial_servo"
。 - version : 项目的版本号,例如
"1.0.5"
。 - description: 对项目的简短描述,通常是用一句话来描述项目的功能。
- author ** **和 author_email: 作者的名称和联系方式。
- url: 项目的主页 URL,通常是 GitHub 或其他托管平台的地址。
- long_description 通过读取
README.md
文件,确保发布到在 PyPI 平台正确显示项目基本信息,注意README.md
文件中图片索引需要使用 Github 中链接,如果使用相对路径引用,则无法在 PyPI 平台正确显示。 - packages=find_packages(where="serial_servo") :这个参数会查找并包含位于
serial_servo
目录下的 Python 包。 - install_requires : 你可以将外部依赖项(如
requests
、numpy
等)列在此, - extras_require : 如果你的项目有特殊的环境或附加功能依赖(例如 MicroPython),你可以在
extras_require
中定义。 - python_requires='>=3.12' : 设置
python_requires
可以确保用户安装时满足 Python 版本要求。 - classifiers : 用于帮助 PyPI 和其他工具分类你的项目,注意这里的
Operating System
对于串口舵机驱动而言我们实际上是写的不对的,应该特别标明在 MicroPython 中应用。
2.3.4.2 MicroPython
对于 MicroPython 而言,我们使用 package.json
或 manifest.py
文件对发布库的依赖库及其版本、项目的元数据以及如何安装和分发项目进行说明。
这里,关于 package.json
文件如何编写我们可以查看 MicroPython 相关文档:

package.json
文件是一个 JSON 格式的配置文件,主要用于在 MicroPython 项目中定义和管理库的依赖、版本和安装源等信息,它用于描述如何将库文件上传并安装到设备中,MicroPython 使用该文件来处理库的安装和依赖项管理。

以下是我们串口舵机驱动库的 package.json
文件:
bash
{
"name": "serial_servo",
"version": "1.0.5",
"description": "A MicroPython library to control servo motors via UART",
"author": "leeqingshui",
"author_email": "1069653183@qq.com",
"url": "https://github.com/leezisheng/freakstudio-micropython-libraries",
"urls": [
["serial_servo/__init__.py", "github:leezisheng/freakstudio-micropython-libraries/serial_servo/serial_servo/__init__.py"],
["serial_servo/serial_servo.py", "github:leezisheng/freakstudio-micropython-libraries/serial_servo/serial_servo/serial_servo.py"]
],
"deps": [
["micropython-machine", "latest"],
["micropython-time", "latest"]
],
"version": "1.0.5",
"long_description": "A MicroPython library to control servo motors via UART",
"long_description_content_type": "text/markdown",
"classifiers": [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"License :: Other/Proprietary License",
"Operating System :: OS Independent"
],
"python_requires": ">=3.12",
"extras_require": {
"micropython": ["machine", "time"]
}
}
其基本信息为:
- name :
serial_servo
,库的名称,用于标识该库。 - version :
1.0.5
,该库的版本号,表示这是第 1.0.5 版本。 - description: 提供对库功能的简要描述。
- author :
leeqingshui
,库的作者名字。 - author_email :
1069653183@qq.com
,作者的电子邮件地址。 - url :
https://github.com/leezisheng/freakstudio-micropython-libraries
,库的 GitHub 仓库 URL,用户可以访问此地址查看源代码、问题和文档等信息。
其他三个主要参数如下:
- 在
urls
中,我们列出了该库的文件和它们在 GitHub 上的具体位置,urls
数组中的每个元素都包含一对文件路径及其远程仓库位置。 deps
数组列出了该库的所有依赖项及其版本要求。version
版本信息中列出文件版本。
在正确编写 package.json
文件的情况下,我们可以使用 mip
和 mpremote
工具实现一行命令将发布到 Github 的第三方库下载到 MCU 的对应文件夹位置,package.json
则适合简单的静态配置。
你也可能在其他地方看到一些 MicroPython 库文件使用 manifest.py
作为项目依赖文件说明,例如我们经常使用的 MQTT 库:

不同于 package.json
文件,manifest.py
是一个 Python 脚本,其作用与 package.json
文件类似,都描述库的元数据、依赖关系以及如何将库集成到 MicroPython 固件中,它通常用于发布到 micropython-lib
的库,主要通过内置的 metadata 函数实现:
bash
def metadata():
return {
"name": "mlx90640",
"version": "0.2",
"description": "Library for controlling the mlx90640 sensor",
"dependencies": [
("collections-defaultdict", "latest"),
("os-path", "latest")
]
}
各个字段含义如下:
- name: 项目的名称。
- version: 项目的版本。
- description: 项目的简短描述。
- dependencies: 项目的依赖项,列出库的依赖项和版本。
manifest.py
文件适合更复杂的配置和动态逻辑,例如可以根据平台选择不同的依赖:
bash
def metadata():
dependencies = {
"collections-defaultdict": "latest",
"os-path": "latest"
}
# 根据平台添加特定依赖
import sys
if sys.platform == "esp32":
dependencies["esp32-specific-lib"] = "latest"
elif sys.platform == "rp2":
dependencies["rp2-specific-lib"] = "latest"
return {
"description": "A library with platform-specific dependencies.",
"version": "0.1",
"dependencies": dependencies
}
在这种场景下,manifest.py
可以根据运行平台动态调整依赖关系,而 package.json
无法实现这种动态逻辑,同时 manifest.py
可以动态生成文件列表,甚至可以根据条件选择性地包含或排除某些文件:
bash
def metadata():
files = [
("mylibrary/__init__.py", "github:org/mylibrary/mylibrary/__init__.py"),
("mylibrary/core.py", "github:org/mylibrary/mylibrary/core.py")
]
# 根据配置添加额外的文件
import os
if os.getenv("USE_ADVANCED_FEATURES") == "1":
files.append(("mylibrary/advanced.py", "github:org/mylibrary/mylibrary/advanced.py"))
return {
"description": "A library with optional advanced features.",
"version": "0.1",
"files": files
}
也可以包含调试日志、错误处理机制等功能:
bash
import logging
logging.basicConfig(level=logging.DEBUG)
def metadata():
logging.debug("Generating metadata for the library")
return {
"name": "library-with-debug",
"version": "1.0",
"description": "Library with dynamic configuration",
"dependencies": [("os-path", "latest")]
}
关于 JSON 格式相关内容,可以查看我们之前的教程:
2.3.5 示例文件
这里,我们没有设置示例文件,使用示例在 README.md
文件中标明:
bash
from machine import Pin, UART
from serial_servo import SerialServo
# 配置UART串口
uart = UART(1, baudrate=115200, tx=Pin(4), rx=Pin(5))
# 初始化串口舵机控制类
servo = SerialServo(uart)
# 控制舵机转到指定角度
servo.move_servo_immediate(servo_id=1, angle=90.0, time_ms=1000)
# 获取舵机的角度和时间设置
angle, time = servo.get_servo_move_immediate(servo_id=1)
print(f"Servo ID: 1, Angle: {angle}, Time: {time}")
2.3.6 其余文件
这里,我们没有其余文件,只有 README.md
文件中引用的图片,将其放到了 image
文件夹中:

实际上,我们可以单独设置一个 extra 文件夹,放置参考文件和图片。
3.代码格式规范和静态检查
在发布 MicroPython 第三方模块时,代码格式规范和静态检查是非常重要的步骤,我们希望自己的每个库文件其缩进、行宽、风格类似,而非混乱:

在编写代码时,我们往往遵守 PEP8 代码规范,PEP8 代码规范是 Python 官方的编码风格指南,它定义了 Python 代码的格式规范:
- 缩进:使用 4 个空格进行缩进,避免使用制表符。
- 行宽:每行的字符数应不超过 79 个字符。
- 空格使用 :运算符(如
=
、+
等)两边应有空格,避免在逗号和参数之间使用空格。 - 函数和类命名 :函数使用小写字母,单词间用下划线分隔(如
my_function
);类名使用驼峰命名法(如MyClass
)。 - 文档字符串:函数和类应有文档字符串,说明其功能和用法。
PEP8 的目的是通过一致的代码风格来提高代码的可读性和可维护性。遵守这些约定可以让 Python 代码更加 Pythonic。
关于 PEP8 规范更多信息可以查看如下链接中文章:
3.1 使用 black 模块规范代码

black
是一个自动化的 Python
代码格式化工具,它会根据 PEP8
规范自动格式化代码,使其符合统一的风格要求,使用 black
的好处在于可以自动修正代码格式,无需手动调整。
其格式化风格类似下面代码所示:

这里,首先在我们需要格式化的代码目录下,打开终端,进入虚拟环境,首先安装 black
:
python
pip install black

安装完成后,可以使用以下命令对代码进行格式化:
python
black <path-to-your-python-files>
这里,我们对目录中 serial_servo.py
文件进行格式化:

查看 Github Desktop 对比修改前后:




可以看到相关注释、缩进和代码行长度均有所改变,完成排版。
3.2 使用 Flake8 模块进行代码静态检查
Flake8
是一个 Python 代码静态检查工具,它通过分析代码,检查是否符合 PEP8
规范和其他风格指南的标准,它的主要作用是帮助开发者保持代码风格的一致性,捕获常见的错误,并提升代码质量。

Flake8 主要有以下几个功能:
- 检查代码的语法错误。
- 检查代码的风格是否符合 PEP 8 规范。
- 检查代码的复杂度,并根据给定的阈值发出警告。
- 可通过配置文件自定义规则,忽略某些错误或警告。
bash
pip install flake8
安装后,你可以在命令行中使用 flake8
来检查 Python 文件:
bash
flake8 <your_python_file>.py

输出如下信息:
bash
(MicroPython_Learn) PS G:\test> flake8 .\serial_servo.py
.\serial_servo.py:8:80: E501 line too long (91 > 79 characters)
.\serial_servo.py:16:80: E501 line too long (92 > 79 characters)
.\serial_servo.py:18:80: E501 line too long (92 > 79 characters)
.\serial_servo.py:20:80: E501 line too long (92 > 79 characters)
.\serial_servo.py:49:80: E501 line too long (80 > 79 characters)
.\serial_servo.py:53:80: E501 line too long (86 > 79 characters)
.\serial_servo.py:65:80: E501 line too long (98 > 79 characters)
.\serial_servo.py:69:80: E501 line too long (89 > 79 characters)
.\serial_servo.py:73:80: E501 line too long (83 > 79 characters)
.\serial_servo.py:107:80: E501 line too long (84 > 79 characters)
.\serial_servo.py:109:80: E501 line too long (119 > 79 characters)
.\serial_servo.py:118:80: E501 line too long (91 > 79 characters)
.\serial_servo.py:131:80: E501 line too long (80 > 79 characters)
.\serial_servo.py:135:80: E501 line too long (86 > 79 characters)
.\serial_servo.py:147:80: E501 line too long (98 > 79 characters)
.\serial_servo.py:151:80: E501 line too long (89 > 79 characters)
.\serial_servo.py:155:80: E501 line too long (83 > 79 characters)
.\serial_servo.py:307:80: E501 line too long (81 > 79 characters)
.\serial_servo.py:328:80: E501 line too long (117 > 79 characters)
.\serial_servo.py:341:80: E501 line too long (84 > 79 characters)
.\serial_servo.py:363:80: E501 line too long (100 > 79 characters)
.\serial_servo.py:371:80: E501 line too long (95 > 79 characters)
.\serial_servo.py:389:80: E501 line too long (84 > 79 characters)
.\serial_servo.py:413:80: E501 line too long (95 > 79 characters)
.\serial_servo.py:419:80: E501 line too long (81 > 79 characters)
.\serial_servo.py:440:80: E501 line too long (132 > 79 characters)
.\serial_servo.py:448:80: E501 line too long (116 > 79 characters)
.\serial_servo.py:451:80: E501 line too long (97 > 79 characters)
.\serial_servo.py:495:80: E501 line too long (86 > 79 characters)
.\serial_servo.py:515:80: E501 line too long (91 > 79 characters)
.\serial_servo.py:516:80: E501 line too long (133 > 79 characters)
.\serial_servo.py:519:80: E501 line too long (103 > 79 characters)
.\serial_servo.py:571:80: E501 line too long (127 > 79 characters)
.\serial_servo.py:578:80: E501 line too long (142 > 79 characters)
.\serial_servo.py:582:80: E501 line too long (108 > 79 characters)
.\serial_servo.py:593:80: E501 line too long (84 > 79 characters)
.\serial_servo.py:637:80: E501 line too long (108 > 79 characters)
.\serial_servo.py:642:80: E501 line too long (115 > 79 characters)
.\serial_servo.py:643:80: E501 line too long (135 > 79 characters)
.\serial_servo.py:646:80: E501 line too long (108 > 79 characters)
.\serial_servo.py:696:80: E501 line too long (92 > 79 characters)
.\serial_servo.py:698:80: E501 line too long (119 > 79 characters)
.\serial_servo.py:705:80: E501 line too long (142 > 79 characters)
.\serial_servo.py:709:80: E501 line too long (108 > 79 characters)
.\serial_servo.py:715:80: E501 line too long (100 > 79 characters)
.\serial_servo.py:719:80: E501 line too long (81 > 79 characters)
.\serial_servo.py:767:80: E501 line too long (129 > 79 characters)
.\serial_servo.py:768:80: E501 line too long (92 > 79 characters)
.\serial_servo.py:774:80: E501 line too long (97 > 79 characters)
.\serial_servo.py:795:80: E501 line too long (141 > 79 characters)
.\serial_servo.py:801:80: E501 line too long (97 > 79 characters)
.\serial_servo.py:822:80: E501 line too long (111 > 79 characters)
.\serial_servo.py:829:80: E501 line too long (112 > 79 characters)
.\serial_servo.py:858:80: E501 line too long (118 > 79 characters)
.\serial_servo.py:867:80: E501 line too long (97 > 79 characters)
.\serial_servo.py:912:80: E501 line too long (113 > 79 characters)
.\serial_servo.py:914:80: E501 line too long (106 > 79 characters)
.\serial_servo.py:919:80: E501 line too long (96 > 79 characters)
.\serial_servo.py:920:80: E501 line too long (120 > 79 characters)
.\serial_servo.py:923:80: E501 line too long (102 > 79 characters)
.\serial_servo.py:943:80: E501 line too long (84 > 79 characters)
.\serial_servo.py:970:80: E501 line too long (124 > 79 characters)
.\serial_servo.py:971:80: E501 line too long (132 > 79 characters)
.\serial_servo.py:977:80: E501 line too long (84 > 79 characters)
.\serial_servo.py:981:80: E501 line too long (109 > 79 characters)
.\serial_servo.py:1032:80: E501 line too long (94 > 79 characters)
.\serial_servo.py:1040:80: E501 line too long (119 > 79 characters)
.\serial_servo.py:1093:80: E501 line too long (123 > 79 characters)
.\serial_servo.py:1100:80: E501 line too long (111 > 79 characters)
.\serial_servo.py:1104:80: E501 line too long (120 > 79 characters)
.\serial_servo.py:1115:80: E501 line too long (88 > 79 characters)
.\serial_servo.py:1159:80: E501 line too long (102 > 79 characters)
.\serial_servo.py:1163:80: E501 line too long (95 > 79 characters)
.\serial_servo.py:1164:80: E501 line too long (95 > 79 characters)
.\serial_servo.py:1167:80: E501 line too long (125 > 79 characters)
.\serial_servo.py:1171:80: E501 line too long (82 > 79 characters)
.\serial_servo.py:1218:80: E501 line too long (123 > 79 characters)
.\serial_servo.py:1225:80: E501 line too long (94 > 79 characters)
.\serial_servo.py:1229:80: E501 line too long (125 > 79 characters)
.\serial_servo.py:1241:80: E501 line too long (84 > 79 characters)
.\serial_servo.py:1283:80: E501 line too long (82 > 79 characters)
.\serial_servo.py:1287:80: E501 line too long (97 > 79 characters)
.\serial_servo.py:1290:80: E501 line too long (119 > 79 characters)
.\serial_servo.py:1294:80: E501 line too long (87 > 79 characters)
.\serial_servo.py:1298:80: E501 line too long (87 > 79 characters)
.\serial_servo.py:1322:80: E501 line too long (142 > 79 characters)
.\serial_servo.py:1329:80: E501 line too long (122 > 79 characters)
.\serial_servo.py:1333:80: E501 line too long (119 > 79 characters)
.\serial_servo.py:1338:80: E501 line too long (81 > 79 characters)
.\serial_servo.py:1382:80: E501 line too long (131 > 79 characters)
.\serial_servo.py:1388:80: E501 line too long (80 > 79 characters)
.\serial_servo.py:1392:80: E501 line too long (112 > 79 characters)
.\serial_servo.py:1439:80: E501 line too long (123 > 79 characters)
.\serial_servo.py:1445:80: E501 line too long (99 > 79 characters)
.\serial_servo.py:1449:80: E501 line too long (109 > 79 characters)
.\serial_servo.py:1499:80: E501 line too long (124 > 79 characters)
.\serial_servo.py:1500:80: E501 line too long (103 > 79 characters)
.\serial_servo.py:1506:80: E501 line too long (95 > 79 characters)
.\serial_servo.py:1510:80: E501 line too long (103 > 79 characters)
.\serial_servo.py:1546:80: E501 line too long (85 > 79 characters)
.\serial_servo.py:1567:80: E501 line too long (96 > 79 characters)
.\serial_servo.py:1568:80: E501 line too long (127 > 79 characters)
.\serial_servo.py:1571:80: E501 line too long (124 > 79 characters)
.\serial_servo.py:1578:80: E501 line too long (92 > 79 characters)
.\serial_servo.py:1627:80: E501 line too long (127 > 79 characters)
.\serial_servo.py:1634:80: E501 line too long (127 > 79 characters)
.\serial_servo.py:1637:80: E501 line too long (119 > 79 characters)
.\serial_servo.py:1642:80: E501 line too long (80 > 79 characters)
.\serial_servo.py:1688:80: E501 line too long (125 > 79 characters)
.\serial_servo.py:1723:80: E501 line too long (123 > 79 characters)
.\serial_servo.py:1724:80: E501 line too long (88 > 79 characters)
.\serial_servo.py:1730:80: E501 line too long (131 > 79 characters)
.\serial_servo.py:1735:80: E501 line too long (81 > 79 characters)
.\serial_servo.py:1775:80: E501 line too long (117 > 79 characters)
.\serial_servo.py:1786:80: E501 line too long (85 > 79 characters)
.\serial_servo.py:1809:80: E501 line too long (102 > 79 characters)
.\serial_servo.py:1810:80: E501 line too long (83 > 79 characters)
.\serial_servo.py:1816:80: E501 line too long (99 > 79 characters)
.\serial_servo.py:1820:80: E501 line too long (108 > 79 characters)
.\serial_servo.py:1831:80: E501 line too long (82 > 79 characters)
.\serial_servo.py:1861:80: E501 line too long (100 > 79 characters)
.\serial_servo.py:1862:80: E501 line too long (97 > 79 characters)
.\serial_servo.py:1866:80: E501 line too long (112 > 79 characters)
.\serial_servo.py:1869:80: E501 line too long (89 > 79 characters)
.\serial_servo.py:1911:80: E501 line too long (111 > 79 characters)
.\serial_servo.py:1912:80: E501 line too long (108 > 79 characters)
.\serial_servo.py:1913:80: E501 line too long (85 > 79 characters)
.\serial_servo.py:1919:80: E501 line too long (101 > 79 characters)
.\serial_servo.py:1923:80: E501 line too long (110 > 79 characters)
.\serial_servo.py:1934:80: E501 line too long (84 > 79 characters)
.\serial_servo.py:1962:80: E501 line too long (91 > 79 characters)
.\serial_servo.py:1964:80: E501 line too long (92 > 79 characters)
这里,首先讲解一下常见的 Flake8 错误代码,它使用了一些常见的错误代码,通常由字母和数字组成。例如:
-
E
错误(错误类型):E101
:缩进使用了不一致的空格和制表符。E302
:期望有 2 个空行,但找到了 1 个。
-
W
警告(风格问题):W291
:行尾有多余的空白。
-
F
错误(静态分析):F401
:导入了未使用的模块。
以上提示代码行过长:

实际上,我们可以修改最大允许代码行数量限制,Flake8
支持通过配置文件来管理和自定义规则。你可以创建一个 .flake8
文件或在 setup.cfg
文件中进行配置。

bash
[flake8]
max-line-length = 150
exclude = .git, __pycache__, build, dist
ignore = E203, W503

这里,我们设置设置行长度限制为 150 字符(默认是 79),并且排除检查 .git
、pycache
、build
和 dist
目录,同时忽略 E203
和 W503
错误。

再次运行,没有错误。
3.3 使用预提交钩子自动执行
可以看到,在使用 black
和 flake8
工具时,我们每次都需要手动输入指令,不仅繁琐,而且容易遗漏,实际上我们可以通过 Git 钩子机制在每次提交代码时进行自动运行 black
和 flake8
工具。


Git 钩子(Git Hooks)是 Git 提供的一种机制,允许你在执行 Git 操作(如提交、推送、合并等)之前或之后,自动运行自定义脚本或程序。通过 Git 钩子,开发者可以在特定时机插入自定义操作,进行自动化处理,从而提升开发效率、确保代码质量和一致性。Git 中的钩子(hooks)这个名字来源于它们的作用和工作方式,在计算机科学中,"hook" 通常指的是某种机制,可以使代码在特定事件发生时被"挂钩"到某个系统或流程中。****Git 钩子本质上就是一些脚本(通常是 shell 脚本),当你执行特定的 Git 操作(commit、push、merge)时,Git 会自动触发相应的钩子,让特定的脚本在这些操作之前或之后自动执行,这就像是将一个钩子挂到事件上,使得当事件发生时,钩子能够执行一些操作。

Git 钩子可分为以下两类:
- 客户端钩子:与用户本地的 Git 操作相关(如提交、合并等)。
- 服务器端钩子:与 Git 服务器的操作相关(如推送、接收等)。
并且,Git 钩子的命名遵循一定的规则,通常是基于 Git 操作的名称或行为,并且在其后加上特定的后缀或前缀,以表明钩子执行的时机,以下是一些常见的 Git 钩子的命名规则:

这里,我们使用 pre-commit
客户端 Git 钩子,在 git commit
命令执行前运行,我们可以通过 pre-commit
在 Git 提交操作(如 git commit)之前自动调用 black 和 flake8 等工具。

首先我们使用 pip
工具安装 pre-commit
模块:
bash
pip install pre-commit

接着在项目根目录下创建一个名为 .pre-commit-config.yaml
的配置文件,该文件包含了你想要在 Git 提交时执行的钩子。

这里,我们设置的配置文件内容如下:
bash
_repos:
- repo: git@github.com:psf/black.git
rev: 24.3.0
hooks:
- id: black
args: [--line-length=150]
- repo: git@github.com:pycqa/flake8.git
rev: 7.0.0
hooks:
- id: flake8
__ args: ["--ignore=E203,E501,W503", "--max-line-length=150"]_
这段 .pre-commit-config.yaml
配置定义了两个 Git 钩子:一个用于运行 black
格式化工具,设置每行最大长度为 150 字符;另一个用于运行 flake8
linting 工具,忽略 E203
、E501
和 W503
错误,并设置每行最大长度为 150 字符。
实际上 pre-commit 也提供了一些其他常用的钩子库,包括 mypy
(类型检查工具,用于检测类型错误)、isort
(自动排序 import 语句)、pytest
(运行测试)以及 trailing-whitespace
(删除行尾多余空格),这些工具都可以根据项目需求进行选择并配置到 .pre-commit-config.yaml
文件中。
首先,我们使用以下命令来自动更新 .pre-commit-config.yaml
文件自动更新 .pre-commit-config.yaml
文件中定义的钩子仓库到最新版本。:
bash
pre-commit autoupdate
然后运行以下命令安装 .pre-commit-config.yaml
配置中指定的钩子:
bash
pre-commit install

这个命令会创建一个 .git/hooks/pre-commit
文件,将钩子脚本与 Git 仓库关联起来。之后每次执行 git commit
时,Git 都会触发该钩子。

我们下一次提交 commit 的时候,会先运行 black
和 flake8
,检查出哪有不规范的地方,并且能自动帮你格式化,你修改之后重新提交 commit,就能顺利提交了。
我们在源代码中加点错误:

点击 commit 提交:

可以看到错误被识别到:

可以看到,每次执行 git commit
时,预提交钩子会自动执行,并在提交之前对代码进行检查、格式化等操作。如果钩子检测到代码有问题(比如代码格式不规范或存在静态检查错误),它会阻止提交,并报告相应的错误信息,删除错误代码行后,再次 commit:

可以看到成功 commit。
我们也可以选择手动运行钩子而不进行提交,可以使用以下命令:
bash
pre-commit run --all-files
我们可以简单测试一下:

3.4 GitHub 加速器的使用
需要注意的是在 .pre-commit-config.yaml
配置文件中如果指定了 repo
为 GitHub 上的在线仓库地址,在拉取或更新 Git 钩子的相关仓库时,可能会遇到访问延迟或失败的问题,导致 commit 操作报错:
bash
(MicroPython_Learn) PS G:\test> pre-commit run --all-files
[INFO] Initializing environment for git@github.com:psf/black.git.
origin', '--tags')
stdout: (none)
stderr:
banner exchange: Connection to 20.205.243.166 port 22: Software caused connection abort
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
Check the log at C:\Users\Administrator\.cache\pre-commit\pre-commit.log
为了避免这种情况,可以考虑开启 GitHub 加速器 ,或者使用一些代理服务来加速访问 GitHub,从而确保 .pre-commit
钩子的正常运行。
这里,我们使用 FastGithub.UI 进行加速:

可以看到,commit 操作时上行流量和下行流量明显加快:

3.5 mpremote 工具简介和一键下载
实际上,如果你只想将第三方库发布到自己的 Github 仓库中,在完成代码模块功能测试、执行代码格式规范和静态检查后,完全可以直接使用 mpremote
工具实现代码的一键下载,mpremote
工具是一个由 MicroPython 官方提供的命令行工具,可以实现上传和下载文件、执行脚本、设备管理等功能,详细信息可以查看如下文档:
这里,我们只对使用 mpremote
工具下载库文件进行讲解,以我们的库为例,地址为:
https://github.com/leezisheng/freakstudio-micropython-libraries/tree/main/serial_servo
使用如下命令即可通过 mpremote
工具和 mip
工具实现库文件一键下载:
bash
mpremote mip install github:leezisheng/freakstudio-micropython-libraries/serial_servo
安装进行中会显示:
bash
(base) PS D:\lee\windows terminal\terminal-1.17.11461.0> mpremote mip install github:leezisheng/freakstudio-micropython-libraries/serial_servo
Install github:leezisheng/freakstudio-micropython-libraries/serial_servo
Installing github:leezisheng/freakstudio-micropython-libraries/serial_servo/package.json to /lib
Installing: /lib/serial_servo/__init__.py
Installing: /lib/serial_servo/serial_servo.py
Installing micropython-machine (latest) from https://micropython.org/pi/v2 to /lib
Package may be partially installed
mpremote: Package not found: https://micropython.org/pi/v2/package/6/micropython-machine/latest.json
安装完成后,通过 mpremote 工具,连接你的开发板,如果成功安装,会显示如下信息:
bash
(base) PS D:\lee\windows terminal\terminal-1.17.11461.0> mpremote connect COM8
Connected to MicroPython at COM8
Use Ctrl-] or Ctrl-x to exit this shell
>>> import os
>>> os.listdir()
['lib']
>>> from serial_servo import SerialServo
>>>
4.PyPi 平台公开发布
4.1 PyPi 平台注册
这里,不多赘述,大家查看以下链接的教程注册即可:
需要注意的是,PyPi 平台均要求启用 Two-Factor Authentication (2FA) 以增强账号安全性,注册完成后我们需要在账号设置页面,选择 Add two-factor authentication,并使用身份验证应用扫描二维码并保存备用代码。
我们需要提前在手机上下载一个 microsoft authenticator 软件进行 2FA 验证登录:

注册完开好 2FA 之后,到账号设置 2FA 下面这个 API token 这里,新建一个 API token:


输入 token 名称和应用范围,如果你之前没有在 PyPi 平台上传过第三方库,选择 Entire account
即可:

复制这个 token 并报错,之后我们每次发布库都需要使用这个 token:

4.2 创建发行包
这里,我们使用 setuptools
和 twine
创建发行包,在使用 setuptools
打包时,生成的分发包是 .tar.gz
和 .whl
文件,你可以在 dist 文件夹下看到,我们创建的发行包是压缩包文件和 whl 文件:

twine
是一个用于安全地上传 Python 包到 Python 包索引(PyPi)上的工具,它使用 HTTPS 和加密传输,我们通过 pip
安装 twine
:
bash
pip install twine

接着,我们使用 setuptools
创建 .tar.gz
和 .whl
格式的分发包,需要注意是在我们要发布的库根目录下打开终端:
bash
python setup.py sdist bdist_wheel
输出如下:
bash
(MicroPython_Learn) PS F:\freakstudio-micropython-libraries\serial_servo> python setup.py sdist bdist_wheel
running sdist
running egg_info
writing serial_servo.egg-info\PKG-INFO
writing dependency_links to serial_servo.egg-info\dependency_links.txt
writing requirements to serial_servo.egg-info\requires.txt
writing top-level names to serial_servo.egg-info\top_level.txt
reading manifest file 'serial_servo.egg-info\SOURCES.txt'
adding license file 'LICENSE'
writing manifest file 'serial_servo.egg-info\SOURCES.txt'
running check
creating serial_servo-1.0.5
creating serial_servo-1.0.5\serial_servo
creating serial_servo-1.0.5\serial_servo.egg-info
copying files to serial_servo-1.0.5...
copying LICENSE -> serial_servo-1.0.5
copying README.md -> serial_servo-1.0.5
copying setup.py -> serial_servo-1.0.5
copying serial_servo\__init__.py -> serial_servo-1.0.5\serial_servo
copying serial_servo\serial_servo.py -> serial_servo-1.0.5\serial_servo
copying serial_servo.egg-info\PKG-INFO -> serial_servo-1.0.5\serial_servo.egg-info
copying serial_servo.egg-info\SOURCES.txt -> serial_servo-1.0.5\serial_servo.egg-info
copying serial_servo.egg-info\dependency_links.txt -> serial_servo-1.0.5\serial_servo.egg-info
copying serial_servo.egg-info\requires.txt -> serial_servo-1.0.5\serial_servo.egg-info
copying serial_servo.egg-info\top_level.txt -> serial_servo-1.0.5\serial_servo.egg-info
copying serial_servo.egg-info\SOURCES.txt -> serial_servo-1.0.5\serial_servo.egg-info
Writing serial_servo-1.0.5\setup.cfg
Creating tar archive
removing 'serial_servo-1.0.5' (and everything under it)
running bdist_wheel
running build
G:\miniconda\setup\envs\MicroPython_Learn\Lib\site-packages\setuptools\_distutils\cmd.py:66: SetuptoolsDeprecationWarning: setup.py install is deprecated.
!!
********************************************************************************
Please avoid running ``setup.py`` directly.
Instead, use pypa/build, pypa/installer or other
standards-based tools.
See https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html for details.
********************************************************************************
!!
self.initialize_options()
installing to build\bdist.win-amd64\wheel
running install
running install_egg_info
Copying serial_servo.egg-info to build\bdist.win-amd64\wheel\.\serial_servo-1.0.5-py3.12.egg-info
running install_scripts
creating build\bdist.win-amd64\wheel\serial_servo-1.0.5.dist-info\WHEEL
creating 'dist\serial_servo-1.0.5-py3-none-any.whl' and adding 'build\bdist.win-amd64\wheel' to it
可以看到,在 dist 文件夹下,创建了 .tar.gz
和 .whl
文件:

接着,使用如下指令上传 dist/
文件夹中的所有包:
bash
twine upload dist/*
输入之前新建的 API token:

可以看到,正在上传:

由于之前这个版本的第三方包已经发布,所以这里显示无法发布,正常情况下,会生成一个第三方库的网址,打开即可到达对应第三方库的界面,类似如下:

我们也可以选择直接下载源文件:

这里,需要注意的是,如果你要发布该第三方库新版本时,需要手动删除dist/ 文件夹中旧版本的包。
上传完成后,我们可以通过 pip 工具进行安装(对于这个库来说,无法在 PC 端运行,只能作为演示):
bash
pip install serial-servo
显示如下:

打开安装位置,可以看到:

5.参考链接
-
https://docs.micropython.org/en/latest/reference/packages.html#writing-publishing-packages
-
https://github.com/leezisheng/freakstudio-micropython-libraries/tree/main/serial_servo
-
https://github.com/micropython/micropython-lib/blob/master/.pre-commit-config.yaml
-
https://blog.csdn.net/wuShiJingZuo/article/details/135765628
