初始提交:algpkg-sdk(算法包开发 SDK)—— 从 sophon-edge 仓库拆分独立建库

本仓库原先内嵌在 sophon-edge 的 `algpkg-sdk/`(该仓库历史上的三次相关提交:
e8101ba「算法开发能力独立成项目」、090c406「契约名落地为真实模块 + 多模型 ctx」、
b666161「sail 存根 + 编写风格修正」)。现拆出独立建库,便于算法作者单独 clone/迭代、
也便于单独发版(打成 wheel 分发给第三方或进 worker 交付包的 vendor/)。

- 内容:`src/algpkg_sdk/`(api/manifest/loader/builder/testkit/cli)+ 契约名模块
  `src/video_algpkg_api/`、`examples/person-count`(完整示例)、`tests/`(pytest,无设备自动跳过)、
  `README.md`(已改成独立仓库口径:命令在项目根执行,配套 worker 用路径依赖/`ALGPKG_SDK_DIR`)、
  `pyproject.toml`(console script `algpkg`)、`uv.lock`;
- 不含 `.venv/`(`.gitignore` 已覆盖,clone 后 `uv sync` 重建)。

配套 worker 仓库(sophon-edge):
`workers/video/pyproject.toml` 以路径依赖指向本仓库(`path = "../../../algpkg-sdk"`,可用
`ALGPKG_SDK_DIR` 覆盖);交付打包 `deploy/pack_worker.py` 会把本仓库打成 wheel 放进 worker 包 `vendor/`。
This commit is contained in:
linaro
2026-10-16 16:15:59 +08:00
commit 170e4a10a4
18 changed files with 3792 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
# 本项目自己的 venv 与构建产物
.venv/
dist/
*.egg-info
__pycache__/
+169
View File
@@ -0,0 +1,169 @@
# algpkg-sdk —— 算法包开发 SDK
独立项目:**算法作者只依赖本项目**,即可完成「写算法 → 本地跑通 → 打包 → 自检 → 交付 `.mbank`」,
不需要 worker 仓库,也不需要了解 worker 内部结构。
```text
算法包开发(本项目) 设备运行(worker)
────────────────────── ──────────────────
写代码 / 本地测试 / 打包 / 签名 → .mbank → 上传 → 挂载 → 推理
用的接口 = algpkg_sdk.api 同一份接口(别名 video_algpkg_api)
```
## 1. 安装
> 本项目是**独立仓库**(2026-10-16 从 sophon-edge 仓库拆出):
> `git clone <本仓库地址> && cd algpkg-sdk`(目录名即项目名;下文命令都在**本项目根**执行)。
```bash
# 从本项目根安装(开发态;也可打成 wheel 分发给第三方)
uv pip install -e . # 只做静态检查/打包的机器
uv pip install -e '.[device]' # 设备上(含 sophon-arm,可跑推理)
```
配套的 **worker(视频推理系统)仓库**只在使用 `algpkg` 工具链做集成联调时才需要:
worker 侧 `workers/video/pyproject.toml` 以**路径依赖**指向本项目(`path = "../../../algpkg-sdk"`),
交付打包(`deploy/pack_worker.py`)会把本项目打成 wheel 放进 worker 交付包的 `vendor/`;
路径可用环境变量 **`ALGPKG_SDK_DIR`** 覆盖(三个开发脚本与打包脚本都认它)。
依赖很轻:`module-bank`(`.mbank` 读写)、`orjson`、`numpy`。
**设备推理包(sophon-arm)是可选的** —— 没有 NPU 的机器照样能打包与做静态校验。
## 2. 三步上手
```bash
# ① 生成脚手架(目录骨架 + manifest 模板 + 可运行示例 + 自测说明)
algpkg init /tmp/smoke-detect --id demo.smoke-detect --name 抽烟检测 --version 1.0.0
# ② 实现 /tmp/smoke-detect/<包内代码目录>/__init__.py 的 load() / infer()
# 模型放 /tmp/smoke-detect/models/,或打包时用 --model 指定
# ③ 打包 + 自检(不需要设备)
algpkg build /tmp/smoke-detect --out demo.smoke-detect-1.0.0.mbank
algpkg inspect demo.smoke-detect-1.0.0.mbank # 清单/模型/快照/签名
algpkg verify demo.smoke-detect-1.0.0.mbank # 结构与模型校验
```
## 3. 本地测试(不需要 worker)
```bash
# 静态检查:manifest 合法性 / 包结构 / 模型文件 / 代码可导入(试加载)
algpkg check demo.smoke-detect-1.0.0.mbank
algpkg check demo.smoke-detect-1.0.0.mbank --deep # 再实例化入口类(需设备)
# 设备上真跑:图片或视频,warmup 后统计单帧耗时,可画框存图
algpkg run demo.smoke-detect-1.0.0.mbank --source clip.mp4 --frames 5 --save-annotated out/
algpkg run a-1.0.0.mbank --source a.jpg --params '{"conf":0.3}'
# 两个包/版本对拍(逐帧检出数 + 逐位一致性)
algpkg compare a-1.0.0.mbank b-1.0.0.mbank --source clip.mp4
```
同样能力在 Python 里直接调(适合写进自己的 pytest):
```python
from algpkg_sdk import testkit
report = testkit.check("demo.smoke-detect-1.0.0.mbank") # 任意机器
runs = testkit.run("demo.smoke-detect-1.0.0.mbank", source="clip.mp4",
params={"conf": 0.3}, save_annotated="out/") # 设备
print(runs.summary())
print(testkit.compare(runs, testkit.run("old.mbank", source="clip.mp4")))
```
## 4. 写算法:接口契约
包内代码只应导入 `video_algpkg_api`(接口契约名,= `algpkg_sdk.api` 的同一批对象)+ 设备框架:
> 装好本 SDK 后,这个导入名在**任何工具里都直接可用**:IDE 补全 / mypy / pyright /
> 本地 `python -c "import video_algpkg_api"`(SDK 自带同名真实模块 + PEP 561 `py.typed`)。
> 设备运行时 worker 仍注册同名别名,两条解析路径拿到的是同一批对象;
> **不要**自己 vendor/复制一份同名模块(两份实现分叉 = "本地过、设备挂")。
```python
from video_algpkg_api import Algorithm, AlgoContext, AlgoResult, AlgoError
class Algorithm(Algorithm): # 类名与 manifest.entry 一致
kind = "detector"
def load(self) -> None:
data = self.ctx.model_bytes() # 包内模型字节(不落盘,直接给引擎)
conf = float(self.ctx.param("conf", 0.25))
self._labels = self.ctx.read_asset("assets/labels.txt").decode().splitlines()
self._engine = MyEngine(data, self.ctx.dev_id, conf) # 例如 sail.Engine(bytes, …)
def infer(self, frame) -> AlgoResult: # 单帧;抽帧由运行方控制
return AlgoResult(boxes=self._engine(frame)) # (N,6): x1,y1,x2,y2,score,cls
def draw(self, frame, result): # 可选:自定义叠加
return None
def close(self) -> None: # 实例回收时调用
self._engine = None
```
| 用法 | 说明 |
| ---- | ---- |
| `ctx.model_bytes(name)` / `ctx.read_asset(rel)` | 读**包内**文件(模型/类别表/阈值表),不落盘;不给名 = 选中模型,给名 = 任意已声明模型(多模型包,见下) |
| `ctx.param(key, default)` | 任务参数(`params_schema` 默认值已合并,越界在装载时被拒) |
| `ctx.dev_id` / `ctx.logger` / `ctx.meta` | 设备号 / 日志 / 选中模型的元信息(精度、分辨率…) |
| `infer` 返回 | `AlgoResult(boxes=(N,6), counts={"person": n}, value=n, labels=[...])` |
| `load()` 抛异常 | 任务转 failed,错误信息带包 id/版本(先在本地 `algpkg check --deep` 拦住) |
约定:同一实例不会被并发调用(运行方串行化),但 `infer` 不要长时间阻塞;
线程与内存预算见《算法包开发指南》§5。
**多模型包**(如"检测 + 二次判定/误报过滤"):一个包声明多份模型(`manifest.models[]`),
代码里 `ctx.model_bytes("fp_filter")` 按名字取第二份——不必硬编码包内路径:
```python
def load(self):
self._det = ctx.model_bytes() # 选中模型(manifest 里 default 的那份)
self._cls = ctx.model_bytes("fp_filter") # 第二份:裁检测框 → 分类判误报
```
两级管线写在 `infer()` 里:返回**过滤后**的 boxes 即可,告警/统计/叠加图自动跟着走
(运行时不做级联,这就是"包内级联"的标准形态;细节与算力旋钮见《算法包开发指南》§5)。
## 5. 打包选项
```bash
algpkg build <源目录> --out x.mbank \
--model <模型名>=<磁盘路径> \ # 模型不在源码目录时显式指定(包内路径以 manifest 为准)
--encrypt <Fernet KEY> \ # 加密包内代码(明文源码会被删除;模型不加密)
--pyarmor \ # PyArmor 混淆(需授权)
--sign <KEY> --key-id <ID> # HMAC-SHA256 签名(对称;设备侧配相同密钥验签)
algpkg list <目录> # 列目录内的包(id/版本/大小)
```
## 6. 目录结构
```text
algpkg-sdk/
├── pyproject.toml 项目定义(scripts: algpkg)
├── src/algpkg_sdk/
│ ├── api.py 接口契约(Algorithm / AlgoContext / AlgoResult)+ 别名注册
│ ├── manifest.py 清单字段与校验(models / params_schema / alarm / legacy…)
│ ├── loader.py AlgoPackage:读 svfs、挂代码(试加载)、构造实例
│ ├── builder.py 打包(module_bank 打代码 + svfs 放模型/资源 + 快照=版本 + 签名)
│ ├── testkit.py 本地测试台:check / run / compare(无需 worker)
│ └── cli.py algpkg build|inspect|verify|init|list|check|run|compare
├── examples/person-count/ 完整示例:人员计数(自带实现,只依赖本 SDK)
│ └── …
└── tests/ pytest(无设备跑静态,有设备跑推理,自动跳过)
```
## 7. 自测
```bash
cd algpkg-sdk
uv run pytest # 静态用例;标记为 device 的用例在无 NPU 机器上自动跳过
uv run pytest -m device # 设备上跑推理用例
```
## 8. 与设备的对接
1. `algpkg build` 产出单文件 `.mbank`;
2. 在设备「算法管理」页上传(或 `POST /api/v1/video/algorithm/install` 分片上传 → `commit`);
3. 任务里选择该算法(参数表单由 `manifest.params_schema` 生成),可与其他算法并行;
4. 现场排障与完整字段表见仓库文档《算法包开发指南》;设计依据见《算法包与多算法任务设计》。
@@ -0,0 +1,146 @@
"""人员计数示例算法包(M8.2/M8.4)——**自带推理实现**,不依赖 worker 内部模块。
这是"第三方算法包"的样板:只依赖
- `video_algpkg_api`(worker 提供的稳定接口:Algorithm / AlgoContext / AlgoResult);
- 设备框架 `sophon.sail` 与 `numpy`;
- 包内模型(`ctx.model_bytes()` 从 `.mbank` 读字节,**不落盘**)。
算法语义:YOLOv26s 检测 → 只保留 person(COCO 类 0)→ 置信度/最小人高过滤 →
人数作为 `AlgoResult(counts={"person": n}, value=n)` 返回,并把人员框一并给出(供绘制/告警)。
与内置包(`builtin.yolov26-detect`)的区别:kind=counter、默认抽帧更慢(0.5s)、
只报人员、带最小人高参数 —— 用来验证一个任务同时挂"检测 + 计数"两个算法。
"""
from __future__ import annotations
import time
from typing import List
import numpy as np
import sophon.sail as sail
from video_algpkg_api import Algorithm as AlgorithmBase, AlgoResult
PERSON_CLASS = 0 # COCO 类别索引(person)
_BATCH_SUPPORTED = (1, 2, 3, 4, 8, 16, 32, 64, 128, 256)
class Algorithm(AlgorithmBase):
"""画面人员计数(单 bmodel,SAIL 引擎 + bmcv 前处理)。"""
kind = "counter"
#: 引擎与输入张量:load() 赋值、close() 释放。类级声明为非 Optional,infer() 里可直接用
#: (不必判空);close() 置 None 与声明冲突,那两处显式 `type: ignore`——运行时语义就是"丢引用"。
_net: sail.Engine
_input_tensor: sail.Tensor
# ------------------------------------------------------------------ 生命周期
def load(self) -> None:
self._conf = float(self.ctx.param("conf", 0.3))
self._min_height = int(self.ctx.param("min_height", 0))
data = self.ctx.model_bytes()
self._bytes = data # 引擎构造后仍持有引用(不依赖 SDK 拷贝语义)
self._handle = sail.Handle(self.ctx.dev_id)
self._bmcv = sail.Bmcv(self._handle)
self._net = sail.Engine(data, len(data), self.ctx.dev_id, sail.IOMode.SYSO)
self._graph = self._net.get_graph_names()[0]
name = self._net.get_input_names(self._graph)[0]
self._input_name = name
self._input_dtype = self._net.get_input_dtype(self._graph, name)
self._img_dtype = self._bmcv.get_bm_image_data_format(self._input_dtype)
self._input_scale = self._net.get_input_scale(self._graph, name)
self._input_shape = self._net.get_input_shape(self._graph, name)
self._input_shapes = {name: self._input_shape}
if self._input_shape[0] not in _BATCH_SUPPORTED:
raise RuntimeError(f"batch={self._input_shape[0]} 不受支持")
self._net_h, self._net_w = self._input_shape[2], self._input_shape[3]
self._output_names = self._net.get_output_names(self._graph)
self._output_tensors = {}
for out in self._output_names:
shape = self._net.get_output_shape(self._graph, out)
dtype = self._net.get_output_dtype(self._graph, out)
self._output_tensors[out] = sail.Tensor(self._handle, shape, dtype, True, True)
# scale/255,均值 0(与训练一致的 letterbox 归一化)
self._ab = [v * self._input_scale / 255.0 for v in (1, 0, 1, 0, 1, 0)]
self._input_tensor = sail.Tensor(self._handle, self._input_shape,
self._input_dtype, False, False)
self.ctx.logger.info("[%s] 模型就绪(%.1f MB, conf=%.2f, min_height=%d)",
self.ctx.algo_id, len(data) / 1048576.0, self._conf, self._min_height)
# ------------------------------------------------------------------ 推理
def infer(self, frame) -> AlgoResult:
started = time.perf_counter()
pre, ratio, tx, ty = self._preprocess(frame)
self._bmcv.bm_image_to_tensor(pre, self._input_tensor)
self._net.process(self._graph, {self._input_name: self._input_tensor},
self._input_shapes, self._output_tensors)
preds = self._output_tensors[self._output_names[0]].asnumpy()[:1]
persons = self._postprocess(preds[0], frame.width(), frame.height(), ratio, tx, ty)
boxes = np.zeros((len(persons), 6), dtype=np.float32) if not persons else \
np.stack(persons).astype(np.float32)
count = int(boxes.shape[0])
return AlgoResult(boxes=boxes if count else None,
counts={"person": count} if count else {},
value=float(count),
meta={"infer_ms": round((time.perf_counter() - started) * 1000, 1)})
def close(self) -> None:
self._net = None # type: ignore[assignment]
self._output_tensors = {}
self._input_tensor = None # type: ignore[assignment]
self._bytes = b""
# ------------------------------------------------------------------ 内部
def _preprocess(self, bmimg):
"""BGRA/NV12 → RGB_PLANAR → letterbox → 归一化 到网络输入。"""
rgb = sail.BMImage(self._handle, bmimg.height(), bmimg.width(),
sail.Format.FORMAT_RGB_PLANAR, sail.DATA_TYPE_EXT_1N_BYTE)
self._bmcv.convert_format(bmimg, rgb)
img_w, img_h = rgb.width(), rgb.height()
r = min(self._net_w / img_w, self._net_h / img_h)
tw, th = int(round(r * img_w)), int(round(r * img_h))
tx, ty = (self._net_w - tw) / 2, (self._net_h - th) / 2
attr = sail.PaddingAtrr()
attr.set_stx(int(round(tx - 0.1)))
attr.set_sty(int(round(ty - 0.1)))
attr.set_w(tw)
attr.set_h(th)
attr.set_r(114)
attr.set_g(114)
attr.set_b(114)
resized = self._bmcv.crop_and_resize_padding(
rgb, 0, 0, img_w, img_h, self._net_w, self._net_h, attr,
sail.bmcv_resize_algorithm.BMCV_INTER_LINEAR,
)
pre = sail.BMImage(self._handle, self._net_h, self._net_w,
sail.Format.FORMAT_RGB_PLANAR, self._img_dtype)
self._bmcv.convert_to(
resized, pre,
((self._ab[0], self._ab[1]), (self._ab[2], self._ab[3]), (self._ab[4], self._ab[5])),
)
return pre, r, tx, ty
def _postprocess(self, batch, org_w: int, org_h: int, ratio: float,
tx: float, ty: float) -> List[list]:
"""(N,6) 端到端输出 → 人员框(原图坐标;conf / 最小人高过滤)。"""
rows = batch[batch[:, 4] > self._conf] if batch.size else batch
out: List[list] = []
for row in rows:
if int(row[5]) != PERSON_CLASS:
continue
x1 = float((row[0] - tx) / ratio)
y1 = float((row[1] - ty) / ratio)
x2 = float((row[2] - tx) / ratio)
y2 = float((row[3] - ty) / ratio)
x1, x2 = max(0.0, min(x1, org_w - 1)), max(0.0, min(x2, org_w - 1))
y1, y2 = max(0.0, min(y1, org_h - 1)), max(0.0, min(y2, org_h - 1))
if self._min_height and (y2 - y1) < self._min_height:
continue
out.append([x1, y1, x2, y2, float(row[4]), PERSON_CLASS])
return out
+80
View File
@@ -0,0 +1,80 @@
person
bicycle
car
motorcycle
airplane
bus
train
truck
boat
traffic light
fire hydrant
stop sign
parking meter
bench
bird
cat
dog
horse
sheep
cow
elephant
bear
zebra
giraffe
backpack
umbrella
handbag
tie
suitcase
frisbee
skis
snowboard
sports ball
kite
baseball bat
baseball glove
skateboard
surfboard
tennis racket
bottle
wine glass
cup
fork
knife
spoon
bowl
banana
apple
sandwich
orange
broccoli
carrot
hot dog
pizza
donut
cake
chair
couch
potted plant
bed
dining table
toilet
tv
laptop
mouse
remote
keyboard
cell phone
microwave
oven
toaster
sink
refrigerator
book
clock
vase
scissors
teddy bear
hair drier
toothbrush
+41
View File
@@ -0,0 +1,41 @@
{
"manifest_format": 1,
"id": "demo.person-count",
"name": "人员计数(示例算法包)",
"version": "1.0.0",
"api_version": 1,
"kind": "counter",
"entry": "Algorithm",
"code_package": "alg_demo_person_count",
"vendor": "chongming",
"description": "画面人员计数示例包(M8.2/M8.4):自带推理实现(不依赖 worker 内部模块),只用到 video_algpkg_api 的 ctx.model_bytes/param 与 AlgoResult;输出人数(counts/value)与人员框,用于验证\"一个任务挂多个算法\"。",
"models": [
{
"name": "yolo26s_fp16_1b",
"file": "models/yolo26s_fp16_1b.bmodel",
"precision": "fp16",
"resolution": [640, 640],
"batch": 1,
"default": true
}
],
"input": {"width": 640, "height": 640, "color": "rgb", "layout": "planar",
"mean": [0, 0, 0], "std": [255, 255, 255]},
"output": {"type": "counter", "format": "N6", "classes_file": "assets/labels.txt"},
"params_schema": [
{"key": "conf", "type": "float", "default": 0.3, "min": 0.01, "max": 0.99, "step": 0.01,
"label": "人员置信度阈值", "desc": "低于该分数的人员不计入"},
{"key": "min_height", "type": "int", "default": 0, "min": 0, "max": 2000, "step": 10,
"label": "最小人高(像素)", "desc": "过滤远处的误检(0 = 不过滤)"},
{"key": "interval", "type": "float", "default": 0.5, "min": 0.02, "max": 60, "step": 0.02,
"label": "抽帧间隔(秒)", "desc": "计数类算法可放慢,省算力"},
{"key": "draw", "type": "bool", "default": false,
"label": "绘制叠加", "desc": "把人员框画到输出帧上"}
],
"defaults": {"conf": 0.3, "min_height": 0, "interval": 0.5, "draw": false},
"alarm": {"type_template": "person_count", "cooldown_key": "algo",
"need_snapshot": true, "need_record": false},
"requires": {"license_features": [], "devices": ["BM1688", "BM1684X", "CV186X"], "frameworks": ["sail"]},
"ui": {"order": 20, "tags": ["示例", "计数"], "icon": "mdi-account-multiple"},
"legacy": {}
}
+50
View File
@@ -0,0 +1,50 @@
# 算法包开发 SDK —— 独立项目(可单独 clone / 分发 / 出包)
#
# 定位:算法作者只需要这一个项目 + 一台设备(或没有设备也能做静态校验),
# 即可完成「写算法 → 本地跑通 → 打包 → 自检 → 交付 .mbank」的全流程,
# 不需要 worker 仓库、也不需要了解 worker 内部结构。
[project]
name = "algpkg-sdk"
version = "0.1.0"
description = "算法包(.mbank)开发、测试与打包工具链"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
# .mbank 读写(svfs 虚拟文件系统 + 模块银行打包器)
"module-bank>=0.4.2",
"orjson>=3.10",
"numpy>=1.26",
# 注:设备侧推理(sophon-arm / sail)**不是必需依赖** —— 没有 NPU 的机器照样能做
# manifest 校验、打包、挂载试加载(不实跑模型);只有 `algpkg run` 需要设备。
]
[project.optional-dependencies]
# 设备上跑推理/本地测试台时需要(BM1684X / BM1688 / CV186X 等 Sophon 设备)
device = ["sophon-arm>=3.11.1"]
# 开发本项目自身(单测 + 类型)
dev = ["pytest>=8", "sophon-arm>=3.11.1"]
[project.scripts]
algpkg = "algpkg_sdk.cli:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
# 两个顶层包同属本发行:algpkg_sdk(工具链)+ video_algpkg_api(接口契约名的真实模块,
# 算法包代码只导入它;重导出 algpkg_sdk.api,见 src/video_algpkg_api/__init__.py)
packages = ["src/algpkg_sdk", "src/video_algpkg_api"]
[tool.pytest.ini_options]
testpaths = ["tests"]
markers = [
"device: 需要 Sophon 设备(NPU)才能跑;无设备时自动跳过",
]
# 索引:内部源(含 sophon-arm 等设备包)优先,公共源兜底 —— 与 workers/video 同口径
[[tool.uv.index]]
url = "http://124.71.68.6:3000/api/packages/chongming/pypi/simple/"
[[tool.uv.index]]
url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"
+39
View File
@@ -0,0 +1,39 @@
"""算法包 SDK(独立项目):算法包(`.mbank`)的开发、测试与打包工具链。
给算法作者的三件东西:
| 用途 | 入口 |
| ---- | ---- |
| **写算法** —— 实现接口、读包内资源、返回结果 | `from algpkg_sdk import Algorithm, AlgoContext, AlgoResult`(也可沿用稳定别名 `video_algpkg_api`) |
| **本地跑通** —— 不用 worker,直接把包跑在图片/视频上 | `algpkg run <包.mbank> --image <图片>` 或 `from algpkg_sdk import testkit` |
| **打包与自检** —— 单文件 `.mbank`,可加密/混淆/签名 | `algpkg build / inspect / verify` |
worker 侧把本项目的 `api` 模块以 `video_algpkg_api` 注入进程(见 :func:`algpkg_sdk.api.install_sdk_alias`),
因此**同一份算法代码在本地测试与设备运行时看到的是同一套接口**。
"""
from __future__ import annotations
from .api import (ALG_API_VERSION, AlgoContext, AlgoError, AlgoResult, Algorithm,
install_sdk_alias)
from .builder import AlgoBuilder, BuildError, inspect_package, verify_package
from .loader import (AlgoPackage, AlgoPackageError, namespace_loaded, purge_namespace,
sha256_file)
from .manifest import (MANIFEST_NAME, Manifest, ManifestError, ModelSpec,
build_manifest, code_package_for)
__version__ = "0.1.0"
#: 算法包代码里 `import` 的稳定模块名(契约名):本 SDK 以**同名真实模块**分发
#: (`src/video_algpkg_api/`,重导出本模块的 api —— 见 video_algpkg_api/__init__.py),
#: worker 运行时再注册同名别名(setdefault,同一批对象,谁先解析都一样)
SDK_MODULE_NAME = "video_algpkg_api"
install_sdk_alias() # 导入本包即注册别名:包内代码 import video_algpkg_api 立即可用
__all__ = [
"ALG_API_VERSION", "AlgoContext", "AlgoError", "AlgoResult", "Algorithm",
"install_sdk_alias", "AlgoBuilder", "BuildError", "inspect_package", "verify_package",
"AlgoPackage", "AlgoPackageError", "purge_namespace", "namespace_loaded", "sha256_file",
"MANIFEST_NAME", "Manifest", "ManifestError", "ModelSpec", "build_manifest",
"code_package_for", "SDK_MODULE_NAME", "__version__",
]
+247
View File
@@ -0,0 +1,247 @@
"""算法包 SDK(M8.1):算法实现方(内置 / 第三方)与 worker 之间的稳定接口。
**这是算法包的"接口契约"层**,与 worker 内部实现解耦:
- 算法包里的代码 `from video_algpkg_api import Algorithm, AlgoContext, AlgoResult`
即可(本模块在 worker 导入时以 `video_algpkg_api` 注册进 ``sys.modules``);
- 版本兼容由 :data:`ALG_API_VERSION` 把关(manifest.api_version,worker 装载时校验);
- 结果结构 :class:`AlgoResult` 是"检测框 / 计数"的统一载体,绘制、告警、统计都只认它。
**生命周期**(worker 侧调用顺序)::
Algorithm(ctx) # 构造:只保存 ctx,不做重活
algo.load() # 加载模型(可失败:抛异常 → 任务 failed,见 controller)
algo.infer(frame) # 单帧推理(抽帧由运行时统一控制)
algo.draw(frame, result) # 可选:自定义叠加;缺省用 worker 的统一 Drawer
algo.close() # 释放(模型实例回收时调用)
线程模型:同一实例**不会**被并发调用(worker 侧 ModelRegistry 用互斥锁串行化),
实现方不必自己加锁;但 `infer` 不得长时间阻塞(抽帧循环是协作式的)。
"""
from __future__ import annotations
import logging
import sys
from typing import Any, Callable, Optional
import numpy as np
#: 接口版本:worker 装载算法包时校验 `manifest.api_version <= ALG_API_VERSION`。
#: 破坏性变更才 +1(不兼容的旧包会被拒绝装载并给出可读原因)。
ALG_API_VERSION = 1
#: 算法包代码里 `import` 的稳定模块名(本模块在 worker 侧以此名注册)。
SDK_MODULE_NAME = "video_algpkg_api"
#: 算法包类型(manifest.kind)——决定 UI 呈现与结果渲染方式。
KINDS = ("detector", "classifier", "counter", "tracker")
#: 结果类型(manifest.output.type)。
OUTPUT_TYPES = ("boxes", "count", "counter")
class AlgoError(RuntimeError):
"""算法实现方主动抛出的可读错误(worker 会把它作为任务 last_error 展示)。"""
class AlgoResult:
"""一次推理的结果(统一载体)。
- ``boxes``:``(N, 6)`` float32/float64 数组(x1,y1,x2,y2,score,cls,原图坐标系)
—— 绘制、告警、统计都基于它;无框的算法(纯计数)留 None;
- ``counts``:计数类算法的分项计数(如 ``{"person": 3}``;无分项可用 ``{"": n}``);
- ``value``:单值结果(如"当前画面人数");
- ``labels``:类别名(下标与 boxes 的 cls 对应;缺省用包内 labels 或 COCO 表)。
"""
__slots__ = ("boxes", "counts", "value", "labels", "meta")
def __init__(self, boxes: Any = None, counts: dict | None = None,
value: float | None = None, labels: Optional[list[str]] = None,
meta: dict | None = None):
if boxes is not None:
boxes = np.asarray(boxes)
if boxes.ndim != 2 or (boxes.size and boxes.shape[1] != 6):
raise AlgoError(f"boxes 形状必须是 (N,6)(x1,y1,x2,y2,score,cls),收到 {boxes.shape}")
self.boxes = boxes
self.counts = counts
self.value = float(value) if value is not None else None
self.labels = labels
self.meta = meta or {}
# ------------------------------------------------------------------ 便捷
def detections(self) -> int:
"""检出目标数(告警/统计口径:boxes 行数,其次 counts 合计,其次 value)。"""
if self.boxes is not None:
return int(self.boxes.shape[0])
if self.counts:
return int(sum(self.counts.values()))
return int(self.value or 0)
def top(self, index: int = 4):
"""置信度最高的那行(告警取"最可信目标"用);无框返回 None。"""
if self.boxes is None or self.boxes.shape[0] == 0:
return None
return max(self.boxes, key=lambda row: float(row[index]))
def summary(self) -> dict:
"""统计/接口展示用的紧凑摘要。"""
return {
"detections": self.detections(),
"counts": self.counts,
"value": self.value,
}
class AlgoContext:
"""算法实现方可见的上下文(模型字节、参数、设备、资源读取、日志)。
由 worker 构造注入;算法实现只读使用。**不要缓存 `model_bytes()` 的返回值之外的重对象**,
实例回收(卸载/空闲 TTL)时 worker 会调用 ``close()``。
**多模型包**(如"检测 + 二次判定/误报过滤"):一个包可声明多份模型(manifest.models[]),
任务选中的那份是"入口模型"(:meth:`model_bytes` 不给名时读它),其余按名字取用::
self._det = ctx.model_bytes() # 选中模型(manifest 里 default 的那份)
self._cls = ctx.model_bytes("fp_filter") # 包内第二份(按 manifest.models[].name)
"""
def __init__(self, *, algo_id: str, version: str, model_name: str, params: dict,
dev_id: int = 0, meta: dict | None = None, kind: str = "detector",
output: dict | None = None, logger: logging.Logger | None = None,
reader: Callable[[str], bytes] | None = None,
models: list[dict] | None = None):
self.algo_id = algo_id
self.version = version
self.model_name = model_name
self.params = dict(params or {})
self.dev_id = int(dev_id)
#: 选中模型在 manifest.models[] 里的完整条目(precision/resolution/batch…)
self.meta = dict(meta or {})
#: 包内**全部**已声明模型条目(含 meta 本身;多模型包按名取用,见 model_bytes)
self.models: tuple[dict, ...] = tuple(
dict(item) for item in (models if models else ([self.meta] if self.meta else []))
)
self.kind = kind
self.output = dict(output or {})
self.logger = logger or logging.getLogger(f"algo.{algo_id}")
self._reader = reader
# ------------------------------------------------------------------ 资源
def model_bytes(self, name: str | None = None) -> bytes:
"""读模型字节(从算法包 VFS 读;每次调用都会重新读,需要缓存请自行持有)。
`name` 空 = **任务选中的模型**(向后兼容的旧写法);给名字 = 按
`manifest.models[]` 的 `name`(或 `file` 路径)取包内任意已声明模型——
多模型包(如检测 + 二次判定)用它读第二份模型,不必硬编码包内路径。
"""
if self._reader is None:
raise AlgoError("ctx 未绑定包内读取器(reader)——算法实现不应自行构造 AlgoContext")
if not str(name or "").strip():
return self._reader(self.meta.get("file") or self.model_name)
spec = self.model_spec(name)
return self._reader(str(spec.get("file") or spec.get("name")))
def model_spec(self, name: str | None = None) -> dict:
"""查已声明模型的完整条目(precision/resolution/batch/default)。
`name` 空 = 选中模型;不存在时抛 :class:`AlgoError`(消息里带可选清单)。
"""
wanted = str(name or "").strip()
if not wanted:
return dict(self.meta)
for spec in self.models:
if str(spec.get("name") or "") == wanted or str(spec.get("file") or "") == wanted:
return dict(spec)
available = ", ".join(str(spec.get("name")) for spec in self.models) or "(无)"
raise AlgoError(f"算法包 {self.algo_id} 内没有模型 {wanted!r}(可选:{available})")
def read_asset(self, rel_path: str) -> bytes:
"""读取包内任意文件(assets/labels.txt 等)。"""
if self._reader is None:
raise AlgoError("ctx 未绑定包内读取器(reader)")
return self._reader(rel_path)
def param(self, key: str, default: Any = None) -> Any:
"""取任务参数(manifest.defaults / params_schema 的默认值已合并进来)。
返回 `Any`:值的类型由 manifest.params_schema 决定,取用处自行转换
(如 `conf = float(ctx.param("conf", 0.3))`)。
"""
return self.params.get(key, default)
class Algorithm:
"""算法实现基类(算法包入口,见 manifest.entry)。
最小实现(包内 `__init__.py`)::
from video_algpkg_api import Algorithm, AlgoContext, AlgoResult
class Algorithm(Algorithm): # 类名固定为 Algorithm(manifest.entry 可改)
def load(self):
self._engine = MyEngine(self.ctx.model_bytes(), self.ctx.dev_id)
def infer(self, frame):
return AlgoResult(boxes=self._engine(frame))
`infer` 返回 :class:`AlgoResult`、``(N,6)`` 数组或 dict(自动包装)均可。
"""
#: 实现方声明的接口版本(与包 manifest.api_version 一致即可)
api_version = ALG_API_VERSION
#: 结果类型提示(缺省由 manifest 决定)
kind = "detector"
def __init__(self, ctx: AlgoContext):
self.ctx = ctx
self._loaded = False
# ------------------------------------------------------------------ 生命周期
def load(self) -> None:
"""加载模型/初始化(可能耗时;失败即抛异常,任务会转 failed 并带可读错误)。"""
def infer(self, frame) -> AlgoResult: # pragma: no cover - 抽象
raise NotImplementedError("算法包必须实现 infer(frame)")
def draw(self, frame, result: AlgoResult):
"""可选:自定义叠加绘制(返回新帧)。缺省返回 None → worker 用统一 Drawer。"""
return None
def close(self) -> None:
"""释放资源(引擎句柄等);worker 在实例回收时调用,可重复调用。"""
# ------------------------------------------------------------------ worker 适配
def ensure_loaded(self) -> "Algorithm":
if not self._loaded:
self.load()
self._loaded = True
return self
def __call__(self, images: list):
"""批量入口(与历史 `ModelEntry.infer(images)` 签名一致):返回每张图的 boxes。"""
self.ensure_loaded()
boxes = []
for image in images:
result = self.infer(image)
if isinstance(result, AlgoResult):
boxes.append(result.boxes if result.boxes is not None
else np.zeros((0, 6), dtype=np.float32))
elif isinstance(result, dict):
boxes.append(np.asarray(result.get("boxes") or np.zeros((0, 6), dtype=np.float32)))
else:
boxes.append(np.asarray(result))
return boxes
def install_sdk_alias(name: str = SDK_MODULE_NAME) -> str:
"""把本模块注册成稳定的导入名(算法包代码 `import video_algpkg_api` 依赖它)。
用 ``setdefault``:包代码若已自带同名模块(自测场景)则不覆盖。
"""
sys.modules.setdefault(name, sys.modules[__name__])
return name
+299
View File
@@ -0,0 +1,299 @@
"""算法包打包器(M8.4 工具链底座):源码目录 → 单文件 `.mbank`。
打包产物 = 一个标准 svfs 包(`.mbank`),内部结构::
manifest.json # 清单(Manifest.parse 校验)
modules/<code_package>/** # 包内代码(.pyc 锚点 + .py 源码 + .meta.json)
models/<...>.bmodel # 随包模型(多份精度/核数)
assets/labels.txt # 可选资源(类别名、阈值表…)
实现复用现有底座(不新造轮子):
- 代码部分 = `module_bank.PythonToSQLite.pack_directory()`(同一套 .pyc/元数据/可选 PyArmor);
- manifest / 模型 / 资源 = `packer.vfs.add_bytes()`(**任意文件**都能进 svfs,见 §3 取证);
- 版本 = svfs 快照:打包完把当前快照命名为包版本(`rename_snapshot`),
之后可用 `--snapshot <版本>` 钉住加载(回滚语义);
- 签名(可选)= `packer.sign_version()`(svfs 的 `signatures` 表,HMAC-SHA256 —— 注意是
**对称密钥**,不是非对称签名:验签方必须持有同一密钥,见《算法包设计》§4.7 的偏差说明)。
打包后自检(`_selfcheck`):重新以只读方式打开,核对 manifest 可解析、模型文件在包内
且非空、代码模块齐全 —— 坏包不许出门。
"""
from __future__ import annotations
import logging
import orjson
from pathlib import Path
from .manifest import MANIFEST_NAME, Manifest, ManifestError
from .loader import sha256_file
logger = logging.getLogger(__name__)
#: 资源目录(随包分发的非代码文件;`assets/**` 原样进包)
ASSETS_DIR = "assets"
class BuildError(RuntimeError):
"""打包失败(消息面向"源目录怎么改")。"""
class AlgoBuilder:
"""源码目录 → `.mbank` 打包器。"""
def __init__(self, src_dir: str | Path, *, key: str | None = None, compress: bool = True,
pyarmor: bool = False, model_files: dict[str, str] | None = None,
verbose: bool = True):
"""
参数:
key: Fernet 密钥(加密包内**代码**;模型/资源不加密——svfs 不加密任意文件,
见设计文档 §4.7 的记录)
pyarmor: 是否用 PyArmor 混淆代码(需要 PyArmor 授权;失败会抛 BuildError)
model_files: `{模型名: 磁盘路径}` —— manifest 里声明的模型不在源目录 models/ 下时
的显式映射(内置包复用 `workers/video/models/<x>.bmodel` 就靠它)
"""
self.src = Path(src_dir)
if not self.src.is_dir():
raise BuildError(f"源码目录不存在: {self.src}")
self.key = key
self.compress = bool(compress)
self.pyarmor = bool(pyarmor)
self.model_files = dict(model_files or {})
self.verbose = bool(verbose)
# ------------------------------------------------------------------ 读取源码
def _read_manifest(self) -> tuple[dict, Manifest]:
path = self.src / MANIFEST_NAME
if not path.is_file():
raise BuildError(f"源码目录缺少 {MANIFEST_NAME}: {path}")
try:
raw = orjson.loads(path.read_bytes())
except Exception as exc:
raise BuildError(f"{MANIFEST_NAME} 不是合法 JSON: {exc}") from exc
try:
manifest = Manifest.parse(raw, source=str(path))
except ManifestError as exc:
raise BuildError(f"{MANIFEST_NAME} 校验失败: {exc}") from exc
return raw, manifest
def _model_sources(self, manifest: Manifest) -> list[tuple[str, Path]]:
"""解析每个模型的磁盘来源:显式映射 > 源目录内同路径 > 源目录 models/<文件名>。"""
resolved: list[tuple[str, Path]] = []
for spec in manifest.models:
candidates: list[Path] = []
if spec.name in self.model_files:
candidates.append(Path(self.model_files[spec.name]))
elif spec.file in self.model_files:
candidates.append(Path(self.model_files[spec.file]))
candidates.append(self.src / spec.file)
candidates.append(self.src / "models" / Path(spec.file).name)
found = next((c for c in candidates if c.is_file()), None)
if found is None:
tried = ", ".join(str(c) for c in candidates)
raise BuildError(
f"模型 {spec.name!r}({spec.file})找不到磁盘文件;找过: {tried}\n"
f"提示: 用 --model {spec.name}=<路径> 显式指定"
)
resolved.append((spec.file, found))
return resolved
def _asset_sources(self) -> list[tuple[str, Path]]:
"""`assets/**` 原样进包(排除 __pycache__ 等)。"""
root = self.src / ASSETS_DIR
if not root.is_dir():
return []
items = []
for path in sorted(root.rglob("*")):
if not path.is_file() or "__pycache__" in path.parts:
continue
items.append((str(path.relative_to(self.src)).replace("\\", "/"), path))
return items
# ------------------------------------------------------------------ 打包
def build(self, out_path: str | Path, *, sign_key: str | None = None,
sign_key_id: str | None = None, replace: bool = True,
code_include: list[str] | None = None) -> dict:
"""执行打包,返回产物摘要(manifest/大小/sha256)。"""
try:
from module_bank import PythonToSQLite
except ImportError as exc: # pragma: no cover - 依赖缺失
raise BuildError(f"缺少 module_bank 依赖: {exc}") from exc
raw_manifest, manifest = self._read_manifest()
out = Path(out_path)
if out.exists():
if not replace:
raise BuildError(f"产物已存在(未指定 replace): {out}")
out.unlink()
out.parent.mkdir(parents=True, exist_ok=True)
code_dir = self.src / manifest.code_package
if not code_dir.is_dir():
raise BuildError(
f"源码目录缺少包代码目录 {manifest.code_package}/(manifest.code_package 指向它): {code_dir}"
)
models = self._model_sources(manifest)
assets = self._asset_sources()
# ① 建包(svfs)+ ② 打代码(module_bank 的既有能力)
try:
packer = PythonToSQLite(str(out), key=self.key, pyarmor_obfuscate=self.pyarmor,
compress=self.compress)
except Exception as exc:
raise BuildError(f"创建算法包失败: {exc}") from exc
try:
self._log(f"打包代码: {manifest.code_package}/ → {out.name}")
packer.pack_directory(
str(code_dir), manifest.code_package,
include_patterns=list(code_include or ["*.py"]),
exclude_patterns=["__pycache__", "*.pyc"],
)
# ③ 清单 / 模型 / 资源(任意文件直接进 svfs)
packer.vfs.add_bytes(MANIFEST_NAME, orjson.dumps(raw_manifest, option=orjson.OPT_INDENT_2),
replace=True)
for rel, source in models:
self._log(f"打包模型: {rel} ← {source}({source.stat().st_size / 1048576:.1f} MB)")
packer.vfs.add_file(str(source), rel, replace=True)
for rel, source in assets:
self._log(f"打包资源: {rel}")
packer.vfs.add_file(str(source), rel, replace=True)
if self.key or self.pyarmor:
# 加密/混淆后**删掉明文源码**:否则包内同时留着 .py,谁都读得到(也绕过了"没密钥装不上"
# 的保护)。参考 chongming-cli 的 build.py::_stage_pack_modules 同样的收尾动作。
removed = packer.delete_source_code(None)
self._log(f"已删除包内明文源码(加密/混淆包): {removed}")
packer.verify_package_structure()
self._tag_version(packer, manifest.version)
if sign_key:
packer.sign_version(sign_key, key_id=sign_key_id, snapshot=manifest.version)
self._log(f"已签名(key_id={sign_key_id or '-'})")
except BuildError:
raise
except Exception as exc:
raise BuildError(f"打包失败: {type(exc).__name__}: {exc}") from exc
finally:
try:
packer.close()
except Exception: # pragma: no cover - 关闭失败不影响产物
pass
summary = self._selfcheck(out)
self._log(f"完成: {out}({summary['size_bytes'] / 1048576:.1f} MB,sha256 {summary['sha256'][:12]}…)")
return summary
@staticmethod
def _tag_version(packer, version: str) -> None:
"""把当前快照命名为包版本(svfs 快照 = 版本;回滚/钉版本靠它)。"""
try:
vfs = packer.vfs
snapshot_id = getattr(vfs, "snapshot_id", None)
snapshots = vfs.list_snapshots()
if snapshot_id is None and snapshots:
snapshot_id = snapshots[-1]["id"]
if snapshot_id is not None:
vfs.rename_snapshot(snapshot_id, str(version))
except Exception as exc: # pragma: no cover - 命名失败不致命
logger.warning("[algpkg] 快照命名失败(不影响装载): %s", exc)
def _selfcheck(self, out: Path) -> dict:
"""重新只读打开产物,核对清单/模型/模块(坏包不许出门)。"""
from .loader import AlgoPackage, AlgoPackageError
try:
pkg = AlgoPackage(out, key=self.key)
pkg.open()
except AlgoPackageError as exc:
raise BuildError(f"产物自检失败: {exc}") from exc
try:
manifest = pkg.require_manifest()
modules = sorted(pkg.list_files(), key=lambda item: item["path"])
return {
"path": str(out),
"algo_id": manifest.id,
"name": manifest.name,
"version": manifest.version,
"api_version": manifest.api_version,
"code_package": manifest.code_package,
"models": [m.to_dict() for m in manifest.models],
"files": modules,
"size_bytes": out.stat().st_size,
"sha256": sha256_file(out),
"encrypted": bool(self.key),
}
finally:
pkg.close()
def _log(self, message: str) -> None:
if self.verbose:
print(f"[algpkg build] {message}")
def inspect_package(path: str | Path, *, key: str | None = None) -> dict:
"""只读检查一个 `.mbank`(`algpkg inspect`):清单 + 文件清单 + 快照/签名。"""
from .loader import AlgoPackage
pkg = AlgoPackage(path, key=key)
pkg.open()
try:
manifest = pkg.require_manifest()
snapshots: list[dict] = []
signatures: list[dict] = []
try:
from sqlite_vfs.core import SQLiteVFS
vfs = SQLiteVFS(str(path), readonly=True, check_same_thread=False)
snapshots = [dict(row) for row in vfs.list_snapshots()]
signatures = [dict(row) for row in vfs.list_signatures()]
vfs.close()
except Exception as exc: # pragma: no cover - 元信息读取失败不致命
logger.debug("[algpkg] 快照/签名读取失败: %s", exc)
return {
**manifest.info(),
"filename": Path(path).name,
"size_bytes": Path(path).stat().st_size,
"sha256": sha256_file(path),
"files": pkg.list_files(),
"snapshots": snapshots,
"signatures": signatures,
}
finally:
pkg.close()
def verify_package(path: str | Path, *, key: str | None = None,
signature_key: str | None = None, snapshot=None) -> dict:
"""校验一个 `.mbank`(`algpkg verify`):结构 + 模型存在 + 可选验签。"""
from .loader import AlgoPackage
pkg = AlgoPackage(path, key=key, snapshot=snapshot)
pkg.open()
try:
manifest = pkg.require_manifest()
report: dict = {
"algo_id": manifest.id, "version": manifest.version,
"api_version": manifest.api_version, "ok": True,
"models": [], "signature": None,
}
for spec in manifest.models:
data = pkg.read_model(spec.name)[0]
report["models"].append({"name": spec.name, "file": spec.file,
"size_bytes": len(data)})
if signature_key:
from sqlite_vfs.core import SQLiteVFS
vfs = SQLiteVFS(str(path), readonly=True, check_same_thread=False)
try:
report["signature"] = dict(vfs.verify_signature(signature_key, snapshot_id=snapshot))
report["ok"] = bool(report["signature"].get("ok"))
finally:
vfs.close()
return report
finally:
pkg.close()
__all__ = ["AlgoBuilder", "BuildError", "inspect_package", "verify_package"]
+448
View File
@@ -0,0 +1,448 @@
"""`algpkg` 命令行:打包 / 检查 / 校验 / 脚手架 / **本地测试台**。
安装本项目后即可直接用(不需要 worker 仓库)::
algpkg init <dir> --id my.algo --name 我的算法 # 脚手架
algpkg build <src> --out my.algo-1.0.0.mbank [--model 名=路径] [--encrypt KEY] [--sign KEY]
algpkg inspect <file.mbank> [--json] # 看清单/模型/快照/签名
algpkg verify <file.mbank> [--signature-key KEY] # 结构+模型+(可选)验签
algpkg list <dir>
本地测试(不需要 worker;`run`/`compare` 需要设备)::
algpkg check <file.mbank> [--deep] # 静态:清单/结构/试加载
algpkg run <file.mbank> --source clip.mp4 [--params '{"conf":0.3}'] [--save-annotated out/]
algpkg compare <a.mbank> <b.mbank> --source clip.mp4 # 两包对拍
等价入口:`python -m algpkg_sdk.cli ...`。
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from .builder import AlgoBuilder, BuildError, inspect_package, verify_package
from .manifest import Manifest, code_package_for
# ====================================================================== 子命令
def cmd_build(args: argparse.Namespace) -> int:
models: dict[str, str] = {}
for item in args.model or []:
if "=" not in item:
print(f"参数格式错误(应为 名=路径): {item}", file=sys.stderr)
return 2
name, path = item.split("=", 1)
models[name.strip()] = path.strip()
builder = AlgoBuilder(args.src, key=args.encrypt, compress=not args.no_compress,
pyarmor=args.pyarmor, model_files=models, verbose=not args.quiet)
try:
summary = builder.build(args.out, sign_key=args.sign, sign_key_id=args.key_id,
replace=not args.no_replace)
except BuildError as exc:
print(f"✗ 打包失败: {exc}", file=sys.stderr)
return 1
if args.json:
print(json.dumps(summary, ensure_ascii=False, indent=2))
else:
print(f"✓ {summary['algo_id']}@{summary['version']} → {summary['path']}"
f"({summary['size_bytes'] / 1048576:.1f} MB)")
print(f" 模型: {', '.join(m['name'] for m in summary['models'])}"
f" | 代码包: {summary['code_package']}"
f" | {'已加密' if summary['encrypted'] else '未加密'}")
return 0
def cmd_inspect(args: argparse.Namespace) -> int:
try:
info = inspect_package(args.mbank, key=args.encrypt)
except Exception as exc:
print(f"✗ 检查失败: {exc}", file=sys.stderr)
return 1
if args.json:
print(json.dumps(info, ensure_ascii=False, indent=2, default=str))
return 0
print(f"算法包: {info['filename']}({info['size_bytes'] / 1048576:.1f} MB)")
print(f" id/name : {info['algo_id']} / {info['name']}")
print(f" 版本/api : {info['version']} / api_version={info['api_version']}")
print(f" 类型/入口 : {info['kind']} / {info['entry']}(代码包 {info['code_package']})")
print(f" sha256 : {info['sha256']}")
print(" 模型:")
for model in info["models"]:
mark = "(默认)" if model.get("default") else ""
print(f" - {model['name']} [{model.get('precision') or '-'}] {model['file']}{mark}")
print(" 文件:")
for item in info["files"]:
print(f" - {item['path']}({item['size']} B)")
print(f" 快照: {', '.join(str(s.get('name')) for s in info['snapshots']) or '(无)'}")
print(f" 签名: {len(info['signatures'])} 条"
+ (f"(key_id={info['signatures'][-1].get('key_id')})" if info["signatures"] else ""))
return 0
def cmd_verify(args: argparse.Namespace) -> int:
try:
report = verify_package(args.mbank, key=args.encrypt,
signature_key=args.signature_key, snapshot=args.snapshot)
except Exception as exc:
print(f"✗ 校验失败: {exc}", file=sys.stderr)
return 1
print(json.dumps(report, ensure_ascii=False, indent=2, default=str))
if report.get("models") and report.get("ok"):
print("✓ 结构与模型校验通过" + ("(签名有效)" if args.signature_key else ""))
return 0
print("✗ 校验未通过", file=sys.stderr)
return 1
def cmd_list(args: argparse.Namespace) -> int:
root = Path(args.dir)
files = sorted(list(root.glob("*.mbank")) + list((root / "builtin").glob("*.mbank")))
if not files:
print(f"({root} 下没有 .mbank)")
return 0
print(f"{'算法':<28} {'版本':<10} {'大小':>9} 文件")
for path in files:
try:
info = inspect_package(path, key=args.encrypt)
print(f"{info['algo_id']:<28} {info['version']:<10} {info['size_bytes'] / 1048576:>7.1f}MB {path.name}")
except Exception as exc:
print(f"{'?':<28} {'-':<10} {path.stat().st_size / 1048576:>7.1f}MB {path.name} ⚠ {exc}")
return 0
def cmd_init(args: argparse.Namespace) -> int:
root = Path(args.dir)
manifest_path = root / "manifest.json"
if manifest_path.exists() and not args.force:
print(f"✗ {manifest_path} 已存在(--force 覆盖)", file=sys.stderr)
return 1
code_package = args.code_package or code_package_for(args.id)
root.mkdir(parents=True, exist_ok=True)
(root / code_package).mkdir(parents=True, exist_ok=True)
(root / "assets").mkdir(parents=True, exist_ok=True)
manifest_path.write_text(json.dumps(_scaffold_manifest(args, code_package),
ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
(root / code_package / "__init__.py").write_text(_scaffold_code(args), encoding="utf-8")
labels = root / "assets" / "labels.txt"
if not labels.exists():
labels.write_text("# 每行一个类别名(第 0 行 = 类别 0);按需替换\nobject\n", encoding="utf-8")
readme = root / "README.md"
if not readme.exists():
readme.write_text(_scaffold_readme(args, code_package), encoding="utf-8")
print(f"✓ 已生成脚手架: {root}")
print(f" 1) 把模型放到 {root}/models/(或打包时用 --model 名=<路径> 指定)")
print(f" 2) 实现 {code_package}/__init__.py 的 load/infer")
print(f" 3) 打包: workers/video/algpkg build {root} --out {args.id}-{args.version}.mbank")
return 0
# ====================================================================== 脚手架内容
def _scaffold_manifest(args: argparse.Namespace, code_package: str) -> dict:
return {
"manifest_format": 1,
"id": args.id,
"name": args.name,
"version": args.version,
"api_version": 1,
"kind": args.kind,
"entry": "Algorithm",
"code_package": code_package,
"vendor": args.vendor or "",
"description": f"{args.name}(由 algpkg init 生成)",
"models": [{
"name": "default",
"file": f"models/{args.id}.bmodel",
"precision": "",
"resolution": [640, 640],
"batch": 1,
"default": True,
}],
"input": {"width": 640, "height": 640, "color": "rgb", "layout": "planar",
"mean": [0, 0, 0], "std": [255, 255, 255]},
"output": {"type": "boxes", "format": "N6", "classes_file": "assets/labels.txt"},
"params_schema": [
{"key": "conf", "type": "float", "default": 0.25, "min": 0.01, "max": 0.99,
"step": 0.01, "label": "置信度阈值", "desc": "低于该分数的目标被丢弃"},
{"key": "interval", "type": "float", "default": 0.2, "min": 0.02, "max": 60,
"step": 0.02, "label": "抽帧间隔(秒)", "desc": "两次推理之间的最小间隔"},
],
"defaults": {"conf": 0.25, "interval": 0.2},
"alarm": {"type_template": "{class}", "cooldown_key": "algo",
"need_snapshot": True, "need_record": True},
"requires": {"license_features": [], "devices": [], "frameworks": ["sail"]},
"ui": {"order": 100, "tags": [], "icon": "mdi-puzzle"},
"legacy": {},
}
def _scaffold_code(args: argparse.Namespace) -> str:
return f'''"""算法包实现:{args.name}({args.id})。
只依赖 `video_algpkg_api`(worker 提供的稳定接口)与设备框架(sophon.sail / numpy)。
本文件由 `algpkg init` 生成,改完即可打包:
workers/video/algpkg build <本目录> --out {args.id}-{args.version}.mbank
"""
from __future__ import annotations
import numpy as np
import sophon.sail as sail
from video_algpkg_api import Algorithm as AlgorithmBase, AlgoResult
class Algorithm(AlgorithmBase):
"""{args.name}。"""
kind = "{args.kind}"
#: 引擎句柄:load() 赋值、close() 释放。类级声明为非 Optional,infer() 里可直接用
#: (不必判空);close() 置 None 与声明冲突,那处显式 `type: ignore`——运行时语义就是"丢引用"。
_net: sail.Engine
def load(self) -> None:
# 模型字节来自算法包自身(.mbank 内的 models/**),不落盘:
data = self.ctx.model_bytes()
size = len(data)
self.ctx.logger.info("加载模型 %s(%d 字节, conf=%s)",
self.ctx.model_name, size, self.ctx.param("conf"))
self._net = sail.Engine(data, size, self.ctx.dev_id, sail.IOMode.SYSO)
self._graph = self._net.get_graph_names()[0]
# TODO: 按模型实际输入做前处理(可参考 worker 的 YOLO26._preprocess)
self._handle = sail.Handle(self.ctx.dev_id)
self._bmcv = sail.Bmcv(self._handle)
def infer(self, frame) -> AlgoResult:
"""单帧推理。返回 AlgoResult(boxes 为 (N,6):x1,y1,x2,y2,score,cls)。
TODO: 前处理 → self._net.process(...) → 后处理(NMS/反变换),
再 return AlgoResult(boxes=boxes, labels=[...])。
下面这行只做"链路可跑通"的占位(空结果),替换为真实实现。
"""
_ = frame
boxes = np.zeros((0, 6), dtype=np.float32)
return AlgoResult(boxes=boxes)
def close(self) -> None:
self._net = None # type: ignore[assignment]
'''
def _scaffold_readme(args: argparse.Namespace, code_package: str) -> str:
return f"""# {args.name}(算法包 {args.id})
由 `algpkg init` 生成。目录结构:
```
manifest.json 清单(算法元数据 / 模型 / 参数表单 / 告警策略)
{code_package}/ 包内代码(入口类 Algorithm,只依赖 video_algpkg_api)
models/ 随包模型(.bmodel;打包时进包,不单独分发)
assets/ 可选资源(labels.txt 类别名等)
```
## 开发步骤
1. 把模型放到 `models/`(或打包时 `--model 名=<磁盘路径>` 指定);
2. 实现 `{code_package}/__init__.py` 的 `load()`(加载模型字节)与 `infer(frame)`(单帧结果);
3. 打包:`workers/video/algpkg build . --out {args.id}-{args.version}.mbank`;
4. 检查:`workers/video/algpkg inspect {args.id}-{args.version}.mbank`;
5. 安装到设备:`POST /api/v1/video/algorithm/install`(算法管理页上传亦可),
随后在任务表单里选择本算法(参数表单由 `manifest.params_schema` 生成)。
## 接口约定(video_algpkg_api)
- `ctx.model_bytes(name=None)` / `ctx.read_asset(path)`:读包内文件字节(模型不落盘);
多模型包(检测 + 二次判定)按名字取第二份:`ctx.model_bytes("fp_filter")`;
- `ctx.param("conf", 默认值)`:任务参数(已按 manifest 校验/补默认值);
- `infer(frame)` 返回 `AlgoResult(boxes=(N,6), counts=...)`;`draw()` 可选自定义叠加;
- `load()` 抛异常 → 任务转 `failed` 且错误可读(含包 id/版本);
- 线程模型:同一实例不会被并发调用。
"""
# ====================================================================== 本地测试台
def cmd_check(args: argparse.Namespace) -> int:
from . import testkit
try:
report = testkit.check(args.mbank, key=args.encrypt, deep=args.deep,
extra_path=args.extra_path)
except Exception as exc:
print(f"✗ 检查失败: {exc}", file=sys.stderr)
return 1
if args.json:
print(json.dumps(report, ensure_ascii=False, indent=2, default=str))
return 0 if report["ok"] else 1
manifest = report.get("manifest") or {}
print(f"算法包: {Path(args.mbank).name}({manifest.get('algo_id')}@{manifest.get('version')})")
for item in report["checks"]:
print(f" {'✓' if item['ok'] else '✗'} {item['name']}"
+ (f" — {item['detail']}" if item["detail"] else ""))
if not report.get("device"):
print(" (本机无 sophon.sail:跳过实跑;`algpkg run` 需在设备上执行)")
print("✓ 检查通过" if report["ok"] else "✗ 检查未通过")
return 0 if report["ok"] else 1
def cmd_run(args: argparse.Namespace) -> int:
from . import testkit
params = {}
if args.params:
try:
params = json.loads(args.params)
except json.JSONDecodeError as exc:
print(f"✗ --params 不是合法 JSON: {exc}", file=sys.stderr)
return 2
try:
report = testkit.run(args.mbank, source=args.source, params=params, model=args.model,
frames=args.frames, warmup=args.warmup, dev_id=args.dev_id,
key=args.encrypt, save_annotated=args.save_annotated,
extra_path=args.extra_path)
except Exception as exc:
print(f"✗ 运行失败: {exc}", file=sys.stderr)
return 1
summary = report.summary()
if args.json:
print(json.dumps(summary, ensure_ascii=False, indent=2, default=str))
else:
times = summary["infer_ms"]
print(f"算法: {summary['algo_id']}@{summary['version']}(模型 {summary['model']})")
print(f"源: {summary['source']} · 帧数 {summary['frames']} · 单帧耗时 "
f"avg {times['avg']}ms / min {times['min']} / max {times['max']}")
print(f"逐帧检出: {summary['detections']}")
if summary["annotated"]:
print(f"已存叠加图 {len(summary['annotated'])} 张 → {Path(summary['annotated'][0]).parent}")
return 0
def cmd_compare(args: argparse.Namespace) -> int:
from . import testkit
try:
a = testkit.run(args.a, source=args.source, frames=args.frames, dev_id=args.dev_id,
extra_path=args.extra_path)
b = testkit.run(args.b, source=args.source, frames=args.frames, dev_id=args.dev_id,
extra_path=args.extra_path)
result = testkit.compare(a, b, tolerance=args.tolerance)
except Exception as exc:
print(f"✗ 对比失败: {exc}", file=sys.stderr)
return 1
if args.json:
print(json.dumps(result, ensure_ascii=False, indent=2, default=str))
else:
print(f"A: {result['a']} B: {result['b']}")
for frame in result["frames"]:
mark = "✓" if frame["identical"] else "✗"
print(f" {mark} 帧{frame['frame']}: A 检出 {frame['a']} / B 检出 {frame['b']}")
print("逐位一致 ✓" if result["identical"] else "存在差异 ✗(--tolerance 可放宽浮点比较)")
return 0 if result["identical"] else 1
# ====================================================================== 入口
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(prog="algpkg", description="算法包工具链(M8.4)")
sub = parser.add_subparsers(dest="command", required=True)
build = sub.add_parser("build", help="打包源码目录 → .mbank")
build.add_argument("src", help="源码目录(含 manifest.json 与代码目录)")
build.add_argument("--out", required=True, help="产物路径(*.mbank)")
build.add_argument("--model", action="append", metavar="名=路径",
help="模型磁盘来源(可多次;缺省用源码目录内的 models/<文件>)")
build.add_argument("--encrypt", metavar="KEY", help="Fernet 密钥:加密包内代码")
build.add_argument("--pyarmor", action="store_true", help="用 PyArmor 混淆代码")
build.add_argument("--sign", metavar="KEY", help="签名密钥(HMAC-SHA256,对称)")
build.add_argument("--key-id", metavar="ID", help="签名 key_id(如 ci-2026)")
build.add_argument("--no-compress", action="store_true", help="不做内容压缩")
build.add_argument("--no-replace", action="store_true", help="产物已存在时报错")
build.add_argument("--json", action="store_true", help="输出 JSON 摘要")
build.add_argument("--quiet", action="store_true")
build.set_defaults(func=cmd_build)
inspect = sub.add_parser("inspect", help="查看包内容(清单/模型/快照/签名)")
inspect.add_argument("mbank")
inspect.add_argument("--encrypt", metavar="KEY")
inspect.add_argument("--json", action="store_true")
inspect.set_defaults(func=cmd_inspect)
verify = sub.add_parser("verify", help="校验包(结构/模型存在性/可选验签)")
verify.add_argument("mbank")
verify.add_argument("--encrypt", metavar="KEY")
verify.add_argument("--signature-key", metavar="KEY", help="签名密钥(验签;HMAC 对称)")
verify.add_argument("--snapshot", help="指定版本(快照 id 或名字)")
verify.add_argument("--json", action="store_true", help="输出 JSON(默认已是结构化输出)")
verify.set_defaults(func=cmd_verify)
listing = sub.add_parser("list", help="列出目录内的算法包")
listing.add_argument("dir")
listing.add_argument("--encrypt", metavar="KEY")
listing.set_defaults(func=cmd_list)
check = sub.add_parser("check", help="静态检查一个包(清单/结构/试加载;不需 worker)")
check.add_argument("mbank")
check.add_argument("--deep", action="store_true", help="再实例化入口类并加载默认模型(需设备)")
check.add_argument("--encrypt", metavar="KEY")
check.add_argument("--json", action="store_true")
check.add_argument("--extra-path", action="append", metavar="DIR",
help="额外包搜索路径(特权包才需要:依赖 worker 内部模块;可多次)")
check.set_defaults(func=cmd_check)
run_cmd = sub.add_parser("run", help="在设备上跑一个包(图片/视频)")
run_cmd.add_argument("mbank")
run_cmd.add_argument("--source", required=True, help="图片或视频路径")
run_cmd.add_argument("--params", metavar="JSON", help='任务参数,如 \'{"conf":0.3}\'')
run_cmd.add_argument("--model", help="指定包内模型名(缺省用 default)")
run_cmd.add_argument("--frames", type=int, default=5, help="取样帧数(默认 5)")
run_cmd.add_argument("--warmup", type=int, default=2, help="预热帧数(不计入耗时)")
run_cmd.add_argument("--dev-id", type=int, default=0)
run_cmd.add_argument("--save-annotated", metavar="DIR", help="把每帧叠加结果存图到目录")
run_cmd.add_argument("--encrypt", metavar="KEY")
run_cmd.add_argument("--json", action="store_true")
run_cmd.add_argument("--extra-path", action="append", metavar="DIR",
help="额外包搜索路径(特权包才需要;可多次)")
run_cmd.set_defaults(func=cmd_run)
cmp_cmd = sub.add_parser("compare", help="两个包/版本对拍(逐帧检出 + 逐位一致)")
cmp_cmd.add_argument("a")
cmp_cmd.add_argument("b")
cmp_cmd.add_argument("--source", required=True)
cmp_cmd.add_argument("--frames", type=int, default=5)
cmp_cmd.add_argument("--dev-id", type=int, default=0)
cmp_cmd.add_argument("--tolerance", type=float, default=0.0, help="浮点容差(默认逐位)")
cmp_cmd.add_argument("--json", action="store_true")
cmp_cmd.add_argument("--extra-path", action="append", metavar="DIR",
help="额外包搜索路径(特权包才需要;可多次)")
cmp_cmd.set_defaults(func=cmd_compare)
init = sub.add_parser("init", help="生成算法包脚手架")
init.add_argument("dir", help="生成目录")
init.add_argument("--id", required=True, help="算法 id(小写字母/数字/._-)")
init.add_argument("--name", required=True, help="显示名(如:抽烟检测)")
init.add_argument("--version", default="1.0.0")
init.add_argument("--kind", default="detector", choices=["detector", "classifier", "counter", "tracker"])
init.add_argument("--vendor", default="")
init.add_argument("--code-package", default="", help="包内代码模块名(缺省由 id 推导)")
init.add_argument("--force", action="store_true")
init.set_defaults(func=cmd_init)
return parser
def main(argv: list[str] | None = None) -> int:
args = build_parser().parse_args(argv)
try:
return int(args.func(args) or 0)
except KeyboardInterrupt: # pragma: no cover
return 130
if __name__ == "__main__":
raise SystemExit(main())
+400
View File
@@ -0,0 +1,400 @@
"""算法包装载器(M8.1):打开 `.mbank`(svfs 包)→ 读 manifest → 挂载包内代码 → 取模型字节。
`.mbank` 的双重身份(《算法包与多算法任务设计》§3 取证):
- 对 `sqlite_vfs` 来说它就是一个**只读虚拟文件系统**(`manifest.json` / `models/*.bmodel`
都是普通文件,用 `SQLiteVFS.read_file()` 读字节,**不落盘**);
- 对 `module_bank` 来说它是**模块银行**(`modules/<code_package>/**`,`install_importer()`
挂进 ``sys.meta_path``,包内代码即可 `import`),装载前可验签。
两条容易踩的实现约束(本模块已处理):
1. **svfs 句柄自带线程约束**:`SQLiteVFS` 的连接默认 `check_same_thread=True`,
而 `module_bank` 的查找器/加载器正是用它读字节码 —— 于是"启动时挂载、任务线程里首次
import"会抛 `ProgrammingError`。本模块的对策:**挂载时把所有模块一次性 import 完**
(`_eager_import`,同时也是"装载前试加载"验收项),此后模块都在 ``sys.modules`` 里,
任务线程不再触发查找器读盘;
2. **挂载/卸载必须在同一线程**(查找器的连接在创建线程建立):由注册表在**专用单线程
执行器**上串行执行 mount/unmount(见 registry.py 的 `_lifecycle`)。
包内自有的读取句柄(读 manifest / 模型字节)用 `check_same_thread=False` + 独占锁,
可以从任意任务线程调用。
"""
from __future__ import annotations
import hashlib
import importlib
import logging
import sys
import threading
from pathlib import Path
from .api import AlgoContext, Algorithm, AlgoResult, install_sdk_alias
from .manifest import MANIFEST_NAME, Manifest, ManifestError
logger = logging.getLogger(__name__)
#: 包内预留的文件名(不与模型/资源混淆)
_RESERVED = {MANIFEST_NAME, "algorithm", "code"}
def purge_namespace(code_package: str) -> int:
"""清理 `sys.modules` 里某个包代码命名空间的全部模块(返回清理数)。"""
prefix = str(code_package or "").strip()
if not prefix:
return 0
removed = 0
for name in [n for n in list(sys.modules) if n == prefix or n.startswith(prefix + ".")]:
sys.modules.pop(name, None)
removed += 1
return removed
def namespace_loaded(code_package: str) -> bool:
"""某个包代码命名空间当前是否有模块在 `sys.modules`(判断能否安全清理)。"""
prefix = str(code_package or "").strip()
return bool(prefix) and any(n == prefix or n.startswith(prefix + ".")
for n in sys.modules)
class AlgoPackageError(RuntimeError):
"""算法包不可用(格式/清单/代码/签名任一环节失败)——消息面向"怎么修"。"""
def sha256_file(path: str | Path, chunk: int = 1 << 20) -> str:
digest = hashlib.sha256()
with open(path, "rb") as handle:
while True:
block = handle.read(chunk)
if not block:
break
digest.update(block)
return digest.hexdigest()
class AlgoPackage:
"""一个 `.mbank` 算法包的装载句柄(读元数据/模型/资源 + 挂载代码 + 构造实例)。
典型用法(注册表内)::
pkg = AlgoPackage("/opt/algpkgs/smoke-detect-1.0.0.mbank")
pkg.open() # 读 manifest(只读 VFS 句柄,本对象持有)
pkg.mount() # 挂代码(必须在注册表的单线程执行器上调用)
algo = pkg.new_instance("smoke_fp16", params, dev_id=0) # 任意线程
pkg.unmount() # 卸载(同一线程)
pkg.close()
"""
def __init__(self, path: str | Path, *, key: str | None = None, snapshot=None,
signature_key: str | None = None, signature_key_id: str | None = None,
allow_unsigned: bool = True, source: str = "disk",
sha256: str = "", enabled: bool = True):
self.path = Path(path)
self.filename = self.path.name
self.source = source # disk | upload | builtin
self.sha256 = sha256 # 安装时算好;scan 时可延迟计算
self._key = key
self._snapshot = snapshot
self._signature_key = signature_key
self._signature_key_id = signature_key_id
self._allow_unsigned = bool(allow_unsigned)
self.manifest: Manifest | None = None
self.signature: dict = {} # 验签报告(装载时填充)
self.last_error: str | None = None
self.mounted = False
self._vfs = None # 自有只读句柄(读文件字节)
self._finder = None # module_bank 的查找器(挂载后非 None)
self._lock = threading.RLock() # 保护 _vfs 读写
self._import_lock = threading.RLock() # 保护 importlib 调用(装载/卸载交错)
# ================================================================== 元数据
@property
def algo_id(self) -> str:
return self.manifest.id if self.manifest else ""
@property
def version(self) -> str:
return self.manifest.version if self.manifest else ""
@property
def size_bytes(self) -> int:
try:
return self.path.stat().st_size
except OSError:
return 0
def open(self) -> "AlgoPackage":
"""打开只读 VFS 并解析 manifest(可重复调用;失败抛 AlgoPackageError)。"""
if self.manifest is not None:
return self
if not self.path.is_file():
raise AlgoPackageError(f"算法包不存在: {self.path}")
try:
from sqlite_vfs.core import SQLiteVFS
except ImportError as exc: # pragma: no cover - 依赖缺失
raise AlgoPackageError(f"缺少 sqlite_vfs 依赖(module-bank): {exc}") from exc
try:
# check_same_thread=False + 自有锁:任务线程要读模型字节(见模块文档)
self._vfs = SQLiteVFS(str(self.path), readonly=True, check_same_thread=False)
except Exception as exc:
raise AlgoPackageError(f"不是有效的 svfs 包({self.path.name}): {exc}") from exc
try:
raw = self._read_raw(MANIFEST_NAME)
except FileNotFoundError as exc:
self._close_vfs()
raise AlgoPackageError(
f"包内缺少 {MANIFEST_NAME}({self.path.name})——不是算法包,或打包时漏了清单"
) from exc
try:
import orjson
self.manifest = Manifest.parse(orjson.loads(raw), source=self.filename)
except ManifestError as exc:
self._close_vfs()
raise AlgoPackageError(f"manifest 校验失败({self.path.name}): {exc}") from exc
except Exception as exc:
self._close_vfs()
raise AlgoPackageError(f"manifest 解析失败({self.path.name}): {exc}") from exc
# 模型文件存在性/非空检查(装载前的静态校验)
for spec in self.manifest.models:
try:
size = len(self._read_raw(spec.file))
except FileNotFoundError as exc:
self._close_vfs()
raise AlgoPackageError(
f"manifest 声明的模型文件不在包内: {spec.file}({self.path.name})"
) from exc
if size <= 0:
self._close_vfs()
raise AlgoPackageError(f"包内模型文件为空: {spec.file}")
return self
# ================================================================== 文件读取
def _read_raw(self, rel_path: str) -> bytes:
"""按包内相对路径读字节(manifest/模型/资源统一入口)。"""
if self._vfs is None:
raise AlgoPackageError("算法包尚未打开(先 open())")
clean = str(rel_path or "").lstrip("/").replace("\\", "/")
if ".." in clean.split("/"):
raise AlgoPackageError(f"非法包内路径: {rel_path!r}")
with self._lock:
return self._vfs.read_file(clean)
def read_file(self, rel_path: str) -> bytes:
return self._read_raw(rel_path)
def read_model(self, model_name: str | None = None) -> tuple[bytes, dict]:
"""读选中模型的字节(返回 (bytes, 模型元信息))。"""
spec = self.require_manifest().model(model_name)
data = self._read_raw(spec.file)
if not data:
raise AlgoPackageError(f"模型文件为空: {spec.file}")
return data, spec.to_dict()
def read_asset(self, rel_path: str) -> bytes:
return self._read_raw(rel_path)
def list_files(self) -> list[dict]:
"""包内文件清单(管理页展示用;不含 modules/ 下的字节码)。"""
if self._vfs is None:
return []
with self._lock:
entries = [e for e in self._vfs.all_entries() if not e.get("is_directory")]
items = []
for entry in entries:
path = entry.get("path", "")
if str(path).startswith("modules/"):
continue
items.append({"path": path, "size": int(entry.get("file_size") or 0)})
return sorted(items, key=lambda i: i["path"])
def require_manifest(self) -> Manifest:
if self.manifest is None:
self.open()
assert self.manifest is not None
return self.manifest
# ================================================================== 挂载 / 卸载
#
# 注意:mount()/unmount() 必须在**同一个线程**调用(module_bank 查找器的 sqlite 连接
# 在创建线程建立)。注册表用专用单线程执行器保证这一点,见 registry.py。
def mount(self) -> "AlgoPackage":
"""挂载包内代码到 sys.meta_path,并**试加载**全部模块(本线程内完成)。"""
manifest = self.require_manifest()
if self.mounted:
return self
try:
from module_bank import PythonToSQLite
from module_bank.layout import SignatureVerificationError
except ImportError as exc: # pragma: no cover
raise AlgoPackageError(f"缺少 module_bank 依赖: {exc}") from exc
with self._import_lock:
try:
packer = PythonToSQLite(str(self.path), key=self._key, readonly=True,
snapshot=self._snapshot)
except Exception as exc:
raise AlgoPackageError(f"打开算法包失败({self.path.name}): {exc}") from exc
try:
finder = packer.install_importer(
snapshot=self._snapshot,
signature_key=self._signature_key,
signature_key_id=self._signature_key_id,
allow_unsigned=self._allow_unsigned,
)
except SignatureVerificationError as exc:
raise AlgoPackageError(f"签名校验未通过,拒绝装载: {exc}") from exc
except Exception as exc:
raise AlgoPackageError(f"装载导入器失败({self.path.name}): {exc}") from exc
self._finder = finder
self.mounted = True
try:
self.signature = self._verify_signature_report()
self._eager_import(manifest)
except BaseException:
self.unmount()
raise
logger.info("[algpkg] 已挂载 %s@%s(代码 %s,模块 %s 个)",
manifest.id, manifest.version, manifest.code_package,
len(getattr(self._finder, "module_cache", {}) or {}))
return self
def _verify_signature_report(self) -> dict:
"""记录验签报告(管理页展示"是否签过/签名者";未配置签名密钥时为空)。"""
key = self._signature_key # 签名密钥独立于(可选的)加密密钥
if not key or self._finder is None:
return {}
try:
return dict(self._finder.vfs.verify_signature(key, key_id=self._signature_key_id))
except Exception as exc: # pragma: no cover - 报告失败不影响装载
logger.debug("[algpkg] 验签报告读取失败: %s", exc)
return {}
def _eager_import(self, manifest: Manifest) -> None:
"""把所有模块一次性 import 完(线程约束 + 试加载验收,见模块文档)。"""
module_cache = dict(getattr(self._finder, "module_cache", {}) or {})
if not module_cache:
raise AlgoPackageError(
f"算法包 {manifest.id} 内没有可导入的模块(modules/ 为空)"
f"——打包时是否漏了代码目录?"
)
install_sdk_alias()
# 先父后子:包(__init__)优先,其余按名字长度排序(父模块先于子模块)
for name in sorted(module_cache, key=lambda n: (n.count("."), n)):
try:
importlib.import_module(name)
except Exception as exc:
raise AlgoPackageError(
f"算法包代码导入失败: {name}: {type(exc).__name__}: {exc}"
) from exc
module = importlib.import_module(manifest.entry_module)
entry = getattr(module, manifest.entry_class, None)
if entry is None:
raise AlgoPackageError(
f"入口类不存在: {manifest.entry_module}.{manifest.entry_class}"
f"(manifest.entry={manifest.entry!r})"
)
if not callable(entry):
raise AlgoPackageError(f"入口不可调用: {manifest.entry!r}")
def unmount(self, *, purge_modules: bool = True) -> None:
"""从 sys.meta_path 摘除(必须在挂载线程调用)。
`purge_modules`:是否同时清理 `sys.modules` 里本包代码命名空间——**同一个 `code_package`
可能同时被"同 id 的另一个版本"的包装载**(如 probe 试用 v1.1.0 时 v1.0.0 还在用),
此时清理会让另一份包的模块消失,任务线程再 import 就会触发查找器的跨线程读
(module_bank 的 svfs 连接有线程约束 → ProgrammingError)。故清理由
:class:`~.registry.AlgoRegistry` 按"命名空间是否仍被其它已挂载包占用"决定。
"""
with self._import_lock:
finder, self._finder = self._finder, None
self.mounted = False
if finder is None:
return
manifest = self.manifest
try:
if finder in sys.meta_path:
sys.meta_path.remove(finder)
except ValueError: # pragma: no cover - 已被移除
pass
if purge_modules and manifest is not None:
purge_namespace(manifest.code_package)
finder.close()
logger.info("[algpkg] 已卸载 %s", manifest.id if manifest else self.filename)
# ================================================================== 实例构造
def new_instance(self, model_name: str | None = None, params: dict | None = None,
dev_id: int = 0, logger_: logging.Logger | None = None) -> Algorithm:
"""按 manifest 入口构造并加载一个算法实例(任意线程可调用,实例不再并发调用)。"""
manifest = self.require_manifest()
spec = manifest.model(model_name)
checked = manifest.check_params(params)
ctx = AlgoContext(
algo_id=manifest.id, version=manifest.version, model_name=spec.name,
params=checked, dev_id=dev_id, meta=spec.to_dict(), kind=manifest.kind,
output=manifest.output, logger=logger_ or logger, reader=self.read_file,
models=[item.to_dict() for item in manifest.models], # 多模型包按名取用
)
module = importlib.import_module(manifest.entry_module)
entry = getattr(module, manifest.entry_class)
instance = entry(ctx)
if isinstance(instance, Algorithm):
return instance.ensure_loaded()
# 鸭子类型兜底:外部包可能自带基类/依赖旧版 SDK;只要有 infer() 就放行
if not callable(getattr(instance, "infer", None)):
raise AlgoPackageError(
f"入口 {manifest.entry} 的实例没有 infer()(应为 video_algpkg_api.Algorithm 子类)"
)
if callable(getattr(instance, "load", None)):
instance.load()
return instance
# ================================================================== 展示与关闭
def info(self, *, enabled: bool = True, active: bool = True, instances: int = 0,
builtin: bool = False) -> dict:
manifest = self.require_manifest()
return {
**manifest.info(),
"filename": self.filename,
"path": str(self.path),
"size_bytes": self.size_bytes,
"sha256": self.sha256,
"source": self.source,
"builtin": builtin,
"enabled": enabled,
"active": active,
"mounted": self.mounted,
"instances": instances,
"signed": bool(self.signature.get("signature")),
"signature": {k: self.signature.get(k) for k in ("key_id", "algorithm", "ok")
if k in self.signature},
"error": self.last_error,
}
def close(self) -> None:
"""卸载(若已挂载)+ 关闭自有 VFS 句柄。"""
self.unmount()
self._close_vfs()
def _close_vfs(self) -> None:
vfs, self._vfs = self._vfs, None
if vfs is not None:
try:
with self._lock:
vfs.close()
except Exception as exc: # pragma: no cover
logger.debug("[algpkg] 关闭 VFS 异常 %s: %s", self.filename, exc)
def __repr__(self) -> str: # pragma: no cover - 调试用
return f"<AlgoPackage {self.algo_id}@{self.version} {self.filename}>"
+350
View File
@@ -0,0 +1,350 @@
"""算法包清单(manifest.json)解析与校验(M8.1,字段见《算法包与多算法任务设计》§4.1)。
职责边界:
- **打包端**(algpkg build)按本模块的 :func:`build_manifest` 生成清单;
- **装载端**(worker)按 :meth:`Manifest.parse` 校验(字段缺失/类型不符 → 拒绝装载,
错误信息要能直接告诉打包方哪一项不对);
- **任务参数**(params)由 :meth:`Manifest.check_params` 校验并按 schema 补默认值。
字段表(`*` 为必需):
| 字段 | 说明 |
| ---- | ---- |
| `id`* / `name`* / `version`* | 唯一标识(`builtin.yolov26-detect`)、显示名、包版本 |
| `api_version` | 接口版本(缺省 1;> worker 的 ALG_API_VERSION 拒绝装载) |
| `kind` | `detector` / `classifier` / `counter` / `tracker`(决定 UI 与结果渲染) |
| `entry` | 入口类:`Algorithm` 或 `submodule:Algorithm`(相对包代码根,缺省 `Algorithm`) |
| `code_package` | 包代码的模块名(`modules/<code_package>/…`;缺省由 id 推导 `alg_<id>`) |
| `models`* | `[{name, file, precision, resolution, batch, default}]`,含唯一 default |
| `input` | `{width,height,color,layout,mean,std}`(前处理契约,展示用) |
| `output` | `{type: boxes|count|counter, format, classes_file}` |
| `params_schema` | `[{key,type,default,min,max,step,label,desc,options}]`(任务参数表单来源) |
| `defaults` | 默认参数(conf/nms/interval/draw 等,任务未显式给值时用) |
| `alarm` | `{type_template,cooldown_key,need_snapshot,need_record}`(告警生成策略) |
| `requires` | `{license_features[],devices[],frameworks[]}`(授权/设备能力校验) |
| `ui` | `{order,tags,icon}`(算法管理页展示) |
| `vendor` / `description` / `built_at` | 溯源信息 |
| `legacy` | 兼容映射:`{algorithm: "yolo", default_model: "x.bmodel"}`(存量任务迁移用) |
"""
from __future__ import annotations
import re
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any
from .api import ALG_API_VERSION, KINDS, OUTPUT_TYPES
MANIFEST_NAME = "manifest.json"
MANIFEST_FORMAT = 1
DEFAULT_ENTRY = "Algorithm"
#: 算法包 id:小写字母/数字开头,允许 . _ -
ID_RE = re.compile(r"^[a-z0-9][a-z0-9._-]*$")
#: 包内相对路径(禁绝对路径与 .. 越界)
_REL_OK = re.compile(r"^[^/\\][^\\]*$")
#: 参数类型 → 取值转换/校验
_PARAM_TYPES = ("int", "float", "bool", "str", "enum")
class ManifestError(ValueError):
"""清单不合法(打包/装载两侧都用它,消息面向"打包方怎么改")。"""
def code_package_for(algo_id: str) -> str:
"""由 algo_id 推导包内代码模块名(`builtin.yolov26-detect` → `alg_builtin_yolov26_detect`)。"""
return "alg_" + re.sub(r"\W+", "_", str(algo_id or "")).strip("_").lower()
def _require(condition: bool, message: str) -> None:
if not condition:
raise ManifestError(message)
def _check_rel_path(value: str, field_name: str) -> str:
text = str(value or "").strip().replace("\\", "/")
_require(bool(text), f"{field_name} 不能为空")
_require(not text.startswith("/") and ".." not in text.split("/"),
f"{field_name} 必须是包内相对路径(不得以 / 开头或含 ..): {text!r}")
return text
@dataclass
class ModelSpec:
"""包内模型条目(一份包可带多份精度/核数不同的模型,任务选其一)。"""
name: str
file: str
precision: str = ""
resolution: list[int] | None = None
batch: int = 1
default: bool = False
@classmethod
def parse(cls, raw: Any, index: int) -> "ModelSpec":
_require(isinstance(raw, dict), f"models[{index}] 必须是对象")
name = str(raw.get("name") or "").strip()
_require(bool(name), f"models[{index}].name 不能为空")
file = _check_rel_path(raw.get("file") or "", f"models[{index}].file")
resolution = raw.get("resolution")
if resolution is not None:
_require(isinstance(resolution, (list, tuple)) and len(resolution) == 2,
f"models[{index}].resolution 必须是 [width, height]")
resolution = [int(resolution[0]), int(resolution[1])]
return cls(
name=name, file=file,
precision=str(raw.get("precision") or "").strip(),
resolution=resolution,
batch=int(raw.get("batch") or 1),
default=bool(raw.get("default", False)),
)
def to_dict(self) -> dict:
return {"name": self.name, "file": self.file, "precision": self.precision,
"resolution": self.resolution, "batch": self.batch, "default": self.default}
@dataclass
class Manifest:
"""已校验的算法包清单(不可变视图 + 查询辅助)。"""
id: str
name: str
version: str
api_version: int = ALG_API_VERSION
kind: str = "detector"
entry: str = DEFAULT_ENTRY
code_package: str = ""
vendor: str = ""
description: str = ""
built_at: str = ""
models: list[ModelSpec] = field(default_factory=list)
input: dict = field(default_factory=dict)
output: dict = field(default_factory=dict)
params_schema: list[dict] = field(default_factory=list)
defaults: dict = field(default_factory=dict)
alarm: dict = field(default_factory=dict)
requires: dict = field(default_factory=dict)
ui: dict = field(default_factory=dict)
legacy: dict = field(default_factory=dict)
raw: dict = field(default_factory=dict)
# ------------------------------------------------------------------ 解析
@classmethod
def parse(cls, data: Any, *, source: str = "") -> "Manifest":
where = f"({source})" if source else ""
_require(isinstance(data, dict), f"manifest 必须是 JSON 对象{where}")
algo_id = str(data.get("id") or "").strip()
_require(bool(algo_id), f"manifest.id 缺失{where}")
_require(bool(ID_RE.match(algo_id)),
f"manifest.id 只能用小写字母/数字/._-(收到 {algo_id!r}){where}")
name = str(data.get("name") or "").strip() or algo_id
version = str(data.get("version") or "").strip()
_require(bool(version), f"manifest.version 缺失{where}(建议语义化版本,如 1.0.0)")
api_version = int(data.get("api_version") or ALG_API_VERSION)
_require(api_version <= ALG_API_VERSION,
f"算法包 api_version={api_version} 高于本 worker 支持的 {ALG_API_VERSION}"
f"{where}——请升级 worker 或改用低版本 api 重新打包")
_require(api_version >= 1, f"manifest.api_version 非法: {api_version}{where}")
kind = str(data.get("kind") or "detector").strip().lower()
_require(kind in KINDS, f"manifest.kind 必须是 {'/'.join(KINDS)} 之一(收到 {kind!r}){where}")
models_raw = data.get("models")
_require(isinstance(models_raw, list) and len(models_raw) > 0,
f"manifest.models 必须是非空数组{where}")
models = [ModelSpec.parse(item, i) for i, item in enumerate(models_raw)]
names = [m.name for m in models]
_require(len(set(names)) == len(names), f"manifest.models[].name 重复{where}: {names}")
if not any(m.default for m in models):
models[0].default = True
else:
first_default = next(i for i, m in enumerate(models) if m.default)
for i, m in enumerate(models): # 多份 default 只认第一个
m.default = i == first_default
output = data.get("output") or {}
_require(isinstance(output, dict), f"manifest.output 必须是对象{where}")
out_type = str(output.get("type") or ({"counter": "counter"}.get(kind, "boxes")))
_require(out_type in OUTPUT_TYPES,
f"manifest.output.type 必须是 {'/'.join(OUTPUT_TYPES)} 之一(收到 {out_type!r}){where}")
output = {**output, "type": out_type}
params_schema = data.get("params_schema") or []
_require(isinstance(params_schema, list), f"manifest.params_schema 必须是数组{where}")
for i, item in enumerate(params_schema):
_require(isinstance(item, dict), f"params_schema[{i}] 必须是对象{where}")
key = str(item.get("key") or "").strip()
_require(bool(key), f"params_schema[{i}].key 不能为空{where}")
ptype = str(item.get("type") or "float").strip().lower()
_require(ptype in _PARAM_TYPES,
f"params_schema[{i}].type 必须是 {'/'.join(_PARAM_TYPES)} 之一(收到 {ptype!r}){where}")
if ptype == "enum":
options = item.get("options")
_require(isinstance(options, list) and len(options) > 0,
f"params_schema[{i}]: type=enum 必须给 options{where}")
schema_keys = [str(i.get("key")) for i in params_schema]
_require(len(set(schema_keys)) == len(schema_keys),
f"params_schema[].key 重复{where}: {schema_keys}")
defaults = data.get("defaults") or {}
_require(isinstance(defaults, dict), f"manifest.defaults 必须是对象{where}")
for section in ("input", "alarm", "requires", "ui", "legacy"):
value = data.get(section) or {}
_require(isinstance(value, dict), f"manifest.{section} 必须是对象{where}")
entry = str(data.get("entry") or DEFAULT_ENTRY).strip() or DEFAULT_ENTRY
code_package = str(data.get("code_package") or "").strip() or code_package_for(algo_id)
return cls(
id=algo_id, name=name, version=version, api_version=api_version, kind=kind,
entry=entry, code_package=code_package,
vendor=str(data.get("vendor") or "").strip(),
description=str(data.get("description") or "").strip(),
built_at=str(data.get("built_at") or "").strip(),
models=models, input=dict(data.get("input") or {}), output=output,
params_schema=[dict(i) for i in params_schema], defaults=dict(defaults),
alarm=dict(data.get("alarm") or {}), requires=dict(data.get("requires") or {}),
ui=dict(data.get("ui") or {}), legacy=dict(data.get("legacy") or {}),
raw=dict(data),
)
# ------------------------------------------------------------------ 查询
def model(self, name: str | None = None) -> ModelSpec:
"""按名取模型(None/空 = 默认模型);不存在抛 ManifestError。"""
wanted = str(name or "").strip()
if not wanted:
return self.default_model()
for spec in self.models:
if spec.name == wanted or spec.file == wanted:
return spec
raise ManifestError(
f"算法包 {self.id} 内没有模型 {wanted!r}(可选:"
f"{', '.join(m.name for m in self.models)})"
)
def default_model(self) -> ModelSpec:
return next((m for m in self.models if m.default), self.models[0])
@property
def entry_module(self) -> str:
"""入口类所在模块(`submodule:Class` 形式时取 submodule,否则包代码根)。"""
module = self.entry.split(":", 1)[0] if ":" in self.entry else ""
module = module.strip()
return f"{self.code_package}.{module}" if module else self.code_package
@property
def entry_class(self) -> str:
"""入口类名。"""
return self.entry.split(":", 1)[1].strip() if ":" in self.entry else self.entry
def schema_defaults(self) -> dict:
"""params_schema 里的默认值(不含 manifest.defaults)。"""
out: dict = {}
for item in self.params_schema:
if "default" in item:
out[str(item["key"])] = item["default"]
return out
# ------------------------------------------------------------------ 任务参数
def check_params(self, params: dict | None) -> dict:
"""校验任务参数并按 schema/defaults 补默认值(返回新 dict)。
规则:schema 声明了类型/范围的键做严格校验(越界/类型不符直接拒绝——
静默截断会让"界面显示 A、实际跑 B");schema 未声明的键原样保留(向前兼容)。
"""
given = dict(params or {})
_require(isinstance(given, dict), "算法参数必须是对象")
merged = {**self.schema_defaults(), **self.defaults, **given}
for item in self.params_schema:
key = str(item["key"])
if key not in merged:
continue
merged[key] = self._coerce(item, merged[key])
return merged
@staticmethod
def _coerce(item: dict, value: Any) -> Any:
key, ptype = str(item["key"]), str(item.get("type") or "float").lower()
label = str(item.get("label") or key)
try:
if ptype == "int":
result: Any = int(value)
elif ptype == "float":
result = float(value)
elif ptype == "bool":
result = bool(value)
elif ptype == "str":
result = str(value)
else: # enum
result = value
options = [o.get("value") if isinstance(o, dict) else o for o in item["options"]]
_require(value in options, f"{label} 必须是 {options} 之一(收到 {value!r})")
return result
except (TypeError, ValueError):
raise ManifestError(f"算法参数 {label} 类型应为 {ptype}(收到 {value!r})") from None
if ptype in ("int", "float"):
if item.get("min") is not None and result < item["min"]:
raise ManifestError(f"算法参数 {label} 不能小于 {item['min']}(收到 {result})")
if item.get("max") is not None and result > item["max"]:
raise ManifestError(f"算法参数 {label} 不能大于 {item['max']}(收到 {result})")
return result
# ------------------------------------------------------------------ 展示
def info(self) -> dict:
"""接口展示用(algorithm.list/get/install 的统一结构,不含运行时字段)。"""
return {
"algo_id": self.id,
"name": self.name,
"version": self.version,
"api_version": self.api_version,
"kind": self.kind,
"vendor": self.vendor,
"description": self.description,
"built_at": self.built_at,
"entry": self.entry,
"code_package": self.code_package,
"models": [m.to_dict() for m in self.models],
"default_model": self.default_model().name,
"input": dict(self.input),
"output": dict(self.output),
"params_schema": [dict(i) for i in self.params_schema],
"defaults": dict(self.defaults),
"alarm": dict(self.alarm),
"requires": dict(self.requires),
"ui": dict(self.ui),
"legacy": dict(self.legacy),
}
def build_manifest(algo_id: str, name: str, version: str, *, models: list[dict],
code_package: str = "", **fields) -> dict:
"""打包端构造清单(algpkg build 用);字段校验交给 :meth:`Manifest.parse`。"""
payload = {
"manifest_format": MANIFEST_FORMAT,
"id": algo_id,
"name": name or algo_id,
"version": version,
"api_version": int(fields.pop("api_version", ALG_API_VERSION)),
"kind": fields.pop("kind", "detector"),
"entry": fields.pop("entry", DEFAULT_ENTRY),
"code_package": code_package or code_package_for(algo_id),
"models": models,
"built_at": fields.pop("built_at", datetime.now(timezone.utc).astimezone()
.isoformat(timespec="seconds")),
}
for key in ("vendor", "description", "input", "output", "params_schema", "defaults",
"alarm", "requires", "ui", "legacy"):
value = fields.pop(key, None)
if value:
payload[key] = value
if fields:
raise ManifestError(f"build_manifest 收到未知字段: {', '.join(sorted(fields))}")
Manifest.parse(payload, source="build") # 打包前先自检:坏清单不许进包
return payload
View File
+306
View File
@@ -0,0 +1,306 @@
"""本地测试台:**不需要 worker**,直接把算法包跑在图片/视频上验证。
覆盖三档(按机器能力自动降级,绝不假装跑过):
| 档位 | 依赖 | 能验证什么 |
| ---- | ---- | ---- |
| 静态 | 无(任意机器) | manifest 合法性、包结构、模型文件存在、代码可导入(`mount` 试加载) |
| 设备 | Sophon NPU(`sophon-arm`) | 真正 `load()` + `infer()`:跑图片/视频、输出检出、可选画框存图、两包结果对比 |
| 性能 | 设备 | 单帧耗时统计(预热后取均值/分位),供"能不能上现场"先做粗判 |
典型用法(也封装成了 `algpkg run`):
from algpkg_sdk import testkit
report = testkit.check("/tmp/my.algo-1.0.0.mbank") # 静态检查
runs = testkit.run("/tmp/my.algo-1.0.0.mbank", source="a.jpg", # 设备上真跑
params={"conf": 0.3}, save_annotated="out/")
testkit.compare(a_runs, b_runs) # 包/版本对拍
"""
from __future__ import annotations
import logging
import os
import sys
import time
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any
from .api import Algorithm, AlgoResult
from .loader import AlgoPackage, AlgoPackageError
from .manifest import Manifest
logger = logging.getLogger(__name__)
#: 额外的包搜索路径(环境变量,冒号分隔)。**只给"特权包"用**:
#: 例如内置包 `builtin.yolov26-detect` 复用 worker 自带的推理实现(`workers.video.algorithm.yolo`),
#: 脱离 worker 环境跑测试时需要把 worker 工程加进来:
#: ALGPKG_EXTRA_PATH=/data/sophon-edge/workers/video algpkg run builtin.mbank --source a.jpg
#: 第三方包应**自带实现**(只依赖 algpkg_sdk / video_algpkg_api),不需要这个开关。
ENV_EXTRA_PATH = "ALGPKG_EXTRA_PATH"
def apply_extra_path(extra: list[str] | None = None) -> list[str]:
"""把额外搜索路径插到 sys.path 最前(幂等);返回实际生效的路径列表。"""
items: list[str] = []
for chunk in (extra or []) + (os.environ.get(ENV_EXTRA_PATH) or "").split(":"):
text = str(chunk or "").strip()
if text and Path(text).is_dir() and text not in items:
items.append(text)
for item in reversed(items):
if item not in sys.path:
sys.path.insert(0, item)
return items
def has_device() -> bool:
"""本机是否具备设备推理能力(能不能 import sophon.sail)。"""
try:
import sophon.sail # noqa: F401
return True
except Exception:
return False
# ---------------------------------------------------------------------- 静态检查
def check(path: str | Path, *, key: str | None = None, deep: bool = False,
dev_id: int = 0, extra_path: list[str] | None = None) -> dict:
"""静态检查一个包(可在任意机器运行)。
`deep=True` 时额外**实例化入口类**(会 load 模型 → 需要设备)。
返回结构化报告:`{ok, manifest, files, checks:[{name, ok, detail}]}`。
"""
applied = apply_extra_path(extra_path)
checks: list[dict] = []
def add(name: str, ok: bool, detail: str = "") -> None:
checks.append({"name": name, "ok": bool(ok), "detail": detail})
pkg = AlgoPackage(path, key=key)
manifest = None
try:
pkg.open()
manifest = pkg.require_manifest()
add("manifest 合法", True, f"{manifest.id}@{manifest.version}(api v{manifest.api_version})")
except AlgoPackageError as exc:
add("manifest 合法", False, str(exc))
return {"ok": False, "manifest": None, "files": [], "checks": checks}
add("模型文件在包内", True,
", ".join(f"{m.name}({m.file})" for m in manifest.models))
files = pkg.list_files()
add("包内文件", True, ", ".join(f["path"] for f in files))
if not has_device():
add("代码挂载(试加载)", False, "本机没有 sophon.sail,跳过(可在设备上重跑)")
return {"ok": False, "manifest": manifest.info(), "files": files, "checks": checks,
"device": False, "extra_path": applied}
try:
pkg.mount() # 挂载 = 全量 import(试加载)
add("代码挂载(试加载)", True, f"代码包 {manifest.code_package}")
except Exception as exc:
add("代码挂载(试加载)", False, f"{type(exc).__name__}: {exc}")
finally:
try:
pkg.unmount() # 清命名空间:检查完不留状态
except Exception: # pragma: no cover
pass
if deep:
try:
spec = manifest.default_model()
instance = pkg.new_instance(spec.name, {}, dev_id=dev_id)
add("默认模型可加载", True, f"{spec.name}({type(instance).__name__})")
try:
instance.close()
except Exception:
pass
except Exception as exc:
add("默认模型可加载", False, f"{type(exc).__name__}: {exc}")
ok = all(c["ok"] for c in checks)
pkg.close()
return {"ok": ok, "manifest": manifest.info(), "files": files, "checks": checks,
"device": True, "extra_path": applied}
# ---------------------------------------------------------------------- 设备上真跑
@dataclass
class FrameResult:
index: int
detections: int
boxes: Any = None
counts: dict | None = None
value: float | None = None
infer_ms: float = 0.0
@dataclass
class RunReport:
package: str
algo_id: str = ""
version: str = ""
model: str = ""
source: str = ""
frames: list[FrameResult] = field(default_factory=list)
total_ms: float = 0.0
annotated: list[str] = field(default_factory=list)
error: str | None = None
@property
def detections(self) -> list[int]:
return [f.detections for f in self.frames]
def summary(self) -> dict:
times = [f.infer_ms for f in self.frames if f.infer_ms]
return {
"algo_id": self.algo_id, "version": self.version, "model": self.model,
"source": self.source, "frames": len(self.frames),
"detections": self.detections,
"infer_ms": {"avg": round(sum(times) / len(times), 1) if times else 0,
"min": round(min(times), 1) if times else 0,
"max": round(max(times), 1) if times else 0},
"annotated": self.annotated, "error": self.error,
}
def run(path: str | Path, *, source: str, params: dict | None = None, model: str | None = None,
frames: int = 5, warmup: int = 2, dev_id: int = 0, key: str | None = None,
save_annotated: str | Path | None = None,
extra_path: list[str] | None = None) -> RunReport:
"""在设备上真跑:解码 `source`(图片或视频)→ 逐帧 `infer` → 汇总。
- `warmup` 帧不计入耗时(首次推理含设备初始化,测性能要排除);
- `save_annotated` 给了目录就把每帧结果画框存图(用包内 labels + worker 同款调色板)。
"""
import sophon.sail as sail
if not has_device():
raise RuntimeError("本机没有 sophon.sail:`run` 需要设备(图片/视频推理);"
"可用 `check()` 做静态检查")
apply_extra_path(extra_path)
pkg = AlgoPackage(path, key=key)
pkg.open()
manifest = pkg.require_manifest()
report = RunReport(package=str(path), algo_id=manifest.id, version=manifest.version)
decoder = None
# 先挂载代码:`new_instance` 要 import 包内模块(等价于 worker 装载时的 install_importer + 试加载)。
# 之前漏了这步,"单进程里先跑过 check"会因模块残留在 sys.modules 而假通过 —— 独立 CLI 一跑就露。
pkg.mount()
try:
model_spec = manifest.model(model or manifest.default_model().name)
merged = manifest.check_params(params or {})
report.model = model_spec.name
report.source = str(source)
instance = pkg.new_instance(model_spec.name, merged, dev_id=dev_id)
decoder = sail.Decoder(str(source), True, dev_id)
if not decoder.is_opened():
raise RuntimeError(f"解码失败(源不可用): {source}")
handle = sail.Handle(dev_id)
total = warmup + max(1, frames)
for index in range(total):
frame = decoder.read(handle)
if frame is None or frame.width() == 0:
break
started = time.perf_counter()
result = instance.infer(frame)
elapsed = (time.perf_counter() - started) * 1000
if index < warmup:
continue
boxed = result if isinstance(result, AlgoResult) else AlgoResult(boxes=result)
report.frames.append(FrameResult(
index=index - warmup, detections=boxed.detections(), boxes=boxed.boxes,
counts=boxed.counts, value=boxed.value, infer_ms=elapsed))
if save_annotated is not None:
path_out = _annotate(frame, boxed, manifest, Path(save_annotated),
index - warmup, dev_id)
report.annotated.append(str(path_out))
try:
instance.close()
except Exception: # pragma: no cover
pass
report.total_ms = sum(f.infer_ms for f in report.frames)
except Exception as exc:
report.error = f"{type(exc).__name__}: {exc}"
raise
finally:
if decoder is not None:
try:
decoder.release()
except Exception:
pass
pkg.close() # close = 卸载(清 sys.modules 命名空间)+ 关闭 VFS
return report
def compare(a: RunReport, b: RunReport, *, tolerance: float = 0.0) -> dict:
"""两个包/版本的推理结果对比(对拍用):逐帧检出数、逐位一致性。"""
import numpy as np
pairs = min(len(a.frames), len(b.frames))
frames = []
identical = True
for i in range(pairs):
fa, fb = a.frames[i], b.frames[i]
same = False
if fa.boxes is None and fb.boxes is None:
same = True
elif fa.boxes is not None and fb.boxes is not None:
same = (fa.boxes.shape == fb.boxes.shape and
bool(np.allclose(fa.boxes, fb.boxes, atol=tolerance)))
identical = identical and same
frames.append({"frame": i, "a": fa.detections, "b": fb.detections, "identical": same})
return {
"a": f"{a.algo_id}@{a.version}:{a.model}", "b": f"{b.algo_id}@{b.version}:{b.model}",
"frames": frames, "identical": identical and pairs > 0,
"note": "identical=True 表示两边的框逐位一致(tolerance 可放宽)",
}
def _annotate(frame, result: AlgoResult, manifest: Manifest, out_dir: Path, index: int,
dev_id: int) -> Path:
"""把结果画到帧上并保存(复用 sail/bmcv;失败时退回保存原始帧)。"""
import sophon.sail as sail
out_dir.mkdir(parents=True, exist_ok=True)
path = out_dir / f"{manifest.id}-{index:04d}.jpg"
try:
handle = sail.Handle(dev_id)
bmcv = sail.Bmcv(handle)
canvas = sail.BMImage(handle, frame.height(), frame.width(),
sail.Format.FORMAT_YUV444P, sail.DATA_TYPE_EXT_1N_BYTE)
bmcv.convert_format(frame, canvas)
if result.boxes is not None:
for det in result.boxes:
x1, y1, x2, y2 = (int(v) for v in det[:4])
w, h = x2 - x1, y2 - y1
if w > 4 and h > 4:
bmcv.rectangle(canvas, x1, y1, w, h, _palette(int(det[5])), 2)
bmcv.imwrite(str(path), canvas)
except Exception as exc: # 画框失败不致命:至少留下原帧
logger.warning("[testkit] 画框失败(%s),保存原始帧", exc)
try:
sail.Bmcv(sail.Handle(dev_id)).imwrite(str(path), frame)
except Exception:
return Path("")
return path
def _palette(cls: int) -> tuple[int, int, int]:
"""类索引 → YUV444P 颜色(与 worker 的统一绘制同色系,够看即可)。"""
colors = [(0, 128, 128), (255, 128, 128), (128, 64, 128), (128, 192, 128),
(128, 128, 64), (64, 128, 128)]
return colors[cls % len(colors)]
__all__ = ["has_device", "check", "run", "compare", "apply_extra_path", "ENV_EXTRA_PATH",
"RunReport", "FrameResult"]
+35
View File
@@ -0,0 +1,35 @@
"""`video_algpkg_api` —— 算法包接口契约名的**真实模块**(随 algpkg-sdk 分发)。
算法包内的代码应当且只应写这一个导入名::
from video_algpkg_api import Algorithm, AlgoContext, AlgoResult
本模块把它变成任何工具都能直接解析的名字(IDE 补全 / mypy / pyright / 本地直跑),
不必先依赖 worker 在运行时把 ``video_algpkg_api`` 注册进 ``sys.modules``。
三条保证(改这里之前先想清楚):
- **对象同一**:本模块只是 `algpkg_sdk.api` 的**重导出**(同一批类/函数对象,不是副本),
因此 ``isinstance(algo, Algorithm)`` 校验(worker 侧 ``AlgoPackage.new_instance``)
在"本地用本模块"与"设备用运行时注册"两种解析路径下完全一致;
- **设备侧不受影响**:worker 启动与每次挂载前仍会调
:func:`algpkg_sdk.api.install_sdk_alias`(``sys.modules.setdefault``)——在 worker 进程里
该名字先被注册为 ``algpkg_sdk.api``,本模块此时被惰性遮蔽,两者指向同一批对象,谁先解析都一样;
- **不要另造同名模块**:自行 vendor/copy 一份会造成"本地用副本、设备用权威版"的分叉,
两份实现不一致时表现为"本地过、设备挂";包内代码禁止携带本模块(打包只收
``manifest.code_package/`` 目录与 models/assets,正常不会带上)。
本地开发只看这一条:装好 algpkg-sdk(``uv pip install -e ./algpkg-sdk``)即可——
导入本模块 → 自动完成 SDK 侧的全部初始化,与设备运行看到的是同一套接口。
"""
from __future__ import annotations
from algpkg_sdk.api import (ALG_API_VERSION, KINDS, OUTPUT_TYPES, SDK_MODULE_NAME,
AlgoContext, AlgoError, AlgoResult, Algorithm,
install_sdk_alias)
#: 与 algpkg_sdk.api 的公共面保持一致(显式列出而非 `import *`:类型检查器据此识别重导出)
__all__ = [
"ALG_API_VERSION", "KINDS", "OUTPUT_TYPES", "SDK_MODULE_NAME",
"AlgoContext", "AlgoError", "AlgoResult", "Algorithm", "install_sdk_alias",
]
View File
+388
View File
@@ -0,0 +1,388 @@
"""SDK 自测:清单校验 / 打包往返 / 装载策略 / 本地测试台(无需 worker)。
有设备的用例标 `@pytest.mark.device`(无 sophon.sail 时自动跳过):
uv run pytest # 静态用例
uv run pytest -m device # 设备上跑推理
"""
from __future__ import annotations
import json
import subprocess
import sys
import tempfile
from pathlib import Path
import pytest
from algpkg_sdk import (AlgoBuilder, AlgoError, AlgoPackage, AlgoPackageError, Manifest,
ManifestError, install_sdk_alias, sha256_file)
from algpkg_sdk import testkit
from algpkg_sdk.cli import main as cli_main
EXAMPLES = Path(__file__).resolve().parents[1] / "examples"
def _manifest(**overrides) -> dict:
payload = {
"manifest_format": 1, "id": "t.demo", "name": "测试包", "version": "1.0.0",
"api_version": 1, "kind": "detector", "entry": "Algorithm",
"code_package": "alg_t_demo",
"models": [{"name": "default", "file": "models/t.bmodel", "precision": "fp16",
"resolution": [640, 640], "batch": 1, "default": True}],
"params_schema": [{"key": "conf", "type": "float", "default": 0.25,
"min": 0.01, "max": 0.99, "label": "置信度"}],
"defaults": {"conf": 0.25},
}
payload.update(overrides)
return payload
# ---------------------------------------------------------------------- 清单
def test_manifest_required_fields() -> None:
Manifest.parse(_manifest())
for missing in ("id", "version", "models"):
bad = _manifest()
bad.pop(missing)
with pytest.raises(ManifestError):
Manifest.parse(bad)
def test_manifest_rejects_bad_values() -> None:
with pytest.raises(ManifestError): # id 形态
Manifest.parse(_manifest(id="Bad Id!"))
with pytest.raises(ManifestError): # 模型路径越界
Manifest.parse(_manifest(models=[{"name": "d", "file": "../x.bmodel"}]))
with pytest.raises(ManifestError): # api_version 过高
Manifest.parse(_manifest(api_version=99))
with pytest.raises(ManifestError): # params_schema 重复键
Manifest.parse(_manifest(params_schema=[
{"key": "conf", "type": "float"}, {"key": "conf", "type": "float"}]))
def test_manifest_default_model_pick() -> None:
manifest = Manifest.parse(_manifest(models=[
{"name": "fp16", "file": "models/a.bmodel"},
{"name": "int8", "file": "models/b.bmodel", "default": True}]))
assert manifest.default_model().name == "int8"
assert manifest.model("fp16").file == "models/a.bmodel"
with pytest.raises(ManifestError):
manifest.model("nope")
def test_manifest_check_params_semantics() -> None:
manifest = Manifest.parse(_manifest())
merged = manifest.check_params({"conf": 0.5})
assert merged["conf"] == 0.5
assert manifest.check_params({})["conf"] == 0.25 # 补默认
with pytest.raises(ManifestError): # 越界拒绝(不静默截断)
manifest.check_params({"conf": 2.0})
with pytest.raises(ManifestError): # 类型不符
manifest.check_params({"conf": "abc"})
def test_manifest_legacy_and_entry_helpers() -> None:
manifest = Manifest.parse(_manifest(entry="sub:Alg", legacy={"algorithm": "yolo"}))
assert manifest.entry_module == "alg_t_demo.sub"
assert manifest.entry_class == "Alg"
assert manifest.legacy["algorithm"] == "yolo"
# ---------------------------------------------------------------------- 打包往返
def _make_source(root: Path, *, model_bytes: bytes = b"FAKE-BMODEL" * 512) -> Path:
src = root / "t.demo"
(src / "alg_t_demo").mkdir(parents=True)
(src / "alg_t_demo" / "__init__.py").write_text(
"from video_algpkg_api import Algorithm as Base, AlgoResult\n"
"class Algorithm(Base):\n"
" def load(self):\n"
" self._data = self.ctx.model_bytes()\n"
" self.ctx.logger.info('loaded %s bytes', len(self._data))\n"
" def infer(self, frame):\n"
" return AlgoResult(boxes=None, value=len(self._data))\n",
encoding="utf-8",
)
(src / "models").mkdir()
(src / "models" / "t.bmodel").write_bytes(model_bytes)
(src / "assets").mkdir()
(src / "assets" / "labels.txt").write_text("person\ncar\n", encoding="utf-8")
(src / "manifest.json").write_text(json.dumps(_manifest()), encoding="utf-8")
return src
def test_build_roundtrip_and_bytes_fidelity() -> None:
install_sdk_alias()
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
src = _make_source(root)
out = root / "t.demo-1.0.0.mbank"
summary = AlgoBuilder(src, verbose=False).build(out)
assert out.is_file() and summary["algo_id"] == "t.demo"
assert summary["sha256"] == sha256_file(out)
pkg = AlgoPackage(out)
pkg.open()
manifest = pkg.require_manifest()
assert manifest.version == "1.0.0"
data, meta = pkg.read_model("default")
assert data == (src / "models" / "t.bmodel").read_bytes() # 包内模型与源文件逐字节相同
assert meta["precision"] == "fp16"
assert pkg.read_asset("assets/labels.txt").decode().splitlines() == ["person", "car"]
files = {item["path"] for item in pkg.list_files()}
assert {"manifest.json", "assets/labels.txt", "models/t.bmodel"} <= files
pkg.close()
def test_build_creates_version_snapshot_and_signature() -> None:
from sqlite_vfs.core import SQLiteVFS
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
src = _make_source(root)
out = root / "signed.mbank"
AlgoBuilder(src, verbose=False).build(out, sign_key="KEY-1", sign_key_id="ci")
vfs = SQLiteVFS(str(out), readonly=True, check_same_thread=False)
try:
assert [s["name"] for s in vfs.list_snapshots()] == ["1.0.0"]
report = vfs.verify_signature("KEY-1")
assert report["ok"] and report["key_id"] == "ci"
assert not vfs.verify_signature("KEY-WRONG")["ok"]
finally:
vfs.close()
def test_build_reports_missing_model_and_code() -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
src = _make_source(root)
(src / "models" / "t.bmodel").unlink()
with pytest.raises(Exception) as exc:
AlgoBuilder(src, verbose=False).build(root / "x.mbank")
assert "模型" in str(exc.value)
src2 = _make_source(root / "other")
import shutil
shutil.rmtree(src2 / "alg_t_demo")
with pytest.raises(Exception) as exc2:
AlgoBuilder(src2, verbose=False).build(root / "y.mbank")
assert "代码目录" in str(exc2.value)
def test_encrypt_removes_plain_source() -> None:
from sqlite_vfs.core import SQLiteVFS
key = "uA9mYq0T2wGZ0dX1vJ8kL3pR5sN7bC4eF6hK2mP0qT8="
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
src = _make_source(root)
out = root / "enc.mbank"
AlgoBuilder(src, key=key, verbose=False).build(out)
vfs = SQLiteVFS(str(out), readonly=True, check_same_thread=False)
try:
files = [e["path"] for e in vfs.all_entries() if not e["is_directory"]]
finally:
vfs.close()
assert not any(f.endswith(".py") for f in files) # 明文源码不得留在包里
# 不带 key 打开结构没问题;装载(import)会因缺密钥失败
pkg = AlgoPackage(out)
pkg.open()
with pytest.raises(AlgoPackageError) as exc:
pkg.mount()
assert "密钥" in str(exc.value)
pkg.close()
def test_bad_file_rejected() -> None:
with tempfile.TemporaryDirectory() as tmp:
bad = Path(tmp) / "bad.mbank"
bad.write_bytes(b"not a database at all")
with pytest.raises(AlgoPackageError):
AlgoPackage(bad).open()
# ---------------------------------------------------------------------- 多模型包
def _make_multi_model_source(root: Path) -> Path:
"""一个包带两份模型(检测 + 二次判定):代码按名字取第二份(多模型包的标准写法)。"""
src = root / "t.multi"
(src / "alg_t_multi").mkdir(parents=True)
(src / "alg_t_multi" / "__init__.py").write_text(
"from video_algpkg_api import Algorithm as Base, AlgoResult\n"
"class Algorithm(Base):\n"
" def load(self):\n"
" self._det = self.ctx.model_bytes() # 选中模型(默认检测器)\n"
" self._cls = self.ctx.model_bytes('fp_filter') # 按名字取第二份\n"
" def infer(self, frame):\n"
" return AlgoResult(boxes=None, value=len(self._det) + len(self._cls))\n",
encoding="utf-8",
)
(src / "models").mkdir()
(src / "models" / "det.bmodel").write_bytes(b"DET" * 256)
(src / "models" / "fp.bmodel").write_bytes(b"FPFILTER" * 128)
(src / "manifest.json").write_text(json.dumps(_manifest(
id="t.multi", name="多模型包", code_package="alg_t_multi",
models=[{"name": "detector", "file": "models/det.bmodel", "default": True},
{"name": "fp_filter", "file": "models/fp.bmodel", "precision": "int8"}])),
encoding="utf-8")
return src
def test_multi_model_package_context_access() -> None:
"""`ctx.model_bytes(name)` 按 manifest.models[] 取包内任意模型;不给名 = 选中模型(旧写法兼容)。"""
install_sdk_alias()
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
src = _make_multi_model_source(root)
out = root / "multi.mbank"
AlgoBuilder(src, verbose=False).build(out)
pkg = AlgoPackage(out)
pkg.open()
pkg.mount()
try:
instance = pkg.new_instance("detector", {})
ctx = instance.ctx
det = (src / "models" / "det.bmodel").read_bytes()
fp = (src / "models" / "fp.bmodel").read_bytes()
assert ctx.model_name == "detector"
assert ctx.model_bytes() == det # 选中模型(向后兼容)
assert ctx.model_bytes("fp_filter") == fp # 按 name 取第二份
assert ctx.model_bytes("models/fp.bmodel") == fp # 按 file 路径也认
assert [m["name"] for m in ctx.models] == ["detector", "fp_filter"]
assert ctx.model_spec("fp_filter")["precision"] == "int8"
assert ctx.model_spec()["name"] == "detector"
with pytest.raises(AlgoError) as exc:
ctx.model_bytes("nope")
assert "fp_filter" in str(exc.value) # 报错带可选清单
assert instance.infer(None).value == len(det) + len(fp) # 包内代码真读到两份不同字节
finally:
pkg.close()
# ---------------------------------------------------------------------- 契约名(video_algpkg_api)
def test_contract_module_importable_without_prior_registration() -> None:
"""`video_algpkg_api` 是随 SDK 分发的**真实模块**,不只是运行时注册的别名。
新解释器里**先** import 它也要成功(本地开发/IDE/类型检查的常见姿势),
且与 `algpkg_sdk.api` 是同一批对象(重导出,非副本)——否则 worker 侧
`isinstance(instance, Algorithm)` 会出现两个基类("本地过、设备挂")。
"""
script = (
"import video_algpkg_api as contract\n"
"assert contract.__file__.endswith('video_algpkg_api/__init__.py'), contract.__file__\n"
"import algpkg_sdk.api as api\n"
"for name in contract.__all__:\n"
" assert getattr(contract, name) is getattr(api, name), name\n"
"print('contract-ok')\n"
)
with tempfile.TemporaryDirectory() as tmp:
proc = subprocess.run([sys.executable, "-c", script], capture_output=True,
text=True, timeout=30, cwd=tmp)
assert proc.returncode == 0, proc.stderr
assert "contract-ok" in proc.stdout
def test_contract_alias_still_registered_to_api_module() -> None:
"""worker 侧注册语义不变:导入 `algpkg_sdk` 后,`video_algpkg_api` 解析到 api 模块本身。
(`install_sdk_alias` 用 setdefault:设备上 worker 先注册,真实模块被惰性遮蔽,
两条路径拿到的是同一批对象。)
"""
import video_algpkg_api as resolved
from algpkg_sdk import api
assert resolved is api
# ---------------------------------------------------------------------- CLI
def test_cli_init_build_inspect_check(tmp_path: Path) -> None:
src = tmp_path / "cli.demo"
assert cli_main(["init", str(src), "--id", "cli.demo", "--name", "CLI 示例"]) == 0
assert (src / "manifest.json").is_file()
code_pkg = next((src.glob("alg_cli_demo")), None)
assert code_pkg is not None and (code_pkg / "__init__.py").is_file()
model = tmp_path / "fake.bmodel"
model.write_bytes(b"FAKE" * 1024)
out = tmp_path / "cli.demo-1.0.0.mbank"
assert cli_main(["build", str(src), "--out", str(out), "--model", f"default={model}",
"--quiet"]) == 0
assert cli_main(["inspect", str(out), "--json"]) == 0
assert cli_main(["verify", str(out), "--json"]) == 0
assert cli_main(["list", str(tmp_path)]) == 0
def test_cli_check_reports_static_results(tmp_path: Path) -> None:
"""check 在无设备时也要给出"清单/结构 OK、试加载跳过"的诚实结论。"""
src = tmp_path / "chk.demo"
cli_main(["init", str(src), "--id", "chk.demo", "--name", "检查示例"])
model = tmp_path / "fake.bmodel"
model.write_bytes(b"FAKE" * 1024)
out = tmp_path / "chk.mbank"
cli_main(["build", str(src), "--out", str(out), "--model", f"default={model}", "--quiet"])
report = testkit.check(out)
names = {item["name"]: item for item in report["checks"]}
assert names["manifest 合法"]["ok"]
assert names["模型文件在包内"]["ok"]
if not testkit.has_device():
assert not names["代码挂载(试加载)"]["ok"] and "跳过" in names["代码挂载(试加载)"]["detail"]
def test_module_entry_point_help() -> None:
proc = subprocess.run([sys.executable, "-m", "algpkg_sdk.cli", "--help"],
capture_output=True, text=True, timeout=30)
assert proc.returncode == 0
assert "check" in proc.stdout and "run" in proc.stdout and "compare" in proc.stdout
# ---------------------------------------------------------------------- 示例包(真实结构)
def test_example_person_count_manifest() -> None:
example = EXAMPLES / "person-count"
manifest = Manifest.parse(json.loads((example / "manifest.json").read_text()))
assert manifest.id == "demo.person-count" and manifest.kind == "counter"
assert manifest.output["type"] == "counter"
assert (example / manifest.code_package).is_dir()
# 模型文件由打包时 --model 指定(示例目录不带 bmodel),这里只校验声明形态
assert manifest.default_model().file.startswith("models/")
# ---------------------------------------------------------------------- 设备用例
@pytest.mark.device
def test_check_deep_on_device() -> None:
"""设备:入口类可实例化(模型真加载)。
默认用**自包含**示例包(examples/person-count 打出来的包);内置包(依赖 worker 内部模块)
属"特权包",需额外传 `extra_path=[worker 工程目录]`。
"""
import os
pkg_path = os.environ.get("ALGPKG_DEVICE_PACKAGE")
if not pkg_path or not Path(pkg_path).is_file():
pytest.skip("未提供 ALGPKG_DEVICE_PACKAGE(指向含真实模型的 .mbank)")
extra = [p for p in (os.environ.get("ALGPKG_EXTRA_PATH") or "").split(":") if p]
report = testkit.check(pkg_path, deep=True, extra_path=extra)
assert report["ok"], report["checks"]
@pytest.mark.device
def test_run_on_device() -> None:
"""设备:跑一段视频,拿到逐帧检出与单帧耗时。"""
import os
pkg_path = os.environ.get("ALGPKG_DEVICE_PACKAGE")
clip = os.environ.get("ALGPKG_DEVICE_CLIP")
if not pkg_path or not clip:
pytest.skip("未提供 ALGPKG_DEVICE_PACKAGE / ALGPKG_DEVICE_CLIP")
extra = [p for p in (os.environ.get("ALGPKG_EXTRA_PATH") or "").split(":") if p]
report = testkit.run(pkg_path, source=clip, frames=3, warmup=1, extra_path=extra)
summary = report.summary()
assert summary["frames"] >= 1
assert summary["infer_ms"]["avg"] > 0
Generated
+789
View File
@@ -0,0 +1,789 @@
version = 1
revision = 3
requires-python = ">=3.10"
resolution-markers = [
"python_full_version >= '3.12'",
"python_full_version == '3.11.*'",
"python_full_version < '3.11'",
]
[[package]]
name = "algpkg-sdk"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "module-bank" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }, marker = "python_full_version < '3.11'" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }, marker = "python_full_version == '3.11.*'" },
{ name = "numpy", version = "2.5.4", source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "orjson" },
]
[package.optional-dependencies]
dev = [
{ name = "pytest" },
{ name = "sophon-arm" },
]
device = [
{ name = "sophon-arm" },
]
[package.metadata]
requires-dist = [
{ name = "module-bank", specifier = ">=0.4.2" },
{ name = "numpy", specifier = ">=1.26" },
{ name = "orjson", specifier = ">=3.10" },
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8" },
{ name = "sophon-arm", marker = "extra == 'dev'", specifier = ">=3.11.1" },
{ name = "sophon-arm", marker = "extra == 'device'", specifier = ">=3.11.1" },
]
provides-extras = ["device", "dev"]
[[package]]
name = "cffi"
version = "2.1.1"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
dependencies = [
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b6/d2/2cde336b375f55c76ca670f0be3978cc048e31e24f3b4d7ce8473150a388/cffi-2.1.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:baed1e86cc735622097354b9d1281406caf42ff42a886d29faa8e8d1630333be", size = 183779, upload-time = "2026-08-03T21:19:15.602Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/94/1a/4b2f7c92293ba05cbd4a9a1b28faaf0326272d9488e6354657571c48a7aa/cffi-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca82be1a1d406ecfe1d25dc16cb33488e5a16bf4438c9fb590484ea29d92478b", size = 184178, upload-time = "2026-08-03T21:19:16.67Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/17/0b/ba385d8ccedf926c3cd06e8e2f327027da5afe5f0eb30f1f7bc43ac55125/cffi-2.1.1-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:42e2f76b9455f5a9a844f770bf3e200ed3da0e15f5df3db9c31fe80b04b3d004", size = 211037, upload-time = "2026-08-03T21:19:17.705Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a3/b9/0f2e58b2cefa33255bff36935d42b13180fe559bba82596540eb404bde7d/cffi-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5a59cc1c4442bc3d5c703bf720b51138d0bfc173618807c9ee2490a7541dd3d9", size = 218652, upload-time = "2026-08-03T21:19:18.735Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/37/15/180e0dab27b9312c7479003d14c9e547634b7dcb934e2cc4650e1b131a7a/cffi-2.1.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:9f8d177621de5cb38ee3e731eda45d421db093ec0739f46a5594babda7987a98", size = 205422, upload-time = "2026-08-03T21:19:19.96Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/18/d4/03026f0c850cbbaa9030750490225b4a7f4d524ea4df72c3cc740a90f4ef/cffi-2.1.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:75f80557d1389eddbd0de2681f6a390a0c5338c31ddaa821381c203fc3fd50d9", size = 205444, upload-time = "2026-08-03T21:19:21.246Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/75/77/60bebf6f818bec84210ac5b6979ce4eeadce6fbbaabc9c7ab23e506d1ce5/cffi-2.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:194cffa889098ced9976c3fc6340305e43f6303657d298da55366907c05c22d6", size = 218742, upload-time = "2026-08-03T21:19:22.523Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b0/ae/679bf47e73fd77b352171727f07de559a003f14de5d02b904a6ec1fa73ca/cffi-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5bb4e7ea95dcd6a014a6fef62e62467d67d8e582326443f3d68e71d6320a9fcf", size = 221054, upload-time = "2026-08-03T21:19:23.694Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/09/b8/eefc0e06913b70aa153bf74c946094a18f58fd4aff11b7f372bfdfdca050/cffi-2.1.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3d22a20b1fb1632cc72c22f95f7b0d2961c3e1c235f245ba4c606c4771035659", size = 213489, upload-time = "2026-08-03T21:19:24.922Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6f/13/4e56852824a03cdf68523a35686f1c28eacd4bd30a7b0a78e682e6e6e1d3/cffi-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1dea0e4d7d4f11f619fe8c1d76caf49e24405b4b5743c0e3be16a500ecd930c9", size = 220241, upload-time = "2026-08-03T21:19:26.214Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/99/7f/040f9e163e4acac3ee3d85b02d00b2576e7ca980d8785f0a3a5f1a9bf7f5/cffi-2.1.1-cp310-cp310-win32.whl", hash = "sha256:7ce713ace7c0e4520535b42b77eaa742c16dab813978064913e5a3cf82973b41", size = 174578, upload-time = "2026-08-03T21:19:27.338Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ba/0b/644a2ec1a4eaba49c2939410bb1eb1d25b09d6d0582f5d2f95c537043725/cffi-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a48d62ab9d6f4f98c983223a547af44be6ca3691074c31cecced6facd3ba2dc1", size = 185082, upload-time = "2026-08-03T21:19:28.409Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/70/d2/16d99a0c4948febc0ebd133a13b2f688ff7f8cb04da971e1128872ce0c03/cffi-2.1.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:c8d2c9fd1f2d16f780d15127abb050d13d1a76c03a4bd87d7e4980e45e511e12", size = 183838, upload-time = "2026-08-03T21:19:29.637Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cd/95/31b535a9f0220ae9f357de4a08d57ce89cb417653c2fd9f075f50822a388/cffi-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:398aff33cee2767e3e781d2554c54bd0dff386bb437581e0d8011fde1a942ec1", size = 184168, upload-time = "2026-08-03T21:19:30.764Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ad/5a/4707a0dc1f203f5dde5a907b0d4e3c25d71120241048bd5bc6f1bb9d4e71/cffi-2.1.1-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:154852545011f779917b11c78db2358d095da62a9a172b78ad0a583ee5adc0d0", size = 211805, upload-time = "2026-08-03T21:19:31.867Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ad/66/c19feabb28485b6e0bbaaafa90837a1ef5d302e90f2178bd33f17a49879b/cffi-2.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3311ed60d36f83378794e1009ac6258bafbf81f7888b4caa7b35a521e3f95813", size = 218716, upload-time = "2026-08-03T21:19:32.896Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a7/92/500760486c8baab49a7a8a58ba7fc3355ec3974b454b8a09e528efde9e1d/cffi-2.1.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6e192623c49c94421616a5778fba35cf0d5a8d000650c1967ef4448ee5cdd990", size = 205569, upload-time = "2026-08-03T21:19:34.142Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a5/a7/a67c733254d6e7373f7822f8082d8d6beade791e0cf12a7611f376fa61c7/cffi-2.1.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a6e721d4b0e45d5b65e87534470e67b18dcd092c83f68fba09f152b9cbc061af", size = 204907, upload-time = "2026-08-03T21:19:35.174Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f7/a4/4399daaf8f7dfee9d7c3327fdb0426ee041cc63edc358b93911ceb2bfc7a/cffi-2.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34e261f78cb6ceaaa36f42f2613f4380d94d9c759a9c73c769ee6e0247364632", size = 217807, upload-time = "2026-08-03T21:19:36.286Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/28/f7/dabe6da2466ecbd82dc62e7342dc6b1065dad990c06f00f0ede9ebf2a0ed/cffi-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7225e4514edb64eb6740324353e0da0711954fd8d7da4576755b1c6e09b697cd", size = 221252, upload-time = "2026-08-03T21:19:37.416Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ce/87/616202d8e51342c07d2534c510111c4cc37201775ce8f60802c9335d1edd/cffi-2.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df913725b79db7bcf03448f36b7bf8815363417d5b58deecf9305e3e30f0f21a", size = 214214, upload-time = "2026-08-03T21:19:38.507Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b4/c6/ab025d75d2c26c19b087c0124e75ee31cb65032f4fe345d356d8c507ab97/cffi-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f5cfbc5fe74540d335175b656c725d74d90e3730c626d92575eea35029d9afaa", size = 219408, upload-time = "2026-08-03T21:19:39.809Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/db/e2/7e8109f65445bdc673a7b54f02c677de462db75674220fd1335efc8eb598/cffi-2.1.1-cp311-cp311-win32.whl", hash = "sha256:f8ec5e643a9a937f64e1999eb9f75d072263751912dc5cd06d3c85f8f44be7c3", size = 174470, upload-time = "2026-08-03T21:19:41.246Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/73/c0/77ba02423c2f7d7091143c45cd49e0e6575c4c1967394bb542bd923a9b74/cffi-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:42f6930c31dc7f50732c9ae793c2786c7b6b044195967bbdde40bb9be81c4cc0", size = 185096, upload-time = "2026-08-03T21:19:42.615Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7c/47/9f1f85f9672ceda4984dc6c4f8824e8558992a2972c3d3c81fb8eb28d4ba/cffi-2.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:c7659f22557c5a0bc4855cd635f55edec690cc008a40768527762cb9fb263455", size = 179941, upload-time = "2026-08-03T21:19:43.747Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/10/69/43965eccfdead3b9220015fd1320e117be8c6ed01a62ffab76eeb752f5d5/cffi-2.1.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0", size = 184821, upload-time = "2026-08-03T21:19:44.887Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/54/7d/16e5a096677b5e313ca80cd5e5170efa3ea44624a82bb111925522da64b1/cffi-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf", size = 184719, upload-time = "2026-08-03T21:19:46.129Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/56/e6/8941622732edec876dd17d0453dce07317ae96db34f2ec1436c9d3785986/cffi-2.1.1-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a", size = 214799, upload-time = "2026-08-03T21:19:47.218Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/44/de/f98430906df1545ffde0d543dd124a7a439bc2cd32b36b9c53f805df7333/cffi-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890", size = 222389, upload-time = "2026-08-03T21:19:48.331Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6a/5b/717f1526b9957b34456313c31645c5b82b8fb5c3fe9e4752999be7128bfc/cffi-2.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50", size = 210249, upload-time = "2026-08-03T21:19:49.543Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/64/b3/f8aa4f3e34986c7e4ec45072d1b1b9dd295b6b18007b45518d79726dd725/cffi-2.1.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e", size = 208775, upload-time = "2026-08-03T21:19:50.918Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b1/db/dceb9dd5b231e1da801793f8acc9f3c52a7e1afe40bb1aae37e02b0faad5/cffi-2.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf", size = 221822, upload-time = "2026-08-03T21:19:52.054Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a0/d2/6cd24ae3be000a634109c247d1475d62e5616d0dc78c82770942ec384248/cffi-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517", size = 225232, upload-time = "2026-08-03T21:19:53.109Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cb/52/3fa190537004dd7f0ab860a6dc7c0175b8667f68d1e618a46f5498d30250/cffi-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735", size = 223597, upload-time = "2026-08-03T21:19:54.515Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/80/fb/0bb75b7039588c074b37ae99f40d9bfddf990ecb2fbc346ebccd2e56b9be/cffi-2.1.1-cp312-cp312-win32.whl", hash = "sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e", size = 175292, upload-time = "2026-08-03T21:19:55.566Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d9/79/615cc094e2fb508cade7de88d3b4f6c4ec2bab695c97bce9153dc65aadf5/cffi-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a", size = 185919, upload-time = "2026-08-03T21:19:56.89Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/70/c6/d0ea84713fe46b243a436a18fcd47d639732747e21635c8a27191b06dc30/cffi-2.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80", size = 180093, upload-time = "2026-08-03T21:19:58.155Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e", size = 194248, upload-time = "2026-08-03T21:19:59.399Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c", size = 196908, upload-time = "2026-08-03T21:20:00.746Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a7/46/2e5fdde8555706dd98139a910ca11be02809f3f605ce956f655d0214e100/cffi-2.1.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6", size = 184805, upload-time = "2026-08-03T21:20:02.02Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/55/41/4c7042f317b9217502988f0873af87e16ad606dc20f84e546e3e6ce9764c/cffi-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971", size = 184764, upload-time = "2026-08-03T21:20:03.141Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/43/1f/1c3d90d91811c8f86ced9ed637956c54bfe5b79ca98fe976d7f8c8979f6b/cffi-2.1.1-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c", size = 214722, upload-time = "2026-08-03T21:20:04.377Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/37/6f/3b5ce4c3b2192d250f04908f2bfd91ef34552ec8f7716a5d4abdb8d67bb2/cffi-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125", size = 222369, upload-time = "2026-08-03T21:20:05.544Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/02/10/4b3c75dde3d9663c9e02ba05c2668b954f671d4bbe346413ca8c696b295a/cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264", size = 210175, upload-time = "2026-08-03T21:20:06.75Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/df/62/14f74b9543e605d17701dc797b815958b8bb70b7624ce1b832ddad48ed6c/cffi-2.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3", size = 208670, upload-time = "2026-08-03T21:20:08.04Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/95/95/86342356ff5953b3fb06f7ef7c5bee212d45e770abc7218d451b9148313c/cffi-2.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2", size = 221824, upload-time = "2026-08-03T21:20:09.274Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/eb/ff/7b3429ff53aafe931ed8a5fc69f481bbef7ba6de87ddcbb63d08f483f613/cffi-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b", size = 225148, upload-time = "2026-08-03T21:20:10.7Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/34/34/a95870b9221e09cf4f2ce3178b1a210abdfe63a1bd357da940418d7b8d15/cffi-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7", size = 223564, upload-time = "2026-08-03T21:20:12.165Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/70/ea/839b50531021a647fb5e929f72cf97bc1ff702b5472166164b5b6e76b851/cffi-2.1.1-cp313-cp313-win32.whl", hash = "sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac", size = 175263, upload-time = "2026-08-03T21:20:13.559Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/60/a6/8b149b2c3f2e11aaa1618ef64500b45f50f22c57a977a4dff1aff1f91042/cffi-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d", size = 185688, upload-time = "2026-08-03T21:20:14.69Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", size = 180078, upload-time = "2026-08-03T21:20:15.917Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d3/7b/d6bbf82b8b96e7391438898c42f5bd96dd02030fd5b64937d248220003e2/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c", size = 194064, upload-time = "2026-08-03T21:20:17.148Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/94/e6/bcc91b283be94735e268487a054004f0aa19947b6348fa367db53230abc8/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb", size = 196720, upload-time = "2026-08-03T21:20:18.268Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d9/99/c4b0c17cacdc9c3b8f280026286a9826d6a208c0f047591a3c3ce99b91fd/cffi-2.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54", size = 184964, upload-time = "2026-08-03T21:20:19.708Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b3/a9/9db617d05d7367c1ad0ab00b3aa6e6f9281edd689b4ee9ea0e5a84e89c97/cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72", size = 184962, upload-time = "2026-08-03T21:20:20.833Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/67/b8/b42132ca113dc567d37684437b46ca1dafc885902b02a110a02d5b511857/cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1", size = 222328, upload-time = "2026-08-03T21:20:22.118Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/80/10/c5c0cbf0a657aecf59ef511409734230bf556f05a0d6c9eed7aa5c0a0166/cffi-2.1.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062", size = 209985, upload-time = "2026-08-03T21:20:23.401Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d5/6c/bfa0b87b03b9238148beca990292843c9396ba069b54496596594173de7b/cffi-2.1.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03", size = 208530, upload-time = "2026-08-03T21:20:24.628Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e9/02/4e7d553a7ac4b4238b38b3c1b80d486e9d4436f8d2acbf87a0997fe3f402/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96", size = 221525, upload-time = "2026-08-03T21:20:25.758Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/82/1d/a4aaf9babd75acb4d5f223bff71533bee748dd770a382619a798960ee9ba/cffi-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527", size = 225053, upload-time = "2026-08-03T21:20:26.985Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/81/10/5dc0e7bdd18e22107054288283380fc97a06ae3f1656a106908d666a3c88/cffi-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13", size = 223213, upload-time = "2026-08-03T21:20:28.277Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0b/e9/d0061c364cde06ee43168a0d076ac1da512cbc380d44767b844ba34fe2b6/cffi-2.1.1-cp314-cp314-win32.whl", hash = "sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c", size = 177682, upload-time = "2026-08-03T21:20:44.288Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a7/06/1c3e01e3ba14c39f6d10bfbac52753b7e22259e38088e5cfe1d704918690/cffi-2.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48", size = 187949, upload-time = "2026-08-03T21:20:45.623Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/87/5b/da4e39efe18eeb89cf580ea9cfc66b6a7c3eadb808fc0cc1d3a295cb5a5d/cffi-2.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836", size = 182947, upload-time = "2026-08-03T21:20:46.955Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/23/59/40338bf421c5accea1d45158170c87006ef1cd371b05c077e76476949728/cffi-2.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3", size = 188504, upload-time = "2026-08-03T21:20:29.495Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7d/47/5ecf1023850036e674c77ec4de86182d309ae344e39e7cba984b7df5d647/cffi-2.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2", size = 188259, upload-time = "2026-08-03T21:20:31.291Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2a/9c/92934c3bea9f785b23eba304538c0b4d37a2a96d2431eb3a1bc87a11aa19/cffi-2.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94", size = 223864, upload-time = "2026-08-03T21:20:32.571Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4d/45/ba4c93527bc38616a8bd36488acb69a2212d60486794f0c1f318949bbb76/cffi-2.1.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc", size = 211538, upload-time = "2026-08-03T21:20:33.808Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/80/e9/b6ef565e452acb932fb0cb5443f44a78efbd1233e566f02b5a83855e9115/cffi-2.1.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29", size = 210688, upload-time = "2026-08-03T21:20:34.974Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9a/95/eff5f0cee78d2eabc7eebffec40d3fc1876b5f3c95582e018bb4b99601f2/cffi-2.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676", size = 223803, upload-time = "2026-08-03T21:20:36.564Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fa/01/579d39fb8bef00a335a23d83757b44feb24cd6345a2c451b64cb67b9c362/cffi-2.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e", size = 226763, upload-time = "2026-08-03T21:20:37.816Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8d/b0/0b44f47c60b01b57b6e2bbd92343f13a85a1d93bc46ccf6e47e244acd99c/cffi-2.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f", size = 225688, upload-time = "2026-08-03T21:20:38.959Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/eb/d2/3b7176cb570a1d3e27faf67b72f591af508036e0d8b2be2ef9af9e8c84bb/cffi-2.1.1-cp314-cp314t-win32.whl", hash = "sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4", size = 182868, upload-time = "2026-08-03T21:20:40.388Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/56/78/31f00c1bcd97c9bbf55f1bfdf5bc809a5de8887473e90bb9960dca825e80/cffi-2.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e", size = 194104, upload-time = "2026-08-03T21:20:41.725Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7b/1b/58496f2ed0a35de575250c02a43ab3cc2c04d494a88fed31c1cabc0fd176/cffi-2.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5", size = 186402, upload-time = "2026-08-03T21:20:43.042Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c1/8f/9ebe220eab48a093d1a5a5e339ab0dc7316eef3bb04d63c42f0251b61f50/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:dddad92b554513a31f272570678ba307fb9f618f05e3d4a5eacafff9eae03e1d", size = 194043, upload-time = "2026-08-03T21:20:48.179Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ff/69/844bad3ece306c4782c2ecb93597035b6690d48704b803914c199da1e8b3/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:da0e573f9f97159390c89d9f1a9e41908b66d408cc5b58d08cf3847d844c531b", size = 196737, upload-time = "2026-08-03T21:20:49.457Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1b/8a/af668013284634733f02d683458a0728739c7d6ddb5e14cb0c20832266fe/cffi-2.1.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:fb92203a88b3d3053034db775110081c49d28be6551923805e039924093761e4", size = 184933, upload-time = "2026-08-03T21:20:50.639Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0c/75/2f5207ff6d1a613133b23a5203cc0c2a628313b5eb3974d7956ae3c57950/cffi-2.1.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2ae64be792b8966f2c69538199728b290e34726562896df1e5dc8ffd8d8188e8", size = 185002, upload-time = "2026-08-03T21:20:52.173Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e2/31/9e1313b0a6e30e91b3b3d3fff51ae99c857c07738e3afcce1f7334e1b7ab/cffi-2.1.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:507a24c282e0f42f8ed737cf048572cbf580468da5555764a8331735e9c736b6", size = 222271, upload-time = "2026-08-03T21:20:53.462Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/50/e3/f6234a833e6e08c7007003074723c406559eecf9b48dfc97471e5a8eb7a0/cffi-2.1.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:246fa40ce8645a614ff682e0b70f37134e460eaf93a775e0cbe3cca585a67a80", size = 209919, upload-time = "2026-08-03T21:20:54.783Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0d/fc/5f74e293fced6edb51af3a46c4ccf6c23c9943774ecb375ddbd522c76add/cffi-2.1.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:471cee653ae88de62096552e6d24ccb4a5adb8c8c9f10b5054d0122c15bf2779", size = 208529, upload-time = "2026-08-03T21:20:56.066Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/44/16/29e6d01b388bef055ecd6ca8244b3f4d336bd09e92d5d892187b9601084e/cffi-2.1.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aeae0e330c9f6acd681f647d46cefd30c29f93e3392882e792e82080c9691399", size = 221630, upload-time = "2026-08-03T21:20:57.336Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a4/18/fa7f1f6857d5eb88a4ca99ffcbfb7c387a287ccc154c64a73e86314745d7/cffi-2.1.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:42a494cee34437f05546455144f2b5d9ac09b1face62bcfce597d2e521066688", size = 225134, upload-time = "2026-08-03T21:20:58.675Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e0/9f/e8e3dfa04a1b4c241f8c91faacad872b4d4efd051d49764ad4e2fd4b9fea/cffi-2.1.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:cc572dace3f60ef98d7b12ff411d20f5362feb31a0439eab0085bbfd349982d7", size = 223197, upload-time = "2026-08-03T21:20:59.968Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f8/7e/8debeb04f1ab9fe2a6963964cd6f1aaf7192627b83926586a6a4e089c9fa/cffi-2.1.1-cp315-cp315-win32.whl", hash = "sha256:4f42141fc14250de6dde5ee7ea4432be017252d91f19c5ad043c084cea629cac", size = 177683, upload-time = "2026-08-03T21:21:14.901Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e0/31/5158704cc474ab65c1647932e88be78dc0873f47130e253be38bcaf13d01/cffi-2.1.1-cp315-cp315-win_amd64.whl", hash = "sha256:e6e8cff14d6fb0be70a09c0bdc58096f501952d04624ebf867e0e56da2df8960", size = 187897, upload-time = "2026-08-03T21:21:16.108Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cc/4b/b3a2da8570c704ffc0f9762cdc3ec0f02c8573798e0b5cf7f11c82bbb70f/cffi-2.1.1-cp315-cp315-win_arm64.whl", hash = "sha256:27350daa11d4f10c540e6e89dada4c54feb7256ad03e9a4dc075ebad7ba360d1", size = 182935, upload-time = "2026-08-03T21:21:17.271Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d0/ef/5443574510a1207e6f6bc38ba6e1f1de36cb48fef07b2728bb896a21f430/cffi-2.1.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c26608d2222fb1e94487e4a387d85f13eb55d5ed725cb25a0c589ac4ee60e7bc", size = 188464, upload-time = "2026-08-03T21:21:01.163Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7e/ae/a56fa8c4686ad50e148fcbc8d3ae0d03915ff5c30d795058988c24118cef/cffi-2.1.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:4be96343e422f2dfcd12ab5c9f5aebe03f82f737c6bffeca6830b3875cb44aab", size = 188262, upload-time = "2026-08-03T21:21:02.382Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/53/b2/6187f46f2912276a3ae284076109cc5c8680482f11f766ccf26db4a86427/cffi-2.1.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:937c0052c05a31ca1daf18de3158eed4dbfcb9cc107adbea227728d647be701e", size = 223779, upload-time = "2026-08-03T21:21:03.553Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8a/f6/c3ad28bd19f77047a03084424fbd4cbe997303267c14423737324be0385d/cffi-2.1.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:df423d40ee8654634421812bc3b196da3f9bd7d32929da813f8394c4348a5358", size = 211520, upload-time = "2026-08-03T21:21:04.863Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a0/cd/ccac9013a5bd9fd764de118674ab9c805b5ca10c19270d90ee273f8b2240/cffi-2.1.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a730a083190634c65cca36ba5f489531576ebd79bcd5c8e172130f6453127231", size = 210673, upload-time = "2026-08-03T21:21:06.223Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/52/86/2976131c639aead931c5bee5aba67e4b09fbeb8018b6f282f70803f923a7/cffi-2.1.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:363e05fa78e15116c3c32c210ee36884fd6b9afa6d440e47112c3bd511d64cb6", size = 223835, upload-time = "2026-08-03T21:21:07.539Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ac/0c/33a7aeab2f9c76918c52e084beb39c570db3588133412929e8ec06fab90b/cffi-2.1.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:770de9db11e84213beec501cfcaa013b019820ca881e03344dea5844f7876d94", size = 226705, upload-time = "2026-08-03T21:21:08.774Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e3/26/2cde30fdde421130bfc18f70395731a6e6b2053c6a1978a5258ff04e72fa/cffi-2.1.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5", size = 225539, upload-time = "2026-08-03T21:21:09.911Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6d/cd/a361394c94b2129d604bb846f624a8e88255a3ee33129c434a00d715e64f/cffi-2.1.1-cp315-cp315t-win32.whl", hash = "sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66", size = 182707, upload-time = "2026-08-03T21:21:11.226Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9b/b5/ba2b299993c26577d529b6ae29841f9e15b9fcf004d65f423f4fcf94ade9/cffi-2.1.1-cp315-cp315t-win_amd64.whl", hash = "sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3", size = 193772, upload-time = "2026-08-03T21:21:12.39Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/aa/29/35e016098c814cd93de9cd320c66b5bfba14dc6ecedd3cb518fa7c408c69/cffi-2.1.1-cp315-cp315t-win_arm64.whl", hash = "sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692", size = 186360, upload-time = "2026-08-03T21:21:13.636Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/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" },
]
[[package]]
name = "cryptography"
version = "50.0.2"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9d/af/182eb91b0df3fe75c4d9f26fe70684569566745f6ba7e5c9c73a862c5252/cryptography-50.0.2.tar.gz", hash = "sha256:7b46165bb56eb4704e2eaaf86f3c940d19154535d9b0ca7d6d590b04060e00d5", size = 880623, upload-time = "2026-09-30T15:30:04.884Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e5/56/d194340cc4a57535e82e1bee9e89667ac4b7c13b5d3f59686deae3094dd5/cryptography-50.0.2-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:fa8f5efb344d6908a1ce62f4a24e2e5780f825d6f53f5f50ec5ffacac72936cb", size = 3914904, upload-time = "2026-09-30T14:43:44.339Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d9/69/c9bd862c3bf43d6399c433caf002df16e2dffd4be49bdf515cda38038711/cryptography-50.0.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:79def8d059362e7831389ed3be0ecdf58a89386e1271e35dd9f5af84e81bffd0", size = 4731146, upload-time = "2026-09-30T14:43:47.113Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/21/69/64cef1f702bf6657e0cc186ed1a2891d50d29fb41586b254e1c07adea261/cryptography-50.0.2-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:630ebfea3bf689d075f82316324ff7433dc447fe6bc1bfc76524b74b4a9567d2", size = 4719841, upload-time = "2026-09-30T14:43:49.01Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/38/6b/61a3f8d8c5e1e49a6cddccafc4015cc1c0021360ab0acb4080e7a423644a/cryptography-50.0.2-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f9f6143a8c75945eb960d9eb98905a441394abfa24afaae239d514ffb2586480", size = 4738340, upload-time = "2026-09-30T14:43:50.932Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7b/2e/7212ca32fd43dc91f2f41db20160b268098874b4c9a0e7be94d6835f5b2e/cryptography-50.0.2-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a582ab2ae1d34f67112cadc86702774c9ea4374df6bca6afe672817203c99134", size = 5367029, upload-time = "2026-09-30T14:43:52.911Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1a/f1/b474e930c4d910328780e3940da76f5aa5cbc48ce1fc14e44d239d9ea9db/cryptography-50.0.2-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4061c0079120205fb760c58acab6443e217307dcf05e3702cf970e0689972856", size = 4753050, upload-time = "2026-09-30T14:43:55.272Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7c/52/9af10e80ac16b0fcc2123f9cbd5e7afbd0fd5075bb7a607c592258a39cda/cryptography-50.0.2-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:ac9ed99d81760c62fe89d5f0815cdfa1ba9a35141cf30f1c2d044f04b4803d2e", size = 4376724, upload-time = "2026-09-30T14:43:57.24Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/71/37/6202e488cc1eb625ea110c292c6bda92823176e023f427d8d5660ce8d632/cryptography-50.0.2-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:87e9ce85beb6b328ba370cc6e6aea483c92617b4c95b1d33a49297eb662bfb04", size = 4737859, upload-time = "2026-09-30T14:43:59.541Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8f/30/e86d7d518489b0ae2497091a35287abcb1a2ce4037837a34afbe9b1d6964/cryptography-50.0.2-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f265528741e048bce55c3463ed721fb0aa45a5888d8add8cfeccb3035451bbdc", size = 5324103, upload-time = "2026-09-30T14:44:01.901Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d3/69/2c833a049475e0a3444e94c7d0aca0aa51d166374a449b09e92ac98138de/cryptography-50.0.2-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9dab55f57c74c3cad24c323bacbbd04be4705ba6eb0d92e920b1fc4837ed5079", size = 4752576, upload-time = "2026-09-30T14:44:04.545Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6c/5d/906970b83bbfc1f5bbfb677a143c181f2801f23b6a7204a3b47c42c97e65/cryptography-50.0.2-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25784ce8b9621c90c643efb9e1e2162ab3b0224cae446ad5e70e7fcb1ce18b51", size = 4870819, upload-time = "2026-09-30T14:44:06.884Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/68/e3/f2298d3bb55e0c4a91841ec4d01b3f020ba8c5fbf15ccdcc6dcf03f97025/cryptography-50.0.2-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:85d0d9a31b9098e98534226d5686b47264b95e62ce459dc2e62fdfc809f9fe93", size = 5030152, upload-time = "2026-09-30T14:44:09.443Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9a/4f/adfc442765721292fff86d314ce385d3249d22db42295c0dd057727b60f3/cryptography-50.0.2-cp311-abi3-win_amd64.whl", hash = "sha256:7afa5a6602a9f29af1f3a2965f831bae7c9d5d597b7cbb716d41ab3b7d89879c", size = 3824692, upload-time = "2026-09-30T14:44:11.671Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ce/cb/52eb3770c0d0be2702a98c6e96065ddc0a2877cf0845aa9c23397c142cd4/cryptography-50.0.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f785f6161f202ab04d8ca194158968798e480ca058943907972da5f12e2881e8", size = 3892731, upload-time = "2026-09-30T14:44:13.485Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/19/8e/aa1fc533d4546b127b45de8aa024eb5933d23eff9debfe25931e56861095/cryptography-50.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0ecbc5652bdb6fc9eaf89a7d196e20941adfe812f43bc4ca05d9150496821047", size = 4710431, upload-time = "2026-09-30T14:44:15.427Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6a/64/72bc3f75176e7e406b748a3e3830432b8c51297b38368713df04dc04898a/cryptography-50.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ab50ee449bf968271e820086f10a33d101dd060370abc10bcd22279be2656539", size = 4694824, upload-time = "2026-09-30T14:44:17.69Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4e/c6/62c77550edfa5ca3f14bf44a1e6739b9fa09d6e998a11d97ed8213bccc98/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a9f7355e6fab51f6c369b86fb7571cffa05edee2c2121e0380a37fb9ac1cd5c1", size = 4716967, upload-time = "2026-09-30T14:44:19.661Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f4/37/cce70f150c432914460157a6ecc161752e053aa5ec0ef3b3f7dc6e31039a/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:94e5e9f108ee10471288214d3d233fbfbb492840a8457eb85178d643ddeb32c7", size = 5328676, upload-time = "2026-09-30T14:44:21.744Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/aa/9a/6f2f0304d634ceafdeaf23e84537336664ac419b5d07611675c2ad3f6b7a/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:241449bf940a5d27309bd317e6f9a2af6932113818bb2b8f5c59ddc7ef16da18", size = 4727698, upload-time = "2026-09-30T14:44:24.178Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1d/de/66bcf9244d118663b2e1aaded8990f4640e3d7b7411870a5765f252074d2/cryptography-50.0.2-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d8947001be83df1394050758ce0e745dd74fb134eef0a4b5124208dfc3a68c37", size = 4354821, upload-time = "2026-09-30T14:44:26.263Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bd/e6/db28a28c7b6c676addce89136de3d8db49ea825a8c863472e36e42ead4ad/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4a20ce1e5cb4284a86692fdcba7cb8754185c6b2e5c56fcef3751cf451d3cdc2", size = 4716748, upload-time = "2026-09-30T14:44:28.447Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/30/96/01546c7f69ea0e2ab790a2e4f0934a4052fb9b388147fbf83c2fd72f1e57/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:84f964e537f916e2cc85199e5a88742e964939b575ac8598b3f9d6cc416cdaf1", size = 5285085, upload-time = "2026-09-30T14:44:30.704Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6c/01/03263395f74d50b071e9e66daace3f8bef80493e5d410726f2ba8554736b/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:828d49b0ff5a0e3975865571c5d91dbbdd0d38d8289b249a163e9425413a5e05", size = 4727268, upload-time = "2026-09-30T14:44:32.92Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/eb/94/2bfe8f29ec0cc9c0d99359c4161adf32858e4934b72c6d100d2ac0bbe962/cryptography-50.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:deb9fde5c60e437ee4821bc9bc39ff31b42135c27e1dc61ef0a629389c1de62e", size = 4849503, upload-time = "2026-09-30T14:44:34.969Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/54/44/e80651ecbf0e42b62e2bb5f5768916e07eea72e1297338956a61df361f88/cryptography-50.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8c71ba2cd31fc93748c38e1b613200ff1c2665cbfd5341fe3a61cfde35a1430e", size = 5004057, upload-time = "2026-09-30T14:44:37.064Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f8/cc/1d33befb3cd7ea7e77d2d73f43f2066471da1b21f24a6156efcaabf6d2e8/cryptography-50.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:78198641e5be9521beea5aa782bb551a58068d10e6eb04c9c680c1b69f2e7d45", size = 3795868, upload-time = "2026-09-30T14:44:39.71Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2d/49/93f6a6e7a87c9aa68d44d3e1cdb5fe8f60c90d5d2f46acae9a56892816b8/cryptography-50.0.2-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:edc3342adf8f697fc5f59c887a304356f147b397809440ed64e2fa6af2f50f37", size = 4133708, upload-time = "2026-09-30T14:44:41.807Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8c/75/32ac2a56243d778805c16ca6a32b8f74fb757df7e28d7ecb560afafb59cf/cryptography-50.0.2-cp315-abi3.abi3t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d370b8d1dfcdf7130178137f6fbee6140774a1acc6cacefc4b42643ec11d0a3a", size = 4956267, upload-time = "2026-09-30T14:44:43.693Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/aa/a4/2c8d734e43d97f0842ee9f1b7b4bfb3d0cf5e19edebf43c2afe6675c2320/cryptography-50.0.2-cp315-abi3.abi3t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f2f9bd7f90c64fe89253f0a2c05e3c4856072660429ce8831b4235bf29403a67", size = 4966465, upload-time = "2026-09-30T14:44:45.769Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c2/58/ee288c829a6f41f6235ae9dd33d82fd19b45442b65b4c8a3da36963d9f7a/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_aarch64.whl", hash = "sha256:e275096ea1e60cc595cda2836fd4a6c725d1125108b868be17f53684d164e2cc", size = 4959356, upload-time = "2026-09-30T14:44:48.211Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/92/20/9ded6d51ddd9897f6b6e81fb9ebea7951d7cc5d6c890b0ed8abf77a51a80/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_ppc64le.whl", hash = "sha256:b13478603dcd0a2479ff8e87e2c19a7d525734686fe3c49542472293a204212d", size = 5548822, upload-time = "2026-09-30T14:44:50.86Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/02/a8/8df951850d6b31d2a00218f19e2b3f999523437ed7a819df7fa427942fca/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_x86_64.whl", hash = "sha256:58a0c478eeca76fe5e07993c5a0703def34a6dc6a0cda4f5564639b33112ffe7", size = 5001199, upload-time = "2026-09-30T14:44:53.379Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8b/f9/36b3022218ce75b7cdf068fb95f809f9bd0d820e4955ef43b90c255cc7ac/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_31_armv7l.whl", hash = "sha256:d38cdff612d06fa6a32840d5e1b1f7a27cee4a349aa9085d94a67789d6bfd408", size = 4629333, upload-time = "2026-09-30T14:44:55.635Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8c/72/20f99a219f6af47cdd1cbd978c243b92d71496e168a746138af44ded4f29/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_aarch64.whl", hash = "sha256:fdd28f912fccfec1846a94e2e1e8f9b0012f557f0c46fe4f3eb0d7a87afcf90b", size = 4958822, upload-time = "2026-09-30T14:44:59.639Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f2/20/196f112617fb08eb4d608a2a6c422373d46f9cc2857f38fc0667033c0899/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_ppc64le.whl", hash = "sha256:cbc8738fd8526d80f35cb3a40d41f41a2e7030bb3b18b09a6778ef63d291c2fd", size = 5506351, upload-time = "2026-09-30T14:45:02.267Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/24/95/83378121ef3eaaaf71d4b781577ff794acb39b9e1b87a3f156898c8497ed/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_x86_64.whl", hash = "sha256:e105ab60406787da31fccc883fc0f733af1efd78f0136a4599692c4083a73d0c", size = 5000859, upload-time = "2026-09-30T14:45:05.009Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/22/f7/70fd7ae4d1dbfa7ba29b02e1b9068771519a86027756510b700ce81086a8/cryptography-50.0.2-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:6f8700550aa1474a91e5dc07049c46f98b423b5b1ddd0483e0b51362eeeaf5be", size = 5092151, upload-time = "2026-09-30T15:29:15.932Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d4/be/688367b74de86984bd58d8efacfc7c9e68b89a6a22ced0fb4f38db50254a/cryptography-50.0.2-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:c71be1cbfa5cd9a41ee452acf1eccd82b2c05950358b106ec8ceb83411d1a020", size = 5286120, upload-time = "2026-09-30T15:29:18.309Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/39/d1/55f8a3f2ef5d1529e16835ef10cf0fe3d559ce237b46dddc440c0bba3649/cryptography-50.0.2-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:c423ab384a46c4dff7217b2ea5ba2e11cffdeab6441acd04cf65a369caf0366c", size = 4111557, upload-time = "2026-09-30T15:29:20.155Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/23/ad/ac987755d00e1e64273760228d2635ae38dae2be83e3c6e0d3289d91dec3/cryptography-50.0.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:0ec5f09541743261e66e291b4a0cbf0fb2997aeaab6d9e9c740b9dba1b58d1c2", size = 3943588, upload-time = "2026-09-30T15:29:22.265Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d5/8d/6d585339bedf85d45044c85d8412dac53f2bb6f918e8b7777efba1787844/cryptography-50.0.2-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c5e67125c7dca78d199ec4e116aa93dbb83494808ecbb8211a2cb09b1bf41dbd", size = 4756166, upload-time = "2026-09-30T15:29:24.58Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bf/f1/1c1f6874e8550cfddd4b688ceb38cefb6ed15ceed224d56f133f3d88c214/cryptography-50.0.2-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee247f5c245c9a2fe7c8e2214e295918838e44e00a45a6718451e4004219e767", size = 4749145, upload-time = "2026-09-30T15:29:26.807Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c1/63/61b15dc1a8de03fe0adbe3fd7608b3ad5c73bf50993bbcb1faaa930afe33/cryptography-50.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dfe9763530994147d9af1def057a5b9658b00e8f8fe8743d144d1e0911c2e454", size = 4763638, upload-time = "2026-09-30T15:29:28.588Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fc/35/b345bdfa40c9126df1a9d33236aa98418367931b8725f84fc3ae2b98dc59/cryptography-50.0.2-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:58ddb5a8e3179d12f19e4ea34d2d32e9d63a4baa142c875c1eb59f41b7243acd", size = 5382217, upload-time = "2026-09-30T15:29:30.589Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4f/87/ef344a9e616871f2519c22d6afcda79ddd5d35e9592d95eb6e677608d055/cryptography-50.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f21e8a22c8605750c7af886bab299a363721264061b4ac0a30efb73cfd58efc5", size = 4781387, upload-time = "2026-09-30T15:29:32.605Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/90/5b/f2fdb13cd0b96f6f932c8627bb292a45f11c64d21620a8e120aee9a3b848/cryptography-50.0.2-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:9c8402a82ea0dc4ceeab793db05f0fafa8ca139ca34fcde5df0f596103c74107", size = 4403790, upload-time = "2026-09-30T15:29:34.374Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bc/ce/7e4f662b1e3c393513569e402cfc85ac7da0bd3d5435e122a3140219eb2d/cryptography-50.0.2-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:0ddc924c04591c2811ca024d62ecad4f7f6f08af8939c211438f48a16bd23602", size = 4764319, upload-time = "2026-09-30T15:29:36.149Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3c/3f/86ff33ce34cc0de6847fb96e035a1a760d81652e38643f617c02ad32ef7a/cryptography-50.0.2-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a6557e5f38e065ca9fbdaf7cfc7435ecb1d113aa81a022d1b51921ee7432e227", size = 5338560, upload-time = "2026-09-30T15:29:39.053Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/40/cf/6b5c8e2fd9202d98988ab7cb5cc5c991704c4ad55f492ff408e4969f83f1/cryptography-50.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:1981f1db4630889b9ef7803fadef12b056f428cb6b85c27ba57b774793b6093c", size = 4780973, upload-time = "2026-09-30T15:29:41.251Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/10/bf/8d6ebc7dded797bd0f0160d52188021211f011a2b164ef0ae1dac4587465/cryptography-50.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7a8701d6b584d76e909e3d305b7d126b41439876a5aaf76cddc67fc230eafa2e", size = 4897738, upload-time = "2026-09-30T15:29:43.106Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d4/aa/f3f6e0de7e6253b8baa8b2d8fb9d50924fa75cee3d4624bd4bc1208ee923/cryptography-50.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ce47f66801c20ec6c6632453bb5960fe38939e9306970b48b3a5a26de7745d94", size = 5058280, upload-time = "2026-09-30T15:29:44.827Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f6/b6/a1faf3a27ae9405fb34b1713cc73b2d8a26b04d5c561578fa2e6ef3e5bb9/cryptography-50.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:4e81d95e5bafc2d6e34e4bed780e53e4d5b9a2f928573428aa4d35fbec1eb0de", size = 3854095, upload-time = "2026-09-30T15:29:46.782Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1d/7a/f08d34ce09d60f89ebd391e2ebc6ba2b995e6dd7552f41820f8085f94e53/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:92e665960f25fcdc73725b9cec7a3824f279ba97a98653afe9ffac2e43668f67", size = 4716035, upload-time = "2026-09-30T15:29:48.681Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/45/67/e18fb65592451a2acb76e9f2fbe14e0f47a8318b4c5430f1633851d03daa/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:eef4c2f3423810b3070ab391f85436d2f8bbfcb286ac15cbc73190b3563b1f1a", size = 4726917, upload-time = "2026-09-30T15:29:50.608Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/83/28/38fdce17e60f6b825e69fc3b7f75e70a6612759980704697e1de4cbfaf6e/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:7c6d0330c472d96f6a6afe24d80dfdf15176c33096f0a4397ae4c60f3dd3be48", size = 4715341, upload-time = "2026-09-30T15:29:52.522Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b6/b1/d9121a717e0f893c64bd6ca7702614778d7df2a5c309128a002421788516/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:1ba34f04897fcdaa73f74145c25f3ec146fbd56593853e88adc2e811303c5f42", size = 4726322, upload-time = "2026-09-30T15:29:54.263Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/36/8b/e6d153808bf353e152abd2fd4d8f09670d956ac78379ac46e60d7efbf04c/cryptography-50.0.2-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:3dc4fd8058cea1644971207d530e1a03a184a805ffc8ebdddf0599d78a331b81", size = 3873097, upload-time = "2026-09-30T15:29:56.097Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ca/1d/1271f287ff7170ddafc2aad36260c4eec20ccd2fea70f38455e9d56d427b/cryptography-50.0.2-pp311-pypy311_pp80-win_amd64.whl", hash = "sha256:7b75de3c8b3be1cdb1052747c929440c3eea46c1bc2cb8a6e3a48388e9b7b452", size = 3805376, upload-time = "2026-09-30T15:29:58.729Z" },
]
[[package]]
name = "exceptiongroup"
version = "1.3.1"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/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" },
]
[[package]]
name = "iniconfig"
version = "2.3.1"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/01/e1/2069291243c926a2ff1cd706c7f3eeb9b62144bf60f77c9fb9ff2fb26bd3/iniconfig-2.3.1.tar.gz", hash = "sha256:67f4b9c50da0dedf52af349e7749a80a9057a5031199791b906c3bb3ae878960", size = 21209, upload-time = "2026-10-06T22:48:38.076Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl", hash = "sha256:9121e2c1fdb355232495be3194c8dfe87ccc2d5dee45947b78e68f499790d7a7", size = 7552, upload-time = "2026-10-06T22:48:36.959Z" },
]
[[package]]
name = "module-bank"
version = "0.4.2"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
dependencies = [
{ name = "cryptography" },
{ name = "pyarmor" },
{ name = "sqlite-vfs" },
]
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bc/a6/436bc5ffd88b5bf00afff5632a3f02a1aa679487fff4e0d4c581af973c6b/module_bank-0.4.2-py3-none-any.whl", hash = "sha256:a89fdd7a5c1b8b2b6a073a4208d42e17cbee7cbf60971436b19d236b97b11b88", size = 44987, upload-time = "2026-09-20T09:32:06.539Z" },
]
[[package]]
name = "numpy"
version = "2.2.6"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
resolution-markers = [
"python_full_version < '3.11'",
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" },
]
[[package]]
name = "numpy"
version = "2.4.6"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
resolution-markers = [
"python_full_version == '3.11.*'",
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194, upload-time = "2026-05-18T23:33:13.503Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111, upload-time = "2026-05-18T23:33:17.795Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159, upload-time = "2026-05-18T23:33:20.654Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936, upload-time = "2026-05-18T23:33:22.987Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692, upload-time = "2026-05-18T23:33:26.62Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164, upload-time = "2026-05-18T23:33:29.955Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877, upload-time = "2026-05-18T23:33:34.724Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487, upload-time = "2026-05-18T23:33:38.217Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/15/a7/9bc1cd626d7bf6869bfedf27b91b6ab5dd607758bf8e959d6fa80c6a59cb/numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8", size = 6233945, upload-time = "2026-05-18T23:33:41.331Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c5/31/7fc6239c12bce7e931463251cca4426c465e1876ba3cc785402ef4dd8f4e/numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147", size = 12608406, upload-time = "2026-05-18T23:33:44.131Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/27/83/140f85a466595a16382996a1bf06b2b54bcd597488921b0c9daaeeda72af/numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577", size = 10479528, upload-time = "2026-05-18T23:33:50.725Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648, upload-time = "2026-05-18T23:34:29.41Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902, upload-time = "2026-05-18T23:34:33.013Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992, upload-time = "2026-05-18T23:34:36.132Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944, upload-time = "2026-05-18T23:34:38.484Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392, upload-time = "2026-05-18T23:34:41.257Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220, upload-time = "2026-05-18T23:34:45.075Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800, upload-time = "2026-05-18T23:34:49.065Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600, upload-time = "2026-05-18T23:34:52.709Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134, upload-time = "2026-05-18T23:34:55.618Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598, upload-time = "2026-05-18T23:34:58.928Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272, upload-time = "2026-05-18T23:35:02.167Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197, upload-time = "2026-05-18T23:35:05.468Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287, upload-time = "2026-05-18T23:35:08.693Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763, upload-time = "2026-05-18T23:35:11.459Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070, upload-time = "2026-05-18T23:35:14.79Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752, upload-time = "2026-05-18T23:35:18.836Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024, upload-time = "2026-05-18T23:35:22.52Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398, upload-time = "2026-05-18T23:35:26.398Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971, upload-time = "2026-05-18T23:35:29.387Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532, upload-time = "2026-05-18T23:35:32.175Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881, upload-time = "2026-05-18T23:35:35.465Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458, upload-time = "2026-05-18T23:35:38.353Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559, upload-time = "2026-05-18T23:35:42.14Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716, upload-time = "2026-05-18T23:35:45.377Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947, upload-time = "2026-05-18T23:35:47.926Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197, upload-time = "2026-05-18T23:35:50.863Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245, upload-time = "2026-05-18T23:35:54.752Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587, upload-time = "2026-05-18T23:35:58.355Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226, upload-time = "2026-05-18T23:36:02.845Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196, upload-time = "2026-05-18T23:36:05.92Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334, upload-time = "2026-05-18T23:36:09.107Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678, upload-time = "2026-05-18T23:36:12.766Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672, upload-time = "2026-05-18T23:36:16.473Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731, upload-time = "2026-05-18T23:36:19.767Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805, upload-time = "2026-05-18T23:36:22.266Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496, upload-time = "2026-05-18T23:36:25.713Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616, upload-time = "2026-05-18T23:36:29.652Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145, upload-time = "2026-05-18T23:36:33.449Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813, upload-time = "2026-05-18T23:36:37.369Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982, upload-time = "2026-05-18T23:36:40.817Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908, upload-time = "2026-05-18T23:36:43.996Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867, upload-time = "2026-05-18T23:36:47.114Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511, upload-time = "2026-05-18T23:36:50.673Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064, upload-time = "2026-05-18T23:36:53.879Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157, upload-time = "2026-05-18T23:36:57.194Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728, upload-time = "2026-05-18T23:36:59.575Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374, upload-time = "2026-05-18T23:37:02.674Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286, upload-time = "2026-05-18T23:37:06.327Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263, upload-time = "2026-05-18T23:37:09.715Z" },
]
[[package]]
name = "numpy"
version = "2.5.4"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
resolution-markers = [
"python_full_version >= '3.12'",
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/95/b0/c7453d0b6e2073c3264468b106ee1563750cecc910965e67357e3698c83e/numpy-2.5.4.tar.gz", hash = "sha256:9a94cf751c9ad8ebaa835bcd3d40dacf8534ad086b88c38029b65123c7999d2a", size = 20866315, upload-time = "2026-10-10T20:05:31.422Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d0/97/ba2074e92b7befea137e77ea8471e768bbd87c339b7e8c9f5a931949f977/numpy-2.5.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c6342f54c67093cae5c0227eb0eb772fdb79f2a2c37a6eb278b9909ee06aa356", size = 17001609, upload-time = "2026-10-10T20:02:40.843Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ff/a9/bac826765e971d8e16e2064e9ac7525fd69b40ac17c905033a7f5442023f/numpy-2.5.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b11e8fda06a7d69f15ebf542660b74466c2e51094800c1fb794f47ad4faeef17", size = 12015718, upload-time = "2026-10-10T20:02:43.45Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/31/2f/5ea3570fcb8ccd0882bea99436a513b2c85dad8f774a2057849130a8fb99/numpy-2.5.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9cb18a327b49c5c337f972b03682f6a49855525faaf3c0d3e9c96cd0fd8880a8", size = 5451717, upload-time = "2026-10-10T20:02:46.169Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/34/f2/b4fc1bafca03868220b5eaf729d2f21ebd7d7b151c0f9e144fe212bbca35/numpy-2.5.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:aec3fc4b32ff82421274f5d205c559c51c840c8df66a78efd7f3612dd005a26a", size = 6789926, upload-time = "2026-10-10T20:02:48.139Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/dc/96/8319e2457ae4333c62c815c7006b869a4f60985c1e01024c2f8c6c040fe5/numpy-2.5.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe4d21ab149f15e4e6043dfb0de87e6e5f34ac176cde83060e9802981fca2ac2", size = 15695312, upload-time = "2026-10-10T20:02:50.115Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/43/a3/c799c62e19c337e6d3770b08e475887fb30ce8477d3c09efca6b2f0228a6/numpy-2.5.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fbde6962867ee75b48b0ee29b2b9372ec5d617799dbaf38e82dc0596f2f7738a", size = 16727283, upload-time = "2026-10-10T20:02:53.186Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/39/6b/3604e53fb00314d0dc1b94ec9125a1484f649c0a17480b1f0f0c7a9d6250/numpy-2.5.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:381a7a3d2e65e64c0ec302795ab9dc12bb1e73f150904699c153716177eebdaf", size = 17047890, upload-time = "2026-10-10T20:02:56.038Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4a/7a/e8b58a5289a0d464c52885de47c35a935cdd70c03a4c3ab94a5126416dd0/numpy-2.5.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b89d0aaae2fe498c648f4c4795c084db535af5bd98ef942b2a3681fb74ce8645", size = 18485839, upload-time = "2026-10-10T20:02:59.018Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6f/c9/47094f597015009f310b8c900def59065ef1ff5a6fe7b51fc65ec58ec2c6/numpy-2.5.4-cp312-cp312-win32.whl", hash = "sha256:9968ab7e49b93ac6e1c3b2239732183152c9150f16308d30b66a372cffe3483c", size = 6138936, upload-time = "2026-10-10T20:03:01.626Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/12/33/fefe62073dc8acfd0f2b9ed7c003af2f50aa61555e113e6db02b8f79f145/numpy-2.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7b1b6353e36a7e50de2973a38d705c88ee93adcf120673cee7f45a4a3fa223a", size = 12573091, upload-time = "2026-10-10T20:03:04.349Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1a/07/161270b0c2eec56e4c905f6d6d22e1b836887b2cb189d3f5820aa588e9dd/numpy-2.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:aa1cce2ff3f8d953de38b76bf44602caeb69f101430208f64a10067f7cb4b1d3", size = 10521630, upload-time = "2026-10-10T20:03:06.767Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/67/14/1c3ee0118a8fce08565a5d8482631608426a33af10a01077fada5dc7c119/numpy-2.5.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2377da2dd3ba2c1200956acbab2a358c83b8e1f8531191672d1cd6ad83250d53", size = 16997729, upload-time = "2026-10-10T20:03:09.291Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/83/8c/b0ea9477fb1f0d4484bbc5cba21678cc9969704d8d7f3f158d1db35f8e14/numpy-2.5.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7415db95818b39ec475a5eea54d9e3b6bc83e3912158e46da3438cdce399804d", size = 12009826, upload-time = "2026-10-10T20:03:11.946Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e2/84/6a3d75b3ba3dfe84ac0053450753d1e6d250a8bf80f66474cc46d1fb643f/numpy-2.5.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:6d6a71b9d9a97c03633aa12565ef2825ffa036cc1d99cfd50dacf0f128af4fe2", size = 5445803, upload-time = "2026-10-10T20:03:14.329Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/61/18/bb993f267ca20b376e07092a16793a5b31ed3138751e9ba480011a14d742/numpy-2.5.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d8200f16437b289a5bb927c6e184eccc3e8389bc0070fea4cd5b9e13c1757959", size = 6786220, upload-time = "2026-10-10T20:03:16.602Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/db/b6/135bb0953b61dc21c6cafa14b424ae666944e4899cf140e00c2b322a1a45/numpy-2.5.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2e71b04c6cad90026e544501bbe0ab9290fa8a4d845e7e8c0d124fb429c988", size = 15689178, upload-time = "2026-10-10T20:03:18.721Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/da/24/3bd070f3269dc609d8f26b2643f62ef91bb415841c0b294805aaf7fe06da/numpy-2.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6ffa07666f8da0eef81d149934a626d0d95fbd6838432a33e66245423a9062c0", size = 16718044, upload-time = "2026-10-10T20:03:21.386Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c7/8e/9d15bd356b0a019c965312b1a3c6a727cac4cae5bc40045fbc12ce4cff9c/numpy-2.5.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2fa3328f784fc8277fc48026f6cad516f5c561c5d8e2e39b3c9e0c8f23223b34", size = 17048364, upload-time = "2026-10-10T20:03:24.468Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/dc/fe/9d5b560db964f15871885f2250795d15945f8699e17ef90c0c2ff4c875b2/numpy-2.5.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b86966fbe4ad7de710422175572bcdc75fdedadfb54bc6fab7deabccddd7780b", size = 18474904, upload-time = "2026-10-10T20:03:27.895Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e9/98/d27552990f1bd611ef3e7466adadc78312ea2df63b83aad47fdc3d3ca8df/numpy-2.5.4-cp313-cp313-win32.whl", hash = "sha256:5258bc06526964be5face2fc6f756857a3f24f21ec3e72ca131337a75b165d6c", size = 6134537, upload-time = "2026-10-10T20:03:30.511Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/90/8c/140a40398a66b4471211be1affdb6ed24c486d581bd28d07b7f2fcb69540/numpy-2.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:8b4d2fd2d34e5f8c9235ee787de5631a37a28402b15cb80814df973d2be54129", size = 12566113, upload-time = "2026-10-10T20:03:32.612Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/34/52/01d205e5e8ccb27b2b0b141e801f22b830198c979111b0fa44771438d9a9/numpy-2.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:bc39ac66a7a9a3fbd6134fda43136b60ffde99c8f4501e64e0d2b24da137babf", size = 10519523, upload-time = "2026-10-10T20:03:35.163Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/99/ba/005cb5edd580d2f84d7ca3206b92dc17d4388e56e6f87ffe8f2762f83139/numpy-2.5.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c668b2f0d651605b58892644b0e302c7157f7159544227758c896982ef384b18", size = 17005499, upload-time = "2026-10-10T20:03:37.961Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f3/49/fee7587c33ee35f7977f9051d7f2023d4e7246d62710c80f20c2361ea232/numpy-2.5.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ffa6ce09a1c6a08e9667dd9c97aa0b14184e8d18f2a14b78b2a2328c9147f076", size = 12019666, upload-time = "2026-10-10T20:03:40.606Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d5/b2/c6ce165acffceb15a82c07b9cc77d391f86b3f379ba62911908ae5d34b91/numpy-2.5.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:956555e0603a4d38019ae6925711cb9dc43195c076a928accf7ea5d50bddfe53", size = 5455617, upload-time = "2026-10-10T20:03:43.138Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/77/7f/dd85ce260a669a89be06842cf355d7353a33e6cfbc590fb8ebb947d88dc9/numpy-2.5.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:2c2c4afffdeb7920e445028dd71eb932cac3e704792e964bc2a232426d4f1255", size = 6791932, upload-time = "2026-10-10T20:03:44.874Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/63/d6/34b0a2b0741386a63025a65a2c09caaaaaad6d0ca95b66cd65c30dd7fcb5/numpy-2.5.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4054173604cd8658796053f1f3bc0befb68ec1c0762c57fdad61e199256a8617", size = 15710899, upload-time = "2026-10-10T20:03:46.839Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/16/d5/928078d2b28f26829b138b4a6c3980045022fb409f570657a224ae60ef4e/numpy-2.5.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d549420b8858885cea8838a727842249218b9c1da24dd517e25c9c7a948310a3", size = 16721710, upload-time = "2026-10-10T20:03:49.489Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f9/cf/673fd1b8f4cd78eb6320e87ec4c90ac19c095644259e3749853a405c70f4/numpy-2.5.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:823874a507a84af050493b622affde94b6f7c3a0dc22cb2801381bc03b871c00", size = 17066182, upload-time = "2026-10-10T20:03:52.25Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f3/92/a77b5061b1b3e2643928c37976d79ee173e1b171ed158b7a3c61056b41bc/numpy-2.5.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4e263278bfb5ee6409db8aedbc4cc32973b1b82bc1e8d3c668551d04d83a7e37", size = 18480315, upload-time = "2026-10-10T20:03:55.39Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bb/1d/1486ef3d3fb2279fd93c4c43c1bbbf1ca389a19816696684409f71babaab/numpy-2.5.4-cp314-cp314-win32.whl", hash = "sha256:cfd73180400042a7c532d30c5e287bdd03c59ff9ee1b4c0316af0539e29dfe23", size = 6185739, upload-time = "2026-10-10T20:03:58.186Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/52/9a/e1e512ebc948d5b9dd33b08736760f0ebbed2848fd4eda1f553088a6dcee/numpy-2.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:2ca144f15135b6212a5c47b1e2aeca6e412f102f95a2d5d88d8aec77eb255de3", size = 12703552, upload-time = "2026-10-10T20:04:00.28Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2c/05/de709a982d7bbcd688a3fad71f002e9ff80c2db39e03ee726609b610f1d1/numpy-2.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:468397ba3c64427474706e5c9123fe266395496714dc684294eac75cd4930d1e", size = 10803901, upload-time = "2026-10-10T20:04:02.659Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/13/34/083570ada3bb2a30fbe5d77c8c6fef9141144a15d33e6f793a67e9749ab8/numpy-2.5.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1ef3aa6d7e29bb13677323114280b05acc57607fa2300e66432d665d5418a162", size = 12138695, upload-time = "2026-10-10T20:04:05.012Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/94/06/1f9c24db48eef0c2d1207e3b11fffb0478e39dfd8c1e1be7476936885eed/numpy-2.5.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:98b053943e5a0474ec0da309d2cb9d3f18ea57f8a2067c2ab7b5f763d1068380", size = 5574615, upload-time = "2026-10-10T20:04:07.316Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/da/0f/593fba2e1560e949123bc7d2fc48b5893d56e58cd4bd5a273d2fbf60b220/numpy-2.5.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:b64a85f40e154983960a4167d4c1d57a50c7f109b3d3264a3a984154e90a8454", size = 6889383, upload-time = "2026-10-10T20:04:09.918Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/eb/9f/b799dfdce4e05e80ed4bc815c71ff343a11533b2c0ffc221cae8538cda63/numpy-2.5.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a813ed7719bf45463c51779e6a98d0385fe905e48447526938a4b8337333d551", size = 15753763, upload-time = "2026-10-10T20:04:12.278Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/34/88/16c5f12f86f5ad2817c4d103205131fc6c8acb3d1878af05a1a4f23ec859/numpy-2.5.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9b80cdf5cedba0e90d93fa5f9a333c4d65bd545cd669b71bb97ce2b703c9d73", size = 16757212, upload-time = "2026-10-10T20:04:14.799Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ff/4f/a1fe40e18a898e6a5089f4f0d891f0a493eb0574d5b34458f0fbe5aa3e5c/numpy-2.5.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2199ed071f460487c8db2c0e5c0b564494190edb4772fe80f9aad88b2604def5", size = 17116471, upload-time = "2026-10-10T20:04:17.58Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/aa/46/e923a11c78e65c1722e7aaad817c06bd591324174b9d28ce5d31eee4d432/numpy-2.5.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:64f9c9878c1938476365e11ccfb6b770f3b9e5f045ccddc514235041e6959365", size = 18524063, upload-time = "2026-10-10T20:04:20.365Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5a/fa/84ab064514440c1f64a1b21088f2c82756defdd05e07c75ab233899565b2/numpy-2.5.4-cp314-cp314t-win32.whl", hash = "sha256:64d1c8ac28a4077cf987e0a71a7a0ef7e2df70722f07f0baa42dbb7eb6938647", size = 6340926, upload-time = "2026-10-10T20:04:22.865Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7e/7e/6cd886876f435b10685db9b9f7eeb70356f99e052116f4e5f11c5792c714/numpy-2.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:067374eb538c34c745436365cf7b0112595c1d326f21ce4ff340f61230239fbb", size = 12901584, upload-time = "2026-10-10T20:04:24.99Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/38/1b/3c1684f6a06f7307f2335fca6e486cb162847fb97e91d65f8eb5cabad213/numpy-2.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:e94aef2c639da4a960ad0db8e06471208d8589974953d78b61d345b4eb99e394", size = 10891152, upload-time = "2026-10-10T20:04:27.52Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/08/f4/3224deff3af2bef6bc0b175369698d8cb348f3d91d9bb0286cd5c9eae9e0/numpy-2.5.4-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:8dddfbee2e68d26d0d7d7d9cb247b1fd4409241cce32d815a11d97ec2cfde179", size = 17003231, upload-time = "2026-10-10T20:04:30.021Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/be/75/fee0b8c6d94b44b2fdfae74f6a4ad5a138739589a8aebaec28ce4e713ed5/numpy-2.5.4-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:81e3420b27048b65eb14c3acf0c174a8cb0e023277716110347d2dcb26026dad", size = 12018300, upload-time = "2026-10-10T20:04:32.519Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/47/c0/d0b335a499a04b65f532c3f034346ef390f81299060f928492dabc1e0272/numpy-2.5.4-cp315-cp315-macosx_14_0_arm64.whl", hash = "sha256:0b4724a19de67bea8cfc4970798efa78bcbbe2ac2613cfac16721a42d44de2a5", size = 5454250, upload-time = "2026-10-10T20:04:34.943Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5a/0e/461b3783c03d668052e6a21b01b673db6ffcb7831fd32d9aa5368c1cd426/numpy-2.5.4-cp315-cp315-macosx_14_0_x86_64.whl", hash = "sha256:2132418bf8dd124a427ca9e6a1daf9ee1a87185344c95119ceae868b99466da1", size = 6789644, upload-time = "2026-10-10T20:04:37.258Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b3/02/5dad269b02166965a7b4ca14adaddd75dbee0de42435bfecf561b84ba5a6/numpy-2.5.4-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:325518d4245b9e331387702aa58c2ce1dc4cdcbb41dfb4ccd5dcbc7e08db1266", size = 15704353, upload-time = "2026-10-10T20:04:39.616Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/93/3a/01360c8036822ed9f7aa32189a77d1476567ec1e8e1383522389e4faac45/numpy-2.5.4-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56733449d2544178beaa4545cee357370440cf056c197f9c7bfb19dbfdd0e86d", size = 16718648, upload-time = "2026-10-10T20:04:42.383Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7d/5c/b863a2c093c4d6f21a597fcaf24ead0835c09ab16a8312d5a5a8868af683/numpy-2.5.4-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:5ec3753760c1a6d8bb91200666e545c3a9728e6269dfb5d6ce02340996698aa3", size = 17059053, upload-time = "2026-10-10T20:04:44.976Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0a/60/ced4f57f9a1258a0af74f17cb0b0c2700b5c67cd6678823c803b263e4df3/numpy-2.5.4-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:b1185012870173de7ae33d370bd45b1cf5baee747ea4b97036b65f4e93016877", size = 18477406, upload-time = "2026-10-10T20:04:47.863Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f9/bd/0ef22dafaafcc7d4bb3ca26b8d2afbd55dedad8eaba99a8c864e1997456f/numpy-2.5.4-cp315-cp315-win32.whl", hash = "sha256:298eca75243f2cbbfdb460560b9fb2a1792a33cf2ab4286efd43d92e8d3df508", size = 6185133, upload-time = "2026-10-10T20:04:50.467Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/50/bc/d2651b155ecc608a77e6f4d15495c11f14f19bb98f8bf0c5b0d38f86dda1/numpy-2.5.4-cp315-cp315-win_amd64.whl", hash = "sha256:332f3378fe077dd850e677ec01bdcc4f22368fb5d50ef10b2c79230b1bf5a592", size = 12703085, upload-time = "2026-10-10T20:04:52.63Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/dc/d2/45e404f8abb26fb9eda12b94012936873e827b1be76f2ee7890be128312e/numpy-2.5.4-cp315-cp315-win_arm64.whl", hash = "sha256:d4cccbbc78717966f764cd3af4fb70276fa01fc7a2688af11c78901fa5c04f05", size = 10801451, upload-time = "2026-10-10T20:04:55.677Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c6/c3/2ae14e09cfdb67dc187a342e15308a21c15bf4d2071f8079e6aee5fe56dc/numpy-2.5.4-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:950ea81d57ef070665581b6e1b5f6a029306423cd1739c5b95fe78aa30db6b9d", size = 17097121, upload-time = "2026-10-10T20:04:58.403Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f5/cf/305ae624ef8a039414317224abe9ec9c2fe7ea3c2e1cf204d43ff6b2ffb9/numpy-2.5.4-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:c05ede731b03fb1b7591faca9389ade3267d2bddf1ad8882bb3f2cc5e101694f", size = 12135439, upload-time = "2026-10-10T20:05:01.65Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a9/a8/f75c63813aef95827bb2c0d13b12803016853056e8792c280058cdbfe783/numpy-2.5.4-cp315-cp315t-macosx_14_0_arm64.whl", hash = "sha256:5fbf7141bbfd63aea22f435c9062a032b9ea0082fe9845dad7f021d3f1234e71", size = 5571451, upload-time = "2026-10-10T20:05:04.135Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6f/0f/f17763f983868b5c49b4101ebd7e00760bd1769478a6bb6a8de6e085bbac/numpy-2.5.4-cp315-cp315t-macosx_14_0_x86_64.whl", hash = "sha256:3573cd22564692a5b899ec344e5d5b9cc4576f2985b96f22af3564ed54f2710f", size = 6883356, upload-time = "2026-10-10T20:05:06.249Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/67/a7/8af04c5a79e047996cfa38854dcfbececdd0343a7c933a46fdd03ef6f5da/numpy-2.5.4-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c109eac9cd439193678f69d70733c1108487546ca8eafc107b510ae10c1aecd", size = 15750991, upload-time = "2026-10-10T20:05:08.376Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/57/7a/648254290d0c504faa8f2d07aa206660c728802c781a6f3fc68ab7cb5d71/numpy-2.5.4-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80d6ef6e8620eb2c2b4c4caad50b5935d6db3cde2d51581b55dcc79e14016d1d", size = 16757675, upload-time = "2026-10-10T20:05:11.393Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b8/fe/4a8c3cdb0c70400cfe4c5bec42d3099a5673802a95064614b33e07b82aa1/numpy-2.5.4-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:77045a4b175bbf5316ec08003880804336c78f92281a1b72222b274ea85ec5ac", size = 17113846, upload-time = "2026-10-10T20:05:14.49Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1b/7e/619692bb67778702c0e9eb2d468568a7573f4e269386ea61aed01ee4e557/numpy-2.5.4-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:0f02a46e49cfb6c73bdb7aea1c0d3461dbae9aba613542b65f657cd3d17b9fab", size = 18522915, upload-time = "2026-10-10T20:05:17.33Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b7/b5/4da41c328788f575838f97a098fe8ca691ebc6f6fd73ad4a262ee40b184d/numpy-2.5.4-cp315-cp315t-win32.whl", hash = "sha256:ad62a416ddcf863bf44bba76fbf6b53366ab0692e294f51cae4b5fbe0d246788", size = 6335804, upload-time = "2026-10-10T20:05:19.921Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/98/94/6482ddfa3d312490cb9358f375bf2ad56427dbea8769187158e94d653753/numpy-2.5.4-cp315-cp315t-win_amd64.whl", hash = "sha256:38f47be9f74ab870d2633b5456ae519c43758a8d1fd05342f0ce4ecc034396ee", size = 12890095, upload-time = "2026-10-10T20:05:21.875Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/48/7f/c2d1b436b6e7cfebac140c2579a298344b85f2991a2ce5c3615cefb29400/numpy-2.5.4-cp315-cp315t-win_arm64.whl", hash = "sha256:7a14a461d9340f1b46b8648578aed9cdb8b3b018a8fac6c1dde2c9192a01a87f", size = 10883718, upload-time = "2026-10-10T20:05:28.547Z" },
]
[[package]]
name = "orjson"
version = "3.13.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f2/72/380b97dc45bd162d23afe5194721ef678d9eac7cfaa549fe2873f7f0a518/orjson-3.13.0.tar.gz", hash = "sha256:d1de5eb04485110c5da4c657e49168995d55e076b1ce60f1a042e254f4186c4f", size = 2732604, upload-time = "2026-10-07T14:09:25.719Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/11/8c/25b6e2bd4f6b8e67a6b5acbc11a8cff4970e35c79837a24ec7db8732238d/orjson-3.13.0-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4f66eac85b072092e9941c3111882afd7527bf926cbc717038fa3654b582002b", size = 223510, upload-time = "2026-10-07T14:07:54.539Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/32/4d/5772e32ebc19d0b76b957a48e69a09546400db35cebe76c21b2c341d1a30/orjson-3.13.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:efa160215c4630836d3b1250af4c7a305acd8239e0d75aff986b8088c2fcacb6", size = 113481, upload-time = "2026-10-07T14:07:56.229Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5a/6a/5ce6adad2c0cb734cb9d19b7b9d9c7bbdb16c136af453dd37adace806547/orjson-3.13.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4e5c8175e1574dcbe446ee654275d353c1d78bbd9a0dc9f209bf35c9df72d171", size = 130791, upload-time = "2026-10-07T14:07:57.751Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/96/49/d954f02229efb06850a5f9aaf06e77e03046a009d49eb78f499fbd798ded/orjson-3.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78a12d4f8d740cc9ae197f5223682e5e960ba61b4fb2ce5a6a3bb54e83fde28e", size = 129465, upload-time = "2026-10-07T14:07:59.143Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2f/a2/abcb0647268f334cb85768170b164e4c97f7a2ed5fddd146f79297494d9e/orjson-3.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93c70a5e22bbbbdeafc7b273441e8452a196041d67fd4d9a9c450c66370a8486", size = 130727, upload-time = "2026-10-07T14:08:00.659Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fa/b0/5672f0505e6cde410cc7916cc2fbf88d90216d667b37907df041a659db06/orjson-3.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7b3bc6b81835ce65f4729ae401607583d41139c6de95bc7453f450f1391d3e7b", size = 135280, upload-time = "2026-10-07T14:08:02.167Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d9/58/c223e3ac16193d00c1c3cbc786cb6db47158bff0558c52133e6dd0be7a12/orjson-3.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6d0684895b119ad167fb4ec05113639dc7f728022deec4756a710e838ed92e7a", size = 126844, upload-time = "2026-10-07T14:08:03.549Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/49/a2/f6fd98acef1e36b8c8ae0275f0268a0f22bb6a1b436ee4536e1cdaf31b03/orjson-3.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:7991921c5da527a963b6d4cffd0e4ea89c7e71d4be0c8be1bfe6edb223ce7d96", size = 121455, upload-time = "2026-10-07T14:08:05.024Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ce/a3/0be3b115907fea61ed340639fb0e1562cd18969bad5b3f486f808197aaff/orjson-3.13.0-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:948bad47f2e2e43527f14248364a0e5dee26dd3184691010ec4a1ebeb0fd6771", size = 223146, upload-time = "2026-10-07T14:08:06.474Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9e/f7/665935edb16163f8b764182e29a30cf056947a66893ed032191e5f01eb3d/orjson-3.13.0-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:1807c2fa49d393c7ee95fd1ef1b39cbb24aa3ccd81f30b84503ba59407666960", size = 123546, upload-time = "2026-10-07T14:08:08.324Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/67/ec/e7cde480c0e212594d17ba2b2bd210c002052e9147fc1a1aeafaabe722fb/orjson-3.13.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:637dbca1fccffe83780e806fbc0f17427c0c59bf822528eb0acc8f0aa9f19acb", size = 113290, upload-time = "2026-10-07T14:08:09.816Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/36/59/4455fb11a297af73611dfc437f0f89456220227ed1cb1544a5a0ee9d6c03/orjson-3.13.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:554948becd1110123ef9f6a6e1310fd92b2d07d2cbac6dbf65df3de75702e736", size = 130342, upload-time = "2026-10-07T14:08:11.253Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ca/80/0eec5fbde2e52407646b4cb3118f63175bdcee1e2390c2759dc96e0bc62a/orjson-3.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9d9a101bd8dbfad112170f009cd155e52bb8c936468821a0d03cbb96c0e426", size = 129138, upload-time = "2026-10-07T14:08:12.814Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cd/cc/c0874f13819ae346d69ca00d074d464710b494abd4442bdebf75ac404a98/orjson-3.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89bcf2d4bc6c9a7e1763c8cf534f38712e66b76a0fefda7fb7785462f0d635e4", size = 130518, upload-time = "2026-10-07T14:08:14.392Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/25/ab/140dd9adff84bf64b862c4fcfe2d055af6014d5ba03a075f95c9addb2ec7/orjson-3.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a79cdc4934fe81f593072c94e13da3095e9d41c2deef8f6ff2901794ca1c5042", size = 134924, upload-time = "2026-10-07T14:08:16.09Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/08/0a/e8f6deb032b1d98a39043cf99b863d8b9e842e2ffc2d2067d2e2a88c18e4/orjson-3.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:50a5202ba388b3850ba24437951727d3aa6d79a21964a30ae8dc6a059a5fd34c", size = 126704, upload-time = "2026-10-07T14:08:17.439Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/af/cf/be64b99ff75f7983488390d4ef5df72115119770eed295691c0a715d492a/orjson-3.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:a0377d6962fa431c93ecd78fdea771bb62ec545b24ee0c5d4e32acf2260af259", size = 121287, upload-time = "2026-10-07T14:08:18.843Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ca/ab/1b8ca186baf3420f12db1f2819fcc5f2cae69e4cf051168501726a64c0fa/orjson-3.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:1d84820b2ec4ac975cba482214032de5b0dbdd17046170c98e642ef9c4a4ee4b", size = 126314, upload-time = "2026-10-07T14:08:20.452Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/98/17/ed65f84ed5ed6a1e06eb628611b4172e7480fc4ad92594856751a6363cac/orjson-3.13.0-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:fb8644dc6d705e1269ed2842bf4dbe2b4e50d670de503bf79d5cef3a5148a4c7", size = 223063, upload-time = "2026-10-07T14:08:21.979Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6f/4d/9332eb96d2e379384be0f211f543835eebc81f460c9403b84abe1294c431/orjson-3.13.0-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:6ff2a2c67f35202f7d823753d38ad371a9b7fc297567cdfff4420e763cb9f6f8", size = 123364, upload-time = "2026-10-07T14:08:24.026Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b4/06/558456b7da27e974a8c9ea09117b07119f6fa131cd62b8b9ecad9eea94e1/orjson-3.13.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:65c4e0e106ccc7265b488385659117a6805c37d042f737558ecd68aa0c67ad8f", size = 113199, upload-time = "2026-10-07T14:08:25.476Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b7/f2/1187a9c09965620348262ec0f406868f6d7c234b2e9b5ee51020bdde5748/orjson-3.13.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:fbbad6b9b1da43f25c1f5b20cd5a268e028a2fc95d5a8d1ade6059973bc71584", size = 130329, upload-time = "2026-10-07T14:08:26.877Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/46/07/5d1a151bc11600434fe799e73abfc6a4d463d02e149a20e47c59d3a985ae/orjson-3.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d895cf7bbfd50ef34bb63bb727b14514f259f3e3f8dd010783bd38e864c6e", size = 129072, upload-time = "2026-10-07T14:08:28.355Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ea/8c/bb07c368abbf4021c4cd01c12edb526e00090f7f750ff1b88da6e6b6c7a6/orjson-3.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bceadfd314bd238f584fc229a4bbaf0e573597e7a026dec5429fbf29fd66c641", size = 130612, upload-time = "2026-10-07T14:08:30.041Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d2/8d/4b66d19619ed344ac000ffea7c006477d0061d580646e736ef0e203759e8/orjson-3.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b74c30e56346aad067937d766846ee74c231d1d18aad3f324e9b9261de3b2d5e", size = 134632, upload-time = "2026-10-07T14:08:31.474Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ea/88/f8221f6593e37eb26ec4706e185b9ac6f38ff0c8f7bad5459844031ffd2d/orjson-3.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4329c19b8a25693f60a77b867c9d2a3ab637b20e36f5b7bea7f5acb492b44b15", size = 126807, upload-time = "2026-10-07T14:08:32.914Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/58/9d/a1ca7321eeafd7d72e174cdc388cc96301f41516d863e7b1f64f0a1735be/orjson-3.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:b571236d8393edcd3236e07423f762bfcf571f852aad667a3bce9e7b755e0790", size = 121538, upload-time = "2026-10-07T14:08:34.325Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d0/a0/1f19b4779c910104370932fceb9ed436b47ac077f297db74008062525c04/orjson-3.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:8594956a75223f657e1e68c568c0eeb3dd145f02cd6b78a47fd9a8095dbc4eae", size = 126259, upload-time = "2026-10-07T14:08:35.765Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a9/56/f8ad2546150168858c16915c452b00eecb79597597524d1ad6ae14ad4eab/orjson-3.13.0-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:64e8f345048d988c8b68d3882e5d41028fca1219a9939b32e4a77be34c8ae8e3", size = 222892, upload-time = "2026-10-07T14:08:37.495Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1f/19/725d23160b2471a3f27026c55bb79af34687652d8be8f5f583cee5dcd42f/orjson-3.13.0-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:ded33b972cffdaf4ca0ac917338ab61d2bb10d68987dbcae641c313fbfdbf499", size = 123319, upload-time = "2026-10-07T14:08:38.989Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ac/08/e5d81a00b22c73dfcb60d80da3bd92d5a7684346593536565f184dbae3c9/orjson-3.13.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:45e34deb3437509f4ec9888dd9ee5dc426cfe21be10f1eb4ea3a9e4d33034f9e", size = 113196, upload-time = "2026-10-07T14:08:40.383Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/67/78/fda6117c69a43e470b1e9dff38dd8c5f0bc6fd8a47e4d4561ab023039335/orjson-3.13.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9825b954155b345c4759f24e5f8d652b9aec2261bb5d4e1abe06bba0a1200535", size = 130245, upload-time = "2026-10-07T14:08:41.878Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6d/31/d0cfebd456defb234414795ae7599696bf124843dfe077d0c9ece0c93554/orjson-3.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b081f0e7b600ff24513dec4ca75507fa05e904607847e386e8310d5b7b96b6c7", size = 128981, upload-time = "2026-10-07T14:08:43.716Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/45/46/f8d83189ff5b7b2ff225a58c5908618cc4e86afe09e65d17a30ac68c9da4/orjson-3.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbed5f4c4b88d94bcc36115f4c3bb3aa25da1563a5c3328aa3acebce2b083040", size = 130370, upload-time = "2026-10-07T14:08:45.132Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e6/6a/d6344c305003ea826b3fa0482645a897a3cd6d477ed74e1fe15d3322cb23/orjson-3.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9b61676116f755126b90e740a9cff36b91562f47ec330056cc88cc3b9f02f4b", size = 134595, upload-time = "2026-10-07T14:08:46.63Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9f/52/d73fa44f88d53e02d10de1cf77c16ed13204ff5bca47e1692da6b406619c/orjson-3.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ef75ed7e81dae34a3649f82df52cd85f9ac839a7d6ec78ab355b33b3b27ef7f", size = 126513, upload-time = "2026-10-07T14:08:48.111Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fb/f8/bcfc50b4ab851c4f9c0ee62f52bf3b28f0bcd0d9fe08e0ad98d4585148db/orjson-3.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:4ee06e53b998c71ce3eb93b86222912fdd9dcced685ac64d4525d36fac338ea4", size = 121371, upload-time = "2026-10-07T14:08:49.549Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7b/7a/d6927845712ec2b1e89263cd12d7203531db185dbad67f914226f2fca156/orjson-3.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:89efecad02515df7f318d0613b5dfd6d2a1acd323a2b8294712789a715945525", size = 126134, upload-time = "2026-10-07T14:08:51.118Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f0/10/98b5a3cdc086abf78d8cd20bb0cba124485d4b6a745722197bd209d967a5/orjson-3.13.0-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a7bfc7db961c7d96cb75889dc6a1e4ae1e91d87ee61da564f582bd742b8dfeef", size = 222889, upload-time = "2026-10-07T14:08:52.673Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/22/7c/7728c5280ab5202f4891ff4b0b96e2e1dbd5520dfee53edf083c54409a64/orjson-3.13.0-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:91d933e668ff0ffe164d7c2daec36beba6d1ce7fadb71538fbe142a71f8a1e6e", size = 123312, upload-time = "2026-10-07T14:08:54.25Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a9/a5/d9a44321e6f66c0f64b45be587395f87ad94cb447bce7d92286f6b97d46a/orjson-3.13.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:6c8bfe728b81b0fd58a3c7f3f9c5a113f87f2992c9948e0f28707aafd737c0bc", size = 113146, upload-time = "2026-10-07T14:08:55.803Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/80/da/d95c80d413f288feb471e16d82e5c1512d2439728e3bac917d058c31f098/orjson-3.13.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e8e05549f3b30f9d8a8e28c5aba11cc2a4b90b90961ec685ca58444b0815fc09", size = 130348, upload-time = "2026-10-07T14:08:57.31Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/04/0f/36fdfb32ad1852997bac00e3ce52c7888d8a1094ba9dcdcbb22fcc6b953a/orjson-3.13.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c749ab3ac30b5ab1ffb7677f8b92eacfdfdc5260210baa398f845bc3714c05d8", size = 128971, upload-time = "2026-10-07T14:08:58.843Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/25/de/a82acf93bdcca0c79ccff25ef0c6868d24ccbc2e72f21fae39c8cabce4f1/orjson-3.13.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58a9619d88f8818d9ab6b39d70d203789457ba13c1ed5d274f33ce9ae7e81a36", size = 130359, upload-time = "2026-10-07T14:09:00.412Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/71/ca/2bc4f7697cb9f6897bf61aca11803df096a5d971bf69ef5538b243bb1fa8/orjson-3.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2715c4808d1571029ed18fd07a82140bf3ba7def0dc89f8d015c416e3649bf87", size = 134583, upload-time = "2026-10-07T14:09:02.047Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/23/b3/12b1af9b87ff9fa0aaf4e5724c87672b30bb5de76f275f7fac64e8219c1b/orjson-3.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08bf722f923d2100bc5e5a5dcf72c656db557049c1bea26582fdd5dd9d5395a1", size = 126500, upload-time = "2026-10-07T14:09:03.863Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ad/ea/cf257fc8a7f4b18f5677c22b3a9673a1b51d4b7161f25177ed389b76560e/orjson-3.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:6adcaa85d79977659a448b4123a88eb33511a11ed2db243535ad7ea88a6668e0", size = 121378, upload-time = "2026-10-07T14:09:05.375Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/05/0a/9f4643f849e9918eab11983b83928af3aac14bedb04002e28e885ee1936f/orjson-3.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:83705c12b4afde10c62a5dd3fe6fdb21b7900bd0dcd5af1c85612ae94d0ee590", size = 126123, upload-time = "2026-10-07T14:09:07.085Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8c/15/d265f2b556c0c7c0b30ea830316d6e5af5b85dde08f234a1ebed60fab386/orjson-3.13.0-cp315-cp315-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:5ef4d4157392a0439b74f7e49e5636b4ea43d9616bd0884effc0195fffcaa2d5", size = 223305, upload-time = "2026-10-07T14:09:08.84Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0c/97/781be8b80a33b8171b3f5acea941af47182c8b4b5827c2b7c3fea706f21c/orjson-3.13.0-cp315-cp315-macosx_15_0_arm64.whl", hash = "sha256:84d87e322e1674408f85adea63f11aa19201eba082755aec20ebc217f493bbd2", size = 123515, upload-time = "2026-10-07T14:09:10.792Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/20/68/011bb98fa7da7b430b363db1bb7ef9160c438fc5c43e7468fb593c220037/orjson-3.13.0-cp315-cp315-manylinux_2_39_aarch64.whl", hash = "sha256:8c2ac5c09b017c484df1b4c68b2cf250b4e8ba08204cb58e7cd6cbbc71a9c902", size = 129222, upload-time = "2026-10-07T14:09:12.542Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/86/7f/d96fa2aedaaec14c095ea9cd48d2158fdf33c0f4fd6e7a598d899d536b03/orjson-3.13.0-cp315-cp315-manylinux_2_39_armv7l.whl", hash = "sha256:51d11525bc3ca736fa97ce4e4c7da9999cc00bf261522bede43b4e7531bd7965", size = 113152, upload-time = "2026-10-07T14:09:14.059Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e9/2d/ee77aa685c54bd920a1f0e2936986b46269adb0d72bf5098c2c694dbeb36/orjson-3.13.0-cp315-cp315-manylinux_2_39_i686.whl", hash = "sha256:ac81530647c3423107cf61c3481e91f57134e9ddfb6ef83f5150ccbdcbc3a3ee", size = 130749, upload-time = "2026-10-07T14:09:15.835Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/48/eb/3411fbfdad61b3f3af22343b5af7ed5c8a1679e35f442e8f1b229b33040e/orjson-3.13.0-cp315-cp315-manylinux_2_39_x86_64.whl", hash = "sha256:0526a3456db67b264c6d661b5f090077f326b6cd074d0ef53a72763595dec5d7", size = 130471, upload-time = "2026-10-07T14:09:17.463Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/87/71/abdc2b8c70b8d85a6cb22f404da0f52d7d712f9d49cda039a0cb1adcb973/orjson-3.13.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:dd61e64802d51d1e4f16531c64536354fc3bc67932dc0cff254044f72bf0f187", size = 134793, upload-time = "2026-10-07T14:09:19.084Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0a/2e/1c13552d8b0241083116de02b2f284ee38501ef06ebfb79893f741538168/orjson-3.13.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:c5e3ccaac3106e8fa6e2f2f6962449d7c757d7b067e41b395a19d6f0d6cec892", size = 126711, upload-time = "2026-10-07T14:09:20.645Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/85/f8/d4ece953a519d064cf690adaa68cd389d5b64fd261726334841b32978d6a/orjson-3.13.0-cp315-cp315-win_amd64.whl", hash = "sha256:7804dd1d6161da0e53b284c2aebf20f23e78eaac617300803e1467d1828d987f", size = 121496, upload-time = "2026-10-07T14:09:22.359Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/70/cf/f691388c4a9bc4af7dcc1648c4b40845869908b517d7c0009d005c7d1fa1/orjson-3.13.0-cp315-cp315-win_arm64.whl", hash = "sha256:f5c05a8fee59309f537590a1ff12d3c1009c485e96a50a9ac60dd085c09d0fc0", size = 126260, upload-time = "2026-10-07T14:09:23.928Z" },
]
[[package]]
name = "packaging"
version = "26.3"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" },
]
[[package]]
name = "pluggy"
version = "1.7.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bf/db/7fc19e6f2dc92a966727031389fc2e08b558f0f25eb7403c1119ad4713cd/pluggy-1.7.0.tar.gz", hash = "sha256:d1eaa46ebb595891b860ab086b4d09c8588af65ebd4361b8e8f4bb8920b90ba8", size = 123304, upload-time = "2026-10-15T09:50:58.343Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/40/9e/2b38731e0fc536806f16490e1a12d7f0dc2a1235aa8cc07bcc75416a7daa/pluggy-1.7.0-py3-none-any.whl", hash = "sha256:7dd7b0d8832ba3cb632c306926ded123429211b83641b35dc5c41ad2d34f9bec", size = 27082, upload-time = "2026-10-15T09:50:56.808Z" },
]
[[package]]
name = "pyarmor"
version = "9.2.7"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
dependencies = [
{ name = "pyarmor-cli-core" },
]
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d0/86/1a8937731f28e0fe6fe603da1fc4a8d27525dcd26f7b723326e8e83a562c/pyarmor-9.2.7-2-py3-none-any.whl", hash = "sha256:7cf7659f822b4da2434b2e19c3adc9f17868c5610d0e53f4dc2321ff9eefbf5d", size = 3074479, upload-time = "2026-08-29T04:16:04.021Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/de/0a/a95f56b2ecb352080d9f36d3ace313cdcc2b6662cc66ecbdebd137833f8d/pyarmor-9.2.7-3-py3-none-any.whl", hash = "sha256:7dc03740cd31ab38d6064fbd822a1b62aae7d2267aa1dd99be3055f2107b1909", size = 3075842, upload-time = "2026-09-03T08:02:02.361Z" },
]
[[package]]
name = "pyarmor-cli-core"
version = "8.1.2"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5e/32/899852afcfb2f520bc784ef55f49bd380ce8781e4c517e347eeb2b9f213b/pyarmor_cli_core-8.1.2-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8a97de083597e2b425066376f56ff351c9de9172360cb517a93821d969b4eee7", size = 510412, upload-time = "2026-08-26T04:40:44.23Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/26/14/e65e849f11a891ef0543225c2a76bde4bcd8a447d37d2d5bd84685a08acf/pyarmor_cli_core-8.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bcb9a1b5b6ff39aa11181ec49ddeff83565d9ea2a5b3ec64b80f70e91ffc0809", size = 402425, upload-time = "2026-08-26T04:40:49.053Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/53/af/7a081f160a017c112111aa626a383931de75e8519577012a5c05b39defa2/pyarmor_cli_core-8.1.2-cp310-cp310-manylinux1_i686.whl", hash = "sha256:a15145116399b7fe49d41f853e772994ab3000a590458a4deacbe42fc99dbc0f", size = 481586, upload-time = "2026-08-26T04:40:54.196Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/24/8e/74e14efca8ea8c29a1c1c821d8224c81d343e2ae2f00e6d791e5fe16ab72/pyarmor_cli_core-8.1.2-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:4e443538421b751796ae325b985e52a242e6b80bd6546bb29f4a90c761dc450c", size = 452540, upload-time = "2026-08-26T04:40:59.213Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ce/98/c657485fb0f0a23ef3cf5697be1f736bfaac7c17a67b32261f8e210eb274/pyarmor_cli_core-8.1.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:01508189fa9f7067c3ee1f682db862a6e3e35614c5db65000ae027465f73b081", size = 598865, upload-time = "2026-08-26T04:41:04.637Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6c/88/6e8ad7d885f016a227153ae7b329d63f8a170aa330e1880645bf5f034dc7/pyarmor_cli_core-8.1.2-cp310-cp310-manylinux2014_armv7l.whl", hash = "sha256:a98df839f50d3a219029dd237c44d8624a02f618bedc387d91c37fc163cb2237", size = 994978, upload-time = "2026-08-26T04:41:09.941Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6b/c0/3211b5629f6b1acd6d929c14194dc061af2d12753950530a399ed1981aff/pyarmor_cli_core-8.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c6645667885a76e607acf19ed5296d4bd5a075a48d0da1aec6c7b9428c27f1d1", size = 643979, upload-time = "2026-08-26T04:41:15.33Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/57/cd/6635530492bd4c0d0132464b2897313f47997057e4c1cd1f678de4602100/pyarmor_cli_core-8.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:43b53efc5f7fcc15a7f97de31b7bbd5ab2ff547fb4a8c27683f54211a6d3faa9", size = 439781, upload-time = "2026-08-26T04:41:20.291Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ef/65/0e956ca9d496c029152815e1a5af96435f578af6f4528ee1570173915f4f/pyarmor_cli_core-8.1.2-cp310-cp310-win32.whl", hash = "sha256:f925979a0ff416614302254280dd77091ff46e13c31e5811c11ef17be4f50825", size = 454725, upload-time = "2026-08-26T04:41:26.113Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/23/da/d1072582cf4b264dc777384e768c1a0d0a139c04d1172ccfb2d834953b70/pyarmor_cli_core-8.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:302c7390566193e0dafc690406b55ac083f5bd26ef5d1a16196e43c1b0c6536e", size = 414485, upload-time = "2026-08-26T04:41:31.425Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fd/a9/3d4c59c43efbfde6a197b11c489c82f83052dcfb3ad0f17b89f14523ae88/pyarmor_cli_core-8.1.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:0c697fd88cf7b5d556159bb68e5ac8568aead69bbc113aa8ffe7c539d34245b3", size = 511691, upload-time = "2026-08-26T04:41:47.198Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cf/37/f2777646cacd6eef89d54712d79adafa9455ff2bca8127a5700a4e958178/pyarmor_cli_core-8.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51fcb1c4713dde29acfd18404967365241720d7a3ccef044c643f7d3b3bfb85c", size = 403711, upload-time = "2026-08-26T04:41:52.132Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/50/c8/882f077406942798efe109f6d637a81d9cbec1d1eaed1cabc36cb5a361a4/pyarmor_cli_core-8.1.2-cp311-cp311-manylinux1_i686.whl", hash = "sha256:a7b1118a90b2b9c93b6a4e94ada6362ed943e9a9f1090eb399e7828fab108c61", size = 484046, upload-time = "2026-08-26T04:41:57.025Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bb/3d/0ba40c463739c8775c453754f1d5430c2beb8e040a5ec38092a5a008e48f/pyarmor_cli_core-8.1.2-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:72b3aa570bfb8237314afa6fdeb263b636c747665bde6870c721f786cc9515cc", size = 451888, upload-time = "2026-08-26T04:42:01.962Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e4/c5/a73071878e47ed4742214bd29e218ffb4488c3e985fbfa98aac654e462e9/pyarmor_cli_core-8.1.2-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:1f88c81f78634f343ce8238cc950363cc63a87aa588b847538102fb26772c837", size = 599086, upload-time = "2026-08-26T04:42:09.227Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5a/fd/331f5b07e8bdf24fd31ab1f4e207fbf12959619dfb997fd80f83a77c75d7/pyarmor_cli_core-8.1.2-cp311-cp311-manylinux2014_armv7l.whl", hash = "sha256:ced47b5a154942ca55122d807e1f8ba081b524363b5ebfdeef4364f7edb5a44d", size = 995532, upload-time = "2026-08-26T04:42:14.915Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bc/54/dd2534fcb8002b083b59fc3c4d4001b0b5e57a39b1c17cf7dee61ee11927/pyarmor_cli_core-8.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0507261ce7871223a3c0ae9c8d51a3419cdf90a0ab1bfb6c246c99da08417ee9", size = 644519, upload-time = "2026-08-26T04:42:20.288Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bd/3f/53d6f5fabbac040a6b2f7ff8b20f7a94fee7de761c4478cc6bf0ac76274f/pyarmor_cli_core-8.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:86350fea35a923826725867250de447a37f63194dbb2c3e6a345961c3be19124", size = 441398, upload-time = "2026-08-26T04:42:25.21Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/89/8f/ce7d1b62ae29919ddc229766c0971bed7c9d917d9c3bb623db1517c1eb1f/pyarmor_cli_core-8.1.2-cp311-cp311-win32.whl", hash = "sha256:9014186dacba8cc3fe8d869e01a3e4d8121f05dbc4bfce3fceeb1cda2b1cb779", size = 455042, upload-time = "2026-08-26T04:42:30.33Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e2/75/c5d0e8070538093c1570a8417b53166d965b7efda77340b999855a374d73/pyarmor_cli_core-8.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:fc6c6798631d4369bdff8249b264755b21dc05b94725d5b1351c03855d69fe12", size = 414901, upload-time = "2026-08-26T04:42:35.231Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1e/f2/3dbe618b9a2a21e7eb731b238c297cfd4da8301d2d8cf79ab4ed89537c73/pyarmor_cli_core-8.1.2-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:0bfabe4d8db7510a09419a8cbf38d1952759ae48044854666b2a9e7d1e0bfc16", size = 515507, upload-time = "2026-08-26T04:42:40.407Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/45/47/de7e83d98d15882810bd865ca782101cf7c29b69ea0db42ed5e3b3f2a0a5/pyarmor_cli_core-8.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ce0275112a7e502b18db4a67d292ee7a764dc48cd422334c02377b797a2ce91", size = 407727, upload-time = "2026-08-26T04:42:45.129Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8a/ce/326cb3f7ed988c6bfd8c7365837d2bc99f1105d8a5f029ca591201a962ac/pyarmor_cli_core-8.1.2-cp312-cp312-manylinux1_i686.whl", hash = "sha256:d68e86e01b47f91c1f7b97ed7ff4079159d813624873e4e5d5c660a089f32432", size = 489298, upload-time = "2026-08-26T04:42:51.84Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8c/71/ce177e470033c27d58d519d5f2547edd09b3415ffaa3ac9fa60fa5c7b846/pyarmor_cli_core-8.1.2-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:05212ececa5195d1c72113528b9379f691d5f7671ef3c0b5e1c72908c7a46ae8", size = 457692, upload-time = "2026-08-26T04:42:56.947Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/97/8b/3f68f677ddadf7e3730a91c98de10f048146a359e76542207eac7744b621/pyarmor_cli_core-8.1.2-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:2cf87045cc73fe096721d0e7fd972849bca84da8dde3ba478de75b4abc85e3fe", size = 604351, upload-time = "2026-08-26T04:43:07.276Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a0/f3/1f26f562840586fdcccde66c602f44f3695bcda9b23efaa39227f39e6614/pyarmor_cli_core-8.1.2-cp312-cp312-manylinux2014_armv7l.whl", hash = "sha256:18adeee02d6d829741d28a2ac7a4229020583c491eea475d4164b1d311cc540e", size = 1001047, upload-time = "2026-08-26T04:43:12.975Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/73/4b/50a847ef3a0b8fa12d1891cae2383b6efe04cfafef5bd2137ab4468cb29a/pyarmor_cli_core-8.1.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f5f4c573a435619273442f1b9f8938d31c7d0f26100a80453e6d0ae054d1e0a2", size = 649548, upload-time = "2026-08-26T04:43:20.541Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/64/2d/0ca6b9036195971200975c69c0a65a2049304458ebfa73caeeb860a94414/pyarmor_cli_core-8.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:35ac4f6ba07b20391ab9624c70bbc133537560e227579de6db51fe45535ab93c", size = 446449, upload-time = "2026-08-26T04:43:25.667Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6e/de/6969ae804814dc1842f363847484dfb05664c4ae2ecb60ac9a2ef69a1e3c/pyarmor_cli_core-8.1.2-cp312-cp312-win32.whl", hash = "sha256:609f5a63d0135d3723203f859e89f7a28c45e09474250705e995d59845d8c307", size = 459858, upload-time = "2026-08-26T04:43:30.669Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cb/b7/2f3de91992fd22f7d62d7f8c59cc47898e6a260e195c1131da1e7b6a9c25/pyarmor_cli_core-8.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:952558497cbe06bc12beaf014e2c53b20203e4ddef3c8b6028744abf72e14505", size = 420611, upload-time = "2026-08-26T04:43:35.533Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0d/7e/3d79d27c130736326503abc0ef358e4fb1b102c4a21f2e9affbfbdbd25f2/pyarmor_cli_core-8.1.2-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5dc5cbf1089a503544cadd63baf4a65953983459718d4e1aeb2954a80e8f73dd", size = 517370, upload-time = "2026-08-26T04:43:40.387Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e7/64/bd3419284798569f60ad9db0766cb7c0e3a9ea882489bad0fa8c5a754daa/pyarmor_cli_core-8.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b068b77395b3f74d4384e3c18d4684c56a4360c8fde848692444d5e6dac8d1e", size = 409769, upload-time = "2026-08-26T04:43:45.45Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b7/af/77fa373c09063b91b13579c7321c6e1a5f6f6be1851b3c72ec3c2a4cad32/pyarmor_cli_core-8.1.2-cp313-cp313-manylinux1_i686.whl", hash = "sha256:405405e44855307ee26264ea253358009dc73ad08440ba89935efa4fde87bae4", size = 489760, upload-time = "2026-08-26T04:43:51.707Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/45/8e/b411da54be4bc2143dd35dd6b3423376c052906eb7d0cb63c58dc01a31f6/pyarmor_cli_core-8.1.2-cp313-cp313-manylinux1_x86_64.whl", hash = "sha256:66d6d9c15aabb52f87ff98d4819a5ecdc32b13100348be2872c373354470d8d5", size = 458108, upload-time = "2026-08-26T04:43:56.449Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bb/69/82cd83355e1470eb5f7fb505bc8ff85a8f8caf67c0da02de86beb31505be/pyarmor_cli_core-8.1.2-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e77c780dce6c0a50824927a03e0f9c39c642e1ca8fa1671c70c03b01c2e90cef", size = 604916, upload-time = "2026-08-26T04:44:01.603Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5b/ff/8be81c5d943cc14b8bae44780249b657565690bea65ff52ee692548e8bc0/pyarmor_cli_core-8.1.2-cp313-cp313-manylinux2014_armv7l.whl", hash = "sha256:49ecb1d3f76bedee50be3047d18ce50609db16bff8702a7f096531503d737785", size = 1001331, upload-time = "2026-08-26T04:44:06.741Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/12/6e/cc65256fa20d49dc704231530c693122e31c8c18783b2f7985624d3c2c1b/pyarmor_cli_core-8.1.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:9b1f648743a32f417bf908acc5800711175edf411fa24bf729a6d2aeb24a2617", size = 651236, upload-time = "2026-08-26T04:44:12.025Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c6/69/5c6bf32fed77aed5b50aac0b97b7df0f1f72b3343e9bef4a014392e85cdd/pyarmor_cli_core-8.1.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d890021c640fb01dda0b7a4c8580b12e103bd349817bc86f01c04f3c9358cae1", size = 447212, upload-time = "2026-08-26T04:44:17.021Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0f/b6/58410290dae04ebcf85aa3084d31ba07930121a44ccc7714c738fdfe6810/pyarmor_cli_core-8.1.2-cp313-cp313-win32.whl", hash = "sha256:b3f2d55db32e6453a3e070fadeef827c147f21106e801a7f0688158f665c0726", size = 460848, upload-time = "2026-08-26T04:44:25.4Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/29/e8/d748641756c7a3353ce161b3316eae46fa5f7355a4208b7a3488e18764b1/pyarmor_cli_core-8.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:c61a86ab3f3a85550e148dd874e1ad2f2295870ad3204b1d92287c3fded3d087", size = 421322, upload-time = "2026-08-26T04:44:30.229Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/dc/ce/bbfe583c3e7ee7b76b51173c0ba9a073b222cd3a3e7d28b13e6a5373d5d9/pyarmor_cli_core-8.1.2-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:3416df1e56f10a12f42c124ed4c29a849fca1fe33bbc70e725fd0bb395d2301b", size = 522725, upload-time = "2026-08-26T04:44:43.116Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8f/16/9bbc4f0591bf28b481e079c1c38deae5fa78d23b4eade36f4fa6bcdf1956/pyarmor_cli_core-8.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c5c06fee0a63cc8c3326f6f7e44a3c39d88dfdcc64465c50e584e4fdda6c4da6", size = 414438, upload-time = "2026-08-26T04:44:47.886Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3b/26/e24b62ad18e4c8f18abdae1b12915dbdd9a7e08118082a7f427a7922a93f/pyarmor_cli_core-8.1.2-cp314-cp314-manylinux1_i686.whl", hash = "sha256:0b2a1d47555d750f6c9cc9a7883c12c4f6b81966a85815ba309ae887a0866209", size = 495370, upload-time = "2026-08-26T04:44:52.993Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5a/43/5d9dd141e4bd463092fa2c1b2da524f23e7f78d1a5180664ea9810ac7a85/pyarmor_cli_core-8.1.2-cp314-cp314-manylinux1_x86_64.whl", hash = "sha256:27b5246ec4ef12cf9190da8d0caf4c69ea09c3082ca3b9317b8b27eadb665044", size = 462068, upload-time = "2026-08-26T04:44:58.693Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ba/91/84110437e6cef14eccc4555ddeed0964e36a4d33cde2c6827dbd059d743a/pyarmor_cli_core-8.1.2-cp314-cp314-manylinux2014_aarch64.whl", hash = "sha256:f83c6a2062aaba0a905c9274e53eb89a3054cf5c6d53ec25873dc8e8991408a5", size = 609512, upload-time = "2026-08-26T04:45:04.128Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/47/bf/bef14c1746e0a1ada92e066c899cbb8fc20878d8eeeff9c2893eade22333/pyarmor_cli_core-8.1.2-cp314-cp314-manylinux2014_armv7l.whl", hash = "sha256:ed08a46cc52e9c1d22f1472452f7d0b02eb1cc3d8bd3494f20ecbb20179c8696", size = 1004951, upload-time = "2026-08-26T04:45:11.196Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ce/14/677a5246b700a30b541c4d3c83b654641946a5dc11ba8bb9efc3f647e973/pyarmor_cli_core-8.1.2-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:6ee50a3307ad6c69cdd4873a32591fea6791cbcc040f9271678b6597d9214dc2", size = 655375, upload-time = "2026-08-26T04:45:17.309Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f8/26/8b33b3f3808f792fd35847346814709a722bb981096a785aa0c8dac35deb/pyarmor_cli_core-8.1.2-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:b18735b1f3d26a6b8f3776d82a4457596972f7a2f986d729740b6443812b0f62", size = 451004, upload-time = "2026-08-26T04:45:23.568Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/30/ee/2b8260c1fafd042282eac62dad02285b2088049e6e1a8a1771e87891f707/pyarmor_cli_core-8.1.2-cp314-cp314-win32.whl", hash = "sha256:1baf50f8efde7817899313092de743129da063db2265c66c7ac06003a7b7e8bb", size = 465410, upload-time = "2026-08-26T04:45:28.365Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/73/d5/357b7850aa061eddf14b5fff3b0230eff65d62e3005e008b28f4807aeeed/pyarmor_cli_core-8.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:84a398bc0873890010f812b3190d4518bb393d3c0c17d7b2cdf2f547e65ce119", size = 424558, upload-time = "2026-08-26T04:45:32.967Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8d/8a/873ffc711607601460fffbf91895c04b754ab934bd29e8cb4de6c8cfb785/pyarmor_cli_core-8.1.2-cp315-cp315-macosx_10_14_x86_64.whl", hash = "sha256:794bf98430e6ac28ff2ab61a9771a9c30736182c098eb930d2b31d8cec8782c0", size = 522825, upload-time = "2026-08-26T04:45:39.577Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a0/3c/8f5b61f254324d308ba6164ee11c3970974356d0928bfaaed76d0c2a6cab/pyarmor_cli_core-8.1.2-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:d1bf8f4d9e66af72dcbd14dba919e64e3f415227d159b97813efd728728e8ca8", size = 414438, upload-time = "2026-08-26T04:45:44.23Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d8/33/f8a746bf9dfb634ee7b189668acd58eb730c09305be86fb70149c1c2c442/pyarmor_cli_core-8.1.2-cp315-cp315-manylinux1_i686.whl", hash = "sha256:ed12d37d5be2d8f03248541285bfed0a5545495d7e6d8352652e40f3ff320725", size = 495326, upload-time = "2026-08-26T04:45:50.413Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8c/92/5a1e5fa2fecd0819844678bfa1b3ee3a1358922d47d4925b4f3d4d1f3f5d/pyarmor_cli_core-8.1.2-cp315-cp315-manylinux1_x86_64.whl", hash = "sha256:3ba57f26a78a5ef82fc8b1a79176de9c0c32082e2874b3e71c32178cc3028d75", size = 461872, upload-time = "2026-08-26T04:45:55.631Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f7/45/770ab7dbf55e6af6b38511f14c691a26bfe60940a2e1138ab66f986fe44e/pyarmor_cli_core-8.1.2-cp315-cp315-manylinux2014_aarch64.whl", hash = "sha256:2f0b11a99e348567dc2f7d7ff32fa4c4aec5656db5369eaa8417422c654b9d00", size = 609345, upload-time = "2026-08-26T04:46:01.045Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/71/c7/e4ef2d2c43dff6ead90fb212605725646234b3d440fc38e49cf8d349b1d3/pyarmor_cli_core-8.1.2-cp315-cp315-manylinux2014_armv7l.whl", hash = "sha256:135388b7b20054be4111f78c21fb171f7718bc763edd2395bbf00760d3b059ad", size = 1004824, upload-time = "2026-08-26T04:46:07.942Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e7/11/77da23b337acb01a6a2dcb372eea995bb8bcba4cdd7772cefccb5daf4eb9/pyarmor_cli_core-8.1.2-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:748c2906e29ef4de3bc83e7d39014d4416fa6a76ab1d07770067982fefcc1246", size = 655255, upload-time = "2026-08-26T04:46:13.011Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7a/45/c24cd03929ef50bb34407e58393121179b628c2561fecd7acda062408df7/pyarmor_cli_core-8.1.2-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:9ae31de935d673420cf3b5592a00ff2546d040c65923431f799966ff6e2976e5", size = 450939, upload-time = "2026-08-26T04:46:17.739Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/51/7a/31ea2e2983da14c6d41f4b350d2e127942c504e082b2c46aee0a836ba254/pyarmor_cli_core-8.1.2-cp315-cp315-win32.whl", hash = "sha256:816b34c1565c26d1a302ceae71e1791b4447606bfe8f6548ed4b0ed49ab4b372", size = 465364, upload-time = "2026-08-26T04:46:22.582Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5e/88/18309ad305f0ae6f462d62128d7cb882222ff0a29e8aa3461c60cad14c25/pyarmor_cli_core-8.1.2-cp315-cp315-win_amd64.whl", hash = "sha256:1a288d24fb3ca77f5060976eff3de70fc0a916074fdbe2485649a3cf9c383864", size = 424465, upload-time = "2026-08-26T04:46:27.282Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7e/58/7758001a43902bf162a41d6950cae17bf46432250c37800092b0aea7d815/pyarmor_cli_core-8.1.2-py3-none-any.whl", hash = "sha256:1f07c1cabe45511a4a91a615ded15adabeb8b5b93c4732dd633c4913299185bd", size = 5178, upload-time = "2026-08-26T04:49:14.149Z" },
]
[[package]]
name = "pycparser"
version = "3.11"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/da/a8/c5fdbeee588bb8ada9458774f43adf1bdd30bd59157055142183e769a024/pycparser-3.11.tar.gz", hash = "sha256:d875f09c3507d00e1aba0eecc6dcadc1352f30fff09dc6bff2f1c2935e97c2bc", size = 113796, upload-time = "2026-10-09T12:56:59.539Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/90/11/0e6f11117525ff0eec40ebac3d313376f102df93ca44ad9e893ee85e4f89/pycparser-3.11-py3-none-any.whl", hash = "sha256:51d5a8ba2be0bbe440b99d2112604c95bbbc3c2748a64260186c541e1729cd80", size = 51178, upload-time = "2026-10-09T12:56:58.131Z" },
]
[[package]]
name = "pygments"
version = "2.21.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" },
]
[[package]]
name = "pytest"
version = "9.1.1"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "iniconfig" },
{ name = "packaging" },
{ name = "pluggy" },
{ name = "pygments" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/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" },
]
[[package]]
name = "sophon-arm"
version = "3.11.1"
source = { registry = "http://124.71.68.6:3000/api/packages/chongming/pypi/simple/" }
dependencies = [
{ name = "numpy", version = "2.2.6", source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }, marker = "python_full_version < '3.11'" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }, marker = "python_full_version == '3.11.*'" },
{ name = "numpy", version = "2.5.4", source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }, marker = "python_full_version >= '3.12'" },
]
wheels = [
{ url = "http://124.71.68.6:3000/api/packages/chongming/pypi/files/sophon-arm/3.11.1/sophon_arm-3.11.1-py3-none-any.whl", hash = "sha256:75ab4514562578faf9fe790ca0e147b0db481e691f1b4d165e2c675cd51c7d5d" },
]
[[package]]
name = "sqlite-vfs"
version = "0.4.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1a/b2/d4d1dcdbf13d9a9884b30d5cab90d518d86e5f1ec3531acddfbe8ebcdd1a/sqlite_vfs-0.4.0.tar.gz", hash = "sha256:970cc30d0cd16b3bc9f02b08af37eb8fcf89523b6c17740a86cd9cc260d544a4", size = 740965, upload-time = "2026-09-20T08:13:35.884Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/37/9f/5b32146512efc290df1a7c654f603317346d17b04684ea55dff19affe18d/sqlite_vfs-0.4.0-py3-none-any.whl", hash = "sha256:d2584e8d1cf5908f30aea6db6b95eaa01c62be426bde41e5ff7d226d40128a31", size = 94794, upload-time = "2026-09-20T08:13:34.216Z" },
]
[[package]]
name = "tomli"
version = "2.5.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b0/78/9ad63712633ed3ab5cc1a648d863d7e7da371e9425e209555a0fe711b695/tomli-2.5.0.tar.gz", hash = "sha256:264507556cd8b8c8e7c6ee037cdf443a463f03f4c958e57195e3d369711b8ff6", size = 17662, upload-time = "2026-10-07T12:23:37.892Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/22/a6/ab99b60ee52acd949684febabc3005d0045d0f66bebd9cdebd67372d26dd/tomli-2.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c4dc1c1781f2f716de763d1e9a7b34c6a894e167e291c7c5d16c72f7a9538545", size = 163901, upload-time = "2026-10-07T12:22:15.601Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bc/00/ee01b7ed4579180fff07142d290257f25ba786f23f3ec6005f620933c2f5/tomli-2.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eff8babca5a7999bc137acbc7482a8b7e17ffca5075ab41f5d770ab408c7bfef", size = 163756, upload-time = "2026-10-07T12:22:16.957Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/72/c2/4efebf65372f6583185f79799312109dddb61102d47e5c33dcfd1a297aca/tomli-2.5.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:86665cee9c4835b7a7f1e8ec2c719b5258d4dc782887aded5a8ae7352a96843b", size = 268038, upload-time = "2026-10-07T12:22:18.135Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/53/07/5850468e925d898abb36038666f9c333a94d2a223e802a8ba5b6d319d23f/tomli-2.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e369fd63331746182360977b1892bfc215476a30d61612d732425311639f56", size = 276422, upload-time = "2026-10-07T12:22:19.567Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b4/87/f293984cdcf83c054196d4fd3dad44fc68ae55b4b8c44bc76cef360c3150/tomli-2.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ad1ea345759240d6463efa0ed1c704402752e49aa21476620738d74d72d8aa1", size = 272616, upload-time = "2026-10-07T12:22:20.794Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ce/ce/db582886b3c1219d3fec93ebd669332482e5aee7a91e0f7838d84f2d1759/tomli-2.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:96243987194634bd411066ce40c952e108f86af04db533ecd8ac3ff2a85b1885", size = 276593, upload-time = "2026-10-07T12:22:22.12Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/bf/72/7619b87dea4261fc27dd7b54c4461c129c1f7d9bb7ba3aec89c797a431b8/tomli-2.5.0-cp311-cp311-win32.whl", hash = "sha256:610b27d99f28ec5f191c7064a48f3ddb179a1fe6ca73d571483ae859f57b605e", size = 101830, upload-time = "2026-10-07T12:22:23.651Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1e/74/220106da34502304b6751a2a9b8a9fbca6c3fd47e737a2e2e3da7c61c9db/tomli-2.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:c804ae44fe7b4bab5da295e4f980a1ff04670bca9d23fe0a4e887e08ebd741a8", size = 112742, upload-time = "2026-10-07T12:22:24.972Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/27/99/7d9c8b41837a7773613e169504147375c157a290167aa59ad74a085f521f/tomli-2.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:cfac177ebd6236003846ea339981f71457cb6eb748f23381eb257e45092e3980", size = 109332, upload-time = "2026-10-07T12:22:26.117Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/52/ed/7baa86f87493646a594de388c7c1c40a39dd0461f7e9c0359cbeefc91fe8/tomli-2.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f4a40d03fb9f63424f0979855bdeaf44dd7696b8d59501822c10ed30ba532df", size = 164854, upload-time = "2026-10-07T12:22:27.444Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/a5/b1/44c0341f2224397855723c7a8a39f718ea6fcbcc3dacc66e5aeca0f334e3/tomli-2.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ebf8d19b17bd0daeb7b7dec81a946a439b753942fd0210d6e96c532249eea6b", size = 164074, upload-time = "2026-10-07T12:22:28.679Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/23/04/e2d5b7d3fba47adedb23de616c16d428ea076c79a3d8e1d95d649ffe197e/tomli-2.5.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf0b5e8e0f68ebb494356e577c06c139161efd8d3b9050f93b39b7c26cc54ff0", size = 274274, upload-time = "2026-10-07T12:22:29.804Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/43/90/6090e706ff27a6f89f4a40578e3324b95c3cd8c4150868aabf33a8f414c3/tomli-2.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cf74416bdc94ae458b14e37286c1073081850ac8459a00d0c5efef5d44294c6", size = 286435, upload-time = "2026-10-07T12:22:31.297Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/0a/9e/a2c40768df16c408f22430afb0a73e9d7e5f79c950884954649d1146b74d/tomli-2.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:61ea1ebe1e55a34ea8199cc8dbff398d35027b82271c8ac4802fd3a1fd5b1bcc", size = 278119, upload-time = "2026-10-07T12:22:32.601Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/12/25/3c0cb485b98e9cfac495629b1c93c87ccf0b72fbe9d2689fd8fe62c6d5a3/tomli-2.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed53f7e89bb04f6d9e8e7799112360b0c4d5cbff067de0814c98c37c39b920f7", size = 286177, upload-time = "2026-10-07T12:22:33.745Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/77/8b/0144c65f0e37e51c18d04ae15c21b19431c165002d0131fe9aa8b0b8b1e8/tomli-2.5.0-cp312-cp312-win32.whl", hash = "sha256:e7ad033e27a516a233bea839cdb77b80146facb3b4f40bf02cd0cac165cdd5c2", size = 102760, upload-time = "2026-10-07T12:22:34.887Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/de/32/5d6d8f42fc9a05fce69354e00ff256484192f5f2fc9a2165718fa0de61ec/tomli-2.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd05de8c1698f8413dd7d869492693a0bf2211543b787ac78cd5e7536af1a6d7", size = 112722, upload-time = "2026-10-07T12:22:36.162Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/30/65/df18032218db0fb9b769fb23c8039a051f15c811993995ea04c350273a32/tomli-2.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:069435bd5480429b98c5e5afb02ab21c219b6f0064680671c6dc0d46817346ea", size = 109534, upload-time = "2026-10-07T12:22:37.296Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/42/e5/51736d70da209350969e15aca5c5ab6e2ce1ea87a0a892a6c13aec172a86/tomli-2.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:943276cf269e0071948d9ff697159c1735e623c1151d88abb09b74659ef0cbea", size = 163328, upload-time = "2026-10-07T12:22:38.373Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ec/55/086f80dab4ab497602644274e6dea7ec5dd0b4e262e443a8ad3bb7edee2d/tomli-2.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:463b16086865b97facd8d0b3fb4cb7c544e3f58d2a69dc3113d6db9653fdb043", size = 162246, upload-time = "2026-10-07T12:22:39.673Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/aa/eb/3ecc94459f3635c92321f4e7bde571323fdb2267c50e19e3188a281eae3b/tomli-2.5.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1245a6638fc4bb0a60af38a7d45413db34a13842027c77597c712c998c62fdf0", size = 272655, upload-time = "2026-10-07T12:22:41.08Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c0/d7/494fd1f0c37a621f1ad9975c2efadb523e8101f144ed6edb2e7fe64738f2/tomli-2.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d8bac3d603c97e6854424e5b2b5b741bdbde387e09f162fb0446812b4a8362b", size = 283595, upload-time = "2026-10-07T12:22:42.222Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/70/51/bb8d62b1317e6640866f6949b2d5855e5300f2c99d46de1cd245570bba65/tomli-2.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:21e4cae4114aba25aa0d4f85cdf486d290fb35c0954d7bba536248da64d43066", size = 276253, upload-time = "2026-10-07T12:22:43.625Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/66/f4/f46bd7f0763cd47de2db697dca9257c6a4adfd1a93b018cc75c8190ed5a8/tomli-2.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bbaefc84548d754be821bba7c4141c4787dda182f9e77f2f87b71213529efa7b", size = 283582, upload-time = "2026-10-07T12:22:44.983Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ac/03/70f2bcb2923a6db37818d917e124270a7f4cfd38ea576f5aa753a91c0ef5/tomli-2.5.0-cp313-cp313-win32.whl", hash = "sha256:abdbf6313b8d9efe157edeb7ab6eae4de064b1300ad31abf73755154b30abe68", size = 102628, upload-time = "2026-10-07T12:22:46.508Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/dc/98/d52024bb5b0ff68b4f0d276d867f634c84a67319a7e9f6b7708a37742333/tomli-2.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd4dc129784e0c5335bd4e61dfcc4487499a013419e655cf2da1d091b7e0efdc", size = 113301, upload-time = "2026-10-07T12:22:47.647Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6f/f2/540db3a70572a8c23a28aba3e9c358ce0ffffbafc990905c1343aa265b31/tomli-2.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:69491c143d2fe063046e0301e62a810bed338fa4d1ce0fd870c27dc1e09b0d84", size = 109744, upload-time = "2026-10-07T12:22:48.925Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e4/49/caf6b307766eb9567664a8707e9d6be5fcc0e8903f18781c6677a60d80c7/tomli-2.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d3182ee2d887e507bd67319a0a61105d1dd33facc111329559a233b772c1a105", size = 162899, upload-time = "2026-10-07T12:22:50.088Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d3/c8/68cfce773a2733a49c74f99d627fb461bd990756860099eac25617889585/tomli-2.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:521345fd1f19d45b8df87657aaa38b6f2ca3800059fadf428e7ebf479a383646", size = 162080, upload-time = "2026-10-07T12:22:51.558Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7e/b2/e5bb8651fdad593f670501a7d718b1a7f73f064d44dea15e04c04dfef45d/tomli-2.5.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e95c7614e705bfe2b04b27aa124adec59752d15813df37e2156747cab3a006b", size = 273380, upload-time = "2026-10-07T12:22:52.918Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/8d/d2/9e2d7f8b1dfe0e2b34c245986ebd55c4c553ea4ce6c47c443b332673253f/tomli-2.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ac2027d37c3afbdf4bdd377f2676f6f1d2122a5be1f1137b49dced590b37e75", size = 283228, upload-time = "2026-10-07T12:22:54.173Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ba/df/ec7b876b7b1a2718bd74a3743c076fff565b04029ba33e8f61fac262739f/tomli-2.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c414be4ed9d3cac80c42e348fa5a956117d1a48227f48026e31f59cb4a7671eb", size = 277189, upload-time = "2026-10-07T12:22:55.342Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7d/7b/e192d9eed0b9cb80da799f4d77052297fb9a2c3cc9b19f571f56ea88add6/tomli-2.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9b03d7dc168353b4132965bde20feceabaa470e570c6f59660dfae59b1f9eeb3", size = 283632, upload-time = "2026-10-07T12:22:56.735Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/84/50/ff94454e75461d75623e47401ed323d65c10aab8fe9033242c20cd2fdf32/tomli-2.5.0-cp314-cp314-win32.whl", hash = "sha256:6f041843c4d3a37245c0c056fd955b186bf8b1fb85690cbe40b81230891dc34b", size = 103535, upload-time = "2026-10-07T12:22:58.084Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/54/0b/bdacf05f963bd6026ebf6eeb0beda847d1d60e03e440725c64a4e08a0afd/tomli-2.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:f4b653094e18f9031102d3a1da5c729c8f222d85225b18037dac621695e46e1a", size = 114621, upload-time = "2026-10-07T12:22:59.2Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/61/99/53f438fa6ae4f9d4ed0ddde3e7242b3bdc34b48c8f9948b72b9e9b127676/tomli-2.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:3f89d10c1ff6a38d992c27fc8a4816af71a909e08a40ec66934240b1e74347c3", size = 111572, upload-time = "2026-10-07T12:23:00.479Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/b9/20/1f88f19427d380a40e90a770e087489eaafe4aeee070ae88ed2bbec00acd/tomli-2.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:e9e15b4a6c7dd6b85b5fbab29488a73f1f70de516942308daa266bf0e0aeb0d4", size = 171814, upload-time = "2026-10-07T12:23:01.914Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/d0/56/cbe5079c9f9a54b9b3e27fc82f08f3cb36edee75561679f53d2380c801d6/tomli-2.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e12bbcd32897272fb05929110362ae9ff4c1b9bb26bd9e971e71dcd3275b4c3d", size = 171324, upload-time = "2026-10-07T12:23:03.18Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2b/30/1d53fd3b0f1cb3ba542e345ec32c26aefdddc4e829e4f3429af8a4f27782/tomli-2.5.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20aa36de8f2cf87237143bc1fa1aae8d6612c09118f4da21c6a684db5dd1f6f9", size = 297441, upload-time = "2026-10-07T12:23:04.345Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/66/d9/0800acb6a111686f764c1b91ef15cc42a20a66a46013bb42220f1d2c61c1/tomli-2.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22185fad8a1e622f064e78008018a0dd3323550dcb479cb7a1d296888d74024f", size = 307476, upload-time = "2026-10-07T12:23:05.671Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/e8/63/30a8f3cd51b5bec37f04744bad0b0dc6160df84aad4f27b0e9283d66f221/tomli-2.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:984012f71908165449a951de2050d52f276bfe3aa5d5f570f63ddad814370374", size = 296113, upload-time = "2026-10-07T12:23:07.202Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ab/18/0b9ffc597e69c5a1e20a7823cb60d54b39a9f54e91edcb8574f022186758/tomli-2.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f79203b3965b4000e91808aaa7c040206093f2b8bf86f455982f2274c9ccf442", size = 307725, upload-time = "2026-10-07T12:23:08.508Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ab/c7/18f8baae0b5607a60e8e19b4a7fedee43a8ff6458e3896dcbbadeeac9c22/tomli-2.5.0-cp314-cp314t-win32.whl", hash = "sha256:91294a9fb94a75542f6e46e4a2ae709bd8d9b51134098cae5cf3bea5478b6d03", size = 108546, upload-time = "2026-10-07T12:23:09.956Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/72/34/4cca9739254130627bde87500b3f2b512154fe2f278efa7e2a5e10ad4bcb/tomli-2.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f15e3e0b835a6d68b10c86bf80a3149780498d6911c93c3ffd1861d19f9200f1", size = 117814, upload-time = "2026-10-07T12:23:11.486Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/7d/fb/afa530d47dd80a78fce43beac6bc6e00f84558eafcffbc6f37b21e80d056/tomli-2.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:6664b7ae7af7294256c53960a6103077f4914cec8ff98479c352f622c6f6b2f0", size = 115188, upload-time = "2026-10-07T12:23:12.728Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/66/98/316fdc00f8c0939e6fe50461dd343c162d3ad51d1286eb25b7db54361d50/tomli-2.5.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:a525685c2f97da40762b8695eb7aa0af4c8344ca1905c73e4e29cb04d34607dc", size = 162775, upload-time = "2026-10-07T12:23:13.941Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/c5/22/7b10fa5bb01c9539f53f69b619361b19350acc73657772ea7ac70ba309a8/tomli-2.5.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:9dbb18c1cfb2f6517942fc9314437f66aa06d94436ffb1f06102ef3572f35276", size = 161406, upload-time = "2026-10-07T12:23:15.215Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/9c/e7/1a069d86dfd20f1f84f71c63faed9f83c1d890bc06c27d82dc7d888fb573/tomli-2.5.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:752e8b1aa6a4367ef8bf6a1a1e005540f7ed055ba36d7193796812ca5404eb52", size = 273855, upload-time = "2026-10-07T12:23:16.471Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ae/83/d1ef43d1687d092ab9c235455c76e6e709483b346b056f086095c7c263a5/tomli-2.5.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c47300f9bf791808f77d82747691c4bb09cb14bdf3060cca99b42cdc4361d5a7", size = 284910, upload-time = "2026-10-07T12:23:18.166Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/cc/05/f4d9cf7de61822ece0c3873f30d291e324911c71a378b8bfe5ced13fd9f5/tomli-2.5.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:19b0dd8749f4ea2f112c5fcfb3c5248390c899d7e2e173f1d91abee1fa0ff391", size = 277723, upload-time = "2026-10-07T12:23:19.355Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/42/28/78262493141fa543151cf005760c3cb01d09fc28a11f993c05109902cb8c/tomli-2.5.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:57b1c3b01fab802e2899bc3d168dca320e14165e2fd9fd584760fb4ca5826859", size = 285115, upload-time = "2026-10-07T12:23:20.698Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1a/b9/e1dab9a30bcb677b5cc5cee810609cfd64f24306a3055767dd3fda00b1e0/tomli-2.5.0-cp315-cp315-win32.whl", hash = "sha256:667e521b37a6c5ccaa044202c235b530f90177ffe2cd4a64ecc213c7dd535feb", size = 103475, upload-time = "2026-10-07T12:23:21.941Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4c/bd/31a3790c11d6ea95fcf5e6022ac0f8d0543c9b61120b730fc481bd43d3b4/tomli-2.5.0-cp315-cp315-win_amd64.whl", hash = "sha256:d747252933c8a65ef6bd8da0fbb7ce28a90eb6119d8cd00772cd528aa07b68d5", size = 114589, upload-time = "2026-10-07T12:23:23.098Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/47/a2/4f6310fa699364f0e3af7ee3af88dddd9af066d33e716a0265bbe2b3ea84/tomli-2.5.0-cp315-cp315-win_arm64.whl", hash = "sha256:75dbcde8751b0a960aa3de173aa5e894d590755c6d7758b7e774c06f1dc3cbdd", size = 111493, upload-time = "2026-10-07T12:23:24.233Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/68/14/00853f0b396d8971107ae1921bb5b322fdee1650d2f16bf06c20adb532e5/tomli-2.5.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:2419c2a189551987b59d80e63ec355671283336f41c6b9b89462df679c7d0c57", size = 171380, upload-time = "2026-10-07T12:23:25.512Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/89/ad/fa6949321dadee46b27363974fb197b94c911c3b0f7a5fd26d7dc18fc2a0/tomli-2.5.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:0dc598040da8d42cf20f0be588ed7004f46db12a0ac6c32e03a59dccedaaadcd", size = 170553, upload-time = "2026-10-07T12:23:26.855Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/53/aa/3056c919eb3e084df3752b2cf5f865dcc04af0b27dba2f66d7b28af4633a/tomli-2.5.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49096930c8d886c9bbdab62d2d0d17ce823ddeea522309a190b36245d5b49e01", size = 294428, upload-time = "2026-10-07T12:23:28.132Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/96/b2/faeeb5d8769ea3832021d73e892c8391eae7b4b4f8b55a789127bd8b18a9/tomli-2.5.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8ade5023067f99fe72b88accd30d0ea05a158e9e32a11f124e731ea9695313f", size = 304909, upload-time = "2026-10-07T12:23:29.381Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f6/52/f094c09e73fb654b621716d019acb5d29bdfd1be01df80c281d552bda48d/tomli-2.5.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:b69564772b5c8f22ea5f498dff08cfa825045b4d4c4400529000bdf818aa3b2a", size = 293220, upload-time = "2026-10-07T12:23:30.608Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/86/f5/0c30541078ca4b505ce3bd76ed931facbfec524dd018535d691d1af0a6d2/tomli-2.5.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:8ff3a2ca028c7eee0c777f9a092038d0a594a9fa04e215f929a22c329e2cb142", size = 305705, upload-time = "2026-10-07T12:23:32.181Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/05/74/590e7d19d6a118fc5cc5704ff358e21d95b8573f6b9443b1519f29ca8825/tomli-2.5.0-cp315-cp315t-win32.whl", hash = "sha256:62fc1bc8eb03e3a9cadfca713d65614ed8e09d974a283295ffe3a831976b4dc5", size = 108432, upload-time = "2026-10-07T12:23:33.496Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1c/b8/63a75cfb27a17c38550e44025d3a6e7be64516fd8608a3b75703bf37d81b/tomli-2.5.0-cp315-cp315t-win_amd64.whl", hash = "sha256:f3fcbc57b1791fa6cbe5d8434179d51de12be1a4811469529f47f6e7487a2571", size = 117281, upload-time = "2026-10-07T12:23:34.648Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/72/01/e8c1debb2173973372934c68fc8e46170ab60ef23ed4592dff4dec6e8993/tomli-2.5.0-cp315-cp315t-win_arm64.whl", hash = "sha256:d2ba24db8a9376921b5e87b4762b9adb0f3f1deaea68f2b8b0bb2c11efb9c3e7", size = 115069, upload-time = "2026-10-07T12:23:35.77Z" },
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/60/3f/3e3f8fd0919249b0200c80fbc4f9a1e70be19f9883da71dfb7f8b9ab8aca/tomli-2.5.0-py3-none-any.whl", hash = "sha256:32a7b79ac57a2e83670ce329ccf675798bc5a2094783a63676866b70503f2e2b", size = 14765, upload-time = "2026-10-07T12:23:36.875Z" },
]
[[package]]
name = "typing-extensions"
version = "4.16.0"
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" }
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" }
wheels = [
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/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" },
]