chen-todo 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,10 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 你的名字
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,76 @@
1
+ Metadata-Version: 2.4
2
+ Name: chen-todo
3
+ Version: 0.1.0
4
+ Summary: 一个简单友好的命令行待办清单工具
5
+ Author: chen
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: cli,productivity,task,todo,待办
9
+ Classifier: Environment :: Console
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Utilities
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+
16
+ # todo-cli
17
+
18
+ 一个简单友好的命令行待办清单工具。用一条命令管理你的待办事项,数据保存在本地,随时可用。
19
+
20
+ ## ✨ 功能
21
+
22
+ - ➕ 添加任务
23
+ - 📋 查看任务(编号连续,删除后自动重排)
24
+ - ✅ 标记完成
25
+ - 🗑️ 删除任务
26
+ - 🧹 一键清除所有已完成
27
+ - 数据存在本地 JSON,中文友好,无需联网
28
+
29
+ ## 📦 安装
30
+
31
+ ```bash
32
+ pip install todo-cli
33
+ ```
34
+
35
+ ## 🚀 使用
36
+
37
+ ```bash
38
+ todo add 学会发布到PyPI
39
+ todo add 买 一盒 牛奶
40
+ todo list
41
+ todo done 1
42
+ todo remove 2
43
+ todo clear
44
+ todo --version
45
+ todo --help
46
+ ```
47
+
48
+ `todo list` 的输出长这样:
49
+
50
+ ```
51
+ [✓] 1 买牛奶
52
+ [ ] 2 学会发布到PyPI
53
+
54
+ 共 2 个任务,1 个已完成
55
+ ```
56
+
57
+ ## 💾 数据存在哪?
58
+
59
+ 所有任务保存在你用户主目录下的 `.todo/tasks.json`:
60
+
61
+ - Windows:`C:\Users\你的用户名\.todo\tasks.json`
62
+ - macOS / Linux:`~/.todo/tasks.json`
63
+
64
+ ## 🛠️ 参与开发
65
+
66
+ ```bash
67
+ git clone <你的仓库地址>
68
+ cd todo-cli
69
+ uv sync
70
+ uv run pytest
71
+ uv run todo list
72
+ ```
73
+
74
+ ## 📄 License
75
+
76
+ [MIT](LICENSE)
@@ -0,0 +1,61 @@
1
+ # todo-cli
2
+
3
+ 一个简单友好的命令行待办清单工具。用一条命令管理你的待办事项,数据保存在本地,随时可用。
4
+
5
+ ## ✨ 功能
6
+
7
+ - ➕ 添加任务
8
+ - 📋 查看任务(编号连续,删除后自动重排)
9
+ - ✅ 标记完成
10
+ - 🗑️ 删除任务
11
+ - 🧹 一键清除所有已完成
12
+ - 数据存在本地 JSON,中文友好,无需联网
13
+
14
+ ## 📦 安装
15
+
16
+ ```bash
17
+ pip install todo-cli
18
+ ```
19
+
20
+ ## 🚀 使用
21
+
22
+ ```bash
23
+ todo add 学会发布到PyPI
24
+ todo add 买 一盒 牛奶
25
+ todo list
26
+ todo done 1
27
+ todo remove 2
28
+ todo clear
29
+ todo --version
30
+ todo --help
31
+ ```
32
+
33
+ `todo list` 的输出长这样:
34
+
35
+ ```
36
+ [✓] 1 买牛奶
37
+ [ ] 2 学会发布到PyPI
38
+
39
+ 共 2 个任务,1 个已完成
40
+ ```
41
+
42
+ ## 💾 数据存在哪?
43
+
44
+ 所有任务保存在你用户主目录下的 `.todo/tasks.json`:
45
+
46
+ - Windows:`C:\Users\你的用户名\.todo\tasks.json`
47
+ - macOS / Linux:`~/.todo/tasks.json`
48
+
49
+ ## 🛠️ 参与开发
50
+
51
+ ```bash
52
+ git clone <你的仓库地址>
53
+ cd todo-cli
54
+ uv sync
55
+ uv run pytest
56
+ uv run todo list
57
+ ```
58
+
59
+ ## 📄 License
60
+
61
+ [MIT](LICENSE)
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "chen-todo"
7
+ version = "0.1.0"
8
+ description = "一个简单友好的命令行待办清单工具"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "chen" }]
14
+ keywords = ["todo", "cli", "task", "productivity", "待办"]
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Operating System :: OS Independent",
18
+ "Environment :: Console",
19
+ "Topic :: Utilities",
20
+ ]
21
+ dependencies = []
22
+
23
+ # ✨ 这一段是"魔法":安装后会生成一个叫 todo 的命令,
24
+ # 运行它就等于执行 todo_cli/cli.py 里的 main() 函数。
25
+ [project.scripts]
26
+ todo = "todo_cli.cli:main"
27
+
28
+ # 告诉打包工具:我们的代码在 src/todo_cli 目录里
29
+ [tool.hatch.build.targets.wheel]
30
+ packages = ["src/todo_cli"]
31
+
32
+ [dependency-groups]
33
+ dev = [
34
+ "pytest>=8.4.2",
35
+ ]
@@ -0,0 +1,3 @@
1
+ """todo_cli —— 一个简单友好的命令行待办清单工具。"""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,124 @@
1
+ """命令行界面:把「存取」(storage) 和「逻辑」(core) 拼起来,
2
+ 并用 argparse 解析 add / list / done / remove / clear 等子命令。
3
+
4
+ pyproject.toml 里配的 todo 命令,最终执行的就是这里的 main()。
5
+ """
6
+
7
+ import argparse
8
+ import sys
9
+
10
+ from todo_cli import __version__, core, storage
11
+
12
+
13
+ # ---- storage 和 core 之间的「翻译」:dict ↔ Task ----
14
+
15
+ def _load():
16
+ return [core.Task.from_dict(d) for d in storage.load_tasks()]
17
+
18
+
19
+ def _save(tasks):
20
+ storage.save_tasks([task.to_dict() for task in tasks])
21
+
22
+
23
+ # ---- 每个子命令对应一个处理函数 ----
24
+
25
+ def cmd_add(args):
26
+ title = " ".join(args.title)
27
+ tasks = _load()
28
+ task = core.add_task(tasks, title)
29
+ _save(tasks)
30
+ print(f"已添加 #{len(tasks)}:{task.title}") # 新任务在末尾,编号 = 当前总数
31
+
32
+
33
+ def cmd_list(args):
34
+ tasks = _load()
35
+ if not tasks:
36
+ print('还没有任务。用 todo add "买牛奶" 添加一个吧!')
37
+ return
38
+ width = len(str(len(tasks))) # 最大编号有几位数,用来对齐
39
+ done_count = 0
40
+ for number, task in enumerate(tasks, start=1): # 编号 = 位置,永远连续
41
+ box = "✓" if task.done else " "
42
+ if task.done:
43
+ done_count += 1
44
+ print(f"[{box}] {number:>{width}} {task.title}")
45
+ print()
46
+ print(f"共 {len(tasks)} 个任务,{done_count} 个已完成")
47
+
48
+
49
+ def cmd_done(args):
50
+ tasks = _load()
51
+ try:
52
+ task = core.mark_done(tasks, args.number)
53
+ except core.TaskNotFoundError as error:
54
+ print(error, file=sys.stderr)
55
+ sys.exit(1)
56
+ _save(tasks)
57
+ print(f"已完成 #{args.number}:{task.title}")
58
+
59
+
60
+ def cmd_remove(args):
61
+ tasks = _load()
62
+ try:
63
+ task = core.remove_task(tasks, args.number)
64
+ except core.TaskNotFoundError as error:
65
+ print(error, file=sys.stderr)
66
+ sys.exit(1)
67
+ _save(tasks)
68
+ print(f"已删除:{task.title}") # 删除后编号会重排,这里就不提编号了
69
+
70
+
71
+ def cmd_clear(args):
72
+ tasks = _load()
73
+ count = core.clear_done(tasks)
74
+ _save(tasks)
75
+ print(f"已清除 {count} 个已完成的任务")
76
+
77
+
78
+ # ---- 组装命令行解析器 ----
79
+
80
+ def main():
81
+ parser = argparse.ArgumentParser(
82
+ prog="todo",
83
+ description="一个简单友好的命令行待办清单工具。",
84
+ )
85
+ parser.add_argument(
86
+ "--version", action="version", version=f"%(prog)s {__version__}"
87
+ )
88
+ subparsers = parser.add_subparsers(dest="command")
89
+
90
+ # todo add 买牛奶
91
+ p_add = subparsers.add_parser("add", help="添加一个新任务")
92
+ p_add.add_argument("title", nargs="+", help="任务内容(可以不加引号)")
93
+ p_add.set_defaults(func=cmd_add)
94
+
95
+ # todo list
96
+ p_list = subparsers.add_parser("list", help="列出所有任务")
97
+ p_list.set_defaults(func=cmd_list)
98
+
99
+ # todo done 1
100
+ p_done = subparsers.add_parser("done", help="把某个任务标记为已完成")
101
+ p_done.add_argument("number", type=int, help="任务编号(就是 list 里显示的数字)")
102
+ p_done.set_defaults(func=cmd_done)
103
+
104
+ # todo remove 1
105
+ p_remove = subparsers.add_parser("remove", help="删除某个任务")
106
+ p_remove.add_argument("number", type=int, help="任务编号(就是 list 里显示的数字)")
107
+ p_remove.set_defaults(func=cmd_remove)
108
+
109
+ # todo clear
110
+ p_clear = subparsers.add_parser("clear", help="清除所有已完成的任务")
111
+ p_clear.set_defaults(func=cmd_clear)
112
+
113
+ args = parser.parse_args()
114
+
115
+ # 用户只敲了 todo、没给子命令 → 打印帮助
116
+ if not hasattr(args, "func"):
117
+ parser.print_help()
118
+ return
119
+
120
+ args.func(args)
121
+
122
+
123
+ if __name__ == "__main__":
124
+ main()
@@ -0,0 +1,83 @@
1
+ """核心逻辑层:定义「任务」的结构,以及增、删、改、查的纯逻辑。
2
+
3
+ 这一层只跟「内存里的任务列表」打交道,不碰文件、也不管命令行。
4
+
5
+ 关于「编号」:我们不给任务存固定的 id,而是用它在清单里的「位置」当编号
6
+ (第 1 个就是 1、第 2 个就是 2……)。这样一删任务,后面的自动往前补位,
7
+ 编号永远是连续的 1、2、3——和 ufw 等工具的行为一致。
8
+ """
9
+
10
+ from dataclasses import dataclass
11
+ from datetime import datetime
12
+
13
+
14
+ class TaskNotFoundError(Exception):
15
+ """给的编号在当前清单里不存在(越界)时抛出。"""
16
+
17
+ def __init__(self, number):
18
+ self.number = number
19
+ super().__init__(f"找不到编号为 {number} 的任务")
20
+
21
+
22
+ @dataclass
23
+ class Task:
24
+ """一个待办任务。注意:没有 id 字段,编号靠「位置」决定。"""
25
+
26
+ title: str
27
+ done: bool = False
28
+ created_at: str = ""
29
+
30
+ @classmethod
31
+ def from_dict(cls, data):
32
+ # 用 .get() 给默认值:老数据即使还带着多余的 "id" 字段也照样能读,
33
+ # 缺 created_at 也不会崩。
34
+ return cls(
35
+ title=data["title"],
36
+ done=data.get("done", False),
37
+ created_at=data.get("created_at", ""),
38
+ )
39
+
40
+ def to_dict(self):
41
+ return {
42
+ "title": self.title,
43
+ "done": self.done,
44
+ "created_at": self.created_at,
45
+ }
46
+
47
+
48
+ def _task_at(tasks, number):
49
+ """按「从 1 开始的编号」取任务;越界就抛 TaskNotFoundError。"""
50
+ if number < 1 or number > len(tasks):
51
+ raise TaskNotFoundError(number)
52
+ return tasks[number - 1]
53
+
54
+
55
+ def add_task(tasks, title):
56
+ """新建一个任务,追加到清单末尾,并返回它。"""
57
+ task = Task(
58
+ title=title,
59
+ done=False,
60
+ created_at=datetime.now().isoformat(timespec="seconds"),
61
+ )
62
+ tasks.append(task)
63
+ return task
64
+
65
+
66
+ def mark_done(tasks, number):
67
+ """把第 number 个任务标记为已完成,返回它;编号非法就抛异常。"""
68
+ task = _task_at(tasks, number)
69
+ task.done = True
70
+ return task
71
+
72
+
73
+ def remove_task(tasks, number):
74
+ """删除第 number 个任务,返回被删掉的那个;编号非法就抛异常。"""
75
+ _task_at(tasks, number) # 先校验编号(越界会抛异常)
76
+ return tasks.pop(number - 1) # 按位置删除,后面的自动补位
77
+
78
+
79
+ def clear_done(tasks):
80
+ """删除所有已完成的任务,返回删掉的数量。"""
81
+ before = len(tasks)
82
+ tasks[:] = [task for task in tasks if not task.done] # 原地只保留未完成的
83
+ return before - len(tasks)
@@ -0,0 +1,41 @@
1
+ """数据存储层:负责把任务列表读写到本地 JSON 文件。
2
+
3
+ 这一层只关心「数据怎么存到硬盘、怎么读回来」,
4
+ 完全不关心任务的业务逻辑(增删改查是 core.py 的事)。
5
+ 把「存储」和「逻辑」分开的好处:将来想换存储方式
6
+ (比如换成数据库),只改这一个文件就行,别处都不用动。
7
+ """
8
+
9
+ import json
10
+ from pathlib import Path
11
+
12
+ # 数据文件放在「用户主目录」下的 .todo 文件夹里。
13
+ # Path.home() 在你的电脑上是 C:\Users\chen,
14
+ # 所以数据文件就是 C:\Users\chen\.todo\tasks.json
15
+ DATA_DIR = Path.home() / ".todo"
16
+ DATA_FILE = DATA_DIR / "tasks.json"
17
+
18
+
19
+ def load_tasks(path=DATA_FILE):
20
+ """读出所有任务,返回一个列表(里面每一项是一个 dict)。
21
+
22
+ - 文件还不存在(第一次用) → 返回空列表 []
23
+ - 文件是空的、或内容坏了 → 也返回空列表,保证程序不会崩
24
+ """
25
+ if not path.exists():
26
+ return []
27
+ try:
28
+ text = path.read_text(encoding="utf-8")
29
+ return json.loads(text)
30
+ except (json.JSONDecodeError, ValueError):
31
+ return []
32
+
33
+
34
+ def save_tasks(tasks, path=DATA_FILE):
35
+ """把任务列表写回文件。"""
36
+ # 确保 .todo 文件夹存在,不存在就自动创建(parents=True 连父目录一起建)
37
+ path.parent.mkdir(parents=True, exist_ok=True)
38
+ # ensure_ascii=False → 中文原样保存,不会变成 买 这种乱码
39
+ # indent=2 → 存成带缩进的漂亮格式,方便打开来看
40
+ text = json.dumps(tasks, ensure_ascii=False, indent=2)
41
+ path.write_text(text, encoding="utf-8")
@@ -0,0 +1,66 @@
1
+ """core.py 的测试:纯逻辑,连文件都不用碰。"""
2
+
3
+ import pytest
4
+
5
+ from todo_cli import core
6
+
7
+
8
+ def test_add_appends_to_end():
9
+ tasks = []
10
+ core.add_task(tasks, "买牛奶")
11
+ core.add_task(tasks, "学 Python")
12
+ assert [t.title for t in tasks] == ["买牛奶", "学 Python"]
13
+ assert tasks[0].done is False
14
+
15
+
16
+ def test_remove_renumbers_by_position():
17
+ tasks = []
18
+ core.add_task(tasks, "A")
19
+ core.add_task(tasks, "B")
20
+ core.add_task(tasks, "C")
21
+ core.remove_task(tasks, 2) # 删掉第 2 个(B)
22
+ # 剩下的按位置就是 1=A、2=C —— 编号自动顺序重排,这正是我们想要的
23
+ assert [t.title for t in tasks] == ["A", "C"]
24
+
25
+
26
+ def test_mark_done_by_number():
27
+ tasks = []
28
+ core.add_task(tasks, "买牛奶")
29
+ core.add_task(tasks, "学 Python")
30
+ core.mark_done(tasks, 2) # 完成第 2 个
31
+ assert tasks[1].done is True
32
+ assert tasks[0].done is False
33
+
34
+
35
+ def test_remove_returns_removed_task():
36
+ tasks = []
37
+ core.add_task(tasks, "买牛奶")
38
+ removed = core.remove_task(tasks, 1)
39
+ assert removed.title == "买牛奶"
40
+ assert tasks == []
41
+
42
+
43
+ def test_bad_number_raises():
44
+ tasks = []
45
+ core.add_task(tasks, "只有一个任务")
46
+ with pytest.raises(core.TaskNotFoundError):
47
+ core.mark_done(tasks, 99) # 太大,越界
48
+ with pytest.raises(core.TaskNotFoundError):
49
+ core.remove_task(tasks, 0) # 编号从 1 开始,0 也非法
50
+
51
+
52
+ def test_task_dict_roundtrip():
53
+ task = core.Task(title="买牛奶", done=True, created_at="2026-01-01T00:00:00")
54
+ restored = core.Task.from_dict(task.to_dict())
55
+ assert restored == task # dataclass 自动生成的 __eq__ 让这行成立
56
+
57
+
58
+ def test_clear_done_removes_only_completed():
59
+ tasks = []
60
+ core.add_task(tasks, "A")
61
+ core.add_task(tasks, "B")
62
+ core.add_task(tasks, "C")
63
+ core.mark_done(tasks, 2) # 完成 B
64
+ removed = core.clear_done(tasks)
65
+ assert removed == 1
66
+ assert [t.title for t in tasks] == ["A", "C"] # 只删已完成,其余不动
@@ -0,0 +1,28 @@
1
+ """storage.py 的测试:用 pytest 的临时目录,绝不碰你真正的 ~/.todo 数据。"""
2
+
3
+ from todo_cli import storage
4
+
5
+
6
+ def test_save_and_load_roundtrip(tmp_path):
7
+ path = tmp_path / "tasks.json"
8
+ data = [{"title": "买牛奶", "done": False, "created_at": ""}]
9
+ storage.save_tasks(data, path=path)
10
+ assert storage.load_tasks(path=path) == data
11
+
12
+
13
+ def test_load_missing_file_returns_empty(tmp_path):
14
+ missing = tmp_path / "nope.json"
15
+ assert storage.load_tasks(path=missing) == []
16
+
17
+
18
+ def test_load_corrupt_file_returns_empty(tmp_path):
19
+ bad = tmp_path / "bad.json"
20
+ bad.write_text("这不是合法 JSON {{{", encoding="utf-8")
21
+ assert storage.load_tasks(path=bad) == []
22
+
23
+
24
+ def test_chinese_saved_as_readable_text(tmp_path):
25
+ path = tmp_path / "tasks.json"
26
+ storage.save_tasks([{"title": "买牛奶", "done": False}], path=path)
27
+ text = path.read_text(encoding="utf-8")
28
+ assert "买牛奶" in text # 证明 ensure_ascii=False 生效,中文没被转义
@@ -0,0 +1,200 @@
1
+ version = 1
2
+ revision = 2
3
+ requires-python = ">=3.9"
4
+ resolution-markers = [
5
+ "python_full_version >= '3.10'",
6
+ "python_full_version < '3.10'",
7
+ ]
8
+
9
+ [[package]]
10
+ name = "colorama"
11
+ version = "0.4.6"
12
+ source = { registry = "https://pypi.org/simple" }
13
+ sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
14
+ wheels = [
15
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
16
+ ]
17
+
18
+ [[package]]
19
+ name = "exceptiongroup"
20
+ version = "1.3.1"
21
+ source = { registry = "https://pypi.org/simple" }
22
+ dependencies = [
23
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
24
+ ]
25
+ sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
26
+ wheels = [
27
+ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
28
+ ]
29
+
30
+ [[package]]
31
+ name = "iniconfig"
32
+ version = "2.1.0"
33
+ source = { registry = "https://pypi.org/simple" }
34
+ resolution-markers = [
35
+ "python_full_version < '3.10'",
36
+ ]
37
+ sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" }
38
+ wheels = [
39
+ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
40
+ ]
41
+
42
+ [[package]]
43
+ name = "iniconfig"
44
+ version = "2.3.0"
45
+ source = { registry = "https://pypi.org/simple" }
46
+ resolution-markers = [
47
+ "python_full_version >= '3.10'",
48
+ ]
49
+ sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
50
+ wheels = [
51
+ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
52
+ ]
53
+
54
+ [[package]]
55
+ name = "packaging"
56
+ version = "26.2"
57
+ source = { registry = "https://pypi.org/simple" }
58
+ sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" }
59
+ wheels = [
60
+ { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" },
61
+ ]
62
+
63
+ [[package]]
64
+ name = "pluggy"
65
+ version = "1.6.0"
66
+ source = { registry = "https://pypi.org/simple" }
67
+ sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
68
+ wheels = [
69
+ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
70
+ ]
71
+
72
+ [[package]]
73
+ name = "pygments"
74
+ version = "2.20.0"
75
+ source = { registry = "https://pypi.org/simple" }
76
+ sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
77
+ wheels = [
78
+ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
79
+ ]
80
+
81
+ [[package]]
82
+ name = "pytest"
83
+ version = "8.4.2"
84
+ source = { registry = "https://pypi.org/simple" }
85
+ resolution-markers = [
86
+ "python_full_version < '3.10'",
87
+ ]
88
+ dependencies = [
89
+ { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" },
90
+ { name = "exceptiongroup", marker = "python_full_version < '3.10'" },
91
+ { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
92
+ { name = "packaging", marker = "python_full_version < '3.10'" },
93
+ { name = "pluggy", marker = "python_full_version < '3.10'" },
94
+ { name = "pygments", marker = "python_full_version < '3.10'" },
95
+ { name = "tomli", marker = "python_full_version < '3.10'" },
96
+ ]
97
+ sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" }
98
+ wheels = [
99
+ { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" },
100
+ ]
101
+
102
+ [[package]]
103
+ name = "pytest"
104
+ version = "9.1.1"
105
+ source = { registry = "https://pypi.org/simple" }
106
+ resolution-markers = [
107
+ "python_full_version >= '3.10'",
108
+ ]
109
+ dependencies = [
110
+ { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" },
111
+ { name = "exceptiongroup", marker = "python_full_version == '3.10.*'" },
112
+ { name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
113
+ { name = "packaging", marker = "python_full_version >= '3.10'" },
114
+ { name = "pluggy", marker = "python_full_version >= '3.10'" },
115
+ { name = "pygments", marker = "python_full_version >= '3.10'" },
116
+ { name = "tomli", marker = "python_full_version == '3.10.*'" },
117
+ ]
118
+ sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
119
+ wheels = [
120
+ { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
121
+ ]
122
+
123
+ [[package]]
124
+ name = "todo-cli"
125
+ version = "0.1.0"
126
+ source = { editable = "." }
127
+
128
+ [package.dev-dependencies]
129
+ dev = [
130
+ { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
131
+ { name = "pytest", version = "9.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
132
+ ]
133
+
134
+ [package.metadata]
135
+
136
+ [package.metadata.requires-dev]
137
+ dev = [{ name = "pytest", specifier = ">=8.4.2" }]
138
+
139
+ [[package]]
140
+ name = "tomli"
141
+ version = "2.4.1"
142
+ source = { registry = "https://pypi.org/simple" }
143
+ sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" }
144
+ wheels = [
145
+ { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" },
146
+ { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" },
147
+ { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" },
148
+ { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" },
149
+ { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" },
150
+ { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" },
151
+ { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" },
152
+ { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" },
153
+ { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" },
154
+ { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" },
155
+ { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" },
156
+ { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" },
157
+ { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" },
158
+ { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" },
159
+ { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" },
160
+ { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" },
161
+ { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" },
162
+ { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" },
163
+ { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" },
164
+ { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" },
165
+ { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" },
166
+ { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" },
167
+ { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" },
168
+ { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" },
169
+ { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" },
170
+ { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" },
171
+ { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" },
172
+ { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" },
173
+ { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" },
174
+ { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" },
175
+ { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" },
176
+ { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" },
177
+ { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" },
178
+ { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" },
179
+ { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" },
180
+ { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" },
181
+ { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" },
182
+ { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" },
183
+ { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" },
184
+ { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" },
185
+ { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" },
186
+ { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" },
187
+ { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" },
188
+ { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" },
189
+ { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" },
190
+ { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" },
191
+ ]
192
+
193
+ [[package]]
194
+ name = "typing-extensions"
195
+ version = "4.16.0"
196
+ source = { registry = "https://pypi.org/simple" }
197
+ sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" }
198
+ wheels = [
199
+ { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" },
200
+ ]