py-wlcli 0.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- py_wlcli-0.8.0.dist-info/METADATA +139 -0
- py_wlcli-0.8.0.dist-info/RECORD +320 -0
- py_wlcli-0.8.0.dist-info/WHEEL +4 -0
- py_wlcli-0.8.0.dist-info/entry_points.txt +2 -0
- py_wlcli-0.8.0.dist-info/licenses/LICENSE +21 -0
- wl_cli/__init__.py +27 -0
- wl_cli/__main__.py +130 -0
- wl_cli/capabilities.py +79 -0
- wl_cli/cc/__init__.py +5 -0
- wl_cli/cc/checks.py +1019 -0
- wl_cli/cc/cli.py +370 -0
- wl_cli/cc/mutate.py +1350 -0
- wl_cli/cli_click.py +156 -0
- wl_cli/commands/__init__.py +84 -0
- wl_cli/commands/base.py +80 -0
- wl_cli/commands/buildcommands/__init__.py +1 -0
- wl_cli/commands/buildcommands/build.py +71 -0
- wl_cli/commands/buildcommands/build_utils.py +47 -0
- wl_cli/commands/buildcommands/builders/__init__.py +18 -0
- wl_cli/commands/buildcommands/builders/cleanup.py +35 -0
- wl_cli/commands/buildcommands/builders/coordinator.py +122 -0
- wl_cli/commands/buildcommands/builders/environment.py +81 -0
- wl_cli/commands/buildcommands/builders/rust_builder.py +39 -0
- wl_cli/commands/buildcommands/builders/stubs.py +58 -0
- wl_cli/commands/buildcommands/dist.py +326 -0
- wl_cli/commands/buildcommands/test.py +89 -0
- wl_cli/commands/clean/__init__.py +1 -0
- wl_cli/commands/clean/build.py +151 -0
- wl_cli/commands/clean/clean.py +294 -0
- wl_cli/commands/clean/clean_utils.py +77 -0
- wl_cli/commands/clean/core.py +236 -0
- wl_cli/commands/clean/dead_code/__init__.py +0 -0
- wl_cli/commands/clean/dead_code/dead_code_command.py +70 -0
- wl_cli/commands/clean/dead_code/detector.py +161 -0
- wl_cli/commands/clean/dead_code/fixer.py +176 -0
- wl_cli/commands/clean/dead_code/reporter.py +295 -0
- wl_cli/commands/clean/dead_code/utils.py +0 -0
- wl_cli/commands/clean/lfs.py +215 -0
- wl_cli/commands/clean/python.py +81 -0
- wl_cli/commands/clean/rust.py +91 -0
- wl_cli/commands/clean/utils/__init__.py +34 -0
- wl_cli/commands/clean/venv.py +91 -0
- wl_cli/commands/cleanup.py +73 -0
- wl_cli/commands/click_base.py +79 -0
- wl_cli/commands/config/__init__.py +1 -0
- wl_cli/commands/config/config.py +291 -0
- wl_cli/commands/debug/__init__.py +6 -0
- wl_cli/commands/debug/debug.py +309 -0
- wl_cli/commands/debug/debug_cache.py +86 -0
- wl_cli/commands/debug/debug_subagent.py +156 -0
- wl_cli/commands/debug/output_formatter.py +165 -0
- wl_cli/commands/debug/source_hasher.py +92 -0
- wl_cli/commands/format/__init__.py +13 -0
- wl_cli/commands/format/format_cache_adapter.py +125 -0
- wl_cli/commands/format/format_command.py +197 -0
- wl_cli/commands/format/format_coordinator.py +191 -0
- wl_cli/commands/format/format_executor.py +470 -0
- wl_cli/commands/format/format_outcome.py +44 -0
- wl_cli/commands/format/format_path_handler.py +1016 -0
- wl_cli/commands/format/format_progress.py +62 -0
- wl_cli/commands/format/format_reporter.py +31 -0
- wl_cli/commands/format/python_formatter.py +506 -0
- wl_cli/commands/format/rust_formatter.py +106 -0
- wl_cli/commands/gate/__init__.py +1 -0
- wl_cli/commands/gate/gate_command.py +862 -0
- wl_cli/commands/gate/preflight.py +445 -0
- wl_cli/commands/git/__init__.py +1 -0
- wl_cli/commands/git/config.py +169 -0
- wl_cli/commands/git/git_facts.py +60 -0
- wl_cli/commands/git/git_flow.py +86 -0
- wl_cli/commands/git/hook.py +112 -0
- wl_cli/commands/git/policy.py +216 -0
- wl_cli/commands/git/push_update.py +82 -0
- wl_cli/commands/initenv/__init__.py +1 -0
- wl_cli/commands/initenv/common/__init__.py +23 -0
- wl_cli/commands/initenv/common/check_git_bash.py +78 -0
- wl_cli/commands/initenv/common/error_handler.py +68 -0
- wl_cli/commands/initenv/common/exceptions.py +52 -0
- wl_cli/commands/initenv/components/__init__.py +4 -0
- wl_cli/commands/initenv/components/cargo_sync.py +240 -0
- wl_cli/commands/initenv/components/config_manager.py +106 -0
- wl_cli/commands/initenv/components/directory_utils.py +71 -0
- wl_cli/commands/initenv/components/git/__init__.py +9 -0
- wl_cli/commands/initenv/components/git/branch_creator.py +143 -0
- wl_cli/commands/initenv/components/git/git_flow_interface.py +24 -0
- wl_cli/commands/initenv/components/git/git_flow_manager.py +159 -0
- wl_cli/commands/initenv/components/git/git_lfs_manager.py +272 -0
- wl_cli/commands/initenv/components/git/git_utility.py +254 -0
- wl_cli/commands/initenv/components/git/gitignore_manager.py +53 -0
- wl_cli/commands/initenv/components/git_initializer.py +212 -0
- wl_cli/commands/initenv/components/i18n_manager.py +104 -0
- wl_cli/commands/initenv/components/log_manager.py +241 -0
- wl_cli/commands/initenv/components/project_structure/__init__.py +23 -0
- wl_cli/commands/initenv/components/project_structure/config_generator.py +360 -0
- wl_cli/commands/initenv/components/project_structure/directory_creator.py +184 -0
- wl_cli/commands/initenv/components/project_structure/hook_source.py +110 -0
- wl_cli/commands/initenv/components/project_structure/hooks_manager.py +995 -0
- wl_cli/commands/initenv/components/project_structure/readme_handler.py +56 -0
- wl_cli/commands/initenv/components/project_structure/rust_files_handler.py +30 -0
- wl_cli/commands/initenv/components/project_structure/setup_handler.py +103 -0
- wl_cli/commands/initenv/components/project_structure/sop_generator.py +73 -0
- wl_cli/commands/initenv/components/project_structure/template_copier.py +104 -0
- wl_cli/commands/initenv/components/project_structure/template_manager.py +195 -0
- wl_cli/commands/initenv/components/pyproject_generator.py +194 -0
- wl_cli/commands/initenv/components/rust_initializer.py +145 -0
- wl_cli/commands/initenv/components/symlink_cleaner.py +99 -0
- wl_cli/commands/initenv/components/symlink_manager.py +52 -0
- wl_cli/commands/initenv/components/venv_manager.py +118 -0
- wl_cli/commands/initenv/initenv.py +169 -0
- wl_cli/commands/initenv/services/__init__.py +11 -0
- wl_cli/commands/initenv/services/environment_initializer.py +288 -0
- wl_cli/commands/initenv/services/git_lfs_initializer.py +26 -0
- wl_cli/commands/initenv/services/project_structure_initializer.py +52 -0
- wl_cli/commands/lfs/README.md +221 -0
- wl_cli/commands/lfs/__init__.py +5 -0
- wl_cli/commands/lfs/cli.py +106 -0
- wl_cli/commands/lfs/config.py +164 -0
- wl_cli/commands/lfs/hook.py +61 -0
- wl_cli/commands/lfs/initializer.py +531 -0
- wl_cli/commands/lfs/manager.py +95 -0
- wl_cli/commands/lfs/policy.py +165 -0
- wl_cli/commands/lfs/watcher.py +294 -0
- wl_cli/commands/lint/__init__.py +1 -0
- wl_cli/commands/lint/lint_command.py +297 -0
- wl_cli/commands/lint/lint_executor.py +409 -0
- wl_cli/commands/lint/lint_reporter.py +579 -0
- wl_cli/commands/lint/lint_run.py +258 -0
- wl_cli/commands/lint/mypy_config.py +90 -0
- wl_cli/commands/registry.py +103 -0
- wl_cli/commands/self/__init__.py +7 -0
- wl_cli/commands/self/common/__init__.py +8 -0
- wl_cli/commands/self/common/capabilities.py +30 -0
- wl_cli/commands/self/common/config.py +27 -0
- wl_cli/commands/self/common/version_cache.py +85 -0
- wl_cli/commands/self/handlers/__init__.py +19 -0
- wl_cli/commands/self/handlers/base_handler.py +28 -0
- wl_cli/commands/self/handlers/install_handler.py +85 -0
- wl_cli/commands/self/handlers/status_handler.py +66 -0
- wl_cli/commands/self/handlers/uninstall_handler.py +58 -0
- wl_cli/commands/self/handlers/update_handler.py +102 -0
- wl_cli/commands/self/handlers/version_handler.py +16 -0
- wl_cli/commands/self/self_command.py +237 -0
- wl_cli/commands/self/services/__init__.py +15 -0
- wl_cli/commands/self/services/installation_manager.py +187 -0
- wl_cli/commands/self/services/local_installer.py +165 -0
- wl_cli/commands/self/services/pypi_updater.py +278 -0
- wl_cli/commands/self/services/windows_update.py +174 -0
- wl_cli/commands/testcommands/__init__.py +8 -0
- wl_cli/commands/testcommands/base_dependency_analyzer.py +198 -0
- wl_cli/commands/testcommands/batch_processor.py +278 -0
- wl_cli/commands/testcommands/cache_manager.py +1156 -0
- wl_cli/commands/testcommands/coverage_analyzer.py +955 -0
- wl_cli/commands/testcommands/coverage_cache_manager.py +183 -0
- wl_cli/commands/testcommands/progress_manager.py +79 -0
- wl_cli/commands/testcommands/python_env_detector.py +207 -0
- wl_cli/commands/testcommands/report_generator.py +101 -0
- wl_cli/commands/testcommands/single_test_executor.py +250 -0
- wl_cli/commands/testcommands/speed_checker.py +118 -0
- wl_cli/commands/testcommands/test_command_builder.py +165 -0
- wl_cli/commands/testcommands/test_commands.py +786 -0
- wl_cli/commands/testcommands/test_reporter.py +181 -0
- wl_cli/commands/testcommands/test_result_handler.py +96 -0
- wl_cli/commands/testcommands/test_runner.py +1343 -0
- wl_cli/commands/testcommands/test_utils.py +197 -0
- wl_cli/commands/testcommands/utils/__init__.py +17 -0
- wl_cli/commands/testcommands/utils/coverage_parser.py +98 -0
- wl_cli/commands/testcommands/utils/env_info.py +36 -0
- wl_cli/commands/testcommands/utils/junit_parser.py +75 -0
- wl_cli/commands/testcommands/utils/markdown_utils.py +20 -0
- wl_cli/commands/testcommands/utils/project_root.py +61 -0
- wl_cli/commands/update/__init__.py +9 -0
- wl_cli/commands/update/services/__init__.py +21 -0
- wl_cli/commands/update/services/backup_manager.py +207 -0
- wl_cli/commands/update/services/component_manager.py +83 -0
- wl_cli/commands/update/services/conflict_resolver.py +189 -0
- wl_cli/commands/update/services/vendors_updater.py +85 -0
- wl_cli/commands/update/services/version_tracker.py +127 -0
- wl_cli/commands/update/update_command.py +270 -0
- wl_cli/commands/update/utils/__init__.py +3 -0
- wl_cli/commands/version/__init__.py +9 -0
- wl_cli/commands/version/version_command.py +155 -0
- wl_cli/commands/workflow.py +477 -0
- wl_cli/env/__init__.py +5 -0
- wl_cli/env/cli.py +167 -0
- wl_cli/env_status.py +157 -0
- wl_cli/envelope.py +91 -0
- wl_cli/exceptions.py +85 -0
- wl_cli/lib/__init__.py +3 -0
- wl_cli/lib/rust_utils.py +35 -0
- wl_cli/mcp/__init__.py +6 -0
- wl_cli/mcp/capabilities.py +19 -0
- wl_cli/mcp/cli.py +22 -0
- wl_cli/mcp/gitlab.py +553 -0
- wl_cli/mcp/k8s.py +344 -0
- wl_cli/mcp/protocol.py +159 -0
- wl_cli/platform.py +139 -0
- wl_cli/quality/__init__.py +18 -0
- wl_cli/utils/__init__.py +7 -0
- wl_cli/utils/cache.py +491 -0
- wl_cli/utils/cache_ledger.py +355 -0
- wl_cli/utils/cache_store.py +546 -0
- wl_cli/utils/config/__init__.py +27 -0
- wl_cli/utils/config/config_loader.py +163 -0
- wl_cli/utils/config/core.py +862 -0
- wl_cli/utils/config/defaults.py +142 -0
- wl_cli/utils/config/env_exporter.py +86 -0
- wl_cli/utils/config/env_parser.py +108 -0
- wl_cli/utils/config/validation.py +280 -0
- wl_cli/utils/error_handler.py +165 -0
- wl_cli/utils/file_copier.py +56 -0
- wl_cli/utils/file_operations.py +506 -0
- wl_cli/utils/language_utils.py +122 -0
- wl_cli/utils/lazy_import.py +240 -0
- wl_cli/utils/log_levels.py +35 -0
- wl_cli/utils/log_rotators.py +74 -0
- wl_cli/utils/logging.py +458 -0
- wl_cli/utils/output_context.py +142 -0
- wl_cli/utils/performance/__init__.py +1 -0
- wl_cli/utils/performance/decorators.py +111 -0
- wl_cli/utils/performance/exporter.py +38 -0
- wl_cli/utils/performance/models.py +16 -0
- wl_cli/utils/performance/monitor.py +161 -0
- wl_cli/utils/performance_monitor.py +32 -0
- wl_cli/utils/platform_adapter.py +131 -0
- wl_cli/utils/pre_commit.py +266 -0
- wl_cli/utils/pre_commit_config.py +72 -0
- wl_cli/utils/root.py +521 -0
- wl_cli/utils/root_fallback.py +256 -0
- wl_cli/utils/run_evidence.py +236 -0
- wl_cli/utils/structured_logger.py +18 -0
- wl_cli/utils/structured_logging/__init__.py +19 -0
- wl_cli/utils/structured_logging/config.py +337 -0
- wl_cli/utils/structured_logging/core.py +177 -0
- wl_cli/utils/structured_logging/env.py +56 -0
- wl_cli/utils/structured_logging/filtering.py +75 -0
- wl_cli/utils/structured_logging/filters.py +55 -0
- wl_cli/utils/structured_logging/formatters.py +251 -0
- wl_cli/utils/structured_logging/handlers.py +308 -0
- wl_cli/utils/structured_logging/logger.py +354 -0
- wl_cli/utils/subprocess.py +71 -0
- wl_cli/utils/subprocess_utils.py +260 -0
- wl_cli/utils/test_utils/__init__.py +5 -0
- wl_cli/utils/test_utils/test_filter.py +36 -0
- wl_cli/utils/uv_run.py +79 -0
- wl_cli/utils/uv_tool.py +117 -0
- wl_cli/utils/uv_tree.py +71 -0
- wl_cli/utils/uv_workspace.py +174 -0
- wl_cli/utils/version/__init__.py +15 -0
- wl_cli/utils/version/pypi_version_checker.py +95 -0
- wl_cli/utils/version/version_comparator.py +133 -0
- wl_cli/utils/version/version_detectors.py +312 -0
- wl_cli/utils/version/version_service.py +131 -0
- wl_cli/utils/version/version_updater.py +198 -0
- wl_cli/utils/wl_dir_updater.py +1440 -0
- wl_cli/utils/workspace_detector/__init__.py +28 -0
- wl_cli/utils/workspace_detector/core.py +161 -0
- wl_cli/utils/workspace_detector/detection_strategies.py +107 -0
- wl_cli/utils/workspace_detector/exceptions.py +5 -0
- wl_cli/utils/workspace_detector/types.py +17 -0
- wl_cli/utils/workspace_detector/venv_resolver.py +84 -0
- wl_cli/vendors/config/.codespellrc +2 -0
- wl_cli/vendors/config/dependencies.toml +10 -0
- wl_cli/vendors/config/mypy.ini +62 -0
- wl_cli/vendors/config/pyproject_defaults.toml +28 -0
- wl_cli/vendors/config/template_config.toml +24 -0
- wl_cli/vendors/config/tool_configs.toml +76 -0
- wl_cli/vendors/git/.gitignore +122 -0
- wl_cli/vendors/git/.pre-commit-config.yaml +37 -0
- wl_cli/vendors/hooks/applypatch-msg.sample +15 -0
- wl_cli/vendors/hooks/commit-msg.sample +24 -0
- wl_cli/vendors/hooks/fsmonitor-watchman.sample +174 -0
- wl_cli/vendors/hooks/post-update.sample +8 -0
- wl_cli/vendors/hooks/pre-applypatch.sample +14 -0
- wl_cli/vendors/hooks/pre-commit +45 -0
- wl_cli/vendors/hooks/pre-commit.legacy +31 -0
- wl_cli/vendors/hooks/pre-commit.sample +49 -0
- wl_cli/vendors/hooks/pre-merge-commit.sample +13 -0
- wl_cli/vendors/hooks/pre-push.sample +60 -0
- wl_cli/vendors/hooks/pre-rebase.sample +169 -0
- wl_cli/vendors/hooks/pre-receive.sample +24 -0
- wl_cli/vendors/hooks/prepare-commit-msg.sample +42 -0
- wl_cli/vendors/hooks/push-to-checkout.sample +78 -0
- wl_cli/vendors/hooks/sendemail-validate.sample +77 -0
- wl_cli/vendors/hooks/unix/applypatch-msg.sample +15 -0
- wl_cli/vendors/hooks/unix/check_lockfile.py +58 -0
- wl_cli/vendors/hooks/unix/check_lockfile.sh +36 -0
- wl_cli/vendors/hooks/unix/commit-msg.sample +24 -0
- wl_cli/vendors/hooks/unix/fsmonitor-watchman.sample +174 -0
- wl_cli/vendors/hooks/unix/post-update.sample +8 -0
- wl_cli/vendors/hooks/unix/pre-applypatch.sample +14 -0
- wl_cli/vendors/hooks/unix/pre-commit +190 -0
- wl_cli/vendors/hooks/unix/pre-commit.legacy +31 -0
- wl_cli/vendors/hooks/unix/pre-commit.py +81 -0
- wl_cli/vendors/hooks/unix/pre-commit.sample +49 -0
- wl_cli/vendors/hooks/unix/pre-merge-commit.sample +13 -0
- wl_cli/vendors/hooks/unix/pre-push +417 -0
- wl_cli/vendors/hooks/unix/pre-push.sample +53 -0
- wl_cli/vendors/hooks/unix/pre-rebase.sample +169 -0
- wl_cli/vendors/hooks/unix/pre-receive.sample +24 -0
- wl_cli/vendors/hooks/unix/prepare-commit-msg.sample +42 -0
- wl_cli/vendors/hooks/update.sample +128 -0
- wl_cli/vendors/hooks/win/work.bat +63 -0
- wl_cli/vendors/project/.gitignore +90 -0
- wl_cli/vendors/project/.python-version +1 -0
- wl_cli/vendors/project/LICENSE +21 -0
- wl_cli/vendors/project/README.md +0 -0
- wl_cli/vendors/project/pyproject.toml +27 -0
- wl_cli/vendors/project/pyproject_no_workspace.toml +16 -0
- wl_cli/vendors/project/pyproject_workspace.toml +14 -0
- wl_cli/vendors/readme/README.md +120 -0
- wl_cli/vendors/rust/Cargo.toml +14 -0
- wl_cli/vendors/rust/lib/__init__.py +3 -0
- wl_cli/vendors/rust/lib/rust_utils.py +31 -0
- wl_cli/vendors/rust/src/lib.rs +13 -0
- wl_cli/vendors/rust/src/main.rs +3 -0
- wl_cli/vendors/rust/tests/test_rust_fallback.py +40 -0
- wl_cli/vendors/sop_doc/git-dev-sop.md +220 -0
- wl_cli/vendors/sop_doc/industrial-standard-sop.md +145 -0
- wl_cli/vendors/sop_doc/python-sop.md +342 -0
- wl_cli/vendors/sop_doc/rust-readme.md +219 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""PyProject.toml generator utility."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import toml
|
|
8
|
+
|
|
9
|
+
from ....utils.logging import log_debug
|
|
10
|
+
from .config_manager import ConfigManager
|
|
11
|
+
from .project_structure.directory_creator import normalize_project_name
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def clean_cargo_name(name: str) -> str:
|
|
15
|
+
"""Cleans a string to be a valid Cargo package name.
|
|
16
|
+
- Replaces invalid characters (anything not letter, number, _, -) with underscore.
|
|
17
|
+
- Ensures it doesn't start/end with invalid chars like _ or -.
|
|
18
|
+
"""
|
|
19
|
+
# Replace sequences of invalid characters (anything not alphanumeric or _) with a single underscore
|
|
20
|
+
# Also replace hyphens with underscores as Cargo prefers underscores, though hyphens are technically allowed.
|
|
21
|
+
cleaned = re.sub(r"[^a-zA-Z0-9_]+", "_", name.replace("-", "_"))
|
|
22
|
+
# Remove leading/trailing underscores
|
|
23
|
+
cleaned = cleaned.strip("_")
|
|
24
|
+
# Handle potential empty string after cleaning
|
|
25
|
+
if not cleaned:
|
|
26
|
+
return "rust_package" # Default fallback name
|
|
27
|
+
# Ensure it doesn't start with a digit (invalid for Rust identifiers)
|
|
28
|
+
if cleaned[0].isdigit():
|
|
29
|
+
cleaned = "_" + cleaned
|
|
30
|
+
return cleaned
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def dump_toml(data: dict[str, Any], file_path: str) -> None:
|
|
34
|
+
"""将数据以 TOML 格式写入文件"""
|
|
35
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
36
|
+
toml.dump(data, f)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class PyProjectGenerator:
|
|
40
|
+
"""pyproject.toml 生成器类"""
|
|
41
|
+
|
|
42
|
+
def __init__(self, project_name: str) -> None:
|
|
43
|
+
self.cleaned_python_name = normalize_project_name(project_name)
|
|
44
|
+
self.cleaned_rust_name = clean_cargo_name(f"{project_name}_native")
|
|
45
|
+
self.config: dict[str, Any] = {}
|
|
46
|
+
self.config_manager = ConfigManager()
|
|
47
|
+
|
|
48
|
+
def _load_tool_configs(self) -> dict[str, Any]:
|
|
49
|
+
"""加载工具配置"""
|
|
50
|
+
result = self.config_manager.load_vendor_config("config/tool_configs.toml")
|
|
51
|
+
return result or {}
|
|
52
|
+
|
|
53
|
+
def _load_dependencies(self) -> list[str]:
|
|
54
|
+
"""加载依赖列表"""
|
|
55
|
+
dependencies_config = self.config_manager.load_vendor_config(
|
|
56
|
+
"config/dependencies.toml"
|
|
57
|
+
)
|
|
58
|
+
return dependencies_config.get("dependencies", {}).get("default", []) or []
|
|
59
|
+
|
|
60
|
+
def set_project_info(
|
|
61
|
+
self,
|
|
62
|
+
description: str | None = None,
|
|
63
|
+
authors: list[dict[str, str]] | None = None,
|
|
64
|
+
python_version: str = ">=3.12",
|
|
65
|
+
license_name: str = "MIT",
|
|
66
|
+
dependencies: list[str] | None = None,
|
|
67
|
+
) -> None:
|
|
68
|
+
"""设置项目基本信息"""
|
|
69
|
+
# Load dependencies from config if not provided
|
|
70
|
+
if not dependencies:
|
|
71
|
+
dependencies = self._load_dependencies()
|
|
72
|
+
|
|
73
|
+
# Use cleaned python name for URLs and description default
|
|
74
|
+
project_urls = {
|
|
75
|
+
"Homepage": f"https://github.com/example/{self.cleaned_python_name}",
|
|
76
|
+
"Issues": f"https://github.com/example/{self.cleaned_python_name}/issues",
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
self.config["project"] = {
|
|
80
|
+
"name": self.cleaned_python_name,
|
|
81
|
+
"dynamic": ["version"],
|
|
82
|
+
"description": description or f"{self.cleaned_python_name} - Python项目",
|
|
83
|
+
"requires-python": python_version,
|
|
84
|
+
"license": license_name,
|
|
85
|
+
"readme": "README.md",
|
|
86
|
+
"dependencies": dependencies,
|
|
87
|
+
"authors": authors or [{"name": "Admin", "email": "admin@example.com"}],
|
|
88
|
+
"urls": project_urls,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
def set_build_system(self, is_rust: bool = False) -> None:
|
|
92
|
+
"""设置构建系统配置"""
|
|
93
|
+
if not is_rust:
|
|
94
|
+
log_debug("Non-Rust build system not fully configured in generator.")
|
|
95
|
+
# Set a basic setuptools build system or similar if required
|
|
96
|
+
self.config["build-system"] = {
|
|
97
|
+
"requires": ["setuptools>=61.0"],
|
|
98
|
+
"build-backend": "setuptools.build_meta",
|
|
99
|
+
}
|
|
100
|
+
return # Exit early if not using Rust/Maturin
|
|
101
|
+
|
|
102
|
+
# --- Maturin Configuration ---
|
|
103
|
+
self.config["build-system"] = {
|
|
104
|
+
"requires": ["maturin>=1.0,<2.0"],
|
|
105
|
+
"build-backend": "maturin",
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# Maturin 特定配置
|
|
109
|
+
maturin_config = {
|
|
110
|
+
"manifest-path": "rust/Cargo.toml",
|
|
111
|
+
# Use CLEANED Python and Rust names for module-name
|
|
112
|
+
"module-name": f"{self.cleaned_python_name}.lib.{self.cleaned_rust_name}",
|
|
113
|
+
"python-source": "src",
|
|
114
|
+
# Use cleaned python name for include path
|
|
115
|
+
"include": [f"src/{self.cleaned_python_name}/**/*"],
|
|
116
|
+
"bindings": "pyo3",
|
|
117
|
+
"features": ["pyo3/extension-module"],
|
|
118
|
+
"sdist-include": ["src/**/*"],
|
|
119
|
+
# Use cleaned python name for python-packages
|
|
120
|
+
"python-packages": [self.cleaned_python_name],
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
self.config["tool"] = self.config.get("tool", {})
|
|
124
|
+
self.config["tool"]["maturin"] = maturin_config
|
|
125
|
+
|
|
126
|
+
def set_development_tools(self) -> None:
|
|
127
|
+
"""设置开发工具配置"""
|
|
128
|
+
tool_configs = self._load_tool_configs()
|
|
129
|
+
|
|
130
|
+
log_debug(f"Loaded tool_configs keys: {list(tool_configs.keys())}")
|
|
131
|
+
if "tool" in tool_configs:
|
|
132
|
+
log_debug(f"tool_configs['tool'] keys: {list(tool_configs['tool'].keys())}")
|
|
133
|
+
|
|
134
|
+
self.config["tool"] = self.config.get("tool", {})
|
|
135
|
+
self.config["tool"].update(tool_configs.get("tool", {}))
|
|
136
|
+
|
|
137
|
+
def set_entry_points(self, entry_points: dict[str, Any]) -> None:
|
|
138
|
+
"""设置入口点"""
|
|
139
|
+
if "project" not in self.config:
|
|
140
|
+
self.config["project"] = {}
|
|
141
|
+
|
|
142
|
+
if "scripts" in entry_points:
|
|
143
|
+
self.config["project"]["scripts"] = entry_points["scripts"]
|
|
144
|
+
if "gui_scripts" in entry_points:
|
|
145
|
+
self.config["project"]["gui-scripts"] = entry_points["gui_scripts"]
|
|
146
|
+
|
|
147
|
+
def generate_package_pyproject(
|
|
148
|
+
self, output_path: str, is_rust: bool = False
|
|
149
|
+
) -> None:
|
|
150
|
+
"""Generate pyproject.toml for a workspace sub-package (e.g. packages/{name}/).
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
output_path: Path to write the pyproject.toml file.
|
|
154
|
+
is_rust: Whether to include Rust/maturin build configuration.
|
|
155
|
+
"""
|
|
156
|
+
pkg_config: dict[str, Any] = {
|
|
157
|
+
"project": {
|
|
158
|
+
"name": self.cleaned_python_name,
|
|
159
|
+
"version": "0.1.0",
|
|
160
|
+
"description": f"{self.cleaned_python_name} - Python项目",
|
|
161
|
+
"requires-python": ">=3.12",
|
|
162
|
+
"readme": "README.md",
|
|
163
|
+
"dependencies": self._load_dependencies(),
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if is_rust:
|
|
168
|
+
pkg_config["build-system"] = {
|
|
169
|
+
"requires": ["maturin>=1.0,<2.0"],
|
|
170
|
+
"build-backend": "maturin",
|
|
171
|
+
}
|
|
172
|
+
pkg_config["tool"] = {
|
|
173
|
+
"maturin": {
|
|
174
|
+
"manifest-path": "rust/Cargo.toml",
|
|
175
|
+
"module-name": f"{self.cleaned_python_name}.lib.{self.cleaned_rust_name}",
|
|
176
|
+
"python-source": "src",
|
|
177
|
+
"bindings": "pyo3",
|
|
178
|
+
"features": ["pyo3/extension-module"],
|
|
179
|
+
},
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
dump_toml(pkg_config, output_path)
|
|
183
|
+
|
|
184
|
+
def generate(self, output_path: str = "pyproject.toml") -> None:
|
|
185
|
+
"""生成 pyproject.toml 文件"""
|
|
186
|
+
is_rust = Path("rust").exists()
|
|
187
|
+
|
|
188
|
+
if "project" not in self.config:
|
|
189
|
+
self.set_project_info()
|
|
190
|
+
self.set_development_tools()
|
|
191
|
+
if "build-system" not in self.config:
|
|
192
|
+
self.set_build_system(is_rust=is_rust)
|
|
193
|
+
|
|
194
|
+
dump_toml(self.config, output_path)
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""Custom exceptions for initenv commands."""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from ....utils.logging import log_debug, log_debug_pair, log_info_pair
|
|
7
|
+
from ..common.exceptions import InitEnvError
|
|
8
|
+
from .log_manager import performance_monitor
|
|
9
|
+
from .pyproject_generator import clean_cargo_name # Import the cleaning function
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RustInitializer:
|
|
13
|
+
"""Rust environment initializer."""
|
|
14
|
+
|
|
15
|
+
def __init__(self) -> None:
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
@performance_monitor
|
|
19
|
+
def initialize(self) -> None:
|
|
20
|
+
"""Initialize Rust environment."""
|
|
21
|
+
rust_dir = Path("rust")
|
|
22
|
+
|
|
23
|
+
# Create rust directory if it doesn't exist
|
|
24
|
+
if not rust_dir.exists():
|
|
25
|
+
log_debug_pair("Creating rust directory...", "创建rust目录...")
|
|
26
|
+
rust_dir.mkdir(exist_ok=True)
|
|
27
|
+
|
|
28
|
+
# Check if Cargo.toml exists
|
|
29
|
+
cargo_toml_path = rust_dir / "Cargo.toml"
|
|
30
|
+
if cargo_toml_path.exists():
|
|
31
|
+
log_debug_pair(
|
|
32
|
+
"Cargo.toml already exists, skipping creation",
|
|
33
|
+
"Cargo.toml 已存在,跳过创建",
|
|
34
|
+
)
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
# Use project template files instead of cargo init
|
|
38
|
+
log_debug_pair(
|
|
39
|
+
"Initializing Rust project with project template...",
|
|
40
|
+
"使用项目模板初始化Rust项目...",
|
|
41
|
+
)
|
|
42
|
+
try:
|
|
43
|
+
# Get the template directory path
|
|
44
|
+
# Calculate path to vendors/rust directory relative to this file
|
|
45
|
+
# File is in src/wl_cli/commands/initenv/utils/
|
|
46
|
+
# vendors dir is in src/wl_cli/
|
|
47
|
+
# So we need to go up 4 levels
|
|
48
|
+
template_dir = (
|
|
49
|
+
Path(__file__).parent.parent.parent.parent / "vendors" / "rust"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
log_debug(f"Template directory: {template_dir}")
|
|
53
|
+
|
|
54
|
+
if not template_dir.exists():
|
|
55
|
+
raise InitEnvError(f"Rust template directory not found: {template_dir}")
|
|
56
|
+
|
|
57
|
+
# Get project name from current directory
|
|
58
|
+
project_name = Path.cwd().name
|
|
59
|
+
|
|
60
|
+
# Copy template files to rust directory
|
|
61
|
+
for item in template_dir.iterdir():
|
|
62
|
+
dest_path = rust_dir / item.name
|
|
63
|
+
if item.is_dir():
|
|
64
|
+
shutil.copytree(item, dest_path, dirs_exist_ok=True)
|
|
65
|
+
else:
|
|
66
|
+
# Special handling for Cargo.toml to replace project name
|
|
67
|
+
if item.name == "Cargo.toml":
|
|
68
|
+
self._copy_and_update_cargo_toml(item, dest_path, project_name)
|
|
69
|
+
else:
|
|
70
|
+
shutil.copy2(item, dest_path)
|
|
71
|
+
|
|
72
|
+
# Update lib.rs with the correct module name
|
|
73
|
+
lib_rs_path = rust_dir / "src" / "lib.rs"
|
|
74
|
+
if lib_rs_path.exists():
|
|
75
|
+
self._update_lib_rs(lib_rs_path, project_name)
|
|
76
|
+
|
|
77
|
+
log_info_pair(
|
|
78
|
+
"✓ Rust project initialized successfully with template",
|
|
79
|
+
"✓ 使用模板成功初始化Rust项目",
|
|
80
|
+
)
|
|
81
|
+
except Exception as e:
|
|
82
|
+
raise InitEnvError(f"Failed to initialize Rust project: {e}")
|
|
83
|
+
|
|
84
|
+
def _copy_and_update_cargo_toml(
|
|
85
|
+
self, source: Path, dest: Path, project_name: str
|
|
86
|
+
) -> None:
|
|
87
|
+
"""Copy Cargo.toml template and update project name."""
|
|
88
|
+
# Read the template file
|
|
89
|
+
with open(source, encoding="utf-8") as f:
|
|
90
|
+
content = f.read()
|
|
91
|
+
|
|
92
|
+
# Clean the project name to be a valid Cargo name
|
|
93
|
+
cleaned_project_name = clean_cargo_name(project_name)
|
|
94
|
+
# Ensure the library name uses underscores, not hyphens
|
|
95
|
+
lib_name = cleaned_project_name + "_native"
|
|
96
|
+
|
|
97
|
+
# Warn users if their project name contains hyphens
|
|
98
|
+
if "-" in project_name:
|
|
99
|
+
log_info_pair(
|
|
100
|
+
"Warning: Project name contains hyphens which are not allowed in Rust library names.",
|
|
101
|
+
"警告: 项目名称包含连字符,这在Rust库名称中是不允许的。",
|
|
102
|
+
)
|
|
103
|
+
log_info_pair(
|
|
104
|
+
" Converting to underscores for Rust compatibility.",
|
|
105
|
+
" 已转换为下划线以确保与Rust兼容。",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Replace the template project name with actual project name
|
|
109
|
+
# Replace both the package name and the lib name
|
|
110
|
+
updated_content = content.replace("wl_cli_native", lib_name)
|
|
111
|
+
# Also replace in the [lib] section to ensure library name is correct
|
|
112
|
+
updated_content = updated_content.replace(
|
|
113
|
+
'name = "wl_cli_native"', f'name = "{lib_name}"'
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Write the updated content to destination
|
|
117
|
+
with open(dest, "w", encoding="utf-8") as f:
|
|
118
|
+
f.write(updated_content)
|
|
119
|
+
|
|
120
|
+
def _update_lib_rs(self, lib_rs_path: Path, project_name: str) -> None:
|
|
121
|
+
"""Update lib.rs with the correct module name."""
|
|
122
|
+
# Read the template file
|
|
123
|
+
with open(lib_rs_path, encoding="utf-8") as f:
|
|
124
|
+
content = f.read()
|
|
125
|
+
|
|
126
|
+
# Clean the project name to be a valid Cargo name
|
|
127
|
+
cleaned_project_name = clean_cargo_name(project_name)
|
|
128
|
+
# Ensure the library name uses underscores, not hyphens
|
|
129
|
+
lib_name = cleaned_project_name + "_native"
|
|
130
|
+
|
|
131
|
+
# Warn users if their project name contains hyphens
|
|
132
|
+
if "-" in project_name:
|
|
133
|
+
log_info_pair(
|
|
134
|
+
"Warning: Project name contains hyphens which are not allowed in Rust library names.",
|
|
135
|
+
"警告: 项目名称包含连字符,这在Rust库名称中是不允许的。",
|
|
136
|
+
)
|
|
137
|
+
log_info_pair(
|
|
138
|
+
" Converting to underscores for Rust compatibility.",
|
|
139
|
+
" 已转换为下划线以确保与Rust兼容。",
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Replace the template project name with actual project name in lib.rs
|
|
143
|
+
updated_content = content.replace("wl_cli_native", lib_name)
|
|
144
|
+
with open(lib_rs_path, "w", encoding="utf-8") as f:
|
|
145
|
+
f.write(updated_content)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Symlink cleaning utilities for deployment functionality.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _clean_file_symlinks(root: str, files: list[str], source_dir_basename: str) -> int:
|
|
9
|
+
"""
|
|
10
|
+
清理文件软链接
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
root: 根目录路径
|
|
14
|
+
files: 文件列表
|
|
15
|
+
source_dir_basename: 源目录基本名称
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
int: 清理的链接数量
|
|
19
|
+
"""
|
|
20
|
+
cleaned_count = 0
|
|
21
|
+
for file in files:
|
|
22
|
+
file_path = os.path.join(root, file)
|
|
23
|
+
if os.path.islink(file_path):
|
|
24
|
+
try:
|
|
25
|
+
link_target = os.readlink(file_path)
|
|
26
|
+
if source_dir_basename in link_target:
|
|
27
|
+
os.unlink(file_path)
|
|
28
|
+
cleaned_count += 1
|
|
29
|
+
except OSError:
|
|
30
|
+
pass
|
|
31
|
+
return cleaned_count
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _clean_directory_symlinks(
|
|
35
|
+
root: str, dirs: list[str], source_dir_basename: str
|
|
36
|
+
) -> tuple[int, list[str]]:
|
|
37
|
+
"""
|
|
38
|
+
清理目录软链接
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
root: 根目录路径
|
|
42
|
+
dirs: 目录列表
|
|
43
|
+
source_dir_basename: 源目录基本名称
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
tuple[int, list[str]]: (清理的链接数量, 需要从遍历中移除的目录列表)
|
|
47
|
+
"""
|
|
48
|
+
cleaned_count = 0
|
|
49
|
+
removed_dirs = []
|
|
50
|
+
for dir_name in dirs[:]: # 使用副本进行迭代,因为我们可能会修改dirs
|
|
51
|
+
dir_path = os.path.join(root, dir_name)
|
|
52
|
+
if os.path.islink(dir_path):
|
|
53
|
+
try:
|
|
54
|
+
link_target = os.readlink(dir_path)
|
|
55
|
+
if source_dir_basename in link_target:
|
|
56
|
+
os.unlink(dir_path)
|
|
57
|
+
cleaned_count += 1
|
|
58
|
+
removed_dirs.append(dir_name) # 记录需要移除的目录
|
|
59
|
+
except OSError:
|
|
60
|
+
pass
|
|
61
|
+
return cleaned_count, removed_dirs
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def clean_existing_symlinks(
|
|
65
|
+
py_dirs: list[str], source_dir_name: str = "shared-build-system"
|
|
66
|
+
) -> int:
|
|
67
|
+
"""
|
|
68
|
+
清理目标文件夹中已有的软链接
|
|
69
|
+
"""
|
|
70
|
+
cleaned_count = 0
|
|
71
|
+
source_dir_basename = os.path.basename(source_dir_name)
|
|
72
|
+
|
|
73
|
+
for py_dir in py_dirs:
|
|
74
|
+
if not os.path.exists(py_dir):
|
|
75
|
+
continue
|
|
76
|
+
|
|
77
|
+
# 递归清理子目录中的链接
|
|
78
|
+
try:
|
|
79
|
+
for root, dirs, files in os.walk(py_dir):
|
|
80
|
+
# 清理文件链接
|
|
81
|
+
cleaned_count += _clean_file_symlinks(root, files, source_dir_basename)
|
|
82
|
+
|
|
83
|
+
# 清理目录链接
|
|
84
|
+
dir_cleaned_count, removed_dirs = _clean_directory_symlinks(
|
|
85
|
+
root, dirs, source_dir_basename
|
|
86
|
+
)
|
|
87
|
+
cleaned_count += dir_cleaned_count
|
|
88
|
+
|
|
89
|
+
# 从遍历列表中移除已清理的目录
|
|
90
|
+
for removed_dir in removed_dirs:
|
|
91
|
+
try:
|
|
92
|
+
dirs.remove(removed_dir)
|
|
93
|
+
except ValueError:
|
|
94
|
+
pass # 目录不在列表中,忽略
|
|
95
|
+
|
|
96
|
+
except OSError:
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
return cleaned_count
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Symlink management utilities for deployment.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
|
|
8
|
+
from ....utils.platform_adapter import PlatformAdapter
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def create_symlink(source: str, target: str) -> bool:
|
|
12
|
+
"""
|
|
13
|
+
创建从源到目标的软链接,处理平台差异
|
|
14
|
+
"""
|
|
15
|
+
source = os.path.abspath(source)
|
|
16
|
+
target = os.path.abspath(target)
|
|
17
|
+
|
|
18
|
+
# 如果目标文件已存在,先删除
|
|
19
|
+
if os.path.exists(target):
|
|
20
|
+
try:
|
|
21
|
+
if os.path.islink(target):
|
|
22
|
+
os.unlink(target)
|
|
23
|
+
elif os.path.isdir(target):
|
|
24
|
+
shutil.rmtree(target)
|
|
25
|
+
elif os.path.isfile(target):
|
|
26
|
+
os.remove(target)
|
|
27
|
+
except OSError:
|
|
28
|
+
return False
|
|
29
|
+
|
|
30
|
+
# 确保目标目录存在
|
|
31
|
+
target_dir = os.path.dirname(target)
|
|
32
|
+
try:
|
|
33
|
+
os.makedirs(target_dir, exist_ok=True)
|
|
34
|
+
except OSError:
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
# 创建软链接(处理不同操作系统)
|
|
38
|
+
try:
|
|
39
|
+
if PlatformAdapter.is_windows():
|
|
40
|
+
# Windows 需要管理员权限并区分目录和文件链接
|
|
41
|
+
is_directory = os.path.isdir(source)
|
|
42
|
+
if is_directory:
|
|
43
|
+
# Windows 10以上版本支持目录符号链接,低版本需使用junction
|
|
44
|
+
os.symlink(source, target, target_is_directory=True)
|
|
45
|
+
else:
|
|
46
|
+
os.symlink(source, target)
|
|
47
|
+
else:
|
|
48
|
+
# Unix/Linux/macOS
|
|
49
|
+
os.symlink(source, target)
|
|
50
|
+
return True
|
|
51
|
+
except OSError:
|
|
52
|
+
return False
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Virtual environment manager utility."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
|
|
5
|
+
from ....utils.logging import log_info, log_info_pair
|
|
6
|
+
from ....utils.platform_adapter import PlatformAdapter
|
|
7
|
+
from .log_manager import performance_monitor
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class VenvManager:
|
|
11
|
+
"""Virtual environment manager."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, env: dict[str, str]) -> None:
|
|
14
|
+
self.env = env
|
|
15
|
+
|
|
16
|
+
@performance_monitor
|
|
17
|
+
def create_venv(
|
|
18
|
+
self, venv_path: str, _: str, env: dict[str, str] | None = None
|
|
19
|
+
) -> bool:
|
|
20
|
+
"""Create a virtual environment, retrying on Windows when needed.
|
|
21
|
+
|
|
22
|
+
The platform differences live on :class:`PlatformAdapter`: it supplies
|
|
23
|
+
the primary argv (``python -m uv`` on Windows, ``uv`` elsewhere) and,
|
|
24
|
+
on Windows only, a retry argv for when the interpreter cannot find uv
|
|
25
|
+
as a module.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
venv_path: Destination directory for the virtual environment.
|
|
29
|
+
_: Python version; currently informational only.
|
|
30
|
+
env: Environment for the subprocess. Defaults to this manager's
|
|
31
|
+
own environment; the Windows caller relies on that default.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
True if the environment was created, False otherwise.
|
|
35
|
+
"""
|
|
36
|
+
process_env = self.env if env is None else env
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
cmd = PlatformAdapter.get_venv_create_command(venv_path)
|
|
40
|
+
fallback_cmd = PlatformAdapter.get_venv_create_fallback_command(venv_path)
|
|
41
|
+
|
|
42
|
+
result = subprocess.run(
|
|
43
|
+
cmd, capture_output=True, text=True, env=process_env, encoding="utf-8"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# If the interpreter cannot find uv as a module, retry with the uv
|
|
47
|
+
# command directly (Windows only -- fallback_cmd is None elsewhere).
|
|
48
|
+
if (
|
|
49
|
+
result.returncode != 0
|
|
50
|
+
and fallback_cmd is not None
|
|
51
|
+
and "No module named uv" in result.stderr
|
|
52
|
+
):
|
|
53
|
+
log_info("uv module not found, trying uv command directly...")
|
|
54
|
+
result = subprocess.run(
|
|
55
|
+
fallback_cmd,
|
|
56
|
+
capture_output=True,
|
|
57
|
+
text=True,
|
|
58
|
+
env=process_env,
|
|
59
|
+
encoding="utf-8",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
if result.returncode == 0:
|
|
63
|
+
log_info_pair(
|
|
64
|
+
f"✓ Virtual environment created at {venv_path}",
|
|
65
|
+
f"✓ 虚拟环境已在 {venv_path} 创建",
|
|
66
|
+
)
|
|
67
|
+
return True
|
|
68
|
+
else:
|
|
69
|
+
log_info_pair(
|
|
70
|
+
f"Warning: Failed to create virtual environment: {result.stderr}",
|
|
71
|
+
f"警告: 创建虚拟环境失败: {result.stderr}",
|
|
72
|
+
)
|
|
73
|
+
return False
|
|
74
|
+
except Exception as e:
|
|
75
|
+
log_info_pair(
|
|
76
|
+
f"Warning: Failed to create virtual environment: {e}",
|
|
77
|
+
f"警告: 创建虚拟环境失败: {e}",
|
|
78
|
+
)
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
@performance_monitor
|
|
82
|
+
def sync_environment(self, _venv_path: str = ".venv") -> bool:
|
|
83
|
+
"""Sync the environment by updating dependencies."""
|
|
84
|
+
log_info_pair(
|
|
85
|
+
"Syncing environment dependencies...",
|
|
86
|
+
"同步环境依赖...",
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
# 更新依赖
|
|
91
|
+
log_info_pair(
|
|
92
|
+
"Updating dependencies with uv...",
|
|
93
|
+
"使用uv更新依赖...",
|
|
94
|
+
)
|
|
95
|
+
cmd = ["uv", "sync"]
|
|
96
|
+
|
|
97
|
+
result = subprocess.run(
|
|
98
|
+
cmd, capture_output=True, text=True, env=self.env, encoding="utf-8"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
if result.returncode == 0:
|
|
102
|
+
log_info_pair(
|
|
103
|
+
"✓ Dependencies updated successfully",
|
|
104
|
+
"✓ 依赖更新成功",
|
|
105
|
+
)
|
|
106
|
+
return True
|
|
107
|
+
else:
|
|
108
|
+
log_info_pair(
|
|
109
|
+
f"Warning: Failed to update dependencies: {result.stderr}",
|
|
110
|
+
f"警告: 更新依赖失败: {result.stderr}",
|
|
111
|
+
)
|
|
112
|
+
return False
|
|
113
|
+
except Exception as e:
|
|
114
|
+
log_info_pair(
|
|
115
|
+
f"Warning: Failed to sync environment: {e}",
|
|
116
|
+
f"警告: 同步环境失败: {e}",
|
|
117
|
+
)
|
|
118
|
+
return False
|