chore: 移除独立的envpy和envrs命令,合并功能到envdev
将原来envpy和envrs的环境配置功能整合到envdev命令中,删除了冗余的独立CLI模块和测试文件,统一管理Python、Conda和Rust的环境配置。
This commit is contained in:
@@ -29,8 +29,6 @@ bumpversion = "pyflowx.cli.bumpversion:main"
|
|||||||
clr = "pyflowx.cli.clearscreen:main"
|
clr = "pyflowx.cli.clearscreen:main"
|
||||||
emlman = "pyflowx.cli.emlmanager:main"
|
emlman = "pyflowx.cli.emlmanager:main"
|
||||||
envdev = "pyflowx.cli.envdev:main"
|
envdev = "pyflowx.cli.envdev:main"
|
||||||
envpy = "pyflowx.cli.envpy:main"
|
|
||||||
envrs = "pyflowx.cli.envrs:main"
|
|
||||||
filedate = "pyflowx.cli.filedate:main"
|
filedate = "pyflowx.cli.filedate:main"
|
||||||
filelvl = "pyflowx.cli.filelevel:main"
|
filelvl = "pyflowx.cli.filelevel:main"
|
||||||
foldback = "pyflowx.cli.folderback:main"
|
foldback = "pyflowx.cli.folderback:main"
|
||||||
|
|||||||
@@ -127,6 +127,37 @@ CHINESE_FONTS: list[str] = [
|
|||||||
"fonts-noto-color-emoji",
|
"fonts-noto-color-emoji",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Rust 配置
|
||||||
|
# ============================================================================
|
||||||
|
RustMirrorType = Literal["tsinghua", "ustc", "aliyun"]
|
||||||
|
RustVersionType = Literal["stable", "nightly", "beta"]
|
||||||
|
DEFAULT_RUST_VERSION: RustVersionType = "stable"
|
||||||
|
DEFAULT_MIRROR: RustMirrorType = "tsinghua"
|
||||||
|
|
||||||
|
RUSTUP_MIRRORS: dict[RustMirrorType, dict[str, str]] = {
|
||||||
|
"tsinghua": {
|
||||||
|
"RUSTUP_DIST_SERVER": "https://mirrors.tuna.tsinghua.edu.cn/rustup",
|
||||||
|
"RUSTUP_UPDATE_ROOT": "https://mirrors.tuna.tsinghua.edu.cn/rustup/rustup",
|
||||||
|
"TOML_REGISTRY": "https://mirrors.tuna.tsinghua.edu.cn/crates.io-index/",
|
||||||
|
},
|
||||||
|
"aliyun": {
|
||||||
|
"RUSTUP_DIST_SERVER": "https://mirrors.aliyun.com/rustup",
|
||||||
|
"RUSTUP_UPDATE_ROOT": "https://mirrors.aliyun.com/rustup/rustup",
|
||||||
|
"TOML_REGISTRY": "https://mirrors.aliyun.com/crates.io-index/",
|
||||||
|
},
|
||||||
|
"ustc": {
|
||||||
|
"RUSTUP_DIST_SERVER": "https://mirrors.ustc.edu.cn/rust-static",
|
||||||
|
"RUSTUP_UPDATE_ROOT": "https://mirrors.ustc.edu.cn/rust-static/rustup",
|
||||||
|
"TOML_REGISTRY": "https://mirrors.ustc.edu.cn/crates.io-index/",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
RUSTUP_DOWNLOAD_URL_LINUX = "https://mirrors.aliyun.com/repo/rust/rustup-init.sh"
|
||||||
|
RUSTUP_DOWNLOAD_URL_WINDOWS = "https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe"
|
||||||
|
RUST_CONFIG_PATH = Path.home() / ".cargo" / "config.toml"
|
||||||
|
RUST_SCCACHE_DIR: Path = Path.home() / ".cargo" / "sccache"
|
||||||
|
RUST_SCCACHE_CACHE_SIZE: str = "20G"
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
"""主函数."""
|
"""主函数."""
|
||||||
@@ -147,14 +178,34 @@ def main() -> None:
|
|||||||
choices=get_args(CondaMirrorType),
|
choices=get_args(CondaMirrorType),
|
||||||
help="Conda 镜镜像源",
|
help="Conda 镜镜像源",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--rust-mirror",
|
||||||
|
nargs="?",
|
||||||
|
type=str,
|
||||||
|
default=DEFAULT_MIRROR,
|
||||||
|
choices=get_args(RustMirrorType),
|
||||||
|
help="Rust 镜像源",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--rust-version",
|
||||||
|
nargs="?",
|
||||||
|
type=str,
|
||||||
|
default=DEFAULT_RUST_VERSION,
|
||||||
|
choices=get_args(RustVersionType),
|
||||||
|
help=f"Rust 版本, 推荐: {get_args(RustVersionType)}",
|
||||||
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
python_mirror = args.python_mirror
|
python_mirror = args.python_mirror
|
||||||
conda_mirror_urls = CONDA_MIRROR_URLS[args.conda_mirror]
|
conda_mirror_urls = CONDA_MIRROR_URLS[args.conda_mirror]
|
||||||
|
rust_mirror = args.rust_mirror
|
||||||
|
rust_version = args.rust_version
|
||||||
|
|
||||||
# 确保配置文件目录存在
|
# 确保配置文件目录存在
|
||||||
PIP_CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
PIP_CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||||
CONDA_CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
CONDA_CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
RUST_CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
RUST_SCCACHE_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# 使用 conditions 自动控制任务执行
|
# 使用 conditions 自动控制任务执行
|
||||||
graph = px.Graph.from_specs([
|
graph = px.Graph.from_specs([
|
||||||
@@ -222,5 +273,59 @@ def main() -> None:
|
|||||||
str(CONDA_CONFIG_PATH),
|
str(CONDA_CONFIG_PATH),
|
||||||
"show_channel_urls: true\nchannels:\n - " + "\n - ".join(conda_mirror_urls) + "\n - defaults",
|
"show_channel_urls: true\nchannels:\n - " + "\n - ".join(conda_mirror_urls) + "\n - defaults",
|
||||||
),
|
),
|
||||||
|
# 设置 Rust 镜像源
|
||||||
|
*setenv_group({
|
||||||
|
"RUSTUP_DIST_SERVER": RUSTUP_MIRRORS[rust_mirror]["RUSTUP_DIST_SERVER"],
|
||||||
|
"RUSTUP_UPDATE_ROOT": RUSTUP_MIRRORS[rust_mirror]["RUSTUP_UPDATE_ROOT"],
|
||||||
|
"RUST_SCCACHE_DIR": str(RUST_SCCACHE_DIR),
|
||||||
|
"RUST_SCCACHE_CACHE_SIZE": RUST_SCCACHE_CACHE_SIZE,
|
||||||
|
}),
|
||||||
|
# 写入 Rust 配置(仅当未配置)
|
||||||
|
write_file(
|
||||||
|
str(RUST_CONFIG_PATH),
|
||||||
|
f"""
|
||||||
|
[source.crates-io]
|
||||||
|
replace-with = '{rust_mirror}'
|
||||||
|
|
||||||
|
[source.{rust_mirror}]
|
||||||
|
registry = "sparse+{RUSTUP_MIRRORS[rust_mirror]["TOML_REGISTRY"]}"
|
||||||
|
|
||||||
|
[registries.{rust_mirror}]
|
||||||
|
index = "sparse+{RUSTUP_MIRRORS[rust_mirror]["TOML_REGISTRY"]}"
|
||||||
|
""",
|
||||||
|
),
|
||||||
|
# 下载 Rustup 安装脚本
|
||||||
|
px.TaskSpec(
|
||||||
|
"download_rustup",
|
||||||
|
cmd=["curl", "-fsSL", RUSTUP_DOWNLOAD_URL_LINUX, "-o", "rustup-init.sh"],
|
||||||
|
conditions=(BuiltinConditions.IS_LINUX(), BuiltinConditions.NOT(BuiltinConditions.HAS_INSTALLED("rustup"))),
|
||||||
|
verbose=True,
|
||||||
|
),
|
||||||
|
px.TaskSpec(
|
||||||
|
"download_rustup_win",
|
||||||
|
cmd=[
|
||||||
|
"powershell",
|
||||||
|
"-Command",
|
||||||
|
"Invoke-WebRequest",
|
||||||
|
"-Uri",
|
||||||
|
RUSTUP_DOWNLOAD_URL_WINDOWS,
|
||||||
|
"-OutFile",
|
||||||
|
"rustup-init.exe",
|
||||||
|
],
|
||||||
|
conditions=(
|
||||||
|
BuiltinConditions.IS_WINDOWS(),
|
||||||
|
BuiltinConditions.NOT(BuiltinConditions.HAS_INSTALLED("rustup")),
|
||||||
|
),
|
||||||
|
verbose=True,
|
||||||
|
),
|
||||||
|
# 安装 Rust 工具链
|
||||||
|
px.TaskSpec(
|
||||||
|
"install_rust",
|
||||||
|
cmd=["rustup", "toolchain", "install", rust_version],
|
||||||
|
conditions=(BuiltinConditions.HAS_INSTALLED("rustup"),),
|
||||||
|
depends_on=("setenv_rustup_dist_server",),
|
||||||
|
allow_upstream_skip=True,
|
||||||
|
verbose=True,
|
||||||
|
),
|
||||||
])
|
])
|
||||||
px.run(graph, strategy="thread", verbose=True)
|
px.run(graph, strategy="thread", verbose=True)
|
||||||
|
|||||||
@@ -1,122 +0,0 @@
|
|||||||
"""Python 环境配置工具.
|
|
||||||
|
|
||||||
用于设置 pip 镜像源, 支持清华和阿里云等国内镜像源,
|
|
||||||
同时配置 UV 和 Conda 的镜像源.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import pyflowx as px
|
|
||||||
from pyflowx.conditions import Constants
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# 配置
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
PIP_INDEX_URLS: dict[str, str] = {
|
|
||||||
"tsinghua": "https://pypi.tuna.tsinghua.edu.cn/simple",
|
|
||||||
"aliyun": "https://mirrors.aliyun.com/pypi/simple/",
|
|
||||||
}
|
|
||||||
|
|
||||||
PIP_TRUSTED_HOSTS: dict[str, str] = {
|
|
||||||
"tsinghua": "pypi.tuna.tsinghua.edu.cn",
|
|
||||||
"aliyun": "mirrors.aliyun.com",
|
|
||||||
}
|
|
||||||
|
|
||||||
UV_INDEX_URL: str = "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]] = {
|
|
||||||
"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/cloud/conda-forge/",
|
|
||||||
],
|
|
||||||
"aliyun": [
|
|
||||||
"https://mirrors.aliyun.com/anaconda/pkgs/main/",
|
|
||||||
"https://mirrors.aliyun.com/anaconda/pkgs/free/",
|
|
||||||
"https://mirrors.aliyun.com/anaconda/cloud/conda-forge/",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# 辅助函数
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
|
|
||||||
def set_pip_mirror(mirror: str = "tsinghua", token: str | None = None) -> None:
|
|
||||||
"""设置 pip 镜像源.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
mirror : str
|
|
||||||
镜像源名称: tsinghua, aliyun
|
|
||||||
token : str | None
|
|
||||||
PyPI token for publishing
|
|
||||||
"""
|
|
||||||
index_url = PIP_INDEX_URLS.get(mirror, PIP_INDEX_URLS["tsinghua"])
|
|
||||||
trusted_host = PIP_TRUSTED_HOSTS.get(mirror, "")
|
|
||||||
|
|
||||||
# 设置环境变量
|
|
||||||
os.environ["PIP_INDEX_URL"] = index_url
|
|
||||||
os.environ["UV_INDEX_URL"] = UV_INDEX_URL
|
|
||||||
os.environ["UV_DEFAULT_INDEX"] = UV_INDEX_URL
|
|
||||||
os.environ["UV_PYTHON_INSTALL_MIRROR"] = UV_PYTHON_INSTALL_MIRROR
|
|
||||||
|
|
||||||
# 写入 pip 配置文件
|
|
||||||
pip_dir = Path.home() / "pip"
|
|
||||||
pip_dir.mkdir(exist_ok=True)
|
|
||||||
pip_conf = pip_dir / ("pip.ini" if Constants.IS_WINDOWS else "pip.conf")
|
|
||||||
pip_conf.write_text(f"[global]\nindex-url = {index_url}\n[install]\ntrusted-host = {trusted_host}\n")
|
|
||||||
|
|
||||||
# 写入 conda 配置文件
|
|
||||||
condarc = Path.home() / ".condarc"
|
|
||||||
conda_urls = CONDA_MIRROR_URLS.get(mirror, CONDA_MIRROR_URLS["tsinghua"])
|
|
||||||
condarc.write_text(
|
|
||||||
"show_channel_urls: true\nchannels:\n" + "\n".join(f" - {url}" for url in conda_urls) + "\n - defaults\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 写入 pypirc 配置文件 (如果有 token)
|
|
||||||
if token:
|
|
||||||
pypirc = Path.home() / ".pypirc"
|
|
||||||
pypirc.write_text(
|
|
||||||
f"[pypi]\nrepository: https://upload.pypi.org/legacy/\nusername: __token__\npassword: {token}\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"已设置 pip 镜像源: {mirror} ({index_url})")
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# CLI Runner
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
"""Python 环境配置工具主函数."""
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="EnvPy - Python 环境配置工具",
|
|
||||||
usage="envpy <command> [options]",
|
|
||||||
)
|
|
||||||
subparsers = parser.add_subparsers(dest="command", help="可用命令")
|
|
||||||
|
|
||||||
# 设置镜像源命令
|
|
||||||
mirror_parser = subparsers.add_parser("mirror", help="设置 pip 镜像源")
|
|
||||||
mirror_parser.add_argument("name", choices=["tsinghua", "aliyun"], help="镜像源名称")
|
|
||||||
mirror_parser.add_argument("--token", type=str, help="PyPI token for publishing")
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
if args.command == "mirror":
|
|
||||||
graph = px.Graph.from_specs([
|
|
||||||
px.TaskSpec("set_pip_mirror", fn=set_pip_mirror, args=(args.name,), kwargs={"token": args.token})
|
|
||||||
])
|
|
||||||
else:
|
|
||||||
parser.print_help()
|
|
||||||
return
|
|
||||||
|
|
||||||
px.run(graph, strategy="thread")
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
"""Rust 环境配置工具.
|
|
||||||
|
|
||||||
配置 Rustup 和 Cargo 的国内镜像源,
|
|
||||||
加速 Rust 工具链和依赖包的下载.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Literal, get_args
|
|
||||||
|
|
||||||
import pyflowx as px
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# 配置
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
RUSTUP_MIRRORS: dict[str, dict[str, str]] = {
|
|
||||||
"aliyun": {
|
|
||||||
"RUSTUP_DIST_SERVER": "https://mirrors.aliyun.com/rustup",
|
|
||||||
"RUSTUP_UPDATE_ROOT": "https://mirrors.aliyun.com/rustup/rustup",
|
|
||||||
"TOML_REGISTRY": "https://mirrors.aliyun.com/crates.io-index/",
|
|
||||||
},
|
|
||||||
"ustc": {
|
|
||||||
"RUSTUP_DIST_SERVER": "https://mirrors.ustc.edu.cn/rust-static",
|
|
||||||
"RUSTUP_UPDATE_ROOT": "https://mirrors.ustc.edu.cn/rust-static/rustup",
|
|
||||||
"TOML_REGISTRY": "https://mirrors.ustc.edu.cn/crates.io-index/",
|
|
||||||
},
|
|
||||||
"tsinghua": {
|
|
||||||
"RUSTUP_DIST_SERVER": "https://mirrors.tuna.tsinghua.edu.cn/rustup",
|
|
||||||
"RUSTUP_UPDATE_ROOT": "https://mirrors.tuna.tsinghua.edu.cn/rustup/rustup",
|
|
||||||
"TOML_REGISTRY": "https://mirrors.tuna.tsinghua.edu.cn/crates.io-index/",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
UsableRustVersion = Literal["stable", "nightly", "beta"]
|
|
||||||
UsableMirror = Literal["aliyun", "ustc", "tsinghua"]
|
|
||||||
|
|
||||||
DEFAULT_RUST_VERSION: UsableRustVersion = "stable"
|
|
||||||
DEFAULT_MIRROR: UsableMirror = "tsinghua"
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# 辅助函数
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
|
|
||||||
def set_rust_mirror(mirror: UsableMirror = DEFAULT_MIRROR) -> None:
|
|
||||||
"""设置 Rust 镜像源.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
mirror : str
|
|
||||||
镜像源名称: aliyun, ustc, tsinghua
|
|
||||||
"""
|
|
||||||
mirror_dict = RUSTUP_MIRRORS.get(mirror, RUSTUP_MIRRORS[DEFAULT_MIRROR])
|
|
||||||
server = mirror_dict["RUSTUP_DIST_SERVER"]
|
|
||||||
update_root = mirror_dict["RUSTUP_UPDATE_ROOT"]
|
|
||||||
toml_registry = mirror_dict["TOML_REGISTRY"]
|
|
||||||
|
|
||||||
# 设置环境变量
|
|
||||||
os.environ["RUSTUP_DIST_SERVER"] = server
|
|
||||||
os.environ["RUSTUP_UPDATE_ROOT"] = update_root
|
|
||||||
|
|
||||||
# 写入 cargo 配置
|
|
||||||
cargo_dir = Path.home() / ".cargo"
|
|
||||||
cargo_dir.mkdir(exist_ok=True)
|
|
||||||
cargo_config = cargo_dir / "config.toml"
|
|
||||||
cargo_config.write_text(
|
|
||||||
f"""[source.crates-io]
|
|
||||||
replace-with = '{mirror}'
|
|
||||||
|
|
||||||
[source.{mirror}]
|
|
||||||
registry = "sparse+{toml_registry}"
|
|
||||||
|
|
||||||
[registries.{mirror}]
|
|
||||||
index = "sparse+{toml_registry}"
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"已设置 Rust 镜像源: {mirror}")
|
|
||||||
|
|
||||||
|
|
||||||
def install_rust(version: UsableRustVersion = DEFAULT_RUST_VERSION) -> None:
|
|
||||||
"""安装 Rust 工具链.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
version : str
|
|
||||||
Rust 版本: stable, nightly, beta
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
subprocess.run(["rustup", "toolchain", "install", version], check=True)
|
|
||||||
print(f"已安装 Rust {version}")
|
|
||||||
except FileNotFoundError:
|
|
||||||
print("未找到 rustup,请先安装 Rust: https://rustup.rs")
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# CLI Runner
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
"""Rust 环境配置工具主函数."""
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="EnvRs - Rust 环境配置工具",
|
|
||||||
usage="envrs <command> [options]",
|
|
||||||
)
|
|
||||||
subparsers = parser.add_subparsers(dest="command", help="可用命令")
|
|
||||||
|
|
||||||
# 设置镜像源命令
|
|
||||||
mirror_parser = subparsers.add_parser("mirror", help="设置 Rust 镜像源")
|
|
||||||
mirror_parser.add_argument(
|
|
||||||
"name",
|
|
||||||
nargs="?",
|
|
||||||
default=DEFAULT_MIRROR,
|
|
||||||
choices=get_args(UsableMirror),
|
|
||||||
help=f"镜像源名称 ({get_args(UsableMirror)})",
|
|
||||||
)
|
|
||||||
|
|
||||||
# 安装 Rust 命令
|
|
||||||
install_parser = subparsers.add_parser("install", help="安装 Rust 工具链")
|
|
||||||
install_parser.add_argument(
|
|
||||||
"version",
|
|
||||||
nargs="?",
|
|
||||||
default=DEFAULT_RUST_VERSION,
|
|
||||||
choices=get_args(UsableRustVersion),
|
|
||||||
help=f"Rust 版本 ({get_args(UsableRustVersion)})",
|
|
||||||
)
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
if args.command == "mirror":
|
|
||||||
graph = px.Graph.from_specs([
|
|
||||||
px.TaskSpec("set_rust_mirror", fn=set_rust_mirror, args=(args.name,), verbose=True)
|
|
||||||
])
|
|
||||||
elif args.command == "install":
|
|
||||||
graph = px.Graph.from_specs([
|
|
||||||
px.TaskSpec("install_rust", cmd=["rustup", "toolchain", "install", args.version], verbose=True)
|
|
||||||
])
|
|
||||||
else:
|
|
||||||
parser.print_help()
|
|
||||||
return
|
|
||||||
|
|
||||||
px.run(graph, strategy="thread", verbose=True)
|
|
||||||
@@ -12,6 +12,11 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"Graph",
|
||||||
|
"GraphDefaults",
|
||||||
|
]
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from dataclasses import dataclass, field, replace
|
from dataclasses import dataclass, field, replace
|
||||||
from typing import Any, Callable, Iterable, Mapping, Sequence
|
from typing import Any, Callable, Iterable, Mapping, Sequence
|
||||||
@@ -73,6 +78,7 @@ class Graph:
|
|||||||
specs: dict[str, TaskSpec[Any]] = field(default_factory=dict)
|
specs: dict[str, TaskSpec[Any]] = field(default_factory=dict)
|
||||||
deps: dict[str, tuple[str, ...]] = field(default_factory=dict)
|
deps: dict[str, tuple[str, ...]] = field(default_factory=dict)
|
||||||
defaults: GraphDefaults = field(default_factory=GraphDefaults)
|
defaults: GraphDefaults = field(default_factory=GraphDefaults)
|
||||||
|
|
||||||
# 待解析的字符串引用列表(由 GraphComposer 消费);为空表示无引用。
|
# 待解析的字符串引用列表(由 GraphComposer 消费);为空表示无引用。
|
||||||
_pending_refs: list[str] = field(default_factory=list)
|
_pending_refs: list[str] = field(default_factory=list)
|
||||||
|
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
"""Tests for cli.envpy module."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
import pyflowx as px
|
|
||||||
from pyflowx.cli import envpy
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------- #
|
|
||||||
# set_pip_mirror
|
|
||||||
# ---------------------------------------------------------------------- #
|
|
||||||
class TestSetPipMirror:
|
|
||||||
"""Test set_pip_mirror function."""
|
|
||||||
|
|
||||||
def test_set_pip_mirror_tsinghua(self, tmp_path: Path) -> None:
|
|
||||||
"""Should set tsinghua mirror."""
|
|
||||||
with patch.object(Path, "home", return_value=tmp_path):
|
|
||||||
envpy.set_pip_mirror("tsinghua")
|
|
||||||
# Check pip config
|
|
||||||
pip_config = tmp_path / "pip" / "pip.ini"
|
|
||||||
if envpy.Constants.IS_WINDOWS:
|
|
||||||
assert pip_config.exists() or (tmp_path / "pip" / "pip.conf").exists()
|
|
||||||
|
|
||||||
def test_set_pip_mirror_aliyun(self, tmp_path: Path) -> None:
|
|
||||||
"""Should set aliyun mirror."""
|
|
||||||
with patch.object(Path, "home", return_value=tmp_path):
|
|
||||||
envpy.set_pip_mirror("aliyun")
|
|
||||||
# Check pip config
|
|
||||||
pip_dir = tmp_path / "pip"
|
|
||||||
assert pip_dir.exists()
|
|
||||||
|
|
||||||
def test_set_pip_mirror_with_token(self, tmp_path: Path) -> None:
|
|
||||||
"""Should set mirror with token."""
|
|
||||||
with patch.object(Path, "home", return_value=tmp_path):
|
|
||||||
envpy.set_pip_mirror("tsinghua", token="test_token")
|
|
||||||
# Check that token is set
|
|
||||||
|
|
||||||
def test_set_pip_mirror_creates_pip_dir(self, tmp_path: Path) -> None:
|
|
||||||
"""Should create pip directory if it doesn't exist."""
|
|
||||||
pip_dir = tmp_path / "pip"
|
|
||||||
with patch.object(Path, "home", return_value=tmp_path):
|
|
||||||
envpy.set_pip_mirror("tsinghua")
|
|
||||||
assert pip_dir.exists()
|
|
||||||
assert pip_dir.is_dir()
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------- #
|
|
||||||
# main function
|
|
||||||
# ---------------------------------------------------------------------- #
|
|
||||||
class TestMain:
|
|
||||||
"""Test main function."""
|
|
||||||
|
|
||||||
def test_main_mirror_tsinghua(self) -> None:
|
|
||||||
"""main() should handle mirror tsinghua command."""
|
|
||||||
with patch("sys.argv", ["envpy", "mirror", "tsinghua"]), patch.object(px, "run") as mock_run, patch.object(
|
|
||||||
envpy, "set_pip_mirror"
|
|
||||||
):
|
|
||||||
envpy.main()
|
|
||||||
assert mock_run.called
|
|
||||||
|
|
||||||
def test_main_mirror_aliyun(self) -> None:
|
|
||||||
"""main() should handle mirror aliyun command."""
|
|
||||||
with patch("sys.argv", ["envpy", "mirror", "aliyun"]), patch.object(px, "run") as mock_run, patch.object(
|
|
||||||
envpy, "set_pip_mirror"
|
|
||||||
):
|
|
||||||
envpy.main()
|
|
||||||
assert mock_run.called
|
|
||||||
|
|
||||||
def test_main_mirror_with_token(self) -> None:
|
|
||||||
"""main() should handle mirror with token."""
|
|
||||||
with patch("sys.argv", ["envpy", "mirror", "tsinghua", "--token", "test_token"]), patch.object(
|
|
||||||
px, "run"
|
|
||||||
) as mock_run, patch.object(envpy, "set_pip_mirror"):
|
|
||||||
envpy.main()
|
|
||||||
assert mock_run.called
|
|
||||||
|
|
||||||
def test_main_with_no_args_shows_help(self) -> None:
|
|
||||||
"""main() with no args should show help and return."""
|
|
||||||
with patch("sys.argv", ["envpy"]):
|
|
||||||
envpy.main()
|
|
||||||
# Should print help and return
|
|
||||||
|
|
||||||
def test_main_invalid_mirror_shows_error(self) -> None:
|
|
||||||
"""main() with invalid mirror should show error."""
|
|
||||||
with patch("sys.argv", ["envpy", "mirror", "invalid"]), pytest.raises(SystemExit) as exc_info:
|
|
||||||
envpy.main()
|
|
||||||
assert exc_info.value.code == 2
|
|
||||||
|
|
||||||
def test_main_creates_task_spec_with_correct_name(self) -> None:
|
|
||||||
"""main() should create TaskSpec with correct name."""
|
|
||||||
with patch("sys.argv", ["envpy", "mirror", "tsinghua"]), patch.object(px, "run") as mock_run, patch.object(
|
|
||||||
envpy, "set_pip_mirror"
|
|
||||||
):
|
|
||||||
envpy.main()
|
|
||||||
graph = mock_run.call_args[0][0]
|
|
||||||
task_names = list(graph.all_specs().keys())
|
|
||||||
assert "set_pip_mirror" in task_names
|
|
||||||
|
|
||||||
def test_main_uses_thread_strategy(self) -> None:
|
|
||||||
"""main() should use thread strategy."""
|
|
||||||
with patch("sys.argv", ["envpy", "mirror", "tsinghua"]), patch.object(px, "run") as mock_run, patch.object(
|
|
||||||
envpy, "set_pip_mirror"
|
|
||||||
):
|
|
||||||
envpy.main()
|
|
||||||
assert mock_run.call_args[1]["strategy"] == "thread"
|
|
||||||
@@ -1,210 +0,0 @@
|
|||||||
"""Tests for cli.envrs module."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
from unittest.mock import MagicMock, patch
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
import pyflowx as px
|
|
||||||
from pyflowx.cli import envrs
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------- #
|
|
||||||
# set_rust_mirror
|
|
||||||
# ---------------------------------------------------------------------- #
|
|
||||||
class TestSetRustMirror:
|
|
||||||
"""Test set_rust_mirror function."""
|
|
||||||
|
|
||||||
def test_set_rust_mirror_aliyun(self, tmp_path: Path) -> None:
|
|
||||||
"""Should set aliyun mirror."""
|
|
||||||
with patch.object(Path, "home", return_value=tmp_path):
|
|
||||||
envrs.set_rust_mirror("aliyun")
|
|
||||||
# Check environment variables
|
|
||||||
assert os.environ.get("RUSTUP_DIST_SERVER") == "https://mirrors.aliyun.com/rustup"
|
|
||||||
assert os.environ.get("RUSTUP_UPDATE_ROOT") == "https://mirrors.aliyun.com/rustup/rustup"
|
|
||||||
# Check cargo config
|
|
||||||
cargo_config = tmp_path / ".cargo" / "config.toml"
|
|
||||||
assert cargo_config.exists()
|
|
||||||
content = cargo_config.read_text()
|
|
||||||
assert "aliyun" in content
|
|
||||||
|
|
||||||
def test_set_rust_mirror_ustc(self, tmp_path: Path) -> None:
|
|
||||||
"""Should set ustc mirror."""
|
|
||||||
with patch.object(Path, "home", return_value=tmp_path):
|
|
||||||
envrs.set_rust_mirror("ustc")
|
|
||||||
assert os.environ.get("RUSTUP_DIST_SERVER") == "https://mirrors.ustc.edu.cn/rust-static"
|
|
||||||
assert os.environ.get("RUSTUP_UPDATE_ROOT") == "https://mirrors.ustc.edu.cn/rust-static/rustup"
|
|
||||||
|
|
||||||
def test_set_rust_mirror_tsinghua(self, tmp_path: Path) -> None:
|
|
||||||
"""Should set tsinghua mirror."""
|
|
||||||
with patch.object(Path, "home", return_value=tmp_path):
|
|
||||||
envrs.set_rust_mirror("tsinghua")
|
|
||||||
assert os.environ.get("RUSTUP_DIST_SERVER") == "https://mirrors.tuna.tsinghua.edu.cn/rustup"
|
|
||||||
assert os.environ.get("RUSTUP_UPDATE_ROOT") == "https://mirrors.tuna.tsinghua.edu.cn/rustup/rustup"
|
|
||||||
|
|
||||||
def test_set_rust_mirror_unknown_uses_default(self, tmp_path: Path) -> None:
|
|
||||||
"""Should use default mirror for unknown mirror name."""
|
|
||||||
with patch.object(Path, "home", return_value=tmp_path):
|
|
||||||
# pyrefly: ignore [bad-argument-type]
|
|
||||||
envrs.set_rust_mirror("unknown")
|
|
||||||
# Should use default mirror (tsinghua)
|
|
||||||
assert os.environ.get("RUSTUP_DIST_SERVER") == "https://mirrors.tuna.tsinghua.edu.cn/rustup"
|
|
||||||
|
|
||||||
def test_set_rust_mirror_creates_cargo_dir(self, tmp_path: Path) -> None:
|
|
||||||
"""Should create .cargo directory if it doesn't exist."""
|
|
||||||
cargo_dir = tmp_path / ".cargo"
|
|
||||||
with patch.object(Path, "home", return_value=tmp_path):
|
|
||||||
envrs.set_rust_mirror("aliyun")
|
|
||||||
assert cargo_dir.exists()
|
|
||||||
assert cargo_dir.is_dir()
|
|
||||||
|
|
||||||
def test_set_rust_mirror_prints_message(self, tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None:
|
|
||||||
"""Should print mirror name."""
|
|
||||||
with patch.object(Path, "home", return_value=tmp_path):
|
|
||||||
envrs.set_rust_mirror("aliyun")
|
|
||||||
captured = capsys.readouterr()
|
|
||||||
assert "已设置 Rust 镜像源: aliyun" in captured.out
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------- #
|
|
||||||
# install_rust
|
|
||||||
# ---------------------------------------------------------------------- #
|
|
||||||
class TestInstallRust:
|
|
||||||
"""Test install_rust function."""
|
|
||||||
|
|
||||||
def test_install_rust_stable(self) -> None:
|
|
||||||
"""Should install stable Rust."""
|
|
||||||
with patch("subprocess.run") as mock_run:
|
|
||||||
mock_run.return_value = MagicMock(returncode=0)
|
|
||||||
envrs.install_rust("stable")
|
|
||||||
mock_run.assert_called_once_with(["rustup", "toolchain", "install", "stable"], check=True)
|
|
||||||
|
|
||||||
def test_install_rust_nightly(self) -> None:
|
|
||||||
"""Should install nightly Rust."""
|
|
||||||
with patch("subprocess.run") as mock_run:
|
|
||||||
mock_run.return_value = MagicMock(returncode=0)
|
|
||||||
envrs.install_rust("nightly")
|
|
||||||
mock_run.assert_called_once_with(["rustup", "toolchain", "install", "nightly"], check=True)
|
|
||||||
|
|
||||||
def test_install_rust_beta(self) -> None:
|
|
||||||
"""Should install beta Rust."""
|
|
||||||
with patch("subprocess.run") as mock_run:
|
|
||||||
mock_run.return_value = MagicMock(returncode=0)
|
|
||||||
envrs.install_rust("beta")
|
|
||||||
mock_run.assert_called_once_with(["rustup", "toolchain", "install", "beta"], check=True)
|
|
||||||
|
|
||||||
def test_install_rust_file_not_found(self) -> None:
|
|
||||||
"""Should raise FileNotFoundError when rustup not found."""
|
|
||||||
with patch("subprocess.run", side_effect=FileNotFoundError), pytest.raises(FileNotFoundError):
|
|
||||||
envrs.install_rust("stable")
|
|
||||||
|
|
||||||
def test_install_rust_prints_message(self, capsys: pytest.CaptureFixture[str]) -> None:
|
|
||||||
"""Should print installation message."""
|
|
||||||
with patch("subprocess.run") as mock_run:
|
|
||||||
mock_run.return_value = MagicMock(returncode=0)
|
|
||||||
envrs.install_rust("stable")
|
|
||||||
captured = capsys.readouterr()
|
|
||||||
assert "已安装 Rust stable" in captured.out
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------- #
|
|
||||||
# main function
|
|
||||||
# ---------------------------------------------------------------------- #
|
|
||||||
class TestMain:
|
|
||||||
"""Test main function."""
|
|
||||||
|
|
||||||
def test_main_mirror_aliyun(self) -> None:
|
|
||||||
"""main() should handle mirror aliyun command."""
|
|
||||||
with patch("sys.argv", ["envrs", "mirror", "aliyun"]), patch.object(px, "run") as mock_run, patch.object(
|
|
||||||
envrs, "set_rust_mirror"
|
|
||||||
):
|
|
||||||
envrs.main()
|
|
||||||
assert mock_run.called
|
|
||||||
|
|
||||||
def test_main_mirror_ustc(self) -> None:
|
|
||||||
"""main() should handle mirror ustc command."""
|
|
||||||
with patch("sys.argv", ["envrs", "mirror", "ustc"]), patch.object(px, "run") as mock_run, patch.object(
|
|
||||||
envrs, "set_rust_mirror"
|
|
||||||
):
|
|
||||||
envrs.main()
|
|
||||||
assert mock_run.called
|
|
||||||
|
|
||||||
def test_main_mirror_tsinghua(self) -> None:
|
|
||||||
"""main() should handle mirror tsinghua command."""
|
|
||||||
with patch("sys.argv", ["envrs", "mirror", "tsinghua"]), patch.object(px, "run") as mock_run, patch.object(
|
|
||||||
envrs, "set_rust_mirror"
|
|
||||||
):
|
|
||||||
envrs.main()
|
|
||||||
assert mock_run.called
|
|
||||||
|
|
||||||
def test_main_mirror_default(self) -> None:
|
|
||||||
"""main() should use default mirror when not specified."""
|
|
||||||
with patch("sys.argv", ["envrs", "mirror"]), patch.object(px, "run") as mock_run, patch.object(
|
|
||||||
envrs, "set_rust_mirror"
|
|
||||||
):
|
|
||||||
envrs.main()
|
|
||||||
assert mock_run.called
|
|
||||||
|
|
||||||
def test_main_install_stable(self) -> None:
|
|
||||||
"""main() should handle install stable command."""
|
|
||||||
with patch("sys.argv", ["envrs", "install", "stable"]), patch.object(px, "run") as mock_run:
|
|
||||||
envrs.main()
|
|
||||||
assert mock_run.called
|
|
||||||
|
|
||||||
def test_main_install_nightly(self) -> None:
|
|
||||||
"""main() should handle install nightly command."""
|
|
||||||
with patch("sys.argv", ["envrs", "install", "nightly"]), patch.object(px, "run") as mock_run:
|
|
||||||
envrs.main()
|
|
||||||
assert mock_run.called
|
|
||||||
|
|
||||||
def test_main_install_beta(self) -> None:
|
|
||||||
"""main() should handle install beta command."""
|
|
||||||
with patch("sys.argv", ["envrs", "install", "beta"]), patch.object(px, "run") as mock_run:
|
|
||||||
envrs.main()
|
|
||||||
assert mock_run.called
|
|
||||||
|
|
||||||
def test_main_install_default(self) -> None:
|
|
||||||
"""main() should use default version when not specified."""
|
|
||||||
with patch("sys.argv", ["envrs", "install"]), patch.object(px, "run") as mock_run:
|
|
||||||
envrs.main()
|
|
||||||
assert mock_run.called
|
|
||||||
|
|
||||||
def test_main_with_no_args_shows_help(self) -> None:
|
|
||||||
"""main() with no args should show help and return."""
|
|
||||||
with patch("sys.argv", ["envrs"]):
|
|
||||||
envrs.main()
|
|
||||||
# Should print help and return
|
|
||||||
|
|
||||||
def test_main_invalid_version_shows_error(self) -> None:
|
|
||||||
"""main() with invalid version should show error."""
|
|
||||||
with patch("sys.argv", ["envrs", "install", "invalid"]), pytest.raises(SystemExit) as exc_info:
|
|
||||||
envrs.main()
|
|
||||||
assert exc_info.value.code == 2
|
|
||||||
|
|
||||||
def test_main_invalid_mirror_shows_error(self) -> None:
|
|
||||||
"""main() with invalid mirror should show error."""
|
|
||||||
with patch("sys.argv", ["envrs", "mirror", "invalid"]), pytest.raises(SystemExit) as exc_info:
|
|
||||||
envrs.main()
|
|
||||||
assert exc_info.value.code == 2
|
|
||||||
|
|
||||||
def test_main_creates_task_spec_with_verbose(self) -> None:
|
|
||||||
"""main() should create TaskSpec with verbose=True."""
|
|
||||||
with patch("sys.argv", ["envrs", "mirror", "aliyun"]), patch.object(px, "run") as mock_run, patch.object(
|
|
||||||
envrs, "set_rust_mirror"
|
|
||||||
):
|
|
||||||
envrs.main()
|
|
||||||
graph = mock_run.call_args[0][0]
|
|
||||||
specs = graph.all_specs()
|
|
||||||
for spec in specs.values():
|
|
||||||
assert spec.verbose is True
|
|
||||||
|
|
||||||
def test_main_uses_thread_strategy(self) -> None:
|
|
||||||
"""main() should use thread strategy."""
|
|
||||||
with patch("sys.argv", ["envrs", "mirror", "aliyun"]), patch.object(px, "run") as mock_run, patch.object(
|
|
||||||
envrs, "set_rust_mirror"
|
|
||||||
):
|
|
||||||
envrs.main()
|
|
||||||
assert mock_run.call_args[1]["strategy"] == "thread"
|
|
||||||
Reference in New Issue
Block a user