# laykit **Repository Path**: kingdix10/laykit ## Basic Information - **Project Name**: laykit - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-24 - **Last Updated**: 2026-06-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # laykit Codegen-driven layered architecture framework —— 用 `@gen_expose` 标记 Python Service 方法,自动生成适配层和 CLI/GUI/WebUI 入口。 ## 架构 ``` ┌─ laykit 库(发布为 pip 包,不改)─────┐ │ @gen_expose · Entity · gen_adapters │ └────────────────┬──────────────────────┘ │ AST 扫描 ┌─ 你写的代码 ─────────────────────────────────┼──────────────────────┐ │ ▼ │ │ logic/service.py │ │ │ ┌─────────────────────────────────┐ │ │ │ │ class MyService: │ │ │ │ │ @gen_expose(as_="add", ...) │─────────┘ │ │ │ def create(...) -> Entity: │ │ │ └─────────────────────────────────┘ │ │ │ │ 运行 laykit-gen(或 laykit-gen -r logic/ --disable-api) │ └────────────────────────────────────┬───────────────────────────────┘ │ 生成 ▼ ┌─ adapters/(codegen 产出,不手改) ─────────────────────────────────┐ │ │ │ generated_base.py Adapter 类 + ADAPTERS 注册表 │ │ generated_rest.py REST 路由 + Pydantic models (可选) │ │ websocket.py WS 广播 (可选) │ │ __init__.py API_ENABLED 标记 + 条件导入 │ └────────────────────────────────────┬───────────────────────────────┘ │ 调用 ▼ ┌─ interfaces/(统一入口,按需启用) ──────────────────────────────────┐ │ │ │ 完整模式: cli · flask · web_dj · serve · web · gui_tk · gui │ │ 裁剪模式: cli · flask · web_dj · gui_tk · gui (无 --backend) │ │ │ │ python main.py cli items add "Task" │ │ python main.py serve --port 8081 │ │ python main.py flask --port 8080 --backend http://127.0.0.1:8081 │ └────────────────────────────────────────────────────────────────────┘ ``` ## 安装 ```bash pip install laykit # 可选:按需安装接口依赖 pip install laykit[fastapi] # REST + WS pip install laykit[flask] # Flask Web UI pip install laykit[django] # Django Web UI pip install laykit[pyside6] # Qt6 GUI pip install laykit[all] # 全部 ``` 安装后可用两个 CLI 工具: ```bash laykit-init mytool # 初始化新项目(wrapper 模式) laykit-init mytool --no-wrapper # 最少文件模式 laykit-init mytool --standalone # 独立模式(零依赖) laykit-gen # 扫描 logic/ -> 生成 adapters/ laykit-gen logic/ -r # 递归扫描子目录 ``` ## 快速开始 ### 1. 创建项目 ```bash pip install laykit laykit-init mytool cd mytool ``` 生成结构: ``` mytool/ main.py <- 入口(一行代码) logic/ __init__.py _maker.py <- from laykit import gen_expose(wrapper) models.py <- from laykit import Entity(wrapper) service.py <- 在这里写业务逻辑 interfaces/ __main__.py <- 调度中心(自动发现 + argparse) _interface.py <- adapter_map() cli/ __init__.py <- CLI 占位 requirements.txt ``` ### 2. 写业务逻辑 编辑 `logic/service.py`,用 `@gen_expose` 标记方法: ```python from ._maker import gen_expose from .models import Entity class ItemService: def __init__(self): self._items: dict[str, Entity] = {} @gen_expose(as_="create_item", emit="item_created", rest_method="POST", rest_path="/items") def create(self, name: str, description: str = "", status: str = "pending") -> Entity: entity = Entity(payload={ "name": name, "description": description, "status": status, }) self._items[entity.id] = entity return entity @gen_expose(as_="list_items", result="to_dict_list", rest_method="GET", rest_path="/items") def list_all(self) -> list[Entity]: return list(self._items.values()) @gen_expose(as_="get_item", rest_method="GET", rest_path="/items/{item_id}") def get(self, item_id: str) -> Entity: return self._items[item_id] @gen_expose(as_="update_item", rest_method="PUT", rest_path="/items/{item_id}") def update(self, item_id: str, name: str = None, description: str = None, status: str = None) -> Entity: item = self._items[item_id] if name is not None: item.payload["name"] = name if description is not None: item.payload["description"] = description if status is not None: item.payload["status"] = status return item @gen_expose(as_="delete_item", result="void", rest_method="DELETE", rest_path="/items/{item_id}") def delete(self, item_id: str) -> None: del self._items[item_id] ``` ### 3. 生成适配层 ```bash laykit-gen # 扫描 logic/(非递归) laykit-gen logic/ -r # 递归扫描子目录 laykit-gen logic/ -o output/ # 指定输出目录 # 输出: adapters/generated_base.py + generated_rest.py + websocket.py + __init__.py ``` ### 4. 运行 ```bash python main.py cli items add "Task" --desc "Buy milk" python main.py cli items list # 或者启用 Web UI(需安装对应依赖) python main.py serve --port 8081 # REST + WS 服务 python main.py flask --port 8080 # Flask 管理界面 ``` ## @gen_expose 参数 | 参数 | 类型 | 说明 | |------|------|------| | `as_` | `str` | 适配层方法名(默认同 Service 方法) | | `params` | `str` | 手动指定参数签名,如 `"name:str, age:int=0"` | | `build` | `str` | 手动构造 Service 调用表达式 | | `result` | `str` | `"to_dict"` / `"to_dict_list"` / `"void"` | | `emit` | `str` | WS 广播事件名 | | `rest_method` | `str` | HTTP 方法,省略则跳过 REST 生成 | | `rest_path` | `str` | URL 路径,如 `"/items/{item_id}"` | ## Entity `laykit.Entity` 是通用数据容器。外部直接操作 `payload` 字典: ```python e = Entity(payload={"name": "Task", "status": "pending"}) e.to_dict() # {"id": "...", "created_at": "...", "name": "Task", ...} e.id # 只读 e.payload["status"] = "completed" # 可写 ``` ## --disable-api 裁剪 ```bash laykit-gen --disable-api laykit-gen --disable-api -r logic/ # 递归 + 裁剪 ``` 跳过 `generated_rest.py` 和 `websocket.py`,只生成基础适配器。此时 `serve` 和 `web` 子命令不可用,CLI/Flask/Django/GUI 仅内联模式(无 `--backend`)。 ## 添加更多接口 编辑 `interfaces/__main__.py` 的 `_BASE_REGISTRY`,取消注释: ```python _BASE_REGISTRY = { "cli": "cli", "web": "webui_fastapi", # 取消注释 "flask": "webui_flask", # 取消注释 } ``` 然后将对应的 interface 目录复制到 `interfaces/` 下。完整示例见 `examples/item-manager/`(包含 CLI / Flask / Django / FastAPI / Tkinter / PySide6 六种交互模式)。 ## 项目初始化模式 ```bash laykit-init mytool # 默认:wrapper 模式(推荐) laykit-init mytool --no-wrapper # 最少文件,service 直接 import laykit laykit-init mytool --standalone # 独立模式,完整副本,不依赖 laykit ``` | 模式 | `_maker.py` / `models.py` | `service.py` 导入 | 依赖 laykit | |------|--------------------------|-------------------|------------| | 默认 | thin wrapper(一行 re-export) | 本地导入 | 是 | | `--no-wrapper` | 不生成 | `from laykit import ...` | 是 | | `--standalone` | 完整副本 | 本地导入 | 否 | `--standalone` 和 `--no-wrapper` 互斥。