diff --git a/.github/workflows/pypi-publish.yaml b/.github/workflows/liteyuki-pypi-publish.yaml similarity index 75% rename from .github/workflows/pypi-publish.yaml rename to .github/workflows/liteyuki-pypi-publish.yaml index 110d512c..fbdfcb45 100644 --- a/.github/workflows/pypi-publish.yaml +++ b/.github/workflows/liteyuki-pypi-publish.yaml @@ -1,4 +1,4 @@ -name: Publish +name: Liteyuki PyPI Publish on: push: @@ -6,8 +6,8 @@ on: - 'v*' jobs: - pypi-publish: - name: upload release to PyPI + liteyuki-pypi-publish: + name: upload release to PyPI (Nightly) runs-on: ubuntu-latest permissions: contents: read diff --git a/.gitignore b/.gitignore index 3761e674..8c76e358 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,8 @@ Thumbs.db .env .env.* plugins/ -data/ \ No newline at end of file +data/ +configs/ +config.yaml +config-dev.yaml +config-prod.yaml \ No newline at end of file diff --git a/README.md b/README.md index 849fe53a..02c1e00e 100644 --- a/README.md +++ b/README.md @@ -59,3 +59,21 @@ [liteyukilab-link]:https://lab.liteyuki.icu/@LiteyukiBot [banner]: https://socialify.git.ci/LiteyukiStudio/LiteyukiBot/image?description=1&forks=1&issues=1&Plus&pulls=1&stargazers=1&theme=Auto&logo=https%3a%2f%2fcdn.liteyuki.icu%2fstatic%2fsvg%2flylogo-full.svg + +## 开发环境配置 + +1. 项目使用uv进行包管理,你也可以使用uv进行环境管理,[安装uv](https://docs.astral.sh/uv/#installation) + +2. 进入项目目录使用uv同步环境和依赖 + +```bash +uv sync --all # 安装包括dev和prod的所有依赖 +``` + +3. VSCode扩展 + +- Python +- Mypy +- Ruff + +4. 环境变量指定ENVIRONMENT=dev或prod或其他,然后加载.env.{}文件,环境变量 \ No newline at end of file diff --git a/docker-compose-dev.yaml b/docker-compose-dev.yaml index a3d45764..bd1fb92a 100644 --- a/docker-compose-dev.yaml +++ b/docker-compose-dev.yaml @@ -7,5 +7,6 @@ services: - ./plugins:/liteyukibot/plugins - ./data:/liteyukibot/data - ./configs:/liteyukibot/configs + - ./config.yaml:/liteyukibot/config.yaml ports: - "8090:8080" \ No newline at end of file diff --git a/docker-compose-example.yaml b/docker-compose-example.yaml index a8bb5583..49ea6898 100644 --- a/docker-compose-example.yaml +++ b/docker-compose-example.yaml @@ -11,5 +11,6 @@ services: - ./configs:/liteyukibot/configs # 配置目录,包含配置文件 - ./data:/liteyukibot/data # 数据目录,包含下载器安装的插件目录 - ./plugins:/liteyukibot/plugins # 外部插件目录,插件开发者可用 + - ./config.yaml:/liteyukibot/config.yaml # 配置文件,包含所有配置项 ports: - "8090:8080" \ No newline at end of file diff --git a/liteyukibot/__init__.py b/liteyukibot/__init__.py index 3946a849..2138d32a 100644 --- a/liteyukibot/__init__.py +++ b/liteyukibot/__init__.py @@ -1,3 +1,7 @@ from .daemon import Daemon +from .log import logger -__all__ = ["Daemon"] \ No newline at end of file +__all__ = [ + "Daemon", + "logger" + ] \ No newline at end of file diff --git a/liteyukibot/asgi/__init__.py b/liteyukibot/asgi/__init__.py index e41edf07..221cfcd7 100644 --- a/liteyukibot/asgi/__init__.py +++ b/liteyukibot/asgi/__init__.py @@ -1,8 +1,5 @@ -import asyncio - +import uvicorn from fastapi import FastAPI -import hypercorn -import hypercorn.run app = FastAPI() @@ -11,10 +8,8 @@ async def root(): return {"message": "Hello LiteyukiBot"} -async def run_app(): - """liteyukibot入口函数 - """ - hypercorn.run.serve(app, config=hypercorn.Config.from_mapping( - bind=["localhost:8000"], - workers=1, - )) \ No newline at end of file +async def run_app(**kwargs): + """ASGI app 启动函数,在所有插件加载完后任务启动""" + config = uvicorn.Config(app, **kwargs, log_config=None) + server = uvicorn.Server(config) + await server.serve() \ No newline at end of file diff --git a/liteyukibot/config.py b/liteyukibot/config.py index 9470a6b9..fe1ee2d0 100644 --- a/liteyukibot/config.py +++ b/liteyukibot/config.py @@ -1,13 +1,13 @@ +import json +import os +import tomllib from typing import Any -import json import yaml -import tomllib -config: dict[str, Any] = {} # 全局配置map -flat_config: dict[str, Any] = {} # 扁平化配置map +type RawConfig = dict[str, Any] -def load_from_yaml(file_path: str) -> dict[str, Any]: +def load_from_yaml(file_path: str) -> RawConfig: """从yaml文件中加载配置并返回字典 Args: @@ -19,7 +19,7 @@ def load_from_yaml(file_path: str) -> dict[str, Any]: with open(file_path, "r", encoding="utf-8") as file: return yaml.safe_load(file) -def load_from_json(file_path: str) -> dict[str, Any]: +def load_from_json(file_path: str) -> RawConfig: """从json文件中加载配置并返回字典 Args: @@ -32,7 +32,7 @@ def load_from_json(file_path: str) -> dict[str, Any]: with open(file_path, "r", encoding="utf-8") as file: return json.load(file) -def load_from_toml(file_path: str) -> dict[str, Any]: +def load_from_toml(file_path: str) -> RawConfig: """从toml文件中加载配置并返回字典 Args: @@ -44,18 +44,26 @@ def load_from_toml(file_path: str) -> dict[str, Any]: with open(file_path, "rb") as file: return tomllib.load(file) -def merge_to_config(new_config: dict[str, Any], warn: bool=True) -> None: - """加载配置到全局配置字典,该函数有副作用,开发者尽量不要在多份配置文件中使用重复的配置项,否则会被覆盖 +def merge_dicts(base: RawConfig, new: RawConfig) -> RawConfig: + """递归合并两个字典 Args: - new_config (dict[str, Any]): 新的字典 - warn (bool, optional): 是否启用重复键警告. 默认 True. + base (dict[str, Any]): 原始字典 + new (dict[str, Any]): 新的字典 + + Returns: + dict[str, Any]: 合并后的字典 """ - global config, flat_config - config.update(new_config) - flat_config = flatten_dict(config) + for key, value in new.items(): + if key in base and isinstance(base[key], dict) and isinstance(value, dict): + # 如果当前键对应的值是字典,则递归合并 + base[key] = merge_dicts(base[key], value) + else: + # 否则直接更新值 + base[key] = value + return base -def flatten_dict(d: dict[str, Any], parent_key: str = '', sep: str = '.') -> dict[str, Any]: +def flatten_dict(d: RawConfig, parent_key: str = '', sep: str = '.') -> RawConfig: """将嵌套字典扁平化 Args: @@ -89,10 +97,19 @@ def flatten_dict(d: dict[str, Any], parent_key: str = '', sep: str = '.') -> dic items.append((new_key, v)) return dict(items) -def load_config_to_global(reset: bool = False) -> None: - """加载配置到全局配置字典 +def load_from_dir(dir_path: str) -> RawConfig: + """从目录中加载配置文件 Args: - reset (bool, optional): 是否重置配置项. 默认 False. + dir_path (str): 目录路径 """ - \ No newline at end of file + config: RawConfig = {} + for file_name in os.listdir(dir_path): + if file_name.endswith(".yaml") or file_name.endswith(".yml"): + config = merge_dicts(config, load_from_yaml(os.path.join(dir_path, file_name)) or {}) + elif file_name.endswith(".json"): + config = merge_dicts(config, load_from_json(os.path.join(dir_path, file_name)) or {}) + elif file_name.endswith(".toml"): + config = merge_dicts(config, load_from_toml(os.path.join(dir_path, file_name)) or {}) + + return config \ No newline at end of file diff --git a/liteyukibot/daemon.py b/liteyukibot/daemon.py index ae01383a..8cef5b43 100644 --- a/liteyukibot/daemon.py +++ b/liteyukibot/daemon.py @@ -1,16 +1,71 @@ import asyncio +from typing import Type + +from pydantic import BaseModel + +from .asgi import run_app +from .config import RawConfig, flatten_dict, load_from_dir, merge_dicts +from .log import logger, set_level +from .utils import pretty_format class Daemon: - def __init__(self, **config): - self.config = config - - async def _run(self): - """liteyukibot入口函数 + """Liteyuki 的 守护进程 + """ + def __init__(self, **kwargs: RawConfig): + """Liteyuki Daemon Init + Args: + **kwargs: 其他配置项 """ - pass + # 加载配置项 + self.config: RawConfig = kwargs + # 获取配置文件目录 + if isinstance(config_dir := kwargs.get("config_dir", None), str): + self.config = merge_dicts(self.config, load_from_dir(config_dir)) + # 插入扁平化配置 + self.config = merge_dicts(self.config, flatten_dict(self.config)) + + # 初始化日志 + set_level(self.config.get("log_level", "INFO")) + + logger.debug( + "configs: %s" % pretty_format(self.config, indent=2) + ) + + + async def _run(self): + """liteyukibot事件循环入口 + """ + # load plugins + + # run asgi app + asyncio.create_task( + run_app( + host=self.config.get("host", "127.0.0.1"), + port=self.config.get("port", 8080), + ) + ) + # 挂起 + logger.info("Liteyuki Daemon is running...") + await asyncio.Event().wait() def run(self): - """liteyukibot入口函数 + """Daemon入口函数 """ - asyncio.run(self._run()) \ No newline at end of file + try: + asyncio.run(self._run()) + except KeyboardInterrupt: + logger.info("Liteyuki Daemon is exiting...") + + def bind_config[T: BaseModel](self, model: Type[T]) -> T: + """将配置绑定到 Pydantic 模型,推荐使用`pydantic.Field`声明扁平化键字段名 + + Args: + model (Type[T]): Pydantic 模型类 + + Returns: + T: 绑定后的模型实例 + """ + if not issubclass(model, BaseModel): + raise TypeError("The provided model must be a subclass of BaseModel.") + return model(**self.config) \ No newline at end of file diff --git a/liteyukibot/log.py b/liteyukibot/log.py new file mode 100644 index 00000000..e63e4a5c --- /dev/null +++ b/liteyukibot/log.py @@ -0,0 +1,39 @@ +import inspect +import logging +import sys + +from yukilog import default_debug_and_trace_format, default_format, get_logger + +logger = get_logger("INFO") + +class LoguruHandler(logging.Handler): + def emit(self, record: logging.LogRecord): + try: + level = logger.level(record.levelname).name + except ValueError: + level = str(record.levelno) + + frame, depth = inspect.currentframe(), 0 + while frame and (depth == 0 or frame.f_code.co_filename == logging.__file__): + frame = frame.f_back + depth += 1 + + logger.opt(depth=depth, exception=record.exc_info).log( + level, record.getMessage() + ) + +# 替换 logging 的全局日志器 +root_logger = logging.getLogger() +root_logger.handlers = [LoguruHandler()] # 只保留 LoguruHandler +root_logger.setLevel(logging.INFO) + + +def set_level(level: str): + """设置日志级别 + + Args: + level (str): 日志级别 + """ + logger.remove() + logger.add(sys.stdout, format=default_format if level not in ["DEBUG", "TRACE"] else default_debug_and_trace_format, level=level) + logging.getLogger().setLevel(level) \ No newline at end of file diff --git a/liteyukibot/utils/__init__.py b/liteyukibot/utils/__init__.py new file mode 100644 index 00000000..aa7d15a5 --- /dev/null +++ b/liteyukibot/utils/__init__.py @@ -0,0 +1,24 @@ +import json +from typing import Any + + +def pretty_print(obj: Any, indent: int=2) -> None: + """ + 更好地打印对象 + + Args: + obj (Any): 要打印的对象 + """ + print(json.dumps(obj, indent=indent, ensure_ascii=False)) + +def pretty_format(obj: Any, indent: int =2 ) -> str: + """ + 更好地格式化对象 + + Args: + obj (Any): 要格式化的对象 + + Returns: + str: 格式化后的字符串 + """ + return json.dumps(obj, indent=indent, ensure_ascii=False) \ No newline at end of file diff --git a/main.py b/main.py index 37deb142..f7e69122 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,5 @@ -from liteyukibot import Daemon +from liteyukibot import Daemon, config if __name__ == "__main__": - daemon = Daemon() + daemon = Daemon(**config.load_from_yaml("config.yaml") or {}) daemon.run() \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 994195ec..bd89c2e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,37 +7,46 @@ requires-python = ">=3.12" dependencies = [ "fastapi>=0.115.12", "loguru>=0.7.3", - "nonecorn>=0.17.3", + "pip>=25.1", "pydantic>=2.11.3", "pyyaml>=6.0.2", "uvicorn>=0.34.2", "yukilog>=0.1.1", ] +# 开发依赖 [dependency-groups] dev = [ "mypy>=1.15.0", - "pip>=25.1", "pytest>=8.3.5", "ruff>=0.11.7", ] -# build +# 辅助工具配置 +[tool.ruff] +line-length = 140 +src = ["liteyukibot", "main.py"] +select = ["I", "F", "E"] + + +# 构建相关 [build-system] requires = ["pdm-backend >= 2.4.0"] build-backend = "pdm.backend" -[tool.ruff] -line-length = 80 -src = ["liteyukibot", "main.py"] +[tool.pdm.build] +includes = ["liteyukibot"] -# index and sources + +# 索引和源 [[tool.uv.index]] name = "liteyuki-pypi" url = "https://git.liteyuki.icu/api/packages/LiteyukiStudio/pypi/simple" publish-url = "https://git.liteyuki.icu/api/packages/LiteyukiStudio/pypi" explicit = true + +# 从轻雪源安装的包请在此标记 [tool.uv.sources] yukilog = [ { index = "liteyuki-pypi" }, diff --git a/tests/test_config.py b/tests/test_config.py index 909e4dd7..32701458 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,9 +1,19 @@ import tempfile + from liteyukibot import config +from pydantic import BaseModel, Field + + +class ConfigModel(BaseModel): + name: str + version: int + server_host: str = Field(alias="server.host") + server_port: int = Field(alias="server.port") + def test_load_from_yaml(): - # 创建一个临时 YAML 文件内容 + """测试从yaml文件路径加载配置项""" yaml_content = """ name: LiteyukiBot version: 7.0.0 @@ -18,6 +28,7 @@ def test_load_from_yaml(): def test_load_from_json(): + """测试从json文件路径加载配置""" json_content = '{"name": "LiteyukiBot", "version": "7.0.0"}' with tempfile.NamedTemporaryFile("w", delete=False, suffix=".json") as temp_file: temp_file.write(json_content) @@ -29,6 +40,7 @@ def test_load_from_json(): def test_load_from_toml(): + """测试从toml文件路径加载配置""" toml_content = """ [info] name = "LiteyukiBot" @@ -44,6 +56,7 @@ def test_load_from_toml(): def test_flatten_dict(): + """测试扁平化字典""" nested_dict = { "name": "LiteyukiBot", "version": { @@ -66,4 +79,75 @@ def test_flatten_dict(): assert flat_dict["version.patch"] == 0 assert flat_dict["server.db.host"] == "localhost" assert flat_dict["server.db.port"] == 8080 - assert flat_dict["server.tags"] == ["tag1", "tag2"] \ No newline at end of file + assert flat_dict["server.tags"] == ["tag1", "tag2"] + +def test_merge_to_config(): + """测试合并配置""" + old_config = { + "name": "LiteyukiBot", + "version": 7, + "server": { + "host": "localhost", + "port": 8080 + } + } + config.merge_to_config(old_config) + assert config.config["name"] == "LiteyukiBot" + new_config = { + "version": 8, + "server": { + "port": 9090 + }, + "new_key": "new_value" + } + + config.merge_to_config(new_config) + + # config + assert config.config["name"] == "LiteyukiBot" + assert config.config["version"] == 8 + assert config.config["server"]["host"] == "localhost" + assert config.config["server"]["port"] == 9090 + assert config.config["new_key"] == "new_value" + # test flatten_config + assert config.flat_config["name"] == "LiteyukiBot" + assert config.flat_config["version"] == 8 + assert config.flat_config["server.host"] == "localhost" + assert config.flat_config["server.port"] == 9090 + assert config.flat_config["new_key"] == "new_value" + +def test_get_config(): + """测试获取配置项""" + config_data = { + "name": "LiteyukiBot", + "version": 7, + "server": { + "host": "localhost", + "port": 8080 + } + } + config.merge_to_config(config_data) + assert config.get("name") == "LiteyukiBot" + assert config.get("version") == 7 + assert config.get("server.host") == "localhost" + assert config.get("server.port") == 8080 + assert config.get("non_existent_key", default="default_value") == "default_value" + assert config.get("non_existent_key", default=42) == 42 + + +def test_bind(): + """测试配置项绑定到模型""" + config_data = { + "name": "LiteyukiBot", + "version": 7, + "server": { + "host": "localhost", + "port": 8080 + } + } + config.merge_to_config(config_data) + bound_model = config.bind(ConfigModel) + assert bound_model.name == "LiteyukiBot" + assert bound_model.version == 7 + assert bound_model.server_host == "localhost" + assert bound_model.server_port == 8080 \ No newline at end of file diff --git a/tests/test_log.py b/tests/test_log.py new file mode 100644 index 00000000..efd15cad --- /dev/null +++ b/tests/test_log.py @@ -0,0 +1,12 @@ +from liteyukibot import log, logger + + +def test_set_level(): + logger.info("Testing logger level") + logger.debug("Debug message") + log.set_level("DEBUG") + logger.debug("Debug message after level change") + +if __name__ == "__main__": + test_set_level() + print("测试完成,你应该只会看到一次debug信息") \ No newline at end of file diff --git a/uv.lock b/uv.lock index 014950ff..168094c9 100644 --- a/uv.lock +++ b/uv.lock @@ -46,15 +46,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] -[[package]] -name = "exceptiongroup" -version = "1.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, -] - [[package]] name = "fastapi" version = "0.115.12" @@ -78,37 +69,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, ] -[[package]] -name = "h2" -version = "4.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hpack" }, - { name = "hyperframe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1b/38/d7f80fd13e6582fb8e0df8c9a653dcc02b03ca34f4d72f34869298c5baf8/h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f", size = 2150682 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/9e/984486f2d0a0bd2b024bf4bc1c62688fcafa9e61991f041fb0e2def4a982/h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0", size = 60957 }, -] - -[[package]] -name = "hpack" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357 }, -] - -[[package]] -name = "hyperframe" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007 }, -] - [[package]] name = "idna" version = "3.10" @@ -130,11 +90,11 @@ wheels = [ [[package]] name = "liteyukibot" version = "7.0.0" -source = { virtual = "." } +source = { editable = "." } dependencies = [ { name = "fastapi" }, { name = "loguru" }, - { name = "nonecorn" }, + { name = "pip" }, { name = "pydantic" }, { name = "pyyaml" }, { name = "uvicorn" }, @@ -144,7 +104,6 @@ dependencies = [ [package.dev-dependencies] dev = [ { name = "mypy" }, - { name = "pip" }, { name = "pytest" }, { name = "ruff" }, ] @@ -153,7 +112,7 @@ dev = [ requires-dist = [ { name = "fastapi", specifier = ">=0.115.12" }, { name = "loguru", specifier = ">=0.7.3" }, - { name = "nonecorn", specifier = ">=0.17.3" }, + { name = "pip", specifier = ">=25.1" }, { name = "pydantic", specifier = ">=2.11.3" }, { name = "pyyaml", specifier = ">=6.0.2" }, { name = "uvicorn", specifier = ">=0.34.2" }, @@ -163,7 +122,6 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ { name = "mypy", specifier = ">=1.15.0" }, - { name = "pip", specifier = ">=25.1" }, { name = "pytest", specifier = ">=8.3.5" }, { name = "ruff", specifier = ">=0.11.7" }, ] @@ -215,22 +173,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963 }, ] -[[package]] -name = "nonecorn" -version = "0.17.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "exceptiongroup" }, - { name = "h11" }, - { name = "h2" }, - { name = "priority" }, - { name = "wsproto" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/81/51/3bff01e452f1d12848d1c88be8a59c361d2a7e1b16e1e99c968af62ae8d4/nonecorn-0.17.3.tar.gz", hash = "sha256:8c75c366979f5b11c2bea646133ca1c52f6e20ee03948c192518c496f387ec55", size = 180485 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/cb/a3a50d0e82554845fb93d8a20c119b648f27fa818e4352ca8e6d0f6dda72/nonecorn-0.17.3-py3-none-any.whl", hash = "sha256:8c391be6ae61655805ab2b5c4637953f040db1268ddc75be720f450a7ebe2ad0", size = 72065 }, -] - [[package]] name = "packaging" version = "25.0" @@ -258,15 +200,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, ] -[[package]] -name = "priority" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/3c/eb7c35f4dcede96fca1842dac5f4f5d15511aa4b52f3a961219e68ae9204/priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0", size = 24792 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/5f/82c8074f7e84978129347c2c6ec8b6c59f3584ff1a20bc3c940a3e061790/priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", size = 8946 }, -] - [[package]] name = "pydantic" version = "2.11.3" @@ -454,26 +387,14 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083 }, ] -[[package]] -name = "wsproto" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c9/4a/44d3c295350d776427904d73c189e10aeae66d7f555bb2feee16d1e4ba5a/wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065", size = 53425 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/58/e860788190eba3bcce367f74d29c4675466ce8dddfba85f7827588416f01/wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736", size = 24226 }, -] - [[package]] name = "yukilog" -version = "0.1.1" +version = "1.0.11" source = { registry = "https://git.liteyuki.icu/api/packages/LiteyukiStudio/pypi/simple" } dependencies = [ { name = "loguru" }, ] -sdist = { url = "https://git.liteyuki.icu/api/packages/LiteyukiStudio/pypi/files/yukilog/0.1.1/yukilog-0.1.1.tar.gz", hash = "sha256:aeaf3eed9828325196684fbf9fa284c8a2c01353f1ccdf9a2a649268b05b2da3" } +sdist = { url = "https://git.liteyuki.icu/api/packages/LiteyukiStudio/pypi/files/yukilog/1.0.11/yukilog-1.0.11.tar.gz", hash = "sha256:84d1fbb2825b2e9b2bffa00617ec4d6b7a4723f7a9b100a9500b1f419d04faad" } wheels = [ - { url = "https://git.liteyuki.icu/api/packages/LiteyukiStudio/pypi/files/yukilog/0.1.1/yukilog-0.1.1-py3-none-any.whl", hash = "sha256:27f778d22e6ca26e88197a413b268af709a64ef56c457dc533018da2e264248e" }, + { url = "https://git.liteyuki.icu/api/packages/LiteyukiStudio/pypi/files/yukilog/1.0.11/yukilog-1.0.11-py3-none-any.whl", hash = "sha256:87aba342ee0b9fd51d2768eab7689bdc6e0a633d89055dcecca16168135013d8" }, ]