dongtu_utils 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,248 @@
1
+ Metadata-Version: 2.3
2
+ Name: dongtu_utils
3
+ Version: 0.1.0
4
+ Summary: Personal Python utility library
5
+ Author: aiden
6
+ Author-email: aiden <aidenpeng0504@gmail.com>
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+
10
+ # dongtu_utils
11
+
12
+ 个人 Python 工具库 —— 用于学习 **包发布(打包 / 构建 / 上传 PyPI)** 与 **测试编写(pytest)**。
13
+
14
+ ## 项目结构
15
+
16
+ ```
17
+ dongtu_utils/
18
+ ├── src/ # src 布局:源码统一放 src/ 下
19
+ │ └── dongtu_utils/
20
+ │ ├── __init__.py # 包入口,导出公共 API
21
+ │ ├── math.py # 数学工具函数
22
+ │ └── py.typed # 声明本包带类型标注(供 mypy / IDE 识别)
23
+ ├── tests/
24
+ │ └── test_math.py # pytest 测试
25
+ ├── pyproject.toml # 项目元数据 + 构建配置
26
+ ├── uv.lock # 依赖锁文件
27
+ └── README.md
28
+ ```
29
+
30
+ ## 常用命令
31
+
32
+ ```bash
33
+ uv sync # 安装依赖(含 dev 组)
34
+ uv run pytest # 运行测试
35
+ uv build # 构建发行包(wheel + sdist 到 dist/)
36
+ uv run --env-file .env uv publish # 发布到 PyPI(自动从 .env 读 token)
37
+ ```
38
+
39
+ > 以上命令已封装进 `Makefile`,可用 `make <target>` 调用:`make sync` / `make test` / `make build` / `make publish`。运行 `make`(或 `make help`)查看全部 target。
40
+
41
+ ---
42
+
43
+ ## 一、发包流程
44
+
45
+ ### 0. 前置:完善 pyproject.toml 元数据
46
+
47
+ `[project]` 表是包对外暴露的信息,发布前要填全:
48
+
49
+ | 字段 | 说明 |
50
+ |------|------|
51
+ | `name` | 包名,PyPI 全局唯一,决定 `pip install <name>` |
52
+ | `version` | 版本号(见下方「注意事项」,不可重复发布) |
53
+ | `description` | 一句话描述 |
54
+ | `authors` | 作者与邮箱 |
55
+ | `readme` | 指向 README.md(会渲染在 PyPI 项目页) |
56
+ | `requires-python` | 最低 Python 版本 |
57
+
58
+ 构建后端在 `[build-system]` 声明,本项目用 `uv_build`:
59
+
60
+ ```toml
61
+ [build-system]
62
+ requires = ["uv_build>=0.12.0,<0.13.0"]
63
+ build-backend = "uv_build"
64
+ ```
65
+
66
+ ### 1. 构建
67
+
68
+ ```bash
69
+ uv build
70
+ ```
71
+
72
+ 执行后在 `dist/` 生成两种产物:
73
+
74
+ ```
75
+ dist/
76
+ ├── dongtu_utils-0.1.0-py3-none-any.whl # 二进制发行包(wheel,安装快)
77
+ └── dongtu_utils-0.1.0.tar.gz # 源码发行包(sdist)
78
+ ```
79
+
80
+ > `uv build` 默认会在 `dist/` 里自动生成 `.gitignore`(内容 `*`),配合根目录 `.gitignore` 的 `dist/` 规则,构建产物不会被提交进 git。
81
+
82
+ ### 2. 本地验证产物(发布前必做)
83
+
84
+ 在隔离环境里安装刚构建的 wheel 并实际 import,确认打包内容正确:
85
+
86
+ ```bash
87
+ # --no-project 排除当前项目,--with 临时安装这个 wheel(版本号按实际文件名替换)
88
+ uv run --no-project --with dist/dongtu_utils-0.1.0-py3-none-any.whl \
89
+ python -c "from dongtu_utils import add; print(add(1, 2))"
90
+ ```
91
+
92
+ ### 3. 预览要发布的内容
93
+
94
+ ```bash
95
+ uv publish --dry-run
96
+ ```
97
+
98
+ 只列出会上传的文件,不真正上传。
99
+
100
+ ### 4. 先发到 Test PyPI 验证
101
+
102
+ ```bash
103
+ uv publish --publish-url https://test.pypi.org/legacy/ --token <TEST_PYPI_TOKEN>
104
+ ```
105
+
106
+ Test PyPI(`https://test.pypi.org`)与正式 PyPI 完全隔离,账号和 token 需单独注册。发布后用下面命令验证能否正常安装:
107
+
108
+ ```bash
109
+ pip install -i https://test.pypi.org/simple/ dongtu_utils
110
+ ```
111
+
112
+ ### 5. 使用 API Token 发布到正式 PyPI
113
+
114
+ #### ① 获取 Token
115
+
116
+ 1. 登录 <https://pypi.org> → 右上角账号 → **Account settings** → **API tokens** → **Add API token**。
117
+ 2. **Scope(作用域)** 选「限制到指定项目」(只给 upload 权限),并填项目名 `dongtu_utils`,而不是「Entire account(整个账号)」——这样 token 一旦泄露,影响范围更小。
118
+ 3. 生成后的 token 形如 `pypi-AgEIcHlwaS5vcmc...`,**只显示这一次**,当场复制保存。
119
+ 4. Test PyPI 的 token 需要到 <https://test.pypi.org> 单独注册(入口相同)。
120
+
121
+ #### ② 把 Token 传给 uv publish
122
+
123
+ `uv publish` 只认两个来源:`--token` 参数,或 `UV_PUBLISH_TOKEN` 环境变量。
124
+
125
+ 方式一:`--token` 参数(最直接,但 token 会留在 shell 历史里)
126
+
127
+ ```bash
128
+ uv publish --token "pypi-AgEIcHlwaS5vcmc..."
129
+ ```
130
+
131
+ 方式二:环境变量(推荐,不落进 shell 历史)
132
+
133
+ ```bash
134
+ export UV_PUBLISH_TOKEN="pypi-AgEIcHlwaS5vcmc..."
135
+ uv publish
136
+ ```
137
+
138
+ #### ③ 用 .env 存 Token(本项目已配置好)
139
+
140
+ `uv publish` 本身**不会**自动读取 `.env`,但可以借 `uv run --env-file` 先把 `.env` 加载进环境再执行发布,一条命令搞定:
141
+
142
+ ```bash
143
+ uv run --env-file .env uv publish
144
+ ```
145
+
146
+ `.env` 里变量名要叫 `UV_PUBLISH_TOKEN`(本项目已配好):
147
+
148
+ ```
149
+ # .env
150
+ UV_PUBLISH_TOKEN=pypi-AgEIcHlwaS5vcmc...
151
+ ```
152
+
153
+ 不用 `uv run` 时的等价手动写法:
154
+
155
+ ```bash
156
+ set -a; source .env; set +a # 把 .env 里的变量加载进当前 shell
157
+ uv publish
158
+ ```
159
+
160
+ > 安全提醒:`.env` 已被 `.gitignore` 忽略,但务必确认它**从未被提交进 git**。若 token 曾泄露(截图、贴进聊天、误提交仓库),立刻去 PyPI 后台 **Revoke** 作废并重新生成。
161
+
162
+ 发布成功后,任何人都可以 `pip install dongtu_utils`。
163
+
164
+ ---
165
+
166
+ ## 二、发包注意事项
167
+
168
+ 1. **版本号不可覆盖** —— PyPI 不允许删除或覆盖已发布的版本,同一版本号只能上传一次。每次发布前必须递增 `version`(如 `0.1.0 → 0.1.1`)。本地反复测试时用预发布版本号:`0.1.0.dev1`、`0.1.0rc1`。
169
+
170
+ 2. **用 API Token,不要用密码** —— PyPI 已不支持用户名 + 密码上传,只支持 API token(或受信任发布者)。Token 形如 `pypi-...`,在 <https://pypi.org/manage/account/token/> 生成,**只显示一次,务必当场保存**。不要写进代码或提交到 git。具体用法见上方「使用 API Token 发布」。
171
+
172
+ 3. **先测 Test PyPI** —— 正式发布前先走一遍 Test PyPI,验证打包、上传、安装全链路都没问题。
173
+
174
+ 4. **别提交构建产物与虚拟环境** —— `dist/`、`.venv/`、`__pycache__/` 都应被 `.gitignore` 忽略(本项目根 `.gitignore` 已覆盖)。
175
+
176
+ 5. **`py.typed` 要随包发布** —— 本项目带 `py.typed`(表示有类型标注),在 src 布局下会被自动打进包;后续若新增无标注的子模块,其类型信息会连带缺失。
177
+
178
+ 6. **构建与发布分离** —— `uv build` 负责生成产物,`uv publish` 负责上传;上传前先 `uv build` 保证 `dist/` 是最新的(`uv publish` 默认上传 `dist/*`)。
179
+
180
+ ---
181
+
182
+ ## 三、测试编写
183
+
184
+ ### 1. 添加 pytest
185
+
186
+ ```bash
187
+ uv add --dev pytest
188
+ ```
189
+
190
+ pytest 会被加进 `[dependency-groups].dev`:
191
+
192
+ ```toml
193
+ [dependency-groups]
194
+ dev = [
195
+ "build>=1.6.1",
196
+ "pytest>=9.1.1",
197
+ ]
198
+ ```
199
+
200
+ ### 2. 写测试文件
201
+
202
+ 测试放 `tests/` 目录,文件名用 `test_*.py`。src 布局下,测试里**直接从包名导入**(uv 会以 editable 方式把项目装进环境):
203
+
204
+ ```python
205
+ # tests/test_math.py
206
+ import pytest
207
+
208
+ from dongtu_utils import add
209
+
210
+
211
+ def test_add():
212
+ assert add(1, 2) == 3
213
+
214
+
215
+ @pytest.mark.parametrize(("a", "b", "expected"), [(1, 2, 3), (0, 0, 0), (-1, 1, 0)])
216
+ def test_add_parametrized(a, b, expected):
217
+ assert add(a, b) == expected
218
+
219
+
220
+ def test_add_raises():
221
+ with pytest.raises(TypeError):
222
+ add("1", 2)
223
+ ```
224
+
225
+ ### 3. 运行
226
+
227
+ ```bash
228
+ uv run pytest # 跑全部测试
229
+ uv run pytest -v # 详细输出(显示每个用例名)
230
+ uv run pytest tests/test_math.py # 只跑某个文件
231
+ uv run pytest -k add # 按名称过滤(跑名字含 "add" 的用例)
232
+ ```
233
+
234
+ ### 4. 常用写法速查
235
+
236
+ | 需求 | 写法 |
237
+ |------|------|
238
+ | 参数化(同一测试跑多组输入) | `@pytest.mark.parametrize("a,b,expected", [...])` |
239
+ | 共享前置数据 / 资源 | `@pytest.fixture` |
240
+ | 断言抛异常 | `with pytest.raises(XxxError):` |
241
+ | 浮点近似比较 | `assert result == pytest.approx(3.14)` |
242
+ | 跳过某测试 | `@pytest.mark.skip(reason="...")` |
243
+
244
+ ### 5. 命名约定
245
+
246
+ - 测试文件:`test_*.py`
247
+ - 测试函数:`test_*`
248
+ - 共享 fixture:`tests/conftest.py`(pytest 自动发现,无需手动 import)
@@ -0,0 +1,239 @@
1
+ # dongtu_utils
2
+
3
+ 个人 Python 工具库 —— 用于学习 **包发布(打包 / 构建 / 上传 PyPI)** 与 **测试编写(pytest)**。
4
+
5
+ ## 项目结构
6
+
7
+ ```
8
+ dongtu_utils/
9
+ ├── src/ # src 布局:源码统一放 src/ 下
10
+ │ └── dongtu_utils/
11
+ │ ├── __init__.py # 包入口,导出公共 API
12
+ │ ├── math.py # 数学工具函数
13
+ │ └── py.typed # 声明本包带类型标注(供 mypy / IDE 识别)
14
+ ├── tests/
15
+ │ └── test_math.py # pytest 测试
16
+ ├── pyproject.toml # 项目元数据 + 构建配置
17
+ ├── uv.lock # 依赖锁文件
18
+ └── README.md
19
+ ```
20
+
21
+ ## 常用命令
22
+
23
+ ```bash
24
+ uv sync # 安装依赖(含 dev 组)
25
+ uv run pytest # 运行测试
26
+ uv build # 构建发行包(wheel + sdist 到 dist/)
27
+ uv run --env-file .env uv publish # 发布到 PyPI(自动从 .env 读 token)
28
+ ```
29
+
30
+ > 以上命令已封装进 `Makefile`,可用 `make <target>` 调用:`make sync` / `make test` / `make build` / `make publish`。运行 `make`(或 `make help`)查看全部 target。
31
+
32
+ ---
33
+
34
+ ## 一、发包流程
35
+
36
+ ### 0. 前置:完善 pyproject.toml 元数据
37
+
38
+ `[project]` 表是包对外暴露的信息,发布前要填全:
39
+
40
+ | 字段 | 说明 |
41
+ |------|------|
42
+ | `name` | 包名,PyPI 全局唯一,决定 `pip install <name>` |
43
+ | `version` | 版本号(见下方「注意事项」,不可重复发布) |
44
+ | `description` | 一句话描述 |
45
+ | `authors` | 作者与邮箱 |
46
+ | `readme` | 指向 README.md(会渲染在 PyPI 项目页) |
47
+ | `requires-python` | 最低 Python 版本 |
48
+
49
+ 构建后端在 `[build-system]` 声明,本项目用 `uv_build`:
50
+
51
+ ```toml
52
+ [build-system]
53
+ requires = ["uv_build>=0.12.0,<0.13.0"]
54
+ build-backend = "uv_build"
55
+ ```
56
+
57
+ ### 1. 构建
58
+
59
+ ```bash
60
+ uv build
61
+ ```
62
+
63
+ 执行后在 `dist/` 生成两种产物:
64
+
65
+ ```
66
+ dist/
67
+ ├── dongtu_utils-0.1.0-py3-none-any.whl # 二进制发行包(wheel,安装快)
68
+ └── dongtu_utils-0.1.0.tar.gz # 源码发行包(sdist)
69
+ ```
70
+
71
+ > `uv build` 默认会在 `dist/` 里自动生成 `.gitignore`(内容 `*`),配合根目录 `.gitignore` 的 `dist/` 规则,构建产物不会被提交进 git。
72
+
73
+ ### 2. 本地验证产物(发布前必做)
74
+
75
+ 在隔离环境里安装刚构建的 wheel 并实际 import,确认打包内容正确:
76
+
77
+ ```bash
78
+ # --no-project 排除当前项目,--with 临时安装这个 wheel(版本号按实际文件名替换)
79
+ uv run --no-project --with dist/dongtu_utils-0.1.0-py3-none-any.whl \
80
+ python -c "from dongtu_utils import add; print(add(1, 2))"
81
+ ```
82
+
83
+ ### 3. 预览要发布的内容
84
+
85
+ ```bash
86
+ uv publish --dry-run
87
+ ```
88
+
89
+ 只列出会上传的文件,不真正上传。
90
+
91
+ ### 4. 先发到 Test PyPI 验证
92
+
93
+ ```bash
94
+ uv publish --publish-url https://test.pypi.org/legacy/ --token <TEST_PYPI_TOKEN>
95
+ ```
96
+
97
+ Test PyPI(`https://test.pypi.org`)与正式 PyPI 完全隔离,账号和 token 需单独注册。发布后用下面命令验证能否正常安装:
98
+
99
+ ```bash
100
+ pip install -i https://test.pypi.org/simple/ dongtu_utils
101
+ ```
102
+
103
+ ### 5. 使用 API Token 发布到正式 PyPI
104
+
105
+ #### ① 获取 Token
106
+
107
+ 1. 登录 <https://pypi.org> → 右上角账号 → **Account settings** → **API tokens** → **Add API token**。
108
+ 2. **Scope(作用域)** 选「限制到指定项目」(只给 upload 权限),并填项目名 `dongtu_utils`,而不是「Entire account(整个账号)」——这样 token 一旦泄露,影响范围更小。
109
+ 3. 生成后的 token 形如 `pypi-AgEIcHlwaS5vcmc...`,**只显示这一次**,当场复制保存。
110
+ 4. Test PyPI 的 token 需要到 <https://test.pypi.org> 单独注册(入口相同)。
111
+
112
+ #### ② 把 Token 传给 uv publish
113
+
114
+ `uv publish` 只认两个来源:`--token` 参数,或 `UV_PUBLISH_TOKEN` 环境变量。
115
+
116
+ 方式一:`--token` 参数(最直接,但 token 会留在 shell 历史里)
117
+
118
+ ```bash
119
+ uv publish --token "pypi-AgEIcHlwaS5vcmc..."
120
+ ```
121
+
122
+ 方式二:环境变量(推荐,不落进 shell 历史)
123
+
124
+ ```bash
125
+ export UV_PUBLISH_TOKEN="pypi-AgEIcHlwaS5vcmc..."
126
+ uv publish
127
+ ```
128
+
129
+ #### ③ 用 .env 存 Token(本项目已配置好)
130
+
131
+ `uv publish` 本身**不会**自动读取 `.env`,但可以借 `uv run --env-file` 先把 `.env` 加载进环境再执行发布,一条命令搞定:
132
+
133
+ ```bash
134
+ uv run --env-file .env uv publish
135
+ ```
136
+
137
+ `.env` 里变量名要叫 `UV_PUBLISH_TOKEN`(本项目已配好):
138
+
139
+ ```
140
+ # .env
141
+ UV_PUBLISH_TOKEN=pypi-AgEIcHlwaS5vcmc...
142
+ ```
143
+
144
+ 不用 `uv run` 时的等价手动写法:
145
+
146
+ ```bash
147
+ set -a; source .env; set +a # 把 .env 里的变量加载进当前 shell
148
+ uv publish
149
+ ```
150
+
151
+ > 安全提醒:`.env` 已被 `.gitignore` 忽略,但务必确认它**从未被提交进 git**。若 token 曾泄露(截图、贴进聊天、误提交仓库),立刻去 PyPI 后台 **Revoke** 作废并重新生成。
152
+
153
+ 发布成功后,任何人都可以 `pip install dongtu_utils`。
154
+
155
+ ---
156
+
157
+ ## 二、发包注意事项
158
+
159
+ 1. **版本号不可覆盖** —— PyPI 不允许删除或覆盖已发布的版本,同一版本号只能上传一次。每次发布前必须递增 `version`(如 `0.1.0 → 0.1.1`)。本地反复测试时用预发布版本号:`0.1.0.dev1`、`0.1.0rc1`。
160
+
161
+ 2. **用 API Token,不要用密码** —— PyPI 已不支持用户名 + 密码上传,只支持 API token(或受信任发布者)。Token 形如 `pypi-...`,在 <https://pypi.org/manage/account/token/> 生成,**只显示一次,务必当场保存**。不要写进代码或提交到 git。具体用法见上方「使用 API Token 发布」。
162
+
163
+ 3. **先测 Test PyPI** —— 正式发布前先走一遍 Test PyPI,验证打包、上传、安装全链路都没问题。
164
+
165
+ 4. **别提交构建产物与虚拟环境** —— `dist/`、`.venv/`、`__pycache__/` 都应被 `.gitignore` 忽略(本项目根 `.gitignore` 已覆盖)。
166
+
167
+ 5. **`py.typed` 要随包发布** —— 本项目带 `py.typed`(表示有类型标注),在 src 布局下会被自动打进包;后续若新增无标注的子模块,其类型信息会连带缺失。
168
+
169
+ 6. **构建与发布分离** —— `uv build` 负责生成产物,`uv publish` 负责上传;上传前先 `uv build` 保证 `dist/` 是最新的(`uv publish` 默认上传 `dist/*`)。
170
+
171
+ ---
172
+
173
+ ## 三、测试编写
174
+
175
+ ### 1. 添加 pytest
176
+
177
+ ```bash
178
+ uv add --dev pytest
179
+ ```
180
+
181
+ pytest 会被加进 `[dependency-groups].dev`:
182
+
183
+ ```toml
184
+ [dependency-groups]
185
+ dev = [
186
+ "build>=1.6.1",
187
+ "pytest>=9.1.1",
188
+ ]
189
+ ```
190
+
191
+ ### 2. 写测试文件
192
+
193
+ 测试放 `tests/` 目录,文件名用 `test_*.py`。src 布局下,测试里**直接从包名导入**(uv 会以 editable 方式把项目装进环境):
194
+
195
+ ```python
196
+ # tests/test_math.py
197
+ import pytest
198
+
199
+ from dongtu_utils import add
200
+
201
+
202
+ def test_add():
203
+ assert add(1, 2) == 3
204
+
205
+
206
+ @pytest.mark.parametrize(("a", "b", "expected"), [(1, 2, 3), (0, 0, 0), (-1, 1, 0)])
207
+ def test_add_parametrized(a, b, expected):
208
+ assert add(a, b) == expected
209
+
210
+
211
+ def test_add_raises():
212
+ with pytest.raises(TypeError):
213
+ add("1", 2)
214
+ ```
215
+
216
+ ### 3. 运行
217
+
218
+ ```bash
219
+ uv run pytest # 跑全部测试
220
+ uv run pytest -v # 详细输出(显示每个用例名)
221
+ uv run pytest tests/test_math.py # 只跑某个文件
222
+ uv run pytest -k add # 按名称过滤(跑名字含 "add" 的用例)
223
+ ```
224
+
225
+ ### 4. 常用写法速查
226
+
227
+ | 需求 | 写法 |
228
+ |------|------|
229
+ | 参数化(同一测试跑多组输入) | `@pytest.mark.parametrize("a,b,expected", [...])` |
230
+ | 共享前置数据 / 资源 | `@pytest.fixture` |
231
+ | 断言抛异常 | `with pytest.raises(XxxError):` |
232
+ | 浮点近似比较 | `assert result == pytest.approx(3.14)` |
233
+ | 跳过某测试 | `@pytest.mark.skip(reason="...")` |
234
+
235
+ ### 5. 命名约定
236
+
237
+ - 测试文件:`test_*.py`
238
+ - 测试函数:`test_*`
239
+ - 共享 fixture:`tests/conftest.py`(pytest 自动发现,无需手动 import)
@@ -0,0 +1,21 @@
1
+ [project]
2
+ name = "dongtu_utils"
3
+ version = "0.1.0"
4
+ description = "Personal Python utility library"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ dependencies = []
8
+
9
+ [[project.authors]]
10
+ name = "aiden"
11
+ email = "aidenpeng0504@gmail.com"
12
+
13
+ [build-system]
14
+ requires = ["uv_build>=0.12.0,<0.13.0"]
15
+ build-backend = "uv_build"
16
+
17
+ [dependency-groups]
18
+ dev = [
19
+ "build>=1.6.1",
20
+ "pytest>=9.1.1",
21
+ ]
@@ -0,0 +1,20 @@
1
+ [project]
2
+ name = "dongtu_utils"
3
+ version = "0.1.0"
4
+ description = "Personal Python utility library"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "aiden", email = "aidenpeng0504@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.11"
10
+ dependencies = []
11
+
12
+ [build-system]
13
+ requires = ["uv_build>=0.12.0,<0.13.0"]
14
+ build-backend = "uv_build"
15
+
16
+ [dependency-groups]
17
+ dev = [
18
+ "build>=1.6.1",
19
+ "pytest>=9.1.1",
20
+ ]
@@ -0,0 +1,3 @@
1
+ from .math import add
2
+
3
+ __all__ = ["add"]
@@ -0,0 +1,2 @@
1
+ def add(a: int, b: int) -> int:
2
+ return a + b
File without changes