Compare commits

...

117 Commits

Author SHA1 Message Date
noneflow[bot]
a9a86aba61 🔖 Release 2.3.0 2024-05-01 09:01:32 +00:00
Ju4tCode
6e95d5366c 🔖 bump version 2.3.0 (#2688) 2024-05-01 16:55:17 +08:00
noneflow[bot]
445711e1cb 📝 Update changelog 2024-05-01 08:17:06 +00:00
Bryan不可思议
dfd2096fe5 📝 Docs: 数据库最佳实践 (#2545)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Ju4tCode <42488585+yanyongyu@users.noreply.github.com>
2024-05-01 16:16:02 +08:00
noneflow[bot]
d469c6f287 📝 Update changelog 2024-05-01 05:39:50 +00:00
Ju4tCode
9655b941f3 🐛 Fix: none 系列驱动器启动失败时未退出应用 (#2687) 2024-05-01 13:38:47 +08:00
noneflow[bot]
4254fdfd8c 📝 Update changelog 2024-05-01 03:35:49 +00:00
Tarrailt
1b3cd7e2e2 📝 Docs: 更新最佳实践的 Alconna 部分 (#2686) 2024-05-01 11:34:33 +08:00
noneflow[bot]
897498b7f5 📝 Update changelog 2024-04-29 12:27:53 +00:00
Ju4tCode
34770e4463 🧑‍💻 CI: 修复 NoneFlow reaction 范围 (#2685) 2024-04-29 20:26:35 +08:00
noneflow[bot]
9d14f72249 📝 Update changelog 2024-04-27 09:58:35 +00:00
colasama
87f6e81ffc 🍻 publish plugin 表情包保存器 (#2683) 2024-04-27 09:57:30 +00:00
noneflow[bot]
c3373e141a 📝 Update changelog 2024-04-26 03:12:24 +00:00
StarHeart
a5f2d97b04 👷 CI: 修复测试 (#2682) 2024-04-26 11:11:13 +08:00
noneflow[bot]
80ac6a5ae9 📝 Update changelog 2024-04-25 03:13:26 +00:00
cubstaryow
496475e0ca 🍻 publish plugin HelpWithPic (#2680) 2024-04-25 03:12:15 +00:00
noneflow[bot]
982dbbccdf 📝 Update changelog 2024-04-24 06:57:06 +00:00
cubstaryow
3f9c20c60b 🍻 publish plugin cyberfurry (#2678) 2024-04-24 06:55:56 +00:00
noneflow[bot]
cabb3c6c45 📝 Update changelog 2024-04-23 04:37:41 +00:00
afterow
03bf1fdcfe 🍻 publish plugin 三爻易数 (#2674) 2024-04-23 04:36:36 +00:00
noneflow[bot]
f36f8d1bcc 📝 Update changelog 2024-04-22 07:59:57 +00:00
Ju4tCode
5c2c1770a2 👷 CI: NoneFlow 添加 reaction 响应提示 (#2677) 2024-04-22 15:58:48 +08:00
noneflow[bot]
6810af1e1d 📝 Update changelog 2024-04-22 07:27:58 +00:00
Ju4tCode
78ba6ce973 📝 Docs: 添加 OSPP 2024 项目说明 (#2676) 2024-04-22 15:26:48 +08:00
noneflow[bot]
15bcb7e374 📝 Update changelog 2024-04-22 06:35:01 +00:00
shi-yingyingjiang
7dd7ccbff5 🍻 publish plugin 战双表情 (#2668) 2024-04-22 06:33:57 +00:00
noneflow[bot]
5b17c8de71 📝 Update changelog 2024-04-21 02:39:49 +00:00
Autuamn
5cf4ff66a3 🍻 publish plugin QQ频道-Discord 互通 (#2666) 2024-04-21 02:38:40 +00:00
noneflow[bot]
b6be8a178e 📝 Update changelog 2024-04-20 15:41:30 +00:00
YuxiCN
b77c3b2d0c 🍻 publish plugin Yinying-Chat (#2653) 2024-04-20 15:40:30 +00:00
noneflow[bot]
e4a210b47c 📝 Update changelog 2024-04-20 06:48:21 +00:00
Ju4tCode
6bf10aafb7 Feature: 嵌套插件名称作用域优化 (#2665) 2024-04-20 14:47:12 +08:00
noneflow[bot]
e15d544341 📝 Update changelog 2024-04-19 03:49:38 +00:00
惜月
acdb5787db 📝 Docs: 更新 Villa 适配器说明 (#2661) 2024-04-19 11:48:34 +08:00
noneflow[bot]
18f0c9b500 📝 Update changelog 2024-04-19 02:56:24 +00:00
shi-yingyingjiang
b36e721274 🍻 publish plugin 淫语 (#2649) 2024-04-19 02:55:16 +00:00
noneflow[bot]
9fdc50cd0e 📝 Update changelog 2024-04-19 02:52:46 +00:00
Tarrailt
41abf077bc 📝 Docs: 添加 Kritor 适配器说明 (#2660)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Ju4tCode <42488585+yanyongyu@users.noreply.github.com>
2024-04-19 10:51:37 +08:00
noneflow[bot]
27a4e5a55b 📝 Update changelog 2024-04-19 02:43:27 +00:00
Ju4tCode
65f6a104e9 Update assets/adapters.json 2024-04-19 02:42:17 +00:00
RF-Tar-Railt
415bd07c0d 🍻 publish adapter Kritor (#2658) 2024-04-19 02:42:17 +00:00
noneflow[bot]
3fd26dd937 📝 Update changelog 2024-04-18 10:51:15 +00:00
Azide
f5f5d93b64 🐛 Bug: inherit_supported_adapters 在展开缩写前取交集 (#2654)
Co-authored-by: Ju4tCode <42488585+yanyongyu@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-04-18 18:50:11 +08:00
noneflow[bot]
b497bb8c83 📝 Update changelog 2024-04-18 10:30:35 +00:00
Tarrailt
b0d554eacb 📝 Docs: 更新最佳实践的 Alconna 部分 (#2656)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-04-18 18:28:31 +08:00
noneflow[bot]
cbecc7b930 📝 Update changelog 2024-04-18 06:11:20 +00:00
baiqwerdvd
5e0921aca9 🍻 publish plugin 飞花令 (#2647) 2024-04-18 06:10:11 +00:00
noneflow[bot]
7e8015e828 📝 Update changelog 2024-04-17 11:14:50 +00:00
huanxin996
bef5bdf0bf 🍻 publish plugin Hx_YinYing (#2645) 2024-04-17 11:13:39 +00:00
noneflow[bot]
c04cd5e83e 📝 Update changelog 2024-04-17 09:25:33 +00:00
Ju4tCode
30d3c1bbce Feature: 优化调用栈识别 (#2644) 2024-04-17 17:24:38 +08:00
noneflow[bot]
5e72461391 📝 Update changelog 2024-04-16 12:52:07 +00:00
KarisAya
54fdf71d91 🍻 publish plugin clovers插件框架 (#2642) 2024-04-16 12:51:10 +00:00
noneflow[bot]
420d0cfdc4 📝 Update changelog 2024-04-16 06:45:23 +00:00
zhulinyv
84bfba7a82 🍻 publish plugin nonebot-plugin-nai3 (#2638) 2024-04-16 06:44:27 +00:00
noneflow[bot]
9fd89a6822 📝 Update changelog 2024-04-15 16:34:44 +00:00
Ju4tCode
4a02dde83f 💥 Remove: 移除 Python 3.8 支持 (#2641) 2024-04-16 00:33:48 +08:00
noneflow[bot]
e93ee1ffec 📝 Update changelog 2024-04-14 14:27:20 +00:00
Ju4tCode
e2b6fb12c7 📝 Docs: 添加 RocketChat 适配器说明 (#2640)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-04-14 22:26:23 +08:00
noneflow[bot]
7836073c7e 📝 Update changelog 2024-04-11 05:33:11 +00:00
IllTamer
3119626d89 🍻 publish adapter RocketChat (#2636) 2024-04-11 05:32:21 +00:00
noneflow[bot]
19bebdd923 📝 Update changelog 2024-04-09 03:28:08 +00:00
Well2333
0b0dd8b552 🍻 publish plugin nonebot-plugin-auto-bot-selector (#2634) 2024-04-09 03:27:14 +00:00
noneflow[bot]
47ce7a633f 📝 Update changelog 2024-04-08 12:23:18 +00:00
StarHeart
ca32f68787 📝 Docs: 商店卡片样式调整 (#2633) 2024-04-08 20:22:29 +08:00
noneflow[bot]
0b972ad302 📝 Update changelog 2024-04-08 12:17:53 +00:00
Ju4tCode
9b4b1526b1 🐛 Bug: 添加 HTTP 客户端会话上下文检查 (#2632) 2024-04-08 20:17:03 +08:00
noneflow[bot]
7a232c7a4a 📝 Update changelog 2024-04-06 12:08:18 +00:00
Azide
983351f0b7 📝 Docs: 为商店插件卡片添加更多展示内容 (#2626) 2024-04-06 20:07:09 +08:00
noneflow[bot]
16fb5ac121 📝 Update changelog 2024-04-05 13:44:11 +00:00
mrqx0195
bb1fbca4a7 🍻 publish plugin Chikari_economy (#2630) 2024-04-05 13:43:17 +00:00
noneflow[bot]
b7c0b6b8e0 📝 Update changelog 2024-04-05 13:11:57 +00:00
Ju4tCode
485aa62755 Feature: 支持 HTTP 客户端会话 (#2627) 2024-04-05 21:11:05 +08:00
noneflow[bot]
53e2a86dd9 📝 Update changelog 2024-04-05 03:36:21 +00:00
zhzhongshi
312095d1df 🍻 publish plugin diffsinger (#2624) 2024-04-05 03:35:33 +00:00
noneflow[bot]
b498be1092 📝 Update changelog 2024-04-04 08:13:35 +00:00
Akirami
211ea8427f 📝 Docs: 修复 RegexMatched 文档类型标注错误 (#2629) 2024-04-04 16:12:39 +08:00
noneflow[bot]
407eb69568 📝 Update changelog 2024-04-04 07:30:23 +00:00
Akirami
8a44b4d6ee 📝 Docs: 修复 RegexMatched​ 文档高亮行错误 (#2628) 2024-04-04 15:29:33 +08:00
noneflow[bot]
bc58fbb741 📝 Update changelog 2024-04-02 14:55:01 +00:00
Azide
0c977f5fd7 📝 Docs: 为商店的详情卡片添加跳转链接 (#2623) 2024-04-02 22:54:10 +08:00
noneflow[bot]
7eeccbcb14 📝 Update changelog 2024-04-02 02:04:37 +00:00
Jigsaw
020d2a5687 🐛 Fix: 将 aiohttp 的 quote_fields 默认设为 False (#2619) 2024-04-02 10:03:46 +08:00
pre-commit-ci[bot]
83d61fcffd ⬆️ auto update by pre-commit hooks (#2620)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-04-02 09:55:19 +08:00
noneflow[bot]
c0b222a5fa 📝 Update changelog 2024-04-02 01:38:09 +00:00
Akirami
236e4ea9aa 📝 Docs: 添加 RegexMatched 依赖注入文档 (#2618)
Co-authored-by: Ju4tCode <42488585+yanyongyu@users.noreply.github.com>
2024-04-02 09:37:02 +08:00
noneflow[bot]
0622e16d18 📝 Update changelog 2024-04-02 01:21:36 +00:00
Lipraty
159ca84e46 🍻 publish plugin ghtiles (#2621) 2024-04-02 01:20:19 +00:00
noneflow[bot]
142a61ce5c 📝 Update changelog 2024-03-24 13:17:39 +00:00
QuanhuZeYu
7f226af541 🍻 publish plugin 人类友好数据配置 (#2615) 2024-03-24 13:16:37 +00:00
noneflow[bot]
8bf912499a 📝 Update changelog 2024-03-23 12:20:18 +00:00
Redmomn
a55b10cfa3 🍻 publish plugin nonebot-plugin-pallas-repeater (#2613) 2024-03-23 12:19:13 +00:00
noneflow[bot]
1a4f889b40 📝 Update changelog 2024-03-23 05:24:00 +00:00
Redmomn
f9bc2de4e4 🍻 publish plugin nonebot-plugin-duel (#2611) 2024-03-23 05:22:54 +00:00
noneflow[bot]
5a1c635083 📝 Update changelog 2024-03-23 05:14:56 +00:00
Agnes4m
76e8567f1e 🍻 publish plugin Sekai Stickers (#2609) 2024-03-23 05:13:57 +00:00
noneflow[bot]
9bd349d933 📝 Update changelog 2024-03-17 12:58:31 +00:00
GLDYM
5e8a67b605 🍻 publish plugin 100orangejuice (#2600) 2024-03-17 12:57:28 +00:00
noneflow[bot]
e16799d500 📝 Update changelog 2024-03-15 14:36:09 +00:00
zhaomaoniu
a189846194 🍻 publish plugin Steam Info (#2607) 2024-03-15 14:35:03 +00:00
noneflow[bot]
fcd536aada 📝 Update changelog 2024-03-13 03:32:46 +00:00
KroMiose
c89bafc2c9 🍻 publish plugin nonebot-plugin-dice-narrator (#2604) 2024-03-13 03:31:42 +00:00
dependabot[bot]
f5855a9f9a ⬆️ Bump the actions group with 1 update (#2605)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-11 13:56:57 +08:00
noneflow[bot]
a49b4bccc6 📝 Update changelog 2024-03-11 05:27:16 +00:00
NanakaNeko
b434da29b1 🍻 publish plugin a2s查询 (#2602) 2024-03-11 05:26:16 +00:00
noneflow[bot]
514b3a5afe 📝 Update changelog 2024-03-11 02:35:56 +00:00
student_2333
0d30f81ddb ✏️ Plugin: 移除不维护的插件 eitherchoice (#2599) 2024-03-11 10:34:44 +08:00
noneflow[bot]
9a86c00f62 📝 Update changelog 2024-03-09 05:54:47 +00:00
noneflow[bot]
7648138902 🍻 publish plugin 赛博钓鱼 (#2596)
Co-authored-by: C14H22O <C14H22O@users.noreply.github.com>
2024-03-09 13:53:45 +08:00
noneflow[bot]
2055f092f2 📝 Update changelog 2024-03-07 06:58:32 +00:00
uy/sun
9ff7f4baba 🧑‍💻 Develop: 添加 ruff RUF 规则 (#2598)
Co-authored-by: Ju4tCode <42488585+yanyongyu@users.noreply.github.com>
2024-03-07 14:57:26 +08:00
noneflow[bot]
92ba99c34c 📝 Update changelog 2024-03-05 05:42:03 +00:00
XTxiaoting14332
876cff4daf 🍻 publish plugin 人性化的ChatGLM (#2591) 2024-03-05 05:40:55 +00:00
noneflow[bot]
1ac6a612b0 📝 Update changelog 2024-03-04 09:12:55 +00:00
Redmomn
15ecad9f87 🍻 publish plugin nonebot-plugin-vits-tts (#2594) 2024-03-04 09:11:47 +00:00
noneflow[bot]
587d3f7c7e 📝 Update changelog 2024-03-02 09:49:10 +00:00
Ju4tCode
10e4ea6743 📝 Docs: 添加百度搜索资源验证 (#2590)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-03-02 17:48:04 +08:00
213 changed files with 8621 additions and 3577 deletions

View File

@@ -25,7 +25,6 @@ runs:
- uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
architecture: "x64"
cache: "poetry"
cache-dependency-path: |
./poetry.lock

View File

@@ -23,11 +23,11 @@ jobs:
group: test-coverage-${{ github.ref }}-${{ matrix.os }}-${{ matrix.python-version }}-${{ matrix.env }}
cancel-in-progress: true
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.9", "3.10", "3.11", "3.12"]
os: [ubuntu-latest, windows-latest, macos-latest]
env: [pydantic-v1, pydantic-v2]
fail-fast: false
env:
OS: ${{ matrix.os }}
PYTHON_VERSION: ${{ matrix.python-version }}

View File

@@ -15,9 +15,10 @@ concurrency:
cancel-in-progress: false
jobs:
plugin_test:
check:
runs-on: ubuntu-latest
name: nonebot2 plugin test
name: check
# do not run on forked PRs, do not run on not related issues, do not run on pr comments
if: |
!(
(
@@ -35,6 +36,46 @@ jobs:
github.event_name == 'issue_comment' && github.event.issue.pull_request
)
)
steps:
- run: echo "Check passed"
reaction:
runs-on: ubuntu-latest
name: reaction
needs: check
if: |
(
github.event_name == 'issue_comment' &&
github.event.action == 'created'
) ||
(
github.event_name == 'issues' &&
github.event.action == 'opened'
)
steps:
- name: Generate token
id: generate-token
uses: tibdex/github-app-token@v2
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_KEY }}
- name: Reaction on issue
if: github.event_name == 'issues'
run: |
gh api --method POST /repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/reactions -f "content=rocket"
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
- name: Reaction on issue comment
if: github.event_name == 'issue_comment'
run: |
gh api --method POST /repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions -f "content=rocket"
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
plugin_test:
runs-on: ubuntu-latest
name: nonebot2 plugin test
needs: check
permissions:
issues: read
outputs:

View File

@@ -33,7 +33,7 @@ jobs:
run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV
- name: Deploy to Netlify
uses: nwtgck/actions-netlify@v2
uses: nwtgck/actions-netlify@v3
with:
publish-dir: "./website/build"
production-deploy: true

View File

@@ -33,7 +33,7 @@ jobs:
echo "DEPLOY_NAME=deploy-preview-${{ github.event.number }}" >> $GITHUB_ENV
- name: Deploy to Netlify
uses: nwtgck/actions-netlify@v2
uses: nwtgck/actions-netlify@v3
with:
publish-dir: "./website/build"
production-deploy: false

View File

@@ -7,7 +7,7 @@ ci:
autoupdate_commit_msg: ":arrow_up: auto update by pre-commit hooks"
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.2.0
rev: v0.4.2
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
@@ -20,7 +20,7 @@ repos:
stages: [commit]
- repo: https://github.com/psf/black
rev: 24.1.1
rev: 24.4.2
hooks:
- id: black
stages: [commit]

View File

@@ -21,7 +21,7 @@ _✨ 跨平台 Python 异步机器人框架 ✨_
<a href="https://pypi.python.org/pypi/nonebot2">
<img src="https://img.shields.io/pypi/v/nonebot2?logo=python&logoColor=edb641" alt="pypi">
</a>
<img src="https://img.shields.io/badge/python-3.8+-blue?logo=python&logoColor=edb641" alt="python">
<img src="https://img.shields.io/badge/python-3.9+-blue?logo=python&logoColor=edb641" alt="python">
<a href="https://github.com/psf/black">
<img src="https://img.shields.io/badge/code%20style-black-000000.svg?logo=python&logoColor=edb641" alt="black">
</a>
@@ -111,25 +111,27 @@ NoneBot2 是一个现代、跨平台、可扩展的 Python 聊天机器人框架
- 海纳百川:一个框架,支持多个聊天软件平台,可自定义通信协议
| 协议名称 | 状态 | 注释 |
| :--------------------------------------------------------------------------------------------------------------------------: | :--: | :-----------------------------------------------------------------------: |
| :-------------------------------------------------------------------------------------------------------------------: | :--: | :-----------------------------------------------------------------------: |
| OneBot[仓库](https://github.com/nonebot/adapter-onebot)[协议](https://onebot.dev/) | ✅ | 支持 QQ、TG、微信公众号、KOOK 等[平台](https://onebot.dev/ecosystem.html) |
| Telegram[仓库](https://github.com/nonebot/adapter-telegram)[协议](https://core.telegram.org/bots/api) | ✅ | |
| 飞书([仓库](https://github.com/nonebot/adapter-feishu)[协议](https://open.feishu.cn/document/home/index) | ✅ | |
| GitHub[仓库](https://github.com/nonebot/adapter-github)[协议](https://docs.github.com/en/apps) | ✅ | GitHub APP & OAuth APP |
| QQ[仓库](https://github.com/nonebot/adapter-qq)[协议](https://bot.q.qq.com/wiki/) | ✅ | QQ 官方接口调整较多 |
| 钉钉([仓库](https://github.com/nonebot/adapter-ding)[协议](https://open.dingtalk.com/document/) | 🤗 | 寻找 Maintainer暂不可用 |
| Console[仓库](https://github.com/nonebot/adapter-console) | ✅ | 控制台交互 |
| Red[仓库](https://github.com/nonebot/adapter-red)[协议](https://chrononeko.github.io/QQNTRedProtocol/) | ✅ | QQ 协议 |
| Satori[仓库](https://github.com/nonebot/adapter-satori)[协议](https://satori.js.org/zh-CN) | ✅ | 支持 Onebot、TG、飞书、微信公众号、Koishi 等 |
| Discord[仓库](https://github.com/nonebot/adapter-discord)[协议](https://discord.com/developers/docs/intro) | ✅ | Discord Bot 协议 |
| DoDo[仓库](https://github.com/nonebot/adapter-dodo)[协议](https://open.imdodo.com/) | ✅ | DoDo Bot 协议 |
| Kritor[仓库](https://github.com/nonebot/adapter-kritor)[协议](https://github.com/KarinJS/kritor) | ✅ | Kritor (OnebotX) 协议QQ 机器人接口标准 |
| 钉钉([仓库](https://github.com/nonebot/adapter-ding)[协议](https://open.dingtalk.com/document/) | 🤗 | 寻找 Maintainer暂不可用 |
| 开黑啦([仓库](https://github.com/Tian-que/nonebot-adapter-kaiheila)[协议](https://developer.kookapp.cn/) | ↗️ | 由社区贡献 |
| Mirai[仓库](https://github.com/ieew/nonebot_adapter_mirai2)[协议](https://docs.mirai.mamoe.net/mirai-api-http/) | ↗️ | QQ 协议,由社区贡献 |
| Ntchat[仓库](https://github.com/JustUndertaker/adapter-ntchat) | ↗️ | 微信协议,由社区贡献 |
| MineCraft[仓库](https://github.com/17TheWord/nonebot-adapter-minecraft) | ↗️ | 由社区贡献 |
| BiliBili Live[仓库](https://github.com/wwweww/adapter-bilibili) | ↗️ | 由社区贡献 |
| Walle-Q[仓库](https://github.com/onebot-walle/nonebot_adapter_walleq) | ↗️ | QQ 协议,由社区贡献 |
| Villa[仓库](https://github.com/CMHopeSunshine/nonebot-adapter-villa)[协议](https://webstatic.mihoyo.com/vila/bot/doc/) | ↗️ | 米游社大别野 Bot 协议,由社区贡献 |
| Villa[仓库](https://github.com/CMHopeSunshine/nonebot-adapter-villa) | | 米游社大别野 Bot 协议,官方已下线 |
| Rocket.Chat[仓库](https://github.com/IUnlimit/nonebot-adapter-rocketchat)[协议](https://developer.rocket.chat/) | ↗️ | Rocket.Chat Bot 协议,由社区贡献 |
- 坚实后盾:支持多种 web 框架,可自定义替换、组合

View File

@@ -213,5 +213,30 @@
"homepage": "https://github.com/nonebot/adapter-dodo",
"tags": [],
"is_official": true
},
{
"module_name": "nonebot.adapters.rocketchat",
"project_link": "nonebot-adapter-rocketchat",
"name": "RocketChat",
"desc": "RocketChat adapter for nonebot2",
"author": "IllTamer",
"homepage": "https://github.com/IUnlimit/nonebot-adapter-rocketchat",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot.adapters.kritor",
"project_link": "nonebot-adapter-kritor",
"name": "Kritor",
"desc": "Kritor 协议适配",
"author": "RF-Tar-Railt",
"homepage": "https://github.com/nonebot/adapter-kritor",
"tags": [
{
"label": "QQNT",
"color": "#35a7c9"
}
],
"is_official": true
}
]

View File

@@ -4203,13 +4203,6 @@
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_eitherchoice",
"project_link": "nonebot-plugin-eitherchoice",
"author": "lgc2333",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_poke",
"project_link": "nonebot-plugin-poke",
@@ -5552,5 +5545,363 @@
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_vits_tts",
"project_link": "nonebot-plugin-vits-tts",
"author": "Redmomn",
"tags": [
{
"label": "VITS",
"color": "#ea5252"
},
{
"label": "TTS",
"color": "#52dbea"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_chatglm_plus",
"project_link": "nonebot-plugin-chatglm-plus",
"author": "XTxiaoting14332",
"tags": [
{
"label": "ChatGLM",
"color": "#73cccc"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_fishing",
"project_link": "nonebot-plugin-fishing",
"author": "C14H22O",
"tags": [
{
"label": "钓鱼",
"color": "#87cefa"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_a2s_query",
"project_link": "nonebot-plugin-a2s-query",
"author": "NanakaNeko",
"tags": [
{
"label": "游戏服务器",
"color": "#ea5252"
},
{
"label": "value",
"color": "#99ea52"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_dice_narrator",
"project_link": "nonebot-plugin-dice-narrator",
"author": "KroMiose",
"tags": [
{
"label": "GPT",
"color": "#29b752"
},
{
"label": "掷骰姬",
"color": "#c84b9d"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_steam_info",
"project_link": "nonebot-plugin-steam-info",
"author": "zhaomaoniu",
"tags": [
{
"label": "Steam",
"color": "#14305e"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_orangejuice",
"project_link": "nonebot-plugin-orangejuice",
"author": "GLDYM",
"tags": [
{
"label": "百橙",
"color": "#ed6f00"
},
{
"label": "100OJ",
"color": "#ed6f00"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_md",
"project_link": "nonebot_plugin_md",
"author": "Agnes4m",
"tags": [
{
"label": "muse dash",
"color": "#ea5252"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_duel",
"project_link": "nonebot-plugin-duel",
"author": "Redmomn",
"tags": [
{
"label": "决斗",
"color": "#5be7d1"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_pallas_repeater",
"project_link": "nonebot-plugin-pallas-repeater",
"author": "Redmomn",
"tags": [
{
"label": "复读鸡",
"color": "#52eae7"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_humanaticstore",
"project_link": "nonebot-plugin-humanaticstore",
"author": "QuanhuZeYu",
"tags": [
{
"label": "config",
"color": "#ea5252"
},
{
"label": "配置工具",
"color": "#ea5252"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_ghtiles",
"project_link": "nonebot-plugin-ghtiles",
"author": "Lipraty",
"tags": [
{
"label": "Github",
"color": "#2a2a2a"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_diffsinger",
"project_link": "nonebot-plugin-diffsinger",
"author": "zhzhongshi",
"tags": [
{
"label": "diffsinger",
"color": "#c24444"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_chikari_economy",
"project_link": "nonebot-plugin-chikari-economy",
"author": "mrqx0195",
"tags": [
{
"label": "经济",
"color": "#adad73"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_auto_bot_selector",
"project_link": "nonebot-plugin-auto-bot-selector",
"author": "Well2333",
"tags": [
{
"label": "多适配器",
"color": "#5280ea"
},
{
"label": "跨平台",
"color": "#5452ea"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_nai3",
"project_link": "nonebot-plugin-nai3",
"author": "zhulinyv",
"tags": [
{
"label": "NovelAI",
"color": "#35f139"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_clovers",
"project_link": "nonebot-plugin-clovers",
"author": "KarisAya",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_hx_yinying",
"project_link": "nonebot-plugin-hx-yinying",
"author": "huanxin996",
"tags": [
{
"label": "Chat",
"color": "#3b53ff"
},
{
"label": "幻歆!",
"color": "#0d7ccd"
},
{
"label": "银影!",
"color": "#2b4dae"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_fhl",
"project_link": "nonebot-plugin-fhl",
"author": "baiqwerdvd",
"tags": [
{
"label": "飞花令",
"color": "#ea5252"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_yinyu",
"project_link": "nonebot-plugin-yinyu",
"author": "shi-yingyingjiang",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_yinying_chat",
"project_link": "nonebot-plugin-yinying-chat",
"author": "YuxiCN",
"tags": [
{
"label": "Furry",
"color": "#ea5252"
},
{
"label": "银影",
"color": "#ea5252"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_dcqg_relay",
"project_link": "nonebot-plugin-dcqg-relay",
"author": "Autuamn",
"tags": [
{
"label": "消息互通",
"color": "#52beea"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_zsmeme",
"project_link": "nonebot-plugin-zsmeme",
"author": "shi-yingyingjiang",
"tags": [
{
"label": "帕弥什",
"color": "#ea5252"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_sanyao",
"project_link": "nonebot-plugin-sanyao",
"author": "afterow",
"tags": [
{
"label": "占卜",
"color": "#415656"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_cyberfurry",
"project_link": "nonebot-plugin-cyberfurry",
"author": "cubstaryow",
"tags": [
{
"label": "幼龙云端",
"color": "#ccc719"
},
{
"label": "银影",
"color": "#af2af3"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_helpwithpic",
"project_link": "nonebot-plugin-HelpWithPic",
"author": "cubstaryow",
"tags": [
{
"label": "帮助",
"color": "#c61b1b"
},
{
"label": "图片生成",
"color": "#c61b1b"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_sticker_saver",
"project_link": "nonebot-plugin-sticker-saver",
"author": "colasama",
"tags": [
{
"label": "表情包",
"color": "#f1ce15"
}
],
"is_official": false
}
]

View File

@@ -1,14 +1,14 @@
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
[[package]]
name = "aiodns"
version = "3.1.1"
version = "3.2.0"
description = "Simple DNS resolver for asyncio"
optional = false
python-versions = "*"
files = [
{file = "aiodns-3.1.1-py3-none-any.whl", hash = "sha256:a387b63da4ced6aad35b1dda2d09620ad608a1c7c0fb71efa07ebb4cd511928d"},
{file = "aiodns-3.1.1.tar.gz", hash = "sha256:1073eac48185f7a4150cad7f96a5192d6911f12b4fb894de80a088508c9b3a99"},
{file = "aiodns-3.2.0-py3-none-any.whl", hash = "sha256:e443c0c27b07da3174a109fd9e736d69058d808f144d3c9d56dbd1776964c5f5"},
{file = "aiodns-3.2.0.tar.gz", hash = "sha256:62869b23409349c21b072883ec8998316b234c9a9e36675756e8e317e8768f72"},
]
[package.dependencies]
@@ -27,87 +27,87 @@ files = [
[[package]]
name = "aiohttp"
version = "3.9.3"
version = "3.9.5"
description = "Async http client/server framework (asyncio)"
optional = false
python-versions = ">=3.8"
files = [
{file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54"},
{file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc"},
{file = "aiohttp-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5"},
{file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b"},
{file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768"},
{file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5"},
{file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29"},
{file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec"},
{file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747"},
{file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6"},
{file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c"},
{file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf"},
{file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52"},
{file = "aiohttp-3.9.3-cp310-cp310-win32.whl", hash = "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b"},
{file = "aiohttp-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5"},
{file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d"},
{file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2"},
{file = "aiohttp-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc"},
{file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266"},
{file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce"},
{file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb"},
{file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b"},
{file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4"},
{file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae"},
{file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3"},
{file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283"},
{file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e"},
{file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4"},
{file = "aiohttp-3.9.3-cp311-cp311-win32.whl", hash = "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5"},
{file = "aiohttp-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8"},
{file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60"},
{file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869"},
{file = "aiohttp-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679"},
{file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96"},
{file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d"},
{file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11"},
{file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53"},
{file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5"},
{file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca"},
{file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1"},
{file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5"},
{file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6"},
{file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f"},
{file = "aiohttp-3.9.3-cp312-cp312-win32.whl", hash = "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38"},
{file = "aiohttp-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5"},
{file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c"},
{file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528"},
{file = "aiohttp-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542"},
{file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511"},
{file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672"},
{file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d"},
{file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168"},
{file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b"},
{file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194"},
{file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8"},
{file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4"},
{file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321"},
{file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2"},
{file = "aiohttp-3.9.3-cp38-cp38-win32.whl", hash = "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63"},
{file = "aiohttp-3.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c"},
{file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29"},
{file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1"},
{file = "aiohttp-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f"},
{file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3"},
{file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa"},
{file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e"},
{file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58"},
{file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533"},
{file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb"},
{file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3"},
{file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d"},
{file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a"},
{file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d"},
{file = "aiohttp-3.9.3-cp39-cp39-win32.whl", hash = "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051"},
{file = "aiohttp-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc"},
{file = "aiohttp-3.9.3.tar.gz", hash = "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7"},
{file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"},
{file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"},
{file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"},
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"},
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"},
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"},
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"},
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"},
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"},
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"},
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"},
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"},
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"},
{file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"},
{file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"},
{file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"},
{file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"},
{file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"},
{file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"},
{file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"},
{file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"},
{file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"},
{file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"},
{file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"},
{file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"},
{file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"},
{file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"},
{file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"},
{file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"},
{file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"},
{file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"},
{file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"},
{file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"},
{file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"},
{file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"},
{file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"},
]
[package.dependencies]
@@ -162,13 +162,13 @@ trio = ["trio (>=0.23)"]
[[package]]
name = "asgiref"
version = "3.7.2"
version = "3.8.1"
description = "ASGI specs, helper code, and adapters"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"},
{file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"},
{file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"},
{file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"},
]
[package.dependencies]
@@ -223,13 +223,13 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p
[[package]]
name = "blinker"
version = "1.7.0"
version = "1.8.1"
description = "Fast, simple object-to-object and broadcast signaling"
optional = false
python-versions = ">=3.8"
files = [
{file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"},
{file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"},
{file = "blinker-1.8.1-py3-none-any.whl", hash = "sha256:5f1cdeff423b77c31b89de0565cd03e5275a03028f44b2b15f912632a58cced6"},
{file = "blinker-1.8.1.tar.gz", hash = "sha256:da44ec748222dcd0105ef975eed946da197d5bdf8bafb6aa92f5bc89da63fa25"},
]
[[package]]
@@ -564,63 +564,63 @@ files = [
[[package]]
name = "coverage"
version = "7.4.3"
version = "7.5.0"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"},
{file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"},
{file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"},
{file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"},
{file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"},
{file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"},
{file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"},
{file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"},
{file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"},
{file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"},
{file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"},
{file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"},
{file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"},
{file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"},
{file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"},
{file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"},
{file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"},
{file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"},
{file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"},
{file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"},
{file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"},
{file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"},
{file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"},
{file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"},
{file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"},
{file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"},
{file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"},
{file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"},
{file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"},
{file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"},
{file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"},
{file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"},
{file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"},
{file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"},
{file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"},
{file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"},
{file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"},
{file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"},
{file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"},
{file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"},
{file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"},
{file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"},
{file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"},
{file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"},
{file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"},
{file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"},
{file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"},
{file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"},
{file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"},
{file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"},
{file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"},
{file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"},
{file = "coverage-7.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:432949a32c3e3f820af808db1833d6d1631664d53dd3ce487aa25d574e18ad1c"},
{file = "coverage-7.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bd7065249703cbeb6d4ce679c734bef0ee69baa7bff9724361ada04a15b7e3b"},
{file = "coverage-7.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbfe6389c5522b99768a93d89aca52ef92310a96b99782973b9d11e80511f932"},
{file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39793731182c4be939b4be0cdecde074b833f6171313cf53481f869937129ed3"},
{file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85a5dbe1ba1bf38d6c63b6d2c42132d45cbee6d9f0c51b52c59aa4afba057517"},
{file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:357754dcdfd811462a725e7501a9b4556388e8ecf66e79df6f4b988fa3d0b39a"},
{file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a81eb64feded34f40c8986869a2f764f0fe2db58c0530d3a4afbcde50f314880"},
{file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51431d0abbed3a868e967f8257c5faf283d41ec882f58413cf295a389bb22e58"},
{file = "coverage-7.5.0-cp310-cp310-win32.whl", hash = "sha256:f609ebcb0242d84b7adeee2b06c11a2ddaec5464d21888b2c8255f5fd6a98ae4"},
{file = "coverage-7.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:6782cd6216fab5a83216cc39f13ebe30adfac2fa72688c5a4d8d180cd52e8f6a"},
{file = "coverage-7.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e768d870801f68c74c2b669fc909839660180c366501d4cc4b87efd6b0eee375"},
{file = "coverage-7.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84921b10aeb2dd453247fd10de22907984eaf80901b578a5cf0bb1e279a587cb"},
{file = "coverage-7.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710c62b6e35a9a766b99b15cdc56d5aeda0914edae8bb467e9c355f75d14ee95"},
{file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c379cdd3efc0658e652a14112d51a7668f6bfca7445c5a10dee7eabecabba19d"},
{file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea9d3ca80bcf17edb2c08a4704259dadac196fe5e9274067e7a20511fad1743"},
{file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:41327143c5b1d715f5f98a397608f90ab9ebba606ae4e6f3389c2145410c52b1"},
{file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:565b2e82d0968c977e0b0f7cbf25fd06d78d4856289abc79694c8edcce6eb2de"},
{file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cf3539007202ebfe03923128fedfdd245db5860a36810136ad95a564a2fdffff"},
{file = "coverage-7.5.0-cp311-cp311-win32.whl", hash = "sha256:bf0b4b8d9caa8d64df838e0f8dcf68fb570c5733b726d1494b87f3da85db3a2d"},
{file = "coverage-7.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c6384cc90e37cfb60435bbbe0488444e54b98700f727f16f64d8bfda0b84656"},
{file = "coverage-7.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fed7a72d54bd52f4aeb6c6e951f363903bd7d70bc1cad64dd1f087980d309ab9"},
{file = "coverage-7.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cbe6581fcff7c8e262eb574244f81f5faaea539e712a058e6707a9d272fe5b64"},
{file = "coverage-7.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad97ec0da94b378e593ef532b980c15e377df9b9608c7c6da3506953182398af"},
{file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd4bacd62aa2f1a1627352fe68885d6ee694bdaebb16038b6e680f2924a9b2cc"},
{file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf032b6c105881f9d77fa17d9eebe0ad1f9bfb2ad25777811f97c5362aa07f2"},
{file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ba01d9ba112b55bfa4b24808ec431197bb34f09f66f7cb4fd0258ff9d3711b1"},
{file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f0bfe42523893c188e9616d853c47685e1c575fe25f737adf473d0405dcfa7eb"},
{file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a9a7ef30a1b02547c1b23fa9a5564f03c9982fc71eb2ecb7f98c96d7a0db5cf2"},
{file = "coverage-7.5.0-cp312-cp312-win32.whl", hash = "sha256:3c2b77f295edb9fcdb6a250f83e6481c679335ca7e6e4a955e4290350f2d22a4"},
{file = "coverage-7.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:427e1e627b0963ac02d7c8730ca6d935df10280d230508c0ba059505e9233475"},
{file = "coverage-7.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9dd88fce54abbdbf4c42fb1fea0e498973d07816f24c0e27a1ecaf91883ce69e"},
{file = "coverage-7.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a898c11dca8f8c97b467138004a30133974aacd572818c383596f8d5b2eb04a9"},
{file = "coverage-7.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07dfdd492d645eea1bd70fb1d6febdcf47db178b0d99161d8e4eed18e7f62fe7"},
{file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3d117890b6eee85887b1eed41eefe2e598ad6e40523d9f94c4c4b213258e4a4"},
{file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6afd2e84e7da40fe23ca588379f815fb6dbbb1b757c883935ed11647205111cb"},
{file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9960dd1891b2ddf13a7fe45339cd59ecee3abb6b8326d8b932d0c5da208104f"},
{file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ced268e82af993d7801a9db2dbc1d2322e786c5dc76295d8e89473d46c6b84d4"},
{file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7c211f25777746d468d76f11719e64acb40eed410d81c26cefac641975beb88"},
{file = "coverage-7.5.0-cp38-cp38-win32.whl", hash = "sha256:262fffc1f6c1a26125d5d573e1ec379285a3723363f3bd9c83923c9593a2ac25"},
{file = "coverage-7.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:eed462b4541c540d63ab57b3fc69e7d8c84d5957668854ee4e408b50e92ce26a"},
{file = "coverage-7.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0194d654e360b3e6cc9b774e83235bae6b9b2cac3be09040880bb0e8a88f4a1"},
{file = "coverage-7.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33c020d3322662e74bc507fb11488773a96894aa82a622c35a5a28673c0c26f5"},
{file = "coverage-7.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbdf2cae14a06827bec50bd58e49249452d211d9caddd8bd80e35b53cb04631"},
{file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3235d7c781232e525b0761730e052388a01548bd7f67d0067a253887c6e8df46"},
{file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2de4e546f0ec4b2787d625e0b16b78e99c3e21bc1722b4977c0dddf11ca84e"},
{file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0e206259b73af35c4ec1319fd04003776e11e859936658cb6ceffdeba0f5be"},
{file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2055c4fb9a6ff624253d432aa471a37202cd8f458c033d6d989be4499aed037b"},
{file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075299460948cd12722a970c7eae43d25d37989da682997687b34ae6b87c0ef0"},
{file = "coverage-7.5.0-cp39-cp39-win32.whl", hash = "sha256:280132aada3bc2f0fac939a5771db4fbb84f245cb35b94fae4994d4c1f80dae7"},
{file = "coverage-7.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:c58536f6892559e030e6924896a44098bc1290663ea12532c78cef71d0df8493"},
{file = "coverage-7.5.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:2b57780b51084d5223eee7b59f0d4911c31c16ee5aa12737c7a02455829ff067"},
{file = "coverage-7.5.0.tar.gz", hash = "sha256:cf62d17310f34084c59c01e027259076479128d11e4661bb6c9acb38c5e19bb8"},
]
[package.dependencies]
@@ -647,13 +647,13 @@ packaging = ">=20.4"
[[package]]
name = "exceptiongroup"
version = "1.2.0"
version = "1.2.1"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
files = [
{file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
{file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
{file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"},
{file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"},
]
[package.extras]
@@ -661,13 +661,13 @@ test = ["pytest (>=6)"]
[[package]]
name = "execnet"
version = "2.0.2"
version = "2.1.1"
description = "execnet: rapid multi-Python deployment"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "execnet-2.0.2-py3-none-any.whl", hash = "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41"},
{file = "execnet-2.0.2.tar.gz", hash = "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af"},
{file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"},
{file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"},
]
[package.extras]
@@ -675,32 +675,32 @@ testing = ["hatch", "pre-commit", "pytest", "tox"]
[[package]]
name = "fastapi"
version = "0.109.2"
version = "0.110.3"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
optional = false
python-versions = ">=3.8"
files = [
{file = "fastapi-0.109.2-py3-none-any.whl", hash = "sha256:2c9bab24667293b501cad8dd388c05240c850b58ec5876ee3283c47d6e1e3a4d"},
{file = "fastapi-0.109.2.tar.gz", hash = "sha256:f3817eac96fe4f65a2ebb4baa000f394e55f5fccdaf7f75250804bc58f354f73"},
{file = "fastapi-0.110.3-py3-none-any.whl", hash = "sha256:fd7600612f755e4050beb74001310b5a7e1796d149c2ee363124abdfa0289d32"},
{file = "fastapi-0.110.3.tar.gz", hash = "sha256:555700b0159379e94fdbfc6bb66a0f1c43f4cf7060f25239af3d84b63a656626"},
]
[package.dependencies]
pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
starlette = ">=0.36.3,<0.37.0"
starlette = ">=0.37.2,<0.38.0"
typing-extensions = ">=4.8.0"
[package.extras]
all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
[[package]]
name = "flask"
version = "3.0.2"
version = "3.0.3"
description = "A simple framework for building complex web applications."
optional = false
python-versions = ">=3.8"
files = [
{file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"},
{file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"},
{file = "flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3"},
{file = "flask-3.0.3.tar.gz", hash = "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842"},
]
[package.dependencies]
@@ -840,13 +840,13 @@ files = [
[[package]]
name = "httpcore"
version = "1.0.4"
version = "1.0.5"
description = "A minimal low-level HTTP client."
optional = false
python-versions = ">=3.8"
files = [
{file = "httpcore-1.0.4-py3-none-any.whl", hash = "sha256:ac418c1db41bade2ad53ae2f3834a3a0f5ae76b56cf5aa497d2d033384fc7d73"},
{file = "httpcore-1.0.4.tar.gz", hash = "sha256:cb2839ccfcba0d2d3c1131d3c3e26dfc327326fbe7a5dc0dbfe9f6c9151bb022"},
{file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"},
{file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"},
]
[package.dependencies]
@@ -857,7 +857,7 @@ h11 = ">=0.13,<0.15"
asyncio = ["anyio (>=4.0,<5.0)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
trio = ["trio (>=0.22.0,<0.25.0)"]
trio = ["trio (>=0.22.0,<0.26.0)"]
[[package]]
name = "httptools"
@@ -970,33 +970,33 @@ files = [
[[package]]
name = "idna"
version = "3.6"
version = "3.7"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.5"
files = [
{file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
{file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
{file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
{file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
]
[[package]]
name = "importlib-metadata"
version = "7.0.1"
version = "7.1.0"
description = "Read metadata from Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"},
{file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"},
{file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"},
{file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"},
]
[package.dependencies]
zipp = ">=0.5"
[package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
perf = ["ipython"]
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"]
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
[[package]]
name = "iniconfig"
@@ -1011,13 +1011,13 @@ files = [
[[package]]
name = "itsdangerous"
version = "2.1.2"
version = "2.2.0"
description = "Safely pass data to untrusted environments and back."
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"},
{file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"},
{file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"},
{file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"},
]
[[package]]
@@ -1228,15 +1228,15 @@ name = "nonebot-test"
version = "0.1.0"
description = "Private test env for nonebot"
optional = false
python-versions = "^3.8"
python-versions = "^3.9"
files = []
develop = false
[package.dependencies]
coverage-conditional-plugin = "^0.9.0"
nonebug = "^0.3.0"
nonebug = "^0.3.7"
pytest-asyncio = "^0.23.2"
pytest-cov = "^4.0.0"
pytest-cov = "^5.0.0"
pytest-xdist = "^3.0.2"
werkzeug = ">=2.3.6,<4.0.0"
wsproto = "^1.2.0"
@@ -1247,10 +1247,10 @@ url = "../test"
[[package]]
name = "nonebot2"
version = "2.2.1"
version = "2.3.0"
description = "An asynchronous python bot framework."
optional = false
python-versions = "^3.8"
python-versions = "^3.9"
files = []
develop = true
@@ -1283,42 +1283,42 @@ url = "../.."
[[package]]
name = "nonebug"
version = "0.3.5"
version = "0.3.7"
description = "nonebot2 test framework"
optional = false
python-versions = ">=3.8,<4.0"
python-versions = "<4.0,>=3.8"
files = [
{file = "nonebug-0.3.5-py3-none-any.whl", hash = "sha256:588831b08b3ea42d058874214bedae646e2ab8c1ec4ae1540ff789873107a8fa"},
{file = "nonebug-0.3.5.tar.gz", hash = "sha256:4d4bf9448cd1cbfaaabaab73dbe4ac8757e86dd92a41ef79cdece8dd61e724e2"},
{file = "nonebug-0.3.7-py3-none-any.whl", hash = "sha256:c39f462aafe20660602a8b789a575db6c9346ab5b6f1985eb9d98b861528299a"},
{file = "nonebug-0.3.7.tar.gz", hash = "sha256:8a75183400681f34eafc7caa2bb6dd511c3b5660c59264f1c379a088c7ac2247"},
]
[package.dependencies]
asgiref = ">=3.4.0,<4.0.0"
async-asgi-testclient = ">=1.4.8,<2.0.0"
nonebot2 = ">=2.0.0-rc.2,<3.0.0"
pytest = ">=7.0.0,<8.0.0"
nonebot2 = ">=2.2.0,<3.0.0"
pytest = ">=7.0.0,<9.0.0"
typing-extensions = ">=4.0.0,<5.0.0"
[[package]]
name = "packaging"
version = "23.2"
version = "24.0"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.7"
files = [
{file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
{file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
]
[[package]]
name = "pluggy"
version = "1.4.0"
version = "1.5.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.8"
files = [
{file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
{file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
]
[package.extras]
@@ -1404,58 +1404,58 @@ idna = ["idna (>=2.1)"]
[[package]]
name = "pycparser"
version = "2.21"
version = "2.22"
description = "C parser in Python"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
python-versions = ">=3.8"
files = [
{file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
{file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
]
[[package]]
name = "pydantic"
version = "1.10.14"
version = "1.10.15"
description = "Data validation and settings management using python type hints"
optional = false
python-versions = ">=3.7"
files = [
{file = "pydantic-1.10.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f4fcec873f90537c382840f330b90f4715eebc2bc9925f04cb92de593eae054"},
{file = "pydantic-1.10.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e3a76f571970fcd3c43ad982daf936ae39b3e90b8a2e96c04113a369869dc87"},
{file = "pydantic-1.10.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d886bd3c3fbeaa963692ef6b643159ccb4b4cefaf7ff1617720cbead04fd1d"},
{file = "pydantic-1.10.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:798a3d05ee3b71967844a1164fd5bdb8c22c6d674f26274e78b9f29d81770c4e"},
{file = "pydantic-1.10.14-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:23d47a4b57a38e8652bcab15a658fdb13c785b9ce217cc3a729504ab4e1d6bc9"},
{file = "pydantic-1.10.14-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f9f674b5c3bebc2eba401de64f29948ae1e646ba2735f884d1594c5f675d6f2a"},
{file = "pydantic-1.10.14-cp310-cp310-win_amd64.whl", hash = "sha256:24a7679fab2e0eeedb5a8924fc4a694b3bcaac7d305aeeac72dd7d4e05ecbebf"},
{file = "pydantic-1.10.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d578ac4bf7fdf10ce14caba6f734c178379bd35c486c6deb6f49006e1ba78a7"},
{file = "pydantic-1.10.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa7790e94c60f809c95602a26d906eba01a0abee9cc24150e4ce2189352deb1b"},
{file = "pydantic-1.10.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aad4e10efa5474ed1a611b6d7f0d130f4aafadceb73c11d9e72823e8f508e663"},
{file = "pydantic-1.10.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1245f4f61f467cb3dfeced2b119afef3db386aec3d24a22a1de08c65038b255f"},
{file = "pydantic-1.10.14-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:21efacc678a11114c765eb52ec0db62edffa89e9a562a94cbf8fa10b5db5c046"},
{file = "pydantic-1.10.14-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:412ab4a3f6dbd2bf18aefa9f79c7cca23744846b31f1d6555c2ee2b05a2e14ca"},
{file = "pydantic-1.10.14-cp311-cp311-win_amd64.whl", hash = "sha256:e897c9f35281f7889873a3e6d6b69aa1447ceb024e8495a5f0d02ecd17742a7f"},
{file = "pydantic-1.10.14-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d604be0f0b44d473e54fdcb12302495fe0467c56509a2f80483476f3ba92b33c"},
{file = "pydantic-1.10.14-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a42c7d17706911199798d4c464b352e640cab4351efe69c2267823d619a937e5"},
{file = "pydantic-1.10.14-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:596f12a1085e38dbda5cbb874d0973303e34227b400b6414782bf205cc14940c"},
{file = "pydantic-1.10.14-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bfb113860e9288d0886e3b9e49d9cf4a9d48b441f52ded7d96db7819028514cc"},
{file = "pydantic-1.10.14-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bc3ed06ab13660b565eed80887fcfbc0070f0aa0691fbb351657041d3e874efe"},
{file = "pydantic-1.10.14-cp37-cp37m-win_amd64.whl", hash = "sha256:ad8c2bc677ae5f6dbd3cf92f2c7dc613507eafe8f71719727cbc0a7dec9a8c01"},
{file = "pydantic-1.10.14-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c37c28449752bb1f47975d22ef2882d70513c546f8f37201e0fec3a97b816eee"},
{file = "pydantic-1.10.14-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49a46a0994dd551ec051986806122767cf144b9702e31d47f6d493c336462597"},
{file = "pydantic-1.10.14-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53e3819bd20a42470d6dd0fe7fc1c121c92247bca104ce608e609b59bc7a77ee"},
{file = "pydantic-1.10.14-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbb503bbbbab0c588ed3cd21975a1d0d4163b87e360fec17a792f7d8c4ff29f"},
{file = "pydantic-1.10.14-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:336709883c15c050b9c55a63d6c7ff09be883dbc17805d2b063395dd9d9d0022"},
{file = "pydantic-1.10.14-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4ae57b4d8e3312d486e2498d42aed3ece7b51848336964e43abbf9671584e67f"},
{file = "pydantic-1.10.14-cp38-cp38-win_amd64.whl", hash = "sha256:dba49d52500c35cfec0b28aa8b3ea5c37c9df183ffc7210b10ff2a415c125c4a"},
{file = "pydantic-1.10.14-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c66609e138c31cba607d8e2a7b6a5dc38979a06c900815495b2d90ce6ded35b4"},
{file = "pydantic-1.10.14-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d986e115e0b39604b9eee3507987368ff8148222da213cd38c359f6f57b3b347"},
{file = "pydantic-1.10.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:646b2b12df4295b4c3148850c85bff29ef6d0d9621a8d091e98094871a62e5c7"},
{file = "pydantic-1.10.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282613a5969c47c83a8710cc8bfd1e70c9223feb76566f74683af889faadc0ea"},
{file = "pydantic-1.10.14-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:466669501d08ad8eb3c4fecd991c5e793c4e0bbd62299d05111d4f827cded64f"},
{file = "pydantic-1.10.14-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:13e86a19dca96373dcf3190fcb8797d40a6f12f154a244a8d1e8e03b8f280593"},
{file = "pydantic-1.10.14-cp39-cp39-win_amd64.whl", hash = "sha256:08b6ec0917c30861e3fe71a93be1648a2aa4f62f866142ba21670b24444d7fd8"},
{file = "pydantic-1.10.14-py3-none-any.whl", hash = "sha256:8ee853cd12ac2ddbf0ecbac1c289f95882b2d4482258048079d13be700aa114c"},
{file = "pydantic-1.10.14.tar.gz", hash = "sha256:46f17b832fe27de7850896f3afee50ea682220dd218f7e9c88d436788419dca6"},
{file = "pydantic-1.10.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:22ed12ee588b1df028a2aa5d66f07bf8f8b4c8579c2e96d5a9c1f96b77f3bb55"},
{file = "pydantic-1.10.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75279d3cac98186b6ebc2597b06bcbc7244744f6b0b44a23e4ef01e5683cc0d2"},
{file = "pydantic-1.10.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50f1666a9940d3d68683c9d96e39640f709d7a72ff8702987dab1761036206bb"},
{file = "pydantic-1.10.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82790d4753ee5d00739d6cb5cf56bceb186d9d6ce134aca3ba7befb1eedbc2c8"},
{file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d207d5b87f6cbefbdb1198154292faee8017d7495a54ae58db06762004500d00"},
{file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e49db944fad339b2ccb80128ffd3f8af076f9f287197a480bf1e4ca053a866f0"},
{file = "pydantic-1.10.15-cp310-cp310-win_amd64.whl", hash = "sha256:d3b5c4cbd0c9cb61bbbb19ce335e1f8ab87a811f6d589ed52b0254cf585d709c"},
{file = "pydantic-1.10.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c3d5731a120752248844676bf92f25a12f6e45425e63ce22e0849297a093b5b0"},
{file = "pydantic-1.10.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c365ad9c394f9eeffcb30a82f4246c0006417f03a7c0f8315d6211f25f7cb654"},
{file = "pydantic-1.10.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3287e1614393119c67bd4404f46e33ae3be3ed4cd10360b48d0a4459f420c6a3"},
{file = "pydantic-1.10.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be51dd2c8596b25fe43c0a4a59c2bee4f18d88efb8031188f9e7ddc6b469cf44"},
{file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6a51a1dd4aa7b3f1317f65493a182d3cff708385327c1c82c81e4a9d6d65b2e4"},
{file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4e316e54b5775d1eb59187f9290aeb38acf620e10f7fd2f776d97bb788199e53"},
{file = "pydantic-1.10.15-cp311-cp311-win_amd64.whl", hash = "sha256:0d142fa1b8f2f0ae11ddd5e3e317dcac060b951d605fda26ca9b234b92214986"},
{file = "pydantic-1.10.15-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7ea210336b891f5ea334f8fc9f8f862b87acd5d4a0cbc9e3e208e7aa1775dabf"},
{file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3453685ccd7140715e05f2193d64030101eaad26076fad4e246c1cc97e1bb30d"},
{file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bea1f03b8d4e8e86702c918ccfd5d947ac268f0f0cc6ed71782e4b09353b26f"},
{file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:005655cabc29081de8243126e036f2065bd7ea5b9dff95fde6d2c642d39755de"},
{file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:af9850d98fc21e5bc24ea9e35dd80a29faf6462c608728a110c0a30b595e58b7"},
{file = "pydantic-1.10.15-cp37-cp37m-win_amd64.whl", hash = "sha256:d31ee5b14a82c9afe2bd26aaa405293d4237d0591527d9129ce36e58f19f95c1"},
{file = "pydantic-1.10.15-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5e09c19df304b8123938dc3c53d3d3be6ec74b9d7d0d80f4f4b5432ae16c2022"},
{file = "pydantic-1.10.15-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7ac9237cd62947db00a0d16acf2f3e00d1ae9d3bd602b9c415f93e7a9fc10528"},
{file = "pydantic-1.10.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:584f2d4c98ffec420e02305cf675857bae03c9d617fcfdc34946b1160213a948"},
{file = "pydantic-1.10.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbc6989fad0c030bd70a0b6f626f98a862224bc2b1e36bfc531ea2facc0a340c"},
{file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d573082c6ef99336f2cb5b667b781d2f776d4af311574fb53d908517ba523c22"},
{file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6bd7030c9abc80134087d8b6e7aa957e43d35714daa116aced57269a445b8f7b"},
{file = "pydantic-1.10.15-cp38-cp38-win_amd64.whl", hash = "sha256:3350f527bb04138f8aff932dc828f154847fbdc7a1a44c240fbfff1b57f49a12"},
{file = "pydantic-1.10.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:51d405b42f1b86703555797270e4970a9f9bd7953f3990142e69d1037f9d9e51"},
{file = "pydantic-1.10.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a980a77c52723b0dc56640ced396b73a024d4b74f02bcb2d21dbbac1debbe9d0"},
{file = "pydantic-1.10.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f1a1fb467d3f49e1708a3f632b11c69fccb4e748a325d5a491ddc7b5d22383"},
{file = "pydantic-1.10.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:676ed48f2c5bbad835f1a8ed8a6d44c1cd5a21121116d2ac40bd1cd3619746ed"},
{file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:92229f73400b80c13afcd050687f4d7e88de9234d74b27e6728aa689abcf58cc"},
{file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2746189100c646682eff0bce95efa7d2e203420d8e1c613dc0c6b4c1d9c1fde4"},
{file = "pydantic-1.10.15-cp39-cp39-win_amd64.whl", hash = "sha256:394f08750bd8eaad714718812e7fab615f873b3cdd0b9d84e76e51ef3b50b6b7"},
{file = "pydantic-1.10.15-py3-none-any.whl", hash = "sha256:28e552a060ba2740d0d2aabe35162652c1459a0b9069fe0db7f4ee0e18e74d58"},
{file = "pydantic-1.10.15.tar.gz", hash = "sha256:ca832e124eda231a60a041da4f013e3ff24949d94a01154b137fc2f2a43c3ffb"},
]
[package.dependencies]
@@ -1478,13 +1478,13 @@ files = [
[[package]]
name = "pytest"
version = "7.4.4"
version = "8.2.0"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
{file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
{file = "pytest-8.2.0-py3-none-any.whl", hash = "sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233"},
{file = "pytest-8.2.0.tar.gz", hash = "sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f"},
]
[package.dependencies]
@@ -1492,21 +1492,21 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""}
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<2.0"
tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
pluggy = ">=1.5,<2.0"
tomli = {version = ">=1", markers = "python_version < \"3.11\""}
[package.extras]
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
[[package]]
name = "pytest-asyncio"
version = "0.23.5"
version = "0.23.6"
description = "Pytest support for asyncio"
optional = false
python-versions = ">=3.8"
files = [
{file = "pytest-asyncio-0.23.5.tar.gz", hash = "sha256:3a048872a9c4ba14c3e90cc1aa20cbc2def7d01c7c8db3777ec281ba9c057675"},
{file = "pytest_asyncio-0.23.5-py3-none-any.whl", hash = "sha256:4e7093259ba018d58ede7d5315131d21923a60f8a6e9ee266ce1589685c89eac"},
{file = "pytest-asyncio-0.23.6.tar.gz", hash = "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f"},
{file = "pytest_asyncio-0.23.6-py3-none-any.whl", hash = "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a"},
]
[package.dependencies]
@@ -1518,13 +1518,13 @@ testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"]
[[package]]
name = "pytest-cov"
version = "4.1.0"
version = "5.0.0"
description = "Pytest plugin for measuring coverage."
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"},
{file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"},
{file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"},
{file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"},
]
[package.dependencies]
@@ -1532,22 +1532,22 @@ coverage = {version = ">=5.2.1", extras = ["toml"]}
pytest = ">=4.6"
[package.extras]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
[[package]]
name = "pytest-xdist"
version = "3.5.0"
version = "3.6.1"
description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "pytest-xdist-3.5.0.tar.gz", hash = "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a"},
{file = "pytest_xdist-3.5.0-py3-none-any.whl", hash = "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24"},
{file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"},
{file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"},
]
[package.dependencies]
execnet = ">=1.1"
pytest = ">=6.2.0"
execnet = ">=2.1"
pytest = ">=7.0.0"
[package.extras]
psutil = ["psutil (>=3.0)"]
@@ -1630,13 +1630,13 @@ files = [
[[package]]
name = "quart"
version = "0.19.4"
version = "0.19.5"
description = "A Python ASGI web microframework with the same API as Flask"
optional = false
python-versions = ">=3.8"
files = [
{file = "quart-0.19.4-py3-none-any.whl", hash = "sha256:959da9371b44b6f48d952661863f8f64e68a893481ef3f2ef45b177629dc0928"},
{file = "quart-0.19.4.tar.gz", hash = "sha256:22ff186cf164955a7bf7483ff42a739a9fad3b119041846b15dc9597ec74c85c"},
{file = "quart-0.19.5-py3-none-any.whl", hash = "sha256:581d959bda40d3c45500c50007a6451a157fd381c70d3556811bdd334adb9657"},
{file = "quart-0.19.5.tar.gz", hash = "sha256:fbe3cff25cd18b5c0e8d82bbeeaa43d78f35e5221ca5c50bb0b7c20255c87ab8"},
]
[package.dependencies]
@@ -1679,24 +1679,24 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "sniffio"
version = "1.3.0"
version = "1.3.1"
description = "Sniff out which async library your code is running under"
optional = false
python-versions = ">=3.7"
files = [
{file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
{file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
{file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
{file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
]
[[package]]
name = "starlette"
version = "0.36.3"
version = "0.37.2"
description = "The little ASGI library that shines."
optional = false
python-versions = ">=3.8"
files = [
{file = "starlette-0.36.3-py3-none-any.whl", hash = "sha256:13d429aa93a61dc40bf503e8c801db1f1bca3dc706b10ef2434a36123568f044"},
{file = "starlette-0.36.3.tar.gz", hash = "sha256:90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080"},
{file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"},
{file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"},
]
[package.dependencies]
@@ -1733,13 +1733,13 @@ files = [
[[package]]
name = "typing-extensions"
version = "4.9.0"
version = "4.11.0"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
files = [
{file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"},
{file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"},
{file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"},
{file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"},
]
[[package]]
@@ -1761,13 +1761,13 @@ zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "uvicorn"
version = "0.27.1"
version = "0.29.0"
description = "The lightning-fast ASGI server."
optional = false
python-versions = ">=3.8"
files = [
{file = "uvicorn-0.27.1-py3-none-any.whl", hash = "sha256:5c89da2f3895767472a35556e539fd59f7edbe9b1e9c0e1c99eebeadc61838e4"},
{file = "uvicorn-0.27.1.tar.gz", hash = "sha256:3d9a267296243532db80c83a959a3400502165ade2c1338dea4e67915fd4745a"},
{file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"},
{file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"},
]
[package.dependencies]
@@ -1999,13 +1999,13 @@ files = [
[[package]]
name = "werkzeug"
version = "3.0.1"
version = "3.0.2"
description = "The comprehensive WSGI web application library."
optional = false
python-versions = ">=3.8"
files = [
{file = "werkzeug-3.0.1-py3-none-any.whl", hash = "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"},
{file = "werkzeug-3.0.1.tar.gz", hash = "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"},
{file = "werkzeug-3.0.2-py3-none-any.whl", hash = "sha256:3aac3f5da756f93030740bc235d3e09449efcf65f2f55e3602e1d851b8f48795"},
{file = "werkzeug-3.0.2.tar.gz", hash = "sha256:e39b645a6ac92822588e7b39a692e7828724ceae0b0d702ef96701f90e70128d"},
]
[package.dependencies]
@@ -2147,20 +2147,20 @@ multidict = ">=4.0"
[[package]]
name = "zipp"
version = "3.17.0"
version = "3.18.1"
description = "Backport of pathlib-compatible object wrapper for zip files"
optional = false
python-versions = ">=3.8"
files = [
{file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"},
{file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"},
{file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"},
{file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"},
]
[package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "16419a2a03690fd222b2f8702b2a9a94bf1755bba24ba2336bb95cd41c6d625f"
python-versions = "^3.9"
content-hash = "f094ed8c9ed4aec70ad2498bb5133928c28e45fab952e9acbe5553a433fcaa3b"

View File

@@ -6,7 +6,7 @@ authors = ["yanyongyu <yyy@nonebot.dev>"]
license = "MIT"
[tool.poetry.dependencies]
python = "^3.8"
python = "^3.9"
[tool.poetry.group.dev.dependencies]
pydantic = "^1.0.0"

View File

@@ -1,14 +1,14 @@
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
[[package]]
name = "aiodns"
version = "3.1.1"
version = "3.2.0"
description = "Simple DNS resolver for asyncio"
optional = false
python-versions = "*"
files = [
{file = "aiodns-3.1.1-py3-none-any.whl", hash = "sha256:a387b63da4ced6aad35b1dda2d09620ad608a1c7c0fb71efa07ebb4cd511928d"},
{file = "aiodns-3.1.1.tar.gz", hash = "sha256:1073eac48185f7a4150cad7f96a5192d6911f12b4fb894de80a088508c9b3a99"},
{file = "aiodns-3.2.0-py3-none-any.whl", hash = "sha256:e443c0c27b07da3174a109fd9e736d69058d808f144d3c9d56dbd1776964c5f5"},
{file = "aiodns-3.2.0.tar.gz", hash = "sha256:62869b23409349c21b072883ec8998316b234c9a9e36675756e8e317e8768f72"},
]
[package.dependencies]
@@ -27,87 +27,87 @@ files = [
[[package]]
name = "aiohttp"
version = "3.9.3"
version = "3.9.5"
description = "Async http client/server framework (asyncio)"
optional = false
python-versions = ">=3.8"
files = [
{file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54"},
{file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc"},
{file = "aiohttp-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5"},
{file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b"},
{file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768"},
{file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5"},
{file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29"},
{file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec"},
{file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747"},
{file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6"},
{file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c"},
{file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf"},
{file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52"},
{file = "aiohttp-3.9.3-cp310-cp310-win32.whl", hash = "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b"},
{file = "aiohttp-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5"},
{file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d"},
{file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2"},
{file = "aiohttp-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc"},
{file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266"},
{file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce"},
{file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb"},
{file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b"},
{file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4"},
{file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae"},
{file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3"},
{file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283"},
{file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e"},
{file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4"},
{file = "aiohttp-3.9.3-cp311-cp311-win32.whl", hash = "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5"},
{file = "aiohttp-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8"},
{file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60"},
{file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869"},
{file = "aiohttp-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679"},
{file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96"},
{file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d"},
{file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11"},
{file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53"},
{file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5"},
{file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca"},
{file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1"},
{file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5"},
{file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6"},
{file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f"},
{file = "aiohttp-3.9.3-cp312-cp312-win32.whl", hash = "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38"},
{file = "aiohttp-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5"},
{file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c"},
{file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528"},
{file = "aiohttp-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542"},
{file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511"},
{file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672"},
{file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d"},
{file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168"},
{file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b"},
{file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194"},
{file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8"},
{file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4"},
{file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321"},
{file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2"},
{file = "aiohttp-3.9.3-cp38-cp38-win32.whl", hash = "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63"},
{file = "aiohttp-3.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c"},
{file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29"},
{file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1"},
{file = "aiohttp-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f"},
{file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3"},
{file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa"},
{file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e"},
{file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58"},
{file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533"},
{file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb"},
{file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3"},
{file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d"},
{file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a"},
{file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d"},
{file = "aiohttp-3.9.3-cp39-cp39-win32.whl", hash = "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051"},
{file = "aiohttp-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc"},
{file = "aiohttp-3.9.3.tar.gz", hash = "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7"},
{file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"},
{file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"},
{file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"},
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"},
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"},
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"},
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"},
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"},
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"},
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"},
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"},
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"},
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"},
{file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"},
{file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"},
{file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"},
{file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"},
{file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"},
{file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"},
{file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"},
{file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"},
{file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"},
{file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"},
{file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"},
{file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"},
{file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"},
{file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"},
{file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"},
{file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"},
{file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"},
{file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"},
{file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"},
{file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"},
{file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"},
{file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"},
{file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"},
]
[package.dependencies]
@@ -149,9 +149,6 @@ files = [
{file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"},
]
[package.dependencies]
typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""}
[[package]]
name = "anyio"
version = "4.3.0"
@@ -176,13 +173,13 @@ trio = ["trio (>=0.23)"]
[[package]]
name = "asgiref"
version = "3.7.2"
version = "3.8.1"
description = "ASGI specs, helper code, and adapters"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"},
{file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"},
{file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"},
{file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"},
]
[package.dependencies]
@@ -237,13 +234,13 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p
[[package]]
name = "blinker"
version = "1.7.0"
version = "1.8.1"
description = "Fast, simple object-to-object and broadcast signaling"
optional = false
python-versions = ">=3.8"
files = [
{file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"},
{file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"},
{file = "blinker-1.8.1-py3-none-any.whl", hash = "sha256:5f1cdeff423b77c31b89de0565cd03e5275a03028f44b2b15f912632a58cced6"},
{file = "blinker-1.8.1.tar.gz", hash = "sha256:da44ec748222dcd0105ef975eed946da197d5bdf8bafb6aa92f5bc89da63fa25"},
]
[[package]]
@@ -578,63 +575,63 @@ files = [
[[package]]
name = "coverage"
version = "7.4.3"
version = "7.5.0"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"},
{file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"},
{file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"},
{file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"},
{file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"},
{file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"},
{file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"},
{file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"},
{file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"},
{file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"},
{file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"},
{file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"},
{file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"},
{file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"},
{file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"},
{file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"},
{file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"},
{file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"},
{file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"},
{file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"},
{file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"},
{file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"},
{file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"},
{file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"},
{file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"},
{file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"},
{file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"},
{file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"},
{file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"},
{file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"},
{file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"},
{file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"},
{file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"},
{file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"},
{file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"},
{file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"},
{file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"},
{file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"},
{file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"},
{file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"},
{file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"},
{file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"},
{file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"},
{file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"},
{file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"},
{file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"},
{file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"},
{file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"},
{file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"},
{file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"},
{file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"},
{file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"},
{file = "coverage-7.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:432949a32c3e3f820af808db1833d6d1631664d53dd3ce487aa25d574e18ad1c"},
{file = "coverage-7.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bd7065249703cbeb6d4ce679c734bef0ee69baa7bff9724361ada04a15b7e3b"},
{file = "coverage-7.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbfe6389c5522b99768a93d89aca52ef92310a96b99782973b9d11e80511f932"},
{file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39793731182c4be939b4be0cdecde074b833f6171313cf53481f869937129ed3"},
{file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85a5dbe1ba1bf38d6c63b6d2c42132d45cbee6d9f0c51b52c59aa4afba057517"},
{file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:357754dcdfd811462a725e7501a9b4556388e8ecf66e79df6f4b988fa3d0b39a"},
{file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a81eb64feded34f40c8986869a2f764f0fe2db58c0530d3a4afbcde50f314880"},
{file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51431d0abbed3a868e967f8257c5faf283d41ec882f58413cf295a389bb22e58"},
{file = "coverage-7.5.0-cp310-cp310-win32.whl", hash = "sha256:f609ebcb0242d84b7adeee2b06c11a2ddaec5464d21888b2c8255f5fd6a98ae4"},
{file = "coverage-7.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:6782cd6216fab5a83216cc39f13ebe30adfac2fa72688c5a4d8d180cd52e8f6a"},
{file = "coverage-7.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e768d870801f68c74c2b669fc909839660180c366501d4cc4b87efd6b0eee375"},
{file = "coverage-7.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84921b10aeb2dd453247fd10de22907984eaf80901b578a5cf0bb1e279a587cb"},
{file = "coverage-7.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710c62b6e35a9a766b99b15cdc56d5aeda0914edae8bb467e9c355f75d14ee95"},
{file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c379cdd3efc0658e652a14112d51a7668f6bfca7445c5a10dee7eabecabba19d"},
{file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea9d3ca80bcf17edb2c08a4704259dadac196fe5e9274067e7a20511fad1743"},
{file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:41327143c5b1d715f5f98a397608f90ab9ebba606ae4e6f3389c2145410c52b1"},
{file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:565b2e82d0968c977e0b0f7cbf25fd06d78d4856289abc79694c8edcce6eb2de"},
{file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cf3539007202ebfe03923128fedfdd245db5860a36810136ad95a564a2fdffff"},
{file = "coverage-7.5.0-cp311-cp311-win32.whl", hash = "sha256:bf0b4b8d9caa8d64df838e0f8dcf68fb570c5733b726d1494b87f3da85db3a2d"},
{file = "coverage-7.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c6384cc90e37cfb60435bbbe0488444e54b98700f727f16f64d8bfda0b84656"},
{file = "coverage-7.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fed7a72d54bd52f4aeb6c6e951f363903bd7d70bc1cad64dd1f087980d309ab9"},
{file = "coverage-7.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cbe6581fcff7c8e262eb574244f81f5faaea539e712a058e6707a9d272fe5b64"},
{file = "coverage-7.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad97ec0da94b378e593ef532b980c15e377df9b9608c7c6da3506953182398af"},
{file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd4bacd62aa2f1a1627352fe68885d6ee694bdaebb16038b6e680f2924a9b2cc"},
{file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf032b6c105881f9d77fa17d9eebe0ad1f9bfb2ad25777811f97c5362aa07f2"},
{file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ba01d9ba112b55bfa4b24808ec431197bb34f09f66f7cb4fd0258ff9d3711b1"},
{file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f0bfe42523893c188e9616d853c47685e1c575fe25f737adf473d0405dcfa7eb"},
{file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a9a7ef30a1b02547c1b23fa9a5564f03c9982fc71eb2ecb7f98c96d7a0db5cf2"},
{file = "coverage-7.5.0-cp312-cp312-win32.whl", hash = "sha256:3c2b77f295edb9fcdb6a250f83e6481c679335ca7e6e4a955e4290350f2d22a4"},
{file = "coverage-7.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:427e1e627b0963ac02d7c8730ca6d935df10280d230508c0ba059505e9233475"},
{file = "coverage-7.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9dd88fce54abbdbf4c42fb1fea0e498973d07816f24c0e27a1ecaf91883ce69e"},
{file = "coverage-7.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a898c11dca8f8c97b467138004a30133974aacd572818c383596f8d5b2eb04a9"},
{file = "coverage-7.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07dfdd492d645eea1bd70fb1d6febdcf47db178b0d99161d8e4eed18e7f62fe7"},
{file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3d117890b6eee85887b1eed41eefe2e598ad6e40523d9f94c4c4b213258e4a4"},
{file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6afd2e84e7da40fe23ca588379f815fb6dbbb1b757c883935ed11647205111cb"},
{file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9960dd1891b2ddf13a7fe45339cd59ecee3abb6b8326d8b932d0c5da208104f"},
{file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ced268e82af993d7801a9db2dbc1d2322e786c5dc76295d8e89473d46c6b84d4"},
{file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7c211f25777746d468d76f11719e64acb40eed410d81c26cefac641975beb88"},
{file = "coverage-7.5.0-cp38-cp38-win32.whl", hash = "sha256:262fffc1f6c1a26125d5d573e1ec379285a3723363f3bd9c83923c9593a2ac25"},
{file = "coverage-7.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:eed462b4541c540d63ab57b3fc69e7d8c84d5957668854ee4e408b50e92ce26a"},
{file = "coverage-7.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0194d654e360b3e6cc9b774e83235bae6b9b2cac3be09040880bb0e8a88f4a1"},
{file = "coverage-7.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33c020d3322662e74bc507fb11488773a96894aa82a622c35a5a28673c0c26f5"},
{file = "coverage-7.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbdf2cae14a06827bec50bd58e49249452d211d9caddd8bd80e35b53cb04631"},
{file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3235d7c781232e525b0761730e052388a01548bd7f67d0067a253887c6e8df46"},
{file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2de4e546f0ec4b2787d625e0b16b78e99c3e21bc1722b4977c0dddf11ca84e"},
{file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0e206259b73af35c4ec1319fd04003776e11e859936658cb6ceffdeba0f5be"},
{file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2055c4fb9a6ff624253d432aa471a37202cd8f458c033d6d989be4499aed037b"},
{file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075299460948cd12722a970c7eae43d25d37989da682997687b34ae6b87c0ef0"},
{file = "coverage-7.5.0-cp39-cp39-win32.whl", hash = "sha256:280132aada3bc2f0fac939a5771db4fbb84f245cb35b94fae4994d4c1f80dae7"},
{file = "coverage-7.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:c58536f6892559e030e6924896a44098bc1290663ea12532c78cef71d0df8493"},
{file = "coverage-7.5.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:2b57780b51084d5223eee7b59f0d4911c31c16ee5aa12737c7a02455829ff067"},
{file = "coverage-7.5.0.tar.gz", hash = "sha256:cf62d17310f34084c59c01e027259076479128d11e4661bb6c9acb38c5e19bb8"},
]
[package.dependencies]
@@ -661,13 +658,13 @@ packaging = ">=20.4"
[[package]]
name = "exceptiongroup"
version = "1.2.0"
version = "1.2.1"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
files = [
{file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
{file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
{file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"},
{file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"},
]
[package.extras]
@@ -675,13 +672,13 @@ test = ["pytest (>=6)"]
[[package]]
name = "execnet"
version = "2.0.2"
version = "2.1.1"
description = "execnet: rapid multi-Python deployment"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "execnet-2.0.2-py3-none-any.whl", hash = "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41"},
{file = "execnet-2.0.2.tar.gz", hash = "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af"},
{file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"},
{file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"},
]
[package.extras]
@@ -689,32 +686,32 @@ testing = ["hatch", "pre-commit", "pytest", "tox"]
[[package]]
name = "fastapi"
version = "0.109.2"
version = "0.110.3"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
optional = false
python-versions = ">=3.8"
files = [
{file = "fastapi-0.109.2-py3-none-any.whl", hash = "sha256:2c9bab24667293b501cad8dd388c05240c850b58ec5876ee3283c47d6e1e3a4d"},
{file = "fastapi-0.109.2.tar.gz", hash = "sha256:f3817eac96fe4f65a2ebb4baa000f394e55f5fccdaf7f75250804bc58f354f73"},
{file = "fastapi-0.110.3-py3-none-any.whl", hash = "sha256:fd7600612f755e4050beb74001310b5a7e1796d149c2ee363124abdfa0289d32"},
{file = "fastapi-0.110.3.tar.gz", hash = "sha256:555700b0159379e94fdbfc6bb66a0f1c43f4cf7060f25239af3d84b63a656626"},
]
[package.dependencies]
pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
starlette = ">=0.36.3,<0.37.0"
starlette = ">=0.37.2,<0.38.0"
typing-extensions = ">=4.8.0"
[package.extras]
all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
[[package]]
name = "flask"
version = "3.0.2"
version = "3.0.3"
description = "A simple framework for building complex web applications."
optional = false
python-versions = ">=3.8"
files = [
{file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"},
{file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"},
{file = "flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3"},
{file = "flask-3.0.3.tar.gz", hash = "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842"},
]
[package.dependencies]
@@ -854,13 +851,13 @@ files = [
[[package]]
name = "httpcore"
version = "1.0.4"
version = "1.0.5"
description = "A minimal low-level HTTP client."
optional = false
python-versions = ">=3.8"
files = [
{file = "httpcore-1.0.4-py3-none-any.whl", hash = "sha256:ac418c1db41bade2ad53ae2f3834a3a0f5ae76b56cf5aa497d2d033384fc7d73"},
{file = "httpcore-1.0.4.tar.gz", hash = "sha256:cb2839ccfcba0d2d3c1131d3c3e26dfc327326fbe7a5dc0dbfe9f6c9151bb022"},
{file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"},
{file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"},
]
[package.dependencies]
@@ -871,7 +868,7 @@ h11 = ">=0.13,<0.15"
asyncio = ["anyio (>=4.0,<5.0)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
trio = ["trio (>=0.22.0,<0.25.0)"]
trio = ["trio (>=0.22.0,<0.26.0)"]
[[package]]
name = "httptools"
@@ -984,33 +981,33 @@ files = [
[[package]]
name = "idna"
version = "3.6"
version = "3.7"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.5"
files = [
{file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
{file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
{file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
{file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
]
[[package]]
name = "importlib-metadata"
version = "7.0.1"
version = "7.1.0"
description = "Read metadata from Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"},
{file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"},
{file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"},
{file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"},
]
[package.dependencies]
zipp = ">=0.5"
[package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
perf = ["ipython"]
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"]
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
[[package]]
name = "iniconfig"
@@ -1025,13 +1022,13 @@ files = [
[[package]]
name = "itsdangerous"
version = "2.1.2"
version = "2.2.0"
description = "Safely pass data to untrusted environments and back."
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"},
{file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"},
{file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"},
{file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"},
]
[[package]]
@@ -1242,15 +1239,15 @@ name = "nonebot-test"
version = "0.1.0"
description = "Private test env for nonebot"
optional = false
python-versions = "^3.8"
python-versions = "^3.9"
files = []
develop = false
[package.dependencies]
coverage-conditional-plugin = "^0.9.0"
nonebug = "^0.3.0"
nonebug = "^0.3.7"
pytest-asyncio = "^0.23.2"
pytest-cov = "^4.0.0"
pytest-cov = "^5.0.0"
pytest-xdist = "^3.0.2"
werkzeug = ">=2.3.6,<4.0.0"
wsproto = "^1.2.0"
@@ -1261,10 +1258,10 @@ url = "../test"
[[package]]
name = "nonebot2"
version = "2.2.1"
version = "2.3.0"
description = "An asynchronous python bot framework."
optional = false
python-versions = "^3.8"
python-versions = "^3.9"
files = []
develop = true
@@ -1297,42 +1294,42 @@ url = "../.."
[[package]]
name = "nonebug"
version = "0.3.5"
version = "0.3.7"
description = "nonebot2 test framework"
optional = false
python-versions = ">=3.8,<4.0"
python-versions = "<4.0,>=3.8"
files = [
{file = "nonebug-0.3.5-py3-none-any.whl", hash = "sha256:588831b08b3ea42d058874214bedae646e2ab8c1ec4ae1540ff789873107a8fa"},
{file = "nonebug-0.3.5.tar.gz", hash = "sha256:4d4bf9448cd1cbfaaabaab73dbe4ac8757e86dd92a41ef79cdece8dd61e724e2"},
{file = "nonebug-0.3.7-py3-none-any.whl", hash = "sha256:c39f462aafe20660602a8b789a575db6c9346ab5b6f1985eb9d98b861528299a"},
{file = "nonebug-0.3.7.tar.gz", hash = "sha256:8a75183400681f34eafc7caa2bb6dd511c3b5660c59264f1c379a088c7ac2247"},
]
[package.dependencies]
asgiref = ">=3.4.0,<4.0.0"
async-asgi-testclient = ">=1.4.8,<2.0.0"
nonebot2 = ">=2.0.0-rc.2,<3.0.0"
pytest = ">=7.0.0,<8.0.0"
nonebot2 = ">=2.2.0,<3.0.0"
pytest = ">=7.0.0,<9.0.0"
typing-extensions = ">=4.0.0,<5.0.0"
[[package]]
name = "packaging"
version = "23.2"
version = "24.0"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.7"
files = [
{file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
{file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
]
[[package]]
name = "pluggy"
version = "1.4.0"
version = "1.5.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.8"
files = [
{file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
{file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
]
[package.extras]
@@ -1418,29 +1415,29 @@ idna = ["idna (>=2.1)"]
[[package]]
name = "pycparser"
version = "2.21"
version = "2.22"
description = "C parser in Python"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
python-versions = ">=3.8"
files = [
{file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
{file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
]
[[package]]
name = "pydantic"
version = "2.6.2"
version = "2.7.1"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.8"
files = [
{file = "pydantic-2.6.2-py3-none-any.whl", hash = "sha256:37a5432e54b12fecaa1049c5195f3d860a10e01bdfd24f1840ef14bd0d3aeab3"},
{file = "pydantic-2.6.2.tar.gz", hash = "sha256:a09be1c3d28f3abe37f8a78af58284b236a92ce520105ddc91a6d29ea1176ba7"},
{file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"},
{file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"},
]
[package.dependencies]
annotated-types = ">=0.4.0"
pydantic-core = "2.16.3"
pydantic-core = "2.18.2"
typing-extensions = ">=4.6.1"
[package.extras]
@@ -1448,90 +1445,90 @@ email = ["email-validator (>=2.0.0)"]
[[package]]
name = "pydantic-core"
version = "2.16.3"
description = ""
version = "2.18.2"
description = "Core functionality for Pydantic validation and serialization"
optional = false
python-versions = ">=3.8"
files = [
{file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"},
{file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"},
{file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"},
{file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"},
{file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"},
{file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"},
{file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"},
{file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"},
{file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"},
{file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"},
{file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"},
{file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"},
{file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"},
{file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"},
{file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"},
{file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"},
{file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"},
{file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"},
{file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"},
{file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"},
{file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"},
{file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"},
{file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"},
{file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"},
{file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"},
{file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"},
{file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"},
{file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"},
{file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"},
{file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"},
{file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"},
{file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"},
{file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"},
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"},
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"},
{file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"},
{file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"},
{file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"},
{file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"},
{file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"},
{file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"},
{file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"},
{file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"},
{file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"},
{file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"},
{file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"},
{file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"},
{file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"},
{file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"},
{file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"},
{file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"},
{file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"},
{file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"},
{file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"},
{file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"},
{file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"},
{file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"},
{file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"},
{file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"},
{file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"},
{file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"},
{file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"},
{file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"},
{file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"},
{file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"},
{file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"},
]
[package.dependencies]
@@ -1550,13 +1547,13 @@ files = [
[[package]]
name = "pytest"
version = "7.4.4"
version = "8.2.0"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
{file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
{file = "pytest-8.2.0-py3-none-any.whl", hash = "sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233"},
{file = "pytest-8.2.0.tar.gz", hash = "sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f"},
]
[package.dependencies]
@@ -1564,21 +1561,21 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""}
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<2.0"
tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
pluggy = ">=1.5,<2.0"
tomli = {version = ">=1", markers = "python_version < \"3.11\""}
[package.extras]
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
[[package]]
name = "pytest-asyncio"
version = "0.23.5"
version = "0.23.6"
description = "Pytest support for asyncio"
optional = false
python-versions = ">=3.8"
files = [
{file = "pytest-asyncio-0.23.5.tar.gz", hash = "sha256:3a048872a9c4ba14c3e90cc1aa20cbc2def7d01c7c8db3777ec281ba9c057675"},
{file = "pytest_asyncio-0.23.5-py3-none-any.whl", hash = "sha256:4e7093259ba018d58ede7d5315131d21923a60f8a6e9ee266ce1589685c89eac"},
{file = "pytest-asyncio-0.23.6.tar.gz", hash = "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f"},
{file = "pytest_asyncio-0.23.6-py3-none-any.whl", hash = "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a"},
]
[package.dependencies]
@@ -1590,13 +1587,13 @@ testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"]
[[package]]
name = "pytest-cov"
version = "4.1.0"
version = "5.0.0"
description = "Pytest plugin for measuring coverage."
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"},
{file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"},
{file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"},
{file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"},
]
[package.dependencies]
@@ -1604,22 +1601,22 @@ coverage = {version = ">=5.2.1", extras = ["toml"]}
pytest = ">=4.6"
[package.extras]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
[[package]]
name = "pytest-xdist"
version = "3.5.0"
version = "3.6.1"
description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "pytest-xdist-3.5.0.tar.gz", hash = "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a"},
{file = "pytest_xdist-3.5.0-py3-none-any.whl", hash = "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24"},
{file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"},
{file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"},
]
[package.dependencies]
execnet = ">=1.1"
pytest = ">=6.2.0"
execnet = ">=2.1"
pytest = ">=7.0.0"
[package.extras]
psutil = ["psutil (>=3.0)"]
@@ -1702,13 +1699,13 @@ files = [
[[package]]
name = "quart"
version = "0.19.4"
version = "0.19.5"
description = "A Python ASGI web microframework with the same API as Flask"
optional = false
python-versions = ">=3.8"
files = [
{file = "quart-0.19.4-py3-none-any.whl", hash = "sha256:959da9371b44b6f48d952661863f8f64e68a893481ef3f2ef45b177629dc0928"},
{file = "quart-0.19.4.tar.gz", hash = "sha256:22ff186cf164955a7bf7483ff42a739a9fad3b119041846b15dc9597ec74c85c"},
{file = "quart-0.19.5-py3-none-any.whl", hash = "sha256:581d959bda40d3c45500c50007a6451a157fd381c70d3556811bdd334adb9657"},
{file = "quart-0.19.5.tar.gz", hash = "sha256:fbe3cff25cd18b5c0e8d82bbeeaa43d78f35e5221ca5c50bb0b7c20255c87ab8"},
]
[package.dependencies]
@@ -1751,24 +1748,24 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "sniffio"
version = "1.3.0"
version = "1.3.1"
description = "Sniff out which async library your code is running under"
optional = false
python-versions = ">=3.7"
files = [
{file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
{file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
{file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
{file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
]
[[package]]
name = "starlette"
version = "0.36.3"
version = "0.37.2"
description = "The little ASGI library that shines."
optional = false
python-versions = ">=3.8"
files = [
{file = "starlette-0.36.3-py3-none-any.whl", hash = "sha256:13d429aa93a61dc40bf503e8c801db1f1bca3dc706b10ef2434a36123568f044"},
{file = "starlette-0.36.3.tar.gz", hash = "sha256:90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080"},
{file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"},
{file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"},
]
[package.dependencies]
@@ -1805,13 +1802,13 @@ files = [
[[package]]
name = "typing-extensions"
version = "4.9.0"
version = "4.11.0"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
files = [
{file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"},
{file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"},
{file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"},
{file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"},
]
[[package]]
@@ -1833,13 +1830,13 @@ zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "uvicorn"
version = "0.27.1"
version = "0.29.0"
description = "The lightning-fast ASGI server."
optional = false
python-versions = ">=3.8"
files = [
{file = "uvicorn-0.27.1-py3-none-any.whl", hash = "sha256:5c89da2f3895767472a35556e539fd59f7edbe9b1e9c0e1c99eebeadc61838e4"},
{file = "uvicorn-0.27.1.tar.gz", hash = "sha256:3d9a267296243532db80c83a959a3400502165ade2c1338dea4e67915fd4745a"},
{file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"},
{file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"},
]
[package.dependencies]
@@ -2071,13 +2068,13 @@ files = [
[[package]]
name = "werkzeug"
version = "3.0.1"
version = "3.0.2"
description = "The comprehensive WSGI web application library."
optional = false
python-versions = ">=3.8"
files = [
{file = "werkzeug-3.0.1-py3-none-any.whl", hash = "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"},
{file = "werkzeug-3.0.1.tar.gz", hash = "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"},
{file = "werkzeug-3.0.2-py3-none-any.whl", hash = "sha256:3aac3f5da756f93030740bc235d3e09449efcf65f2f55e3602e1d851b8f48795"},
{file = "werkzeug-3.0.2.tar.gz", hash = "sha256:e39b645a6ac92822588e7b39a692e7828724ceae0b0d702ef96701f90e70128d"},
]
[package.dependencies]
@@ -2219,20 +2216,20 @@ multidict = ">=4.0"
[[package]]
name = "zipp"
version = "3.17.0"
version = "3.18.1"
description = "Backport of pathlib-compatible object wrapper for zip files"
optional = false
python-versions = ">=3.8"
files = [
{file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"},
{file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"},
{file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"},
{file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"},
]
[package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "ef03690feac931e808173fb341bfbb07aaff0164af5f7c61d68fa3c69f30e7e5"
python-versions = "^3.9"
content-hash = "aa80c12980b5ee70f8920f383dd7ad35b832931c486c84070eefab08a5cafd74"

View File

@@ -6,7 +6,7 @@ authors = ["yanyongyu <yyy@nonebot.dev>"]
license = "MIT"
[tool.poetry.dependencies]
python = "^3.8"
python = "^3.9"
[tool.poetry.group.dev.dependencies]
pydantic = "^2.0.0"

417
envs/test/poetry.lock generated
View File

@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
[[package]]
name = "annotated-types"
@@ -11,18 +11,15 @@ files = [
{file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"},
]
[package.dependencies]
typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""}
[[package]]
name = "asgiref"
version = "3.7.2"
version = "3.8.1"
description = "ASGI specs, helper code, and adapters"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"},
{file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"},
{file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"},
{file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"},
]
[package.dependencies]
@@ -168,63 +165,63 @@ files = [
[[package]]
name = "coverage"
version = "7.4.3"
version = "7.5.0"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"},
{file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"},
{file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"},
{file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"},
{file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"},
{file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"},
{file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"},
{file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"},
{file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"},
{file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"},
{file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"},
{file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"},
{file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"},
{file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"},
{file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"},
{file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"},
{file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"},
{file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"},
{file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"},
{file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"},
{file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"},
{file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"},
{file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"},
{file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"},
{file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"},
{file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"},
{file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"},
{file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"},
{file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"},
{file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"},
{file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"},
{file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"},
{file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"},
{file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"},
{file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"},
{file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"},
{file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"},
{file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"},
{file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"},
{file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"},
{file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"},
{file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"},
{file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"},
{file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"},
{file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"},
{file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"},
{file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"},
{file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"},
{file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"},
{file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"},
{file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"},
{file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"},
{file = "coverage-7.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:432949a32c3e3f820af808db1833d6d1631664d53dd3ce487aa25d574e18ad1c"},
{file = "coverage-7.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bd7065249703cbeb6d4ce679c734bef0ee69baa7bff9724361ada04a15b7e3b"},
{file = "coverage-7.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbfe6389c5522b99768a93d89aca52ef92310a96b99782973b9d11e80511f932"},
{file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39793731182c4be939b4be0cdecde074b833f6171313cf53481f869937129ed3"},
{file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85a5dbe1ba1bf38d6c63b6d2c42132d45cbee6d9f0c51b52c59aa4afba057517"},
{file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:357754dcdfd811462a725e7501a9b4556388e8ecf66e79df6f4b988fa3d0b39a"},
{file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a81eb64feded34f40c8986869a2f764f0fe2db58c0530d3a4afbcde50f314880"},
{file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51431d0abbed3a868e967f8257c5faf283d41ec882f58413cf295a389bb22e58"},
{file = "coverage-7.5.0-cp310-cp310-win32.whl", hash = "sha256:f609ebcb0242d84b7adeee2b06c11a2ddaec5464d21888b2c8255f5fd6a98ae4"},
{file = "coverage-7.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:6782cd6216fab5a83216cc39f13ebe30adfac2fa72688c5a4d8d180cd52e8f6a"},
{file = "coverage-7.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e768d870801f68c74c2b669fc909839660180c366501d4cc4b87efd6b0eee375"},
{file = "coverage-7.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84921b10aeb2dd453247fd10de22907984eaf80901b578a5cf0bb1e279a587cb"},
{file = "coverage-7.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710c62b6e35a9a766b99b15cdc56d5aeda0914edae8bb467e9c355f75d14ee95"},
{file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c379cdd3efc0658e652a14112d51a7668f6bfca7445c5a10dee7eabecabba19d"},
{file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea9d3ca80bcf17edb2c08a4704259dadac196fe5e9274067e7a20511fad1743"},
{file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:41327143c5b1d715f5f98a397608f90ab9ebba606ae4e6f3389c2145410c52b1"},
{file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:565b2e82d0968c977e0b0f7cbf25fd06d78d4856289abc79694c8edcce6eb2de"},
{file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cf3539007202ebfe03923128fedfdd245db5860a36810136ad95a564a2fdffff"},
{file = "coverage-7.5.0-cp311-cp311-win32.whl", hash = "sha256:bf0b4b8d9caa8d64df838e0f8dcf68fb570c5733b726d1494b87f3da85db3a2d"},
{file = "coverage-7.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c6384cc90e37cfb60435bbbe0488444e54b98700f727f16f64d8bfda0b84656"},
{file = "coverage-7.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fed7a72d54bd52f4aeb6c6e951f363903bd7d70bc1cad64dd1f087980d309ab9"},
{file = "coverage-7.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cbe6581fcff7c8e262eb574244f81f5faaea539e712a058e6707a9d272fe5b64"},
{file = "coverage-7.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad97ec0da94b378e593ef532b980c15e377df9b9608c7c6da3506953182398af"},
{file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd4bacd62aa2f1a1627352fe68885d6ee694bdaebb16038b6e680f2924a9b2cc"},
{file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf032b6c105881f9d77fa17d9eebe0ad1f9bfb2ad25777811f97c5362aa07f2"},
{file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ba01d9ba112b55bfa4b24808ec431197bb34f09f66f7cb4fd0258ff9d3711b1"},
{file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f0bfe42523893c188e9616d853c47685e1c575fe25f737adf473d0405dcfa7eb"},
{file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a9a7ef30a1b02547c1b23fa9a5564f03c9982fc71eb2ecb7f98c96d7a0db5cf2"},
{file = "coverage-7.5.0-cp312-cp312-win32.whl", hash = "sha256:3c2b77f295edb9fcdb6a250f83e6481c679335ca7e6e4a955e4290350f2d22a4"},
{file = "coverage-7.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:427e1e627b0963ac02d7c8730ca6d935df10280d230508c0ba059505e9233475"},
{file = "coverage-7.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9dd88fce54abbdbf4c42fb1fea0e498973d07816f24c0e27a1ecaf91883ce69e"},
{file = "coverage-7.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a898c11dca8f8c97b467138004a30133974aacd572818c383596f8d5b2eb04a9"},
{file = "coverage-7.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07dfdd492d645eea1bd70fb1d6febdcf47db178b0d99161d8e4eed18e7f62fe7"},
{file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3d117890b6eee85887b1eed41eefe2e598ad6e40523d9f94c4c4b213258e4a4"},
{file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6afd2e84e7da40fe23ca588379f815fb6dbbb1b757c883935ed11647205111cb"},
{file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9960dd1891b2ddf13a7fe45339cd59ecee3abb6b8326d8b932d0c5da208104f"},
{file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ced268e82af993d7801a9db2dbc1d2322e786c5dc76295d8e89473d46c6b84d4"},
{file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7c211f25777746d468d76f11719e64acb40eed410d81c26cefac641975beb88"},
{file = "coverage-7.5.0-cp38-cp38-win32.whl", hash = "sha256:262fffc1f6c1a26125d5d573e1ec379285a3723363f3bd9c83923c9593a2ac25"},
{file = "coverage-7.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:eed462b4541c540d63ab57b3fc69e7d8c84d5957668854ee4e408b50e92ce26a"},
{file = "coverage-7.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0194d654e360b3e6cc9b774e83235bae6b9b2cac3be09040880bb0e8a88f4a1"},
{file = "coverage-7.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33c020d3322662e74bc507fb11488773a96894aa82a622c35a5a28673c0c26f5"},
{file = "coverage-7.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbdf2cae14a06827bec50bd58e49249452d211d9caddd8bd80e35b53cb04631"},
{file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3235d7c781232e525b0761730e052388a01548bd7f67d0067a253887c6e8df46"},
{file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2de4e546f0ec4b2787d625e0b16b78e99c3e21bc1722b4977c0dddf11ca84e"},
{file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0e206259b73af35c4ec1319fd04003776e11e859936658cb6ceffdeba0f5be"},
{file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2055c4fb9a6ff624253d432aa471a37202cd8f458c033d6d989be4499aed037b"},
{file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075299460948cd12722a970c7eae43d25d37989da682997687b34ae6b87c0ef0"},
{file = "coverage-7.5.0-cp39-cp39-win32.whl", hash = "sha256:280132aada3bc2f0fac939a5771db4fbb84f245cb35b94fae4994d4c1f80dae7"},
{file = "coverage-7.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:c58536f6892559e030e6924896a44098bc1290663ea12532c78cef71d0df8493"},
{file = "coverage-7.5.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:2b57780b51084d5223eee7b59f0d4911c31c16ee5aa12737c7a02455829ff067"},
{file = "coverage-7.5.0.tar.gz", hash = "sha256:cf62d17310f34084c59c01e027259076479128d11e4661bb6c9acb38c5e19bb8"},
]
[package.dependencies]
@@ -251,13 +248,13 @@ packaging = ">=20.4"
[[package]]
name = "exceptiongroup"
version = "1.2.0"
version = "1.2.1"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
files = [
{file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
{file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
{file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"},
{file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"},
]
[package.extras]
@@ -265,13 +262,13 @@ test = ["pytest (>=6)"]
[[package]]
name = "execnet"
version = "2.0.2"
version = "2.1.1"
description = "execnet: rapid multi-Python deployment"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "execnet-2.0.2-py3-none-any.whl", hash = "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41"},
{file = "execnet-2.0.2.tar.gz", hash = "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af"},
{file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"},
{file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"},
]
[package.extras]
@@ -290,33 +287,33 @@ files = [
[[package]]
name = "idna"
version = "3.6"
version = "3.7"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.5"
files = [
{file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
{file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
{file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
{file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
]
[[package]]
name = "importlib-metadata"
version = "7.0.1"
version = "7.1.0"
description = "Read metadata from Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"},
{file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"},
{file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"},
{file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"},
]
[package.dependencies]
zipp = ">=0.5"
[package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
perf = ["ipython"]
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"]
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
[[package]]
name = "iniconfig"
@@ -517,13 +514,13 @@ files = [
[[package]]
name = "nonebot2"
version = "2.2.0"
version = "2.2.1"
description = "An asynchronous python bot framework."
optional = false
python-versions = ">=3.8,<4.0"
files = [
{file = "nonebot2-2.2.0-py3-none-any.whl", hash = "sha256:447fa63d384414c0e610f4ce6d2b3999db81ac2becd8d86716c4117013dc032f"},
{file = "nonebot2-2.2.0.tar.gz", hash = "sha256:138800846fa3dc635bda9f2ddc589519ee8d9d3b401013fbb95e47676fc830fb"},
{file = "nonebot2-2.2.1-py3-none-any.whl", hash = "sha256:88f2bb456bf90922925bbe489a9effe3b09300f3aa50bfa75ee50d8a83d7330f"},
{file = "nonebot2-2.2.1.tar.gz", hash = "sha256:fe57692300571b00724999238545d8d894523460e6835a11b326a2e1cdf98fc4"},
]
[package.dependencies]
@@ -545,42 +542,42 @@ websockets = ["websockets (>=10.0)"]
[[package]]
name = "nonebug"
version = "0.3.5"
version = "0.3.7"
description = "nonebot2 test framework"
optional = false
python-versions = ">=3.8,<4.0"
python-versions = "<4.0,>=3.8"
files = [
{file = "nonebug-0.3.5-py3-none-any.whl", hash = "sha256:588831b08b3ea42d058874214bedae646e2ab8c1ec4ae1540ff789873107a8fa"},
{file = "nonebug-0.3.5.tar.gz", hash = "sha256:4d4bf9448cd1cbfaaabaab73dbe4ac8757e86dd92a41ef79cdece8dd61e724e2"},
{file = "nonebug-0.3.7-py3-none-any.whl", hash = "sha256:c39f462aafe20660602a8b789a575db6c9346ab5b6f1985eb9d98b861528299a"},
{file = "nonebug-0.3.7.tar.gz", hash = "sha256:8a75183400681f34eafc7caa2bb6dd511c3b5660c59264f1c379a088c7ac2247"},
]
[package.dependencies]
asgiref = ">=3.4.0,<4.0.0"
async-asgi-testclient = ">=1.4.8,<2.0.0"
nonebot2 = ">=2.0.0-rc.2,<3.0.0"
pytest = ">=7.0.0,<8.0.0"
nonebot2 = ">=2.2.0,<3.0.0"
pytest = ">=7.0.0,<9.0.0"
typing-extensions = ">=4.0.0,<5.0.0"
[[package]]
name = "packaging"
version = "23.2"
version = "24.0"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.7"
files = [
{file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
{file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
]
[[package]]
name = "pluggy"
version = "1.4.0"
version = "1.5.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.8"
files = [
{file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
{file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
]
[package.extras]
@@ -589,18 +586,18 @@ testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "pydantic"
version = "2.6.2"
version = "2.7.1"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.8"
files = [
{file = "pydantic-2.6.2-py3-none-any.whl", hash = "sha256:37a5432e54b12fecaa1049c5195f3d860a10e01bdfd24f1840ef14bd0d3aeab3"},
{file = "pydantic-2.6.2.tar.gz", hash = "sha256:a09be1c3d28f3abe37f8a78af58284b236a92ce520105ddc91a6d29ea1176ba7"},
{file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"},
{file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"},
]
[package.dependencies]
annotated-types = ">=0.4.0"
pydantic-core = "2.16.3"
pydantic-core = "2.18.2"
typing-extensions = ">=4.6.1"
[package.extras]
@@ -608,90 +605,90 @@ email = ["email-validator (>=2.0.0)"]
[[package]]
name = "pydantic-core"
version = "2.16.3"
description = ""
version = "2.18.2"
description = "Core functionality for Pydantic validation and serialization"
optional = false
python-versions = ">=3.8"
files = [
{file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"},
{file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"},
{file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"},
{file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"},
{file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"},
{file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"},
{file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"},
{file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"},
{file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"},
{file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"},
{file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"},
{file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"},
{file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"},
{file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"},
{file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"},
{file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"},
{file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"},
{file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"},
{file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"},
{file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"},
{file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"},
{file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"},
{file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"},
{file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"},
{file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"},
{file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"},
{file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"},
{file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"},
{file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"},
{file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"},
{file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"},
{file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"},
{file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"},
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"},
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"},
{file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"},
{file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"},
{file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"},
{file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"},
{file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"},
{file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"},
{file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"},
{file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"},
{file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"},
{file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"},
{file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"},
{file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"},
{file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"},
{file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"},
{file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"},
{file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"},
{file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"},
{file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"},
{file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"},
{file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"},
{file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"},
{file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"},
{file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"},
{file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"},
{file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"},
{file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"},
{file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"},
{file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"},
{file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"},
{file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"},
{file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"},
]
[package.dependencies]
@@ -710,13 +707,13 @@ files = [
[[package]]
name = "pytest"
version = "7.4.4"
version = "8.2.0"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
{file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
{file = "pytest-8.2.0-py3-none-any.whl", hash = "sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233"},
{file = "pytest-8.2.0.tar.gz", hash = "sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f"},
]
[package.dependencies]
@@ -724,21 +721,21 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""}
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<2.0"
tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
pluggy = ">=1.5,<2.0"
tomli = {version = ">=1", markers = "python_version < \"3.11\""}
[package.extras]
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
[[package]]
name = "pytest-asyncio"
version = "0.23.5"
version = "0.23.6"
description = "Pytest support for asyncio"
optional = false
python-versions = ">=3.8"
files = [
{file = "pytest-asyncio-0.23.5.tar.gz", hash = "sha256:3a048872a9c4ba14c3e90cc1aa20cbc2def7d01c7c8db3777ec281ba9c057675"},
{file = "pytest_asyncio-0.23.5-py3-none-any.whl", hash = "sha256:4e7093259ba018d58ede7d5315131d21923a60f8a6e9ee266ce1589685c89eac"},
{file = "pytest-asyncio-0.23.6.tar.gz", hash = "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f"},
{file = "pytest_asyncio-0.23.6-py3-none-any.whl", hash = "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a"},
]
[package.dependencies]
@@ -750,13 +747,13 @@ testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"]
[[package]]
name = "pytest-cov"
version = "4.1.0"
version = "5.0.0"
description = "Pytest plugin for measuring coverage."
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"},
{file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"},
{file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"},
{file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"},
]
[package.dependencies]
@@ -764,22 +761,22 @@ coverage = {version = ">=5.2.1", extras = ["toml"]}
pytest = ">=4.6"
[package.extras]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
[[package]]
name = "pytest-xdist"
version = "3.5.0"
version = "3.6.1"
description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "pytest-xdist-3.5.0.tar.gz", hash = "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a"},
{file = "pytest_xdist-3.5.0-py3-none-any.whl", hash = "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24"},
{file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"},
{file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"},
]
[package.dependencies]
execnet = ">=1.1"
pytest = ">=6.2.0"
execnet = ">=2.1"
pytest = ">=7.0.0"
[package.extras]
psutil = ["psutil (>=3.0)"]
@@ -834,13 +831,13 @@ files = [
[[package]]
name = "typing-extensions"
version = "4.9.0"
version = "4.11.0"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
files = [
{file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"},
{file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"},
{file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"},
{file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"},
]
[[package]]
@@ -862,13 +859,13 @@ zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "werkzeug"
version = "3.0.1"
version = "3.0.2"
description = "The comprehensive WSGI web application library."
optional = false
python-versions = ">=3.8"
files = [
{file = "werkzeug-3.0.1-py3-none-any.whl", hash = "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"},
{file = "werkzeug-3.0.1.tar.gz", hash = "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"},
{file = "werkzeug-3.0.2-py3-none-any.whl", hash = "sha256:3aac3f5da756f93030740bc235d3e09449efcf65f2f55e3602e1d851b8f48795"},
{file = "werkzeug-3.0.2.tar.gz", hash = "sha256:e39b645a6ac92822588e7b39a692e7828724ceae0b0d702ef96701f90e70128d"},
]
[package.dependencies]
@@ -1010,20 +1007,20 @@ multidict = ">=4.0"
[[package]]
name = "zipp"
version = "3.17.0"
version = "3.18.1"
description = "Backport of pathlib-compatible object wrapper for zip files"
optional = false
python-versions = ">=3.8"
files = [
{file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"},
{file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"},
{file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"},
{file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"},
]
[package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "ab5729309587cb130ac7848e8862368995372cf2fc91d0966598b3c6b49028e5"
python-versions = "^3.9"
content-hash = "ee7bfccee3250456dbe0b9f11914fbbc290b83abdc1a82ae6f4c3525c2818416"

View File

@@ -7,10 +7,10 @@ license = "MIT"
packages = [{ include = "nonebot-test.py" }]
[tool.poetry.dependencies]
python = "^3.8"
nonebug = "^0.3.0"
python = "^3.9"
nonebug = "^0.3.7"
wsproto = "^1.2.0"
pytest-cov = "^4.0.0"
pytest-cov = "^5.0.0"
pytest-xdist = "^3.0.2"
pytest-asyncio = "^0.23.2"
werkzeug = ">=2.3.6,<4.0.0"

View File

@@ -45,7 +45,7 @@ FrontMatter:
import os
from importlib.metadata import version
from typing import Any, Dict, Type, Union, TypeVar, Optional, overload
from typing import Any, Union, TypeVar, Optional, overload
import loguru
@@ -100,7 +100,7 @@ def get_adapter(name: str) -> Adapter:
@overload
def get_adapter(name: Type[A]) -> A:
def get_adapter(name: type[A]) -> A:
"""
参数:
name: 适配器类型
@@ -110,7 +110,7 @@ def get_adapter(name: Type[A]) -> A:
"""
def get_adapter(name: Union[str, Type[Adapter]]) -> Adapter:
def get_adapter(name: Union[str, type[Adapter]]) -> Adapter:
"""获取已注册的 {ref}`nonebot.adapters.Adapter` 实例。
异常:
@@ -131,7 +131,7 @@ def get_adapter(name: Union[str, Type[Adapter]]) -> Adapter:
return adapters[target]
def get_adapters() -> Dict[str, Adapter]:
def get_adapters() -> dict[str, Adapter]:
"""获取所有已注册的 {ref}`nonebot.adapters.Adapter` 实例。
返回:
@@ -230,7 +230,7 @@ def get_bot(self_id: Optional[str] = None) -> Bot:
raise ValueError("There are no bots to get.")
def get_bots() -> Dict[str, Bot]:
def get_bots() -> dict[str, Bot]:
"""获取所有连接到 NoneBot 的 {ref}`nonebot.adapters.Bot` 对象。
返回:
@@ -249,7 +249,7 @@ def get_bots() -> Dict[str, Bot]:
return get_driver().bots
def _resolve_combine_expr(obj_str: str) -> Type[Driver]:
def _resolve_combine_expr(obj_str: str) -> type[Driver]:
drivers = obj_str.split("+")
DriverClass = resolve_dot_notation(
drivers[0], "Driver", default_prefix="nonebot.drivers."
@@ -266,11 +266,12 @@ def _resolve_combine_expr(obj_str: str) -> Type[Driver]:
def _log_patcher(record: "loguru.Record"):
"""使用插件标识优化日志展示"""
record["name"] = (
plugin.name
plugin.id_
if (module_name := record["name"])
and (plugin := get_plugin_by_module_name(module_name))
else (module_name and module_name.split(".")[0])
else (module_name and module_name.split(".", maxsplit=1)[0])
)

View File

@@ -7,21 +7,18 @@ FrontMatter:
description: nonebot.compat 模块
"""
from collections.abc import Generator
from dataclasses import dataclass, is_dataclass
from typing_extensions import Self, Annotated, get_args, get_origin, is_typeddict
from typing_extensions import Self, get_args, get_origin, is_typeddict
from typing import (
TYPE_CHECKING,
Any,
Set,
Dict,
List,
Type,
Union,
TypeVar,
Callable,
Optional,
Protocol,
Generator,
Annotated,
)
from pydantic import VERSION, BaseModel
@@ -94,7 +91,7 @@ if PYDANTIC_V2: # pragma: pydantic-v2
super().__init__(default=default, **kwargs)
@property
def extra(self) -> Dict[str, Any]:
def extra(self) -> dict[str, Any]:
"""Extra data that is not part of the standard pydantic fields.
For compatibility with pydantic v1.
@@ -160,7 +157,7 @@ if PYDANTIC_V2: # pragma: pydantic-v2
# to allow store them in a set.
return id(self)
def extract_field_info(field_info: BaseFieldInfo) -> Dict[str, Any]:
def extract_field_info(field_info: BaseFieldInfo) -> dict[str, Any]:
"""Get FieldInfo init kwargs from a FieldInfo instance."""
kwargs = field_info._attributes_set.copy()
@@ -176,7 +173,7 @@ if PYDANTIC_V2: # pragma: pydantic-v2
type, config=None if model_field._annotation_has_config() else config
).validate_python(value)
def model_fields(model: Type[BaseModel]) -> List[ModelField]:
def model_fields(model: type[BaseModel]) -> list[ModelField]:
"""Get field list of a model."""
return [
@@ -188,19 +185,19 @@ if PYDANTIC_V2: # pragma: pydantic-v2
for name, field_info in model.model_fields.items()
]
def model_config(model: Type[BaseModel]) -> Any:
def model_config(model: type[BaseModel]) -> Any:
"""Get config of a model."""
return model.model_config
def model_dump(
model: BaseModel,
include: Optional[Set[str]] = None,
exclude: Optional[Set[str]] = None,
include: Optional[set[str]] = None,
exclude: Optional[set[str]] = None,
by_alias: bool = False,
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
) -> Dict[str, Any]:
) -> dict[str, Any]:
return model.model_dump(
include=include,
exclude=exclude,
@@ -210,16 +207,16 @@ if PYDANTIC_V2: # pragma: pydantic-v2
exclude_none=exclude_none,
)
def type_validate_python(type_: Type[T], data: Any) -> T:
def type_validate_python(type_: type[T], data: Any) -> T:
"""Validate data with given type."""
return TypeAdapter(type_).validate_python(data)
def type_validate_json(type_: Type[T], data: Union[str, bytes]) -> T:
def type_validate_json(type_: type[T], data: Union[str, bytes]) -> T:
"""Validate JSON with given type."""
return TypeAdapter(type_).validate_json(data)
def __get_pydantic_core_schema__(
cls: Type["_CustomValidationClass"],
cls: type["_CustomValidationClass"],
source_type: Any,
handler: GetCoreSchemaHandler,
) -> CoreSchema:
@@ -230,7 +227,7 @@ if PYDANTIC_V2: # pragma: pydantic-v2
[core_schema.no_info_plain_validator_function(func) for func in validators]
)
def custom_validation(class_: Type["CVC"]) -> Type["CVC"]:
def custom_validation(class_: type["CVC"]) -> type["CVC"]:
"""Use pydantic v1 like validator generator in pydantic v2"""
setattr(
@@ -308,7 +305,7 @@ else: # pragma: pydantic-v1
)
return cls._construct(name, annotation, field_info or FieldInfo())
def extract_field_info(field_info: BaseFieldInfo) -> Dict[str, Any]:
def extract_field_info(field_info: BaseFieldInfo) -> dict[str, Any]:
"""Get FieldInfo init kwargs from a FieldInfo instance."""
kwargs = {
@@ -318,7 +315,7 @@ else: # pragma: pydantic-v1
return kwargs
def model_field_validate(
model_field: ModelField, value: Any, config: Optional[Type[ConfigDict]] = None
model_field: ModelField, value: Any, config: Optional[type[ConfigDict]] = None
) -> Any:
"""Validate the value pass to the field.
@@ -333,7 +330,7 @@ else: # pragma: pydantic-v1
raise ValueError(value, model_field)
return v
def model_fields(model: Type[BaseModel]) -> List[ModelField]:
def model_fields(model: type[BaseModel]) -> list[ModelField]:
"""Get field list of a model."""
# construct the model field without preprocess to avoid error
@@ -348,19 +345,19 @@ else: # pragma: pydantic-v1
for model_field in model.__fields__.values()
]
def model_config(model: Type[BaseModel]) -> Any:
def model_config(model: type[BaseModel]) -> Any:
"""Get config of a model."""
return model.__config__
def model_dump(
model: BaseModel,
include: Optional[Set[str]] = None,
exclude: Optional[Set[str]] = None,
include: Optional[set[str]] = None,
exclude: Optional[set[str]] = None,
by_alias: bool = False,
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
) -> Dict[str, Any]:
) -> dict[str, Any]:
return model.dict(
include=include,
exclude=exclude,
@@ -370,14 +367,14 @@ else: # pragma: pydantic-v1
exclude_none=exclude_none,
)
def type_validate_python(type_: Type[T], data: Any) -> T:
def type_validate_python(type_: type[T], data: Any) -> T:
"""Validate data with given type."""
return parse_obj_as(type_, data)
def type_validate_json(type_: Type[T], data: Union[str, bytes]) -> T:
def type_validate_json(type_: type[T], data: Union[str, bytes]) -> T:
"""Validate JSON with given type."""
return parse_raw_as(type_, data)
def custom_validation(class_: Type["CVC"]) -> Type["CVC"]:
def custom_validation(class_: type["CVC"]) -> type["CVC"]:
"""Do nothing in pydantic v1"""
return class_

View File

@@ -17,19 +17,9 @@ import json
from pathlib import Path
from datetime import timedelta
from ipaddress import IPv4Address
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, Union, Optional
from typing_extensions import TypeAlias, get_args, get_origin
from typing import (
TYPE_CHECKING,
Any,
Set,
Dict,
List,
Type,
Tuple,
Union,
Mapping,
Optional,
)
from dotenv import dotenv_values
from pydantic import Field, BaseModel
@@ -49,7 +39,7 @@ from nonebot.compat import (
)
DOTENV_TYPE: TypeAlias = Union[
Path, str, List[Union[Path, str]], Tuple[Union[Path, str], ...]
Path, str, list[Union[Path, str]], tuple[Union[Path, str], ...]
]
ENV_FILE_SENTINEL = Path("")
@@ -59,7 +49,7 @@ class SettingsError(ValueError): ...
class BaseSettingsSource(abc.ABC):
def __init__(self, settings_cls: Type["BaseSettings"]) -> None:
def __init__(self, settings_cls: type["BaseSettings"]) -> None:
self.settings_cls = settings_cls
@property
@@ -67,7 +57,7 @@ class BaseSettingsSource(abc.ABC):
return model_config(self.settings_cls)
@abc.abstractmethod
def __call__(self) -> Dict[str, Any]:
def __call__(self) -> dict[str, Any]:
raise NotImplementedError
@@ -75,12 +65,12 @@ class InitSettingsSource(BaseSettingsSource):
__slots__ = ("init_kwargs",)
def __init__(
self, settings_cls: Type["BaseSettings"], init_kwargs: Dict[str, Any]
self, settings_cls: type["BaseSettings"], init_kwargs: dict[str, Any]
) -> None:
self.init_kwargs = init_kwargs
super().__init__(settings_cls)
def __call__(self) -> Dict[str, Any]:
def __call__(self) -> dict[str, Any]:
return self.init_kwargs
def __repr__(self) -> str:
@@ -90,7 +80,7 @@ class InitSettingsSource(BaseSettingsSource):
class DotEnvSettingsSource(BaseSettingsSource):
def __init__(
self,
settings_cls: Type["BaseSettings"],
settings_cls: type["BaseSettings"],
env_file: Optional[DOTENV_TYPE] = ENV_FILE_SENTINEL,
env_file_encoding: Optional[str] = None,
case_sensitive: Optional[bool] = None,
@@ -121,7 +111,7 @@ class DotEnvSettingsSource(BaseSettingsSource):
def _apply_case_sensitive(self, var_name: str) -> str:
return var_name if self.case_sensitive else var_name.lower()
def _field_is_complex(self, field: ModelField) -> Tuple[bool, bool]:
def _field_is_complex(self, field: ModelField) -> tuple[bool, bool]:
if type_is_complex(field.annotation):
return True, False
elif origin_is_union(get_origin(field.annotation)) and any(
@@ -132,16 +122,16 @@ class DotEnvSettingsSource(BaseSettingsSource):
def _parse_env_vars(
self, env_vars: Mapping[str, Optional[str]]
) -> Dict[str, Optional[str]]:
) -> dict[str, Optional[str]]:
return {
self._apply_case_sensitive(key): value for key, value in env_vars.items()
}
def _read_env_file(self, file_path: Path) -> Dict[str, Optional[str]]:
def _read_env_file(self, file_path: Path) -> dict[str, Optional[str]]:
file_vars = dotenv_values(file_path, encoding=self.env_file_encoding)
return self._parse_env_vars(file_vars)
def _read_env_files(self) -> Dict[str, Optional[str]]:
def _read_env_files(self) -> dict[str, Optional[str]]:
env_files = self.env_file
if env_files is None:
return {}
@@ -149,7 +139,7 @@ class DotEnvSettingsSource(BaseSettingsSource):
if isinstance(env_files, (str, os.PathLike)):
env_files = [env_files]
dotenv_vars: Dict[str, Optional[str]] = {}
dotenv_vars: dict[str, Optional[str]] = {}
for env_file in env_files:
env_path = Path(env_file).expanduser()
if env_path.is_file():
@@ -170,14 +160,14 @@ class DotEnvSettingsSource(BaseSettingsSource):
def _explode_env_vars(
self,
field: ModelField,
env_vars: Dict[str, Optional[str]],
env_file_vars: Dict[str, Optional[str]],
) -> Dict[str, Any]:
env_vars: dict[str, Optional[str]],
env_file_vars: dict[str, Optional[str]],
) -> dict[str, Any]:
if self.env_nested_delimiter is None:
return {}
prefix = f"{field.name}{self.env_nested_delimiter}"
result: Dict[str, Any] = {}
result: dict[str, Any] = {}
for env_name, env_val in env_vars.items():
if not env_name.startswith(prefix):
continue
@@ -209,10 +199,10 @@ class DotEnvSettingsSource(BaseSettingsSource):
return result
def __call__(self) -> Dict[str, Any]:
def __call__(self) -> dict[str, Any]:
"""从环境变量和 dotenv 配置文件中读取配置项。"""
d: Dict[str, Any] = {}
d: dict[str, Any] = {}
env_vars = self._parse_env_vars(os.environ)
env_file_vars = self._read_env_files()
@@ -317,7 +307,7 @@ class BaseSettings(BaseModel):
return self.__dict__.get(name)
if PYDANTIC_V2: # pragma: pydantic-v2
model_config: SettingsConfig = SettingsConfig(
model_config = SettingsConfig(
extra="allow",
env_file=".env",
env_file_encoding="utf-8",
@@ -351,11 +341,11 @@ class BaseSettings(BaseModel):
def _settings_build_values(
self,
init_kwargs: Dict[str, Any],
init_kwargs: dict[str, Any],
env_file: Optional[DOTENV_TYPE] = None,
env_file_encoding: Optional[str] = None,
env_nested_delimiter: Optional[str] = None,
) -> Dict[str, Any]:
) -> dict[str, Any]:
init_settings = InitSettingsSource(self.__class__, init_kwargs=init_kwargs)
env_settings = DotEnvSettingsSource(
self.__class__,
@@ -426,7 +416,7 @@ class Config(BaseSettings):
"""API 请求超时时间,单位: 秒。"""
# bot runtime configs
superusers: Set[str] = set()
superusers: set[str] = set()
"""机器人超级用户。
用法:
@@ -434,9 +424,9 @@ class Config(BaseSettings):
SUPERUSERS=["12345789"]
```
"""
nickname: Set[str] = set()
nickname: set[str] = set()
"""机器人昵称。"""
command_start: Set[str] = {"/"}
command_start: set[str] = {"/"}
"""命令的起始标记,用于判断一条消息是不是命令。
参考[命令响应规则](https://nonebot.dev/docs/advanced/matcher#command)。
@@ -446,7 +436,7 @@ class Config(BaseSettings):
COMMAND_START=["/", ""]
```
"""
command_sep: Set[str] = {"."}
command_sep: set[str] = {"."}
"""命令的分隔标记,用于将文本形式的命令切分为元组(实际的命令名)。
参考[命令响应规则](https://nonebot.dev/docs/advanced/matcher#command)。
@@ -477,7 +467,9 @@ class Config(BaseSettings):
model_config = SettingsConfig(env_file=(".env", ".env.prod"))
else: # pragma: pydantic-v1
class Config(SettingsConfig):
class Config( # pyright: ignore[reportIncompatibleVariableOverride]
SettingsConfig
):
env_file = ".env", ".env.prod"

View File

@@ -9,20 +9,8 @@ import abc
import asyncio
import inspect
from dataclasses import field, dataclass
from typing import (
Any,
Dict,
List,
Type,
Tuple,
Generic,
TypeVar,
Callable,
Iterable,
Optional,
Awaitable,
cast,
)
from collections.abc import Iterable, Awaitable
from typing import Any, Generic, TypeVar, Callable, Optional, cast
from nonebot.log import logger
from nonebot.typing import _DependentCallable
@@ -48,13 +36,13 @@ class Param(abc.ABC, FieldInfo):
@classmethod
def _check_param(
cls, param: inspect.Parameter, allow_types: Tuple[Type["Param"], ...]
cls, param: inspect.Parameter, allow_types: tuple[type["Param"], ...]
) -> Optional["Param"]:
return
@classmethod
def _check_parameterless(
cls, value: Any, allow_types: Tuple[Type["Param"], ...]
cls, value: Any, allow_types: tuple[type["Param"], ...]
) -> Optional["Param"]:
return
@@ -79,8 +67,8 @@ class Dependent(Generic[R]):
"""
call: _DependentCallable[R]
params: Tuple[ModelField, ...] = field(default_factory=tuple)
parameterless: Tuple[Param, ...] = field(default_factory=tuple)
params: tuple[ModelField, ...] = field(default_factory=tuple)
parameterless: tuple[Param, ...] = field(default_factory=tuple)
def __repr__(self) -> str:
if inspect.isfunction(self.call) or inspect.isclass(self.call):
@@ -112,9 +100,9 @@ class Dependent(Generic[R]):
@staticmethod
def parse_params(
call: _DependentCallable[R], allow_types: Tuple[Type[Param], ...]
) -> Tuple[ModelField, ...]:
fields: List[ModelField] = []
call: _DependentCallable[R], allow_types: tuple[type[Param], ...]
) -> tuple[ModelField, ...]:
fields: list[ModelField] = []
params = get_typed_signature(call).parameters.values()
for param in params:
@@ -144,9 +132,9 @@ class Dependent(Generic[R]):
@staticmethod
def parse_parameterless(
parameterless: Tuple[Any, ...], allow_types: Tuple[Type[Param], ...]
) -> Tuple[Param, ...]:
parameterless_params: List[Param] = []
parameterless: tuple[Any, ...], allow_types: tuple[type[Param], ...]
) -> tuple[Param, ...]:
parameterless_params: list[Param] = []
for value in parameterless:
for allow_type in allow_types:
if param := allow_type._check_parameterless(value, allow_types):
@@ -162,7 +150,7 @@ class Dependent(Generic[R]):
*,
call: _DependentCallable[R],
parameterless: Optional[Iterable[Any]] = None,
allow_types: Iterable[Type[Param]],
allow_types: Iterable[type[Param]],
) -> "Dependent[R]":
allow_types = tuple(allow_types)
@@ -181,7 +169,7 @@ class Dependent(Generic[R]):
*(cast(Param, param.field_info)._check(**params) for param in self.params)
)
async def _solve_field(self, field: ModelField, params: Dict[str, Any]) -> Any:
async def _solve_field(self, field: ModelField, params: dict[str, Any]) -> Any:
param = cast(Param, field.field_info)
value = await param._solve(**params)
if value is PydanticUndefined:
@@ -189,7 +177,7 @@ class Dependent(Generic[R]):
v = check_field_type(field, value)
return v if param.validate else value
async def solve(self, **params: Any) -> Dict[str, Any]:
async def solve(self, **params: Any) -> dict[str, Any]:
# solve parameterless
for param in self.parameterless:
await param._solve(**params)

View File

@@ -5,7 +5,7 @@ FrontMatter:
"""
import inspect
from typing import Any, Dict, Callable, ForwardRef
from typing import Any, Callable, ForwardRef
from loguru import logger
@@ -31,7 +31,7 @@ def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
return inspect.Signature(typed_params)
def get_typed_annotation(param: inspect.Parameter, globalns: Dict[str, Any]) -> Any:
def get_typed_annotation(param: inspect.Parameter, globalns: dict[str, Any]) -> Any:
"""获取参数的类型注解"""
annotation = param.annotation

View File

@@ -23,6 +23,7 @@ from nonebot.internal.driver import ReverseDriver as ReverseDriver
from nonebot.internal.driver import combine_driver as combine_driver
from nonebot.internal.driver import HTTPClientMixin as HTTPClientMixin
from nonebot.internal.driver import HTTPServerSetup as HTTPServerSetup
from nonebot.internal.driver import HTTPClientSession as HTTPClientSession
from nonebot.internal.driver import WebSocketClientMixin as WebSocketClientMixin
from nonebot.internal.driver import WebSocketServerSetup as WebSocketServerSetup

View File

@@ -16,16 +16,21 @@ FrontMatter:
"""
from typing_extensions import override
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager
from typing import TYPE_CHECKING, AsyncGenerator
from typing import TYPE_CHECKING, Union, Optional
from multidict import CIMultiDict
from nonebot.drivers import Request, Response
from nonebot.exception import WebSocketClosed
from nonebot.drivers import URL, Request, Response
from nonebot.drivers.none import Driver as NoneDriver
from nonebot.drivers import WebSocket as BaseWebSocket
from nonebot.internal.driver import Cookies, QueryTypes, CookieTypes, HeaderTypes
from nonebot.drivers import (
HTTPVersion,
HTTPClientMixin,
HTTPClientSession,
WebSocketClientMixin,
combine_driver,
)
@@ -39,6 +44,107 @@ except ModuleNotFoundError as e: # pragma: no cover
) from e
class Session(HTTPClientSession):
@override
def __init__(
self,
params: QueryTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
version: Union[str, HTTPVersion] = HTTPVersion.H11,
timeout: Optional[float] = None,
proxy: Optional[str] = None,
):
self._client: Optional[aiohttp.ClientSession] = None
self._params = URL.build(query=params).query if params is not None else None
self._headers = CIMultiDict(headers) if headers is not None else None
self._cookies = tuple(
(cookie.name, cookie.value)
for cookie in Cookies(cookies)
if cookie.value is not None
)
version = HTTPVersion(version)
if version == HTTPVersion.H10:
self._version = aiohttp.HttpVersion10
elif version == HTTPVersion.H11:
self._version = aiohttp.HttpVersion11
else:
raise RuntimeError(f"Unsupported HTTP version: {version}")
self._timeout = timeout
self._proxy = proxy
@property
def client(self) -> aiohttp.ClientSession:
if self._client is None:
raise RuntimeError("Session is not initialized")
return self._client
@override
async def request(self, setup: Request) -> Response:
if self._params:
params = self._params.copy()
params.update(setup.url.query)
url = setup.url.with_query(params)
else:
url = setup.url
data = setup.data
if setup.files:
data = aiohttp.FormData(data or {}, quote_fields=False)
for name, file in setup.files:
data.add_field(name, file[1], content_type=file[2], filename=file[0])
cookies = (
(cookie.name, cookie.value)
for cookie in setup.cookies
if cookie.value is not None
)
timeout = aiohttp.ClientTimeout(setup.timeout)
async with await self.client.request(
setup.method,
url,
data=setup.content or data,
json=setup.json,
cookies=cookies,
headers=setup.headers,
proxy=setup.proxy or self._proxy,
timeout=timeout,
) as response:
return Response(
response.status,
headers=response.headers.copy(),
content=await response.read(),
request=setup,
)
@override
async def setup(self) -> None:
if self._client is not None:
raise RuntimeError("Session has already been initialized")
self._client = aiohttp.ClientSession(
cookies=self._cookies,
headers=self._headers,
version=self._version,
timeout=self._timeout,
trust_env=True,
)
await self._client.__aenter__()
@override
async def close(self) -> None:
try:
if self._client is not None:
await self._client.close()
finally:
self._client = None
class Mixin(HTTPClientMixin, WebSocketClientMixin):
"""AIOHTTP Mixin"""
@@ -49,42 +155,8 @@ class Mixin(HTTPClientMixin, WebSocketClientMixin):
@override
async def request(self, setup: Request) -> Response:
if setup.version == HTTPVersion.H10:
version = aiohttp.HttpVersion10
elif setup.version == HTTPVersion.H11:
version = aiohttp.HttpVersion11
else:
raise RuntimeError(f"Unsupported HTTP version: {setup.version}")
timeout = aiohttp.ClientTimeout(setup.timeout)
data = setup.data
if setup.files:
data = aiohttp.FormData(data or {})
for name, file in setup.files:
data.add_field(name, file[1], content_type=file[2], filename=file[0])
cookies = {
cookie.name: cookie.value for cookie in setup.cookies if cookie.value
}
async with aiohttp.ClientSession(
cookies=cookies, version=version, trust_env=True
) as session:
async with session.request(
setup.method,
setup.url,
data=setup.content or data,
json=setup.json,
headers=setup.headers,
timeout=timeout,
proxy=setup.proxy,
) as response:
return Response(
response.status,
headers=response.headers.copy(),
content=await response.read(),
request=setup,
)
async with self.get_session() as session:
return await session.request(setup)
@override
@asynccontextmanager
@@ -106,6 +178,25 @@ class Mixin(HTTPClientMixin, WebSocketClientMixin):
) as ws:
yield WebSocket(request=setup, session=session, websocket=ws)
@override
def get_session(
self,
params: QueryTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
version: Union[str, HTTPVersion] = HTTPVersion.H11,
timeout: Optional[float] = None,
proxy: Optional[str] = None,
) -> Session:
return Session(
params=params,
headers=headers,
cookies=cookies,
version=version,
timeout=timeout,
proxy=proxy,
)
class WebSocket(BaseWebSocket):
"""AIOHTTP Websocket Wrapper"""
@@ -131,8 +222,8 @@ class WebSocket(BaseWebSocket):
raise NotImplementedError
@override
async def close(self, code: int = 1000):
await self.websocket.close(code=code)
async def close(self, code: int = 1000, reason: str = ""):
await self.websocket.close(code=code, message=reason.encode("utf-8"))
await self.session.close()
async def _receive(self) -> aiohttp.WSMessage:

View File

@@ -19,7 +19,7 @@ import logging
import contextlib
from functools import wraps
from typing_extensions import override
from typing import Any, Dict, List, Tuple, Union, Optional
from typing import Any, Union, Optional
from pydantic import BaseModel
@@ -72,15 +72,15 @@ class Config(BaseModel):
"""是否包含适配器路由的 schema默认为 `True`"""
fastapi_reload: bool = False
"""开启/关闭冷重载"""
fastapi_reload_dirs: Optional[List[str]] = None
fastapi_reload_dirs: Optional[list[str]] = None
"""重载监控文件夹列表,默认为 uvicorn 默认值"""
fastapi_reload_delay: float = 0.25
"""重载延迟,默认为 uvicorn 默认值"""
fastapi_reload_includes: Optional[List[str]] = None
fastapi_reload_includes: Optional[list[str]] = None
"""要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值"""
fastapi_reload_excludes: Optional[List[str]] = None
fastapi_reload_excludes: Optional[list[str]] = None
"""不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值"""
fastapi_extra: Dict[str, Any] = {}
fastapi_extra: dict[str, Any] = {}
"""传递给 `FastAPI` 的其他参数。"""
@@ -161,7 +161,7 @@ class Driver(BaseDriver, ASGIMixin):
self,
host: Optional[str] = None,
port: Optional[int] = None,
*,
*args,
app: Optional[str] = None,
**kwargs,
):
@@ -206,7 +206,7 @@ class Driver(BaseDriver, ASGIMixin):
json = await request.json()
data: Optional[dict] = None
files: Optional[List[Tuple[str, FileTypes]]] = None
files: Optional[list[tuple[str, FileTypes]]] = None
with contextlib.suppress(Exception):
form = await request.form()
data = {}

View File

@@ -15,15 +15,20 @@ FrontMatter:
description: nonebot.drivers.httpx 模块
"""
from typing import TYPE_CHECKING
from typing_extensions import override
from typing import TYPE_CHECKING, Union, Optional
from multidict import CIMultiDict
from nonebot.drivers.none import Driver as NoneDriver
from nonebot.internal.driver import Cookies, QueryTypes, CookieTypes, HeaderTypes
from nonebot.drivers import (
URL,
Request,
Response,
HTTPVersion,
HTTPClientMixin,
HTTPClientSession,
combine_driver,
)
@@ -36,6 +41,79 @@ except ModuleNotFoundError as e: # pragma: no cover
) from e
class Session(HTTPClientSession):
@override
def __init__(
self,
params: QueryTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
version: Union[str, HTTPVersion] = HTTPVersion.H11,
timeout: Optional[float] = None,
proxy: Optional[str] = None,
):
self._client: Optional[httpx.AsyncClient] = None
self._params = (
tuple(URL.build(query=params).query.items()) if params is not None else None
)
self._headers = (
tuple(CIMultiDict(headers).items()) if headers is not None else None
)
self._cookies = Cookies(cookies)
self._version = HTTPVersion(version)
self._timeout = timeout
self._proxy = proxy
@property
def client(self) -> httpx.AsyncClient:
if self._client is None:
raise RuntimeError("Session is not initialized")
return self._client
@override
async def request(self, setup: Request) -> Response:
response = await self.client.request(
setup.method,
str(setup.url),
content=setup.content,
data=setup.data,
files=setup.files,
json=setup.json,
headers=tuple(setup.headers.items()),
cookies=setup.cookies.jar,
timeout=setup.timeout,
)
return Response(
response.status_code,
headers=response.headers.multi_items(),
content=response.content,
request=setup,
)
@override
async def setup(self) -> None:
if self._client is not None:
raise RuntimeError("Session has already been initialized")
self._client = httpx.AsyncClient(
params=self._params,
headers=self._headers,
cookies=self._cookies.jar,
http2=self._version == HTTPVersion.H2,
proxies=self._proxy,
follow_redirects=True,
)
await self._client.__aenter__()
@override
async def close(self) -> None:
try:
if self._client is not None:
await self._client.aclose()
finally:
self._client = None
class Mixin(HTTPClientMixin):
"""HTTPX Mixin"""
@@ -46,27 +124,28 @@ class Mixin(HTTPClientMixin):
@override
async def request(self, setup: Request) -> Response:
async with httpx.AsyncClient(
cookies=setup.cookies.jar,
http2=setup.version == HTTPVersion.H2,
proxies=setup.proxy,
follow_redirects=True,
) as client:
response = await client.request(
setup.method,
str(setup.url),
content=setup.content,
data=setup.data,
json=setup.json,
files=setup.files,
headers=tuple(setup.headers.items()),
timeout=setup.timeout,
)
return Response(
response.status_code,
headers=response.headers.multi_items(),
content=response.content,
request=setup,
async with self.get_session(
version=setup.version, proxy=setup.proxy
) as session:
return await session.request(setup)
@override
def get_session(
self,
params: QueryTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
version: Union[str, HTTPVersion] = HTTPVersion.H11,
timeout: Optional[float] = None,
proxy: Optional[str] = None,
) -> Session:
return Session(
params=params,
headers=headers,
cookies=cookies,
version=version,
timeout=timeout,
proxy=proxy,
)

View File

@@ -68,9 +68,11 @@ class Driver(BaseDriver):
await self._lifespan.startup()
except Exception as e:
logger.opt(colors=True, exception=e).error(
"<r><bg #f8bbd0>Error when running startup function. "
"Ignored!</bg #f8bbd0></r>"
"<r><bg #f8bbd0>Application startup failed. "
"Exiting.</bg #f8bbd0></r>"
)
self.should_exit.set()
return
logger.info("Application startup completed.")

View File

@@ -18,7 +18,7 @@ FrontMatter:
import asyncio
from functools import wraps
from typing_extensions import override
from typing import Any, Dict, List, Tuple, Union, Optional, cast
from typing import Any, Union, Optional, cast
from pydantic import BaseModel
@@ -64,15 +64,15 @@ class Config(BaseModel):
quart_reload: bool = False
"""开启/关闭冷重载"""
quart_reload_dirs: Optional[List[str]] = None
quart_reload_dirs: Optional[list[str]] = None
"""重载监控文件夹列表,默认为 uvicorn 默认值"""
quart_reload_delay: float = 0.25
"""重载延迟,默认为 uvicorn 默认值"""
quart_reload_includes: Optional[List[str]] = None
quart_reload_includes: Optional[list[str]] = None
"""要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值"""
quart_reload_excludes: Optional[List[str]] = None
quart_reload_excludes: Optional[list[str]] = None
"""不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值"""
quart_extra: Dict[str, Any] = {}
quart_extra: dict[str, Any] = {}
"""传递给 `Quart` 的其他参数。"""
@@ -142,7 +142,7 @@ class Driver(BaseDriver, ASGIMixin):
self,
host: Optional[str] = None,
port: Optional[int] = None,
*,
*args,
app: Optional[str] = None,
**kwargs,
):
@@ -184,7 +184,7 @@ class Driver(BaseDriver, ASGIMixin):
data = await request.form
files_dict = await request.files
files: List[Tuple[str, FileTypes]] = []
files: list[tuple[str, FileTypes]] = []
key: str
value: FileStorage
for key, value in files_dict.items():

View File

@@ -19,7 +19,8 @@ import logging
from functools import wraps
from contextlib import asynccontextmanager
from typing_extensions import ParamSpec, override
from typing import TYPE_CHECKING, Union, TypeVar, Callable, Awaitable, AsyncGenerator
from collections.abc import Coroutine, AsyncGenerator
from typing import TYPE_CHECKING, Any, Union, TypeVar, Callable
from nonebot.drivers import Request
from nonebot.log import LoguruHandler
@@ -44,7 +45,9 @@ logger = logging.Logger("websockets.client", "INFO")
logger.addHandler(LoguruHandler())
def catch_closed(func: Callable[P, Awaitable[T]]) -> Callable[P, Awaitable[T]]:
def catch_closed(
func: Callable[P, Coroutine[Any, Any, T]]
) -> Callable[P, Coroutine[Any, Any, T]]:
@wraps(func)
async def decorator(*args: P.args, **kwargs: P.kwargs) -> T:
try:

View File

@@ -1,6 +1,7 @@
import abc
from typing import Any
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager
from typing import Any, Dict, AsyncGenerator
from nonebot.config import Config
from nonebot.internal.driver._lifespan import LIFESPAN_FUNC
@@ -32,7 +33,7 @@ class Adapter(abc.ABC):
def __init__(self, driver: Driver, **kwargs: Any):
self.driver: Driver = driver
"""{ref}`nonebot.drivers.Driver` 实例"""
self.bots: Dict[str, Bot] = {}
self.bots: dict[str, Bot] = {}
"""本协议适配器已建立连接的 {ref}`nonebot.adapters.Bot` 实例"""
def __repr__(self) -> str:

View File

@@ -1,7 +1,7 @@
import abc
import asyncio
from functools import partial
from typing import TYPE_CHECKING, Any, Set, Union, Optional, Protocol
from typing import TYPE_CHECKING, Any, Union, ClassVar, Optional, Protocol
from nonebot.log import logger
from nonebot.config import Config
@@ -27,9 +27,9 @@ class Bot(abc.ABC):
self_id: 机器人 ID
"""
_calling_api_hook: Set[T_CallingAPIHook] = set()
_calling_api_hook: ClassVar[set[T_CallingAPIHook]] = set()
"""call_api 时执行的函数"""
_called_api_hook: Set[T_CalledAPIHook] = set()
_called_api_hook: ClassVar[set[T_CalledAPIHook]] = set()
"""call_api 后执行的函数"""
def __init__(self, adapter: "Adapter", self_id: str):

View File

@@ -1,5 +1,5 @@
import abc
from typing import Any, Type, TypeVar
from typing import Any, TypeVar
from pydantic import BaseModel
@@ -20,12 +20,12 @@ class Event(abc.ABC, BaseModel):
class Config(ConfigDict):
extra = "allow" # type: ignore
json_encoders = {Message: DataclassEncoder}
json_encoders = {Message: DataclassEncoder} # noqa: RUF012
if not PYDANTIC_V2: # pragma: pydantic-v1
@classmethod
def validate(cls: Type["E"], value: Any) -> "E":
def validate(cls: type["E"], value: Any) -> "E":
if isinstance(value, Event) and not isinstance(value, cls):
raise TypeError(f"{value} is incompatible with Event type {cls}")
return super().validate(value)

View File

@@ -1,17 +1,14 @@
import abc
from copy import deepcopy
from typing_extensions import Self
from collections.abc import Iterable
from dataclasses import field, asdict, dataclass
from typing import (
from typing import ( # noqa: UP035
Any,
Dict,
List,
Type,
Tuple,
Union,
Generic,
TypeVar,
Iterable,
Optional,
SupportsIndex,
overload,
@@ -32,12 +29,12 @@ class MessageSegment(abc.ABC, Generic[TM]):
type: str
"""消息段类型"""
data: Dict[str, Any] = field(default_factory=dict)
data: dict[str, Any] = field(default_factory=dict)
"""消息段数据"""
@classmethod
@abc.abstractmethod
def get_message_class(cls) -> Type[TM]:
def get_message_class(cls) -> Type[TM]: # noqa: UP006
"""获取消息数组类型"""
raise NotImplementedError
@@ -49,7 +46,9 @@ class MessageSegment(abc.ABC, Generic[TM]):
def __len__(self) -> int:
return len(str(self))
def __ne__(self, other: Self) -> bool:
def __ne__( # pyright: ignore[reportIncompatibleMethodOverride]
self, other: Self
) -> bool:
return not self == other
def __add__(self: TMS, other: Union[str, TMS, Iterable[TMS]]) -> TM:
@@ -101,7 +100,7 @@ class MessageSegment(abc.ABC, Generic[TM]):
@custom_validation
class Message(List[TMS], abc.ABC):
class Message(list[TMS], abc.ABC):
"""消息序列
参数:
@@ -142,7 +141,7 @@ class Message(List[TMS], abc.ABC):
@classmethod
@abc.abstractmethod
def get_segment_class(cls) -> Type[TMS]:
def get_segment_class(cls) -> type[TMS]:
"""获取消息段类型"""
raise NotImplementedError
@@ -177,7 +176,9 @@ class Message(List[TMS], abc.ABC):
"""构造消息数组"""
raise NotImplementedError
def __add__(self, other: Union[str, TMS, Iterable[TMS]]) -> Self:
def __add__( # pyright: ignore[reportIncompatibleMethodOverride]
self, other: Union[str, TMS, Iterable[TMS]]
) -> Self:
result = self.copy()
result += other
return result
@@ -209,7 +210,7 @@ class Message(List[TMS], abc.ABC):
"""
@overload
def __getitem__(self, args: Tuple[str, int]) -> TMS:
def __getitem__(self, args: tuple[str, int]) -> TMS:
"""索引指定类型的消息段
参数:
@@ -220,7 +221,7 @@ class Message(List[TMS], abc.ABC):
"""
@overload
def __getitem__(self, args: Tuple[str, slice]) -> Self:
def __getitem__(self, args: tuple[str, slice]) -> Self:
"""切片指定类型的消息段
参数:
@@ -252,12 +253,12 @@ class Message(List[TMS], abc.ABC):
消息切片 `args`
"""
def __getitem__(
def __getitem__( # pyright: ignore[reportIncompatibleMethodOverride]
self,
args: Union[
str,
Tuple[str, int],
Tuple[str, slice],
tuple[str, int],
tuple[str, slice],
int,
slice,
],
@@ -276,7 +277,9 @@ class Message(List[TMS], abc.ABC):
else:
raise ValueError("Incorrect arguments to slice") # pragma: no cover
def __contains__(self, value: Union[TMS, str]) -> bool:
def __contains__( # pyright: ignore[reportIncompatibleMethodOverride]
self, value: Union[TMS, str]
) -> bool:
"""检查消息段是否存在
参数:
@@ -359,7 +362,9 @@ class Message(List[TMS], abc.ABC):
return all(seg.type == value for seg in self)
return all(seg == value for seg in self)
def append(self, obj: Union[str, TMS]) -> Self:
def append( # pyright: ignore[reportIncompatibleMethodOverride]
self, obj: Union[str, TMS]
) -> Self:
"""添加一个消息段到消息数组末尾。
参数:
@@ -373,7 +378,9 @@ class Message(List[TMS], abc.ABC):
raise ValueError(f"Unexpected type: {type(obj)} {obj}") # pragma: no cover
return self
def extend(self, obj: Union[Self, Iterable[TMS]]) -> Self:
def extend( # pyright: ignore[reportIncompatibleMethodOverride]
self, obj: Union[Self, Iterable[TMS]]
) -> Self:
"""拼接一个消息数组或多个消息段到消息数组末尾。
参数:

View File

@@ -1,21 +1,15 @@
import functools
from string import Formatter
from typing_extensions import TypeAlias
from collections.abc import Mapping, Sequence
from typing import (
TYPE_CHECKING,
Any,
Set,
Dict,
List,
Type,
Tuple,
Union,
Generic,
Mapping,
TypeVar,
Callable,
Optional,
Sequence,
cast,
overload,
)
@@ -25,9 +19,9 @@ from _string import formatter_field_name_split # type: ignore
if TYPE_CHECKING:
from .message import Message, MessageSegment
def formatter_field_name_split( # noqa: F811
def formatter_field_name_split(
field_name: str,
) -> Tuple[str, List[Tuple[bool, str]]]: ...
) -> tuple[str, list[tuple[bool, str]]]: ...
TM = TypeVar("TM", bound="Message")
@@ -50,7 +44,7 @@ class MessageTemplate(Formatter, Generic[TF]):
def __init__(
self: "MessageTemplate[str]",
template: str,
factory: Type[str] = str,
factory: type[str] = str,
private_getattr: bool = False,
) -> None: ...
@@ -58,19 +52,19 @@ class MessageTemplate(Formatter, Generic[TF]):
def __init__(
self: "MessageTemplate[TM]",
template: Union[str, TM],
factory: Type[TM],
factory: type[TM],
private_getattr: bool = False,
) -> None: ...
def __init__(
self,
template: Union[str, TM],
factory: Union[Type[str], Type[TM]] = str,
factory: Union[type[str], type[TM]] = str,
private_getattr: bool = False,
) -> None:
self.template: TF = template # type: ignore
self.factory: Type[TF] = factory # type: ignore
self.format_specs: Dict[str, FormatSpecFunc] = {}
self.factory: type[TF] = factory # type: ignore
self.format_specs: dict[str, FormatSpecFunc] = {}
self.private_getattr = private_getattr
def __repr__(self) -> str:
@@ -85,7 +79,9 @@ class MessageTemplate(Formatter, Generic[TF]):
self.format_specs[name] = spec
return spec
def format(self, *args, **kwargs):
def format( # pyright: ignore[reportIncompatibleMethodOverride]
self, *args, **kwargs
) -> TF:
"""根据传入参数和模板生成消息对象"""
return self._format(args, kwargs)
@@ -118,7 +114,7 @@ class MessageTemplate(Formatter, Generic[TF]):
self.check_unused_args(used_args, args, kwargs)
return cast(TF, full_message)
def vformat(
def vformat( # pyright: ignore[reportIncompatibleMethodOverride]
self,
format_string: str,
args: Sequence[Any],
@@ -126,15 +122,15 @@ class MessageTemplate(Formatter, Generic[TF]):
) -> TF:
raise NotImplementedError("`vformat` has merged into `_format`")
def _vformat(
def _vformat( # pyright: ignore[reportIncompatibleMethodOverride]
self,
format_string: str,
args: Sequence[Any],
kwargs: Mapping[str, Any],
used_args: Set[Union[int, str]],
used_args: set[Union[int, str]],
auto_arg_index: int = 0,
) -> Tuple[TF, int]:
results: List[Any] = [self.factory()]
) -> tuple[TF, int]:
results: list[Any] = [self.factory()]
for literal_text, field_name, format_spec, conversion in self.parse(
format_string
@@ -185,7 +181,7 @@ class MessageTemplate(Formatter, Generic[TF]):
def get_field(
self, field_name: str, args: Sequence[Any], kwargs: Mapping[str, Any]
) -> Tuple[Any, Union[int, str]]:
) -> tuple[Any, Union[int, str]]:
first, rest = formatter_field_name_split(field_name)
obj = self.get_value(first, args, kwargs)
@@ -199,7 +195,7 @@ class MessageTemplate(Formatter, Generic[TF]):
def format_field(self, value: Any, format_spec: str) -> Any:
formatter: Optional[FormatSpecFunc] = self.format_specs.get(format_spec)
if formatter is None and not issubclass(self.factory, str):
segment_class: Type["MessageSegment"] = self.factory.get_segment_class()
segment_class: type["MessageSegment"] = self.factory.get_segment_class()
method = getattr(segment_class, format_spec, None)
if callable(method) and not cast(str, method.__name__).startswith("_"):
formatter = getattr(segment_class, format_spec)

View File

@@ -26,5 +26,6 @@ from .abstract import ReverseDriver as ReverseDriver
from .combine import combine_driver as combine_driver
from .model import HTTPServerSetup as HTTPServerSetup
from .abstract import HTTPClientMixin as HTTPClientMixin
from .abstract import HTTPClientSession as HTTPClientSession
from .model import WebSocketServerSetup as WebSocketServerSetup
from .abstract import WebSocketClientMixin as WebSocketClientMixin

View File

@@ -1,5 +1,6 @@
from collections.abc import Awaitable
from typing_extensions import TypeAlias
from typing import Any, List, Union, Callable, Awaitable, cast
from typing import Any, Union, Callable, cast
from nonebot.utils import run_sync, is_coroutine_callable
@@ -10,9 +11,9 @@ LIFESPAN_FUNC: TypeAlias = Union[SYNC_LIFESPAN_FUNC, ASYNC_LIFESPAN_FUNC]
class Lifespan:
def __init__(self) -> None:
self._startup_funcs: List[LIFESPAN_FUNC] = []
self._ready_funcs: List[LIFESPAN_FUNC] = []
self._shutdown_funcs: List[LIFESPAN_FUNC] = []
self._startup_funcs: list[LIFESPAN_FUNC] = []
self._ready_funcs: list[LIFESPAN_FUNC] = []
self._shutdown_funcs: list[LIFESPAN_FUNC] = []
def on_startup(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
self._startup_funcs.append(func)
@@ -28,7 +29,7 @@ class Lifespan:
@staticmethod
async def _run_lifespan_func(
funcs: List[LIFESPAN_FUNC],
funcs: list[LIFESPAN_FUNC],
) -> None:
for func in funcs:
if is_coroutine_callable(func):

View File

@@ -1,8 +1,10 @@
import abc
import asyncio
from typing_extensions import TypeAlias
from types import TracebackType
from collections.abc import AsyncGenerator
from typing_extensions import Self, TypeAlias
from contextlib import AsyncExitStack, asynccontextmanager
from typing import TYPE_CHECKING, Any, Set, Dict, Type, AsyncGenerator
from typing import TYPE_CHECKING, Any, Union, ClassVar, Optional
from nonebot.log import logger
from nonebot.config import Env, Config
@@ -17,7 +19,17 @@ from nonebot.typing import (
)
from ._lifespan import LIFESPAN_FUNC, Lifespan
from .model import Request, Response, WebSocket, HTTPServerSetup, WebSocketServerSetup
from .model import (
Request,
Response,
WebSocket,
QueryTypes,
CookieTypes,
HeaderTypes,
HTTPVersion,
HTTPServerSetup,
WebSocketServerSetup,
)
if TYPE_CHECKING:
from nonebot.internal.adapter import Bot, Adapter
@@ -36,11 +48,11 @@ class Driver(abc.ABC):
config: 包含配置信息的 Config 对象
"""
_adapters: Dict[str, "Adapter"] = {}
_adapters: ClassVar[dict[str, "Adapter"]] = {}
"""已注册的适配器列表"""
_bot_connection_hook: Set[Dependent[Any]] = set()
_bot_connection_hook: ClassVar[set[Dependent[Any]]] = set()
"""Bot 连接建立时执行的函数"""
_bot_disconnection_hook: Set[Dependent[Any]] = set()
_bot_disconnection_hook: ClassVar[set[Dependent[Any]]] = set()
"""Bot 连接断开时执行的函数"""
def __init__(self, env: Env, config: Config):
@@ -48,8 +60,8 @@ class Driver(abc.ABC):
"""环境名称"""
self.config: Config = config
"""全局配置对象"""
self._bots: Dict[str, "Bot"] = {}
self._bot_tasks: Set[asyncio.Task] = set()
self._bots: dict[str, "Bot"] = {}
self._bot_tasks: set[asyncio.Task] = set()
self._lifespan = Lifespan()
def __repr__(self) -> str:
@@ -59,11 +71,11 @@ class Driver(abc.ABC):
)
@property
def bots(self) -> Dict[str, "Bot"]:
def bots(self) -> dict[str, "Bot"]:
"""获取当前所有已连接的 Bot"""
return self._bots
def register_adapter(self, adapter: Type["Adapter"], **kwargs) -> None:
def register_adapter(self, adapter: type["Adapter"], **kwargs) -> None:
"""注册一个协议适配器
参数:
@@ -222,6 +234,49 @@ class ReverseMixin(Mixin):
"""服务端混入基类。"""
class HTTPClientSession(abc.ABC):
"""HTTP 客户端会话基类。"""
@abc.abstractmethod
def __init__(
self,
params: QueryTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
version: Union[str, HTTPVersion] = HTTPVersion.H11,
timeout: Optional[float] = None,
proxy: Optional[str] = None,
):
raise NotImplementedError
@abc.abstractmethod
async def request(self, setup: Request) -> Response:
"""发送一个 HTTP 请求"""
raise NotImplementedError
@abc.abstractmethod
async def setup(self) -> None:
"""初始化会话"""
raise NotImplementedError
@abc.abstractmethod
async def close(self) -> None:
"""关闭会话"""
raise NotImplementedError
async def __aenter__(self) -> Self:
await self.setup()
return self
async def __aexit__(
self,
exc_type: Optional[type[BaseException]],
exc: Optional[BaseException],
tb: Optional[TracebackType],
) -> None:
await self.close()
class HTTPClientMixin(ForwardMixin):
"""HTTP 客户端混入基类。"""
@@ -230,6 +285,19 @@ class HTTPClientMixin(ForwardMixin):
"""发送一个 HTTP 请求"""
raise NotImplementedError
@abc.abstractmethod
def get_session(
self,
params: QueryTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
version: Union[str, HTTPVersion] = HTTPVersion.H11,
timeout: Optional[float] = None,
proxy: Optional[str] = None,
) -> HTTPClientSession:
"""获取一个 HTTP 会话"""
raise NotImplementedError
class WebSocketClientMixin(ForwardMixin):
"""WebSocket 客户端混入基类。"""

View File

@@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, Type, Union, TypeVar, overload
from typing import TYPE_CHECKING, Union, TypeVar, overload
from .abstract import Mixin, Driver
@@ -10,16 +10,18 @@ if TYPE_CHECKING:
@overload
def combine_driver(driver: Type[D]) -> Type[D]: ...
def combine_driver(driver: type[D]) -> type[D]: ...
@overload
def combine_driver(driver: Type[D], *mixins: Type[Mixin]) -> Type["CombinedDriver"]: ...
def combine_driver(
driver: type[D], _m: type[Mixin], *mixins: type[Mixin]
) -> type["CombinedDriver"]: ...
def combine_driver(
driver: Type[D], *mixins: Type[Mixin]
) -> Union[Type[D], Type["CombinedDriver"]]:
driver: type[D], *mixins: type[Mixin]
) -> Union[type[D], type["CombinedDriver"]]:
"""将一个驱动器和多个混入类合并。"""
# check first
if not issubclass(driver, Driver):

View File

@@ -4,56 +4,44 @@ from enum import Enum
from dataclasses import dataclass
from typing_extensions import TypeAlias
from http.cookiejar import Cookie, CookieJar
from typing import (
IO,
Any,
Dict,
List,
Tuple,
Union,
Mapping,
Callable,
Iterator,
Optional,
Awaitable,
MutableMapping,
)
from typing import IO, Any, Union, Callable, Optional
from collections.abc import Mapping, Iterator, Awaitable, MutableMapping
from yarl import URL as URL
from multidict import CIMultiDict
RawURL: TypeAlias = Tuple[bytes, bytes, Optional[int], bytes]
RawURL: TypeAlias = tuple[bytes, bytes, Optional[int], bytes]
SimpleQuery: TypeAlias = Union[str, int, float]
QueryVariable: TypeAlias = Union[SimpleQuery, List[SimpleQuery]]
QueryVariable: TypeAlias = Union[SimpleQuery, list[SimpleQuery]]
QueryTypes: TypeAlias = Union[
None, str, Mapping[str, QueryVariable], List[Tuple[str, QueryVariable]]
None, str, Mapping[str, QueryVariable], list[tuple[str, SimpleQuery]]
]
HeaderTypes: TypeAlias = Union[
None,
CIMultiDict[str],
Dict[str, str],
List[Tuple[str, str]],
dict[str, str],
list[tuple[str, str]],
]
CookieTypes: TypeAlias = Union[
None, "Cookies", CookieJar, Dict[str, str], List[Tuple[str, str]]
None, "Cookies", CookieJar, dict[str, str], list[tuple[str, str]]
]
ContentTypes: TypeAlias = Union[str, bytes, None]
DataTypes: TypeAlias = Union[dict, None]
FileContent: TypeAlias = Union[IO[bytes], bytes]
FileType: TypeAlias = Tuple[Optional[str], FileContent, Optional[str]]
FileType: TypeAlias = tuple[Optional[str], FileContent, Optional[str]]
FileTypes: TypeAlias = Union[
# file (or bytes)
FileContent,
# (filename, file (or bytes))
Tuple[Optional[str], FileContent],
tuple[Optional[str], FileContent],
# (filename, file (or bytes), content_type)
FileType,
]
FilesTypes: TypeAlias = Union[Dict[str, FileTypes], List[Tuple[str, FileTypes]], None]
FilesTypes: TypeAlias = Union[dict[str, FileTypes], list[tuple[str, FileTypes]], None]
class HTTPVersion(Enum):
@@ -119,7 +107,7 @@ class Request:
self.content: ContentTypes = content
self.data: DataTypes = data
self.json: Any = json
self.files: Optional[List[Tuple[str, FileType]]] = None
self.files: Optional[list[tuple[str, FileType]]] = None
if files:
self.files = []
files_ = files.items() if isinstance(files, dict) else files
@@ -257,7 +245,7 @@ class Cookies(MutableMapping):
)
self.jar.set_cookie(cookie)
def get(
def get( # pyright: ignore[reportIncompatibleMethodOverride]
self,
name: str,
default: Optional[str] = None,
@@ -298,12 +286,14 @@ class Cookies(MutableMapping):
def clear(self, domain: Optional[str] = None, path: Optional[str] = None) -> None:
self.jar.clear(domain, path)
def update(self, cookies: CookieTypes = None) -> None:
def update( # pyright: ignore[reportIncompatibleMethodOverride]
self, cookies: CookieTypes = None
) -> None:
cookies = Cookies(cookies)
for cookie in cookies.jar:
self.jar.set_cookie(cookie)
def as_header(self, request: Request) -> Dict[str, str]:
def as_header(self, request: Request) -> dict[str, str]:
urllib_request = self._CookieCompatRequest(request)
self.jar.add_cookie_header(urllib_request)
return urllib_request.added_headers
@@ -341,9 +331,11 @@ class Cookies(MutableMapping):
method=request.method,
)
self.request = request
self.added_headers: Dict[str, str] = {}
self.added_headers: dict[str, str] = {}
def add_unredirected_header(self, key: str, value: str) -> None:
def add_unredirected_header( # pyright: ignore[reportIncompatibleMethodOverride]
self, key: str, value: str
) -> None:
super().add_unredirected_header(key, value)
self.added_headers[key] = value

View File

@@ -1,18 +1,5 @@
from typing import (
TYPE_CHECKING,
List,
Type,
Tuple,
Union,
TypeVar,
Iterator,
KeysView,
Optional,
ItemsView,
ValuesView,
MutableMapping,
overload,
)
from typing import TYPE_CHECKING, Union, TypeVar, Optional, overload
from collections.abc import Iterator, KeysView, ItemsView, ValuesView, MutableMapping
from .provider import DEFAULT_PROVIDER_CLASS, MatcherProvider
@@ -22,7 +9,7 @@ if TYPE_CHECKING:
T = TypeVar("T")
class MatcherManager(MutableMapping[int, List[Type["Matcher"]]]):
class MatcherManager(MutableMapping[int, list[type["Matcher"]]]):
"""事件响应器管理器
实现了常用字典操作,用于管理事件响应器。
@@ -43,10 +30,10 @@ class MatcherManager(MutableMapping[int, List[Type["Matcher"]]]):
def __len__(self) -> int:
return len(self.provider)
def __getitem__(self, key: int) -> List[Type["Matcher"]]:
def __getitem__(self, key: int) -> list[type["Matcher"]]:
return self.provider[key]
def __setitem__(self, key: int, value: List[Type["Matcher"]]) -> None:
def __setitem__(self, key: int, value: list[type["Matcher"]]) -> None:
self.provider[key] = value
def __delitem__(self, key: int) -> None:
@@ -58,41 +45,45 @@ class MatcherManager(MutableMapping[int, List[Type["Matcher"]]]):
def keys(self) -> KeysView[int]:
return self.provider.keys()
def values(self) -> ValuesView[List[Type["Matcher"]]]:
def values(self) -> ValuesView[list[type["Matcher"]]]:
return self.provider.values()
def items(self) -> ItemsView[int, List[Type["Matcher"]]]:
def items(self) -> ItemsView[int, list[type["Matcher"]]]:
return self.provider.items()
@overload
def get(self, key: int) -> Optional[List[Type["Matcher"]]]: ...
def get(self, key: int) -> Optional[list[type["Matcher"]]]: ...
@overload
def get(self, key: int, default: T) -> Union[List[Type["Matcher"]], T]: ...
def get(self, key: int, default: T) -> Union[list[type["Matcher"]], T]: ...
def get(
self, key: int, default: Optional[T] = None
) -> Optional[Union[List[Type["Matcher"]], T]]:
) -> Optional[Union[list[type["Matcher"]], T]]:
return self.provider.get(key, default)
def pop(self, key: int) -> List[Type["Matcher"]]:
def pop( # pyright: ignore[reportIncompatibleMethodOverride]
self, key: int
) -> list[type["Matcher"]]:
return self.provider.pop(key)
def popitem(self) -> Tuple[int, List[Type["Matcher"]]]:
def popitem(self) -> tuple[int, list[type["Matcher"]]]:
return self.provider.popitem()
def clear(self) -> None:
self.provider.clear()
def update(self, __m: MutableMapping[int, List[Type["Matcher"]]]) -> None:
def update( # pyright: ignore[reportIncompatibleMethodOverride]
self, __m: MutableMapping[int, list[type["Matcher"]]]
) -> None:
self.provider.update(__m)
def setdefault(
self, key: int, default: List[Type["Matcher"]]
) -> List[Type["Matcher"]]:
self, key: int, default: list[type["Matcher"]]
) -> list[type["Matcher"]]:
return self.provider.setdefault(key, default)
def set_provider(self, provider_class: Type[MatcherProvider]) -> None:
def set_provider(self, provider_class: type[MatcherProvider]) -> None:
"""设置事件响应器存储器
参数:

View File

@@ -6,19 +6,17 @@ from types import ModuleType
from dataclasses import dataclass
from contextvars import ContextVar
from typing_extensions import Self
from collections.abc import Iterable
from datetime import datetime, timedelta
from contextlib import AsyncExitStack, contextmanager
from typing import (
from typing import ( # noqa: UP035
TYPE_CHECKING,
Any,
List,
Type,
Tuple,
Union,
TypeVar,
Callable,
ClassVar,
Iterable,
NoReturn,
Optional,
overload,
@@ -85,8 +83,8 @@ current_handler: ContextVar[Dependent] = ContextVar("current_handler")
class MatcherSource:
"""Matcher 源代码上下文信息"""
plugin_name: Optional[str] = None
"""事件响应器所在插件名称"""
plugin_id: Optional[str] = None
"""事件响应器所在插件标识符"""
module_name: Optional[str] = None
"""事件响应器所在插件模块的路径名"""
lineno: Optional[int] = None
@@ -97,8 +95,13 @@ class MatcherSource:
"""事件响应器所在插件"""
from nonebot.plugin import get_plugin
if self.plugin_name is not None:
return get_plugin(self.plugin_name)
if self.plugin_id is not None:
return get_plugin(self.plugin_id)
@property
def plugin_name(self) -> Optional[str]:
"""事件响应器所在插件名"""
return self.plugin and self.plugin.name
@property
def module(self) -> Optional[ModuleType]:
@@ -141,7 +144,7 @@ class Matcher(metaclass=MatcherMeta):
"""事件响应器匹配规则"""
permission: ClassVar[Permission] = Permission()
"""事件响应器触发权限"""
handlers: List[Dependent[Any]] = []
handlers: ClassVar[list[Dependent[Any]]] = []
"""事件响应器拥有的事件处理函数列表"""
priority: ClassVar[int] = 1
"""事件响应器优先级"""
@@ -160,7 +163,7 @@ class Matcher(metaclass=MatcherMeta):
_default_permission_updater: ClassVar[Optional[Dependent[Permission]]] = None
"""事件响应器权限更新函数"""
HANDLER_PARAM_TYPES: ClassVar[Tuple[Type[Param], ...]] = (
HANDLER_PARAM_TYPES: ClassVar[tuple[Type[Param], ...]] = ( # noqa: UP006
DependParam,
BotParam,
EventParam,
@@ -171,7 +174,7 @@ class Matcher(metaclass=MatcherMeta):
)
def __init__(self):
self.handlers = self.handlers.copy()
self.remain_handlers: list[Dependent[Any]] = self.handlers.copy()
self.state = self._default_state.copy()
def __repr__(self) -> str:
@@ -192,7 +195,7 @@ class Matcher(metaclass=MatcherMeta):
type_: str = "",
rule: Optional[Rule] = None,
permission: Optional[Permission] = None,
handlers: Optional[List[Union[T_Handler, Dependent[Any]]]] = None,
handlers: Optional[list[Union[T_Handler, Dependent[Any]]]] = None,
temp: bool = False,
priority: int = 1,
block: bool = False,
@@ -206,7 +209,7 @@ class Matcher(metaclass=MatcherMeta):
default_permission_updater: Optional[
Union[T_PermissionUpdater, Dependent[Permission]]
] = None,
) -> Type[Self]:
) -> Type[Self]: # noqa: UP006
"""
创建一个新的事件响应器,并存储至 `matchers <#matchers>`_
@@ -247,7 +250,7 @@ class Matcher(metaclass=MatcherMeta):
)
source = source or (
MatcherSource(
plugin_name=plugin and plugin.name,
plugin_id=plugin and plugin.id_,
module_name=module and module.__name__,
)
if plugin is not None or module is not None
@@ -330,15 +333,20 @@ class Matcher(metaclass=MatcherMeta):
return cls._source and cls._source.plugin
@classproperty
def module(cls) -> Optional[ModuleType]:
"""事件响应器所在插件模块"""
return cls._source and cls._source.module
def plugin_id(cls) -> Optional[str]:
"""事件响应器所在插件标识符"""
return cls._source and cls._source.plugin_id
@classproperty
def plugin_name(cls) -> Optional[str]:
"""事件响应器所在插件名"""
return cls._source and cls._source.plugin_name
@classproperty
def module(cls) -> Optional[ModuleType]:
"""事件响应器所在插件模块"""
return cls._source and cls._source.module
@classproperty
def module_name(cls) -> Optional[str]:
"""事件响应器所在插件模块路径"""
@@ -457,7 +465,7 @@ class Matcher(metaclass=MatcherMeta):
parameterless: 非参数类型依赖列表
"""
async def _receive(event: Event, matcher: "Matcher") -> Union[None, NoReturn]:
async def _receive(event: Event, matcher: "Matcher") -> None:
matcher.set_target(RECEIVE_KEY.format(id=id))
if matcher.get_target() == RECEIVE_KEY.format(id=id):
matcher.set_receive(id, event)
@@ -775,7 +783,7 @@ class Matcher(metaclass=MatcherMeta):
async def resolve_reject(self):
handler = current_handler.get()
self.handlers.insert(0, handler)
self.remain_handlers.insert(0, handler)
if REJECT_CACHE_TARGET in self.state:
self.state[REJECT_TARGET] = self.state[REJECT_CACHE_TARGET]
@@ -809,8 +817,8 @@ class Matcher(metaclass=MatcherMeta):
# Refresh preprocess state
self.state.update(state)
while self.handlers:
handler = self.handlers.pop(0)
while self.remain_handlers:
handler = self.remain_handlers.pop(0)
current_handler.set(handler)
logger.debug(f"Running handler {handler}")
try:
@@ -852,7 +860,7 @@ class Matcher(metaclass=MatcherMeta):
type_,
Rule(),
permission,
self.handlers,
self.remain_handlers,
temp=True,
priority=0,
block=True,
@@ -872,7 +880,7 @@ class Matcher(metaclass=MatcherMeta):
type_,
Rule(),
permission,
self.handlers,
self.remain_handlers,
temp=True,
priority=0,
block=True,

View File

@@ -1,12 +1,13 @@
import abc
from typing import TYPE_CHECKING
from collections import defaultdict
from typing import TYPE_CHECKING, List, Type, Mapping, MutableMapping
from collections.abc import Mapping, MutableMapping
if TYPE_CHECKING:
from .matcher import Matcher
class MatcherProvider(abc.ABC, MutableMapping[int, List[Type["Matcher"]]]):
class MatcherProvider(abc.ABC, MutableMapping[int, list[type["Matcher"]]]):
"""事件响应器存储器基类
参数:
@@ -14,12 +15,12 @@ class MatcherProvider(abc.ABC, MutableMapping[int, List[Type["Matcher"]]]):
"""
@abc.abstractmethod
def __init__(self, matchers: Mapping[int, List[Type["Matcher"]]]):
def __init__(self, matchers: Mapping[int, list[type["Matcher"]]]):
raise NotImplementedError
class _DictProvider(defaultdict, MatcherProvider):
def __init__(self, matchers: Mapping[int, List[Type["Matcher"]]]):
def __init__(self, matchers: Mapping[int, list[type["Matcher"]]]):
super().__init__(list, matchers)

View File

@@ -1,16 +1,15 @@
import asyncio
import inspect
from typing_extensions import Self, get_args, override, get_origin
from contextlib import AsyncExitStack, contextmanager, asynccontextmanager
from typing_extensions import Self, Annotated, get_args, override, get_origin
from typing import (
TYPE_CHECKING,
Any,
Type,
Tuple,
Union,
Literal,
Callable,
Optional,
Annotated,
cast,
)
@@ -126,7 +125,7 @@ class DependParam(Param):
@classmethod
@override
def _check_param(
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
cls, param: inspect.Parameter, allow_types: tuple[type[Param], ...]
) -> Optional[Self]:
type_annotation, depends_inner = param.annotation, None
# extract type annotation and dependency from Annotated
@@ -166,7 +165,7 @@ class DependParam(Param):
@classmethod
@override
def _check_parameterless(
cls, value: Any, allow_types: Tuple[Type[Param], ...]
cls, value: Any, allow_types: tuple[type[Param], ...]
) -> Optional["Param"]:
if isinstance(value, DependsInner):
assert value.dependency, "Dependency cannot be empty"
@@ -249,7 +248,7 @@ class BotParam(Param):
@classmethod
@override
def _check_param(
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
cls, param: inspect.Parameter, allow_types: tuple[type[Param], ...]
) -> Optional[Self]:
from nonebot.adapters import Bot
@@ -266,11 +265,15 @@ class BotParam(Param):
return cls()
@override
async def _solve(self, bot: "Bot", **kwargs: Any) -> Any:
async def _solve( # pyright: ignore[reportIncompatibleMethodOverride]
self, bot: "Bot", **kwargs: Any
) -> Any:
return bot
@override
async def _check(self, bot: "Bot", **kwargs: Any) -> None:
async def _check( # pyright: ignore[reportIncompatibleMethodOverride]
self, bot: "Bot", **kwargs: Any
) -> None:
if self.checker is not None:
check_field_type(self.checker, bot)
@@ -299,7 +302,7 @@ class EventParam(Param):
@classmethod
@override
def _check_param(
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
cls, param: inspect.Parameter, allow_types: tuple[type[Param], ...]
) -> Optional[Self]:
from nonebot.adapters import Event
@@ -316,11 +319,15 @@ class EventParam(Param):
return cls()
@override
async def _solve(self, event: "Event", **kwargs: Any) -> Any:
async def _solve( # pyright: ignore[reportIncompatibleMethodOverride]
self, event: "Event", **kwargs: Any
) -> Any:
return event
@override
async def _check(self, event: "Event", **kwargs: Any) -> Any:
async def _check( # pyright: ignore[reportIncompatibleMethodOverride]
self, event: "Event", **kwargs: Any
) -> Any:
if self.checker is not None:
check_field_type(self.checker, event)
@@ -339,7 +346,7 @@ class StateParam(Param):
@classmethod
@override
def _check_param(
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
cls, param: inspect.Parameter, allow_types: tuple[type[Param], ...]
) -> Optional[Self]:
# param type is T_State
if param.annotation is T_State:
@@ -349,7 +356,9 @@ class StateParam(Param):
return cls()
@override
async def _solve(self, state: T_State, **kwargs: Any) -> Any:
async def _solve( # pyright: ignore[reportIncompatibleMethodOverride]
self, state: T_State, **kwargs: Any
) -> Any:
return state
@@ -377,7 +386,7 @@ class MatcherParam(Param):
@classmethod
@override
def _check_param(
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
cls, param: inspect.Parameter, allow_types: tuple[type[Param], ...]
) -> Optional[Self]:
from nonebot.matcher import Matcher
@@ -394,11 +403,15 @@ class MatcherParam(Param):
return cls()
@override
async def _solve(self, matcher: "Matcher", **kwargs: Any) -> Any:
async def _solve( # pyright: ignore[reportIncompatibleMethodOverride]
self, matcher: "Matcher", **kwargs: Any
) -> Any:
return matcher
@override
async def _check(self, matcher: "Matcher", **kwargs: Any) -> Any:
async def _check( # pyright: ignore[reportIncompatibleMethodOverride]
self, matcher: "Matcher", **kwargs: Any
) -> Any:
if self.checker is not None:
check_field_type(self.checker, matcher)
@@ -455,7 +468,7 @@ class ArgParam(Param):
@classmethod
@override
def _check_param(
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
cls, param: inspect.Parameter, allow_types: tuple[type[Param], ...]
) -> Optional[Self]:
if isinstance(param.default, ArgInner):
return cls(key=param.default.key or param.name, type=param.default.type)
@@ -464,7 +477,9 @@ class ArgParam(Param):
if isinstance(arg, ArgInner):
return cls(key=arg.key or param.name, type=arg.type)
async def _solve(self, matcher: "Matcher", **kwargs: Any) -> Any:
async def _solve( # pyright: ignore[reportIncompatibleMethodOverride]
self, matcher: "Matcher", **kwargs: Any
) -> Any:
message = matcher.get_arg(self.key)
if message is None:
return message
@@ -490,7 +505,7 @@ class ExceptionParam(Param):
@classmethod
@override
def _check_param(
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
cls, param: inspect.Parameter, allow_types: tuple[type[Param], ...]
) -> Optional[Self]:
# param type is Exception(s) or subclass(es) of Exception or None
if generic_check_issubclass(param.annotation, Exception):
@@ -518,7 +533,7 @@ class DefaultParam(Param):
@classmethod
@override
def _check_param(
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
cls, param: inspect.Parameter, allow_types: tuple[type[Param], ...]
) -> Optional[Self]:
if param.default != param.empty:
return cls(default=param.default)

View File

@@ -1,7 +1,7 @@
import asyncio
from typing_extensions import Self
from contextlib import AsyncExitStack
from typing import Set, Tuple, Union, NoReturn, Optional
from typing import Union, ClassVar, NoReturn, Optional
from nonebot.dependencies import Dependent
from nonebot.utils import run_coro_with_catch
@@ -9,7 +9,7 @@ from nonebot.exception import SkippedException
from nonebot.typing import T_DependencyCache, T_PermissionChecker
from .adapter import Bot, Event
from .params import BotParam, EventParam, DependParam, DefaultParam
from .params import Param, BotParam, EventParam, DependParam, DefaultParam
class Permission:
@@ -30,7 +30,7 @@ class Permission:
__slots__ = ("checkers",)
HANDLER_PARAM_TYPES = [
HANDLER_PARAM_TYPES: ClassVar[list[type[Param]]] = [
DependParam,
BotParam,
EventParam,
@@ -38,7 +38,7 @@ class Permission:
]
def __init__(self, *checkers: Union[T_PermissionChecker, Dependent[bool]]) -> None:
self.checkers: Set[Dependent[bool]] = {
self.checkers: set[Dependent[bool]] = {
(
checker
if isinstance(checker, Dependent)
@@ -122,7 +122,7 @@ class User:
__slots__ = ("users", "perm")
def __init__(
self, users: Tuple[str, ...], perm: Optional[Permission] = None
self, users: tuple[str, ...], perm: Optional[Permission] = None
) -> None:
self.users = users
self.perm = perm
@@ -146,7 +146,7 @@ class User:
@classmethod
def _clean_permission(cls, perm: Permission) -> Optional[Permission]:
if len(perm.checkers) == 1 and isinstance(
user_perm := tuple(perm.checkers)[0].call, cls
user_perm := next(iter(perm.checkers)).call, cls
):
return user_perm.perm
return perm

View File

@@ -1,13 +1,13 @@
import asyncio
from contextlib import AsyncExitStack
from typing import Set, Union, NoReturn, Optional
from typing import Union, ClassVar, NoReturn, Optional
from nonebot.dependencies import Dependent
from nonebot.exception import SkippedException
from nonebot.typing import T_State, T_RuleChecker, T_DependencyCache
from .adapter import Bot, Event
from .params import BotParam, EventParam, StateParam, DependParam, DefaultParam
from .params import Param, BotParam, EventParam, StateParam, DependParam, DefaultParam
class Rule:
@@ -28,7 +28,7 @@ class Rule:
__slots__ = ("checkers",)
HANDLER_PARAM_TYPES = [
HANDLER_PARAM_TYPES: ClassVar[list[type[Param]]] = [
DependParam,
BotParam,
EventParam,
@@ -37,7 +37,7 @@ class Rule:
]
def __init__(self, *checkers: Union[T_RuleChecker, Dependent[bool]]) -> None:
self.checkers: Set[Dependent[bool]] = {
self.checkers: set[Dependent[bool]] = {
(
checker
if isinstance(checker, Dependent)

View File

@@ -13,6 +13,7 @@ FrontMatter:
"""
import sys
import inspect
import logging
from typing import TYPE_CHECKING
@@ -45,6 +46,7 @@ logger: "Logger" = loguru.logger
# logger.addHandler(default_handler)
# https://loguru.readthedocs.io/en/stable/overview.html#entirely-compatible-with-standard-logging
class LoguruHandler(logging.Handler): # pragma: no cover
"""logging 与 loguru 之间的桥梁,将 logging 的日志转发到 loguru。"""
@@ -54,8 +56,8 @@ class LoguruHandler(logging.Handler): # pragma: no cover
except ValueError:
level = record.levelno
frame, depth = sys._getframe(6), 6
while frame and frame.f_code.co_filename == logging.__file__:
frame, depth = inspect.currentframe(), 0
while frame and (depth == 0 or frame.f_code.co_filename == logging.__file__):
frame = frame.f_back
depth += 1

View File

@@ -11,7 +11,7 @@ import asyncio
import contextlib
from datetime import datetime
from contextlib import AsyncExitStack
from typing import TYPE_CHECKING, Any, Set, Dict, Type, Optional
from typing import TYPE_CHECKING, Any, Optional
from nonebot.log import logger
from nonebot.rule import TrieRule
@@ -46,10 +46,10 @@ from nonebot.internal.params import (
if TYPE_CHECKING:
from nonebot.adapters import Bot, Event
_event_preprocessors: Set[Dependent[Any]] = set()
_event_postprocessors: Set[Dependent[Any]] = set()
_run_preprocessors: Set[Dependent[Any]] = set()
_run_postprocessors: Set[Dependent[Any]] = set()
_event_preprocessors: set[Dependent[Any]] = set()
_event_postprocessors: set[Dependent[Any]] = set()
_run_preprocessors: set[Dependent[Any]] = set()
_run_postprocessors: set[Dependent[Any]] = set()
EVENT_PCS_PARAMS = (
DependParam,
@@ -330,7 +330,7 @@ async def _apply_run_postprocessors(
async def _check_matcher(
Matcher: Type[Matcher],
Matcher: type[Matcher],
bot: "Bot",
event: "Event",
state: T_State,
@@ -381,7 +381,7 @@ async def _check_matcher(
async def _run_matcher(
Matcher: Type[Matcher],
Matcher: type[Matcher],
bot: "Bot",
event: "Event",
state: T_State,
@@ -446,7 +446,7 @@ async def _run_matcher(
async def check_and_run_matcher(
Matcher: Type[Matcher],
Matcher: type[Matcher],
bot: "Bot",
event: "Event",
state: T_State,
@@ -505,7 +505,7 @@ async def handle_event(bot: "Bot", event: "Event") -> None:
if show_log:
logger.opt(colors=True).success(log_msg)
state: Dict[Any, Any] = {}
state: dict[Any, Any] = {}
dependency_cache: T_DependencyCache = {}
# create event scope context

View File

@@ -5,18 +5,8 @@ FrontMatter:
description: nonebot.params 模块
"""
from typing import (
Any,
Dict,
List,
Match,
Tuple,
Union,
Literal,
Callable,
Optional,
overload,
)
from re import Match
from typing import Any, Union, Literal, Callable, Optional, overload
from nonebot.typing import T_State
from nonebot.matcher import Matcher
@@ -90,7 +80,7 @@ def _command(state: T_State) -> Message:
return state[PREFIX_KEY][CMD_KEY]
def Command() -> Tuple[str, ...]:
def Command() -> tuple[str, ...]:
"""消息命令元组"""
return Depends(_command)
@@ -140,7 +130,7 @@ def ShellCommandArgs() -> Any:
return Depends(_shell_command_args, use_cache=False)
def _shell_command_argv(state: T_State) -> List[Union[str, MessageSegment]]:
def _shell_command_argv(state: T_State) -> list[Union[str, MessageSegment]]:
return state[SHELL_ARGV]
@@ -159,11 +149,11 @@ def RegexMatched() -> Match[str]:
def _regex_str(
groups: Tuple[Union[str, int], ...]
) -> Callable[[T_State], Union[str, Tuple[Union[str, Any], ...], Any]]:
groups: tuple[Union[str, int], ...]
) -> Callable[[T_State], Union[str, tuple[Union[str, Any], ...], Any]]:
def _regex_str_dependency(
state: T_State,
) -> Union[str, Tuple[Union[str, Any], ...], Any]:
) -> Union[str, tuple[Union[str, Any], ...], Any]:
return _regex_matched(state).group(*groups)
return _regex_str_dependency
@@ -180,28 +170,28 @@ def RegexStr(__group: Union[str, int]) -> Union[str, Any]: ...
@overload
def RegexStr(
__group1: Union[str, int], __group2: Union[str, int], *groups: Union[str, int]
) -> Tuple[Union[str, Any], ...]: ...
) -> tuple[Union[str, Any], ...]: ...
def RegexStr(*groups: Union[str, int]) -> Union[str, Tuple[Union[str, Any], ...], Any]:
def RegexStr(*groups: Union[str, int]) -> Union[str, tuple[Union[str, Any], ...], Any]:
"""正则匹配结果文本"""
return Depends(_regex_str(groups), use_cache=False)
def _regex_group(state: T_State) -> Tuple[Any, ...]:
def _regex_group(state: T_State) -> tuple[Any, ...]:
return _regex_matched(state).groups()
def RegexGroup() -> Tuple[Any, ...]:
def RegexGroup() -> tuple[Any, ...]:
"""正则匹配结果 group 元组"""
return Depends(_regex_group, use_cache=False)
def _regex_dict(state: T_State) -> Dict[str, Any]:
def _regex_dict(state: T_State) -> dict[str, Any]:
return _regex_matched(state).groupdict()
def RegexDict() -> Dict[str, Any]:
def RegexDict() -> dict[str, Any]:
"""正则匹配结果 group 字典"""
return Depends(_regex_dict, use_cache=False)

View File

@@ -39,7 +39,7 @@ FrontMatter:
from itertools import chain
from types import ModuleType
from contextvars import ContextVar
from typing import Set, Dict, List, Type, Tuple, TypeVar, Optional
from typing import TypeVar, Optional
from pydantic import BaseModel
@@ -48,10 +48,10 @@ from nonebot.compat import model_dump, type_validate_python
C = TypeVar("C", bound=BaseModel)
_plugins: Dict[str, "Plugin"] = {}
_managers: List["PluginManager"] = []
_current_plugin_chain: ContextVar[Tuple["Plugin", ...]] = ContextVar(
"_current_plugin_chain", default=()
_plugins: dict[str, "Plugin"] = {}
_managers: list["PluginManager"] = []
_current_plugin: ContextVar[Optional["Plugin"]] = ContextVar(
"_current_plugin", default=None
)
@@ -59,34 +59,87 @@ def _module_name_to_plugin_name(module_name: str) -> str:
return module_name.rsplit(".", 1)[-1]
def _controlled_modules() -> dict[str, str]:
return {
plugin_id: module_name
for manager in _managers
for plugin_id, module_name in manager.controlled_modules.items()
}
def _find_parent_plugin_id(
module_name: str, controlled_modules: Optional[dict[str, str]] = None
) -> Optional[str]:
if controlled_modules is None:
controlled_modules = _controlled_modules()
available = {
module_name: plugin_id for plugin_id, module_name in controlled_modules.items()
}
while "." in module_name:
module_name, _ = module_name.rsplit(".", 1)
if module_name in available:
return available[module_name]
def _module_name_to_plugin_id(
module_name: str, controlled_modules: Optional[dict[str, str]] = None
) -> str:
plugin_name = _module_name_to_plugin_name(module_name)
if parent_plugin_id := _find_parent_plugin_id(module_name, controlled_modules):
return f"{parent_plugin_id}:{plugin_name}"
return plugin_name
def _new_plugin(
module_name: str, module: ModuleType, manager: "PluginManager"
) -> "Plugin":
plugin_name = _module_name_to_plugin_name(module_name)
if plugin_name in _plugins:
raise RuntimeError("Plugin already exists! Check your plugin name.")
plugin = Plugin(plugin_name, module, module_name, manager)
_plugins[plugin_name] = plugin
plugin_id = _module_name_to_plugin_id(module_name)
if plugin_id in _plugins:
raise RuntimeError(
f"Plugin {plugin_id} already exists! Check your plugin name."
)
parent_plugin_id = _find_parent_plugin_id(module_name)
if parent_plugin_id is not None and parent_plugin_id not in _plugins:
raise RuntimeError(
f"Parent plugin {parent_plugin_id} must "
f"be loaded before loading {plugin_id}."
)
parent_plugin = _plugins[parent_plugin_id] if parent_plugin_id is not None else None
plugin = Plugin(
name=_module_name_to_plugin_name(module_name),
module=module,
module_name=module_name,
manager=manager,
parent_plugin=parent_plugin,
)
if parent_plugin:
parent_plugin.sub_plugins.add(plugin)
_plugins[plugin_id] = plugin
return plugin
def _revert_plugin(plugin: "Plugin") -> None:
if plugin.name not in _plugins:
if plugin.id_ not in _plugins:
raise RuntimeError("Plugin not found!")
del _plugins[plugin.name]
del _plugins[plugin.id_]
if parent_plugin := plugin.parent_plugin:
parent_plugin.sub_plugins.remove(plugin)
parent_plugin.sub_plugins.discard(plugin)
def get_plugin(name: str) -> Optional["Plugin"]:
def get_plugin(plugin_id: str) -> Optional["Plugin"]:
"""获取已经导入的某个插件。
如果为 `load_plugins` 文件夹导入的插件,则为文件(夹)名。
如果为嵌套的子插件,标识符为 `父插件标识符:子插件文件(夹)名`。
参数:
name: 插件,即 {ref}`nonebot.plugin.model.Plugin.name`。
plugin_id: 插件标识符,即 {ref}`nonebot.plugin.model.Plugin.id_`。
"""
return _plugins.get(name)
return _plugins.get(plugin_id)
def get_plugin_by_module_name(module_name: str) -> Optional["Plugin"]:
@@ -105,17 +158,17 @@ def get_plugin_by_module_name(module_name: str) -> Optional["Plugin"]:
module_name, *has_parent = module_name.rsplit(".", 1)
def get_loaded_plugins() -> Set["Plugin"]:
def get_loaded_plugins() -> set["Plugin"]:
"""获取当前已导入的所有插件。"""
return set(_plugins.values())
def get_available_plugin_names() -> Set[str]:
"""获取当前所有可用的插件(包含尚未加载的插件)。"""
def get_available_plugin_names() -> set[str]:
"""获取当前所有可用的插件标识符(包含尚未加载的插件)。"""
return {*chain.from_iterable(manager.available_plugins for manager in _managers)}
def get_plugin_config(config: Type[C]) -> C:
def get_plugin_config(config: type[C]) -> C:
"""从全局配置获取当前插件需要的配置项。"""
return type_validate_python(config, model_dump(get_driver().config))

View File

@@ -8,13 +8,14 @@ FrontMatter:
import json
from pathlib import Path
from types import ModuleType
from typing import Set, Union, Iterable, Optional
from typing import Union, Optional
from collections.abc import Iterable
from nonebot.utils import path_to_module_name
from .model import Plugin
from .manager import PluginManager
from . import _managers, get_plugin, _current_plugin_chain, _module_name_to_plugin_name
from . import _managers, get_plugin, _module_name_to_plugin_id
try: # pragma: py-gte-311
import tomllib # pyright: ignore[reportMissingImports]
@@ -39,7 +40,7 @@ def load_plugin(module_path: Union[str, Path]) -> Optional[Plugin]:
return manager.load_plugin(module_path)
def load_plugins(*plugin_dir: str) -> Set[Plugin]:
def load_plugins(*plugin_dir: str) -> set[Plugin]:
"""导入文件夹下多个插件,以 `_` 开头的插件不会被导入!
参数:
@@ -52,7 +53,7 @@ def load_plugins(*plugin_dir: str) -> Set[Plugin]:
def load_all_plugins(
module_path: Iterable[str], plugin_dir: Iterable[str]
) -> Set[Plugin]:
) -> set[Plugin]:
"""导入指定列表中的插件以及指定目录下多个插件,以 `_` 开头的插件不会被导入!
参数:
@@ -64,7 +65,7 @@ def load_all_plugins(
return manager.load_all_plugins()
def load_from_json(file_path: str, encoding: str = "utf-8") -> Set[Plugin]:
def load_from_json(file_path: str, encoding: str = "utf-8") -> set[Plugin]:
"""导入指定 json 文件中的 `plugins` 以及 `plugin_dirs` 下多个插件。
以 `_` 开头的插件不会被导入!
@@ -95,7 +96,7 @@ def load_from_json(file_path: str, encoding: str = "utf-8") -> Set[Plugin]:
return load_all_plugins(set(plugins), set(plugin_dirs))
def load_from_toml(file_path: str, encoding: str = "utf-8") -> Set[Plugin]:
def load_from_toml(file_path: str, encoding: str = "utf-8") -> set[Plugin]:
"""导入指定 toml 文件 `[tool.nonebot]` 中的
`plugins` 以及 `plugin_dirs` 下多个插件。
以 `_` 开头的插件不会被导入!
@@ -139,7 +140,7 @@ def load_builtin_plugin(name: str) -> Optional[Plugin]:
return load_plugin(f"nonebot.plugins.{name}")
def load_builtin_plugins(*plugins: str) -> Set[Plugin]:
def load_builtin_plugins(*plugins: str) -> set[Plugin]:
"""导入多个 NoneBot 内置插件。
参数:
@@ -150,41 +151,45 @@ def load_builtin_plugins(*plugins: str) -> Set[Plugin]:
def _find_manager_by_name(name: str) -> Optional[PluginManager]:
for manager in reversed(_managers):
if name in manager.plugins or name in manager.searched_plugins:
if (
name in manager.controlled_modules
or name in manager.controlled_modules.values()
):
return manager
def require(name: str) -> ModuleType:
"""获取一个插件的导出内容
如果为 `load_plugins` 文件夹导入的插件,则为文件(夹)名。
"""声明依赖插件
参数:
name: 插件名,即 {ref}`nonebot.plugin.model.Plugin.name`
name: 插件模块名或插件标识符,仅在已声明插件的情况下可使用标识符
异常:
RuntimeError: 插件无法加载
"""
plugin = get_plugin(_module_name_to_plugin_name(name))
if "." in name:
# name is a module name
plugin = get_plugin(_module_name_to_plugin_id(name))
else:
# name is a plugin id or simple module name (equals to plugin id)
plugin = get_plugin(name)
# if plugin not loaded
if not plugin:
# plugin already declared
if plugin is None:
# plugin already declared, module name / plugin id
if manager := _find_manager_by_name(name):
plugin = manager.load_plugin(name)
# plugin not declared, try to declare and load it
else:
# clear current plugin chain, ensure plugin loaded in a new context
_t = _current_plugin_chain.set(())
try:
plugin = load_plugin(name)
finally:
_current_plugin_chain.reset(_t)
if not plugin:
if plugin is None:
raise RuntimeError(f'Cannot load plugin "{name}"!')
return plugin.module
def inherit_supported_adapters(*names: str) -> Optional[Set[str]]:
def inherit_supported_adapters(*names: str) -> Optional[set[str]]:
"""获取已加载插件的适配器支持状态集合。
如果传入了多个插件名称,返回值会自动取交集。
@@ -196,27 +201,28 @@ def inherit_supported_adapters(*names: str) -> Optional[Set[str]]:
RuntimeError: 插件未加载
ValueError: 插件缺少元数据
"""
final_supported: Optional[Set[str]] = None
final_supported: Optional[set[str]] = None
for name in names:
plugin = get_plugin(_module_name_to_plugin_name(name))
plugin = get_plugin(_module_name_to_plugin_id(name))
if plugin is None:
raise RuntimeError(f'Plugin "{name}" is not loaded!')
raise RuntimeError(
f'Plugin "{name}" is not loaded! You should require it first.'
)
meta = plugin.metadata
if meta is None:
raise ValueError(f'Plugin "{name}" has no metadata!')
support = meta.supported_adapters
if support is None:
if (raw := meta.supported_adapters) is None:
continue
support = {
f"nonebot.adapters.{adapter[1:]}" if adapter.startswith("~") else adapter
for adapter in raw
}
final_supported = (
support if final_supported is None else (final_supported & support)
)
return final_supported and {
(
f"nonebot.adapters.{adapter_name[1:]}"
if adapter_name.startswith("~")
else adapter_name
)
for adapter_name in final_supported
}
return final_supported

View File

@@ -12,10 +12,11 @@ import pkgutil
import importlib
from pathlib import Path
from itertools import chain
from typing import Optional
from types import ModuleType
from importlib.abc import MetaPathFinder
from collections.abc import Iterable, Sequence
from importlib.machinery import PathFinder, SourceFileLoader
from typing import Set, Dict, List, Iterable, Optional, Sequence
from nonebot.log import logger
from nonebot.utils import escape_tag, path_to_module_name
@@ -25,8 +26,8 @@ from . import (
_managers,
_new_plugin,
_revert_plugin,
_current_plugin_chain,
_module_name_to_plugin_name,
_current_plugin,
_module_name_to_plugin_id,
)
@@ -35,7 +36,7 @@ class PluginManager:
参数:
plugins: 独立插件模块名集合。
search_path: 插件搜索路径(文件夹)。
search_path: 插件搜索路径(文件夹),相对于当前工作目录
"""
def __init__(
@@ -44,60 +45,78 @@ class PluginManager:
search_path: Optional[Iterable[str]] = None,
):
# simple plugin not in search path
self.plugins: Set[str] = set(plugins or [])
self.search_path: Set[str] = set(search_path or [])
self.plugins: set[str] = set(plugins or [])
self.search_path: set[str] = set(search_path or [])
# cache plugins
self._third_party_plugin_names: Dict[str, str] = {}
self._searched_plugin_names: Dict[str, Path] = {}
self.prepare_plugins()
self._third_party_plugin_ids: dict[str, str] = {}
self._searched_plugin_ids: dict[str, str] = {}
self._prepare_plugins()
def __repr__(self) -> str:
return f"PluginManager(plugins={self.plugins}, search_path={self.search_path})"
return f"PluginManager(available_plugins={self.controlled_modules})"
@property
def third_party_plugins(self) -> Set[str]:
"""返回所有独立插件名称"""
return set(self._third_party_plugin_names.keys())
def third_party_plugins(self) -> set[str]:
"""返回所有独立插件标识符"""
return set(self._third_party_plugin_ids.keys())
@property
def searched_plugins(self) -> Set[str]:
"""返回已搜索到的插件名称"""
return set(self._searched_plugin_names.keys())
def searched_plugins(self) -> set[str]:
"""返回已搜索到的插件标识符"""
return set(self._searched_plugin_ids.keys())
@property
def available_plugins(self) -> Set[str]:
"""返回当前插件管理器中可用的插件名称"""
def available_plugins(self) -> set[str]:
"""返回当前插件管理器中可用的插件标识符"""
return self.third_party_plugins | self.searched_plugins
def _previous_plugins(self) -> Set[str]:
_pre_managers: List[PluginManager]
@property
def controlled_modules(self) -> dict[str, str]:
"""返回当前插件管理器中控制的插件标识符与模块路径映射字典。"""
return dict(
chain(
self._third_party_plugin_ids.items(), self._searched_plugin_ids.items()
)
)
def _previous_controlled_modules(self) -> dict[str, str]:
_pre_managers: list[PluginManager]
if self in _managers:
_pre_managers = _managers[: _managers.index(self)]
else:
_pre_managers = _managers[:]
return {
*chain.from_iterable(manager.available_plugins for manager in _pre_managers)
plugin_id: module_name
for manager in _pre_managers
for plugin_id, module_name in manager.controlled_modules.items()
}
def prepare_plugins(self) -> Set[str]:
def _prepare_plugins(self) -> set[str]:
"""搜索插件并缓存插件名称。"""
# get all previous ready to load plugins
previous_plugins = self._previous_plugins()
searched_plugins: Dict[str, Path] = {}
third_party_plugins: Dict[str, str] = {}
previous_plugin_ids = self._previous_controlled_modules()
# if self not in global managers, merge self's controlled modules
def get_controlled_modules():
return (
previous_plugin_ids
if self in _managers
else {**previous_plugin_ids, **self.controlled_modules}
)
# check third party plugins
for plugin in self.plugins:
name = _module_name_to_plugin_name(plugin)
if name in third_party_plugins or name in previous_plugins:
plugin_id = _module_name_to_plugin_id(plugin, get_controlled_modules())
if (
plugin_id in self._third_party_plugin_ids
or plugin_id in previous_plugin_ids
):
raise RuntimeError(
f"Plugin already exists: {name}! Check your plugin name"
f"Plugin already exists: {plugin_id}! Check your plugin name"
)
third_party_plugins[name] = plugin
self._third_party_plugin_names = third_party_plugins
self._third_party_plugin_ids[plugin_id] = plugin
# check plugins in search path
for module_info in pkgutil.iter_modules(self.search_path):
@@ -105,47 +124,55 @@ class PluginManager:
if module_info.name.startswith("_"):
continue
if (
module_info.name in searched_plugins
or module_info.name in previous_plugins
or module_info.name in third_party_plugins
):
raise RuntimeError(
f"Plugin already exists: {module_info.name}! Check your plugin name"
)
if not (
module_spec := module_info.module_finder.find_spec(
module_info.name, None
)
):
continue
if not (module_path := module_spec.origin):
continue
searched_plugins[module_info.name] = Path(module_path).resolve()
self._searched_plugin_names = searched_plugins
if not module_spec.origin:
continue
# get module name from path, pkgutil does not return the actual module name
module_path = Path(module_spec.origin).resolve()
module_name = path_to_module_name(module_path)
plugin_id = _module_name_to_plugin_id(module_name, get_controlled_modules())
if (
plugin_id in previous_plugin_ids
or plugin_id in self._third_party_plugin_ids
or plugin_id in self._searched_plugin_ids
):
raise RuntimeError(
f"Plugin already exists: {plugin_id}! Check your plugin name"
)
self._searched_plugin_ids[plugin_id] = module_name
return self.available_plugins
def load_plugin(self, name: str) -> Optional[Plugin]:
"""加载指定插件。
对于独立插件,可以使用完整插件模块名或者插件名称
可以使用完整插件模块名或者插件标识符加载
参数:
name: 插件名称。
name: 插件名称或插件标识符
"""
try:
if name in self.plugins:
# load using plugin id
if name in self._third_party_plugin_ids:
module = importlib.import_module(self._third_party_plugin_ids[name])
elif name in self._searched_plugin_ids:
module = importlib.import_module(self._searched_plugin_ids[name])
# load using module name
elif (
name in self._third_party_plugin_ids.values()
or name in self._searched_plugin_ids.values()
):
module = importlib.import_module(name)
elif name in self._third_party_plugin_names:
module = importlib.import_module(self._third_party_plugin_names[name])
elif name in self._searched_plugin_names:
module = importlib.import_module(
path_to_module_name(self._searched_plugin_names[name])
)
else:
raise RuntimeError(f"Plugin not found: {name}! Check your plugin name")
@@ -154,13 +181,13 @@ class PluginManager:
) is None or not isinstance(plugin, Plugin):
raise RuntimeError(
f"Module {module.__name__} is not loaded as a plugin! "
"Make sure not to import it before loading."
f"Make sure not to import it before loading."
)
logger.opt(colors=True).success(
f'Succeeded to load plugin "<y>{escape_tag(plugin.name)}</y>"'
f'Succeeded to load plugin "<y>{escape_tag(plugin.id_)}</y>"'
+ (
f' from "<m>{escape_tag(plugin.module_name)}</m>"'
if plugin.module_name != plugin.name
if plugin.module_name != plugin.id_
else ""
)
)
@@ -170,7 +197,7 @@ class PluginManager:
f'<r><bg #f8bbd0>Failed to import "{escape_tag(name)}"</bg #f8bbd0></r>'
)
def load_all_plugins(self) -> Set[Plugin]:
def load_all_plugins(self) -> set[Plugin]:
"""加载所有可用插件。"""
return set(
@@ -192,21 +219,16 @@ class PluginFinder(MetaPathFinder):
module_origin = module_spec.origin
if not module_origin:
return
module_path = Path(module_origin).resolve()
for manager in reversed(_managers):
# use path instead of name in case of submodule name conflict
if (
fullname in manager.plugins
or module_path in manager._searched_plugin_names.values()
):
if fullname in manager.controlled_modules.values():
module_spec.loader = PluginLoader(manager, fullname, module_origin)
return module_spec
return
class PluginLoader(SourceFileLoader):
def __init__(self, manager: PluginManager, fullname: str, path) -> None:
def __init__(self, manager: PluginManager, fullname: str, path: str) -> None:
self.manager = manager
self.loaded = False
super().__init__(fullname, path)
@@ -226,17 +248,8 @@ class PluginLoader(SourceFileLoader):
plugin = _new_plugin(self.name, module, self.manager)
setattr(module, "__plugin__", plugin)
# detect parent plugin before entering current plugin context
parent_plugins = _current_plugin_chain.get()
for pre_plugin in reversed(parent_plugins):
# ensure parent plugin is declared before current plugin
if _managers.index(pre_plugin.manager) < _managers.index(self.manager):
plugin.parent_plugin = pre_plugin
pre_plugin.sub_plugins.add(plugin)
break
# enter plugin context
_plugin_token = _current_plugin_chain.set(parent_plugins + (plugin,))
_plugin_token = _current_plugin.set(plugin)
try:
super().exec_module(module)
@@ -245,7 +258,7 @@ class PluginLoader(SourceFileLoader):
raise
finally:
# leave plugin context
_current_plugin_chain.reset(_plugin_token)
_current_plugin.reset(_plugin_token)
# get plugin metadata
metadata: Optional[PluginMetadata] = getattr(module, "__plugin_meta__", None)

View File

@@ -8,7 +8,7 @@ FrontMatter:
import contextlib
from types import ModuleType
from dataclasses import field, dataclass
from typing import TYPE_CHECKING, Any, Set, Dict, Type, Optional
from typing import TYPE_CHECKING, Any, Type, Optional # noqa: UP035
from pydantic import BaseModel
@@ -35,19 +35,19 @@ class PluginMetadata:
"""插件类型,用于商店分类"""
homepage: Optional[str] = None
"""插件主页"""
config: Optional[Type[BaseModel]] = None
config: Optional[Type[BaseModel]] = None # noqa: UP006
"""插件配置项"""
supported_adapters: Optional[Set[str]] = None
supported_adapters: Optional[set[str]] = None
"""插件支持的适配器模块路径
格式为 `<module>[:<Adapter>]``~` 为 `nonebot.adapters.` 的缩写。
`None` 表示支持**所有适配器**。
"""
extra: Dict[Any, Any] = field(default_factory=dict)
extra: dict[Any, Any] = field(default_factory=dict)
"""插件额外信息,可由插件编写者自由扩展定义"""
def get_supported_adapters(self) -> Optional[Set[Type["Adapter"]]]:
def get_supported_adapters(self) -> Optional[set[Type["Adapter"]]]: # noqa: UP006
"""获取当前已安装的插件支持适配器类列表"""
if self.supported_adapters is None:
return None
@@ -66,17 +66,24 @@ class Plugin:
"""存储插件信息"""
name: str
"""插件索引标识NoneBot 使用 文件/文件夹 名称作为标识符"""
"""插件名称NoneBot 使用 文件/文件夹 名称作为插件名称"""
module: ModuleType
"""插件模块对象"""
module_name: str
"""点分割模块路径"""
manager: "PluginManager"
"""导入该插件的插件管理器"""
matcher: Set[Type[Matcher]] = field(default_factory=set)
matcher: set[type[Matcher]] = field(default_factory=set)
"""插件加载时定义的 `Matcher`"""
parent_plugin: Optional["Plugin"] = None
"""父插件"""
sub_plugins: Set["Plugin"] = field(default_factory=set)
sub_plugins: set["Plugin"] = field(default_factory=set)
"""子插件集合"""
metadata: Optional[PluginMetadata] = None
@property
def id_(self) -> str:
"""插件索引标识"""
return (
f"{self.parent_plugin.id_}:{self.name}" if self.parent_plugin else self.name
)

View File

@@ -9,8 +9,8 @@ import re
import inspect
import warnings
from types import ModuleType
from typing import Any, Union, Optional
from datetime import datetime, timedelta
from typing import Any, Set, Dict, List, Type, Tuple, Union, Optional
from nonebot.adapters import Event
from nonebot.permission import Permission
@@ -31,19 +31,19 @@ from nonebot.rule import (
)
from .model import Plugin
from .manager import _current_plugin
from . import get_plugin_by_module_name
from .manager import _current_plugin_chain
def store_matcher(matcher: Type[Matcher]) -> None:
def store_matcher(matcher: type[Matcher]) -> None:
"""存储一个事件响应器到插件。
参数:
matcher: 事件响应器
"""
# only store the matcher defined when plugin loading
if plugin_chain := _current_plugin_chain.get():
plugin_chain[-1].matcher.add(matcher)
if plugin := _current_plugin.get():
plugin.matcher.add(matcher)
def get_matcher_plugin(depth: int = 1) -> Optional[Plugin]: # pragma: no cover
@@ -76,7 +76,7 @@ def get_matcher_module(depth: int = 1) -> Optional[ModuleType]: # pragma: no co
return (source := get_matcher_source(depth + 1)) and source.module
def get_matcher_source(depth: int = 1) -> Optional[MatcherSource]:
def get_matcher_source(depth: int = 0) -> Optional[MatcherSource]:
"""获取事件响应器定义所在源码信息。
参数:
@@ -85,20 +85,25 @@ def get_matcher_source(depth: int = 1) -> Optional[MatcherSource]:
current_frame = inspect.currentframe()
if current_frame is None:
return None
frame = inspect.getouterframes(current_frame)[depth + 1].frame
frame = current_frame
d = depth + 1
while d > 0:
frame = frame.f_back
if frame is None:
raise ValueError("Depth out of range")
d -= 1
module_name = (module := inspect.getmodule(frame)) and module.__name__
plugin: Optional["Plugin"] = None
# matcher defined when plugin loading
if plugin_chain := _current_plugin_chain.get():
plugin = plugin_chain[-1]
plugin: Optional["Plugin"] = _current_plugin.get()
# matcher defined when plugin running
elif module_name:
if plugin is None and module_name:
plugin = get_plugin_by_module_name(module_name)
return MatcherSource(
plugin_name=plugin and plugin.name,
plugin_id=plugin and plugin.id_,
module_name=module_name,
lineno=frame.f_lineno,
)
@@ -109,14 +114,14 @@ def on(
rule: Optional[Union[Rule, T_RuleChecker]] = None,
permission: Optional[Union[Permission, T_PermissionChecker]] = None,
*,
handlers: Optional[List[Union[T_Handler, Dependent]]] = None,
handlers: Optional[list[Union[T_Handler, Dependent]]] = None,
temp: bool = False,
expire_time: Optional[Union[datetime, timedelta]] = None,
priority: int = 1,
block: bool = False,
state: Optional[T_State] = None,
_depth: int = 0,
) -> Type[Matcher]:
) -> type[Matcher]:
"""注册一个基础事件响应器,可自定义类型。
参数:
@@ -146,7 +151,7 @@ def on(
return matcher
def on_metaevent(*args, _depth: int = 0, **kwargs) -> Type[Matcher]:
def on_metaevent(*args, _depth: int = 0, **kwargs) -> type[Matcher]:
"""注册一个元事件响应器。
参数:
@@ -162,7 +167,7 @@ def on_metaevent(*args, _depth: int = 0, **kwargs) -> Type[Matcher]:
return on("meta_event", *args, **kwargs, _depth=_depth + 1)
def on_message(*args, _depth: int = 0, **kwargs) -> Type[Matcher]:
def on_message(*args, _depth: int = 0, **kwargs) -> type[Matcher]:
"""注册一个消息事件响应器。
参数:
@@ -179,7 +184,7 @@ def on_message(*args, _depth: int = 0, **kwargs) -> Type[Matcher]:
return on("message", *args, **kwargs, _depth=_depth + 1)
def on_notice(*args, _depth: int = 0, **kwargs) -> Type[Matcher]:
def on_notice(*args, _depth: int = 0, **kwargs) -> type[Matcher]:
"""注册一个通知事件响应器。
参数:
@@ -195,7 +200,7 @@ def on_notice(*args, _depth: int = 0, **kwargs) -> Type[Matcher]:
return on("notice", *args, **kwargs, _depth=_depth + 1)
def on_request(*args, _depth: int = 0, **kwargs) -> Type[Matcher]:
def on_request(*args, _depth: int = 0, **kwargs) -> type[Matcher]:
"""注册一个请求事件响应器。
参数:
@@ -212,12 +217,12 @@ def on_request(*args, _depth: int = 0, **kwargs) -> Type[Matcher]:
def on_startswith(
msg: Union[str, Tuple[str, ...]],
msg: Union[str, tuple[str, ...]],
rule: Optional[Union[Rule, T_RuleChecker]] = None,
ignorecase: bool = False,
_depth: int = 0,
**kwargs,
) -> Type[Matcher]:
) -> type[Matcher]:
"""注册一个消息事件响应器,并且当消息的**文本部分**以指定内容开头时响应。
参数:
@@ -236,12 +241,12 @@ def on_startswith(
def on_endswith(
msg: Union[str, Tuple[str, ...]],
msg: Union[str, tuple[str, ...]],
rule: Optional[Union[Rule, T_RuleChecker]] = None,
ignorecase: bool = False,
_depth: int = 0,
**kwargs,
) -> Type[Matcher]:
) -> type[Matcher]:
"""注册一个消息事件响应器,并且当消息的**文本部分**以指定内容结尾时响应。
参数:
@@ -260,12 +265,12 @@ def on_endswith(
def on_fullmatch(
msg: Union[str, Tuple[str, ...]],
msg: Union[str, tuple[str, ...]],
rule: Optional[Union[Rule, T_RuleChecker]] = None,
ignorecase: bool = False,
_depth: int = 0,
**kwargs,
) -> Type[Matcher]:
) -> type[Matcher]:
"""注册一个消息事件响应器,并且当消息的**文本部分**与指定内容完全一致时响应。
参数:
@@ -284,11 +289,11 @@ def on_fullmatch(
def on_keyword(
keywords: Set[str],
keywords: set[str],
rule: Optional[Union[Rule, T_RuleChecker]] = None,
_depth: int = 0,
**kwargs,
) -> Type[Matcher]:
) -> type[Matcher]:
"""注册一个消息事件响应器,并且当消息纯文本部分包含关键词时响应。
参数:
@@ -306,13 +311,13 @@ def on_keyword(
def on_command(
cmd: Union[str, Tuple[str, ...]],
cmd: Union[str, tuple[str, ...]],
rule: Optional[Union[Rule, T_RuleChecker]] = None,
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = None,
aliases: Optional[set[Union[str, tuple[str, ...]]]] = None,
force_whitespace: Optional[Union[str, bool]] = None,
_depth: int = 0,
**kwargs,
) -> Type[Matcher]:
) -> type[Matcher]:
"""注册一个消息事件响应器,并且当消息以指定命令开头时响应。
命令匹配规则参考: `命令形式匹配 <rule.md#command-command>`_
@@ -341,13 +346,13 @@ def on_command(
def on_shell_command(
cmd: Union[str, Tuple[str, ...]],
cmd: Union[str, tuple[str, ...]],
rule: Optional[Union[Rule, T_RuleChecker]] = None,
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = None,
aliases: Optional[set[Union[str, tuple[str, ...]]]] = None,
parser: Optional[ArgumentParser] = None,
_depth: int = 0,
**kwargs,
) -> Type[Matcher]:
) -> type[Matcher]:
"""注册一个支持 `shell_like` 解析参数的命令消息事件响应器。
与普通的 `on_command` 不同的是,在添加 `parser` 参数时, 响应器会自动处理消息。
@@ -383,7 +388,7 @@ def on_regex(
rule: Optional[Union[Rule, T_RuleChecker]] = None,
_depth: int = 0,
**kwargs,
) -> Type[Matcher]:
) -> type[Matcher]:
"""注册一个消息事件响应器,并且当消息匹配正则表达式时响应。
命令匹配规则参考: `正则匹配 <rule.md#regex-regex-flags-0>`_
@@ -404,12 +409,12 @@ def on_regex(
def on_type(
types: Union[Type[Event], Tuple[Type[Event], ...]],
types: Union[type[Event], tuple[type[Event], ...]],
rule: Optional[Union[Rule, T_RuleChecker]] = None,
*,
_depth: int = 0,
**kwargs,
) -> Type[Matcher]:
) -> type[Matcher]:
"""注册一个事件响应器,并且当事件为指定类型时响应。
参数:
@@ -430,14 +435,14 @@ def on_type(
class _Group:
def __init__(self, **kwargs):
"""创建一个事件响应器组合,参数为默认值,与 `on` 一致"""
self.matchers: List[Type[Matcher]] = []
self.matchers: list[type[Matcher]] = []
"""组内事件响应器列表"""
self.base_kwargs: Dict[str, Any] = kwargs
self.base_kwargs: dict[str, Any] = kwargs
"""其他传递给 `on` 的参数默认值"""
def _get_final_kwargs(
self, update: Dict[str, Any], *, exclude: Optional[Set[str]] = None
) -> Dict[str, Any]:
self, update: dict[str, Any], *, exclude: Optional[set[str]] = None
) -> dict[str, Any]:
"""获取最终传递给 `on` 的参数
参数:
@@ -470,18 +475,18 @@ class CommandGroup(_Group):
"""
def __init__(
self, cmd: Union[str, Tuple[str, ...]], prefix_aliases: bool = False, **kwargs
self, cmd: Union[str, tuple[str, ...]], prefix_aliases: bool = False, **kwargs
):
"""命令前缀"""
super().__init__(**kwargs)
self.basecmd: Tuple[str, ...] = (cmd,) if isinstance(cmd, str) else cmd
self.basecmd: tuple[str, ...] = (cmd,) if isinstance(cmd, str) else cmd
self.base_kwargs.pop("aliases", None)
self.prefix_aliases = prefix_aliases
def __repr__(self) -> str:
return f"CommandGroup(cmd={self.basecmd}, matchers={len(self.matchers)})"
def command(self, cmd: Union[str, Tuple[str, ...]], **kwargs) -> Type[Matcher]:
def command(self, cmd: Union[str, tuple[str, ...]], **kwargs) -> type[Matcher]:
"""注册一个新的命令。新参数将会覆盖命令组默认值
参数:
@@ -509,8 +514,8 @@ class CommandGroup(_Group):
return matcher
def shell_command(
self, cmd: Union[str, Tuple[str, ...]], **kwargs
) -> Type[Matcher]:
self, cmd: Union[str, tuple[str, ...]], **kwargs
) -> type[Matcher]:
"""注册一个新的 `shell_like` 命令。新参数将会覆盖命令组默认值
参数:
@@ -544,7 +549,7 @@ class MatcherGroup(_Group):
def __repr__(self) -> str:
return f"MatcherGroup(matchers={len(self.matchers)})"
def on(self, **kwargs) -> Type[Matcher]:
def on(self, **kwargs) -> type[Matcher]:
"""注册一个基础事件响应器,可自定义类型。
参数:
@@ -562,7 +567,7 @@ class MatcherGroup(_Group):
self.matchers.append(matcher)
return matcher
def on_metaevent(self, **kwargs) -> Type[Matcher]:
def on_metaevent(self, **kwargs) -> type[Matcher]:
"""注册一个元事件响应器。
参数:
@@ -580,7 +585,7 @@ class MatcherGroup(_Group):
self.matchers.append(matcher)
return matcher
def on_message(self, **kwargs) -> Type[Matcher]:
def on_message(self, **kwargs) -> type[Matcher]:
"""注册一个消息事件响应器。
参数:
@@ -598,7 +603,7 @@ class MatcherGroup(_Group):
self.matchers.append(matcher)
return matcher
def on_notice(self, **kwargs) -> Type[Matcher]:
def on_notice(self, **kwargs) -> type[Matcher]:
"""注册一个通知事件响应器。
参数:
@@ -616,7 +621,7 @@ class MatcherGroup(_Group):
self.matchers.append(matcher)
return matcher
def on_request(self, **kwargs) -> Type[Matcher]:
def on_request(self, **kwargs) -> type[Matcher]:
"""注册一个请求事件响应器。
参数:
@@ -635,8 +640,8 @@ class MatcherGroup(_Group):
return matcher
def on_startswith(
self, msg: Union[str, Tuple[str, ...]], **kwargs
) -> Type[Matcher]:
self, msg: Union[str, tuple[str, ...]], **kwargs
) -> type[Matcher]:
"""注册一个消息事件响应器,并且当消息的**文本部分**以指定内容开头时响应。
参数:
@@ -656,7 +661,7 @@ class MatcherGroup(_Group):
self.matchers.append(matcher)
return matcher
def on_endswith(self, msg: Union[str, Tuple[str, ...]], **kwargs) -> Type[Matcher]:
def on_endswith(self, msg: Union[str, tuple[str, ...]], **kwargs) -> type[Matcher]:
"""注册一个消息事件响应器,并且当消息的**文本部分**以指定内容结尾时响应。
参数:
@@ -676,7 +681,7 @@ class MatcherGroup(_Group):
self.matchers.append(matcher)
return matcher
def on_fullmatch(self, msg: Union[str, Tuple[str, ...]], **kwargs) -> Type[Matcher]:
def on_fullmatch(self, msg: Union[str, tuple[str, ...]], **kwargs) -> type[Matcher]:
"""注册一个消息事件响应器,并且当消息的**文本部分**与指定内容完全一致时响应。
参数:
@@ -696,7 +701,7 @@ class MatcherGroup(_Group):
self.matchers.append(matcher)
return matcher
def on_keyword(self, keywords: Set[str], **kwargs) -> Type[Matcher]:
def on_keyword(self, keywords: set[str], **kwargs) -> type[Matcher]:
"""注册一个消息事件响应器,并且当消息纯文本部分包含关键词时响应。
参数:
@@ -717,11 +722,11 @@ class MatcherGroup(_Group):
def on_command(
self,
cmd: Union[str, Tuple[str, ...]],
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = None,
cmd: Union[str, tuple[str, ...]],
aliases: Optional[set[Union[str, tuple[str, ...]]]] = None,
force_whitespace: Optional[Union[str, bool]] = None,
**kwargs,
) -> Type[Matcher]:
) -> type[Matcher]:
"""注册一个消息事件响应器,并且当消息以指定命令开头时响应。
命令匹配规则参考: `命令形式匹配 <rule.md#command-command>`_
@@ -748,11 +753,11 @@ class MatcherGroup(_Group):
def on_shell_command(
self,
cmd: Union[str, Tuple[str, ...]],
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = None,
cmd: Union[str, tuple[str, ...]],
aliases: Optional[set[Union[str, tuple[str, ...]]]] = None,
parser: Optional[ArgumentParser] = None,
**kwargs,
) -> Type[Matcher]:
) -> type[Matcher]:
"""注册一个支持 `shell_like` 解析参数的命令消息事件响应器。
与普通的 `on_command` 不同的是,在添加 `parser` 参数时, 响应器会自动处理消息。
@@ -780,7 +785,7 @@ class MatcherGroup(_Group):
def on_regex(
self, pattern: str, flags: Union[int, re.RegexFlag] = 0, **kwargs
) -> Type[Matcher]:
) -> type[Matcher]:
"""注册一个消息事件响应器,并且当消息匹配正则表达式时响应。
命令匹配规则参考: `正则匹配 <rule.md#regex-regex-flags-0>`_
@@ -803,8 +808,8 @@ class MatcherGroup(_Group):
return matcher
def on_type(
self, types: Union[Type[Event], Tuple[Type[Event]]], **kwargs
) -> Type[Matcher]:
self, types: Union[type[Event], tuple[type[Event]]], **kwargs
) -> type[Matcher]:
"""注册一个事件响应器,并且当事件为指定类型时响应。
参数:

View File

@@ -1,4 +1,4 @@
from typing import Dict, AsyncGenerator
from collections.abc import AsyncGenerator
from nonebot.adapters import Event
from nonebot.params import Depends
@@ -15,7 +15,7 @@ __plugin_meta__ = PluginMetadata(
supported_adapters=None,
)
_running_matcher: Dict[str, int] = {}
_running_matcher: dict[str, int] = {}
async def matcher_mutex(event: Event) -> AsyncGenerator[bool, None]:

View File

@@ -15,19 +15,16 @@ from argparse import Action
from gettext import gettext
from argparse import ArgumentError
from contextvars import ContextVar
from collections.abc import Sequence
from itertools import chain, product
from argparse import Namespace as Namespace
from argparse import ArgumentParser as ArgParser
from typing import (
IO,
TYPE_CHECKING,
List,
Type,
Tuple,
Union,
TypeVar,
Optional,
Sequence,
TypedDict,
NamedTuple,
cast,
@@ -63,7 +60,7 @@ T = TypeVar("T")
class CMD_RESULT(TypedDict):
command: Optional[Tuple[str, ...]]
command: Optional[tuple[str, ...]]
raw_command: Optional[str]
command_arg: Optional[Message]
command_start: Optional[str]
@@ -72,7 +69,7 @@ class CMD_RESULT(TypedDict):
class TRIE_VALUE(NamedTuple):
command_start: str
command: Tuple[str, ...]
command: tuple[str, ...]
parser_message: ContextVar[str] = ContextVar("parser_message")
@@ -149,7 +146,7 @@ class StartswithRule:
__slots__ = ("msg", "ignorecase")
def __init__(self, msg: Tuple[str, ...], ignorecase: bool = False):
def __init__(self, msg: tuple[str, ...], ignorecase: bool = False):
self.msg = msg
self.ignorecase = ignorecase
@@ -181,7 +178,7 @@ class StartswithRule:
return False
def startswith(msg: Union[str, Tuple[str, ...]], ignorecase: bool = False) -> Rule:
def startswith(msg: Union[str, tuple[str, ...]], ignorecase: bool = False) -> Rule:
"""匹配消息纯文本开头。
参数:
@@ -204,7 +201,7 @@ class EndswithRule:
__slots__ = ("msg", "ignorecase")
def __init__(self, msg: Tuple[str, ...], ignorecase: bool = False):
def __init__(self, msg: tuple[str, ...], ignorecase: bool = False):
self.msg = msg
self.ignorecase = ignorecase
@@ -236,7 +233,7 @@ class EndswithRule:
return False
def endswith(msg: Union[str, Tuple[str, ...]], ignorecase: bool = False) -> Rule:
def endswith(msg: Union[str, tuple[str, ...]], ignorecase: bool = False) -> Rule:
"""匹配消息纯文本结尾。
参数:
@@ -259,7 +256,7 @@ class FullmatchRule:
__slots__ = ("msg", "ignorecase")
def __init__(self, msg: Tuple[str, ...], ignorecase: bool = False):
def __init__(self, msg: tuple[str, ...], ignorecase: bool = False):
self.msg = tuple(map(str.casefold, msg) if ignorecase else msg)
self.ignorecase = ignorecase
@@ -290,7 +287,7 @@ class FullmatchRule:
return False
def fullmatch(msg: Union[str, Tuple[str, ...]], ignorecase: bool = False) -> Rule:
def fullmatch(msg: Union[str, tuple[str, ...]], ignorecase: bool = False) -> Rule:
"""完全匹配消息。
参数:
@@ -361,7 +358,7 @@ class CommandRule:
def __init__(
self,
cmds: List[Tuple[str, ...]],
cmds: list[tuple[str, ...]],
force_whitespace: Optional[Union[str, bool]] = None,
):
self.cmds = tuple(cmds)
@@ -380,7 +377,7 @@ class CommandRule:
async def __call__(
self,
cmd: Optional[Tuple[str, ...]] = Command(),
cmd: Optional[tuple[str, ...]] = Command(),
cmd_arg: Optional[Message] = CommandArg(),
cmd_whitespace: Optional[str] = CommandWhitespace(),
) -> bool:
@@ -394,7 +391,7 @@ class CommandRule:
def command(
*cmds: Union[str, Tuple[str, ...]],
*cmds: Union[str, tuple[str, ...]],
force_whitespace: Optional[Union[str, bool]] = None,
) -> Rule:
"""匹配消息命令。
@@ -424,7 +421,7 @@ def command(
config = get_driver().config
command_start = config.command_start
command_sep = config.command_sep
commands: List[Tuple[str, ...]] = []
commands: list[tuple[str, ...]] = []
for command in cmds:
if isinstance(command, str):
command = (command,)
@@ -460,23 +457,23 @@ class ArgumentParser(ArgParser):
self,
args: Optional[Sequence[Union[str, MessageSegment]]] = None,
namespace: None = None,
) -> Tuple[Namespace, List[Union[str, MessageSegment]]]: ...
) -> tuple[Namespace, list[Union[str, MessageSegment]]]: ...
@overload
def parse_known_args(
self, args: Optional[Sequence[Union[str, MessageSegment]]], namespace: T
) -> Tuple[T, List[Union[str, MessageSegment]]]: ...
) -> tuple[T, list[Union[str, MessageSegment]]]: ...
@overload
def parse_known_args(
self, *, namespace: T
) -> Tuple[T, List[Union[str, MessageSegment]]]: ...
) -> tuple[T, list[Union[str, MessageSegment]]]: ...
def parse_known_args(
def parse_known_args( # pyright: ignore[reportIncompatibleMethodOverride]
self,
args: Optional[Sequence[Union[str, MessageSegment]]] = None,
namespace: Optional[T] = None,
) -> Tuple[Union[Namespace, T], List[Union[str, MessageSegment]]]: ...
) -> tuple[Union[Namespace, T], list[Union[str, MessageSegment]]]: ...
@overload
def parse_args(
@@ -506,7 +503,7 @@ class ArgumentParser(ArgParser):
def _parse_optional(
self, arg_string: Union[str, MessageSegment]
) -> Optional[Tuple[Optional[Action], str, Optional[str]]]:
) -> Optional[tuple[Optional[Action], str, Optional[str]]]:
return (
super()._parse_optional(arg_string) if isinstance(arg_string, str) else None
)
@@ -533,7 +530,7 @@ class ShellCommandRule:
__slots__ = ("cmds", "parser")
def __init__(self, cmds: List[Tuple[str, ...]], parser: Optional[ArgumentParser]):
def __init__(self, cmds: list[tuple[str, ...]], parser: Optional[ArgumentParser]):
self.cmds = tuple(cmds)
self.parser = parser
@@ -553,7 +550,7 @@ class ShellCommandRule:
async def __call__(
self,
state: T_State,
cmd: Optional[Tuple[str, ...]] = Command(),
cmd: Optional[tuple[str, ...]] = Command(),
msg: Optional[Message] = CommandArg(),
) -> bool:
if cmd not in self.cmds or msg is None:
@@ -571,7 +568,7 @@ class ShellCommandRule:
try:
args = self.parser.parse_args(state[SHELL_ARGV])
state[SHELL_ARGS] = args
except ArgumentError as e: # pragma: py-gte-39
except ArgumentError as e:
state[SHELL_ARGS] = ParserExit(status=2, message=str(e))
except ParserExit as e:
state[SHELL_ARGS] = e
@@ -581,7 +578,7 @@ class ShellCommandRule:
def shell_command(
*cmds: Union[str, Tuple[str, ...]], parser: Optional[ArgumentParser] = None
*cmds: Union[str, tuple[str, ...]], parser: Optional[ArgumentParser] = None
) -> Rule:
"""匹配 `shell_like` 形式的消息命令。
@@ -629,7 +626,7 @@ def shell_command(
config = get_driver().config
command_start = config.command_start
command_sep = config.command_sep
commands: List[Tuple[str, ...]] = []
commands: list[tuple[str, ...]] = []
for command in cmds:
if isinstance(command, str):
command = (command,)
@@ -740,7 +737,7 @@ class IsTypeRule:
__slots__ = ("types",)
def __init__(self, *types: Type[Event]):
def __init__(self, *types: type[Event]):
self.types = types
def __repr__(self) -> str:
@@ -756,7 +753,7 @@ class IsTypeRule:
return isinstance(event, self.types)
def is_type(*types: Type[Event]) -> Rule:
def is_type(*types: type[Event]) -> Rule:
"""匹配事件类型。
参数:

View File

@@ -45,26 +45,38 @@ def overrides(InterfaceClass: object):
if sys.version_info < (3, 10):
def origin_is_union(origin: t.Optional[t.Type[t.Any]]) -> bool:
def type_has_args(type_: type[t.Any]) -> bool:
"""判断类型是否有参数"""
return isinstance(type_, (t._GenericAlias, types.GenericAlias)) # type: ignore
else:
def type_has_args(type_: type[t.Any]) -> bool:
return isinstance(type_, (t._GenericAlias, types.GenericAlias, types.UnionType)) # type: ignore
if sys.version_info < (3, 10):
def origin_is_union(origin: t.Optional[type[t.Any]]) -> bool:
"""判断是否是 Union 类型"""
return origin is t.Union
else:
def origin_is_union(origin: t.Optional[t.Type[t.Any]]) -> bool:
def origin_is_union(origin: t.Optional[type[t.Any]]) -> bool:
return origin is t.Union or origin is types.UnionType
def origin_is_literal(origin: t.Optional[t.Type[t.Any]]) -> bool:
def origin_is_literal(origin: t.Optional[type[t.Any]]) -> bool:
"""判断是否是 Literal 类型"""
return origin is t.Literal or origin is t_ext.Literal
def _literal_values(type_: t.Type[t.Any]) -> t.Tuple[t.Any, ...]:
def _literal_values(type_: type[t.Any]) -> tuple[t.Any, ...]:
return get_args(type_)
def all_literal_values(type_: t.Type[t.Any]) -> t.List[t.Any]:
def all_literal_values(type_: type[t.Any]) -> list[t.Any]:
"""获取 Literal 类型包含的所有值"""
if not origin_is_literal(get_origin(type_)):
return [type_]
@@ -72,7 +84,7 @@ def all_literal_values(type_: t.Type[t.Any]) -> t.List[t.Any]:
return [x for value in _literal_values(type_) for x in all_literal_values(value)]
def origin_is_annotated(origin: t.Optional[t.Type[t.Any]]) -> bool:
def origin_is_annotated(origin: t.Optional[type[t.Any]]) -> bool:
"""判断是否是 Annotated 类型"""
with contextlib.suppress(TypeError):
return origin is not None and issubclass(origin, t_ext.Annotated)
@@ -84,28 +96,19 @@ if sys.version_info >= (3, 10):
NONE_TYPES.add(types.NoneType)
def is_none_type(type_: t.Type[t.Any]) -> bool:
def is_none_type(type_: type[t.Any]) -> bool:
"""判断是否是 None 类型"""
return type_ in NONE_TYPES
if sys.version_info < (3, 9): # pragma: py-lt-39
def evaluate_forwardref(
ref: t.ForwardRef, globalns: t.Dict[str, t.Any], localns: t.Dict[str, t.Any]
) -> t.Any:
return ref._evaluate(globalns, localns)
else: # pragma: py-gte-39
def evaluate_forwardref(
ref: t.ForwardRef, globalns: t.Dict[str, t.Any], localns: t.Dict[str, t.Any]
ref: t.ForwardRef, globalns: dict[str, t.Any], localns: dict[str, t.Any]
) -> t.Any:
return ref._evaluate(globalns, localns, frozenset())
# state
T_State: TypeAlias = t.Dict[t.Any, t.Any]
T_State: TypeAlias = dict[t.Any, t.Any]
"""事件处理状态 State 类型"""
_DependentCallable: TypeAlias = t.Union[
@@ -134,11 +137,11 @@ T_BotDisconnectionHook: TypeAlias = _DependentCallable[t.Any]
# api hooks
T_CallingAPIHook: TypeAlias = t.Callable[
["Bot", str, t.Dict[str, t.Any]], t.Awaitable[t.Any]
["Bot", str, dict[str, t.Any]], t.Awaitable[t.Any]
]
"""`bot.call_api` 钩子函数"""
T_CalledAPIHook: TypeAlias = t.Callable[
["Bot", t.Optional[Exception], str, t.Dict[str, t.Any], t.Any], t.Awaitable[t.Any]
["Bot", t.Optional[Exception], str, dict[str, t.Any], t.Any], t.Awaitable[t.Any]
]
"""`bot.call_api` 后执行的函数,参数分别为 bot, exception, api, data, result"""
@@ -244,5 +247,5 @@ T_PermissionUpdater: TypeAlias = _DependentCallable["Permission"]
- MatcherParam: Matcher 对象
- DefaultParam: 带有默认值的参数
"""
T_DependencyCache: TypeAlias = t.Dict[_DependentCallable[t.Any], "Task[t.Any]"]
T_DependencyCache: TypeAlias = dict[_DependentCallable[t.Any], "Task[t.Any]"]
"""依赖缓存, 用于存储依赖函数的返回值"""

View File

@@ -10,36 +10,23 @@ import json
import asyncio
import inspect
import importlib
import contextlib
import dataclasses
from pathlib import Path
from collections import deque
from contextvars import copy_context
from functools import wraps, partial
from contextlib import asynccontextmanager
from contextlib import AbstractContextManager, asynccontextmanager
from typing_extensions import ParamSpec, get_args, override, get_origin
from typing import (
Any,
Dict,
Type,
Tuple,
Union,
Generic,
Mapping,
TypeVar,
Callable,
Optional,
Sequence,
Coroutine,
AsyncGenerator,
ContextManager,
overload,
)
from collections.abc import Mapping, Sequence, Coroutine, AsyncGenerator
from typing import Any, Union, Generic, TypeVar, Callable, Optional, overload
from pydantic import BaseModel
from nonebot.log import logger
from nonebot.typing import (
is_none_type,
type_has_args,
origin_is_union,
origin_is_literal,
all_literal_values,
@@ -64,8 +51,8 @@ def escape_tag(s: str) -> str:
def deep_update(
mapping: Dict[K, Any], *updating_mappings: Dict[K, Any]
) -> Dict[K, Any]:
mapping: dict[K, Any], *updating_mappings: dict[K, Any]
) -> dict[K, Any]:
"""深度更新合并字典"""
updated_mapping = mapping.copy()
for updating_mapping in updating_mappings:
@@ -82,7 +69,7 @@ def deep_update(
def lenient_issubclass(
cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...]]
cls: Any, class_or_tuple: Union[type[Any], tuple[type[Any], ...]]
) -> bool:
"""检查 cls 是否是 class_or_tuple 中的一个类型子类并忽略类型错误。"""
try:
@@ -92,7 +79,7 @@ def lenient_issubclass(
def generic_check_issubclass(
cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...]]
cls: Any, class_or_tuple: Union[type[Any], tuple[type[Any], ...]]
) -> bool:
"""检查 cls 是否是 class_or_tuple 中的一个类型子类。
@@ -106,9 +93,10 @@ def generic_check_issubclass(
则会检查其 `__bound__` 或 `__constraints__`
是否是 class_or_tuple 中一个类型的子类或 None。
"""
try:
if not type_has_args(cls):
with contextlib.suppress(TypeError):
return issubclass(cls, class_or_tuple)
except TypeError:
origin = get_origin(cls)
if origin_is_union(origin):
return all(
@@ -130,8 +118,7 @@ def generic_check_issubclass(
elif isinstance(cls, TypeVar):
if cls.__constraints__:
return all(
is_none_type(type_)
or generic_check_issubclass(type_, class_or_tuple)
is_none_type(type_) or generic_check_issubclass(type_, class_or_tuple)
for type_ in cls.__constraints__
)
elif cls.__bound__:
@@ -139,13 +126,13 @@ def generic_check_issubclass(
return False
def type_is_complex(type_: Type[Any]) -> bool:
def type_is_complex(type_: type[Any]) -> bool:
"""检查 type_ 是否是复杂类型"""
origin = get_origin(type_)
return _type_is_complex_inner(type_) or _type_is_complex_inner(origin)
def _type_is_complex_inner(type_: Optional[Type[Any]]) -> bool:
def _type_is_complex_inner(type_: Optional[type[Any]]) -> bool:
if lenient_issubclass(type_, (str, bytes)):
return False
@@ -200,7 +187,7 @@ def run_sync(call: Callable[P, R]) -> Callable[P, Coroutine[None, None, R]]:
@asynccontextmanager
async def run_sync_ctx_manager(
cm: ContextManager[T],
cm: AbstractContextManager[T],
) -> AsyncGenerator[T, None]:
"""一个用于包装 sync context manager 为 async context manager 的执行函数"""
try:
@@ -216,7 +203,7 @@ async def run_sync_ctx_manager(
@overload
async def run_coro_with_catch(
coro: Coroutine[Any, Any, T],
exc: Tuple[Type[Exception], ...],
exc: tuple[type[Exception], ...],
return_on_err: None = None,
) -> Union[T, None]: ...
@@ -224,14 +211,14 @@ async def run_coro_with_catch(
@overload
async def run_coro_with_catch(
coro: Coroutine[Any, Any, T],
exc: Tuple[Type[Exception], ...],
exc: tuple[type[Exception], ...],
return_on_err: R,
) -> Union[T, R]: ...
async def run_coro_with_catch(
coro: Coroutine[Any, Any, T],
exc: Tuple[Type[Exception], ...],
exc: tuple[type[Exception], ...],
return_on_err: Optional[R] = None,
) -> Optional[Union[T, R]]:
"""运行协程并当遇到指定异常时返回指定值。
@@ -289,7 +276,7 @@ class classproperty(Generic[T]):
def __init__(self, func: Callable[[Any], T]) -> None:
self.func = func
def __get__(self, instance: Any, owner: Optional[Type[Any]] = None) -> T:
def __get__(self, instance: Any, owner: Optional[type[Any]] = None) -> T:
return self.func(type(instance) if owner is None else owner)

View File

@@ -17,7 +17,7 @@ _✨ NoneBot 本地文档插件 ✨_
<a href="https://pypi.python.org/pypi/nonebot-plugin-docs">
<img src="https://img.shields.io/pypi/v/nonebot-plugin-docs.svg" alt="pypi">
</a>
<img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="python">
<img src="https://img.shields.io/badge/python-3.9+-blue.svg" alt="python">
</p>
## 使用方式

View File

@@ -12,7 +12,7 @@ include = ["nonebot_plugin_docs/dist/**/*"]
[tool.poetry.dependencies]
python = "^3.8"
python = "^3.9"
nonebot2 = "^2.0.0"
[tool.poetry.dev-dependencies]

780
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "nonebot2"
version = "2.2.1"
version = "2.3.0"
description = "An asynchronous python bot framework."
authors = ["yanyongyu <yyy@nonebot.dev>"]
license = "MIT"
@@ -25,7 +25,7 @@ include = ["nonebot/py.typed"]
"Funding" = "https://afdian.net/@nonebot"
[tool.poetry.dependencies]
python = "^3.8"
python = "^3.9"
yarl = "^1.7.2"
pygtrie = "^2.4.1"
loguru = ">=0.6.0,<1.0.0"
@@ -44,7 +44,7 @@ uvicorn = { version = ">=0.20.0,<1.0.0", extras = [
], optional = true }
[tool.poetry.group.dev.dependencies]
ruff = "^0.2.0"
ruff = "^0.4.0"
isort = "^5.10.1"
black = "^24.0.0"
nonemoji = "^0.1.2"
@@ -66,12 +66,12 @@ all = ["fastapi", "quart", "aiohttp", "httpx", "websockets", "uvicorn"]
[tool.pytest.ini_options]
asyncio_mode = "strict"
addopts = "--cov=nonebot --cov-append --cov-report=term-missing"
addopts = "--cov=nonebot --cov-report=term-missing"
filterwarnings = ["error", "ignore::DeprecationWarning"]
[tool.black]
line-length = 88
target-version = ["py38", "py39", "py310", "py311"]
target-version = ["py39", "py310", "py311", "py312"]
include = '\.pyi?$'
extend-exclude = '''
'''
@@ -87,18 +87,37 @@ extra_standard_library = ["typing_extensions"]
[tool.ruff]
line-length = 88
target-version = "py38"
target-version = "py39"
[tool.ruff.lint]
select = ["E", "W", "F", "UP", "C", "T", "PYI", "PT", "Q"]
ignore = ["E402", "C901", "UP037"]
select = [
"F", # Pyflakes
"W", # pycodestyle warnings
"E", # pycodestyle errors
"UP", # pyupgrade
"ASYNC", # flake8-async
"C4", # flake8-comprehensions
"T10", # flake8-debugger
"T20", # flake8-print
"PYI", # flake8-pyi
"PT", # flake8-pytest-style
"Q", # flake8-quotes
"RUF", # Ruff-specific rules
]
ignore = [
"E402", # module-import-not-at-top-of-file
"UP037", # quoted-annotation
"RUF001", # ambiguous-unicode-character-string
"RUF002", # ambiguous-unicode-character-docstring
"RUF003", # ambiguous-unicode-character-comment
]
[tool.ruff.lint.flake8-pytest-style]
fixture-parentheses = false
mark-parentheses = false
[tool.pyright]
pythonVersion = "3.8"
pythonVersion = "3.9"
pythonPlatform = "All"
defineConstant = { PYDANTIC_V2 = true }
executionEnvironments = [
@@ -108,7 +127,7 @@ executionEnvironments = [
{ root = "./" },
]
typeCheckingMode = "basic"
typeCheckingMode = "standard"
reportShadowedImports = false
disableBytesTypePromotions = true

View File

@@ -4,4 +4,4 @@
cd "$(dirname "$0")/../tests"
# Run the tests
pytest -n auto --cov-report xml $@
pytest -n auto --cov-append --cov-report xml $@

View File

@@ -21,8 +21,6 @@ rules =
"sys_platform != 'win32'": py-win32
"sys_platform != 'linux'": py-linux
"sys_platform != 'darwin'": py-darwin
"sys_version_info < (3, 9)": py-gte-39
"sys_version_info >= (3, 9)": py-lt-39
"sys_version_info < (3, 11)": py-gte-311
"sys_version_info >= (3, 11)": py-lt-311
"package_version('pydantic') < (2,)": pydantic-v2

View File

@@ -1,7 +1,8 @@
import os
import threading
from pathlib import Path
from typing import TYPE_CHECKING, Set, Generator
from typing import TYPE_CHECKING
from collections.abc import Generator
import pytest
from nonebug import NONEBOT_INIT_KWARGS
@@ -38,13 +39,13 @@ def load_driver(request: pytest.FixtureRequest) -> Driver:
@pytest.fixture(scope="session", autouse=True)
def load_plugin(nonebug_init: None) -> Set["Plugin"]:
def load_plugin(nonebug_init: None) -> set["Plugin"]:
# preload global plugins
return nonebot.load_plugins(str(Path(__file__).parent / "plugins"))
@pytest.fixture(scope="session", autouse=True)
def load_builtin_plugin(nonebug_init: None) -> Set["Plugin"]:
def load_builtin_plugin(nonebug_init: None) -> set["Plugin"]:
# preload builtin plugins
return nonebot.load_builtin_plugins("echo", "single_session")

View File

@@ -1,7 +1,7 @@
import json
import base64
import socket
from typing import Dict, List, Union, TypeVar
from typing import Union, TypeVar
from wsproto.events import Ping
from werkzeug import Request, Response
@@ -31,7 +31,7 @@ def json_safe(string, content_type="application/octet-stream") -> str:
).decode("utf-8")
def flattern(d: "MultiDict[K, V]") -> Dict[K, Union[V, List[V]]]:
def flattern(d: "MultiDict[K, V]") -> dict[K, Union[V, list[V]]]:
return {k: v[0] if len(v) == 1 else v for k, v in d.to_dict(flat=False).items()}

View File

@@ -0,0 +1,15 @@
from nonebot.plugin import PluginMetadata
__plugin_meta__ = PluginMetadata(
name="测试插件3",
description="测试继承适配器, 使用内置适配器全名",
usage="无法使用",
type="application",
homepage="https://nonebot.dev",
supported_adapters={
"nonebot.adapters.onebot.v11",
"nonebot.adapters.onebot.v12",
"~qq",
},
extra={"author": "NoneBot"},
)

View File

@@ -8,5 +8,5 @@ manager = PluginManager(
_managers.append(manager)
# test load nested plugin with require
manager.load_plugin("nested_subplugin")
manager.load_plugin("nested_subplugin2")
manager.load_plugin("plugins.nested.plugins.nested_subplugin")
manager.load_plugin("nested:nested_subplugin2")

View File

@@ -1,4 +1,4 @@
from typing_extensions import Annotated
from typing import Annotated
from nonebot.adapters import Message
from nonebot.params import Arg, ArgStr, ArgPlainText

View File

@@ -1,5 +1,5 @@
from typing import Annotated
from dataclasses import dataclass
from typing_extensions import Annotated
from pydantic import Field
@@ -32,8 +32,8 @@ async def gen_async():
@dataclass
class ClassDependency:
x: int = Depends(gen_sync)
y: int = Depends(gen_async)
x: int = Depends(gen_sync) # noqa: RUF009
y: int = Depends(gen_async) # noqa: RUF009
class FooBot(Bot): ...

View File

@@ -1,4 +1,4 @@
from typing import List, Match, Tuple
from re import Match
from nonebot.typing import T_State
from nonebot.adapters import Message
@@ -32,7 +32,7 @@ async def legacy_state(state):
async def not_legacy_state(state: int): ...
async def command(cmd: Tuple[str, ...] = Command()) -> Tuple[str, ...]:
async def command(cmd: tuple[str, ...] = Command()) -> tuple[str, ...]:
return cmd
@@ -59,8 +59,8 @@ async def shell_command_args(
async def shell_command_argv(
shell_command_argv: List[str] = ShellCommandArgv(),
) -> List[str]:
shell_command_argv: list[str] = ShellCommandArgv(),
) -> list[str]:
return shell_command_argv
@@ -68,7 +68,7 @@ async def regex_dict(regex_dict: dict = RegexDict()) -> dict:
return regex_dict
async def regex_group(regex_group: Tuple = RegexGroup()) -> Tuple:
async def regex_group(regex_group: tuple = RegexGroup()) -> tuple:
return regex_group
@@ -80,8 +80,8 @@ async def regex_str(
entire: str = RegexStr(),
type_: str = RegexStr("type"),
second: str = RegexStr(2),
groups: Tuple[str, ...] = RegexStr(1, "arg"),
) -> Tuple[str, str, str, Tuple[str, ...]]:
groups: tuple[str, ...] = RegexStr(1, "arg"),
) -> tuple[str, str, str, tuple[str, ...]]:
return entire, type_, second, groups

View File

@@ -1,4 +1,3 @@
from typing import Type
from datetime import datetime, timezone
from nonebot.adapters import Event
@@ -52,7 +51,7 @@ matcher_on = on(
)
def matcher_on_factory() -> Type[Matcher]:
def matcher_on_factory() -> type[Matcher]:
return on(
"test",
rule=rule,

View File

@@ -1,4 +1,4 @@
from typing import Any, Dict, Optional
from typing import Any, Optional
import pytest
from nonebug import App
@@ -27,7 +27,7 @@ async def test_bot_call_api(app: App):
async def test_bot_calling_api_hook_simple(app: App):
runned: bool = False
async def calling_api_hook(bot: Bot, api: str, data: Dict[str, Any]):
async def calling_api_hook(bot: Bot, api: str, data: dict[str, Any]):
nonlocal runned
runned = True
@@ -53,7 +53,7 @@ async def test_bot_calling_api_hook_simple(app: App):
async def test_bot_calling_api_hook_mock(app: App):
runned: bool = False
async def calling_api_hook(bot: Bot, api: str, data: Dict[str, Any]):
async def calling_api_hook(bot: Bot, api: str, data: dict[str, Any]):
nonlocal runned
runned = True
@@ -84,7 +84,7 @@ async def test_bot_called_api_hook_simple(app: App):
bot: Bot,
exception: Optional[Exception],
api: str,
data: Dict[str, Any],
data: dict[str, Any],
result: Any,
):
nonlocal runned
@@ -116,7 +116,7 @@ async def test_bot_called_api_hook_mock(app: App):
bot: Bot,
exception: Optional[Exception],
api: str,
data: Dict[str, Any],
data: dict[str, Any],
result: Any,
):
nonlocal runned

View File

@@ -1,9 +1,10 @@
from typing import List, Union, Optional
from typing import TYPE_CHECKING, Union, Optional
import pytest
from pydantic import BaseModel
from pydantic import Field, BaseModel
from nonebot.config import DOTENV_TYPE, BaseSettings, SettingsError
from nonebot.compat import PYDANTIC_V2
from nonebot.config import DOTENV_TYPE, BaseSettings, SettingsError, SettingsConfig
class Simple(BaseModel):
@@ -14,23 +15,38 @@ class Simple(BaseModel):
class Example(BaseSettings):
if TYPE_CHECKING:
_env_file: Optional[DOTENV_TYPE] = ".env", ".env.example"
_env_nested_delimiter: Optional[str] = "__"
simple: str = ""
complex: List[int] = [1]
complex_none: Optional[List[int]] = None
complex_union: Union[int, List[int]] = 1
nested: Simple = Simple()
nested_inner: Simple = Simple()
if PYDANTIC_V2:
model_config = SettingsConfig(
env_file=(".env", ".env.example"), env_nested_delimiter="__"
)
else:
class Config:
class Config( # pyright: ignore[reportIncompatibleVariableOverride]
SettingsConfig
):
env_file = ".env", ".env.example"
env_nested_delimiter = "__"
simple: str = ""
complex: list[int] = Field(default=[1])
complex_none: Optional[list[int]] = None
complex_union: Union[int, list[int]] = 1
nested: Simple = Simple()
nested_inner: Simple = Simple()
class ExampleWithoutDelimiter(Example):
class Config:
if PYDANTIC_V2:
model_config = SettingsConfig(env_nested_delimiter=None)
else:
class Config( # pyright: ignore[reportIncompatibleVariableOverride]
SettingsConfig
):
env_nested_delimiter = None

View File

@@ -1,6 +1,7 @@
import json
import asyncio
from typing import Any, Set, Optional
from typing import Any, Optional
from http.cookies import SimpleCookie
import pytest
from nonebug import App
@@ -306,6 +307,121 @@ async def test_http_client(driver: Driver, server_url: URL):
await asyncio.sleep(1)
@pytest.mark.asyncio
@pytest.mark.parametrize(
"driver",
[
pytest.param("nonebot.drivers.httpx:Driver", id="httpx"),
pytest.param("nonebot.drivers.aiohttp:Driver", id="aiohttp"),
],
indirect=True,
)
async def test_http_client_session(driver: Driver, server_url: URL):
assert isinstance(driver, HTTPClientMixin)
session = driver.get_session(
params={"session": "test"},
headers={"X-Session": "test"},
cookies={"session": "test"},
)
request = Request("GET", server_url)
with pytest.raises(RuntimeError):
await session.request(request)
with pytest.raises(RuntimeError): # noqa: PT012
async with session:
async with session:
...
async with session as session:
# simple post with query, headers, cookies and content
request = Request(
"POST",
server_url,
params={"param": "test"},
headers={"X-Test": "test"},
cookies={"cookie": "test"},
content="test",
)
response = await session.request(request)
assert response.status_code == 200
assert response.content
data = json.loads(response.content)
assert data["method"] == "POST"
assert data["args"] == {"session": "test", "param": "test"}
assert data["headers"].get("X-Session") == "test"
assert data["headers"].get("X-Test") == "test"
assert {
key: cookie.value
for key, cookie in SimpleCookie(data["headers"].get("Cookie")).items()
} == {
"session": "test",
"cookie": "test",
}
assert data["data"] == "test"
# post with data body
request = Request("POST", server_url, data={"form": "test"})
response = await session.request(request)
assert response.status_code == 200
assert response.content
data = json.loads(response.content)
assert data["method"] == "POST"
assert data["args"] == {"session": "test"}
assert data["headers"].get("X-Session") == "test"
assert {
key: cookie.value
for key, cookie in SimpleCookie(data["headers"].get("Cookie")).items()
} == {"session": "test"}
assert data["form"] == {"form": "test"}
# post with json body
request = Request("POST", server_url, json={"json": "test"})
response = await session.request(request)
assert response.status_code == 200
assert response.content
data = json.loads(response.content)
assert data["method"] == "POST"
assert data["args"] == {"session": "test"}
assert data["headers"].get("X-Session") == "test"
assert {
key: cookie.value
for key, cookie in SimpleCookie(data["headers"].get("Cookie")).items()
} == {"session": "test"}
assert data["json"] == {"json": "test"}
# post with files and form data
request = Request(
"POST",
server_url,
data={"form": "test"},
files=[
("test1", b"test"),
("test2", ("test.txt", b"test")),
("test3", ("test.txt", b"test", "text/plain")),
],
)
response = await session.request(request)
assert response.status_code == 200
assert response.content
data = json.loads(response.content)
assert data["method"] == "POST"
assert data["args"] == {"session": "test"}
assert data["headers"].get("X-Session") == "test"
assert {
key: cookie.value
for key, cookie in SimpleCookie(data["headers"].get("Cookie")).items()
} == {"session": "test"}
assert data["form"] == {"form": "test"}
assert data["files"] == {
"test1": "test",
"test2": "test",
"test3": "test",
}, "file parsing error"
await asyncio.sleep(1)
@pytest.mark.asyncio
@pytest.mark.parametrize(
"driver",
@@ -363,8 +479,8 @@ async def test_combine_driver(driver: Driver, driver_type: str):
@pytest.mark.asyncio
async def test_bot_connect_hook(app: App, driver: Driver):
with pytest.MonkeyPatch.context() as m:
conn_hooks: Set[Dependent[Any]] = set()
disconn_hooks: Set[Dependent[Any]] = set()
conn_hooks: set[Dependent[Any]] = set()
disconn_hooks: set[Dependent[Any]] = set()
m.setattr(Driver, "_bot_connection_hook", conn_hooks)
m.setattr(Driver, "_bot_disconnection_hook", disconn_hooks)
@@ -409,9 +525,9 @@ async def test_bot_connect_hook(app: App, driver: Driver):
disconn_should_be_called = True
if conn_hook not in {hook.call for hook in conn_hooks}:
if conn_hook not in {hook.call for hook in conn_hooks}: # type: ignore
pytest.fail("on_bot_connect hook not registered")
if disconn_hook not in {hook.call for hook in disconn_hooks}:
if disconn_hook not in {hook.call for hook in disconn_hooks}: # type: ignore
pytest.fail("on_bot_disconnect hook not registered")
async with app.test_api() as ctx:

View File

@@ -29,8 +29,10 @@ async def test_matcher_info(app: App):
assert matcher.module is sys.modules["plugins.matcher.matcher_info"]
assert matcher.module_name == "plugins.matcher.matcher_info"
assert matcher._source.plugin_id == "matcher:matcher_info"
assert matcher._source.plugin_name == "matcher_info"
assert matcher.plugin is get_plugin("matcher_info")
assert matcher.plugin is get_plugin("matcher:matcher_info")
assert matcher.plugin_id == "matcher:matcher_info"
assert matcher.plugin_name == "matcher_info"
assert (
@@ -244,7 +246,7 @@ async def test_default_permission_updater(app: App):
matcher = test_permission_updater()
new_perm = await matcher.update_permission(bot, event)
assert len(new_perm.checkers) == 1
checker = list(new_perm.checkers)[0].call
checker = next(iter(new_perm.checkers)).call
assert isinstance(checker, User)
assert checker.users == ("test",)
assert checker.perm is default_permission
@@ -258,7 +260,7 @@ async def test_user_permission_updater(app: App):
)
event = make_fake_event(_session_id="test")()
user_permission = list(test_user_permission_updater.permission.checkers)[0].call
user_permission = next(iter(test_user_permission_updater.permission.checkers)).call
assert isinstance(user_permission, User)
assert user_permission.perm is default_permission
async with app.test_api() as ctx:
@@ -266,7 +268,7 @@ async def test_user_permission_updater(app: App):
matcher = test_user_permission_updater()
new_perm = await matcher.update_permission(bot, event)
assert len(new_perm.checkers) == 1
checker = list(new_perm.checkers)[0].call
checker = next(iter(new_perm.checkers)).call
assert isinstance(checker, User)
assert checker.users == ("test",)
assert checker.perm is default_permission

View File

@@ -212,7 +212,7 @@ async def test_event(app: App):
ctx.pass_params(event=fake_fooevent)
ctx.should_return(fake_fooevent)
with pytest.raises(TypeMisMatch): # noqa: PT012
with pytest.raises(TypeMisMatch):
async with app.test_dependent(sub_event, allow_types=[EventParam]) as ctx:
ctx.pass_params(event=fake_event)
@@ -436,7 +436,7 @@ async def test_matcher(app: App):
ctx.pass_params(matcher=foo_matcher)
ctx.should_return(foo_matcher)
with pytest.raises(TypeMisMatch): # noqa: PT012
with pytest.raises(TypeMisMatch):
async with app.test_dependent(sub_matcher, allow_types=[MatcherParam]) as ctx:
ctx.pass_params(matcher=fake_matcher)

View File

@@ -1,4 +1,4 @@
from typing import Tuple, Optional
from typing import Optional
import pytest
from nonebug import App
@@ -57,7 +57,7 @@ async def test_permission(app: App):
@pytest.mark.asyncio
@pytest.mark.parametrize(("type", "expected"), [("message", True), ("notice", False)])
async def test_message(type: str, expected: bool):
dependent = list(MESSAGE.checkers)[0]
dependent = next(iter(MESSAGE.checkers))
checker = dependent.call
assert isinstance(checker, Message)
@@ -69,7 +69,7 @@ async def test_message(type: str, expected: bool):
@pytest.mark.asyncio
@pytest.mark.parametrize(("type", "expected"), [("message", False), ("notice", True)])
async def test_notice(type: str, expected: bool):
dependent = list(NOTICE.checkers)[0]
dependent = next(iter(NOTICE.checkers))
checker = dependent.call
assert isinstance(checker, Notice)
@@ -81,7 +81,7 @@ async def test_notice(type: str, expected: bool):
@pytest.mark.asyncio
@pytest.mark.parametrize(("type", "expected"), [("message", False), ("request", True)])
async def test_request(type: str, expected: bool):
dependent = list(REQUEST.checkers)[0]
dependent = next(iter(REQUEST.checkers))
checker = dependent.call
assert isinstance(checker, Request)
@@ -95,7 +95,7 @@ async def test_request(type: str, expected: bool):
("type", "expected"), [("message", False), ("meta_event", True)]
)
async def test_metaevent(type: str, expected: bool):
dependent = list(METAEVENT.checkers)[0]
dependent = next(iter(METAEVENT.checkers))
checker = dependent.call
assert isinstance(checker, MetaEvent)
@@ -116,7 +116,7 @@ async def test_metaevent(type: str, expected: bool):
],
)
async def test_superuser(app: App, type: str, user_id: str, expected: bool):
dependent = list(SUPERUSER.checkers)[0]
dependent = next(iter(SUPERUSER.checkers))
checker = dependent.call
assert isinstance(checker, SuperUser)
@@ -138,9 +138,9 @@ async def test_superuser(app: App, type: str, user_id: str, expected: bool):
],
)
async def test_user(
app: App, session_ids: Tuple[str, ...], session_id: Optional[str], expected: bool
app: App, session_ids: tuple[str, ...], session_id: Optional[str], expected: bool
):
dependent = list(USER(*session_ids).checkers)[0]
dependent = next(iter(USER(*session_ids).checkers))
checker = dependent.call
assert isinstance(checker, User)

View File

@@ -10,18 +10,43 @@ async def test_get_plugin():
# check simple plugin
plugin = nonebot.get_plugin("export")
assert plugin
assert plugin.id_ == "export"
assert plugin.name == "export"
assert plugin.module_name == "plugins.export"
# check sub plugin
plugin = nonebot.get_plugin("nested_subplugin")
plugin = nonebot.get_plugin("nested:nested_subplugin")
assert plugin
assert plugin.id_ == "nested:nested_subplugin"
assert plugin.name == "nested_subplugin"
assert plugin.module_name == "plugins.nested.plugins.nested_subplugin"
# check get plugin by module name
@pytest.mark.asyncio
async def test_get_plugin_by_module_name():
# check get plugin by exact module name
plugin = nonebot.get_plugin_by_module_name("plugins.nested")
assert plugin
assert plugin.id_ == "nested"
assert plugin.name == "nested"
assert plugin.module_name == "plugins.nested"
# check get plugin by sub module name
plugin = nonebot.get_plugin_by_module_name("plugins.nested.utils")
assert plugin
assert plugin.id_ == "nested"
assert plugin.name == "nested"
assert plugin.module_name == "plugins.nested"
# check get plugin by sub plugin exact module name
plugin = nonebot.get_plugin_by_module_name(
"plugins.nested.plugins.nested_subplugin"
)
assert plugin
assert plugin.id_ == "nested:nested_subplugin"
assert plugin.name == "nested_subplugin"
assert plugin.module_name == "plugins.nested.plugins.nested_subplugin"
@pytest.mark.asyncio
async def test_get_available_plugin():
@@ -31,8 +56,8 @@ async def test_get_available_plugin():
_managers.append(PluginManager(["plugins.export", "plugin.require"]))
# check get available plugins
plugin_names = nonebot.get_available_plugin_names()
assert plugin_names == {"export", "require"}
plugin_ids = nonebot.get_available_plugin_names()
assert plugin_ids == {"export", "require"}
finally:
_managers.clear()
_managers.extend(old_managers)

View File

@@ -1,5 +1,4 @@
import sys
from typing import Set
from pathlib import Path
from dataclasses import asdict
@@ -22,7 +21,7 @@ async def test_load_plugin():
@pytest.mark.asyncio
async def test_load_plugins(load_plugin: Set[Plugin], load_builtin_plugin: Set[Plugin]):
async def test_load_plugins(load_plugin: set[Plugin], load_builtin_plugin: set[Plugin]):
loaded_plugins = {
plugin for plugin in nonebot.get_loaded_plugins() if not plugin.parent_plugin
}
@@ -30,12 +29,13 @@ async def test_load_plugins(load_plugin: Set[Plugin], load_builtin_plugin: Set[P
# check simple plugin
assert "plugins.export" in sys.modules
assert "plugin._hidden" not in sys.modules
# check sub plugin
plugin = nonebot.get_plugin("nested_subplugin")
plugin = nonebot.get_plugin("nested:nested_subplugin")
assert plugin
assert "plugins.nested.plugins.nested_subplugin" in sys.modules
assert plugin.parent_plugin == nonebot.get_plugin("nested")
assert plugin.parent_plugin is nonebot.get_plugin("nested")
# check load again
with pytest.raises(RuntimeError):
@@ -47,8 +47,8 @@ async def test_load_plugins(load_plugin: Set[Plugin], load_builtin_plugin: Set[P
@pytest.mark.asyncio
async def test_load_nested_plugin():
parent_plugin = nonebot.get_plugin("nested")
sub_plugin = nonebot.get_plugin("nested_subplugin")
sub_plugin2 = nonebot.get_plugin("nested_subplugin2")
sub_plugin = nonebot.get_plugin("nested:nested_subplugin")
sub_plugin2 = nonebot.get_plugin("nested:nested_subplugin2")
assert parent_plugin
assert sub_plugin
assert sub_plugin2
@@ -90,12 +90,16 @@ async def test_require_loaded(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setattr("nonebot.plugin.load._find_manager_by_name", _patched_find)
# require use module name
nonebot.require("plugins.export")
# require use plugin id
nonebot.require("export")
nonebot.require("nested:nested_subplugin")
@pytest.mark.asyncio
async def test_require_not_loaded(monkeypatch: pytest.MonkeyPatch):
m = PluginManager(["dynamic.require_not_loaded"])
m = PluginManager(["dynamic.require_not_loaded"], ["dynamic/require_not_loaded/"])
_managers.append(m)
num_managers = len(_managers)
@@ -107,7 +111,11 @@ async def test_require_not_loaded(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setattr(PluginManager, "load_plugin", _patched_load)
# require standalone plugin
nonebot.require("dynamic.require_not_loaded")
# require searched plugin
nonebot.require("dynamic.require_not_loaded.subplugin1")
nonebot.require("require_not_loaded:subplugin2")
assert len(_managers) == num_managers
@@ -150,32 +158,82 @@ async def test_plugin_metadata():
@pytest.mark.asyncio
async def test_inherit_supported_adapters():
async def test_inherit_supported_adapters_not_found():
with pytest.raises(RuntimeError):
inherit_supported_adapters("some_plugin_not_exist")
with pytest.raises(ValueError, match="has no metadata!"):
inherit_supported_adapters("export")
echo = nonebot.get_plugin("echo")
assert echo
assert echo.metadata
assert inherit_supported_adapters("echo") is None
plugin_1 = nonebot.get_plugin("metadata")
assert plugin_1
assert plugin_1.metadata
assert inherit_supported_adapters("metadata") == {
@pytest.mark.asyncio
@pytest.mark.parametrize(
("inherit_plugins", "expected"),
[
(("echo",), None),
(
("metadata",),
{
"nonebot.adapters.onebot.v11",
"plugins.metadata:FakeAdapter",
}
plugin_2 = nonebot.get_plugin("metadata_2")
assert plugin_2
assert plugin_2.metadata
assert inherit_supported_adapters("metadata", "metadata_2") == {
"nonebot.adapters.onebot.v11"
}
assert inherit_supported_adapters("metadata", "echo", "metadata_2") == {
"nonebot.adapters.onebot.v11"
}
},
),
(
("metadata_2",),
{
"nonebot.adapters.onebot.v11",
"nonebot.adapters.onebot.v12",
},
),
(
("metadata_3",),
{
"nonebot.adapters.onebot.v11",
"nonebot.adapters.onebot.v12",
"nonebot.adapters.qq",
},
),
(
("metadata", "metadata_2"),
{
"nonebot.adapters.onebot.v11",
},
),
(
("metadata", "metadata_3"),
{
"nonebot.adapters.onebot.v11",
},
),
(
("metadata_2", "metadata_3"),
{
"nonebot.adapters.onebot.v11",
"nonebot.adapters.onebot.v12",
},
),
(
("metadata", "metadata_2", "metadata_3"),
{
"nonebot.adapters.onebot.v11",
},
),
(
("metadata", "echo"),
{
"nonebot.adapters.onebot.v11",
"plugins.metadata:FakeAdapter",
},
),
(
("metadata", "metadata_2", "echo"),
{
"nonebot.adapters.onebot.v11",
},
),
],
)
async def test_inherit_supported_adapters_combine(
inherit_plugins: tuple[str], expected: set[str]
):
assert inherit_supported_adapters(*inherit_plugins) == expected

View File

@@ -1,11 +1,17 @@
import pytest
from nonebot.plugin import PluginManager
from nonebot.plugin import PluginManager, _managers
@pytest.mark.asyncio
async def test_load_plugin_name():
m = PluginManager(plugins=["dynamic.manager"])
_managers.append(m)
# load by plugin id
module1 = m.load_plugin("manager")
# load by module name
module2 = m.load_plugin("dynamic.manager")
assert module1
assert module2
assert module1 is module2

View File

@@ -1,4 +1,4 @@
from typing import Type, Callable, Optional
from typing import Callable, Optional
import pytest
@@ -104,7 +104,7 @@ from nonebot.rule import (
)
async def test_on(
matcher_name: str,
pre_rule_factory: Optional[Callable[[Type[Event]], T_RuleChecker]],
pre_rule_factory: Optional[Callable[[type[Event]], T_RuleChecker]],
has_permission: bool,
):
import plugins.plugin.matchers as module
@@ -145,6 +145,7 @@ async def test_on(
assert matcher.plugin is plugin
assert matcher in plugin.matcher
assert matcher.module is module
assert matcher.plugin_id == "plugin"
assert matcher.plugin_name == "plugin"
assert matcher.module_name == "plugins.plugin.matchers"
@@ -163,6 +164,7 @@ async def test_runtime_on():
assert matcher.plugin is plugin
assert matcher not in plugin.matcher
assert matcher.module is module
assert matcher.plugin_id == "plugin"
assert matcher.plugin_name == "plugin"
assert matcher.module_name == "plugins.plugin.matchers"
finally:

View File

@@ -1,6 +1,6 @@
import re
import sys
from typing import Match, Tuple, Union, Optional
from re import Match
from typing import Union, Optional
import pytest
from nonebug import App
@@ -163,14 +163,14 @@ async def test_trie(app: App):
],
)
async def test_startswith(
msg: Union[str, Tuple[str, ...]],
msg: Union[str, tuple[str, ...]],
ignorecase: bool,
type: str,
text: Optional[str],
expected: bool,
):
test_startswith = startswith(msg, ignorecase)
dependent = list(test_startswith.checkers)[0]
dependent = next(iter(test_startswith.checkers))
checker = dependent.call
msg = (msg,) if isinstance(msg, str) else msg
@@ -203,14 +203,14 @@ async def test_startswith(
],
)
async def test_endswith(
msg: Union[str, Tuple[str, ...]],
msg: Union[str, tuple[str, ...]],
ignorecase: bool,
type: str,
text: Optional[str],
expected: bool,
):
test_endswith = endswith(msg, ignorecase)
dependent = list(test_endswith.checkers)[0]
dependent = next(iter(test_endswith.checkers))
checker = dependent.call
msg = (msg,) if isinstance(msg, str) else msg
@@ -243,14 +243,14 @@ async def test_endswith(
],
)
async def test_fullmatch(
msg: Union[str, Tuple[str, ...]],
msg: Union[str, tuple[str, ...]],
ignorecase: bool,
type: str,
text: Optional[str],
expected: bool,
):
test_fullmatch = fullmatch(msg, ignorecase)
dependent = list(test_fullmatch.checkers)[0]
dependent = next(iter(test_fullmatch.checkers))
checker = dependent.call
msg = (msg,) if isinstance(msg, str) else msg
@@ -279,13 +279,13 @@ async def test_fullmatch(
],
)
async def test_keyword(
kws: Tuple[str, ...],
kws: tuple[str, ...],
type: str,
text: Optional[str],
expected: bool,
):
test_keyword = keyword(*kws)
dependent = list(test_keyword.checkers)[0]
dependent = next(iter(test_keyword.checkers))
checker = dependent.call
assert isinstance(checker, KeywordsRule)
@@ -323,15 +323,15 @@ async def test_keyword(
],
)
async def test_command(
cmds: Tuple[Tuple[str, ...]],
cmds: tuple[tuple[str, ...]],
force_whitespace: Optional[Union[str, bool]],
cmd: Tuple[str, ...],
cmd: tuple[str, ...],
whitespace: Optional[str],
arg_text: Optional[str],
expected: bool,
):
test_command = command(*cmds, force_whitespace=force_whitespace)
dependent = list(test_command.checkers)[0]
dependent = next(iter(test_command.checkers))
checker = dependent.call
assert isinstance(checker, CommandRule)
@@ -352,7 +352,7 @@ async def test_shell_command():
MessageSegment = Message.get_segment_class()
test_not_cmd = shell_command(CMD)
dependent = list(test_not_cmd.checkers)[0]
dependent = next(iter(test_not_cmd.checkers))
checker = dependent.call
assert isinstance(checker, ShellCommandRule)
message = Message()
@@ -361,7 +361,7 @@ async def test_shell_command():
assert not await dependent(event=event, state=state)
test_no_parser = shell_command(CMD)
dependent = list(test_no_parser.checkers)[0]
dependent = next(iter(test_no_parser.checkers))
checker = dependent.call
assert isinstance(checker, ShellCommandRule)
message = Message()
@@ -375,7 +375,7 @@ async def test_shell_command():
parser.add_argument("-a", required=True)
test_simple_parser = shell_command(CMD, parser=parser)
dependent = list(test_simple_parser.checkers)[0]
dependent = next(iter(test_simple_parser.checkers))
checker = dependent.call
assert isinstance(checker, ShellCommandRule)
message = Message("-a 1")
@@ -386,7 +386,7 @@ async def test_shell_command():
assert state[SHELL_ARGS] == Namespace(a="1")
test_parser_help = shell_command(CMD, parser=parser)
dependent = list(test_parser_help.checkers)[0]
dependent = next(iter(test_parser_help.checkers))
checker = dependent.call
assert isinstance(checker, ShellCommandRule)
message = Message("-h")
@@ -399,7 +399,7 @@ async def test_shell_command():
assert state[SHELL_ARGS].message == parser.format_help()
test_parser_error = shell_command(CMD, parser=parser)
dependent = list(test_parser_error.checkers)[0]
dependent = next(iter(test_parser_error.checkers))
checker = dependent.call
assert isinstance(checker, ShellCommandRule)
message = Message()
@@ -412,7 +412,7 @@ async def test_shell_command():
assert state[SHELL_ARGS].message.startswith(parser.format_usage() + "test: error:")
test_parser_remain_args = shell_command(CMD, parser=parser)
dependent = list(test_parser_remain_args.checkers)[0]
dependent = next(iter(test_parser_remain_args.checkers))
checker = dependent.call
assert isinstance(checker, ShellCommandRule)
message = MessageSegment.text("-a 1 2") + MessageSegment.image("test")
@@ -425,7 +425,7 @@ async def test_shell_command():
assert state[SHELL_ARGS].message.startswith(parser.format_usage() + "test: error:")
test_message_parser = shell_command(CMD, parser=parser)
dependent = list(test_message_parser.checkers)[0]
dependent = next(iter(test_message_parser.checkers))
checker = dependent.call
assert isinstance(checker, ShellCommandRule)
message = MessageSegment.text("-a") + MessageSegment.image("test")
@@ -435,12 +435,11 @@ async def test_shell_command():
assert state[SHELL_ARGV] == ["-a", MessageSegment.image("test")]
assert state[SHELL_ARGS] == Namespace(a=MessageSegment.image("test"))
if sys.version_info >= (3, 9):
parser = ArgumentParser("test", exit_on_error=False)
parser.add_argument("-a", required=True)
test_not_exit = shell_command(CMD, parser=parser)
dependent = list(test_not_exit.checkers)[0]
dependent = next(iter(test_not_exit.checkers))
checker = dependent.call
assert isinstance(checker, ShellCommandRule)
message = Message()
@@ -476,7 +475,7 @@ async def test_regex(
matched: Optional[Match[str]],
):
test_regex = regex(pattern)
dependent = list(test_regex.checkers)[0]
dependent = next(iter(test_regex.checkers))
checker = dependent.call
assert isinstance(checker, RegexRule)
@@ -499,7 +498,7 @@ async def test_regex(
@pytest.mark.parametrize("expected", [True, False])
async def test_to_me(expected: bool):
test_to_me = to_me()
dependent = list(test_to_me.checkers)[0]
dependent = next(iter(test_to_me.checkers))
checker = dependent.call
assert isinstance(checker, ToMeRule)
@@ -515,7 +514,7 @@ async def test_is_type():
Event3 = make_fake_event()
test_type = is_type(Event1, Event2)
dependent = list(test_type.checkers)[0]
dependent = next(iter(test_type.checkers))
checker = dependent.call
assert isinstance(checker, IsTypeRule)

View File

@@ -1,5 +1,5 @@
import json
from typing import Dict, List, Union, Literal, TypeVar, ClassVar
from typing import Dict, List, Union, Literal, TypeVar, ClassVar # noqa: UP035
from utils import FakeMessage, FakeMessageSegment
from nonebot.utils import (
@@ -26,8 +26,10 @@ def test_generic_check_issubclass():
assert generic_check_issubclass(Union[int, float, None], (int, float))
assert generic_check_issubclass(Literal[1, 2, 3], int)
assert not generic_check_issubclass(Literal[1, 2, "3"], int)
assert generic_check_issubclass(List[int], list)
assert generic_check_issubclass(Dict[str, int], dict)
assert generic_check_issubclass(List[int], list) # noqa: UP006
assert generic_check_issubclass(Dict[str, int], dict) # noqa: UP006
assert generic_check_issubclass(list[int], list)
assert generic_check_issubclass(dict[str, int], dict)
assert not generic_check_issubclass(ClassVar[int], int)
assert generic_check_issubclass(TypeVar("T", int, float), (int, float))
assert generic_check_issubclass(TypeVar("T", bound=int), (int, float))

View File

@@ -1,5 +1,6 @@
from typing import Union, Optional
from typing_extensions import override
from typing import Type, Union, Mapping, Iterable, Optional
from collections.abc import Mapping, Iterable
from pydantic import Extra, create_model
@@ -76,7 +77,7 @@ class FakeMessage(Message[FakeMessageSegment]):
def make_fake_event(
_base: Optional[Type[Event]] = None,
_base: Optional[type[Event]] = None,
_type: str = "message",
_name: str = "test",
_description: str = "test",
@@ -85,7 +86,7 @@ def make_fake_event(
_message: Optional[Message] = None,
_to_me: bool = True,
**fields,
) -> Type[Event]:
) -> type[Event]:
Base = _base or Event
class FakeEvent(Base, extra=Extra.forbid):

View File

@@ -8,7 +8,7 @@ slug: /
NoneBot2 是一个现代、跨平台、可扩展的 Python 聊天机器人框架(下称 NoneBot它基于 Python 的类型注解和异步优先特性兼容同步能够为你的需求实现提供便捷灵活的支持。同时NoneBot 拥有大量的开发者为其开发插件,用户无需编写任何代码,仅需完成环境配置及插件安装,就可以正常使用 NoneBot。
需要注意的是NoneBot 仅支持 **Python 3.8 以上版本**
需要注意的是NoneBot 仅支持 **Python 3.9 以上版本**
## 特色

View File

@@ -89,7 +89,7 @@ async def _(bot): ... # 兼容性处理
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="3.9" label="Python 3.9">
```python
from typing import Union
@@ -127,7 +127,7 @@ async def _(event): ... # 兼容性处理
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="3.9" label="Python 3.9">
```python
from typing import Union
@@ -191,7 +191,7 @@ async def _(e: ActionFailed | NetworkError): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="3.9" label="Python 3.9">
```python {6,9}
from typing import Union
@@ -216,8 +216,8 @@ async def _(e: Union[ActionFailed, NetworkError]): ...
子依赖使用 `Depends` 标记进行定义,其参数即依赖的函数或可调用对象,同样会被解析为 `Dependent` 对象,将会在依赖注入期间执行。我们来看一个例子:
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {5,15}
from typing import Annotated
@@ -239,7 +239,7 @@ async def _(event: Annotated[Event, Depends(check)]):
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {3,13}
from nonebot import on_command
@@ -287,8 +287,8 @@ async def _():
NoneBot 在执行子依赖时,会将其返回值缓存起来。当我们在使用子依赖时,`Depends` 具有一个参数 `use_cache`,默认为 `True`。此时在事件处理流程中,多次使用同一个子依赖时,将会使用缓存中的结果而不会重复执行。这在很多情景中非常有用,例如:
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {7}
import random
@@ -302,7 +302,7 @@ async def _(x: Annotated[int, Depends(random_result)]):
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {6}
import random
@@ -319,8 +319,8 @@ async def _(x: int = Depends(random_result)):
此时,在同一事件处理流程中,这个随机函数的返回值将会保持一致。如果我们希望每次都重新执行子依赖,可以将 `use_cache` 设置为 `False`。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {7}
import random
@@ -334,7 +334,7 @@ async def _(x: Annotated[int, Depends(random_result, use_cache=False)]):
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {6}
import random
@@ -357,8 +357,8 @@ async def _(x: int = Depends(random_result, use_cache=False)):
在依赖注入系统中,我们可以对子依赖的返回值进行自动类型转换与校验。这个功能由 Pydantic 支持,因此我们通过参数类型注解自动使用 Pydantic 支持的类型转换。例如:
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {6,9}
from typing import Annotated
@@ -374,7 +374,7 @@ async def _(user_id: Annotated[int, Depends(get_user_id, validate=True)]):
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {4,7}
from nonebot.params import Depends
@@ -392,8 +392,8 @@ async def _(user_id: int = Depends(get_user_id, validate=True)):
在进行类型自动转换的同时Pydantic 还支持对数据进行更多的限制,如:大于、小于、长度等。使用方法如下:
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {7,10}
from typing import Annotated
@@ -410,7 +410,7 @@ async def _(user_id: Annotated[int, Depends(get_user_id, validate=Field(gt=100))
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {5,8}
from pydantic import Field
@@ -431,8 +431,8 @@ async def _(user_id: int = Depends(get_user_id, validate=Field(gt=100))):
在前面的事例中,我们使用了函数作为子依赖。实际上,我们还可以使用类作为依赖。当我们在实例化一个类的时候,其实我们就在调用它,类本身也是一个可调用对象。例如:
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {16}
from typing import Annotated
@@ -455,7 +455,7 @@ async def _(data: Annotated[ClassDependency, Depends(ClassDependency)]):
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {15}
from dataclasses import dataclass
@@ -481,8 +481,8 @@ async def _(data: ClassDependency = Depends(ClassDependency)):
可以看到,我们使用 `dataclass` 定义了一个类。由于这个类的 `__init__` 方法可以被依赖注入系统解析,因此,我们可以将其作为子依赖进行声明。特别地,对于类依赖,`Depends` 的参数可以为空NoneBot 将会使用参数的类型注解进行解析与推断:
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python
from typing import Annotated
@@ -492,7 +492,7 @@ async def _(data: Annotated[ClassDependency, Depends()]):
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python
async def _(data: ClassDependency = Depends()):
@@ -510,11 +510,12 @@ NoneBot 的依赖注入支持依赖项在事件处理流程结束后进行一些
我们可以看下述代码段, 使用 `httpx.AsyncClient` 异步网络 IO并在事件处理流程中共用一个 client
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {15}
from typing import Annotated, AsyncGenerator
from typing import Annotated
from collections.abc import AsyncGenerator
import httpx
from nonebot.params import Depends
@@ -533,10 +534,10 @@ async def _(x: Annotated[httpx.AsyncClient, Depends(get_client)]):
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {15}
from typing import AsyncGenerator
from collections.abc import AsyncGenerator
import httpx
from nonebot.params import Depends
@@ -566,11 +567,10 @@ async def _(x: httpx.AsyncClient = Depends(get_client)):
在 Python 里,为类定义 `__call__` 方法就可以使得这个类的实例成为一个可调用对象。因此,我们也可以将定义了 `__call__` 方法的类的实例作为依赖。事实上NoneBot 的[内置响应规则](./matcher.md#内置响应规则)就广泛使用了这种方式,以 `is_type` 规则为例:
```python
from typing import Type
from nonebot.adapters import Event
class IsTypeRule:
def __init__(self, *types: Type[Event]):
def __init__(self, *types: type[Event]):
self.types = types
async def __call__(self, event: Event) -> bool:
@@ -587,8 +587,8 @@ class IsTypeRule:
获取当前事件的类型。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {4}
from typing import Annotated
@@ -598,7 +598,7 @@ async def _(foo: Annotated[str, EventType()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {3}
from nonebot.params import EventType
@@ -613,8 +613,8 @@ async def _(foo: str = EventType()): ...
获取当前事件的消息。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {5}
from typing import Annotated
@@ -625,7 +625,7 @@ async def _(foo: Annotated[Message, EventMessage()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {4}
from nonebot.adapters import Message
@@ -641,8 +641,8 @@ async def _(foo: Message = EventMessage()): ...
获取当前事件的消息纯文本部分。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {4}
from typing import Annotated
@@ -652,7 +652,7 @@ async def _(foo: Annotated[str, EventPlainText()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {3}
from nonebot.params import EventPlainText
@@ -667,8 +667,8 @@ async def _(foo: str = EventPlainText()): ...
获取当前事件是否与机器人相关。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {4}
from typing import Annotated
@@ -678,7 +678,7 @@ async def _(foo: Annotated[bool, EventToMe()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {3}
from nonebot.params import EventToMe
@@ -693,8 +693,8 @@ async def _(foo: bool = EventToMe()): ...
获取当前命令型消息的元组形式命令名。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {4}
from typing import Annotated
@@ -704,13 +704,12 @@ async def _(foo: Annotated[tuple[str, ...], Command()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {4}
from typing import Tuple
from nonebot.params import Command
async def _(foo: Tuple[str, ...] = Command()): ...
async def _(foo: tuple[str, ...] = Command()): ...
```
</TabItem>
@@ -724,8 +723,8 @@ async def _(foo: Tuple[str, ...] = Command()): ...
获取当前命令型消息的文本形式命令名。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {4}
from typing import Annotated
@@ -735,7 +734,7 @@ async def _(foo: Annotated[str, RawCommand()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {3}
from nonebot.params import RawCommand
@@ -754,8 +753,8 @@ async def _(foo: str = RawCommand()): ...
获取命令型消息命令后跟随的参数。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {5}
from typing import Annotated
@@ -766,7 +765,7 @@ async def _(foo: Annotated[Message, CommandArg()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {4}
from nonebot.adapters import Message
@@ -786,8 +785,8 @@ async def _(foo: Message = CommandArg()): ...
获取命令型消息命令前缀。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {4}
from typing import Annotated
@@ -797,7 +796,7 @@ async def _(foo: Annotated[str, CommandStart()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {3}
from nonebot.params import CommandStart
@@ -816,8 +815,8 @@ async def _(foo: str = CommandStart()): ...
获取命令型消息命令与参数间空白符。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {4}
from typing import Annotated
@@ -827,7 +826,7 @@ async def _(foo: Annotated[str, CommandWhitespace()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {3}
from nonebot.params import CommandWhitespace
@@ -854,10 +853,16 @@ from typing import Annotated
from nonebot.params import ShellCommandArgs
async def _(foo: Annotated[list[str | MessageSegment], ShellCommandArgv()]): ...
```
```python {4}
from nonebot.params import ShellCommandArgs
async def _(foo: list[str | MessageSegment] = ShellCommandArgv()): ...
```
</TabItem>
<TabItem value="3.9" label="Python 3.9+">
<TabItem value="3.9" label="Python 3.9">
```python {4}
from typing import Union, Annotated
@@ -866,14 +871,11 @@ from nonebot.params import ShellCommandArgs
async def _(foo: Annotated[list[Union[str, MessageSegment]], ShellCommandArgv()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
```python {4}
from typing import List, Union
from typing import Union
from nonebot.params import ShellCommandArgs
async def _(foo: List[Union[str, MessageSegment]] = ShellCommandArgv()): ...
async def _(foo: list[Union[str, MessageSegment]] = ShellCommandArgv()): ...
```
</TabItem>
@@ -889,8 +891,8 @@ async def _(foo: List[Union[str, MessageSegment]] = ShellCommandArgv()): ...
由于 `ArgumentParser` 在解析到 `--help` 参数时也会抛出异常,这种情况下错误码为 `0` 且错误信息即为帮助信息。
:::
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {14,22}
from typing import Annotated
@@ -919,7 +921,7 @@ async def _(foo: Annotated[Namespace, ShellCommandArgs()]):
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {12,20}
from nonebot import on_shell_command
@@ -948,12 +950,40 @@ async def _(foo: Namespace = ShellCommandArgs()):
</TabItem>
</Tabs>
### RegexMatched
获取正则匹配结果的对象。
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {5}
from re import Match
from typing import Annotated
from nonebot.params import RegexMatched
async def _(foo: Annotated[Match[str], RegexMatched()]): ...
```
</TabItem>
<TabItem value="no-annotated" label="Without Annotated">
```python {4}
from re import Match
from nonebot.params import RegexMatched
async def _(foo: Match[str] = RegexMatched()): ...
```
</TabItem>
</Tabs>
### RegexStr
获取正则匹配结果的文本。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {4}
from typing import Annotated
@@ -963,7 +993,7 @@ async def _(foo: Annotated[str, RegexStr()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {3}
from nonebot.params import RegexStr
@@ -978,8 +1008,8 @@ async def _(foo: str = RegexStr()): ...
获取正则匹配结果的 group 元组。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {4}
from typing import Any, Annotated
@@ -989,13 +1019,13 @@ async def _(foo: Annotated[tuple[Any, ...], RegexGroup()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {4}
from typing import Tuple, Any
from typing import Any
from nonebot.params import RegexGroup
async def _(foo: Tuple[Any, ...] = RegexGroup()): ...
async def _(foo: tuple[Any, ...] = RegexGroup()): ...
```
</TabItem>
@@ -1005,8 +1035,8 @@ async def _(foo: Tuple[Any, ...] = RegexGroup()): ...
获取正则匹配结果的 group 字典。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {4}
from typing import Any, Annotated
@@ -1016,13 +1046,13 @@ async def _(foo: Annotated[dict[str, Any], RegexDict()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {4}
from typing import Any, Dict
from typing import Any
from nonebot.params import RegexDict
async def _(foo: Dict[str, Any] = RegexDict()): ...
async def _(foo: dict[str, Any] = RegexDict()): ...
```
</TabItem>
@@ -1032,8 +1062,8 @@ async def _(foo: Dict[str, Any] = RegexDict()): ...
获取触发响应器的消息前缀字符串。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {4}
from typing import Annotated
@@ -1043,7 +1073,7 @@ async def _(foo: Annotated[str, Startswith()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {3}
from nonebot.params import Startswith
@@ -1058,8 +1088,8 @@ async def _(foo: str = Startswith()): ...
获取触发响应器的消息后缀字符串。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {4}
from typing import Annotated
@@ -1069,7 +1099,7 @@ async def _(foo: Annotated[str, Endswith()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {3}
from nonebot.params import Endswith
@@ -1084,8 +1114,8 @@ async def _(foo: str = Endswith()): ...
获取触发响应器的消息字符串。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {4}
from typing import Annotated
@@ -1095,7 +1125,7 @@ async def _(foo: Annotated[str, Fullmatch()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {3}
from nonebot.params import Fullmatch
@@ -1110,8 +1140,8 @@ async def _(foo: str = Fullmatch()): ...
获取触发响应器的关键字字符串。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {4}
from typing import Annotated
@@ -1121,7 +1151,7 @@ async def _(foo: Annotated[str, Keyword()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {3}
from nonebot.params import Keyword
@@ -1136,8 +1166,8 @@ async def _(foo: str = Keyword()): ...
获取某次 `receive` 接收的事件。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {7}
from typing import Annotated
@@ -1150,7 +1180,7 @@ async def _(foo: Annotated[Event, Received("id")]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {5}
from nonebot.adapters import Event
@@ -1167,8 +1197,8 @@ async def _(foo: Event = Received("id")): ...
获取最近一次 `receive` 接收的事件。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {7}
from typing import Annotated
@@ -1181,7 +1211,7 @@ async def _(foo: Annotated[Event, LastReceived()]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {5}
from nonebot.adapters import Event
@@ -1198,8 +1228,8 @@ async def _(foo: Event = LastReceived()): ...
获取某次 `got` 接收的参数。如果 `Arg` 参数留空,则使用函数的参数名作为要获取的参数。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {7,8}
from typing import Annotated
@@ -1213,7 +1243,7 @@ async def _(foo: Annotated[Message, Arg("key")]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {5,6}
from nonebot.params import Arg
@@ -1231,8 +1261,8 @@ async def _(foo: Message = Arg("key")): ...
获取某次 `got` 接收的参数,并转换为字符串。如果 `Arg` 参数留空,则使用函数的参数名作为要获取的参数。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {6,7}
from typing import Annotated
@@ -1245,7 +1275,7 @@ async def _(foo: Annotated[str, ArgStr("key")]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {4,5}
from nonebot.params import ArgStr
@@ -1262,8 +1292,8 @@ async def _(foo: str = ArgStr("key")): ...
获取某次 `got` 接收的参数的纯文本部分。如果 `Arg` 参数留空,则使用函数的参数名作为要获取的参数。
<Tabs groupId="python">
<TabItem value="3.9" label="Python 3.9+" default>
<Tabs groupId="annotated">
<TabItem value="annotated" label="Use Annotated" default>
```python {6,7}
from typing import Annotated
@@ -1276,7 +1306,7 @@ async def _(foo: Annotated[str, ArgPlainText("key")]): ...
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="no-annotated" label="Without Annotated">
```python {4,5}
from nonebot.params import ArgPlainText

View File

@@ -102,17 +102,16 @@ async def got_location(location: str = ArgPlainText()):
</details>
```python {5-10,14-16,18-19}
```python {5-9,13-15,17-18}
from nonebot.rule import to_me
from arclet.alconna import Alconna, Args
from nonebot_plugin_alconna import Match, on_alconna
weather = on_alconna(
Alconna("天气", Args["location?", str]),
aliases={"weather", "天气预报"},
rule=to_me(),
)
weather.shortcut("weather", {"command": "天气"})
weather.shortcut("天气预报", {"command": "天气"})
@weather.handle()
@@ -133,7 +132,7 @@ async def got_location(location: str):
或阅读 [Alconna 基本介绍](./command.md) 一节。
关于更多 `on_alconna` 的使用方法,可参考 [插件文档](https://github.com/nonebot/plugin-alconna/blob/master/docs.md)
或阅读 [响应规则的使用](./matcher.md) 一节。
或阅读 [响应规则的使用](./matcher.mdx) 一节。
## 交流与反馈

View File

@@ -5,7 +5,7 @@ description: Alconna 基本介绍
# Alconna 本体
[`Alconna`](https://github.com/ArcletProject/Alconna) 隶属于 `ArcletProject`,是一个简单、灵活、高效的命令参数解析器, 并且不局限于解析命令式字符串。
[`Alconna`](https://github.com/ArcletProject/Alconna) 隶属于 `ArcletProject`,是一个简单、灵活、高效的命令参数解析器, 并且不局限于解析命令式字符串。
我们通过一个例子来讲解 **Alconna** 的核心 —— `Args`, `Subcommand`, `Option`
@@ -34,9 +34,7 @@ print(res.all_matched_args)
这段代码通过`Alconna`创捷了一个接受主命令名为`pip`, 子命令为`install`且子命令接受一个 **Args** 参数`package`和二个 **Option** 参数`-r``-i`的命令参数解析器, 通过`parse`方法返回解析结果 **Arparma** 的实例。
## 组成
### 命令头
## 命令头
命令头是指命令的前缀 (Prefix) 与命令名 (Command) 的组合,例如 !help 中的 ! 与 help。
@@ -55,15 +53,15 @@ print(res.all_matched_args)
| [123, "foo"] | "bar" | `[123, "bar"]``"foobar"``["foo", "bar"]` | 混合头 |
| [(int, "foo"), (456, "bar")] | "baz" | `[123, "foobaz"]``[456, "foobaz"]``[456, "barbaz"]` | 对头 |
无前缀的类型头此时会将传入的值尝试转为 BasePattern例如 `int` 会转为 `nepattern.INTEGER`此时命令头会匹配对应的类型, 例如 `int` 会匹配 `123` 或 `"456"`,但不会匹配 `"foo"`同时Alconna 会将命令头匹配到的值转为对应的类型,例如 `int` 会将 `"123"` 转为 `123`
对于无前缀的类型头此时会将传入的值尝试转为 BasePattern例如 `int` 会转为 `nepattern.INTEGER`如此该命令头会匹配对应的类型, 例如 `int` 会匹配 `123``"456"`,但不会匹配 `"foo"`解析后Alconna 会将命令头匹配到的值转为对应的类型,例如 `int` 会将 `"123"` 转为 `123`
:::tip
**正则只在命令名上生效,命令前缀中的正则会被转义**
**正则内容只在命令名上生效,前缀中的正则会被转义**
:::
除了通过传入 `re:xxx` 来使用正则表达式外Alconna 还提供了一种更加简洁的方式来使用正则表达式,那就是 Bracket Header
除了通过传入 `re:xxx` 来使用正则表达式外Alconna 还提供了一种更加简洁的方式来使用正则表达式,称为 Bracket Header
```python
from alconna import Alconna
@@ -82,34 +80,34 @@ Bracket Header 类似 python 里的 f-string 写法,通过 "{}" 声明匹配
- "{:\d+}" ⇔ "(\d+)"
- "{foo:int}" ⇔ "(?P&lt;foo&gt;\d+)",其中 "int" 部分若能转为 `BasePattern` 则读取里面的表达式
### 参数声明(Args)
## 参数声明(Args)
`Args` 是用于声明命令参数的组件, 可以通过以下几种方式构造 **Args**
`Args` 是用于声明命令参数的组件, 可以通过以下几种方式构造 **Args**
- `Args[key, var, default][key1, var1, default1][...]`
- `Args[(key, var, default)]`
- `Args.key[var, default]`
其中key **一定**是字符串,而 var 一般为参数的类型default 为具体的值或者 **arclet.alconna.args.Field**
其中key **一定**是字符串,而 var 一般为参数的类型default 为具体的值或者 **arclet.alconna.args.Field**
其与函数签名类似,但是允许含有默认值的参数在前;同时支持 keyword-only 参数不依照构造顺序传入 (但是仍需要在非 keyword-only 参数之后)。
#### key
### key
`key` 的作用是用以标记解析出来的参数并存放于 **Arparma** 中,以方便用户调用。
`key` 的作用是用以标记解析出来的参数并存放于 **Arparma** 中,以方便用户调用。
其有三种为 Args 注解的标识符:  `?``/` `!`, 标识符与 key 之间建议以 `;` 分隔:
其有三种为 Args 注解的标识符: `?``/` `!`, 标识符与 key 之间建议以 `;` 分隔:
- `!` 标识符表示该处传入的参数应**不是**规定的类型,或**不在**指定的值中。
- `?` 标识符表示该参数为**可选**参数,会在无参数匹配时跳过。
- `/` 标识符表示该参数的类型注解需要隐藏。
- `!` 标识符表示该处传入的参数应**不是**规定的类型,或**不在**指定的值中。
- `?` 标识符表示该参数为**可选**参数,会在无参数匹配时跳过。
- `/` 标识符表示该参数的类型注解需要隐藏。
另外,对于参数的注释也可以标记在 `key` 中,其与 key 或者标识符 以 `#` 分割:
`foo#这是注释;?` 或 `foo?#这是注释`
另外,对于参数的注释也可以标记在 `key` 中,其与 key 或者标识符 以 `#` 分割:
`foo#这是注释;?``foo?#这是注释`
:::tip
`Args` 中的 `key` 在实际命令中并不需要传入keyword 参数除外):
`Args` 中的 `key` 在实际命令中并不需要传入keyword 参数除外):
```python
from arclet.alconna import Alconna, Args
@@ -120,7 +118,7 @@ alc.parse("test --foo abc") # 错误
alc.parse("test abc") # 正确
```
若需要 `test --foo abc`,你应该使用 `Option`
若需要 `test --foo abc`,你应该使用 `Option`
```python
from arclet.alconna import Alconna, Args, Option
@@ -131,11 +129,11 @@ alc = Alconna("test", Option("--foo", Args["foo", str]))
:::
#### var
### var
var 负责命令参数的**类型检查**与**类型转化**
`Args` `var`表面上看需要传入一个 `type`,但实际上它需要的是一个 `nepattern.BasePattern` 的实例:
`Args` `var`表面上看需要传入一个 `type`,但实际上它需要的是一个 `nepattern.BasePattern` 的实例:
```python
from arclet.alconna import Args
@@ -146,89 +144,109 @@ from nepattern import BasePattern
args = Args["foo", BasePattern("@\d+")]
```
示例中可以传入 `str` 是因为 `str` 已经注册在了 `nepattern.global_patterns` 中,因此会替换为 `nepattern.global_patterns[str]`
`pip` 示例中可以传入 `str` 是因为 `str` 已经注册在了 `nepattern.global_patterns` 中,因此会替换为 `nepattern.global_patterns[str]`
`nepattern.global_patterns`默认支持的类型有:
- `str`: 匹配任意字符串
- `int`: 匹配整数
- `float`: 匹配浮点数
- `bool`: 匹配 `True` 与 `False` 以及他们小写形式
- `hex`: 匹配 `0x` 开头的十六进制字符串
- `bool`: 匹配 `True``False` 以及他们小写形式
- `hex`: 匹配 `0x` 开头的十六进制字符串
- `url`: 匹配网址
- `email`: 匹配 `xxxx@xxx` 的字符串
- `ipv4`: 匹配 `xxx.xxx.xxx.xxx` 的字符串
- `list`: 匹配类似 `["foo","bar","baz"]` 的字符串
- `dict`: 匹配类似 `{"foo":"bar","baz":"qux"}` 的字符串
- `datetime`: 传入一个 `datetime` 支持的格式字符串,或时间戳
- `email`: 匹配 `xxxx@xxx` 的字符串
- `ipv4`: 匹配 `xxx.xxx.xxx.xxx` 的字符串
- `list`: 匹配类似 `["foo","bar","baz"]` 的字符串
- `dict`: 匹配类似 `{"foo":"bar","baz":"qux"}` 的字符串
- `datetime`: 传入一个 `datetime` 支持的格式字符串,或时间戳
- `Any`: 匹配任意类型
- `AnyString`: 匹配任意类型,转为 `str`
- `Number`: 匹配 `int` 与 `float`,转为 `int`
- `AnyString`: 匹配任意类型,转为 `str`
- `Number`: 匹配 `int``float`,转为 `int`
同时可以使用 typing 中的类型:
- `Literal[X]`: 匹配其中的任意一个值
- `Union[X, Y]`: 匹配其中的任意一个类型
- `Optional[xxx]`: 会自动将默认值设为 `None`,并在解析失败时使用默认值
- `List[X]`: 匹配一个列表,其中的元素为 `X` 类型
- `Dict[X, Y]`: 匹配一个字典,其中的 key 为 `X` 类型value 为 `Y` 类型
- `Optional[xxx]`: 会自动将默认值设为 `None`,并在解析失败时使用默认值
- `List[X]`: 匹配一个列表,其中的元素为 `X` 类型
- `Dict[X, Y]`: 匹配一个字典,其中的 key 为 `X` 类型value 为 `Y` 类型
- ...
:::tip
几类特殊的传入标记:
- `"foo"`: 匹配字符串 "foo" (若没有某个 `BasePattern` 与之关联)
- `RawStr("foo")`: 匹配字符串 "foo" (不会被 `BasePattern` 替换)
- `"foo"`: 匹配字符串 "foo" (若没有某个 `BasePattern` 与之关联)
- `RawStr("foo")`: 匹配字符串 "foo" (即使有 `BasePattern` 与之关联也不会被替换)
- `"foo|bar|baz"`: 匹配 "foo" 或 "bar" 或 "baz"
- `[foo, bar, Baz, ...]`: 匹配其中的任意一个值或类型
- `Callable[[X], Y]`: 匹配一个参数为 `X` 类型的值,并返回通过该函数调用得到的 `Y` 类型的值
- `"re:xxx"`: 匹配一个正则表达式 `xxx`,会返回 Match[0]
- `"rep:xxx"`: 匹配一个正则表达式 `xxx`,会返回 `re.Match` 对象
- `Callable[[X], Y]`: 匹配一个参数为 `X` 类型的值,并返回通过该函数调用得到的 `Y` 类型的值
- `"re:xxx"`: 匹配一个正则表达式 `xxx`,会返回 Match[0]
- `"rep:xxx"`: 匹配一个正则表达式 `xxx`,会返回 `re.Match` 对象
- `{foo: bar, baz: qux}`: 匹配字典中的任意一个键, 并返回对应的值 (特殊的键 ... 会匹配任意的值)
- ...
**特别的**,你可以不传入 `var`,此时会使用 `key` 作为 `var`, 匹配 `key` 字符串。
:::
`MultiVar` 则是一个特殊的标注,用于告知解析器该参数可以接受多个值,其构造方法形如 `MultiVar(str)`。 同样的还有 `KeyWordVar`,其构造方法形如 `KeyWordVar(str)`,用于告知解析器该参数为一个 keyword-only 参数。
#### MultiVar 与 KeyWordVar
`MultiVar` 是一个特殊的标注,用于告知解析器该参数可以接受多个值,类似于函数中的 `*args`,其构造方法形如 `MultiVar(str)`
同样的还有 `KeyWordVar`,类似于函数中的 `*, name: type`,其构造方法形如 `KeyWordVar(str)`,用于告知解析器该参数为一个 keyword-only 参数。
:::tip
`MultiVar` 与 `KeyWordVar` 组合时,代表该参数为一个可接受多个 key-value 的参数,其构造方法形如 `MultiVar(KeyWordVar(str))`
`MultiVar``KeyWordVar` 组合时,代表该参数为一个可接受多个 key-value 的参数,类似于函数中的 `**kwargs`其构造方法形如 `MultiVar(KeyWordVar(str))`
`MultiVar` 与 `KeyWordVar` 也可以传入 `default` 参数,用于指定默认值
`MultiVar``KeyWordVar` 也可以传入 `default` 参数,用于指定默认值
`MultiVar` 不能在 `KeyWordVar` 之后传入
`MultiVar` 不能在 `KeyWordVar` 之后传入
:::
### Option 和 Subcommand
### default
`Option` 可以传入一组 `alias`,如 `Option("--foo|-F|--FOO|-f")` 或 `Option("--foo", alias=["-F"]`
`default` 传入的是该参数的默认值或者 `Field`,以携带对于该参数的更多信息。
传入别名后,`option` 会选择其中长度最长的作为选项名称。若传入为 "--foo|-f",则命令名称为 "--foo"
默认情况下 (即不声明) `default` 的值为特殊值 `Empty`。这也意味着你可以将默认值设置为 `None` 表示默认值为空值。
`Field` 构造需要的参数说明如下:
- default: 参数单元的默认值
- alias: 参数单元默认值的别名
- completion: 参数单元的补全说明生成函数
- unmatch_tips: 参数单元的错误提示生成函数,其接收一个表示匹配失败的元素的参数
- missing_tips: 参数单元的缺失提示生成函数
## 选项与子命令(Option & Subcommand)
`Option``Subcommand` 可以传入一组 `alias`,如 `Option("--foo|-F|--FOO|-f")``Subcommand("foo", alias=["F"])`
传入别名后,选项与子命令会选择其中长度最长的作为其名称。若传入为 "--foo|-f",则命令名称为 "--foo"
:::tip 特别提醒!!!
在 Alconna 中 Option 的名字或别名**没有要求**必须在前面写上 `-`
Option 的名字或别名**没有要求**必须在前面写上 `-`
Option 与 Subcommand 的唯一区别在于 Subcommand 可以传入自己的 **Option****Subcommand**
:::
`Subcommand` 可以传入自己的 **Option** 与 **Subcommand**
他们拥有如下共同参数:
- `help_text`: 传入该组件的帮助信息
- `dest`: 被指定为解析完成时标注匹配结果的标识符,不传入时默认为选项或子命令的名称 (name)
- `requires`: 一段指定顺序的字符串列表,作为唯一的前置序列与命令嵌套替换
对于命令 `test foo bar baz qux <a:int>` 来讲,因为`foo bar baz` 仅需要判断是否相等, 所以可以这么编写:
对于命令 `test foo bar baz qux <a:int>` 来讲,因为`foo bar baz` 仅需要判断是否相等, 所以可以这么编写:
```python
Alconna("test", Option("qux", Args.a[int], requires=["foo", "bar", "baz"]))
```
- `default`: 默认值,在该组件未被解析时使用使用该值替换。
特别的,使用 `OptionResult` 或 `SubcomanndResult` 可以设置包括参数字典在内的默认值:
特别的,使用 `OptionResult``SubcomanndResult` 可以设置包括参数字典在内的默认值:
```python
from arclet.alconna import Option, OptionResult
@@ -237,25 +255,27 @@ opt1 = Option("--foo", default=False)
opt2 = Option("--foo", default=OptionResult(value=False, args={"bar": 1}))
```
`Option` 可以特别设置传入一类 `Action`,作为解析操作
### Action
`Action` 分为三类:
`Option` 可以特别设置传入一类 `Action`,作为解析操作
`Action` 分为三类:
- `store`: 无 Args 时, 仅存储一个值, 默认为 Ellipsis 有 Args 时, 后续的解析结果会覆盖之前的值
- `append`: 无 Args 时, 将多个值存为列表, 默认为 Ellipsis 有 Args 时, 每个解析结果会追加到列表中, 当存在默认值并且不为列表时, 会自动将默认值变成列表, 以保证追加的正确性
- `count`: 无 Args 时, 计数器加一; 有 Args 时, 表现与 STORE 相同, 当存在默认值并且不为数字时, 会自动将默认值变成 1 以保证计数器的正确性。
`Alconna` 提供了预制的几类 `Action`
`Alconna` 提供了预制的几类 `Action`
- `store`(默认)`store_value``store_true``store_false`
- `append``append_value`
- `count`
### Arparma
## 解析结果(Arparma)
`Alconna.parse` 会返回由 **Arparma** 承载的解析结果
`Alconna.parse` 会返回由 **Arparma** 承载的解析结果
`Arparma` 会有如下参数
`Arparma` 有如下属性
- 调试类
@@ -272,22 +292,53 @@ opt2 = Option("--foo", default=OptionResult(value=False, args={"bar": 1}))
- other_args: 除主参数外的其他解析结果
- all_matched_args: 所有 Args 的解析结果
`Arparma` 同时提供了便捷的查询方法 `query[type]()`,会根据传入的 `path` 查找参数并返回
`Arparma` 同时提供了便捷的查询方法 `query[type]()`,会根据传入的 `path` 查找参数并返回
`path` 支持如下:
`path` 支持如下:
- `main_args`, `options`, ...: 返回对应的属性
- `main_args`, `options`, ...: 返回对应的属性
- `args`: 返回 all_matched_args
- `main_args.xxx`, `options.xxx`, ...: 返回字典中 `xxx`键对应的值
- `args.xxx`: 返回 all_matched_args 中 `xxx`键对应的值
- `options.foo`, `foo`: 返回选项 `foo` 的解析结果 (OptionResult)
- `options.foo.value`, `foo.value`: 返回选项 `foo` 的解析值
- `options.foo.args`, `foo.args`: 返回选项 `foo` 的解析参数字典
- `options.foo.args.bar`, `foo.bar`: 返回选项 `foo` 的参数字典中 `bar` 键对应的值 ...
- `main_args.xxx`, `options.xxx`, ...: 返回字典中 `xxx`键对应的值
- `args.xxx`: 返回 all_matched_args 中 `xxx`键对应的值
- `options.foo`, `foo`: 返回选项 `foo` 的解析结果 (OptionResult)
- `options.foo.value`, `foo.value`: 返回选项 `foo` 的解析值
- `options.foo.args`, `foo.args`: 返回选项 `foo` 的解析参数字典
- `options.foo.args.bar`, `foo.bar`: 返回选项 `foo` 的参数字典中 `bar` 键对应的值 ...
## 元数据(CommandMeta)
`Alconna` 的元数据相当于其配置,拥有以下条目:
- `description`: 命令的描述
- `usage`: 命令的用法
- `example`: 命令的使用样例
- `author`: 命令的作者
- `fuzzy_match`: 命令是否开启模糊匹配
- `fuzzy_threshold`: 模糊匹配阈值
- `raise_exception`: 命令是否抛出异常
- `hide`: 命令是否对 manager 隐藏
- `hide_shortcut`: 命令的快捷指令是否在 help 信息中隐藏
- `keep_crlf`: 命令解析时是否保留换行字符
- `compact`: 命令是否允许第一个参数紧随头部
- `strict`: 命令是否严格匹配,若为 False 则未知参数将作为名为 $extra 的参数
- `context_style`: 命令上下文插值的风格None 为关闭bracket 为 {...}parentheses 为 $(...)
- `extra`: 命令的自定义额外信息
元数据一定使用 `meta=...` 形式传入:
```python
from arclet.alconna import Alconna, CommandMeta
alc = Alconna(..., meta=CommandMeta("foo", example="bar"))
```
## 命名空间配置
命名空间配置 (以下简称命名空间) 相当于`Alconna`的设置,`Alconna`默认使用“Alconna”命名空间命名空间有以下几个属性
命名空间配置 (以下简称命名空间) 相当于 `Alconna` 的默认配置,其优先度低于 `CommandMeta`
`Alconna` 默认使用 "Alconna" 命名空间。
命名空间有以下几个属性:
- name: 命名空间名称
- prefixes: 默认前缀配置
@@ -296,9 +347,11 @@ opt2 = Option("--foo", default=OptionResult(value=False, args={"bar": 1}))
- fuzzy_match: 默认是否开启模糊匹配
- raise_exception: 默认是否抛出异常
- builtin_option_name: 默认的内置选项名称(--help, --shortcut, --comp)
- disable_builtin_options: 默认禁用的内置选项(--help, --shortcut, --comp)
- enable_message_cache: 默认是否启用消息缓存
- compact: 默认是否开启紧凑模式
- strict: 命令是否严格匹配
- context_style: 命令上下文插值的风格
- ...
### 新建命名空间并替换
@@ -307,7 +360,7 @@ opt2 = Option("--foo", default=OptionResult(value=False, args={"bar": 1}))
from arclet.alconna import Alconna, namespace, Namespace, Subcommand, Args, config
ns = Namespace("foo", prefixes=["/"])  # 创建 "foo"命名空间配置, 它要求创建的Alconna的主命令前缀必须是/
ns = Namespace("foo", prefixes=["/"]) # 创建 "foo"命名空间配置, 它要求创建的Alconna的主命令前缀必须是/
alc = Alconna("pip", Subcommand("install", Args["package", str]), namespace=ns) # 在创建Alconna时候传入命名空间以替换默认命名空间
@@ -342,15 +395,15 @@ with namespace(config.default_namespace.name) as np:
快捷命令可以做到标识一段命令, 并且传递参数给原命令
一般情况下你可以通过 `Alconna.shortcut` 进行快捷指令操作 (创建,删除)
一般情况下你可以通过 `Alconna.shortcut` 进行快捷指令操作 (创建,删除)
`shortcut` 的第一个参数为快捷指令名称,第二个参数为 `ShortcutArgs`,作为快捷指令的配置:
`shortcut` 的第一个参数为快捷指令名称,第二个参数为 `ShortcutArgs`,作为快捷指令的配置:
```python
class ShortcutArgs(TypedDict):
"""快捷指令参数"""
command: NotRequired[DataCollection[Any]]
command: NotRequired[str]
"""快捷指令的命令"""
args: NotRequired[list[Any]]
"""快捷指令的附带参数"""
@@ -358,6 +411,10 @@ class ShortcutArgs(TypedDict):
"""是否允许命令后随参数"""
prefix: NotRequired[bool]
"""是否调用时保留指令前缀"""
wrapper: NotRequired[ShortcutRegWrapper]
"""快捷指令的正则匹配结果的额外处理函数"""
humanized: NotRequired[str]
"""快捷指令的人类可读描述"""
```
### args的使用
@@ -400,28 +457,28 @@ alc.parse("echo hello world!")
# hello world!
```
 `fuzzy` 为 False 时,第一个例子中传入 `"涩图1张 abc"` 之类的快捷指令将视为解析失败
`fuzzy` 为 False 时,第一个例子中传入 `"涩图1张 abc"` 之类的快捷指令将视为解析失败
快捷指令允许三类特殊的 placeholder
- `{%X}`: 如 `setu {%0}`,表示此处填入快捷指令后随的第 X 个参数。
- `{%X}`: 如 `setu {%0}`,表示此处填入快捷指令后随的第 X 个参数。
例如,若快捷指令为 `涩图`, 配置为 `{"command": "setu {%0}"}`, 则指令 `涩图 1` 相当于 `setu 1`
例如,若快捷指令为 `涩图`, 配置为 `{"command": "setu {%0}"}`, 则指令 `涩图 1` 相当于 `setu 1`
- `{*}`: 表示此处填入所有后随参数,并且可以通过 `{*X}` 的方式指定组合参数之间的分隔符。
- `{*}`: 表示此处填入所有后随参数,并且可以通过 `{*X}` 的方式指定组合参数之间的分隔符。
- `{X}`: 表示此处填入可能的正则匹配的组:
- `command` 中存在匹配组 `(xxx)`,则 `{X}` 表示第 X 个匹配组的内容
- `command` 中存储匹配组 `(?P<xxx>...)`, 则 `{X}` 表示 **名字** 为 X 的匹配结果
- `command` 中存在匹配组 `(xxx)`,则 `{X}` 表示第 X 个匹配组的内容
- `command` 中存储匹配组 `(?P<xxx>...)`, 则 `{X}` 表示 **名字** 为 X 的匹配结果
除此之外, 通过 **Alconna** 内置选项 `--shortcut` 可以动态操作快捷指令
除此之外, 通过 **Alconna** 内置选项 `--shortcut` 可以动态操作快捷指令
例如:
- `cmd --shortcut <key> <cmd>` 来增加一个快捷指令
- `cmd --shortcut list` 来列出当前指令的所有快捷指令
- `cmd --shortcut delete key` 来删除一个快捷指令
- `cmd --shortcut <key> <cmd>` 来增加一个快捷指令
- `cmd --shortcut list` 来列出当前指令的所有快捷指令
- `cmd --shortcut delete key` 来删除一个快捷指令
```python
from arclet.alconna import Alconna, Args
@@ -437,7 +494,7 @@ alc.parse("eval --shortcut list")
## 紧凑命令
`Alconna`,  `Option` 与 `Subcommand` 可以设置 `compact=True` 使得解析命令时允许名称与后随参数之间没有分隔:
`Alconna`, `Option``Subcommand` 可以设置 `compact=True` 使得解析命令时允许名称与后随参数之间没有分隔:
```python
from arclet.alconna import Alconna, Option, CommandMeta, Args
@@ -459,7 +516,7 @@ print(alc.parse("gcc -Fabc -Fdef -Fxyz").query[list]("flag.content"))
# ['abc', 'def', 'xyz']
```
 `Option` 的 `action` 为 `count` 时,其自动支持 `compact` 特性:
`Option``action``count` 时,其自动支持 `compact` 特性:
```python
from arclet.alconna import Alconna, Option, count
@@ -472,8 +529,6 @@ print(alc.parse("pp -vvv").query[int]("verbose.value"))
## 模糊匹配
模糊匹配通过在 Alconna 中设置其 CommandMeta 开启
模糊匹配会应用在任意需要进行名称判断的地方,如 **命令名称****选项名称** 和 **参数名称** (如指定需要传入参数名称)。
```python
@@ -490,7 +545,7 @@ alc.parse("test_fuzy")
半自动补全为用户提供了推荐后续输入的功能
补全默认通过 `--comp` 或 `-cp` 或 `?` 触发:(命名空间配置可修改名称)
补全默认通过 `--comp``-cp``?` 触发:(命名空间配置可修改名称)
```python
from arclet.alconna import Alconna, Args, Option
@@ -515,9 +570,9 @@ output
## Duplication
**Duplication** 用来提供更好的自动补全,类似于 **ArgParse** 的 **Namespace**
**Duplication** 用来提供更好的自动补全,类似于 **ArgParse****Namespace**
普通情况下使用,需要利用到 **ArgsStub**、**OptionStub** 和 **SubcommandStub** 三个部分
普通情况下使用,需要利用到 **ArgsStub**、**OptionStub****SubcommandStub** 三个部分
以pip为例其对应的 Duplication 应如下构造:
@@ -526,20 +581,20 @@ from arclet.alconna import Alconna, Args, Option, OptionResult, Duplication, Sub
class MyDup(Duplication):
    verbose: OptionResult
    install: SubcommandStub
verbose: OptionResult
install: SubcommandStub
alc = Alconna(
    "pip",
    Subcommand(
        "install",
        Args["package", str],
        Option("-r|--requirement", Args["file", str]),
        Option("-i|--index-url", Args["url", str]),
    ),
    Option("-v|--version"),
    Option("-v|--verbose", action=count),
"pip",
Subcommand(
"install",
Args["package", str],
Option("-r|--requirement", Args["file", str]),
Option("-i|--index-url", Args["url", str]),
),
Option("-v|--version"),
Option("-v|--verbose", action=count),
)
res = alc.parse("pip -v install ...") # 不使用duplication获得的提示较少
@@ -551,7 +606,7 @@ print(result.install)
# SubcommandStub(_origin=Subcommand('install', args=Args('package': str)), _value=Ellipsis, available=True, args=ArgsStub(_origin=Args('package': str), _value={'package': '...'}, available=True), dest='install', options=[OptionStub(_origin=Option('requirement', args=Args('file': str)), _value=None, available=False, args=ArgsStub(_origin=Args('file': str), _value={}, available=False), dest='requirement', aliases=['r', 'requirement'], name='requirement'), OptionStub(_origin=Option('index-url', args=Args('url': str)), _value=None, available=False, args=ArgsStub(_origin=Args('url': str), _value={}, available=False), dest='index-url', aliases=['index-url', 'i'], name='index-url')], subcommands=[], name='install')
```
**Duplication** 也可以如 **Namespace** 一样直接标明参数名称和类型:
**Duplication** 也可以如 **Namespace** 一样直接标明参数名称和类型:
```python
from typing import Optional
@@ -563,3 +618,23 @@ class MyDup(Duplication):
file: Optional[str] = None
url: Optional[str] = None
```
## 上下文插值
`context_style` 条目被设置后,传入的命令中符合上下文插值的字段会被自动替换成当前上下文中的信息。
上下文可以在 `parse` 中传入:
```python
from arclet.alconna import Alconna, Args, CommandMeta
alc = Alconna("test", Args["foo", int], meta=CommandMeta(context_style="parentheses"))
alc.parse("test $(bar)", {"bar": 123})
# {"foo": 123}
```
context_style 的值分两种:
- `"bracket"`: 插值格式为 `{...}`,例如 `{foo}`
- `"parentheses"`: 插值格式为 `$(...)`,例如 `$(bar)`

View File

@@ -46,3 +46,31 @@ description: 配置项
- **默认值**: `[]`
全局加载的扩展,路径以 . 分隔,如 `foo.bar.baz:DemoExtension`
## alconna_context_style
- **类型**: `Optional[Literal["bracket", "parentheses"]]`
- **默认值**: `None`
全局命令上下文插值的风格None 为关闭bracket 为 `{...}`parentheses 为 `$(...)`
## alconna_enable_saa_patch
- **类型**: `bool`
- **默认值**: `False`
是否启用 SAA 补丁。
## alconna_apply_filehost
- **类型**: `bool`
- **默认值**: `False`
是否启用文件托管。
## alconna_apply_fetch_targets
- **类型**: `bool`
- **默认值**: `False`
是否启动时拉取一次发送对象列表。

View File

@@ -3,13 +3,14 @@ sidebar_position: 3
description: 响应规则的使用
---
import Messenger from "@site/src/components/Messenger";
# Alconna 插件
展示:
```python
from nonebot_plugin_alconna.adapters.onebot12 import Image
from nonebot_plugin_alconna import At, on_alconna
from nonebot_plugin_alconna import At, Image, on_alconna
from arclet.alconna import Args, Option, Alconna, Arparma, MultiVar, Subcommand
@@ -30,8 +31,8 @@ rg = on_alconna(alc, auto_send_output=True)
@rg.handle()
async def _(result: Arparma):
if result.find("list"):
img = await ob12_gen_role_group_list_image()
await rg.finish(Image(img))
img: bytes = await gen_role_group_list_image()
await rg.finish(Image(raw=img))
if result.find("add"):
group = await create_role_group(result.query[str]("add.name"))
if result.find("add.member"):
@@ -42,7 +43,7 @@ async def _(result: Arparma):
## 响应器使用
本插件基于 **Alconna**,为 **Nonebot** 提供了一类新的事件响应器辅助函数 `on_alconna`
本插件基于 **Alconna**,为 **Nonebot** 提供了一类新的事件响应器辅助函数 `on_alconna`
```python
def on_alconna(
@@ -61,10 +62,10 @@ def on_alconna(
):
```
- `command`: Alconna 命令或字符串,字符串将通过 `AlconnaFormat` 转换为 Alconna 命令
- `command`: Alconna 命令或字符串,字符串将通过 `AlconnaFormat` 转换为 Alconna 命令
- `skip_for_unmatch`: 是否在命令不匹配时跳过该响应
- `auto_send_output`: 是否自动发送输出信息并跳过响应
- `aliases`: 命令别名, 作用类似于 `on_command` 中的 aliases
- `aliases`: 命令别名, 作用类似于 `on_command` 中的 aliases
- `comp_config`: 补全会话配置, 不传入则不启用补全会话
- `extensions`: 需要加载的匹配扩展, 可以是扩展类或扩展实例
- `exclude_ext`: 需要排除的匹配扩展, 可以是扩展类或扩展的id
@@ -72,14 +73,14 @@ def on_alconna(
- `use_cmd_start`: 是否使用 COMMAND_START 作为命令前缀
- `use_cmd_sep`: 是否使用 COMMAND_SEP 作为命令分隔符
`on_alconna` 返回的是 `Matcher` 的子类 `AlconnaMatcher` ,其拓展了如下方法:
`on_alconna` 返回的是 `Matcher` 的子类 `AlconnaMatcher` ,其拓展了如下方法:
- `.assign(path, value, or_not)`: 用于对包含多个选项/子命令的命令的分派处理(具体请看[条件控制](./matcher.md#条件控制)
- `.got_path(path, prompt, middleware)`: 在 `got` 方法的基础上,会以 path 对应的参数为准,读取传入 message 的最后一个消息段并验证转换
- `.set_path_arg(key, value)`, `.get_path_arg(key)`: 类似 `set_arg` 和 `got_arg`,为 `got_path` 的特化版本
- `.reject_path(path[, prompt, fallback])`: 类似于 `reject_arg`,对应 `got_path`
- `.dispatch`: 同样的分派处理,但是是类似 `CommandGroup` 一样返回新的 `AlconnaMatcher`
- `.got`, `send`, `reject`, ... : 拓展了 prompt 类型,即支持使用 `UniMessage` 作为 prompt
- `.assign(path, value, or_not)`: 用于对包含多个选项/子命令的命令的分派处理(具体请看[条件控制](./matcher.mdx#条件控制)
- `.got_path(path, prompt, middleware)`: 在 `got` 方法的基础上,会以 path 对应的参数为准,读取传入 message 的最后一个消息段并验证转换
- `.set_path_arg(key, value)`, `.get_path_arg(key)`: 类似 `set_arg``got_arg`,为 `got_path` 的特化版本
- `.reject_path(path[, prompt, fallback])`: 类似于 `reject_arg`,对应 `got_path`
- `.dispatch`: 同样的分派处理,但是是类似 `CommandGroup` 一样返回新的 `AlconnaMatcher`
- `.got`, `send`, `reject`, ... : 拓展了 prompt 类型,即支持使用 `UniMessage` 作为 prompt
实例:
@@ -88,7 +89,7 @@ from nonebot import require
require("nonebot_plugin_alconna")
from arclet.alconna import Alconna, Option, Args
from nonebot_plugin_alconna import on_alconna, AlconnaMatch, Match, UniMessage
from nonebot_plugin_alconna import on_alconna, Match, UniMessage
login = on_alconna(Alconna(["/"], "login", Args["password?", str], Option("-r|--recall"))) # 这里["/"]指命令前缀必须是/
@@ -96,13 +97,13 @@ login = on_alconna(Alconna(["/"], "login", Args["password?", str], Option("-r|--
# /login -r 触发
@login.assign("recall")
async def login_exit():
    await login.finish("已退出")
await login.finish("已退出")
# /login xxx 触发
@login.assign("password")
async def login_handle(pw: Match[str] = AlconnaMatch("password")):
    if pw.available:
        login.set_path_arg("password", pw.result)
async def login_handle(pw: Match[str]):
if pw.available:
login.set_path_arg("password", pw.result)
# /login 触发
@login.got_path("password", prompt=UniMessage.template("{:At(user, $event.get_user_id())} 请输入密码"))
@@ -115,24 +116,24 @@ async def login_got(password: str):
本插件提供了一系列依赖注入函数,便于在响应函数中获取解析结果:
- `AlconnaResult`: `CommandResult` 类型的依赖注入函数
- `AlconnaMatches`: `Arparma` 类型的依赖注入函数
- `AlconnaDuplication`: `Duplication` 类型的依赖注入函数
- `AlconnaMatch`: `Match` 类型的依赖注入函数
- `AlconnaQuery`: `Query` 类型的依赖注入函数
- `AlconnaResult`: `CommandResult` 类型的依赖注入函数
- `AlconnaMatches`: `Arparma` 类型的依赖注入函数
- `AlconnaDuplication`: `Duplication` 类型的依赖注入函数
- `AlconnaMatch`: `Match` 类型的依赖注入函数
- `AlconnaQuery`: `Query` 类型的依赖注入函数
同时,基于 [`Annotated` 支持](https://github.com/nonebot/nonebot2/pull/1832),添加了两类注解:
同时,基于 [`Annotated` 支持](https://github.com/nonebot/nonebot2/pull/1832),添加了两类注解:
- `AlcMatches`:同 `AlconnaMatches`
- `AlcResult`:同 `AlconnaResult`
- `AlcMatches`:同 `AlconnaMatches`
- `AlcResult`:同 `AlconnaResult`
可以看到,本插件提供了几类额外的模型:
- `CommandResult`: 解析结果,包括了源命令 `source: Alconna` ,解析结果 `result: Arparma`,以及可能的输出信息 `output: str | None` 字段
- `Match`: 匹配项,表示参数是否存在于 `all_matched_args` 内,可用 `Match.available` 判断是否匹配,`Match.result` 获取匹配的值
- `Query`: 查询项,表示参数是否可由 `Arparma.query` 查询并获得结果,可用 `Query.available` 判断是否查询成功,`Query.result` 获取查询结果
- `CommandResult`: 解析结果,包括了源命令 `source: Alconna` ,解析结果 `result: Arparma`,以及可能的输出信息 `output: str | None` 字段
- `Match`: 匹配项,表示参数是否存在于 `all_matched_args` 内,可用 `Match.available` 判断是否匹配,`Match.result` 获取匹配的值
- `Query`: 查询项,表示参数是否可由 `Arparma.query` 查询并获得结果,可用 `Query.available` 判断是否查询成功,`Query.result` 获取查询结果
**Alconna** 默认依赖注入的目标参数皆不需要使用依赖注入函数, 该效果对于 `AlconnaMatcher.got_path` 下的 Arg 同样有效:
**Alconna** 默认依赖注入的目标参数皆不需要使用依赖注入函数, 该效果对于 `AlconnaMatcher.got_path` 下的 Arg 同样有效:
```python
async def handle(
@@ -224,8 +225,8 @@ async def handle_test4(qux: Query[bool] = Query("baz.qux", False)):
| [飞书](https://github.com/nonebot/adapter-feishu) | adapters.feishu |
| [GitHub](https://github.com/nonebot/adapter-github) | adapters.github |
| [QQ bot](https://github.com/nonebot/adapter-qq) | adapters.qq |
| [QQ 频道 bot](https://github.com/nonebot/adapter-qq) | adapters.qqguild |
| [钉钉](https://github.com/nonebot/adapter-ding) | adapters.ding |
| [Dodo](https://github.com/nonebot/adapter-dodo) | adapters.dodo |
| [Console](https://github.com/nonebot/adapter-console) | adapters.console |
| [开黑啦](https://github.com/Tian-que/nonebot-adapter-kaiheila) | adapters.kook |
| [Mirai](https://github.com/ieew/nonebot_adapter_mirai2) | adapters.mirai |
@@ -233,7 +234,6 @@ async def handle_test4(qux: Query[bool] = Query("baz.qux", False)):
| [MineCraft](https://github.com/17TheWord/nonebot-adapter-minecraft) | adapters.minecraft |
| [BiliBili Live](https://github.com/wwweww/adapter-bilibili) | adapters.bilibili |
| [Walle-Q](https://github.com/onebot-walle/nonebot_adapter_walleq) | adapters.onebot12 |
| [Villa](https://github.com/CMHopeSunshine/nonebot-adapter-villa) | adapters.villa |
| [Discord](https://github.com/nonebot/adapter-discord) | adapters.discord |
| [Red 协议](https://github.com/nonebot/adapter-red) | adapters.red |
| [Satori 协议](https://github.com/nonebot/adapter-satori) | adapters.satori |
@@ -341,7 +341,7 @@ async def tt(target: Union[str, At]):
## 响应器创建装饰
本插件提供了一个 `funcommand` 装饰器, 其用于将一个接受任意参数, 返回 `str` 或 `Message` 或 `MessageSegment` 的函数转换为命令响应器:
本插件提供了一个 `funcommand` 装饰器, 其用于将一个接受任意参数, 返回 `str``Message``MessageSegment` 的函数转换为命令响应器:
```python
from nonebot_plugin_alconna import funcommand
@@ -369,7 +369,7 @@ async def echo_exit(msg: Match[str] = AlconnaMatch("msg")):
## 类Koishi构造器
本插件提供了一个 `Command` 构造器,其基于 `arclet.alconna.tools` 中的 `AlconnaString` 以类似 `Koishi` 中注册命令的方式来构建一个 **AlconnaMatcher**
本插件提供了一个 `Command` 构造器,其基于 `arclet.alconna.tools` 中的 `AlconnaString` 以类似 `Koishi` 中注册命令的方式来构建一个 **AlconnaMatcher**
```python
from nonebot_plugin_alconna import Command, Arparma
@@ -389,7 +389,7 @@ async def _(arp: Arparma):
await book.send(str(arp.options))
```
甚至,你可以设置 `action` 来设定响应行为:
甚至,你可以设置 `action` 来设定响应行为:
```python
book = (
@@ -403,9 +403,9 @@ book = (
)
```
## 返回值回调
## 返回值中间件
 `AlconnaMatch``AlconnaQuery` 或 `got_path` 中,你可以使用 `middleware` 参数来传入一个对返回值进行处理的函数:
`AlconnaMatch``AlconnaQuery``got_path` 中,你可以使用 `middleware` 参数来传入一个对返回值进行处理的函数:
```python
from nonebot_plugin_alconna import image_fetch
@@ -422,13 +422,13 @@ async def mask_h(matcher: AlconnaMatcher, img: Match[bytes] = AlconnaMatch("img"
await matcher.send(result.content)
```
其中,`image_fetch` 是一个中间件,其接受一个 `Image` 对象,并提取图片的二进制数据返回。
其中,`image_fetch` 是一个中间件,其接受一个 `Image` 对象,并提取图片的二进制数据返回。
## 匹配拓展
本插件提供了一个 `Extension` 类,其用于自定义 AlconnaMatcher 的部分行为
本插件提供了一个 `Extension` 类,其用于自定义 AlconnaMatcher 的部分行为
例如 `LLMExtension` (仅举例)
例如一个 `LLMExtension` 可以如下实现 (仅举例)
```python
from nonebot_plugin_alconna import Extension, Alconna, on_alconna, Interface
@@ -467,14 +467,15 @@ matcher = on_alconna(
...
```
那么添加了 `LLMExtension` 的响应器便能接受任何能通过 llm 翻译为具体命令的自然语言消息,同时可以在响应器中为所有 `llm` 参数注入模型变量。
那么添加了 `LLMExtension` 的响应器便能接受任何能通过 llm 翻译为具体命令的自然语言消息,同时可以在响应器中为所有 `llm` 参数注入模型变量。
目前 `Extension` 的功能有:
目前 `Extension` 的功能有:
- `validate`: 对于事件的来源适配器或 bot 选择是否接受响应
- `output_converter`: 输出信息的自定义转换方法
- `message_provider`: 从传入事件中自定义提取消息的方法
- `receive_provider`: 对传入的消息 (Message 或 UniMessage) 的额外处理
- `context_provider`: 对命令上下文的额外处理
- `permission_check`: 命令对消息解析并确认头部匹配(即确认选择响应)时对发送者的权限判断
- `parse_wrapper`: 对命令解析结果的额外处理
- `send_wrapper`: 对发送的消息 (Message 或 UniMessage) 的额外处理
@@ -482,11 +483,11 @@ matcher = on_alconna(
- `catch`: 自定义依赖注入处理函数
- `post_init`: 响应器创建后对命令对象的额外处理
例如内置的 `DiscordSlashExtension`,其可自动将 Alconna 对象翻译成 slash 指令并注册,且将收到的指令交互事件转为指令供命令解析:
例如内置的 `DiscordSlashExtension`,其可自动将 Alconna 对象翻译成 slash 指令并注册,且将收到的指令交互事件转为指令供命令解析:
```python
from nonebot_plugin_alconna import Match, on_alconna
from nonebot_plugin_alconna.adapters.discord import DiscordSlashExtension
from nonebot_plugin_alconna.builtins.plugins.discord import DiscordSlashExtension
alc = Alconna(
@@ -508,8 +509,99 @@ async def remove(plugin: Match[str], time: Match[int]):
await matcher.finish(f"removed {plugin.result} with {time.result if time.available else -1}")
```
目前插件提供了 4 个内置的 `Extension`,它们在 `nonebot_plugin_alconna.builtins.extensions` 下:
- `ReplyRecordExtension`: 将消息事件中的回复暂存在 extension 中,使得解析用的消息不带回复信息,同时可以在后续的处理中获取回复信息。
- `DiscordSlashExtension`: 将 Alconna 的命令自动转换为 Discord 的 Slash Command并将 Slash Command 的交互事件转换为消息交给 Alconna 处理。
- `MarkdownOutputExtension`: 将 Alconna 的自动输出转换为 Markdown 格式
- `TelegramSlashExtension`: 将 Alconna 的命令注册在 Telegram 上以获得提示。
:::tip
全局的 Extension 可延迟加载 (即若有全局拓展加载于部分 AlconnaMatcher 之后,这部分响应器会被追加拓展)
:::
## 补全会话
补全会话基于 [`半自动补全`](./command.md#半自动补全),用于指令参数缺失或参数错误时给予交互式提示,类似于 `got-reject`
```python
from nonebot_plugin_alconna import Alconna, Args, Field, At, on_alconna
alc = Alconna(
"添加教师",
Args["name", str, Field(completion=lambda: "请输入姓名")],
Args["phone", int, Field(completion=lambda: "请输入手机号")],
Args["at", [str, At], Field(completion=lambda: "请输入教师号")],
)
cmd = on_alconna(alc, comp_config={"lite": True}, skip_for_unmatch=False)
@cmd.handle()
async def handle(result: Arparma):
cmd.finish("添加成功")
```
此时,当用户输入 `添加教师` 时,会自动提示用户输入姓名,手机号和教师号,用户输入后会自动进入下一个提示:
<Messenger
msgs={[
{ position: "right", msg: "添加教师" },
{ position: "left", msg: "以下是建议的输入: \n- name: 请输入姓名" },
{ position: "right", msg: "foo" },
{ position: "left", msg: "以下是建议的输入: \n- phone: 请输入手机号" },
{ position: "right", msg: "12345" },
{ position: "left", msg: "以下是建议的输入: \n- at: 请输入教师号" },
{ position: "right", msg: "@me" },
{ position: "left", msg: "添加成功" },
]}
/>
补全会话配置如下:
```python
class CompConfig(TypedDict):
tab: NotRequired[str]
"""用于切换提示的指令的名称"""
enter: NotRequired[str]
"""用于输入提示的指令的名称"""
exit: NotRequired[str]
"""用于退出会话的指令的名称"""
timeout: NotRequired[int]
"""超时时间"""
hide_tabs: NotRequired[bool]
"""是否隐藏所有提示"""
hides: NotRequired[Set[Literal["tab", "enter", "exit"]]]
"""隐藏的指令"""
disables: NotRequired[Set[Literal["tab", "enter", "exit"]]]
"""禁用的指令"""
lite: NotRequired[bool]
"""是否使用简洁版本的补全会话(相当于同时配置 disables、hides、hide_tabs"""
```
## 内置插件
类似于 Nonebot 本身提供的内置插件,`nonebot_plugin_alconna` 提供了两个内置插件:`echo` 和 `help`。
你可以用本插件的 `load_builtin_plugin(s)` 来加载它们:
```python
from nonebot_plugin_alconna import load_builtin_plugins
load_builtin_plugins("echo", "help")
```
其中 `help` 仅能列出所有 Alconna 指令。
<Messenger
msgs={[
{ position: "right", msg: "/帮助" },
{
position: "left",
msg: "# 当前可用的命令有:\n 0 /echo : echo 指令\n 1 /help : 显示所有命令帮助\n# 输入'命令名 -h|--help' 查看特定命令的语法",
},
{ position: "right", msg: "/echo [图片]" },
{ position: "left", msg: "[图片]" },
]}
/>

View File

@@ -12,27 +12,30 @@ import TabItem from "@theme/TabItem";
## 通用消息段
适配器下的消息段标注会匹配适配器特定的 `MessageSegment` 而通用消息段与适配器消息段的区别在于:
通用消息段会匹配多个适配器中相似类型的消息段,并返回 `uniseg` 模块中定义的 [`Segment` 模型](https://nonebot.dev/docs/next/best-practice/alconna/utils#%E9%80%9A%E7%94%A8%E6%B6%88%E6%81%AF%E6%AE%B5), 以达到**跨平台接收消息**的作用。
适配器下的消息段标注会匹配适配器特定的 `MessageSegment` 而通用消息段与适配器消息段的区别在于:
通用消息段会匹配多个适配器中相似类型的消息段,并返回 `uniseg` 模块中定义的 [`Segment` 模型](https://nonebot.dev/docs/next/best-practice/alconna/utils#%E9%80%9A%E7%94%A8%E6%B6%88%E6%81%AF%E6%AE%B5), 以达到**跨平台接收消息**的作用。
`nonebot-plugin-alconna.uniseg` 提供了类似 `MessageSegment` 的通用消息段,并可在 `Alconna` 下直接标注使用:
```python
class Segment:
"""基类标注"""
children: List["Segment"]
class Text(Segment):
"""Text对象, 表示一类文本元素"""
text: str
style: Optional[str]
styles: Dict[Tuple[int, int], List[str]]
class At(Segment):
"""At对象, 表示一类提醒某用户的元素"""
type: Literal["user", "role", "channel"]
flag: Literal["user", "role", "channel"]
target: str
display: Optional[str]
class AtAll(Segment):
"""AtAll对象, 表示一类提醒所有人的元素"""
here: bool
class Emoji(Segment):
"""Emoji对象, 表示一类表情元素"""
@@ -42,17 +45,23 @@ class Emoji(Segment):
class Media(Segment):
url: Optional[str]
id: Optional[str]
path: Optional[str]
raw: Optional[bytes]
path: Optional[Union[str, Path]]
raw: Optional[Union[bytes, BytesIO]]
mimetype: Optional[str]
name: str
to_url: ClassVar[Optional[MediaToUrl]]
class Image(Media):
"""Image对象, 表示一类图片元素"""
class Audio(Media):
"""Audio对象, 表示一类音频元素"""
duration: Optional[int]
class Voice(Media):
"""Voice对象, 表示一类语音元素"""
duration: Optional[int]
class Video(Media):
"""Video对象, 表示一类视频元素"""
@@ -73,17 +82,41 @@ class Reference(Segment):
"""Reference对象表示一类引用消息。转发消息 (Forward) 也属于此类"""
id: Optional[str]
"""此处不一定是消息ID可能是其他ID如消息序号等"""
content: Optional[Union[Message, str, List[Union[RefNode, CustomNode]]]]
children: List[Union[RefNode, CustomNode]]
class Card(Segment):
type: Literal["xml", "json"]
raw: str
class Hyper(Segment):
"""Hyper对象表示一类超级消息。如卡片消息、ark消息、小程序等"""
format: Literal["xml", "json"]
raw: Optional[str]
content: Optional[Union[dict, list]]
class Other(Segment):
"""其他 Segment"""
origin: MessageSegment
```
此类消息段通过 `UniMessage.export` 可以转为特定的 `MessageSegment`
:::tips
或许你注意到了 `Segment` 上有一个 `children` 属性。
这是因为在 [`Satori`](https://satori.js.org/zh-CN/) 协议的规定下,一类元素可以用其子元素来代表一类兼容性消息
例如qq 的商场表情在某些平台上可以用图片代替)。
为此,本插件提供了两种方式来表达 "获取子元素" 的方法:
```python
from nonebot_plugin_alconna.builtins.uniseg.chronocat import MarketFace
from nonebot_plugin_alconna import Args, Image, Alconna, select, select_first
# 表示这个指令需要的图片要么直接是 Image 要么是在 MarketFace 元素内的 Image
alc1 = Alconna("make_meme", Args["img", [Image, Image.from_(MarketFace)]])
# 表示这个指令需要的图片会在目标元素下进行搜索,将所有符合 Image 的元素选出来并将第一个作为结果
alc2 = Alconna("make_meme", Args["img", select(Image, index=0)]) # 也可以使用 select_first(Image)
```
:::
## 通用消息序列
@@ -127,6 +160,7 @@ matcher = on_xxx(...)
@matcher.handle()
async def _(message: Message = EventMessage()):
msg = await UniMessage.generate(message=message)
msg1 = UniMessage.generate_without_reply(message=message)
```
</TabItem>
@@ -148,6 +182,21 @@ async def handle_test():
await test.send(await UniMessage(Image(path="path/to/img")).export())
```
除此之外 `UniMessage.send` 方法基于 `UniMessage.export` 并调用各适配器下的发送消息方法,返回一个 `Receipt` 对象,用于修改/撤回消息:
```python
from nonebot import Bot, on_command
from nonebot_plugin_alconna.uniseg import UniMessage
test = on_command("test")
@test.handle()
async def handle():
receipt = await UniMessage.text("hello!").send(at_sender=True, reply_to=True)
await receipt.recall(delay=1)
```
而在 `AlconnaMatcher` 下,`got`, `send`, `reject` 等可以发送消息的方法皆支持使用 `UniMessage`,不需要手动调用 export 方法:
```python
@@ -168,24 +217,9 @@ async def tt(target: At):
await test_cmd.send(UniMessage([target, "\ndone."]))
```
除此之外 `UniMessage.send` 方法基于 `UniMessage.export` 并调用各适配器下的发送消息方法,返回一个 `Receipt` 对象,用于修改/撤回消息:
```python
from nonebot import Bot, on_command
from nonebot_plugin_alconna.uniseg import UniMessage
test = on_command("test")
@test.handle()
async def handle():
receipt = await UniMessage.text("hello!").send(at_sender=True, reply_to=True)
await receipt.recall(delay=1)
```
:::caution
在响应器以外的地方,`bot` 参数必须手动传入。
在响应器以外的地方,除非启用了 `alconna_apply_fetch_targets` 配置项,否则 `bot` 参数必须手动传入。
:::
@@ -391,7 +425,7 @@ message.count(Text) == 2
message.get(Text, 1) == UniMessage([Text("test1")])
```
### 消息发送
## 消息发送
前面提到,通用消息可用 `UniMessage.send` 发送自身:
@@ -408,6 +442,25 @@ async def send(
实际上,`UniMessage` 同时提供了获取消息事件 id 与消息发送对象的方法:
<Tabs groupId="get_unimsg">
<TabItem value="depend" label="使用依赖注入">
通过提供的 `MessageTarget`, `MessageId` 或 `MsgTarget`, `MsgId` 依赖注入器来获取消息事件 id 与消息发送对象。
```python
from nonebot_plugin_alconna.uniseg import MessageId, MsgTarget
matcher = on_xxx(...)
@matcher.handle()
asycn def _(target: MsgTarget, msg_id: MessageId):
...
```
</TabItem>
<TabItem value="method" label="使用 UniMessage 的方法">
```python
from nonebot import Event, Bot
from nonebot_plugin_alconna.uniseg import UniMessage, Target
@@ -422,24 +475,116 @@ asycn def _(bot: Bot, event: Event):
```
</TabItem>
</Tabs>
`send`, `get_target`, `get_message_id` 中与 `event`, `bot` 相关的参数都会尝试从上下文中获取对象。
其中,`Target`:
### 消息发送对象
消息发送对象是用来描述响应消息时的发送对象或者主动发送消息时的目标对象的对象,它包含了以下属性:
```python
class Target:
id: str
"""目标id若为群聊则为group_id或者channel_id若为私聊则为user_id"""
parent_id: str = ""
"""父级id若为频道则为guild_id其他情况为空字符串"""
channel: bool = False
"""是否为频道,仅当目标平台同时支持群聊和频道时有效"""
private: bool = False
parent_id: str
"""父级id若为频道则为guild_id其他情况下可能为空字符串(例如 Feishu 下可作为部门 id"""
channel: bool
"""是否为频道,仅当目标平台符合频道概念时"""
private: bool
"""是否为私聊"""
source: str = ""
source: str
"""可能的事件id"""
self_id: Union[str, None]
"""机器人id若为 None 则 Bot 对象会随机选择"""
selector: Union[Callable[[Bot], Awaitable[bool]], None]
"""选择器,用于在多个 Bot 对象中选择特定 Bot"""
extra: Dict[str, Any]
"""额外信息,用于适配器扩展"""
```
是用来描述响应消息时的发送对象。
其构造时需要如下参数:
同样的,你可以通过依赖注入的方式在响应器中直接获取它们。
- `id` 为目标id若为群聊则为 group_id 或者 channel_id若为私聊则为user_id
- `parent_id` 为父级id若为频道则为 guild_id其他情况下可能为空字符串例如 Feishu 下可作为部门 id
- `channel` 为是否为频道,仅当目标平台符合频道概念时
- `private` 为是否为私聊
- `source` 为可能的事件id
- `self_id` 为机器人id若为 None 则 Bot 对象会随机选择
- `selector` 为选择器,用于在多个 Bot 对象中选择特定 Bot
- `scope` 为适配器范围,用于传入内置的特定选择器
- `adapter` 为适配器名称,若为 None 则需要明确指定 Bot 对象
- `platform` 为平台名称,仅当目标适配器存在多个平台时使用
- `extra` 为额外信息,用于适配器扩展
通过 `Target` 对象,我们可以在 `UniMessage.send` 中指定发送对象:
```python
from nonebot_plugin_alconna.uniseg import UniMessage, MsgTarget, Target, SupportScope
matcher = on_xxx(...)
@matcher.handle()
async def _(target: MsgTarget):
await UniMessage("Hello!").send(target=target)
target1 = Target("xxxx", scope=SupportScope.qq_client)
await UniMessage("Hello!").send(target=target1)
```
### 主动发送消息
`UniMessage.send` 也可以用于主动发送消息:
```python
from nonebot_plugin_alconna.uniseg import UniMessage, Target, SupportScope
from nonebot import get_driver
driver = get_driver()
@driver.on_startup
async def on_startup():
target = Target("xxxx", scope=SupportScope.qq_client)
await UniMessage("Hello!").send(target=target)
```
## 自定义消息段
`uniseg` 提供了部分方法来允许用户自定义 Segment 的序列化和反序列化:
```python
from dataclasses import dataclass
from nonebot.adapters import Bot
from nonebot.adapters import MessageSegment as BaseMessageSegment
from nonebot.adapters.satori import Custom, Message, MessageSegment
from nonebot_plugin_alconna.uniseg.builder import MessageBuilder
from nonebot_plugin_alconna.uniseg.exporter import MessageExporter
from nonebot_plugin_alconna.uniseg import Segment, custom_handler, custom_register
@dataclass
class MarketFace(Segment):
tabId: str
faceId: str
key: str
@custom_register(MarketFace, "chronocat:marketface")
def mfbuild(builder: MessageBuilder, seg: BaseMessageSegment):
if not isinstance(seg, Custom):
raise ValueError("MarketFace can only be built from Satori Message")
return MarketFace(**seg.data)(*builder.generate(seg.children))
@custom_handler(MarketFace)
async def mfexport(exporter: MessageExporter, seg: MarketFace, bot: Bot, fallback: bool):
if exporter.get_message_type() is Message:
return MessageSegment("chronocat:marketface", seg.data)(await exporter.export(seg.children, bot, fallback))
```
具体而言,你可以使用 `custom_register` 来增加一个从 MessageSegment 到 Segment 的处理方法;使用 `custom_handler` 来增加一个从 Segment 到 MessageSegment 的处理方法。

View File

@@ -0,0 +1,145 @@
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
# 数据库
[`nonebot-plugin-orm`](https://github.com/nonebot/plugin-orm) 是 NoneBot 的数据库支持插件。
本插件基于 [SQLAlchemy](https://www.sqlalchemy.org/) 和 [Alembic](https://alembic.sqlalchemy.org/),提供了许多与 NoneBot 紧密集成的功能:
- 多 Engine / Connection 支持
- Session 管理
- 关系模型管理、依赖注入支持
- 数据库迁移
## 安装
<Tabs groupId="install">
<TabItem value="cli" label="使用 nb-cli">
```shell
nb plugin install nonebot-plugin-orm
```
</TabItem>
<TabItem value="pip" label="使用 pip">
```shell
pip install nonebot-plugin-orm
```
</TabItem>
<TabItem value="pdm" label="使用 pdm">
```shell
pdm add nonebot-plugin-orm
```
</TabItem>
</Tabs>
## 数据库驱动和后端
本插件只提供了 ORM 功能,没有数据库后端,也没有直接连接数据库后端的能力。
所以你需要另行安装数据库驱动和数据库后端,并且配置数据库连接信息。
### SQLite
[SQLite](https://www.sqlite.org/) 是一个轻量级的嵌入式数据库,它的数据以单文件的形式存储在本地,不需要单独的数据库后端。
SQLite 非常适合用于开发环境和小型应用,但是不适合用于大型应用的生产环境。
虽然不需要另行安装数据库后端,但你仍然需要安装数据库驱动:
<Tabs groupId="install">
<TabItem value="pip" label="使用 pip">
```shell
pip install "nonebot-plugin-orm[sqlite]"
```
</TabItem>
<TabItem value="pdm" label="使用 pdm">
```shell
pdm add "nonebot-plugin-orm[sqlite]"
```
</TabItem>
</Tabs>
默认情况下,数据库文件为 `<data path>/nonebot-plugin-orm/db.sqlite3`(数据目录由 [nonebot-plugin-localstore](../data-storing) 提供)。
或者,你可以通过配置 `SQLALCHEMY_DATABASE_URL` 来指定数据库文件路径:
```shell
SQLALCHEMY_DATABASE_URL=sqlite+aiosqlite:///file_path
```
### PostgreSQL
[PostgreSQL](https://www.postgresql.org/) 是世界上最先进的开源关系数据库之一,对各种高级且广泛应用的功能有最好的支持,是中小型应用的首选数据库。
<Tabs groupId="install">
<TabItem value="pip" label="使用 pip">
```shell
pip install nonebot-plugin-orm[postgresql]
```
</TabItem>
<TabItem value="pdm" label="使用 pdm">
```shell
pdm add nonebot-plugin-orm[postgresql]
```
</TabItem>
</Tabs>
```shell
SQLALCHEMY_DATABASE_URL=postgresql+psycopg://user:password@host:port/dbname[?key=value&key=value...]
```
### MySQL / MariaDB
[MySQL](https://www.mysql.com/) 和 [MariaDB](https://mariadb.com/) 是经典的开源关系数据库,适合用于中小型应用。
<Tabs groupId="install">
<TabItem value="pip" label="使用 pip">
```shell
pip install nonebot-plugin-orm[mysql]
```
</TabItem>
<TabItem value="pdm" label="使用 pdm">
```shell
pdm add nonebot-plugin-orm[mysql]
```
</TabItem>
</Tabs>
```shell
SQLALCHEMY_DATABASE_URL=mysql+aiomysql://user:password@host:port/dbname[?key=value&key=value...]
```
## 使用
本插件提供了数据库迁移功能(此功能依赖于 [nb-cli 脚手架](../../quick-start#安装脚手架))。
在安装了新的插件或机器人之后,你需要执行一次数据库迁移操作,将数据库同步至与机器人一致的状态:
```shell
nb orm upgrade
```
运行完毕后,可以检查一下:
```shell
nb orm check
```
如果输出是 `没有检测到新的升级操作`,那么恭喜你,数据库已经迁移完成了,你可以启动机器人了。

View File

@@ -0,0 +1,4 @@
{
"label": "数据库",
"position": 7
}

View File

@@ -0,0 +1,378 @@
# 开发者指南
开发者指南内容较多,故分为了一个示例以及数个专题。
阅读(并且最好跟随实践)示例后,你将会对使用 `nonebot-plugin-orm` 开发插件有一个基本的认识。
如果想要更深入地学习关于 [SQLAlchemy](https://www.sqlalchemy.org/) 和 [Alembic](https://alembic.sqlalchemy.org/) 的知识,或者在使用过程中遇到了问题,可以查阅专题以及其官方文档。
## 示例
### 模型定义
首先,我们需要设计存储的数据的结构。
例如天气插件,需要存储**什么地方 (`location`)** 的**天气是什么 (`weather`)**。
其中,一个地方只会有一种天气,而不同地方可能有相同的天气。
所以,我们可以设计出如下的模型:
```python title=weather/__init__.py showLineNumbers
from nonebot_plugin_orm import Model
from sqlalchemy.orm import Mapped, mapped_column
class Weather(Model):
location: Mapped[str] = mapped_column(primary_key=True)
weather: Mapped[str]
```
其中,`primary_key=True` 意味着此列 (`location`) 是主键,即内容是唯一的且非空的。
每一个模型必须有至少一个主键。
我们可以用以下代码检查模型生成的数据库模式是否正确:
```python
from sqlalchemy.schema import CreateTable
print(CreateTable(Weather.__table__))
```
```sql
CREATE TABLE weather_weather (
location VARCHAR NOT NULL,
weather VARCHAR NOT NULL,
CONSTRAINT pk_weather_weather PRIMARY KEY (location)
)
```
可以注意到表名是 `weather_weather` 而不是 `Weather` 或者 `weather`。
这是因为 `nonebot-plugin-orm` 会自动为模型生成一个表名,规则是:`<插件模块名>_<类名小写>`。
你也可以通过指定 `__tablename__` 属性来自定义表名:
```python {2}
class Weather(Model):
__tablename__ = "weather"
...
```
```sql {1}
CREATE TABLE weather (
...
)
```
但是,并不推荐你这么做,因为这可能会导致不同插件间的表名重复,引发冲突。
特别是当你会发布插件时,你并不知道其他插件会不会使用相同的表名。
### 首次迁移
我们成功定义了模型,现在启动机器人试试吧:
```shell
$ nb run
01-02 15:04:05 [SUCCESS] nonebot | NoneBot is initializing...
01-02 15:04:05 [ERROR] nonebot_plugin_orm | 启动检查失败
01-02 15:04:05 [ERROR] nonebot | Application startup failed. Exiting.
Traceback (most recent call last):
...
click.exceptions.UsageError: 检测到新的升级操作:
[('add_table',
Table('weather', MetaData(), Column('location', String(), table=<weather>, primary_key=True, nullable=False), Column('weather', String(), table=<weather>, nullable=False), schema=None))]
```
咦,发生了什么?
`nonebot-plugin-orm` 试图阻止我们启动机器人。
原来是我们定义了模型,但是数据库中并没有对应的表,这会导致插件不能正常运行。
所以,我们需要迁移数据库。
首先,我们需要创建一个迁移脚本:
```shell
nb orm revision -m "first revision" --branch-label weather
```
其中,`-m` 参数是迁移脚本的描述,`--branch-label` 参数是迁移脚本的分支,一般为插件模块名。
执行命令过后,出现了一个 `weather/migrations` 目录,其中有一个 `xxxxxxxxxxxx_first_revision.py` 文件:
```shell {4,5}
weather
├── __init__.py
├── config.py
└── migrations
└── xxxxxxxxxxxx_first_revision.py
```
这就是我们创建的迁移脚本,它记录了数据库模式的变化。
我们可以查看一下它的内容:
```python title=weather/migrations/xxxxxxxxxxxx_first_revision.py {25-33,39-41} showLineNumbers
"""first revision
迁移 ID: xxxxxxxxxxxx
父迁移:
创建时间: 2006-01-02 15:04:05.999999
"""
from __future__ import annotations
from collections.abc import Sequence
import sqlalchemy as sa
from alembic import op
revision: str = "xxxxxxxxxxxx"
down_revision: str | Sequence[str] | None = None
branch_labels: str | Sequence[str] | None = ("weather",)
depends_on: str | Sequence[str] | None = None
def upgrade(name: str = "") -> None:
if name:
return
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"weather_weather",
sa.Column("location", sa.String(), nullable=False),
sa.Column("weather", sa.String(), nullable=False),
sa.PrimaryKeyConstraint("location", name=op.f("pk_weather_weather")),
info={"bind_key": "weather"},
)
# ### end Alembic commands ###
def downgrade(name: str = "") -> None:
if name:
return
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("weather_weather")
# ### end Alembic commands ###
```
可以注意到脚本的主体部分(其余是模版代码,请勿修改)是:
```python
# ### commands auto generated by Alembic - please adjust! ###
op.create_table( # CREATE TABLE
"weather_weather", # weather_weather
sa.Column("location", sa.String(), nullable=False), # location VARCHAR NOT NULL,
sa.Column("weather", sa.String(), nullable=False), # weather VARCHAR NOT NULL,
sa.PrimaryKeyConstraint("location", name=op.f("pk_weather_weather")), # CONSTRAINT pk_weather_weather PRIMARY KEY (location)
info={"bind_key": "weather"},
)
# ### end Alembic commands ###
```
```python
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("weather_weather") # DROP TABLE weather_weather;
# ### end Alembic commands ###
```
虽然我们不是很懂这些代码的意思,但是可以注意到它们几乎与 SQL 语句 (DDL) 一一对应。
显然,它们是用来创建和删除表的。
我们还可以注意到,`upgrade()` 和 `downgrade()` 函数中的代码是**互逆**的。
也就是说,执行一次 `upgrade()` 函数,再执行一次 `downgrade()` 函数后,数据库的模式就会回到原来的状态。
这就是迁移脚本的作用:记录数据库模式的变化,以便我们在不同的环境中(例如开发环境和生产环境)**可复现地**、**可逆地**同步数据库模式,正如 git 对我们的代码做的事情那样。
对了,不要忘记还有一段注释:`commands auto generated by Alembic - please adjust!`。
它在提醒我们,这些代码是由 Alembic 自动生成的,我们应该检查它们,并且根据需要进行调整。
:::caution 注意
迁移脚本冗长且繁琐,我们一般不会手写它们,而是由 Alembic 自动生成。
一般情况下Alembic 足够智能,可以正确地生成迁移脚本。
但是,在复杂或有歧义的情况下,我们可能需要手动调整迁移脚本。
所以,**永远要检查迁移脚本,并且在开发环境中测试!**
**迁移脚本中任何一处错误都足以使数据付之东流!**
:::
确定迁移脚本正确后,我们就可以执行迁移脚本,将数据库模式同步到数据库中:
```shell
nb orm upgrade
```
现在,我们可以正常启动机器人了。
开发过程中,我们可能会频繁地修改模型,这意味着我们需要频繁地创建并执行迁移脚本,非常繁琐。
实际上,此时我们不在乎数据安全,只需要数据库模式与模型定义一致即可。
所以,我们可以关闭 `nonebot-plugin-orm` 的启动检查:
```shell title=.env.dev
ALEMBIC_STARTUP_CHECK=false
```
现在,每次启动机器人时,数据库模式会自动与模型定义同步,无需手动迁移。
### 会话管理
我们已经成功定义了模型,并且迁移了数据库,现在可以开始使用数据库了……吗?
并不能,因为模型只是数据结构的定义,并不能通过它操作数据(如果你曾经使用过 [Tortoise ORM](https://tortoise.github.io/),可能会知道 `await Weather.get(location="上海")` 这样的面向对象编程。
但是 SQLAlchemy 不同,选择了命令式编程)。
我们需要使用**会话**操作数据:
```python title=weather/__init__.py {10,13} showLineNumbers
from nonebot import on_command
from nonebot.adapters import Message
from nonebot.params import CommandArg
from nonebot_plugin_orm import async_scoped_session
weather = on_command("天气")
@weather.handle()
async def _(session: async_scoped_session, args: Message = CommandArg()):
location = args.extract_plain_text()
if wea := await session.get(Weather, location):
await weather.finish(f"今天{location}的天气是{wea.weather}")
await weather.finish(f"未查询到{location}的天气")
```
我们通过 `session: async_scoped_session` 依赖注入获得了一个会话,然后使用 `await session.get(Weather, location)` 查询数据库。
`async_scoped_session` 是一个有作用域限制的会话,作用域为当前事件、当前事件响应器。
会话产生的模型实例(例如此处的 `wea := await session.get(Weather, location)`)作用域与会话相同。
:::caution 注意
此处提到的“会话”指的是 ORM 会话,而非 [NoneBot 会话](../../../appendices/session-control)两者的生命周期也是不同的NoneBot 会话的生命周期中可能包含多个事件,不同的事件也会有不同的事件响应器)。
具体而言,就是不要将 ORM 会话和模型实例存储在 NoneBot 会话状态中:
```python {12}
from nonebot.params import ArgPlainText
from nonebot.typing import T_State
@weather.got("location", prompt="请输入地名")
async def _(state: T_State, session: async_scoped_session, location: str = ArgPlainText()):
wea = await session.get(Weather, location)
if not wea:
await weather.finish(f"未查询到{location}的天气")
state["weather"] = wea # 不要这么做,除非你知道自己在做什么
```
当然非要这么做也不是不可以:
```python {6}
@weather.handle()
async def _(state: T_State, session: async_scoped_session):
# 通过 await session.merge(state["weather"]) 获得了此 ORM 会话中的相应模型实例,
# 而非直接使用会话状态中的模型实例,
# 因为先前的 ORM 会话已经关闭了。
wea = await session.merge(state["weather"])
await weather.finish(f"今天{state['location']}的天气是{wea.weather}")
```
:::
当有数据更改时,我们需要提交事务,也要注意会话作用域问题:
```python title=weather/__init__.py {12,20} showLineNumbers
from nonebot.params import Depends
async def get_weather(
session: async_scoped_session, args: Message = CommandArg()
) -> Weather:
location = args.extract_plain_text()
if not (wea := await session.get(Weather, location)):
wea = Weather(location=location, weather="未知")
session.add(wea)
# await session.commit() # 不应该在其他地方提交事务
return wea
@weather.handle()
async def _(session: async_scoped_session, wea: Weather = Depends(get_weather)):
await weather.send(f"今天的天气是{wea.weather}")
await session.commit() # 而应该在事件响应器结束前提交事务
```
当然我们也可以获得一个新的会话,不过此时就要手动管理会话了:
```python title=weather/__init__.py {5-6} showLineNumbers
from nonebot_plugin_orm import get_session
async def get_weather(location: str) -> str:
session = get_session()
async with session.begin():
wea = await session.get(Weather, location)
if not wea:
wea = Weather(location=location, weather="未知")
session.add(wea)
return wea.weather
@weather.handle()
async def _(args: Message = CommandArg()):
wea = await get_weather(args.extract_plain_text())
await weather.send(f"今天的天气是{wea}")
```
### 依赖注入
在上面的示例中,我们都是通过会话获得数据的。
不过,我们也可以通过依赖注入获得数据:
```python title=weather/__init__.py {12-14} showLineNumbers
from sqlalchemy import select
from nonebot.params import Depends
from nonebot_plugin_orm import SQLDepends
def extract_arg_plain_text(args: Message = CommandArg()) -> str:
return args.extract_plain_text()
@weather.handle()
async def _(
wea: Weather = SQLDepends(
select(Weather).where(Weather.location == Depends(extract_arg_plain_text))
),
):
await weather.send(f"今天的天气是{wea.weather}")
```
其中,`SQLDepends` 是一个特殊的依赖注入,它会根据类型标注和 SQL 语句提供数据SQL 语句中也可以有子依赖。
不同的类型标注也会获得不同形式的数据:
```python title=weather/__init__.py {5} showLineNumbers
from collections.abc import Sequence
@weather.handle()
async def _(
weas: Sequence[Weather] = SQLDepends(
select(Weather).where(Weather.weather == Depends(extract_arg_plain_text))
),
):
await weather.send(f"今天的天气是{weas[0].weather}的城市有{''.join(wea.location for wea in weas)}")
```
支持的类型标注请参见 [依赖注入](dependency)。
我们也可以像 [类作为依赖](../../../advanced/dependency#类作为依赖) 那样,在类属性中声明子依赖:
```python title=weather/__init__.py {5-6,10} showLineNumbers
from collections.abc import Sequence
class Weather(Model):
location: Mapped[str] = mapped_column(primary_key=True)
weather: Mapped[str] = Depends(extract_arg_plain_text)
# weather: Annotated[Mapped[str], Depends(extract_arg_plain_text)] # Annotated 支持
@weather.handle()
async def _(weas: Sequence[Weather]):
await weather.send(
f"今天的天气是{weas[0].weather}的城市有{''.join(wea.location for wea in weas)}"
)
```

View File

@@ -0,0 +1,4 @@
{
"label": "开发者指南",
"position": 3
}

View File

@@ -0,0 +1,240 @@
---
sidebar_position: 3
description: 依赖注入
---
# 依赖注入
`nonebot-plugin-orm` 提供了强大且灵活的依赖注入,可以方便地帮助你获取数据库会话和查询数据。
## 数据库会话
### AsyncSession
新数据库会话,常用于有独立的数据库操作逻辑的插件。
```python {13,26}
from nonebot import on_message
from nonebot.params import Depends
from nonebot_plugin_orm import AsyncSession, Model, async_scoped_session
from sqlalchemy.orm import Mapped, mapped_column
message = on_message()
class Message(Model):
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
async def get_message(session: AsyncSession) -> Message:
# 等价于 session = get_session()
async with session:
msg = Message()
session.add(msg)
await session.commit()
await session.refresh(msg)
return msg
@message.handle()
async def _(session: async_scoped_session, msg: Message = Depends(get_message)):
await session.rollback() # 无法回退 get_message() 中的更改
await message.send(str(msg.id)) # msg 被存储msg.id 递增
```
### async_scoped_session
数据库作用域会话,常用于事件响应器和有与响应逻辑相关的数据库操作逻辑的插件。
```python {1326}
from nonebot import on_message
from nonebot.params import Depends
from nonebot_plugin_orm import Model, async_scoped_session
from sqlalchemy.orm import Mapped, mapped_column
message = on_message()
class Message(Model):
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
async def get_message(session: async_scoped_session) -> Message:
# 等价于 session = get_scoped_session()
msg = Message()
session.add(msg)
await session.flush()
await session.refresh(msg)
return msg
@message.handle()
async def _(session: async_scoped_session, msg: Message = Depends(get_message)):
await session.rollback() # 可以回退 get_message() 中的更改
await message.send(str(msg.id)) # msg 没有被存储msg.id 不变
```
## 查询数据
### Model
支持类作为依赖。
```python
from typing import Annotated
from nonebot.params import Depends
from nonebot_plugin_orm import Model
from sqlalchemy.orm import Mapped, mapped_column
def get_id() -> int: ...
class Message(Model):
id: Annotated[Mapped[int], Depends(get_id)] = mapped_column(
primary_key=True, autoincrement=True
)
async def _(msg: Message):
# 等价于 msg = (
# await (await session.stream(select(Message).where(Message.id == get_id())))
# .scalars()
# .one_or_none()
# )
...
```
### SQLDepends
参数为一个 SQL 语句决定依赖注入的内容SQL 语句中可以使用子依赖。
```python {11-13}
from nonebot.params import Depends
from nonebot_plugin_orm import Model, SQLDepends
from sqlalchemy import select
def get_id() -> int: ...
async def _(
model: Model = SQLDepends(select(Model).where(Model.id == Depends(get_id))),
): ...
```
参数可以是任意 SQL 语句,但不建议使用 `select` 以外的语句,因为语句可能没有返回值(`returning` 除外),而且代码不清晰。
### 类型标注
类型标注决定依赖注入的数据结构,主要影响以下几个层面:
- 迭代器(`session.execute()`)或异步迭代器(`session.stream()`
- 标量(`session.execute().scalars()`)或元组(`session.execute()`
- 一个(`session.execute().one_or_none()`,注意 `None` 时可能触发 [重载](../../../appendices/overload#重载))或全部(`session.execute()` / `session.execute().all()`
- 连续(`session().execute()`)或分块(`session.execute().partitions()`
具体如下(可以使用父类型作为类型标注):
- ```python
async def _(rows_partitions: AsyncIterator[Sequence[Tuple[Model, ...]]]):
# 等价于 rows_partitions = await (await session.stream(sql).partitions())
async for partition in rows_partitions:
for row in partition:
print(row[0], row[1], ...)
```
- ```python
async def _(model_partitions: AsyncIterator[Sequence[Model]]):
# 等价于 model_partitions = await (await session.stream(sql).scalars().partitions())
async for partition in model_partitions:
for model in partition:
print(model)
```
- ```python
async def _(row_partitions: Iterator[Sequence[Tuple[Model, ...]]]):
# 等价于 row_partitions = await session.execute(sql).partitions()
for partition in rows_partitions:
for row in partition:
print(row[0], row[1], ...)
```
- ```python
async def _(model_partitions: Iterator[Sequence[Model]]):
# 等价于 model_partitions = await (await session.execute(sql).scalars().partitions())
for partition in model_partitions:
for model in partition:
print(model)
```
- ```python
async def _(rows: sa_async.AsyncResult[Tuple[Model, ...]]):
# 等价于 rows = await session.stream(sql)
async for row in rows:
print(row[0], row[1], ...)
```
- ```python
async def _(models: sa_async.AsyncScalarResult[Model]):
# 等价于 models = await session.stream(sql).scalars()
async for model in models:
print(model)
```
- ```python
async def _(rows: sa.Result[Tuple[Model, ...]]):
# 等价于 rows = await session.execute(sql)
for row in rows:
print(row[0], row[1], ...)
```
- ```python
async def _(models: sa.ScalarResult[Model]):
# 等价于 models = await session.execute(sql).scalars()
for model in models:
print(model)
```
- ```python
async def _(rows: Sequence[Tuple[Model, ...]]):
# 等价于 rows = await (await session.stream(sql).all())
for row in rows:
print(row[0], row[1], ...)
```
- ```python
async def _(models: Sequence[Model]):
# 等价于 models = await (await session.stream(sql).scalars().all())
for model in models:
print(model)
```
- ```python
async def _(row: Tuple[Model, ...]):
# 等价于 row = await (await session.stream(sql).one_or_none())
print(row[0], row[1], ...)
```
- ```python
async def _(model: Model):
# 等价于 model = await (await session.stream(sql).scalars().one_or_none())
print(model)
```

View File

@@ -0,0 +1,147 @@
---
sidebar_position: 2
description: 测试
---
# 测试
百思不如一试,测试是发现问题的最佳方式。
不同的用户会有不同的配置,为了提高项目的兼容性,我们需要在不同数据库后端上测试。
手动进行大量的、重复的测试不可靠,也不现实,因此我们推荐使用 [GitHub Actions](https://github.com/features/actions) 进行自动化测试:
```yaml title=.github/workflows/test.yml {12-42,52-53} showLineNumbers
name: Test
on:
push:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
db:
- sqlite+aiosqlite:///db.sqlite3
- postgresql+psycopg://postgres:postgres@localhost:5432/postgres
- mysql+aiomysql://mysql:mysql@localhost:3306/mymysql
fail-fast: false
env:
SQLALCHEMY_DATABASE_URL: ${{ matrix.db }}
services:
postgresql:
image: ${{ startsWith(matrix.db, 'postgresql') && 'postgres' || '' }}
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
- 5432:5432
mysql:
image: ${{ startsWith(matrix.db, 'mysql') && 'mysql' || '' }}
env:
MYSQL_ROOT_PASSWORD: mysql
MYSQL_USER: mysql
MYSQL_PASSWORD: mysql
MYSQL_DATABASE: mymysql
ports:
- 3306:3306
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- name: Install dependencies
run: pip install -r requirements.txt
- name: Run migrations
run: pipx run nb-cli orm upgrade
- name: Run tests
run: pytest
```
如果项目还需要考虑跨平台和跨 Python 版本兼容,测试矩阵中还需要增加这两个维度。
但是,我们没必要在所有平台和 Python 版本上运行所有数据库的测试因为很显然PostgreSQL 和 MySQL 这类独立的数据库后端不会受平台和 Python 影响,而且 Github Actions 的非 Linux 平台不支持运行独立服务:
| | Python 3.9 | Python 3.10 | Python 3.11 | Python 3.12 |
| ----------- | ---------- | ----------- | ----------- | --------------------------- |
| **Linux** | SQLite | SQLite | SQLite | SQLite / PostgreSQL / MySQL |
| **Windows** | SQLite | SQLite | SQLite | SQLite |
| **macOS** | SQLite | SQLite | SQLite | SQLite |
```yaml title=.github/workflows/test.yml {12-24} showLineNumbers
name: Test
on:
push:
branches:
- main
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.9", "3.10", "3.11", "3.12"]
db: ["sqlite+aiosqlite:///db.sqlite3"]
include:
- os: ubuntu-latest
python-version: "3.12"
db: postgresql+psycopg://postgres:postgres@localhost:5432/postgres
- os: ubuntu-latest
python-version: "3.12"
db: mysql+aiomysql://mysql:mysql@localhost:3306/mymysql
fail-fast: false
env:
SQLALCHEMY_DATABASE_URL: ${{ matrix.db }}
services:
postgresql:
image: ${{ startsWith(matrix.db, 'postgresql') && 'postgres' || '' }}
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
- 5432:5432
mysql:
image: ${{ startsWith(matrix.db, 'mysql') && 'mysql' || '' }}
env:
MYSQL_ROOT_PASSWORD: mysql
MYSQL_USER: mysql
MYSQL_PASSWORD: mysql
MYSQL_DATABASE: mymysql
ports:
- 3306:3306
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: pip install -r requirements.txt
- name: Run migrations
run: pipx run nb-cli orm upgrade
- name: Run tests
run: pytest
```

View File

@@ -0,0 +1,158 @@
---
sidebar_position: 2
description: 用户指南
---
# 用户指南
`nonebot-plugin-orm` 功能强大且复杂,使用上有一定难度。
不过,对于用户而言,只需要掌握部分功能即可。
:::caution 注意
请注意区分插件的项目名(如:`nonebot-plugin-wordcloud`)和模块名(如:`nonebot_plugin_wordcloud`)。`nonebot-plugin-orm` 中统一使用插件模块名。参见 [插件命名规范](../../developer/plugin-publishing#插件命名规范)。
:::
## 示例
### 创建新机器人
我们想要创建一个机器人,并安装 `nonebot-plugin-wordcloud` 插件,只需要执行以下命令:
```shell
nb init # 初始化项目文件夹
pip install nonebot-plugin-orm[sqlite] # 安装 nonebot-plugin-orm并附带 SQLite 支持
nb plugin install nonebot-plugin-wordcloud # 安装插件
# nb orm heads # 查看有什么插件使用到了数据库(可选)
nb orm upgrade # 升级数据库
# nb orm check # 检查一下数据库模式是否与模型定义一致(可选)
nb run # 启动机器人
```
### 卸载插件
我们已经安装了 `nonebot-plugin-wordcloud` 插件,但是现在想要卸载它,并且**删除它的数据**,只需要执行以下命令:
```shell
nb plugin uninstall nonebot-plugin-wordcloud # 卸载插件
# nb orm heads # 查看有什么插件使用到了数据库。(可选)
nb orm downgrade nonebot_plugin_wordcloud@base # 降级数据库,删除数据
# nb orm check # 检查一下数据库模式是否与模型定义一致(可选)
```
## CLI
接下来,让我们了解下示例中出现的 CLI 命令的含义:
### heads
显示所有的分支头。一般一个分支对应一个插件。
```shell
nb orm heads
```
输出格式为 `<迁移 ID> (<插件模块名>) (<头部类型>)`
```
46327b837dd8 (nonebot_plugin_chatrecorder) (head)
9492159f98f7 (nonebot_plugin_user) (head)
71a72119935f (nonebot_plugin_session_orm) (effective head)
ade8cdca5470 (nonebot_plugin_wordcloud) (head)
```
### upgrade
升级数据库。每次安装新的插件或更新插件版本后,都需要执行此命令。
```shell
nb orm upgrade <插件模块名>@<迁移 ID>
```
其中,`<插件模块名>@<迁移 ID>` 是可选参数。如果不指定,则会将所有分支升级到最新版本,这也是最常见的用法:
```shell
nb orm upgrade
```
### downgrade
降级数据库。当需要回滚插件版本或删除插件时,可以执行此命令。
```shell
nb orm downgrade <插件模块名>@<迁移 ID>
```
其中,`<迁移 ID>` 也可以是 `base`,即回滚到初始状态。常用于卸载插件后删除其数据:
```shell
nb orm downgrade <插件模块名>@base
```
### check
检查数据库模式是否与模型定义一致。机器人启动前会自动运行此命令(`ALEMBIC_STARTUP_CHECK=true` 时),并在检查失败时阻止启动。
```shell
nb orm check
```
## 配置
### sqlalchemy_database_url
默认数据库连接 URL。参见 [数据库驱动和后端](.#数据库驱动和后端) 和 [引擎配置 — SQLAlchemy 2.0 文档](https://docs.sqlalchemy.org/en/20/core/engines.html#database-urls)。
```shell
SQLALCHEMY_DATABASE_URL=dialect+driver://username:password@host:port/database
```
### sqlalchemy_bind
bind keys一般为插件模块名到数据库连接 URL、[`create_async_engine()`](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html#sqlalchemy.ext.asyncio.create_async_engine) 参数字典或 [`AsyncEngine`](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html#sqlalchemy.ext.asyncio.AsyncEngine) 实例的字典。
例如,我们想要让 `nonebot-plugin-wordcloud` 插件使用一个 SQLite 数据库,并开启 [Echo 选项](https://docs.sqlalchemy.org/en/20/core/engines.html#sqlalchemy.create_engine.params.echo) 便于 debug而其他插件使用默认的 PostgreSQL 数据库,可以这样配置:
```shell
SQLALCHEMY_BINDS='{
"": "postgresql+psycopg://scott:tiger@localhost/mydatabase",
"nonebot_plugin_wordcloud": {
"url": "sqlite+aiosqlite://",
"echo": true
}
}'
```
### sqlalchemy_engine_options
[`create_async_engine()`](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html#sqlalchemy.ext.asyncio.create_async_engine) 默认参数字典。
```shell
SQLALCHEMY_ENGINE_OPTIONS='{
"pool_size": 5,
"max_overflow": 10,
"pool_timeout": 30,
"pool_recycle": 3600,
"echo": true
}'
```
### sqlalchemy_echo
开启 [Echo 选项](https://docs.sqlalchemy.org/en/20/core/engines.html#sqlalchemy.create_engine.params.echo) 和 [Echo Pool 选项](https://docs.sqlalchemy.org/en/20/core/engines.html#sqlalchemy.create_engine.params.echo_pool) 便于 debug。
```shell
SQLALCHEMY_ECHO=true
```
:::caution 注意
以上配置之间有覆盖关系,遵循特殊优先于一般的原则,具体为 [`sqlalchemy_database_url`](#sqlalchemy_database_url) > [`sqlalchemy_bind`](#sqlalchemy_bind) > [`sqlalchemy_echo`](#sqlalchemy_echo) > [`sqlalchemy_engine_options`](#sqlalchemy_engine_options)。
但覆盖顺序并非显而易见,出于清晰考虑,请只配置必要的选项。
:::

View File

@@ -60,7 +60,7 @@ async def handle_function(event: OnebotV11MessageEvent | OnebotV12MessageEvent,
```
</TabItem>
<TabItem value="3.8" label="Python 3.8+">
<TabItem value="3.9" label="Python 3.9">
```python
from typing import Union

Some files were not shown because too many files have changed in this diff Show More