arclet-letoderea


Namearclet-letoderea JSON
Version 0.9.2 PyPI version JSON
download
home_page
SummaryA high-performance, simple-structured event system, relies on asyncio
upload_time2023-12-05 12:18:50
maintainer
docs_urlNone
author
requires_python>=3.8
licenseMIT
keywords asyncio event-system dispatch dependency-injection
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Letoderea
[![Licence](https://img.shields.io/github/license/ArcletProject/Letoderea)](https://github.com/ArcletProject/Letoderea/blob/master/LICENSE)
[![PyPI](https://img.shields.io/pypi/v/arclet-letoderea)](https://pypi.org/project/arclet-letoderea)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/arclet-letoderea)](https://www.python.org/)

一个高性能,结构简洁,依赖于 Python内置库`asyncio` 的事件系统, 设计灵感来自[`Graia BroadcastControl`](https://github.com/GraiaProject/BroadcastControl)。

项目仍处于开发阶段,部分内容可能会有较大改变

## 安装
### 从 PyPI 安装
``` bash
pip install arclet-letoderea
```

## 样例

```python
import asyncio
from arclet.letoderea import EventSystem, make_event

es = EventSystem()

@make_event
class TestEvent:
    name = "Letoderea"


@es.on()
async def test_subscriber(name: str):
    print(name)

async def main():
    await es.publish(TestEvent())

asyncio.run(main())
```

## 说明

### 事件

- 事件可以是任何对象,只要实现了 `gather` 异步方法
- `gather` 方法的参数为 `Contexts` 类型,用于传递上下文信息
- 事件可以通过 `gather` 方法将自身想要传递的信息整合进 `Contexts` 中
- 事件可以携带 `Provider` 与 `Auxiliary`,它们会在事件被订阅时注入到订阅者中
- 订阅子类事件时,父类事件的 `Provider` 与 `Auxiliary` 会被继承
- 订阅父类事件时,其子类事件也会被分发给订阅者

### 订阅

- 通过 `Publisher.register`, `EventSystem.on` 或 `subscribe` 装饰器可以将一个函数注册为事件的订阅者
- 订阅者的参数可以是任何类型,事件系统会尝试从 `Contexts` 中查找对应的值并注入
- 默认情况下 `event` 为名字的参数会被注入为事件的实例
- 订阅者可以设置优先级,值越小优先级越高

### 上下文

- `Contexts` 类型是一个 `dict` 的子类,用于传递上下文信息,除此之外与 `dict` 没有区别
- `Contexts` 默认包含 `$event` 键,其值为事件的实例
- `Contexts` 默认包含 `$subscriber` 键,其值为订阅者的实例


### 依赖注入

- `Provider[T]` 负责管理参数的注入, 其会尝试从 `Contexts` 中选择需求的参数返回
- 对于订阅者的每个参数,在订阅者注册后,事件系统会遍历该订阅者拥有的所有 `Provider`,
    并依次调用 `Provider.validate` 方法,如果返回 `True`,则将该 `Provider` 绑定到该参数上。
    当进行依赖解析时,事件系统会遍历该参数绑定的所有 `Provider`,并依次调用 `Provider.__call__` 方法,
    如果返回值不为 `None`,则将该返回值注入到该参数中。
- `Provider.validate` 方法用于验证订阅函数的参数是否为该 `Provider` 可绑定的参数。默认实现为检查目标参数的类型声明是否为 `T`。
    也可以通过重写该方法来实现自定义的验证逻辑。
- `Provider.__call__` 方法用于从 `Contexts` 中获取参数
- 原则上 `Provider` 只负责注入单一类型的参数。若想处理多个类型的参数,可以声明自己为 `Provider[Union[A, B, ...]]` 类型,
    并在 `Provider.validate` 方法中进行自定义的逻辑判断。但更推荐的做法是构造多个 `Provider`,并将其绑定到同一个参数上。
- 对于特殊的辅助器 `Depend`,事件系统会将其作为特殊的 `Provider` 处理,绑定了 `Depend` 的参数在解析时将直接调用
    `Depend.__call__` 方法。
- `Provider` 可以设置优先级,值越小优先级越高
- 另有 `ProviderFactory`,用于集成多个 `Provider` 的分配,以方便 `event.providers` 的设置

### 事件发布

- 一般情况下通过 `EventSystem.publish` 方法可以发布一个事件让事件系统进行处理
- `Publisher` 类负责管理订阅者与事件的交互
- `Publisher.validate` 方法用于验证该事件是否为该发布者的订阅者所关注的事件
- `Publisher.publish` 方法用于将事件直接分发给属于自身的订阅者
- `Publisher.supply` 方法用于让事件系统主动获取事件并分发给所有订阅者
- `EventSystem.on` 与 `EventSystem.publish` 可以指定 `Publisher`

### 辅助

- `Auxiliary` 提供了一系列辅助方法,方便事件的处理
- `Auxiliary` 分为 `Judge`, `Supply` 与 `Depend` 三类:
    - `Judge`: 用于判断此时是否应该处理事件
    - `Supply`: 用于为 `Contexts` 提供额外的信息
    - `Depend`: 用于依赖注入
- `Auxiliary.scopes` 声明了 `Auxiliary` 的作用域:
    - `prepare`: 表示该 `Auxiliary` 会在依赖注入之前执行
    - `parsing`: 表示该 `Auxiliary` 会在依赖注入解析时执行
    - `complete`: 表示该 `Auxiliary` 会在依赖注入完成后执行
    - `cleanup`: 表示该 `Auxiliary` 会在事件处理完成后执行
- `Auxiliary` 可以设置 `CombineMode`, 用来设置多个 `Auxiliary` 的组合方式:
    - `single`: 表示该 `Auxiliary` 独立执行
    - `and`: 表示该 `Auxiliary` 的执行结果应该与其他 `Auxiliary` 的执行结果都为有效值
    - `or`: 表示该 `Auxiliary` 的执行结果应该与其他 `Auxiliary` 的执行结果至少有一个为有效值
- `Auxiliary` 可以设置优先级,值越小优先级越高

## 开源协议
本实现以 MIT 为开源协议。


            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "arclet-letoderea",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "",
    "keywords": "asyncio,event-system,dispatch,dependency-injection",
    "author": "",
    "author_email": "RF-Tar-Railt <rf_tar_railt@qq.com>",
    "download_url": "https://files.pythonhosted.org/packages/db/9f/3293525060bc26fdffac298a9ba99b5e01e08f5de48c58b82ba55e31cb5a/arclet-letoderea-0.9.2.tar.gz",
    "platform": null,
    "description": "# 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\n\u4e00\u4e2a\u9ad8\u6027\u80fd\uff0c\u7ed3\u6784\u7b80\u6d01\uff0c\u4f9d\u8d56\u4e8e Python\u5185\u7f6e\u5e93`asyncio` \u7684\u4e8b\u4ef6\u7cfb\u7edf, \u8bbe\u8ba1\u7075\u611f\u6765\u81ea[`Graia BroadcastControl`](https://github.com/GraiaProject/BroadcastControl)\u3002\n\n\u9879\u76ee\u4ecd\u5904\u4e8e\u5f00\u53d1\u9636\u6bb5\uff0c\u90e8\u5206\u5185\u5bb9\u53ef\u80fd\u4f1a\u6709\u8f83\u5927\u6539\u53d8\n\n## \u5b89\u88c5\n### \u4ece PyPI \u5b89\u88c5\n``` bash\npip install arclet-letoderea\n```\n\n## \u6837\u4f8b\n\n```python\nimport asyncio\nfrom arclet.letoderea import EventSystem, make_event\n\nes = EventSystem()\n\n@make_event\nclass TestEvent:\n    name = \"Letoderea\"\n\n\n@es.on()\nasync def test_subscriber(name: str):\n    print(name)\n\nasync def main():\n    await es.publish(TestEvent())\n\nasyncio.run(main())\n```\n\n## \u8bf4\u660e\n\n### \u4e8b\u4ef6\n\n- \u4e8b\u4ef6\u53ef\u4ee5\u662f\u4efb\u4f55\u5bf9\u8c61\uff0c\u53ea\u8981\u5b9e\u73b0\u4e86 `gather` \u5f02\u6b65\u65b9\u6cd5\n- `gather` \u65b9\u6cd5\u7684\u53c2\u6570\u4e3a `Contexts` \u7c7b\u578b\uff0c\u7528\u4e8e\u4f20\u9012\u4e0a\u4e0b\u6587\u4fe1\u606f\n- \u4e8b\u4ef6\u53ef\u4ee5\u901a\u8fc7 `gather` \u65b9\u6cd5\u5c06\u81ea\u8eab\u60f3\u8981\u4f20\u9012\u7684\u4fe1\u606f\u6574\u5408\u8fdb `Contexts` \u4e2d\n- \u4e8b\u4ef6\u53ef\u4ee5\u643a\u5e26 `Provider` \u4e0e `Auxiliary`\uff0c\u5b83\u4eec\u4f1a\u5728\u4e8b\u4ef6\u88ab\u8ba2\u9605\u65f6\u6ce8\u5165\u5230\u8ba2\u9605\u8005\u4e2d\n- \u8ba2\u9605\u5b50\u7c7b\u4e8b\u4ef6\u65f6\uff0c\u7236\u7c7b\u4e8b\u4ef6\u7684 `Provider` \u4e0e `Auxiliary` \u4f1a\u88ab\u7ee7\u627f\n- \u8ba2\u9605\u7236\u7c7b\u4e8b\u4ef6\u65f6\uff0c\u5176\u5b50\u7c7b\u4e8b\u4ef6\u4e5f\u4f1a\u88ab\u5206\u53d1\u7ed9\u8ba2\u9605\u8005\n\n### \u8ba2\u9605\n\n- \u901a\u8fc7 `Publisher.register`, `EventSystem.on` \u6216 `subscribe` \u88c5\u9970\u5668\u53ef\u4ee5\u5c06\u4e00\u4e2a\u51fd\u6570\u6ce8\u518c\u4e3a\u4e8b\u4ef6\u7684\u8ba2\u9605\u8005\n- \u8ba2\u9605\u8005\u7684\u53c2\u6570\u53ef\u4ee5\u662f\u4efb\u4f55\u7c7b\u578b\uff0c\u4e8b\u4ef6\u7cfb\u7edf\u4f1a\u5c1d\u8bd5\u4ece `Contexts` \u4e2d\u67e5\u627e\u5bf9\u5e94\u7684\u503c\u5e76\u6ce8\u5165\n- \u9ed8\u8ba4\u60c5\u51b5\u4e0b `event` \u4e3a\u540d\u5b57\u7684\u53c2\u6570\u4f1a\u88ab\u6ce8\u5165\u4e3a\u4e8b\u4ef6\u7684\u5b9e\u4f8b\n- \u8ba2\u9605\u8005\u53ef\u4ee5\u8bbe\u7f6e\u4f18\u5148\u7ea7\uff0c\u503c\u8d8a\u5c0f\u4f18\u5148\u7ea7\u8d8a\u9ad8\n\n### \u4e0a\u4e0b\u6587\n\n- `Contexts` \u7c7b\u578b\u662f\u4e00\u4e2a `dict` \u7684\u5b50\u7c7b\uff0c\u7528\u4e8e\u4f20\u9012\u4e0a\u4e0b\u6587\u4fe1\u606f\uff0c\u9664\u6b64\u4e4b\u5916\u4e0e `dict` \u6ca1\u6709\u533a\u522b\n- `Contexts` \u9ed8\u8ba4\u5305\u542b `$event` \u952e\uff0c\u5176\u503c\u4e3a\u4e8b\u4ef6\u7684\u5b9e\u4f8b\n- `Contexts` \u9ed8\u8ba4\u5305\u542b `$subscriber` \u952e\uff0c\u5176\u503c\u4e3a\u8ba2\u9605\u8005\u7684\u5b9e\u4f8b\n\n\n### \u4f9d\u8d56\u6ce8\u5165\n\n- `Provider[T]` \u8d1f\u8d23\u7ba1\u7406\u53c2\u6570\u7684\u6ce8\u5165, \u5176\u4f1a\u5c1d\u8bd5\u4ece `Contexts` \u4e2d\u9009\u62e9\u9700\u6c42\u7684\u53c2\u6570\u8fd4\u56de\n- \u5bf9\u4e8e\u8ba2\u9605\u8005\u7684\u6bcf\u4e2a\u53c2\u6570\uff0c\u5728\u8ba2\u9605\u8005\u6ce8\u518c\u540e\uff0c\u4e8b\u4ef6\u7cfb\u7edf\u4f1a\u904d\u5386\u8be5\u8ba2\u9605\u8005\u62e5\u6709\u7684\u6240\u6709 `Provider`\uff0c\n    \u5e76\u4f9d\u6b21\u8c03\u7528 `Provider.validate` \u65b9\u6cd5\uff0c\u5982\u679c\u8fd4\u56de `True`\uff0c\u5219\u5c06\u8be5 `Provider` \u7ed1\u5b9a\u5230\u8be5\u53c2\u6570\u4e0a\u3002\n    \u5f53\u8fdb\u884c\u4f9d\u8d56\u89e3\u6790\u65f6\uff0c\u4e8b\u4ef6\u7cfb\u7edf\u4f1a\u904d\u5386\u8be5\u53c2\u6570\u7ed1\u5b9a\u7684\u6240\u6709 `Provider`\uff0c\u5e76\u4f9d\u6b21\u8c03\u7528 `Provider.__call__` \u65b9\u6cd5\uff0c\n    \u5982\u679c\u8fd4\u56de\u503c\u4e0d\u4e3a `None`\uff0c\u5219\u5c06\u8be5\u8fd4\u56de\u503c\u6ce8\u5165\u5230\u8be5\u53c2\u6570\u4e2d\u3002\n- `Provider.validate` \u65b9\u6cd5\u7528\u4e8e\u9a8c\u8bc1\u8ba2\u9605\u51fd\u6570\u7684\u53c2\u6570\u662f\u5426\u4e3a\u8be5 `Provider` \u53ef\u7ed1\u5b9a\u7684\u53c2\u6570\u3002\u9ed8\u8ba4\u5b9e\u73b0\u4e3a\u68c0\u67e5\u76ee\u6807\u53c2\u6570\u7684\u7c7b\u578b\u58f0\u660e\u662f\u5426\u4e3a `T`\u3002\n    \u4e5f\u53ef\u4ee5\u901a\u8fc7\u91cd\u5199\u8be5\u65b9\u6cd5\u6765\u5b9e\u73b0\u81ea\u5b9a\u4e49\u7684\u9a8c\u8bc1\u903b\u8f91\u3002\n- `Provider.__call__` \u65b9\u6cd5\u7528\u4e8e\u4ece `Contexts` \u4e2d\u83b7\u53d6\u53c2\u6570\n- \u539f\u5219\u4e0a `Provider` \u53ea\u8d1f\u8d23\u6ce8\u5165\u5355\u4e00\u7c7b\u578b\u7684\u53c2\u6570\u3002\u82e5\u60f3\u5904\u7406\u591a\u4e2a\u7c7b\u578b\u7684\u53c2\u6570\uff0c\u53ef\u4ee5\u58f0\u660e\u81ea\u5df1\u4e3a `Provider[Union[A, B, ...]]` \u7c7b\u578b\uff0c\n    \u5e76\u5728 `Provider.validate` \u65b9\u6cd5\u4e2d\u8fdb\u884c\u81ea\u5b9a\u4e49\u7684\u903b\u8f91\u5224\u65ad\u3002\u4f46\u66f4\u63a8\u8350\u7684\u505a\u6cd5\u662f\u6784\u9020\u591a\u4e2a `Provider`\uff0c\u5e76\u5c06\u5176\u7ed1\u5b9a\u5230\u540c\u4e00\u4e2a\u53c2\u6570\u4e0a\u3002\n- \u5bf9\u4e8e\u7279\u6b8a\u7684\u8f85\u52a9\u5668 `Depend`\uff0c\u4e8b\u4ef6\u7cfb\u7edf\u4f1a\u5c06\u5176\u4f5c\u4e3a\u7279\u6b8a\u7684 `Provider` \u5904\u7406\uff0c\u7ed1\u5b9a\u4e86 `Depend` \u7684\u53c2\u6570\u5728\u89e3\u6790\u65f6\u5c06\u76f4\u63a5\u8c03\u7528\n    `Depend.__call__` \u65b9\u6cd5\u3002\n- `Provider` \u53ef\u4ee5\u8bbe\u7f6e\u4f18\u5148\u7ea7\uff0c\u503c\u8d8a\u5c0f\u4f18\u5148\u7ea7\u8d8a\u9ad8\n- \u53e6\u6709 `ProviderFactory`\uff0c\u7528\u4e8e\u96c6\u6210\u591a\u4e2a `Provider` \u7684\u5206\u914d\uff0c\u4ee5\u65b9\u4fbf `event.providers` \u7684\u8bbe\u7f6e\n\n### \u4e8b\u4ef6\u53d1\u5e03\n\n- \u4e00\u822c\u60c5\u51b5\u4e0b\u901a\u8fc7 `EventSystem.publish` \u65b9\u6cd5\u53ef\u4ee5\u53d1\u5e03\u4e00\u4e2a\u4e8b\u4ef6\u8ba9\u4e8b\u4ef6\u7cfb\u7edf\u8fdb\u884c\u5904\u7406\n- `Publisher` \u7c7b\u8d1f\u8d23\u7ba1\u7406\u8ba2\u9605\u8005\u4e0e\u4e8b\u4ef6\u7684\u4ea4\u4e92\n- `Publisher.validate` \u65b9\u6cd5\u7528\u4e8e\u9a8c\u8bc1\u8be5\u4e8b\u4ef6\u662f\u5426\u4e3a\u8be5\u53d1\u5e03\u8005\u7684\u8ba2\u9605\u8005\u6240\u5173\u6ce8\u7684\u4e8b\u4ef6\n- `Publisher.publish` \u65b9\u6cd5\u7528\u4e8e\u5c06\u4e8b\u4ef6\u76f4\u63a5\u5206\u53d1\u7ed9\u5c5e\u4e8e\u81ea\u8eab\u7684\u8ba2\u9605\u8005\n- `Publisher.supply` \u65b9\u6cd5\u7528\u4e8e\u8ba9\u4e8b\u4ef6\u7cfb\u7edf\u4e3b\u52a8\u83b7\u53d6\u4e8b\u4ef6\u5e76\u5206\u53d1\u7ed9\u6240\u6709\u8ba2\u9605\u8005\n- `EventSystem.on` \u4e0e `EventSystem.publish` \u53ef\u4ee5\u6307\u5b9a `Publisher`\n\n### \u8f85\u52a9\n\n- `Auxiliary` \u63d0\u4f9b\u4e86\u4e00\u7cfb\u5217\u8f85\u52a9\u65b9\u6cd5\uff0c\u65b9\u4fbf\u4e8b\u4ef6\u7684\u5904\u7406\n- `Auxiliary` \u5206\u4e3a `Judge`, `Supply` \u4e0e `Depend` \u4e09\u7c7b:\n    - `Judge`: \u7528\u4e8e\u5224\u65ad\u6b64\u65f6\u662f\u5426\u5e94\u8be5\u5904\u7406\u4e8b\u4ef6\n    - `Supply`: \u7528\u4e8e\u4e3a `Contexts` \u63d0\u4f9b\u989d\u5916\u7684\u4fe1\u606f\n    - `Depend`: \u7528\u4e8e\u4f9d\u8d56\u6ce8\u5165\n- `Auxiliary.scopes` \u58f0\u660e\u4e86 `Auxiliary` \u7684\u4f5c\u7528\u57df:\n    - `prepare`: \u8868\u793a\u8be5 `Auxiliary` \u4f1a\u5728\u4f9d\u8d56\u6ce8\u5165\u4e4b\u524d\u6267\u884c\n    - `parsing`: \u8868\u793a\u8be5 `Auxiliary` \u4f1a\u5728\u4f9d\u8d56\u6ce8\u5165\u89e3\u6790\u65f6\u6267\u884c\n    - `complete`: \u8868\u793a\u8be5 `Auxiliary` \u4f1a\u5728\u4f9d\u8d56\u6ce8\u5165\u5b8c\u6210\u540e\u6267\u884c\n    - `cleanup`: \u8868\u793a\u8be5 `Auxiliary` \u4f1a\u5728\u4e8b\u4ef6\u5904\u7406\u5b8c\u6210\u540e\u6267\u884c\n- `Auxiliary` \u53ef\u4ee5\u8bbe\u7f6e `CombineMode`, \u7528\u6765\u8bbe\u7f6e\u591a\u4e2a `Auxiliary` \u7684\u7ec4\u5408\u65b9\u5f0f:\n    - `single`: \u8868\u793a\u8be5 `Auxiliary` \u72ec\u7acb\u6267\u884c\n    - `and`: \u8868\u793a\u8be5 `Auxiliary` \u7684\u6267\u884c\u7ed3\u679c\u5e94\u8be5\u4e0e\u5176\u4ed6 `Auxiliary` \u7684\u6267\u884c\u7ed3\u679c\u90fd\u4e3a\u6709\u6548\u503c\n    - `or`: \u8868\u793a\u8be5 `Auxiliary` \u7684\u6267\u884c\u7ed3\u679c\u5e94\u8be5\u4e0e\u5176\u4ed6 `Auxiliary` \u7684\u6267\u884c\u7ed3\u679c\u81f3\u5c11\u6709\u4e00\u4e2a\u4e3a\u6709\u6548\u503c\n- `Auxiliary` \u53ef\u4ee5\u8bbe\u7f6e\u4f18\u5148\u7ea7\uff0c\u503c\u8d8a\u5c0f\u4f18\u5148\u7ea7\u8d8a\u9ad8\n\n## \u5f00\u6e90\u534f\u8bae\n\u672c\u5b9e\u73b0\u4ee5 MIT \u4e3a\u5f00\u6e90\u534f\u8bae\u3002\n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A high-performance, simple-structured event system, relies on asyncio",
    "version": "0.9.2",
    "project_urls": {
        "Homepage": "https://github.com/ArcletProject/Letoderea",
        "Repository": "https://github.com/ArcletProject/Letoderea"
    },
    "split_keywords": [
        "asyncio",
        "event-system",
        "dispatch",
        "dependency-injection"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "6a9632219a031c2f0b129e00a1169f67281d465034569ec973f191189c2253d8",
                "md5": "9fa5dce2665f192a9913e1e0346efcee",
                "sha256": "3e8c9d1e4a20835469fc0de4a9e556dd0b2162f6afee2285eaf7d84f075a277b"
            },
            "downloads": -1,
            "filename": "arclet_letoderea-0.9.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "9fa5dce2665f192a9913e1e0346efcee",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 20619,
            "upload_time": "2023-12-05T12:18:48",
            "upload_time_iso_8601": "2023-12-05T12:18:48.094156Z",
            "url": "https://files.pythonhosted.org/packages/6a/96/32219a031c2f0b129e00a1169f67281d465034569ec973f191189c2253d8/arclet_letoderea-0.9.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "db9f3293525060bc26fdffac298a9ba99b5e01e08f5de48c58b82ba55e31cb5a",
                "md5": "e51c491bd9ce925acd33d49c65dc9d66",
                "sha256": "75ca11e3a27a44769255ecd2de9f761c079c9291ab07f1c38cd8eb3b065ee8da"
            },
            "downloads": -1,
            "filename": "arclet-letoderea-0.9.2.tar.gz",
            "has_sig": false,
            "md5_digest": "e51c491bd9ce925acd33d49c65dc9d66",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 21633,
            "upload_time": "2023-12-05T12:18:50",
            "upload_time_iso_8601": "2023-12-05T12:18:50.208708Z",
            "url": "https://files.pythonhosted.org/packages/db/9f/3293525060bc26fdffac298a9ba99b5e01e08f5de48c58b82ba55e31cb5a/arclet-letoderea-0.9.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-12-05 12:18:50",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "ArcletProject",
    "github_project": "Letoderea",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "arclet-letoderea"
}
        
Elapsed time: 0.15565s