{"id":25858766,"url":"https://github.com/arcletproject/letoderea","last_synced_at":"2025-03-01T20:33:24.943Z","repository":{"id":57411330,"uuid":"429860825","full_name":"ArcletProject/Letoderea","owner":"ArcletProject","description":"High-performance, simple-structured event system with Dependency injection","archived":false,"fork":false,"pushed_at":"2025-02-25T04:15:51.000Z","size":471,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-02-25T05:21:10.735Z","etag":null,"topics":["asyncio","dependency-injection","event-system","eventbus","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ArcletProject.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-19T16:16:32.000Z","updated_at":"2025-02-25T04:15:55.000Z","dependencies_parsed_at":"2024-05-03T15:59:40.295Z","dependency_job_id":"0174a26f-bdd6-47f9-9beb-05645cbdf2f9","html_url":"https://github.com/ArcletProject/Letoderea","commit_stats":{"total_commits":40,"total_committers":2,"mean_commits":20.0,"dds":"0.22499999999999998","last_synced_commit":"3f47b90d6e26b9794b66380bc052efcc7bd301d1"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArcletProject%2FLetoderea","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArcletProject%2FLetoderea/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArcletProject%2FLetoderea/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArcletProject%2FLetoderea/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ArcletProject","download_url":"https://codeload.github.com/ArcletProject/Letoderea/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241421937,"owners_count":19960384,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asyncio","dependency-injection","event-system","eventbus","python"],"created_at":"2025-03-01T20:33:24.202Z","updated_at":"2025-03-01T20:33:24.933Z","avatar_url":"https://github.com/ArcletProject.png","language":"Python","readme":"# Letoderea\n[![Licence](https://img.shields.io/github/license/ArcletProject/Letoderea)](https://github.com/ArcletProject/Letoderea/blob/master/LICENSE)\n[![PyPI](https://img.shields.io/pypi/v/arclet-letoderea)](https://pypi.org/project/arclet-letoderea)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/arclet-letoderea)](https://www.python.org/)\n[![codecov](https://codecov.io/gh/ArcletProject/Letoderea/branch/main/graph/badge.svg?token=DOMUPLN5XO)](https://codecov.io/gh/ArcletProject/Letoderea)\n\n一个高性能，结构简洁，依赖于 Python内置库`asyncio` 的事件系统, 设计灵感来自[`Graia BroadcastControl`](https://github.com/GraiaProject/BroadcastControl)。\n\n项目仍处于开发阶段，部分内容可能会有较大改变\n\n## 安装\n### 从 PyPI 安装\n``` bash\npip install arclet-letoderea\n```\n\n## 样例\n\n### 基本使用\n```python\nimport asyncio\nimport arclet.letoderea as le\n\n@le.make_event\nclass TestEvent:\n    name: str = \"Letoderea\"\n\n@le.on()\nasync def test_subscriber(name: str):\n    print(name)\n\nasync def main():\n    await le.publish(TestEvent())\n\nasyncio.run(main())\n```\n\n### 依赖注入\n```python\nimport asyncio\nimport arclet.letoderea as le\n\n@le.make_event\nclass TestEvent:\n    name: str = \"Letoderea\"\n\n@le.depends()\nasync def get_msg(event):\n    return f\"Hello, {event.name}\"\n\n@le.on(TestEvent)\nasync def test_subscriber(msg: str = get_msg):\n    print(msg)\n\nasync def main():\n    await le.publish(TestEvent())\n\nasyncio.run(main())\n```\n\n### 通信\n```python\nimport asyncio\nimport random\nfrom dataclasses import dataclass\nimport arclet.letoderea as le\n\n@le.make_event\n@dataclass\nclass Event:\n    name: str\n\n@le.make_event(name=\"rand\")\n@dataclass\nclass RandomData:\n    seed: int\n    \n    __result_type__ = float\n\n@le.on(RandomData)\ndef random_subscriber(seed: int):\n    return random.Random(seed).random()\n\n@le.on(Event)\nasync def event_subscriber(event: Event):\n    print(f\"Event: {event.name}\")\n    result = await le.post(RandomData(42))\n    if result:\n        print(f\"Random: {result.value}\")\n\nasync def main():\n    await le.publish(Event(\"Letoderea\"))\n    \nasyncio.run(main())\n```\n\n## 说明\n\n### 事件\n\n- 事件可以是任何对象，只要实现了 `gather` 异步方法, 或使用 `EventSystem.define` 并传入 `supplier` 参数\n- `gather` 方法的参数为 `Contexts` 类型，用于传递上下文信息\n- 事件可以通过 `gather` 方法将自身想要传递的信息整合进 `Contexts` 中\n- 事件可以携带 `Provider`，它们会在事件被订阅时注入到订阅者中\n- 订阅子类事件时，父类事件的 `Provider` 会被继承\n- 订阅父类事件时，其子类事件也会被分发给订阅者\n\n### 订阅\n\n- 通过 `Scope.register`, `EventSystem.on`, `EventSystem.use` 装饰器可以将一个函数注册为事件的订阅者\n- 上述方法会返回 `Subscriber` 类型对象，可以通过其 `.dispose` 方法取消订阅\n- 订阅者的参数可以是任何类型，事件系统会尝试从 `Contexts` 中查找对应的值并注入\n- 默认情况下 `event` 为名字的参数会被注入为事件的实例\n- 订阅者可以设置优先级，值越小优先级越高\n\n### 上下文\n\n- `Contexts` 类型是一个 `dict` 的子类，用于传递上下文信息，除此之外与 `dict` 没有区别\n- `Contexts` 默认包含 `$event` 键，其值为事件的实例\n- `Contexts` 默认包含 `$subscriber` 键，其值为订阅者的实例\n- 在订阅者的函数执行后，其结果会被存储在 `Contexts` 中，键为 `$result`\n- 若在解析参数时抛出异常，异常值会被存储在 `Contexts` 中，键为 `$error`\n\n\n### 依赖注入\n\n- `Provider[T]` 负责管理参数的注入, 其会尝试从 `Contexts` 中选择需求的参数返回\n- 对于订阅者的每个参数，在订阅者注册后，事件系统会遍历该订阅者拥有的所有 `Provider`，\n    并依次调用 `Provider.validate` 方法，如果返回 `True`，则将该 `Provider` 绑定到该参数上。\n    当进行依赖解析时，事件系统会遍历该参数绑定的所有 `Provider`，并依次调用 `Provider.__call__` 方法，\n    如果返回值不为 `None`，则将该返回值注入到该参数中。\n- `Provider.validate` 方法用于验证订阅函数的参数是否为该 `Provider` 可绑定的参数。默认实现为检查目标参数的类型声明是否为 `T`。\n    也可以通过重写该方法来实现自定义的验证逻辑。\n- `Provider.__call__` 方法用于从 `Contexts` 中获取参数\n- 原则上 `Provider` 只负责注入单一类型的参数。若想处理多个类型的参数，可以声明自己为 `Provider[Union[A, B, ...]]` 类型，\n    并在 `Provider.validate` 方法中进行自定义的逻辑判断。但更推荐的做法是构造多个 `Provider`，并将其绑定到同一个参数上。\n- 对于特殊的辅助器 `Depend`，事件系统会将其作为特殊的 `Provider` 处理，绑定了 `Depend` 的参数在解析时将直接调用设置在\n    `Depend` 上的方法。\n- `Provider` 可以设置优先级，值越小优先级越高\n- 另有 `ProviderFactory`，用于集成多个 `Provider` 的分配，以方便 `event.providers` 的设置\n\n### 事件发布\n\n- 一般情况下通过 `EventSystem.publish` 或 `EventSystem.post` 方法可以发布一个事件让事件系统进行处理\n- `publish` 会处理所有合适的订阅者，而 `post` 会在某一个订阅者返回了有效值后停止处理，并返回该值\n- `Publisher.validate` 方法用于验证该事件是否为该发布者的订阅者所关注的事件\n- `Publisher.supply` 方法用于让事件系统主动获取事件并分发给所有订阅者\n- `EventSystem.use`, `Scope.register` 可以指定 `Publisher`\n- 通过 `EventSystem.define` 可以便捷的定义发布者，并在 `.use` 等处通过定义的名字引用\n\n### 层次\n\n- `Scope` 类负责管理订阅者与事件的交互\n- 所有的订阅者都会存储在 `Scope` 中\n- `Scope.emit` 和 `Scope.bail` 方法用于将事件直接分发给属于自身的订阅者，`emit` 与 `bail` 的区别类似于 `publish` 与 `post`\n- `EventSystem.publish` 与 `EventSystem.post` 可以指定 `Scope`\n\n### 传播\n\n- `Subscriber` 类型对象被创建后，其能通过 `propagate` 方法来设置订阅的同级传播。\n- 传播中的订阅者会在当前订阅者执行后或执行前执行，取决于 `propagate` 的参数 `prepend`。 向后传播的订阅者能拿到上一个订阅者的返回值。\n- 传播中的订阅者会继承当前订阅者的 `Provider`。\n- 传播中的订阅者可以通过返回特殊值 `STOP` 来中止同级传播。\n- 对于传播中的订阅者，若其依赖注入的参数未满足，则会尝试延迟执行。若所有的传播订阅者都无法满足其依赖注入的参数，则会抛出异常。\n- `propagate` 方法可以为传播订阅者设置优先级，值越小优先级越高。\n- `propagate` 方法可以接受特殊类型 `Propagator`, 其有一个 `compose` 方法，用来提供一系列的传播订阅者。\n\n\n## 开源协议\n本实现以 MIT 为开源协议。\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farcletproject%2Fletoderea","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farcletproject%2Fletoderea","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farcletproject%2Fletoderea/lists"}