From 9d033e1c0b4eabfba967ac166d91cefcd2108323 Mon Sep 17 00:00:00 2001 From: gooker_young Date: Sat, 27 Jun 2026 17:12:53 +0800 Subject: [PATCH] refactor(system): add setenv_group and write_file task helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 为setenv和which函数添加正确的返回类型注解 2. 新增setenv_group批量设置环境变量的任务组 3. 新增write_file写入文件的任务工具函数 4. 更新__all__导出所有新增的工具函数 feat(cli/envdev): rewrite envdev cli with proper config and args 1. 重构环境开发CLI脚本,使用argparse替换原有TypedDict配置 2. 新增Python和Conda镜像源选择参数 3. 自动生成并写入Python pip和Conda配置文件 4. 优化任务依赖和命名,统一使用系统工具函数 --- src/pyflowx/cli/envdev.py | 152 ++++++++++++++++++++++++++++++------ src/pyflowx/tasks/system.py | 24 +++++- 2 files changed, 147 insertions(+), 29 deletions(-) diff --git a/src/pyflowx/cli/envdev.py b/src/pyflowx/cli/envdev.py index fc59c5c..e8ed0a0 100644 --- a/src/pyflowx/cli/envdev.py +++ b/src/pyflowx/cli/envdev.py @@ -1,54 +1,101 @@ -from typing import TypedDict +from __future__ import annotations + +import argparse +from pathlib import Path +from typing import Literal, get_args import pyflowx as px - - -class EnvConfig(TypedDict): - """环境配置项.""" - - name: str - value: str - description: str - - -PIP_INDEX_URL_CONFIG: EnvConfig = { - "name": "PIP_INDEX_URL", - "value": "https://pypi.tuna.tsinghua.edu.cn/simple", - "description": "PIP索引URL", -} - +from pyflowx.tasks.system import Constants, setenv_group, write_file # ============================================================================ -# 配置 +# Mirror 配置 # ============================================================================ +DOWNLOAD_MIRROR_SCRIPT: str = "curl -sSL https://linuxmirrors.cn/main.sh -o /tmp/linuxmirrors.sh" +INSTALL_MIRROR_SCRIPT: str = "sudo bash /tmp/linuxmirrors.sh" -PIP_INDEX_URLS: dict[str, str] = { +# ============================================================================ +# Python 配置 +# ============================================================================ +PyMirrorType = Literal["tsinghua", "aliyun"] + +PIP_INDEX_URLS: dict[PyMirrorType, str] = { "tsinghua": "https://pypi.tuna.tsinghua.edu.cn/simple", "aliyun": "https://mirrors.aliyun.com/pypi/simple/", } -PIP_TRUSTED_HOSTS: dict[str, str] = { +PIP_TRUSTED_HOSTS: dict[PyMirrorType, str] = { "tsinghua": "pypi.tuna.tsinghua.edu.cn", "aliyun": "mirrors.aliyun.com", } +PIP_CONFIG_PATH = Path.home() / ".pip" / "pip.conf" if not Constants.IS_WINDOWS else Path.home() / "pip" / "pip.ini" -UV_INDEX_URL: str = "https://mirrors.aliyun.com/pypi/simple/" +UV_INDEX_URLS: dict[PyMirrorType, str] = { + "tsinghua": "https://pypi.tuna.tsinghua.edu.cn/simple", + "aliyun": "https://mirrors.aliyun.com/pypi/simple/", +} UV_PYTHON_INSTALL_MIRROR: str = "https://registry.npmmirror.com/-/binary/python-build-standalone" -CONDA_MIRROR_URLS: dict[str, list[str]] = { +# ============================================================================ +# Conda 配置 +# ============================================================================ +CondaMirrorType = Literal["tsinghua", "ustc", "bsfu", "aliyun"] + +CONDA_MIRROR_URLS: dict[CondaMirrorType, list[str]] = { "tsinghua": [ "https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/", "https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/", + "https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r/", + "https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2/", + "https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/pro/", "https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/", + "https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda/", + "https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/menpo/", + "https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/", + ], + "ustc": [ + "https://mirrors.ustc.edu.cn/anaconda/pkgs/main/", + "https://mirrors.ustc.edu.cn/anaconda/pkgs/free/", + "https://mirrors.ustc.edu.cn/anaconda/pkgs/r/", + "https://mirrors.ustc.edu.cn/anaconda/pkgs/msys2/", + "https://mirrors.ustc.edu.cn/anaconda/pkgs/pro/", + "https://mirrors.ustc.edu.cn/anaconda/pkgs/dev/", + "https://mirrors.ustc.edu.cn/anaconda/cloud/conda-forge/", + "https://mirrors.ustc.edu.cn/anaconda/cloud/bioconda/", + "https://mirrors.ustc.edu.cn/anaconda/cloud/menpo/", + "https://mirrors.ustc.edu.cn/anaconda/cloud/pytorch/", + ], + "bsfu": [ + "https://mirrors.bsfu.edu.cn/anaconda/pkgs/main/", + "https://mirrors.bsfu.edu.cn/anaconda/pkgs/free/", + "https://mirrors.bsfu.edu.cn/anaconda/pkgs/r/", + "https://mirrors.bsfu.edu.cn/anaconda/pkgs/msys2/", + "https://mirrors.bsfu.edu.cn/anaconda/pkgs/pro/", + "https://mirrors.bsfu.edu.cn/anaconda/pkgs/dev/", + "https://mirrors.bsfu.edu.cn/anaconda/cloud/conda-forge/", + "https://mirrors.bsfu.edu.cn/anaconda/cloud/bioconda/", + "https://mirrors.bsfu.edu.cn/anaconda/cloud/menpo/", + "https://mirrors.bsfu.edu.cn/anaconda/cloud/pytorch/", ], "aliyun": [ "https://mirrors.aliyun.com/anaconda/pkgs/main/", "https://mirrors.aliyun.com/anaconda/pkgs/free/", + "https://mirrors.aliyun.com/anaconda/pkgs/r/", + "https://mirrors.aliyun.com/anaconda/pkgs/msys2/", + "https://mirrors.aliyun.com/anaconda/pkgs/pro/", + "https://mirrors.aliyun.com/anaconda/pkgs/dev/", "https://mirrors.aliyun.com/anaconda/cloud/conda-forge/", + "https://mirrors.aliyun.com/anaconda/cloud/bioconda/", + "https://mirrors.aliyun.com/anaconda/cloud/menpo/", + "https://mirrors.aliyun.com/anaconda/cloud/pytorch/", ], } +CONDA_CONFIG_PATH = Path.home() / ".condarc" +# ============================================================================ +# Qt 配置 +# ============================================================================ + QT_LIBS: list[str] = [ "build-essential", "libgl1", @@ -79,19 +126,72 @@ CHINESE_FONTS: list[str] = [ def main() -> None: """主函数.""" + parser = argparse.ArgumentParser(description="环境开发工具") + parser.add_argument( + "--python-mirror", + nargs="?", + type=str, + default="tsinghua", + choices=get_args(PyMirrorType), + help="Python 镜像源", + ) + parser.add_argument( + "--conda-mirror", + nargs="?", + type=str, + default="tsinghua", + choices=get_args(CondaMirrorType), + help="Conda 镜镜像源", + ) + args = parser.parse_args() + + python_mirror = args.python_mirror + python_envs: dict[str, str] = { + "PIP_INDEX_URL": PIP_INDEX_URLS[python_mirror], + "PIP_TRUSTED_HOSTS": PIP_TRUSTED_HOSTS[python_mirror], + "UV_INDEX_URL": UV_INDEX_URLS[python_mirror], + "UV_PYTHON_INSTALL_MIRROR": UV_PYTHON_INSTALL_MIRROR, + "UV_HTTP_TIMEOUT": "600", + "UV_LINK_MODE": "copy", + } + + conda_mirror_urls = CONDA_MIRROR_URLS[args.conda_mirror] + + # 确保配置文件目录存在 + PIP_CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True) + CONDA_CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True) + # 使用更安全的分步执行方式,便于调试和捕获错误 graph = px.Graph.from_specs([ # 下载镜像 - px.TaskSpec("download", cmd="curl -sSL https://linuxmirrors.cn/main.sh -o /tmp/linuxmirrors.sh", verbose=True), + px.TaskSpec("download_mirror", cmd=DOWNLOAD_MIRROR_SCRIPT, verbose=True), # 安装镜像 - px.TaskSpec("install", cmd="sudo bash /tmp/linuxmirrors.sh", verbose=True, depends_on=("download",)), + px.TaskSpec("install_mirror", cmd=INSTALL_MIRROR_SCRIPT, verbose=True, depends_on=("download_mirror",)), # 安装 PyQt 相关依赖 px.TaskSpec( - "envqt_install", cmd=["sudo", "apt", "install", "-y", *QT_LIBS], verbose=True, depends_on=("install",) + "install_qt_libs", + cmd=["sudo", "apt", "install", "-y", *QT_LIBS], + verbose=True, + depends_on=("install_mirror",), ), # 安装中文字体 px.TaskSpec( - "envqt_fonts", cmd=["sudo", "apt", "install", "-y", *CHINESE_FONTS], verbose=True, depends_on=("install",) + "install_fonts", + cmd=["sudo", "apt", "install", "-y", *CHINESE_FONTS], + verbose=True, + depends_on=("install_mirror",), + ), + # 设置 Python 环境变量 + *setenv_group(python_envs), + # 写入 Python 配置 + write_file( + str(PIP_CONFIG_PATH), + f"[global]\nindex-url = {PIP_INDEX_URLS[python_mirror]}\ntrusted-host = {PIP_TRUSTED_HOSTS[python_mirror]}", + ), + # 写入 Conda 配置 + write_file( + str(CONDA_CONFIG_PATH), + "show_channel_urls: true\nchannels:" + "\n".join(conda_mirror_urls) + "\n - defaults", ), ]) px.run(graph, strategy="thread", verbose=True) diff --git a/src/pyflowx/tasks/system.py b/src/pyflowx/tasks/system.py index fc866e3..4ca1c6d 100644 --- a/src/pyflowx/tasks/system.py +++ b/src/pyflowx/tasks/system.py @@ -66,7 +66,7 @@ def reset_icon_cache() -> list[px.TaskSpec]: ] -def setenv(name: str, value: str, default: bool = False): +def setenv(name: str, value: str, default: bool = False) -> px.TaskSpec: """设置环境变量任务.""" def set_env(): @@ -78,7 +78,12 @@ def setenv(name: str, value: str, default: bool = False): return px.TaskSpec(f"setenv_{name.lower()}", fn=set_env, verbose=True) -def which(cmd: str): +def setenv_group(envs: dict[str, str], default: bool = False) -> list[px.TaskSpec]: + """设置环境变量组任务.""" + return [setenv(name, value, default) for name, value in envs.items()] + + +def which(cmd: str) -> px.TaskSpec: """查找命令路径任务.""" which_cmd = "where" if Constants.IS_WINDOWS else "which" @@ -95,4 +100,17 @@ def which(cmd: str): return px.TaskSpec(f"which_{cmd}", fn=find_command) -__all__ = ["clr", "setenv", "which"] +def write_file(path: str, content: str, encoding: str = "utf-8") -> px.TaskSpec: + """写入文件任务.""" + + def write(): + try: + with open(path, "w", encoding=encoding) as f: + f.write(content) + except Exception as e: + print(f"写入文件 {path} 失败: {e}") + + return px.TaskSpec(f"write_file_{path}", fn=write, verbose=True) + + +__all__ = ["clr", "reset_icon_cache", "setenv", "setenv_group", "which", "write_file"]