diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..4a9cb5ed --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,51 @@ +name: Docs + +on: + push: + branches: [main] + paths: + - "docs/**" + - "mkdocs.yml" + - "src/genelab/**" + - "pyproject.toml" + - "README.md" + - "CONTRIBUTING.md" + - ".github/workflows/docs.yml" + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - uses: astral-sh/setup-uv@v8.1.0 + with: + enable-cache: true + python-version: "3.12" + - run: uv sync --extra docs --extra torch-cpu + - run: uv run mkdocs build --strict + - uses: actions/configure-pages@v5 + - uses: actions/upload-pages-artifact@v3 + with: + path: site + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 58cbacdd..7932d2f7 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,7 @@ AGENTS.md /tmp # train log -/logs \ No newline at end of file +/logs + +# mkdocs build output +/site/ \ No newline at end of file diff --git a/README.md b/README.md index 49359031..3d6ab37e 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,8 @@ uv run genelab project new my_robot_project ## Documentation +- [Documentation site](https://krahsu.github.io/GeneLab/) — full guides, CLI reference, concepts, + and auto-generated API docs. Bilingual (English default, 中文 at `/zh/`). - [Examples](examples/README.md): bundled tasks, demo scripts, config overrides, and downstream project integration. - [中文 README](docs/README_CN.md): concise Chinese project overview. diff --git a/docs/README_CN.md b/docs/README_CN.md index 9f1331aa..ea565f68 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -1,5 +1,8 @@ # GeneLab +> **本文已迁移至 [文档站点(中文)](https://krahsu.github.io/GeneLab/zh/)。** +> 完整中文文档(CLI 参考、核心概念、API 自动生成参考)请见站点;本文件保留以兼容旧链接。 + GeneLab 是一个面向强化学习与机器人研究的 Isaac Lab 风格 API,由 [Genesis](https://github.com/Genesis-Embodied-AI/Genesis) 提供仿真后端。它保留了机器人、 环境、任务注册,manager-based MDP 配置,以及 CLI 调度这些常见组织方式。 diff --git a/docs/api/reference.md b/docs/api/reference.md new file mode 100644 index 00000000..c6b15133 --- /dev/null +++ b/docs/api/reference.md @@ -0,0 +1,44 @@ +# API Reference + +Auto-generated from the source docstrings via +[`mkdocstrings`](https://mkdocstrings.github.io/). The API surface below covers GeneLab's public +core. Extension packages document their own APIs in their own projects. + +!!! note "Single-source" + The API reference is generated from English docstrings and is shared by both language + versions of this site. + +--- + +## `genelab.lab` + +The public facade. Most user code should import from here rather than from `genelab.registry` +directly. + +::: genelab.lab + options: + show_if_no_docstring: true + +--- + +## `genelab.registry` + +::: genelab.registry + options: + show_if_no_docstring: true + +--- + +## `genelab.configs` + +::: genelab.configs + options: + show_if_no_docstring: true + +--- + +## `genelab.cache` + +::: genelab.cache + options: + show_if_no_docstring: true diff --git a/docs/cli/overview.en.md b/docs/cli/overview.en.md new file mode 100644 index 00000000..b6c40185 --- /dev/null +++ b/docs/cli/overview.en.md @@ -0,0 +1,46 @@ +# CLI overview + +`genelab` is exposed as a console script via the `genelab = "genelab.cli:main"` entry point. With +`uv` you typically invoke it as: + +```bash +uv run genelab [GLOBAL OPTIONS] [ARGS] +``` + +## Subcommands + +| Subcommand | Purpose | +|------------|---------| +| `cache` | Create project-local simulation cache directories (`.cache/`) and set `XDG_CACHE_HOME` / `MPLCONFIGDIR`. | +| `list robots` | List registered robots from the `ROBOTS` registry. | +| `list envs` | List registered environments from the `ENVS` registry. | +| `list tasks` | List registered tasks from the `TASKS` registry. | +| `play` | Run a registered task in the configured Genesis backend; supports config overrides. | +| `train` | Train a registered task when an RL runner exists; supports multi-GPU via `torchrun`. | +| `project new` | Scaffold a new external extension package with all three registries wired up. | + +## Global options + +These flags work in front of any subcommand: + +- `--version` — print the GeneLab version and exit. +- `--import MODULE` — eagerly import an extension module before dispatching. Repeatable. Useful + when you want to load an extension that does not (yet) ship a `genelab.extensions` entry point. +- `--no-entry-points` — skip auto-discovery of installed extensions via the `genelab.extensions` + entry-point group. Combine with `--import` for fully explicit, reproducible loading. + +## Extension loading + +When the CLI starts, it discovers extensions in this order: + +1. **Entry points** under the `genelab.extensions` group (auto, unless `--no-entry-points`). +2. **Explicit `--import MODULE` flags** (repeatable). +3. **Programmatic** `genelab.registry.load_extension_module(...)` (used by tests and embedding + scripts). + +See [Extensions](../concepts/extensions.md) for details on writing a downstream extension. + +## See also + +- [Play and Train](play-train.md) — config override grammar, multi-GPU training, checkpoints. +- [Project new](project-new.md) — extension package scaffolding. diff --git a/docs/cli/overview.zh.md b/docs/cli/overview.zh.md new file mode 100644 index 00000000..db60903d --- /dev/null +++ b/docs/cli/overview.zh.md @@ -0,0 +1,44 @@ +# CLI 总览 + +`genelab` 通过 `genelab = "genelab.cli:main"` entry point 暴露为命令行脚本。配合 `uv` 时通常这样调用: + +```bash +uv run genelab [全局选项] <子命令> [参数] +``` + +## 子命令 + +| 子命令 | 作用 | +|--------|------| +| `cache` | 创建项目本地仿真缓存目录(`.cache/`),并设置 `XDG_CACHE_HOME` / `MPLCONFIGDIR`。 | +| `list robots` | 列出 `ROBOTS` 注册表里已注册的机器人。 | +| `list envs` | 列出 `ENVS` 注册表里已注册的环境。 | +| `list tasks` | 列出 `TASKS` 注册表里已注册的任务。 | +| `play` | 在 Genesis 后端运行已注册任务,支持配置 override。 | +| `train` | 任务带 RL runner 时启动训练,通过 `torchrun` 支持多 GPU。 | +| `project new` | 生成一个新的下游扩展包骨架,三种注册表全部接通。 | + +## 全局选项 + +放在任何子命令之前: + +- `--version` —— 打印 GeneLab 版本并退出。 +- `--import MODULE` —— 在派发子命令前显式导入一个扩展模块。可重复多次。适合扩展尚未提供 + `genelab.extensions` entry point 时使用。 +- `--no-entry-points` —— 跳过通过 `genelab.extensions` entry-point 组的自动发现。 + 与 `--import` 搭配可实现完全显式、可复现的扩展加载。 + +## 扩展加载顺序 + +CLI 启动时按如下顺序发现扩展: + +1. **Entry points**:`genelab.extensions` 组的自动发现(除非加 `--no-entry-points`)。 +2. **显式 `--import MODULE`**:可多次。 +3. **程序内调用** `genelab.registry.load_extension_module(...)`(测试与嵌入脚本使用)。 + +写下游扩展的详细方式见 [扩展加载](../concepts/extensions.md)。 + +## 另见 + +- [play 与 train](play-train.md) —— override 语法、多 GPU 训练、checkpoint。 +- [新建项目](project-new.md) —— 扩展包骨架生成。 diff --git a/docs/cli/play-train.en.md b/docs/cli/play-train.en.md new file mode 100644 index 00000000..afc94058 --- /dev/null +++ b/docs/cli/play-train.en.md @@ -0,0 +1,83 @@ +# Play and Train + +`play` and `train` share a common dispatch path: resolve `` against the `TASKS` registry, +construct its `TaskCfg`, apply any command-line overrides, and hand the configured task to either +a single rollout (`play`) or the task's RL runner (`train`). + +## Play + +```bash +uv run genelab play [SHORT FLAGS] [-- OVERRIDES] +``` + +### Short flags + +These three short flags rewrite to the corresponding `env.scene.*` overrides: + +| Flag | Equivalent override | Effect | +|------|--------------------|--------| +| `--vis` | `env.scene.vis=true` | Enable Genesis visualization. | +| `--gpu N` | `env.scene.gpu=N` | Pin the rollout to a single GPU index. | +| `--steps N` | `env.scene.steps=N` | Cap episode length to `N` steps. | + +### Override grammar + +After the short flags, any `-- VALUE` argument is parsed as a config override: + +```bash +uv run genelab play \ + --env.scene.dt 0.005 \ + --env.actions.scale 0.5 \ + --env.observations.include_velocity true +``` + +The dotted path walks the `TaskCfg` dataclass tree (typically rooted at `env.*`). Values are +coerced from string using the field's type hint — `int`, `float`, `bool`, `Path`, `list[...]`, +`tuple[...]` are all supported. See [Configs](../concepts/configs.md) for details on the coercion +rules and how to handle unions and `Literal`. + +!!! tip "Flag ordering" + The CLI's `_normalize_run_flags` rewrites `play --steps 5 ...` into + `play --steps 5 ...` so that `argparse.REMAINDER` works. You can keep the short + flags before or after the task ID. + +## Train + +```bash +uv run genelab train [SHORT FLAGS] [-- OVERRIDES] +``` + +Train requires the registered task to expose an RL runner (commonly `rsl_rl_lib`). Override syntax +is identical to `play`. Additional flags: + +| Flag | Effect | +|------|--------| +| `--gpus N` | Dispatch via `torchrun` for `N`-process distributed training. | +| `--checkpoint PATH` | Resume training from a checkpoint file. | + +### Multi-GPU + +`--gpus N` wraps the underlying training entry point with `torchrun --standalone --nproc_per_node=N`. +The task's runner must be `torchrun`-compatible; environments that depend on global Genesis state +typically are. + +When `--gpus N` is set, the CLI also masks `CUDA_VISIBLE_DEVICES` to the requested devices so that +each rank sees a distinct GPU. + +## Examples + +```bash +# Quick local rollout with visualization. +uv run genelab play wuji_hand --vis --steps 200 + +# Train on 4 GPUs with a custom action scale. +uv run genelab train wuji_hand --gpus 4 --env.actions.scale 0.3 + +# Resume from a checkpoint. +uv run genelab train wuji_hand --checkpoint logs/wuji_hand/run_42/model_100.pt +``` + +## See also + +- [Configs](../concepts/configs.md) — full `apply_overrides` semantics. +- [Registry](../concepts/registry.md) — how tasks resolve from ``. diff --git a/docs/cli/play-train.zh.md b/docs/cli/play-train.zh.md new file mode 100644 index 00000000..aa178704 --- /dev/null +++ b/docs/cli/play-train.zh.md @@ -0,0 +1,78 @@ +# play 与 train + +`play` 与 `train` 共享同一条派发路径:在 `TASKS` 注册表查找 ``,构造其 `TaskCfg`, +应用命令行 override,然后交给单次 rollout(`play`)或任务自带的 RL runner(`train`)。 + +## play + +```bash +uv run genelab play [短标志] [-- 覆盖项] +``` + +### 短标志 + +这三个短标志会被改写为对应的 `env.scene.*` override: + +| 标志 | 等价 override | 作用 | +|------|--------------|------| +| `--vis` | `env.scene.vis=true` | 开启 Genesis 可视化。 | +| `--gpu N` | `env.scene.gpu=N` | 把 rollout 锁定到指定 GPU。 | +| `--steps N` | `env.scene.steps=N` | 限制 episode 步数。 | + +### Override 语法 + +短标志之后的任意 `-- VALUE` 都会被当作配置 override: + +```bash +uv run genelab play \ + --env.scene.dt 0.005 \ + --env.actions.scale 0.5 \ + --env.observations.include_velocity true +``` + +点路径会沿 `TaskCfg` 的 dataclass 树(通常以 `env.*` 为根)下钻。字符串值按目标字段类型注解 +自动转换 —— 支持 `int`、`float`、`bool`、`Path`、`list[...]`、`tuple[...]`。详细的转换规则 +和如何处理 union 与 `Literal` 见 [配置系统](../concepts/configs.md)。 + +!!! tip "短标志位置" + CLI 内的 `_normalize_run_flags` 会把 `play --steps 5 ...` 改写成 + `play --steps 5 ...`,让 `argparse.REMAINDER` 正常工作。短标志放在任务 ID 前后 + 都可以。 + +## train + +```bash +uv run genelab train [短标志] [-- 覆盖项] +``` + +`train` 需要任务暴露一个 RL runner(通常是 `rsl_rl_lib`)。Override 语法与 `play` 一致。额外标志: + +| 标志 | 作用 | +|------|------| +| `--gpus N` | 通过 `torchrun` 启动 `N` 进程分布式训练。 | +| `--checkpoint PATH` | 从 checkpoint 文件继续训练。 | + +### 多 GPU + +`--gpus N` 会把底层训练入口包装成 `torchrun --standalone --nproc_per_node=N`。任务自身的 runner +必须支持 `torchrun`;依赖 Genesis 全局状态的环境通常天然兼容。 + +设置 `--gpus N` 时 CLI 还会按所选设备掩码 `CUDA_VISIBLE_DEVICES`,让每个 rank 看到独立 GPU。 + +## 示例 + +```bash +# 本地可视化跑一遍。 +uv run genelab play wuji_hand --vis --steps 200 + +# 4 GPU 训练,并修改 action scale。 +uv run genelab train wuji_hand --gpus 4 --env.actions.scale 0.3 + +# 从 checkpoint 继续。 +uv run genelab train wuji_hand --checkpoint logs/wuji_hand/run_42/model_100.pt +``` + +## 另见 + +- [配置系统](../concepts/configs.md) —— `apply_overrides` 完整语义。 +- [注册表](../concepts/registry.md) —— `` 如何解析。 diff --git a/docs/cli/project-new.en.md b/docs/cli/project-new.en.md new file mode 100644 index 00000000..9483fd5d --- /dev/null +++ b/docs/cli/project-new.en.md @@ -0,0 +1,59 @@ +# Project new + +`genelab project new` scaffolds a downstream extension package — a self-contained Python project +that registers robots, environments, and tasks into GeneLab's global registries. + +## Usage + +```bash +uv run genelab project new my_robot_project +``` + +### Options + +| Option | Default | Effect | +|--------|---------|--------| +| `--path PATH` | `./` | Output directory. Created if missing. | +| `--package NAME` | derived from project name | Python import name (e.g. `my_robot_project`). | +| `--task-id ID` | `/-v0` | The first task ID registered by the scaffold. | +| `--force` | off | Overwrite an existing target directory. Use with care. | + +## What it generates + +``` +my_robot_project/ +├── pyproject.toml # with [project.entry-points."genelab.extensions"] +├── README.md +└── src/ + └── my_robot_project/ + ├── __init__.py # exposes register() entry-point callable + ├── config.py # task-specific dataclass plugged into TaskCfg.env + ├── robots.py # robot registrations + ├── envs.py # environment registrations + └── tasks.py # task registration (using --task-id) +``` + +The generated `pyproject.toml` declares: + +```toml +[project.entry-points."genelab.extensions"] +my_robot_project = "my_robot_project:register" +``` + +Once installed (`uv pip install -e ./my_robot_project`), GeneLab discovers the extension on next +invocation without any `--import` flag. + +## After scaffolding + +```bash +cd my_robot_project +uv pip install -e . # install your extension into the GeneLab venv +uv run genelab list tasks # confirm the new task ID shows up +uv run genelab play --vis +``` + +## See also + +- [Extensions](../concepts/extensions.md) — the three extension-loading pathways and how to keep + loading idempotent. +- [Configs](../concepts/configs.md) — how to type the `TaskCfg.env` field in your `config.py`. diff --git a/docs/cli/project-new.zh.md b/docs/cli/project-new.zh.md new file mode 100644 index 00000000..64d9d18d --- /dev/null +++ b/docs/cli/project-new.zh.md @@ -0,0 +1,58 @@ +# 新建项目 + +`genelab project new` 会生成一个下游扩展包骨架 —— 一个独立 Python 项目,把机器人、环境、 +任务注册进 GeneLab 的全局注册表。 + +## 用法 + +```bash +uv run genelab project new my_robot_project +``` + +### 选项 + +| 选项 | 默认 | 作用 | +|------|------|------| +| `--path PATH` | `./` | 输出目录,不存在时自动创建。 | +| `--package NAME` | 由项目名推导 | Python 导入名(如 `my_robot_project`)。 | +| `--task-id ID` | `/-v0` | 骨架注册的首个任务 ID。 | +| `--force` | 关闭 | 已存在目标目录时覆盖。慎用。 | + +## 生成的内容 + +``` +my_robot_project/ +├── pyproject.toml # 含 [project.entry-points."genelab.extensions"] +├── README.md +└── src/ + └── my_robot_project/ + ├── __init__.py # 暴露 register() entry-point 调用入口 + ├── config.py # 接入 TaskCfg.env 的任务专属 dataclass + ├── robots.py # 机器人注册 + ├── envs.py # 环境注册 + └── tasks.py # 任务注册(使用 --task-id) +``` + +生成的 `pyproject.toml` 声明: + +```toml +[project.entry-points."genelab.extensions"] +my_robot_project = "my_robot_project:register" +``` + +安装后(`uv pip install -e ./my_robot_project`),GeneLab 下次启动会自动发现该扩展, +无需任何 `--import` 标志。 + +## 生成之后 + +```bash +cd my_robot_project +uv pip install -e . # 安装扩展到 GeneLab 的 venv +uv run genelab list tasks # 确认新任务 ID 出现 +uv run genelab play --vis +``` + +## 另见 + +- [扩展加载](../concepts/extensions.md) —— 三种扩展加载路径,以及保持加载幂等的方式。 +- [配置系统](../concepts/configs.md) —— `config.py` 里 `TaskCfg.env` 字段的类型化方法。 diff --git a/docs/concepts/configs.en.md b/docs/concepts/configs.en.md new file mode 100644 index 00000000..76a1aea3 --- /dev/null +++ b/docs/concepts/configs.en.md @@ -0,0 +1,62 @@ +# Configs + +GeneLab's config system is a small dataclass hierarchy under `genelab.configs`: + +``` +TaskCfg +└── env: object # downstream extensions plug in their own env dataclass + ManagerBasedEnvCfg + ├── scene + ├── actions + ├── observations + ├── rewards + ├── terminations + └── events +``` + +`TaskCfg.env` is typed `object` on purpose — downstream extensions plug their own env dataclass +into it without touching core. Most users will subclass `ManagerBasedEnvCfg` for their `env` field. + +## apply_overrides + +The headline function is `apply_overrides(cfg, dict)`, which parses dotted paths and applies them +to the config tree: + +```python +from genelab.configs import apply_overrides + +apply_overrides(cfg, { + "env.scene.dt": "0.005", + "env.scene.steps": "500", + "env.actions.scale": "0.3", + "env.observations.include_velocity": "true", +}) +``` + +### Type coercion + +Strings are coerced using the field's type hint on the target dataclass. Supported targets: + +| Hint | Accepted strings | +|------|------------------| +| `bool` | `true`/`false`/`1`/`0`/`yes`/`no` (case-insensitive) | +| `int` | any base-10 integer literal | +| `float` | any Python float literal | +| `Path` | passed through `pathlib.Path(...)` | +| `list[T]` | comma-separated, each element coerced as `T` | +| `tuple[T, ...]` | comma-separated, each element coerced as `T` | +| `str` | identity | + +If a path doesn't resolve to a known field or the value can't be coerced, `apply_overrides` raises +a descriptive error early — failures surface at config build time, not at simulation runtime. + +### CLI integration + +The `play` / `train` subcommands forward every `-- VALUE` flag into `apply_overrides`. The +three short flags (`--vis`, `--gpu`, `--steps`) rewrite to `env.scene.{vis,gpu,steps}` overrides +before forwarding. See [Play and Train](../cli/play-train.md). + +## API + +The full auto-generated reference for `genelab.configs` is on the +[API Reference](../api/reference.md) page. diff --git a/docs/concepts/configs.zh.md b/docs/concepts/configs.zh.md new file mode 100644 index 00000000..97b94634 --- /dev/null +++ b/docs/concepts/configs.zh.md @@ -0,0 +1,60 @@ +# 配置系统 + +GeneLab 的配置体系是 `genelab.configs` 下的一个小型 dataclass 层级: + +``` +TaskCfg +└── env: object # 下游扩展把自己的 env dataclass 接入这里 + ManagerBasedEnvCfg + ├── scene + ├── actions + ├── observations + ├── rewards + ├── terminations + └── events +``` + +`TaskCfg.env` 类型刻意写成 `object` —— 下游扩展无需触碰核心即可接入自己的 env dataclass。 +大多数用户会把 `ManagerBasedEnvCfg` 的子类放进 `env` 字段。 + +## apply_overrides + +核心函数是 `apply_overrides(cfg, dict)`,解析点路径并把值应用到配置树: + +```python +from genelab.configs import apply_overrides + +apply_overrides(cfg, { + "env.scene.dt": "0.005", + "env.scene.steps": "500", + "env.actions.scale": "0.3", + "env.observations.include_velocity": "true", +}) +``` + +### 类型转换 + +字符串值按目标 dataclass 字段的类型注解自动转换: + +| 类型注解 | 接受的字符串 | +|---------|-------------| +| `bool` | `true`/`false`/`1`/`0`/`yes`/`no`(大小写不敏感) | +| `int` | 任意 10 进制整数字面量 | +| `float` | 任意 Python float 字面量 | +| `Path` | 经过 `pathlib.Path(...)` | +| `list[T]` | 逗号分隔,每个元素按 `T` 转换 | +| `tuple[T, ...]` | 逗号分隔,每个元素按 `T` 转换 | +| `str` | 原样 | + +如果点路径无法解析到已知字段,或值无法转换,`apply_overrides` 会在配置构造期就抛出明确错误, +避免错误延后到仿真运行时才暴露。 + +### CLI 集成 + +`play` / `train` 把每个 `-- VALUE` 标志转发给 `apply_overrides`。三个短标志 +(`--vis`、`--gpu`、`--steps`)在转发前被改写为 `env.scene.{vis,gpu,steps}`。 +详见 [play 与 train](../cli/play-train.md)。 + +## API + +`genelab.configs` 的完整自动生成参考见 [API 参考](../api/reference.md)。 diff --git a/docs/concepts/extensions.en.md b/docs/concepts/extensions.en.md new file mode 100644 index 00000000..2c247428 --- /dev/null +++ b/docs/concepts/extensions.en.md @@ -0,0 +1,63 @@ +# Extensions + +GeneLab core ships **no** robots, environments, or tasks. All content lives in downstream +extension packages. The CLI discovers extensions through three pathways, in order of preference: + +## 1. Entry-point auto-discovery (recommended) + +Declare in your extension's `pyproject.toml`: + +```toml +[project.entry-points."genelab.extensions"] +my_robot_project = "my_robot_project:register" +``` + +The CLI auto-imports every entry point in the `genelab.extensions` group on startup. `register()` +is a no-argument callable inside your package that performs the actual `ROBOTS.register(...)` / +`ENVS.register(...)` / `TASKS.register(...)` calls (or simply imports modules that perform them +as a side effect). + +This is the pathway `genelab project new` wires up for you. + +## 2. Explicit `--import` + +```bash +uv run genelab --import my_pkg.module1 --import my_pkg.module2 list tasks +``` + +Repeatable. Useful when: + +- The extension does not (yet) ship a `genelab.extensions` entry point. +- You want a fully explicit, reproducible loading order (combine with `--no-entry-points`). +- You're iterating on a not-yet-installed package and need to add its source directory to + `sys.path` ad-hoc (the CLI also adds the current working directory to `sys.path`). + +## 3. Programmatic + +```python +from genelab.registry import load_extension_module + +load_extension_module("my_pkg.module") +``` + +Used by tests and embedding scripts. The same idempotency guard applies, so calling it after the +CLI has already auto-discovered the same module is a no-op. + +## Disabling auto-discovery + +```bash +uv run genelab --no-entry-points --import my_pkg list tasks +``` + +This is the most reproducible setup — only the modules you explicitly named are loaded. + +## Canonical example + +`examples/genelab_examples/` is the reference shape: a `pyproject.toml` with the entry point, +a top-level `register()` callable, and `config.py` / `robots.py` / `envs.py` / `tasks.py` modules. +`tests/fake_extension.py` is the same shape stripped to bare minimum for the test suite. + +## See also + +- [Project new](../cli/project-new.md) — scaffold a new extension package. +- [Registry](registry.md) — what `register()` actually does. diff --git a/docs/concepts/extensions.zh.md b/docs/concepts/extensions.zh.md new file mode 100644 index 00000000..6c447969 --- /dev/null +++ b/docs/concepts/extensions.zh.md @@ -0,0 +1,61 @@ +# 扩展加载 + +GeneLab 核心**不**自带机器人、环境、任务,所有内容都由下游扩展包提供。CLI 通过三条路径发现 +扩展,按以下优先级: + +## 1. Entry-point 自动发现(推荐) + +在扩展的 `pyproject.toml` 中声明: + +```toml +[project.entry-points."genelab.extensions"] +my_robot_project = "my_robot_project:register" +``` + +CLI 启动时自动 import `genelab.extensions` 组下的每个 entry point。`register()` 是一个无参 +可调用,位于扩展包顶层,负责真正调用 `ROBOTS.register(...)` / `ENVS.register(...)` / +`TASKS.register(...)`(或仅 import 一些以副作用方式注册的模块)。 + +这条路径正是 `genelab project new` 默认替你接好的。 + +## 2. 显式 `--import` + +```bash +uv run genelab --import my_pkg.module1 --import my_pkg.module2 list tasks +``` + +可重复。适用场景: + +- 扩展尚未提供 `genelab.extensions` entry point。 +- 想要完全显式、可复现的加载顺序(搭配 `--no-entry-points`)。 +- 调试尚未安装的包,需要把源码目录临时塞进 `sys.path`(CLI 也会把当前工作目录加入 `sys.path`)。 + +## 3. 程序内调用 + +```python +from genelab.registry import load_extension_module + +load_extension_module("my_pkg.module") +``` + +测试与嵌入脚本使用。同样的幂等守卫生效,因此即使 CLI 已经自动发现过同一模块,再调用一次 +也是 no-op。 + +## 关闭自动发现 + +```bash +uv run genelab --no-entry-points --import my_pkg list tasks +``` + +这是最可复现的组合 —— 只加载你显式命名的模块。 + +## 参考形态 + +`examples/genelab_examples/` 是参考形态:`pyproject.toml` 带 entry point、顶层有 `register()` +可调用、并提供 `config.py` / `robots.py` / `envs.py` / `tasks.py`。`tests/fake_extension.py` +是同样形态但削减到最小,供测试套件使用。 + +## 另见 + +- [新建项目](../cli/project-new.md) —— 生成新的扩展包骨架。 +- [注册表](registry.md) —— `register()` 实际做了什么。 diff --git a/docs/concepts/registry.en.md b/docs/concepts/registry.en.md new file mode 100644 index 00000000..54f4048a --- /dev/null +++ b/docs/concepts/registry.en.md @@ -0,0 +1,51 @@ +# Registry + +GeneLab provides a generic `Registry[T]` plus three module-level singletons exported from +`genelab.lab`: + +| Singleton | Holds | +|-----------|-------| +| `ROBOTS` | Robot factories. | +| `ENVS` | Environment factories. | +| `TASKS` | Task factories (each task pairs an environment with optional runner / agent config). | + +## What an entry looks like + +A registry entry is a 4-tuple: **`(name, description, factory, cfg_type)`**. The `factory` is a +callable invoked lazily on `get(name)`, so importing a registration site does not eagerly build +heavy objects. + +## Basic usage + +```python +from genelab.lab import ROBOTS, ENVS, TASKS, TaskCfg + +def make_my_robot(): + ... + +ROBOTS.register( + name="my_robot", + description="A demo robot.", + factory=make_my_robot, + cfg_type=None, +) + +# Later: +robot = ROBOTS.get("my_robot") +``` + +The `name` is the canonical lookup key; the `description` shows up in `genelab list robots`. + +## Idempotent extension loading + +The registry module also tracks `_loaded_extension_modules` and `_loaded_entrypoints` sets so that +repeated calls to load the same extension are no-ops. This matters when a user combines the entry +point auto-discovery (default) with explicit `--import` flags — both pathways end up calling +`load_extension_module`, but the second call is short-circuited. + +See [Extensions](extensions.md) for the three pathways and when to use which. + +## API + +The full auto-generated reference for `genelab.registry` lives on the +[API Reference](../api/reference.md) page. diff --git a/docs/concepts/registry.zh.md b/docs/concepts/registry.zh.md new file mode 100644 index 00000000..724f9167 --- /dev/null +++ b/docs/concepts/registry.zh.md @@ -0,0 +1,47 @@ +# 注册表 + +GeneLab 提供泛型 `Registry[T]` 与三个由 `genelab.lab` 导出的模块级单例: + +| 单例 | 持有内容 | +|------|---------| +| `ROBOTS` | 机器人工厂。 | +| `ENVS` | 环境工厂。 | +| `TASKS` | 任务工厂(每个任务把环境与可选 runner / agent 配置打包)。 | + +## 一个条目长什么样 + +注册条目是一个四元组:**`(name, description, factory, cfg_type)`**。`factory` 是一个可调用, +只在 `get(name)` 时才会被调用,因此导入注册位点不会立即构造重型对象。 + +## 基本用法 + +```python +from genelab.lab import ROBOTS, ENVS, TASKS, TaskCfg + +def make_my_robot(): + ... + +ROBOTS.register( + name="my_robot", + description="A demo robot.", + factory=make_my_robot, + cfg_type=None, +) + +# 之后: +robot = ROBOTS.get("my_robot") +``` + +`name` 是规范查找键;`description` 会出现在 `genelab list robots` 输出里。 + +## 幂等扩展加载 + +注册模块还维护 `_loaded_extension_modules` 和 `_loaded_entrypoints` 两个集合,重复加载同一 +扩展会被短路。当用户同时使用 entry point 自动发现(默认)与显式 `--import` 时,这一点尤其 +重要 —— 两条路径最终都会调用 `load_extension_module`,但第二次调用直接返回。 + +三种路径以及何时使用哪种,详见 [扩展加载](extensions.md)。 + +## API + +`genelab.registry` 的完整自动生成参考见 [API 参考](../api/reference.md)。 diff --git a/docs/contributing.en.md b/docs/contributing.en.md new file mode 100644 index 00000000..b28faeaf --- /dev/null +++ b/docs/contributing.en.md @@ -0,0 +1,11 @@ +# Contributing + +The canonical contributor guide lives in [`CONTRIBUTING.md`](https://github.com/KraHsu/GeneLab/blob/main/CONTRIBUTING.md) +at the repository root and is mirrored below. + +--- + +{% + include-markdown "../CONTRIBUTING.md" + heading-offset=1 +%} diff --git a/docs/contributing.zh.md b/docs/contributing.zh.md new file mode 100644 index 00000000..e9b7b2e9 --- /dev/null +++ b/docs/contributing.zh.md @@ -0,0 +1,58 @@ +# 贡献指南 + +> 详细的贡献者指南目前以英文为权威版本,见仓库根目录 +> [`CONTRIBUTING.md`](https://github.com/KraHsu/GeneLab/blob/main/CONTRIBUTING.md)。本页提供 +> 关键要点的中文摘要,欢迎贡献完整中文翻译。 + +## 开发环境 + +GeneLab 使用 [uv](https://github.com/astral-sh/uv) 并要求 Python `>=3.12`。挑选一个互斥的 +`torch-*` extra: + +```bash +uv sync --extra torch-cpu # 或 torch-cu126 / torch-cu128 / torch-cu130 +uv run genelab --help # CLI 烟雾测试 +uv run genelab cache # 创建 Genesis / Matplotlib 用的项目本地 .cache 目录 +``` + +`examples/genelab_examples/` 已加入 `pytest` 的 `pythonpath`,测试可直接 import 而无需安装。 + +## Python 3.12+ 风格 + +GeneLab 锁定 Python `>=3.12`。只写现代代码,不要为旧解释器加兼容垫片: + +- **不要** `from __future__ import annotations`。注解会立即被求值;只在 `TYPE_CHECKING` 下导入的 + 名字在使用点引号化(`def f(env: "ManagerBasedRlEnv") -> ...`)。 +- **PEP 604 union**:`X | Y`、`X | None`,不要 `Optional[X]` / `Union[X, Y]`。 +- **PEP 585 内置泛型**:`list[T]`、`dict[K, V]`、`tuple[T, ...]`、`type[T]`,不要 `typing.List` 等。 +- **PEP 695 泛型**:`class Foo[T]:`、`def f[T](...)`、`type Alias = ...`,不要 `TypeVar + Generic[T]`。 +- 用 `collections.abc` 代替 `typing` 中的 `Callable` / `Iterable` / `Iterator` / `Sequence` / `Mapping`。 +- 仅以下 `typing` 名字预期出现:`Any`、`cast`、`Protocol`、`Literal`、`Final`、`Annotated`、 + `TYPE_CHECKING`、`runtime_checkable`、`get_args`、`get_origin`、`get_type_hints`。其他名字 + 视为可疑。 + +## PR 前检查 + +```bash +uv run ruff check # lint +uv run ruff format --check # 格式(用 `uv run ruff format` 自动修复) +uv run pyright # src/genelab 的 strict 类型检查 +uv run pytest # 完整测试套件 +``` + +四项均须通过 —— CI 把同一组检查设为每个 PR 的必需 status checks。 + +## 分支与 PR 流程 + +`main` 受保护:直接推送会被拒绝,必需的 CI 检查(`lint`、`typecheck`、`test`)必须全绿后才能 +合入 PR。 + +1. 从 `main` 派生分支,使用描述性前缀:`fix/...`、`feat/...`、`ci/...`、`chore/...`、`docs/...`。 +2. 本地跑上述检查。 +3. 推送并开 PR —— CI 会在每次 push 时自动运行。 +4. 评审期间若 `main` 前进,把 `main` 合入分支(或 rebase),让 PR 保持最新。 + +## 提交信息 + +- 祈使语气、首字母大写:"Bump…"、"Fix…"、"Add…"。不要前缀、不要 emoji。 +- 主题行控制在约 72 字符内。正文解释 *why* —— *what* 已经被 diff 表达。 diff --git a/docs/examples/overview.en.md b/docs/examples/overview.en.md new file mode 100644 index 00000000..778e7b3e --- /dev/null +++ b/docs/examples/overview.en.md @@ -0,0 +1,46 @@ +# Examples + +The repository ships several reference extensions under `examples/`. They double as integration +tests for the CLI and registry. + +## genelab_examples + +Path: [`examples/genelab_examples/`](https://github.com/KraHsu/GeneLab/tree/main/examples/genelab_examples) + +The canonical in-tree extension. Two tasks are wired up: + +- **`wuji_hand`** — a hand-manipulation task. +- **`rubiks`** — a Rubik's cube task. + +`pyproject.toml` declares the `genelab.extensions` entry point, so this extension is discovered +automatically when the package is installed (it is also on `pytest`'s `pythonpath` via the +project's `pyproject.toml`, allowing the tests to import from it without installation). + +## unitree + +Path: [`examples/unitree/`](https://github.com/KraHsu/GeneLab/tree/main/examples/unitree) + +A robot example focused on Unitree platforms. Same extension shape as `genelab_examples` — +entry point, `register()`, and per-module registration files. + +## external_project + +Path: [`examples/external_project/`](https://github.com/KraHsu/GeneLab/tree/main/examples/external_project) + +A minimal downstream project template. This is what `genelab project new` produces, kept in-tree +as a reference for the scaffolding output. + +## Using an example + +```bash +# Confirm the example tasks are visible. +uv run genelab list tasks + +# Play a task with visualization. +uv run genelab play wuji_hand --vis --steps 200 +``` + +## See also + +- [Project new](../cli/project-new.md) — scaffold your own extension. +- [Extensions](../concepts/extensions.md) — how extensions are discovered and loaded. diff --git a/docs/examples/overview.zh.md b/docs/examples/overview.zh.md new file mode 100644 index 00000000..41722033 --- /dev/null +++ b/docs/examples/overview.zh.md @@ -0,0 +1,43 @@ +# 示例 + +仓库在 `examples/` 下提供数个参考扩展,同时也是 CLI 与注册表的集成测试。 + +## genelab_examples + +路径:[`examples/genelab_examples/`](https://github.com/KraHsu/GeneLab/tree/main/examples/genelab_examples) + +仓库内的标准扩展,接通两个任务: + +- **`wuji_hand`** —— 手部操作任务。 +- **`rubiks`** —— 魔方任务。 + +`pyproject.toml` 声明了 `genelab.extensions` entry point,因此安装该包后会被自动发现 +(通过项目 `pyproject.toml` 的 `pythonpath` 设置,测试也可以直接 import,而无需安装)。 + +## unitree + +路径:[`examples/unitree/`](https://github.com/KraHsu/GeneLab/tree/main/examples/unitree) + +聚焦 Unitree 平台的机器人示例。形态与 `genelab_examples` 相同 —— entry point、`register()`、 +按模块拆分的注册文件。 + +## external_project + +路径:[`examples/external_project/`](https://github.com/KraHsu/GeneLab/tree/main/examples/external_project) + +下游项目最小模板。`genelab project new` 生成的内容与之结构一致,留在仓库里作为脚手架输出参考。 + +## 使用示例 + +```bash +# 确认示例任务可见。 +uv run genelab list tasks + +# 可视化运行一个任务。 +uv run genelab play wuji_hand --vis --steps 200 +``` + +## 另见 + +- [新建项目](../cli/project-new.md) —— 生成自己的扩展包。 +- [扩展加载](../concepts/extensions.md) —— 扩展如何被发现与加载。 diff --git a/docs/getting-started/installation.en.md b/docs/getting-started/installation.en.md new file mode 100644 index 00000000..d329d21e --- /dev/null +++ b/docs/getting-started/installation.en.md @@ -0,0 +1,75 @@ +# Installation + +GeneLab requires **Python 3.12 or newer** and uses [`uv`](https://docs.astral.sh/uv/) for +dependency management. + +## 1. Sync the environment + +From the repository root: + +```bash +uv sync +uv run genelab --help +``` + +`uv sync` creates the project virtual environment, installs GeneLab from this checkout, and +installs the dependencies pinned by `uv.lock`. `uv run ...` runs commands inside that environment. +A bare `genelab` command works only after `.venv` is activated or GeneLab is installed into the +active Python environment. + +## 2. Pick exactly one PyTorch extra + +The `torch-*` extras are **mutually exclusive** — pick one that matches your hardware: + +```bash +# CPU-only or non-NVIDIA development machines. +uv sync --extra torch-cpu + +# NVIDIA machines; choose the CUDA wheel supported by your driver. +uv sync --extra torch-cu126 +uv sync --extra torch-cu128 +uv sync --extra torch-cu130 +``` + +!!! warning "PyTorch version requirement" + Genesis requires `torch>=2.8.0` — older builds emit a `'torch<2.8.0' is not supported` warning + at import time and may break Genesis runtime assumptions. All `torch-*` extras pin + `torch>=2.8.0`, so `uv sync` will pull a compatible wheel automatically. PyTorch only publishes + 2.8+ wheels on the `cpu`, `cu126`, `cu128`, and `cu130` indices; older CUDA flavours + (`cu118` / `cu121` / `cu124`) are intentionally not offered as extras. If you already have an + older `torch` in your environment, run + `uv sync --reinstall-package torch --extra torch-cuXXX` to refresh it. + +If you are not sure which CUDA build to use, check `nvidia-smi` and follow the PyTorch +installation selector for your platform. + +## 3. Initialize project-local caches + +Genesis, Quadrants, and Matplotlib all want a writable cache directory. Create the project-local +folders and the matching environment variables in one shot: + +```bash +uv run genelab cache +``` + +This sets `XDG_CACHE_HOME` and `MPLCONFIGDIR` under `.cache/`, so the simulator never writes to +your home directory. + +## 4. Verify + +```bash +uv run python -c "import genelab; print(genelab.__version__)" +uv run python -c "from genelab.lab import ManagerBasedEnvCfg; print(ManagerBasedEnvCfg.__name__)" +uv run python -c "import genesis; print(genesis.__version__)" +uv run pytest +uv run ruff check +uv run pyright +``` + +After syncing one of the `torch-*` extras, verify the selected PyTorch build: + +```bash +uv run python -c "import torch; print(torch.__version__, torch.version.cuda)" +``` + +Next: head over to the [Quickstart](quickstart.md) to run a registered task. diff --git a/docs/getting-started/installation.zh.md b/docs/getting-started/installation.zh.md new file mode 100644 index 00000000..3be0ab81 --- /dev/null +++ b/docs/getting-started/installation.zh.md @@ -0,0 +1,68 @@ +# 安装 + +GeneLab 需要 **Python 3.12 或更新版本**,使用 [`uv`](https://docs.astral.sh/uv/) 管理依赖。 + +## 1. 同步环境 + +在仓库根目录运行: + +```bash +uv sync +uv run genelab --help +``` + +`uv sync` 会创建项目虚拟环境,从当前 checkout 安装 GeneLab,并安装 `uv.lock` 锁定的依赖。 +`uv run ...` 会在该环境中运行命令。裸 `genelab` 命令只有在 `.venv` 已激活、或 GeneLab 已安装 +到当前 Python 环境后才可用。 + +## 2. 挑选一个 PyTorch extra(互斥) + +`torch-*` extras **互斥**,只能挑选一个匹配你的硬件: + +```bash +# 仅 CPU 或非 NVIDIA 开发机器。 +uv sync --extra torch-cpu + +# NVIDIA 机器,按驱动支持的 CUDA wheel 挑选。 +uv sync --extra torch-cu126 +uv sync --extra torch-cu128 +uv sync --extra torch-cu130 +``` + +!!! warning "PyTorch 版本要求" + Genesis 要求 `torch>=2.8.0`,旧版本在导入时会报 `'torch<2.8.0' is not supported`,并可能 + 破坏 Genesis 的运行时假设。所有 `torch-*` extras 都固定 `torch>=2.8.0`,`uv sync` 会自动 + 拉取兼容 wheel。PyTorch 只在 `cpu`、`cu126`、`cu128`、`cu130` 索引提供 2.8+ wheel,因此 + 较旧的 CUDA flavour(`cu118` / `cu121` / `cu124`)不再以 extra 形式提供。如果环境里已经 + 存在旧 torch,可用 `uv sync --reinstall-package torch --extra torch-cuXXX` 刷新。 + +不确定使用哪个 CUDA build 时,用 `nvidia-smi` 查看驱动,并参考 PyTorch 官方安装选择器。 + +## 3. 初始化项目本地缓存 + +Genesis、Quadrants、Matplotlib 都需要可写缓存目录。一条命令同时创建项目本地目录和环境变量: + +```bash +uv run genelab cache +``` + +它会把 `XDG_CACHE_HOME` 和 `MPLCONFIGDIR` 指向 `.cache/`,避免仿真器写到用户主目录。 + +## 4. 验证 + +```bash +uv run python -c "import genelab; print(genelab.__version__)" +uv run python -c "from genelab.lab import ManagerBasedEnvCfg; print(ManagerBasedEnvCfg.__name__)" +uv run python -c "import genesis; print(genesis.__version__)" +uv run pytest +uv run ruff check +uv run pyright +``` + +同步任意 `torch-*` extra 后,可验证当前 PyTorch build: + +```bash +uv run python -c "import torch; print(torch.__version__, torch.version.cuda)" +``` + +下一步:阅读 [快速开始](quickstart.md) 运行一个已注册任务。 diff --git a/docs/getting-started/quickstart.en.md b/docs/getting-started/quickstart.en.md new file mode 100644 index 00000000..1169bdc2 --- /dev/null +++ b/docs/getting-started/quickstart.en.md @@ -0,0 +1,69 @@ +# Quickstart + +This walks through the shortest path from a fresh `uv sync` to running a registered task. + +## List what's available + +The core `genelab` package ships with empty registries — robots, environments, and tasks come from +extension packages. The example extension under `examples/genelab_examples/` is discovered +automatically because its `pyproject.toml` declares a `genelab.extensions` entry point. + +```bash +uv run genelab list robots +uv run genelab list envs +uv run genelab list tasks +``` + +If a registry is empty, install or import an extension (see [Extensions](../concepts/extensions.md)). + +## Play a task + +```bash +uv run genelab play +``` + +The CLI loads the registered factory for ``, applies any config overrides you pass on the +command line, and runs the rollout in the configured Genesis backend. + +Common shortcuts: + +```bash +uv run genelab play --vis # enable visualization +uv run genelab play --steps 500 # cap the episode length +uv run genelab play --gpu 1 # pin to a single GPU +``` + +For arbitrary overrides, use the dotted `--a.b.c VALUE` syntax — strings are coerced to the type +declared on the target dataclass field. See [Play and Train](../cli/play-train.md) for the full +override grammar. + +## Train (when a runner exists) + +If a task ships with an RL runner (rsl_rl, etc.), launch training with: + +```bash +uv run genelab train +uv run genelab train --gpus 4 # multi-GPU via torchrun +uv run genelab train --checkpoint path/to/model.pt +``` + +The `--gpus N` flag transparently dispatches through `torchrun` for distributed training; the +target task's runner needs to be `torchrun`-compatible. + +## Start a downstream project + +When you outgrow the example extension and want your own package: + +```bash +uv run genelab project new my_robot_project +``` + +This scaffolds `config.py`, `robots.py`, `envs.py`, `tasks.py`, and a `pyproject.toml` with a +`genelab.extensions` entry point. See [Project new](../cli/project-new.md). + +## Next steps + +- [CLI overview](../cli/overview.md) — all subcommands and global flags. +- [Registry](../concepts/registry.md) — how `ROBOTS` / `ENVS` / `TASKS` work. +- [Configs](../concepts/configs.md) — `ManagerBasedEnvCfg` and `apply_overrides`. +- [Extensions](../concepts/extensions.md) — three ways to register downstream code. diff --git a/docs/getting-started/quickstart.zh.md b/docs/getting-started/quickstart.zh.md new file mode 100644 index 00000000..8d5e0038 --- /dev/null +++ b/docs/getting-started/quickstart.zh.md @@ -0,0 +1,66 @@ +# 快速开始 + +本节走通从 `uv sync` 到运行已注册任务的最短路径。 + +## 列出可用项 + +核心 `genelab` 包自带空注册表 —— 机器人、环境、任务都由扩展包贡献。仓库内 +`examples/genelab_examples/` 通过 `pyproject.toml` 中的 `genelab.extensions` entry point +被自动发现。 + +```bash +uv run genelab list robots +uv run genelab list envs +uv run genelab list tasks +``` + +如果某个注册表为空,请安装或导入扩展(详见 [扩展加载](../concepts/extensions.md))。 + +## 运行一个任务 + +```bash +uv run genelab play +``` + +CLI 会查找 `` 的注册工厂,应用命令行传入的配置 override,然后在 Genesis 后端运行。 + +常用快捷: + +```bash +uv run genelab play --vis # 启用可视化 +uv run genelab play --steps 500 # 限制单 episode 步数 +uv run genelab play --gpu 1 # 锁定到单张 GPU +``` + +任意 override 使用 `--a.b.c VALUE` 点路径写法 —— 字符串会按目标 dataclass 字段的类型注解 +自动转换。完整 override 语法见 [play 与 train](../cli/play-train.md)。 + +## 训练(任务带 runner 时) + +如果任务自带 RL runner(rsl_rl 等),可用: + +```bash +uv run genelab train +uv run genelab train --gpus 4 # 多 GPU 通过 torchrun 启动 +uv run genelab train --checkpoint path/to/model.pt +``` + +`--gpus N` 会透明走 `torchrun` 启动分布式训练,前提是任务自身的 runner 支持 `torchrun`。 + +## 新建下游项目 + +当示例扩展不够用、想要自己的包时: + +```bash +uv run genelab project new my_robot_project +``` + +生成包含 `config.py`、`robots.py`、`envs.py`、`tasks.py` 以及带 `genelab.extensions` entry +point 的 `pyproject.toml`。详见 [新建项目](../cli/project-new.md)。 + +## 下一步 + +- [CLI 总览](../cli/overview.md) —— 所有子命令与全局 flag。 +- [注册表](../concepts/registry.md) —— `ROBOTS` / `ENVS` / `TASKS` 的工作机制。 +- [配置系统](../concepts/configs.md) —— `ManagerBasedEnvCfg` 与 `apply_overrides`。 +- [扩展加载](../concepts/extensions.md) —— 三种注册下游代码的方式。 diff --git a/docs/index.en.md b/docs/index.en.md new file mode 100644 index 00000000..1b48253a --- /dev/null +++ b/docs/index.en.md @@ -0,0 +1,68 @@ +# GeneLab + +GeneLab is an Isaac Lab-inspired API for RL and robotics research powered by +[Genesis](https://github.com/Genesis-Embodied-AI/Genesis). It keeps the familiar shape of registered +robots, environments, tasks, manager-based MDP configuration, and CLI dispatch, while using Genesis +as the simulation backend. + +## Goals + +- Provide small registries for robots, environments, and tasks. +- Keep core API layers separate from example assets and demo scripts. +- Use manager-style config hooks for actions, observations, rewards, events, and terminations. +- Keep Genesis backend integration explicit and easy to extend. +- Support downstream robotics projects through a stable package layout and CLI. + +## Quick start + +
+ +- :material-download:{ .lg .middle } **Install** + + --- + + Set up `uv`, pick a `torch-*` extra, and verify. + + [:octicons-arrow-right-24: Installation](getting-started/installation.md) + +- :material-rocket-launch-outline:{ .lg .middle } **Run** + + --- + + List registered tasks and play one. + + [:octicons-arrow-right-24: Quickstart](getting-started/quickstart.md) + +- :material-console-line:{ .lg .middle } **CLI** + + --- + + `play`, `train`, `project new`, and override syntax. + + [:octicons-arrow-right-24: CLI overview](cli/overview.md) + +- :material-package-variant-closed:{ .lg .middle } **Extend** + + --- + + Write a downstream extension package. + + [:octicons-arrow-right-24: Extensions](concepts/extensions.md) + +
+ +## Requirements + +- Python 3.12 or newer. +- [uv](https://docs.astral.sh/uv/) for dependency management. + +## At a glance + +- `genelab.registry` — registries, registration helpers, and extension loading. +- `genelab.configs` — reusable dataclass configs, including `ManagerBasedEnvCfg` and `TaskCfg`. +- `genelab.lab` — public facade for registry and manager-based environment primitives. +- `genelab.envs` / `genelab.robots` / `genelab.tasks` — core registry helper namespaces. +- `genelab.actuator` / `genelab.entity` / `genelab.scene` / `genelab.sensor` / `genelab.terrains` / + `genelab.rl` — extension namespaces for robotics research code. + +See the [API Reference](api/reference.md) for the full auto-generated module documentation. diff --git a/docs/index.zh.md b/docs/index.zh.md new file mode 100644 index 00000000..05ae6775 --- /dev/null +++ b/docs/index.zh.md @@ -0,0 +1,67 @@ +# GeneLab + +GeneLab 是一个面向强化学习与机器人研究的 Isaac Lab 风格 API,由 +[Genesis](https://github.com/Genesis-Embodied-AI/Genesis) 提供仿真后端。它保留了机器人、环境、 +任务注册,manager-based MDP 配置,以及 CLI 调度这些常见的组织方式。 + +## 目标 + +- 提供小型机器人、环境和任务注册表。 +- 将核心 API 层与示例资产、演示脚本分离。 +- 使用 manager 风格配置钩子组织 actions、observations、rewards、events 和 terminations。 +- 保持 Genesis 后端集成显式,便于扩展。 +- 通过稳定的包结构与 CLI 支持下游机器人研究项目。 + +## 快速开始 + +
+ +- :material-download:{ .lg .middle } **安装** + + --- + + 准备 `uv`,挑选一个 `torch-*` extra,并完成验证。 + + [:octicons-arrow-right-24: 安装](getting-started/installation.md) + +- :material-rocket-launch-outline:{ .lg .middle } **运行** + + --- + + 列出已注册任务并 play 一个。 + + [:octicons-arrow-right-24: 快速开始](getting-started/quickstart.md) + +- :material-console-line:{ .lg .middle } **CLI** + + --- + + `play`、`train`、`project new` 与 override 语法。 + + [:octicons-arrow-right-24: CLI 总览](cli/overview.md) + +- :material-package-variant-closed:{ .lg .middle } **扩展** + + --- + + 编写下游扩展包接入注册表。 + + [:octicons-arrow-right-24: 扩展加载](concepts/extensions.md) + +
+ +## 要求 + +- Python 3.12 或更新版本。 +- 使用 [uv](https://docs.astral.sh/uv/) 管理依赖。 + +## 模块速览 + +- `genelab.registry`:注册表、注册 helper 与扩展加载。 +- `genelab.configs`:可复用的 dataclass 配置,包括 `ManagerBasedEnvCfg` 与 `TaskCfg`。 +- `genelab.lab`:注册表与 manager-based 环境原语的公共 API facade。 +- `genelab.envs` / `genelab.robots` / `genelab.tasks`:核心注册 helper 的命名空间。 +- `genelab.actuator` / `genelab.entity` / `genelab.scene` / `genelab.sensor` / + `genelab.terrains` / `genelab.rl`:面向机器人研究代码的扩展命名空间。 + +完整自动生成的模块文档见 [API 参考](api/reference.md)。 diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css new file mode 100644 index 00000000..25541b2a --- /dev/null +++ b/docs/stylesheets/extra.css @@ -0,0 +1,166 @@ +/* GeneLab docs — soft, flat, modern tweaks on top of Material. */ + +:root { + --md-text-font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", + "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", + "Microsoft YaHei", sans-serif; + --md-code-font: "JetBrains Mono", "SF Mono", Menlo, Consolas, monospace; +} + +[data-md-color-scheme="default"] { + --md-primary-fg-color: #5b6cff; + --md-primary-fg-color--light: #8a96ff; + --md-primary-fg-color--dark: #4554d6; + --md-accent-fg-color: #16b8a0; + --md-typeset-a-color: #5b6cff; + --md-default-bg-color: #fafbfc; + --md-code-bg-color: #f3f4f8; + --md-code-fg-color: #2c3144; +} + +[data-md-color-scheme="slate"] { + --md-primary-fg-color: #8693ff; + --md-primary-fg-color--light: #a5afff; + --md-primary-fg-color--dark: #5b6cff; + --md-accent-fg-color: #34d4bb; + --md-typeset-a-color: #a5afff; + --md-default-bg-color: #14161d; + --md-default-fg-color--lightest: #1c1f2a; + --md-code-bg-color: #1c1f2a; + --md-code-fg-color: #d8dbe6; +} + +.md-typeset { + font-feature-settings: "ss01", "cv02", "cv11"; + letter-spacing: -0.005em; + line-height: 1.7; +} + +.md-typeset h1, +.md-typeset h2, +.md-typeset h3, +.md-typeset h4 { + font-weight: 600; + letter-spacing: -0.02em; +} + +.md-typeset h1 { + margin-bottom: 1.2rem; +} + +.md-header { + background: var(--md-default-bg-color); + color: var(--md-default-fg-color); + box-shadow: none; + border-bottom: 1px solid var(--md-default-fg-color--lightest); +} + +[data-md-color-scheme="slate"] .md-header { + border-bottom-color: rgba(255, 255, 255, 0.06); +} + +.md-tabs { + background: var(--md-default-bg-color); + color: var(--md-default-fg-color--light); + border-bottom: 1px solid var(--md-default-fg-color--lightest); +} + +[data-md-color-scheme="slate"] .md-tabs { + border-bottom-color: rgba(255, 255, 255, 0.06); +} + +.md-search__form { + border-radius: 10px; + background: var(--md-code-bg-color); +} + +.md-typeset code { + border-radius: 6px; + padding: 0.1em 0.4em; +} + +.md-typeset pre > code, +.md-typeset .highlight pre { + border-radius: 12px; + padding: 1rem 1.1rem; + line-height: 1.55; +} + +.md-typeset .admonition, +.md-typeset details { + border-radius: 12px; + border-width: 1px; + box-shadow: none; +} + +.md-typeset table:not([class]) { + border-radius: 12px; + overflow: hidden; + border: 1px solid var(--md-default-fg-color--lightest); + font-size: 0.85rem; +} + +.md-typeset table:not([class]) th { + background: var(--md-code-bg-color); + font-weight: 600; +} + +.md-typeset blockquote { + border-left-width: 3px; + border-radius: 0 12px 12px 0; + background: var(--md-code-bg-color); + padding: 0.6rem 1rem; + color: var(--md-default-fg-color--light); +} + +.md-nav__link--active, +.md-nav__link:focus, +.md-nav__link:hover { + color: var(--md-primary-fg-color); +} + +.md-button { + border-radius: 10px; + border-width: 1px; + font-weight: 500; + padding: 0.55em 1.2em; +} + +.md-button--primary { + background-color: var(--md-primary-fg-color); + border-color: var(--md-primary-fg-color); +} + +.md-button--primary:hover, +.md-button--primary:focus { + background-color: var(--md-primary-fg-color--dark); + border-color: var(--md-primary-fg-color--dark); +} + +.md-typeset .grid.cards > :is(ul, ol) > li, +.md-typeset .grid > .card { + border-radius: 14px; + border: 1px solid var(--md-default-fg-color--lightest); + box-shadow: none; + transition: border-color 120ms ease, transform 120ms ease; +} + +.md-typeset .grid.cards > :is(ul, ol) > li:hover, +.md-typeset .grid > .card:hover { + border-color: var(--md-primary-fg-color--light); + transform: translateY(-2px); +} + +.md-footer { + background: var(--md-default-bg-color); + color: var(--md-default-fg-color--light); + border-top: 1px solid var(--md-default-fg-color--lightest); +} + +.md-footer-meta { + background: transparent; +} + +[data-md-color-scheme="slate"] .md-footer { + border-top-color: rgba(255, 255, 255, 0.06); +} diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000..5cc3fd6f --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,165 @@ +site_name: GeneLab +site_description: Isaac Lab-inspired API for RL and robotics research powered by Genesis. +site_url: https://krahsu.github.io/GeneLab/ +site_author: GeneLab contributors + +repo_name: KraHsu/GeneLab +repo_url: https://github.com/KraHsu/GeneLab +edit_uri: edit/main/docs/ + +docs_dir: docs +site_dir: site + +exclude_docs: | + README_CN.md + +theme: + name: material + language: en + font: + text: Inter + code: JetBrains Mono + icon: + repo: fontawesome/brands/github + edit: material/pencil + features: + - navigation.tabs + - navigation.tabs.sticky + - navigation.sections + - navigation.indexes + - navigation.top + - navigation.footer + - navigation.tracking + - content.code.copy + - content.code.annotate + - content.action.edit + - content.tooltips + - search.suggest + - search.highlight + - search.share + - toc.follow + palette: + - media: "(prefers-color-scheme)" + toggle: + icon: material/brightness-auto + name: Switch to light mode + - media: "(prefers-color-scheme: light)" + scheme: default + primary: indigo + accent: teal + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: indigo + accent: teal + toggle: + icon: material/brightness-4 + name: Switch to system preference + +extra_css: + - stylesheets/extra.css + +extra: + social: + - icon: fontawesome/brands/github + link: https://github.com/KraHsu/GeneLab + +plugins: + - search: + lang: + - en + - zh + - include-markdown + - i18n: + docs_structure: suffix + reconfigure_material: true + reconfigure_search: true + languages: + - locale: en + default: true + name: English + build: true + - locale: zh + name: 简体中文 + build: true + site_name: GeneLab + nav_translations: + Home: 首页 + Getting Started: 快速上手 + Installation: 安装 + Quickstart: 快速开始 + CLI: 命令行 + Overview: 总览 + Play and Train: play 与 train + Project New: 新建项目 + Concepts: 核心概念 + Registry: 注册表 + Configs: 配置系统 + Extensions: 扩展加载 + Examples: 示例 + API Reference: API 参考 + Contributing: 贡献指南 + - mkdocstrings: + default_handler: python + handlers: + python: + paths: [src] + options: + docstring_style: google + show_source: true + show_signature_annotations: true + separate_signature: true + members_order: source + heading_level: 2 + show_root_heading: true + show_root_full_path: false + show_symbol_type_heading: true + show_symbol_type_toc: true + +markdown_extensions: + - admonition + - attr_list + - md_in_html + - footnotes + - tables + - toc: + permalink: true + - pymdownx.details + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.snippets: + base_path: + - . + - docs + check_paths: true + - pymdownx.superfences + - pymdownx.tabbed: + alternate_style: true + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + +nav: + - Home: index.md + - Getting Started: + - Installation: getting-started/installation.md + - Quickstart: getting-started/quickstart.md + - CLI: + - Overview: cli/overview.md + - Play and Train: cli/play-train.md + - Project New: cli/project-new.md + - Concepts: + - Registry: concepts/registry.md + - Configs: concepts/configs.md + - Extensions: concepts/extensions.md + - Examples: + - Overview: examples/overview.md + - API Reference: api/reference.md + - Contributing: contributing.md diff --git a/pyproject.toml b/pyproject.toml index dd7e4988..4898a639 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,19 @@ rl = [ "gymnasium>=1.0", "tensorboard>=2.20.0", ] +docs = [ + "mkdocs>=1.6", + "mkdocs-material>=9.5", + "mkdocs-static-i18n>=1.2", + "mkdocs-include-markdown-plugin>=7", + "mkdocstrings[python]>=0.26", + "pymdown-extensions>=10", +] + +[project.urls] +Documentation = "https://krahsu.github.io/GeneLab/" +Repository = "https://github.com/KraHsu/GeneLab" +Issues = "https://github.com/KraHsu/GeneLab/issues" [project.scripts] genelab = "genelab.cli:main" diff --git a/uv.lock b/uv.lock index 9a55065c..5899d81b 100644 --- a/uv.lock +++ b/uv.lock @@ -61,6 +61,46 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] +[[package]] +name = "babel" +version = "2.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, +] + +[[package]] +name = "backrefs" +version = "7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/a7dd63622beef68cc0d3c3c36d472e143dd95443d5ebf14cd1a5b4dfbf11/backrefs-7.0.tar.gz", hash = "sha256:4989bb9e1e99eb23647c7160ed51fb21d0b41b5d200f2d3017da41e023097e82", size = 7012453, upload-time = "2026-04-28T16:28:04.215Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/39/39a31d7eae729ea14ed10c3ccef79371197177b9355a86cb3525709e8502/backrefs-7.0-py310-none-any.whl", hash = "sha256:b57cd227ea556b0aed3dc9b8da4628db4eabc0402c6d7fcfc69283a93955f7e9", size = 380824, upload-time = "2026-04-28T16:27:55.647Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b5/9302644225ba7dfa934a2ff2b9c7bb85701313a90dddb3dfaf693fa5bae2/backrefs-7.0-py311-none-any.whl", hash = "sha256:a0fa7360c63509e9e077e174ef4e6d3c21c8db94189b9d957289ae6d794b9475", size = 392626, upload-time = "2026-04-28T16:27:57.42Z" }, + { url = "https://files.pythonhosted.org/packages/36/da/87912ddec6e06feffbaa3d7aa18fc6352bee2e8f1fee185d7d1690f8f4e8/backrefs-7.0-py312-none-any.whl", hash = "sha256:ca42ce6a49ace3d75684dfa9937f3373902a63284ecb385ce36d15e5dcb41c12", size = 398537, upload-time = "2026-04-28T16:27:58.913Z" }, + { url = "https://files.pythonhosted.org/packages/00/bb/90ba423612b6aa0adccc6b1874bcd4a9b44b660c0c16f346611e00f64ac3/backrefs-7.0-py313-none-any.whl", hash = "sha256:f2c52955d631b9e1ac4cd56209f0a3a946d592b98e7790e77699339ae01c102a", size = 400491, upload-time = "2026-04-28T16:28:00.928Z" }, + { url = "https://files.pythonhosted.org/packages/3e/5c/fb93d3092640a24dfb7bd7727a24016d7c01774ca013e60efd3f683c8002/backrefs-7.0-py314-none-any.whl", hash = "sha256:a6448b28180e3ca01134c9cf09dcebafad8531072e09903c5451748a05f24bc9", size = 412349, upload-time = "2026-04-28T16:28:02.412Z" }, +] + +[[package]] +name = "bracex" +version = "2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/9a/fec38644694abfaaeca2798b58e276a8e61de49e2e37494ace423395febc/bracex-2.6.tar.gz", hash = "sha256:98f1347cd77e22ee8d967a30ad4e310b233f7754dbf31ff3fceb76145ba47dc7", size = 26642, upload-time = "2025-06-22T19:12:31.254Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/2a/9186535ce58db529927f6cf5990a849aa9e052eea3e2cfefe20b9e1802da/bracex-2.6-py3-none-any.whl", hash = "sha256:0b0049264e7340b3ec782b5cb99beb325f36c3782a32e36e876452fd49a09952", size = 11508, upload-time = "2025-06-22T19:12:29.781Z" }, +] + +[[package]] +name = "certifi" +version = "2026.4.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" }, +] + [[package]] name = "cffi" version = "2.0.0" @@ -118,6 +158,79 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, ] +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + [[package]] name = "click" version = "8.3.3" @@ -659,6 +772,14 @@ dependencies = [ ] [package.optional-dependencies] +docs = [ + { name = "mkdocs" }, + { name = "mkdocs-include-markdown-plugin" }, + { name = "mkdocs-material" }, + { name = "mkdocs-static-i18n" }, + { name = "mkdocstrings", extra = ["python"] }, + { name = "pymdown-extensions" }, +] rl = [ { name = "gymnasium" }, { name = "rsl-rl-lib" }, @@ -690,6 +811,12 @@ dev = [ requires-dist = [ { name = "genesis-world" }, { name = "gymnasium", marker = "extra == 'rl'", specifier = ">=1.0" }, + { name = "mkdocs", marker = "extra == 'docs'", specifier = ">=1.6" }, + { name = "mkdocs-include-markdown-plugin", marker = "extra == 'docs'", specifier = ">=7" }, + { name = "mkdocs-material", marker = "extra == 'docs'", specifier = ">=9.5" }, + { name = "mkdocs-static-i18n", marker = "extra == 'docs'", specifier = ">=1.2" }, + { name = "mkdocstrings", extras = ["python"], marker = "extra == 'docs'", specifier = ">=0.26" }, + { name = "pymdown-extensions", marker = "extra == 'docs'", specifier = ">=10" }, { name = "questionary", specifier = ">=2" }, { name = "rich", specifier = ">=13" }, { name = "rsl-rl-lib", marker = "extra == 'rl'", specifier = ">=5.2.0" }, @@ -701,7 +828,7 @@ requires-dist = [ { name = "torch", marker = "extra == 'torch-cu130'", specifier = ">=2.8.0", index = "https://download.pytorch.org/whl/cu130", conflict = { package = "genelab", extra = "torch-cu130" } }, { name = "typer", specifier = ">=0.12" }, ] -provides-extras = ["torch-cpu", "torch-cu126", "torch-cu128", "torch-cu130", "rl"] +provides-extras = ["torch-cpu", "torch-cu126", "torch-cu128", "torch-cu130", "rl", "docs"] [package.metadata.requires-dev] dev = [ @@ -752,6 +879,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/46/8a/8b1e49767d010a33d909608dcb14b61f37cb9263239399260632bee63df4/genesis_world-0.4.6-py3-none-any.whl", hash = "sha256:d2cf05d0e416b3432e1e02d86b6269c5e8f1a8458852836a2dda6040543d2195", size = 82572753, upload-time = "2026-04-11T19:37:41.859Z" }, ] +[[package]] +name = "ghp-import" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" }, +] + [[package]] name = "gitdb" version = "4.0.12" @@ -808,6 +947,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cc/9b/4366ad3e1c0688146c70aa6143584d6a8d88583b9390f106250e25a3d5cd/glfw-2.10.0-py2.py3-none-win_amd64.whl", hash = "sha256:7f787ee8645781f10e8800438ce4357ab38c573ffb191aba380c1e72eba6311c", size = 559423, upload-time = "2026-03-10T17:21:34.766Z" }, ] +[[package]] +name = "griffelib" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/82/74f4a3310cdabfbb10da554c3a672847f1ed33c6f61dd472681ce7f1fe67/griffelib-2.0.2.tar.gz", hash = "sha256:3cf20b3bc470e83763ffbf236e0076b1211bac1bc67de13daf494640f2de707e", size = 166461, upload-time = "2026-03-27T11:34:51.091Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/8c/c9138d881c79aa0ea9ed83cbd58d5ca75624378b38cee225dcf5c42cc91f/griffelib-2.0.2-py3-none-any.whl", hash = "sha256:925c857658fb1ba40c0772c37acbc2ab650bd794d9c1b9726922e36ea4117ea1", size = 142357, upload-time = "2026-03-27T11:34:46.275Z" }, +] + [[package]] name = "grpcio" version = "1.80.0" @@ -872,6 +1020,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/73/fda6a25f3beeb5e49d74330b44092b9e5a547395ccd478d1103ddcbff1fc/gymnasium-1.3.0-py3-none-any.whl", hash = "sha256:6b8c159a8540dcbcb221722d7efda24d78ebbcbc3bd2ea1c2611aa2a34471fc2", size = 953904, upload-time = "2026-04-22T13:47:12.13Z" }, ] +[[package]] +name = "idna" +version = "3.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size = 199245, upload-time = "2026-05-12T22:45:57.011Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" }, +] + [[package]] name = "imageio" version = "2.37.3" @@ -1230,6 +1387,159 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] +[[package]] +name = "mergedeep" +version = "1.3.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-7-genelab-torch-cpu' and extra == 'extra-7-genelab-torch-cu126') or (extra == 'extra-7-genelab-torch-cpu' and extra == 'extra-7-genelab-torch-cu128') or (extra == 'extra-7-genelab-torch-cpu' and extra == 'extra-7-genelab-torch-cu130') or (extra == 'extra-7-genelab-torch-cu126' and extra == 'extra-7-genelab-torch-cu128') or (extra == 'extra-7-genelab-torch-cu126' and extra == 'extra-7-genelab-torch-cu130') or (extra == 'extra-7-genelab-torch-cu128' and extra == 'extra-7-genelab-torch-cu130')" }, + { name = "ghp-import" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mergedeep" }, + { name = "mkdocs-get-deps" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "pyyaml" }, + { name = "pyyaml-env-tag" }, + { name = "watchdog" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" }, +] + +[[package]] +name = "mkdocs-autorefs" +version = "1.4.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mkdocs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/c0/f641843de3f612a6b48253f39244165acff36657a91cc903633d456ae1ac/mkdocs_autorefs-1.4.4.tar.gz", hash = "sha256:d54a284f27a7346b9c38f1f852177940c222da508e66edc816a0fa55fc6da197", size = 56588, upload-time = "2026-02-10T15:23:55.105Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl", hash = "sha256:834ef5408d827071ad1bc69e0f39704fa34c7fc05bc8e1c72b227dfdc5c76089", size = 25530, upload-time = "2026-02-10T15:23:53.817Z" }, +] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mergedeep" }, + { name = "platformdirs" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/25/b3cccb187655b9393572bde9b09261d267c3bf2f2cdabe347673be5976a6/mkdocs_get_deps-0.2.2.tar.gz", hash = "sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1", size = 11047, upload-time = "2026-03-10T02:46:33.632Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl", hash = "sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650", size = 9555, upload-time = "2026-03-10T02:46:32.256Z" }, +] + +[[package]] +name = "mkdocs-include-markdown-plugin" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mkdocs" }, + { name = "wcmatch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/2d/bdf1aee3f4f7b34148b0f62298b62f03415160cb2707f09503c99a0a7cd5/mkdocs_include_markdown_plugin-7.2.2.tar.gz", hash = "sha256:f052ccb741eccf498116b826c1d78a2d761c56747372594709441cee0963fbc9", size = 25415, upload-time = "2026-03-29T15:15:14.2Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/a5/f6b2f0aa805dbda52f6265e9aff1450c8643195442facf29d475bdeba15d/mkdocs_include_markdown_plugin-7.2.2-py3-none-any.whl", hash = "sha256:f2ec4487cf32d3e33ca528f9366f20fb9280ded9c8d1630eb2bbda244962dcd1", size = 29528, upload-time = "2026-03-29T15:15:13.079Z" }, +] + +[[package]] +name = "mkdocs-material" +version = "9.7.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "babel" }, + { name = "backrefs" }, + { name = "colorama" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "mkdocs" }, + { name = "mkdocs-material-extensions" }, + { name = "paginate" }, + { name = "pygments" }, + { name = "pymdown-extensions" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/45/29/6d2bcf41ae40802c4beda2432396fff97b8456fb496371d1bc7aad6512ec/mkdocs_material-9.7.6.tar.gz", hash = "sha256:00bdde50574f776d328b1862fe65daeaf581ec309bd150f7bff345a098c64a69", size = 4097959, upload-time = "2026-03-19T15:41:58.161Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl", hash = "sha256:71b84353921b8ea1ba84fe11c50912cc512da8fe0881038fcc9a0761c0e635ba", size = 9305470, upload-time = "2026-03-19T15:41:55.217Z" }, +] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847, upload-time = "2023-11-22T19:09:45.208Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" }, +] + +[[package]] +name = "mkdocs-static-i18n" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mkdocs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/f9/51e2ffda9c7210bc35a24f3717b08c052cd4b728dfa87f901c00d8005259/mkdocs_static_i18n-1.3.1.tar.gz", hash = "sha256:a6125ea7db6cc1a900d76a967f262535af09831160a93c56d7f0d522a79b5faf", size = 1371325, upload-time = "2026-02-20T10:42:41.835Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/0b/43ff4afb6b438d47718b1959a22075ed95d8460d8c47381878b37a40de63/mkdocs_static_i18n-1.3.1-py3-none-any.whl", hash = "sha256:4036e24795a150c9c4d4b001ed24a43aec01335f76188dbe5a5d8fb4a27eba65", size = 21853, upload-time = "2026-02-20T10:42:40.551Z" }, +] + +[[package]] +name = "mkdocstrings" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mkdocs" }, + { name = "mkdocs-autorefs" }, + { name = "pymdown-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/5d/f888d4d3eb31359b327bc9b17a212d6ef03fe0b0682fbb3fc2cb849fb12b/mkdocstrings-1.0.4.tar.gz", hash = "sha256:3969a6515b77db65fd097b53c1b7aa4ae840bd71a2ee62a6a3e89503446d7172", size = 100088, upload-time = "2026-04-15T09:16:53.376Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/94/be70f8ee9c45f2f62b39a1f0e9303bc20e138a8f3b8e50ffd89498e177e1/mkdocstrings-1.0.4-py3-none-any.whl", hash = "sha256:63464b4b29053514f32a1dbbf604e52876d5e638111b0c295ab7ed3cac73ca9b", size = 35560, upload-time = "2026-04-15T09:16:51.436Z" }, +] + +[package.optional-dependencies] +python = [ + { name = "mkdocstrings-python" }, +] + +[[package]] +name = "mkdocstrings-python" +version = "2.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "griffelib" }, + { name = "mkdocs-autorefs" }, + { name = "mkdocstrings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/29/33/c225eaf898634bdda489a6766fc35d1683c640bffe0e0acd10646b13536d/mkdocstrings_python-2.0.3.tar.gz", hash = "sha256:c518632751cc869439b31c9d3177678ad2bfa5c21b79b863956ad68fc92c13b8", size = 199083, upload-time = "2026-02-20T10:38:36.368Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl", hash = "sha256:0b83513478bdfd803ff05aa43e9b1fca9dd22bcd9471f09ca6257f009bc5ee12", size = 104779, upload-time = "2026-02-20T10:38:34.517Z" }, +] + [[package]] name = "ml-dtypes" version = "0.5.4" @@ -2222,6 +2532,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, ] +[[package]] +name = "paginate" +version = "0.5.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" }, +] + +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, +] + [[package]] name = "pillow" version = "11.3.0" @@ -2288,6 +2616,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" }, ] +[[package]] +name = "platformdirs" +version = "4.9.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, +] + [[package]] name = "plotly" version = "5.24.1" @@ -2539,6 +2876,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] +[[package]] +name = "pymdown-extensions" +version = "10.21.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/08/f1c908c581fd11913da4711ea7ba32c0eee40b0190000996bb863b0c9349/pymdown_extensions-10.21.2.tar.gz", hash = "sha256:c3f55a5b8a1d0edf6699e35dcbea71d978d34ff3fa79f3d807b8a5b3fa90fbdc", size = 853922, upload-time = "2026-03-29T15:01:55.233Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/27/a2fc51a4a122dfd1015e921ae9d22fee3d20b0b8080d9a704578bf9deece/pymdown_extensions-10.21.2-py3-none-any.whl", hash = "sha256:5c0fd2a2bea14eb39af8ff284f1066d898ab2187d81b889b75d46d4348c01638", size = 268901, upload-time = "2026-03-29T15:01:53.244Z" }, +] + [[package]] name = "pymeshlab" version = "2025.7.post1" @@ -2710,6 +3060,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] +[[package]] +name = "pyyaml-env-tag" +version = "1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" }, +] + [[package]] name = "quadrants" version = "0.6.2" @@ -2746,6 +3108,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl", hash = "sha256:a51af13f345f1cdea62347589fbb6df3b290306ab8930713bfae4d475a7d4a59", size = 36753, upload-time = "2025-08-28T19:00:19.56Z" }, ] +[[package]] +name = "requests" +version = "2.34.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/b8/7a707d60fea4c49094e40262cc0e2ca6c768cca21587e34d3f705afec47e/requests-2.34.0.tar.gz", hash = "sha256:7d62fe92f50eb82c529b0916bb445afa1531a566fc8f35ffdc64446e771b856a", size = 142436, upload-time = "2026-05-11T19:29:51.717Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/e6/e300fce5fe83c30520607a015dabd985df3251e188d234bfe9492e17a389/requests-2.34.0-py3-none-any.whl", hash = "sha256:917520a21b767485ce7c588f4ebb917c436b24a31231b44228715eaeb5a52c60", size = 73021, upload-time = "2026-05-11T19:29:49.923Z" }, +] + [[package]] name = "rich" version = "15.0.0" @@ -3475,6 +3852,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, ] +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + [[package]] name = "vtk" version = "9.6.1" @@ -3502,6 +3888,42 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e1/d5/41edd3f8b38ad45b8fb30d12ef35be3d62e1221e455722a5d7d103ca2f0d/vtk-9.6.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:d08b3760cbd8bffdbb4551033c4c9d87afe30e9e9d4c0e5cad29cbe7107c9af7", size = 135524312, upload-time = "2026-03-27T14:02:46.035Z" }, ] +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, + { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, + { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, +] + +[[package]] +name = "wcmatch" +version = "10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bracex" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/3e/c0bdc27cf06f4e47680bd5803a07cb3dfd17de84cde92dd217dcb9e05253/wcmatch-10.1.tar.gz", hash = "sha256:f11f94208c8c8484a16f4f48638a85d771d9513f4ab3f37595978801cb9465af", size = 117421, upload-time = "2025-06-22T19:14:02.49Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/d8/0d1d2e9d3fabcf5d6840362adcf05f8cf3cd06a73358140c3a97189238ae/wcmatch-10.1-py3-none-any.whl", hash = "sha256:5848ace7dbb0476e5e55ab63c6bbd529745089343427caa5537f230cc01beb8a", size = 39854, upload-time = "2025-06-22T19:14:00.978Z" }, +] + [[package]] name = "wcwidth" version = "0.7.0"