mirror of
https://github.com/suyiiyii/nonebot-bison.git
synced 2026-05-09 18:27:56 +08:00
✨ 通过 nb-cli 实现数据库一键导入导出 (#210)
* feat: 实现导出存储的订阅信息的功能 * test: 编写导出功能测试 * test: 使用tmp_path * feat: 实现导入订阅文件功能 * refactor: 将订阅导入导出部分独立出来 * fix: 修复一些拼写错误 test: 完成import的第一个测试 * feat: 将订阅导入导出函数加入nb script test: 添加cli测试 * test: 完善subs import测试 * 🐛 fix nb cli entrypoint name error * fix: 修改错误的entry_point, 关闭yaml导出时对键名的排序 * fix: 使用更简短的命令名 * 🚚 将subs_io迁移到config下 * ♻️ 不再使用抛出异常的方式创建目录 * refactor: 将subscribe_export类转为函数 * refactor: 将subscribe_import类转为函数 * refactor: 根据重写的subs_io重新调整cli * test: 调整重写subs_io后的test * chore: 清理未使用的import内容 * feat(cli): 将--yaml更改为--format * test: 调整测试 * fix(cli): 为import添加不支持格式的报错 * ⚡ improve export performace * feat: subscribes_import函数不再需要传入参数函数,而是指定为add_subscribes fix: nbesf_parser在传入str时将调用parse_raw, 否则调用parse_obj * feat: subscribes_import现在会根据nbesf_data的版本选择合适的导入方式 * fix(test): 调整测试 * feat: nb bison export命令不再将文件导出到data目录,而是当前工作目录 * docs: 增添相关文档 * fix(test): 修复错误的变量名 --------- Co-authored-by: felinae98 <731499577@qq.com>
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
import importlib
|
||||
import json
|
||||
import time
|
||||
from functools import partial, wraps
|
||||
from pathlib import Path
|
||||
from types import ModuleType
|
||||
from typing import Any, Callable, Coroutine, TypeVar
|
||||
|
||||
from nonebot.log import logger
|
||||
|
||||
from ..config.subs_io import nbesf_parser, subscribes_export, subscribes_import
|
||||
from ..config.subs_io.nbesf_model import SubGroup
|
||||
from ..scheduler.manager import init_scheduler
|
||||
|
||||
try:
|
||||
import anyio
|
||||
import click
|
||||
from typing_extensions import ParamSpec
|
||||
except ImportError as e: # pragma: no cover
|
||||
raise ImportError("请使用 `pip install nonebot-bison[cli]` 安装所需依赖") from e
|
||||
|
||||
|
||||
def import_yaml_module() -> ModuleType:
|
||||
try:
|
||||
pyyaml = importlib.import_module("yaml")
|
||||
except ImportError as e:
|
||||
raise ImportError("请使用 `pip install nonebot-bison[yaml]` 安装所需依赖") from e
|
||||
|
||||
return pyyaml
|
||||
|
||||
|
||||
P = ParamSpec("P")
|
||||
R = TypeVar("R")
|
||||
|
||||
|
||||
def run_sync(func: Callable[P, R]) -> Callable[P, Coroutine[Any, Any, R]]:
|
||||
@wraps(func)
|
||||
async def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
||||
return await anyio.to_thread.run_sync(partial(func, *args, **kwargs))
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def run_async(func: Callable[P, Coroutine[Any, Any, R]]) -> Callable[P, R]:
|
||||
@wraps(func)
|
||||
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
||||
return anyio.from_thread.run(partial(func, *args, **kwargs))
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@click.group()
|
||||
def cli():
|
||||
"""Nonebot Bison CLI"""
|
||||
pass
|
||||
|
||||
|
||||
def path_init(ctx, param, value):
|
||||
if not value:
|
||||
export_path = Path.cwd()
|
||||
else:
|
||||
export_path = Path(value)
|
||||
|
||||
return export_path
|
||||
|
||||
|
||||
@cli.command(help="导出Nonebot Bison Exchangable Subcribes File", name="export")
|
||||
@click.option(
|
||||
"--path", "-p", default=None, callback=path_init, help="导出路径, 如果不指定,则默认为工作目录"
|
||||
)
|
||||
@click.option(
|
||||
"--format",
|
||||
default="json",
|
||||
type=click.Choice(["json", "yaml", "yml"]),
|
||||
help="指定导出格式[json, yaml],默认为 json",
|
||||
)
|
||||
@run_async
|
||||
async def subs_export(path: Path, format: str):
|
||||
|
||||
await init_scheduler()
|
||||
|
||||
export_file = path / f"bison_subscribes_export_{int(time.time())}.{format}"
|
||||
|
||||
logger.info("正在获取订阅信息...")
|
||||
export_data: SubGroup = await subscribes_export(lambda x: x)
|
||||
|
||||
with export_file.open("w", encoding="utf-8") as f:
|
||||
match format:
|
||||
case "yaml" | "yml":
|
||||
logger.info("正在导出为yaml...")
|
||||
|
||||
pyyaml = import_yaml_module()
|
||||
pyyaml.safe_dump(export_data.dict(), f, sort_keys=False)
|
||||
|
||||
case "json":
|
||||
logger.info("正在导出为json...")
|
||||
|
||||
json.dump(export_data.dict(), f, indent=4, ensure_ascii=False)
|
||||
|
||||
case _:
|
||||
raise click.BadParameter(message=f"不支持的导出格式: {format}")
|
||||
|
||||
logger.success(f"导出完毕!已导出到 {path} ")
|
||||
|
||||
|
||||
@cli.command(help="从Nonebot Biosn Exchangable Subscribes File导入订阅", name="import")
|
||||
@click.option("--path", "-p", required=True, help="导入文件名")
|
||||
@click.option(
|
||||
"--format",
|
||||
default="json",
|
||||
type=click.Choice(["json", "yaml", "yml"]),
|
||||
help="指定导入格式[json, yaml],默认为 json",
|
||||
)
|
||||
@run_async
|
||||
async def subs_import(path: str, format: str):
|
||||
|
||||
await init_scheduler()
|
||||
|
||||
import_file_path = Path(path)
|
||||
assert import_file_path.is_file(), "该路径不是文件!"
|
||||
|
||||
with import_file_path.open("r", encoding="utf-8") as f:
|
||||
match format:
|
||||
case "yaml" | "yml":
|
||||
logger.info("正在从yaml导入...")
|
||||
|
||||
pyyaml = import_yaml_module()
|
||||
import_items = pyyaml.safe_load(f)
|
||||
|
||||
case "json":
|
||||
logger.info("正在从json导入...")
|
||||
|
||||
import_items = json.load(f)
|
||||
|
||||
case _:
|
||||
raise click.BadParameter(message=f"不支持的导入格式: {format}")
|
||||
|
||||
nbesf_data = nbesf_parser(import_items)
|
||||
await subscribes_import(nbesf_data)
|
||||
|
||||
|
||||
def main():
|
||||
anyio.run(run_sync(cli)) # pragma: no cover
|
||||
Reference in New Issue
Block a user