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,81 @@
|
|
|
1
|
+
"""Environment detection and Python executable resolution utilities."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from ....exceptions import CommandError
|
|
8
|
+
from ....utils.logging import log_error, log_info
|
|
9
|
+
from ....utils.platform_adapter import PlatformAdapter
|
|
10
|
+
from ....utils.workspace_detector import WorkspaceDetector
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class EnvironmentDetector:
|
|
14
|
+
"""Detect project environment and resolve Python executable paths."""
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def is_rust_enabled() -> bool:
|
|
18
|
+
"""
|
|
19
|
+
Check if Rust is enabled for this project.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
bool: True if Rust is enabled, False otherwise.
|
|
23
|
+
"""
|
|
24
|
+
rust_dir = os.path.join(os.getcwd(), "rust")
|
|
25
|
+
cargo_toml = os.path.join(rust_dir, "Cargo.toml")
|
|
26
|
+
return os.path.exists(cargo_toml)
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def _create_venv() -> None:
|
|
30
|
+
"""Create virtual environment using uv."""
|
|
31
|
+
try:
|
|
32
|
+
# Use uv to create virtual environment
|
|
33
|
+
cmd = ["uv", "venv"]
|
|
34
|
+
subprocess.run(
|
|
35
|
+
cmd,
|
|
36
|
+
check=True,
|
|
37
|
+
capture_output=False,
|
|
38
|
+
text=True,
|
|
39
|
+
)
|
|
40
|
+
log_info("✓ Virtual environment created successfully")
|
|
41
|
+
except subprocess.CalledProcessError as e:
|
|
42
|
+
log_error(f"Failed to create virtual environment: {e}")
|
|
43
|
+
raise CommandError(f"Failed to create virtual environment: {e}")
|
|
44
|
+
except Exception as e:
|
|
45
|
+
log_error(f"Unexpected error creating virtual environment: {e}")
|
|
46
|
+
raise CommandError(f"Failed to create virtual environment: {e}")
|
|
47
|
+
|
|
48
|
+
def resolve_python_executable(self) -> tuple[str | None, bool]:
|
|
49
|
+
"""
|
|
50
|
+
Resolve the appropriate Python executable for the current environment.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
tuple: (python_executable_path, is_workspace) where:
|
|
54
|
+
- python_executable_path: Path to Python executable or None
|
|
55
|
+
- is_workspace: True if running in a workspace environment
|
|
56
|
+
"""
|
|
57
|
+
detector = WorkspaceDetector()
|
|
58
|
+
is_workspace = detector.detect(Path.cwd())
|
|
59
|
+
|
|
60
|
+
if not is_workspace:
|
|
61
|
+
log_info("Not in uv workspace environment")
|
|
62
|
+
return "python", False
|
|
63
|
+
|
|
64
|
+
log_info("✓ uv workspace environment detected")
|
|
65
|
+
venv_root = detector.get_venv_path(Path.cwd())
|
|
66
|
+
|
|
67
|
+
if venv_root is None:
|
|
68
|
+
try:
|
|
69
|
+
venv_str = detector.get_active_venv_path_str(Path.cwd())
|
|
70
|
+
venv_root = Path(venv_str)
|
|
71
|
+
except Exception:
|
|
72
|
+
venv_root = None
|
|
73
|
+
|
|
74
|
+
if venv_root is None:
|
|
75
|
+
log_info("No venv found, creating local .venv for build...")
|
|
76
|
+
self._create_venv()
|
|
77
|
+
venv_root = Path(".venv")
|
|
78
|
+
|
|
79
|
+
if PlatformAdapter.is_windows():
|
|
80
|
+
return str((venv_root / "Scripts" / "python.exe").resolve()), True
|
|
81
|
+
return str((venv_root / "bin" / "python").resolve()), True
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Rust build utilities using maturin."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
|
|
5
|
+
from ....utils.logging import log_info, log_info_pair
|
|
6
|
+
from ....utils.subprocess import run_command
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RustBuilder:
|
|
10
|
+
"""Handle Rust build processes using maturin."""
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def run_maturin_build(python_executable: str | None) -> None:
|
|
14
|
+
"""Run maturin build process."""
|
|
15
|
+
command = ["maturin", "build", "--release", "--out", "dist"]
|
|
16
|
+
if python_executable:
|
|
17
|
+
command.extend(["-i", python_executable])
|
|
18
|
+
log_info(f"Trying to build with: {' '.join(command)}")
|
|
19
|
+
subprocess.run(command, check=True, capture_output=False, text=True)
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def run_maturin_develop() -> None:
|
|
23
|
+
"""Run maturin develop for incremental builds."""
|
|
24
|
+
log_info_pair(
|
|
25
|
+
"Using maturin develop to build and install editable package...",
|
|
26
|
+
"使用 maturin develop 构建和安装可编辑包...",
|
|
27
|
+
)
|
|
28
|
+
# 使用maturin的原生增量编译功能
|
|
29
|
+
run_command(["maturin", "develop", "--skip-install"], capture_output=False)
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def build_full(python_executable: str | None) -> None:
|
|
33
|
+
"""Perform a full build with maturin."""
|
|
34
|
+
RustBuilder.run_maturin_build(python_executable)
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
def build_incremental() -> None:
|
|
38
|
+
"""Perform an incremental build with maturin develop."""
|
|
39
|
+
RustBuilder.run_maturin_develop()
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Type stubs generation and management utilities."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from ....commands.format.python_formatter import generate_type_stubs
|
|
8
|
+
from ....utils.logging import log_error, log_info
|
|
9
|
+
from ....utils.uv_tool import is_running_in_uv_tool
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class StubsManager:
|
|
13
|
+
"""Manage type stubs generation and copying."""
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def generate_and_copy_stubs() -> None:
|
|
17
|
+
"""Generate type stubs and copy them to the appropriate locations."""
|
|
18
|
+
try:
|
|
19
|
+
root = Path.cwd()
|
|
20
|
+
src_path = root / "src"
|
|
21
|
+
typings_path = root / "typings"
|
|
22
|
+
|
|
23
|
+
if not src_path.exists():
|
|
24
|
+
if not is_running_in_uv_tool():
|
|
25
|
+
log_info(f"Target {src_path} does not exist, skipping...")
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
log_info(f"Generating type stubs for {src_path} -> {typings_path}")
|
|
29
|
+
generate_type_stubs(
|
|
30
|
+
str(src_path), str(typings_path), os.environ.copy(), quiet=False
|
|
31
|
+
)
|
|
32
|
+
log_info("✓ Type stubs generated")
|
|
33
|
+
|
|
34
|
+
package_root = src_path / "wl_cli"
|
|
35
|
+
stub_root = typings_path / "wl_cli"
|
|
36
|
+
|
|
37
|
+
if stub_root.exists():
|
|
38
|
+
for pyi in stub_root.rglob("*.pyi"):
|
|
39
|
+
rel = pyi.relative_to(stub_root)
|
|
40
|
+
dest = package_root / rel
|
|
41
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
42
|
+
shutil.copy2(pyi, dest)
|
|
43
|
+
log_info("✓ Type stubs copied into package source")
|
|
44
|
+
else:
|
|
45
|
+
for pyi in typings_path.rglob("*.pyi"):
|
|
46
|
+
try:
|
|
47
|
+
parts = list(pyi.parts)
|
|
48
|
+
if "wl_cli" in parts:
|
|
49
|
+
idx = parts.index("wl_cli")
|
|
50
|
+
rel = Path(*parts[idx + 1 :])
|
|
51
|
+
dest = package_root / rel
|
|
52
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
53
|
+
shutil.copy2(pyi, dest)
|
|
54
|
+
except Exception:
|
|
55
|
+
continue
|
|
56
|
+
|
|
57
|
+
except Exception as e:
|
|
58
|
+
log_error(f"Failed to generate type stubs: {e}")
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"""Build dist command for WL Commands."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
import subprocess
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
8
|
+
|
|
9
|
+
import tomllib
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from wl_cli.utils.workspace_detector.types import WorkspaceDetectionRules
|
|
13
|
+
|
|
14
|
+
import click
|
|
15
|
+
|
|
16
|
+
from ...exceptions import CommandError
|
|
17
|
+
from ...utils.logging import log_error, log_info
|
|
18
|
+
from ...utils.platform_adapter import PlatformAdapter
|
|
19
|
+
from ...utils.uv_workspace import is_uv_workspace
|
|
20
|
+
from ...utils.workspace_detector import WorkspaceDetectionError, WorkspaceDetector
|
|
21
|
+
from .. import Command
|
|
22
|
+
from ..format.python_formatter import generate_type_stubs
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class BuildDistCommand(Command):
|
|
26
|
+
"""Command to build distribution packages."""
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def name(self) -> str:
|
|
30
|
+
"""Return the command name."""
|
|
31
|
+
return "dist"
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def help(self) -> str:
|
|
35
|
+
"""Return the command help text."""
|
|
36
|
+
return "Build distribution packages"
|
|
37
|
+
|
|
38
|
+
def execute(self, *_: Any, **kwargs: Any) -> None:
|
|
39
|
+
"""Execute the build dist command."""
|
|
40
|
+
# Determine the platform and run the appropriate build command
|
|
41
|
+
created_venv = False # Track if we created a new venv
|
|
42
|
+
try:
|
|
43
|
+
log_info("Building distribution packages...")
|
|
44
|
+
detector = WorkspaceDetector(self._build_rules(kwargs))
|
|
45
|
+
is_workspace = detector.detect(Path.cwd())
|
|
46
|
+
self._validate_workspace(detector, is_workspace, kwargs)
|
|
47
|
+
python_executable, created_venv = self._resolve_python_executable(
|
|
48
|
+
detector, is_workspace
|
|
49
|
+
)
|
|
50
|
+
self._generate_type_stubs()
|
|
51
|
+
self._run_build(python_executable)
|
|
52
|
+
self._cleanup_stubs()
|
|
53
|
+
self._cleanup_venv_if_needed(is_workspace, created_venv)
|
|
54
|
+
log_info("✓ Distribution packages built successfully")
|
|
55
|
+
except subprocess.CalledProcessError as e:
|
|
56
|
+
log_error(f"Failed to build distribution packages: {e}")
|
|
57
|
+
raise CommandError(f"Build dist failed with return code {e.returncode}")
|
|
58
|
+
except FileNotFoundError:
|
|
59
|
+
log_error(
|
|
60
|
+
"Build backend not found. Pure-Python projects need uv; Rust "
|
|
61
|
+
"projects need maturin. Ensure it is installed and in PATH."
|
|
62
|
+
)
|
|
63
|
+
raise CommandError("Build backend not found")
|
|
64
|
+
except WorkspaceDetectionError as e:
|
|
65
|
+
log_error(f"Workspace detection error: {e}")
|
|
66
|
+
raise CommandError(f"Build dist failed: {e}")
|
|
67
|
+
except Exception as e:
|
|
68
|
+
log_error(f"Unexpected error during build dist: {e}")
|
|
69
|
+
raise CommandError(f"Build dist failed: {e}")
|
|
70
|
+
|
|
71
|
+
def _detect_uv_workspace(self) -> bool:
|
|
72
|
+
"""
|
|
73
|
+
Detect if we are in a uv workspace.
|
|
74
|
+
This is kept for backward compatibility with tests.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
bool: True if in a uv workspace, False otherwise.
|
|
78
|
+
"""
|
|
79
|
+
return is_uv_workspace()
|
|
80
|
+
|
|
81
|
+
def _create_venv(self) -> None:
|
|
82
|
+
"""Create virtual environment using uv."""
|
|
83
|
+
try:
|
|
84
|
+
# Use uv to create virtual environment
|
|
85
|
+
cmd = ["uv", "venv"]
|
|
86
|
+
subprocess.run(
|
|
87
|
+
cmd,
|
|
88
|
+
check=True,
|
|
89
|
+
capture_output=False,
|
|
90
|
+
text=True,
|
|
91
|
+
)
|
|
92
|
+
log_info("✓ Virtual environment created successfully")
|
|
93
|
+
except subprocess.CalledProcessError as e:
|
|
94
|
+
log_error(f"Failed to create virtual environment: {e}")
|
|
95
|
+
raise CommandError(f"Failed to create virtual environment: {e}")
|
|
96
|
+
except Exception as e:
|
|
97
|
+
log_error(f"Unexpected error creating virtual environment: {e}")
|
|
98
|
+
raise CommandError(f"Failed to create virtual environment: {e}")
|
|
99
|
+
|
|
100
|
+
def _build_rules(self, kwargs: dict[str, object]) -> "WorkspaceDetectionRules":
|
|
101
|
+
return cast(
|
|
102
|
+
"WorkspaceDetectionRules",
|
|
103
|
+
{
|
|
104
|
+
"uv_tree_min_roots": kwargs.get("ws_min_roots", 2),
|
|
105
|
+
"use_uv_tree": not kwargs.get("ws_disable_tree", False),
|
|
106
|
+
"use_uv_init": not kwargs.get("ws_disable_init", False),
|
|
107
|
+
"use_pyproject": not kwargs.get("ws_disable_pyproject", False),
|
|
108
|
+
"use_uv_lock": not kwargs.get("ws_disable_uv_lock", False),
|
|
109
|
+
},
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def _validate_workspace(
|
|
113
|
+
self, detector: WorkspaceDetector, is_workspace: bool, kwargs: dict[str, object]
|
|
114
|
+
) -> None:
|
|
115
|
+
validation = detector.validate(Path.cwd())
|
|
116
|
+
if not is_workspace and validation.details.get("pyproject.exists") == "True":
|
|
117
|
+
msg = "Detected pyproject but workspace detection failed; check tool.uv.workspace"
|
|
118
|
+
if kwargs.get("ws_strict", False):
|
|
119
|
+
log_error(msg)
|
|
120
|
+
raise CommandError(msg)
|
|
121
|
+
else:
|
|
122
|
+
log_info(f"Warning: {msg}")
|
|
123
|
+
|
|
124
|
+
def _resolve_python_executable(
|
|
125
|
+
self, detector: WorkspaceDetector, is_workspace: bool
|
|
126
|
+
) -> tuple[str | None, bool]:
|
|
127
|
+
"""Resolve Python executable and track if we created a new venv."""
|
|
128
|
+
if not is_workspace:
|
|
129
|
+
log_info("Not in uv workspace environment")
|
|
130
|
+
return None, False
|
|
131
|
+
log_info("✓ uv workspace environment detected")
|
|
132
|
+
venv_root = detector.get_venv_path(Path.cwd())
|
|
133
|
+
created_venv = False
|
|
134
|
+
if venv_root is None:
|
|
135
|
+
try:
|
|
136
|
+
venv_str = detector.getActiveVenvPath(Path.cwd())
|
|
137
|
+
venv_root = Path(venv_str)
|
|
138
|
+
except Exception:
|
|
139
|
+
venv_root = None
|
|
140
|
+
if venv_root is None:
|
|
141
|
+
log_info("No venv found, creating local .venv for build...")
|
|
142
|
+
self._create_venv()
|
|
143
|
+
venv_root = Path(".venv")
|
|
144
|
+
created_venv = True
|
|
145
|
+
if PlatformAdapter.is_windows():
|
|
146
|
+
return str((venv_root / "Scripts" / "python.exe").resolve()), created_venv
|
|
147
|
+
return str((venv_root / "bin" / "python").resolve()), created_venv
|
|
148
|
+
|
|
149
|
+
def _uses_maturin(self) -> bool:
|
|
150
|
+
"""Return True when the project is a maturin/Rust distribution.
|
|
151
|
+
|
|
152
|
+
A pure-Python project (the normal case after #226) has neither a
|
|
153
|
+
maturin backend nor a ``rust/Cargo.toml``; it must build with
|
|
154
|
+
``uv build`` and needs no Rust toolchain.
|
|
155
|
+
"""
|
|
156
|
+
root = Path.cwd()
|
|
157
|
+
data: dict[str, Any] = {}
|
|
158
|
+
pyproject = root / "pyproject.toml"
|
|
159
|
+
if pyproject.exists():
|
|
160
|
+
try:
|
|
161
|
+
data = tomllib.loads(pyproject.read_text(encoding="utf-8"))
|
|
162
|
+
except (OSError, tomllib.TOMLDecodeError):
|
|
163
|
+
data = {}
|
|
164
|
+
build_system = data.get("build-system") or {}
|
|
165
|
+
backend = str(build_system.get("build-backend") or "").lower()
|
|
166
|
+
return "maturin" in backend or (root / "rust" / "Cargo.toml").exists()
|
|
167
|
+
|
|
168
|
+
def _run_build(self, python_executable: str | None) -> None:
|
|
169
|
+
"""Build the distribution with the project's own backend.
|
|
170
|
+
|
|
171
|
+
Rust/maturin projects keep using maturin; everything else (including
|
|
172
|
+
``wl`` itself) uses ``uv build``.
|
|
173
|
+
"""
|
|
174
|
+
if self._uses_maturin():
|
|
175
|
+
command = ["maturin", "build", "--release", "--out", "dist"]
|
|
176
|
+
if python_executable:
|
|
177
|
+
command.extend(["-i", python_executable])
|
|
178
|
+
else:
|
|
179
|
+
command = ["uv", "build", "--out-dir", "dist"]
|
|
180
|
+
if python_executable:
|
|
181
|
+
command.extend(["--python", python_executable])
|
|
182
|
+
log_info(f"Trying to build with: {' '.join(command)}")
|
|
183
|
+
subprocess.run(command, check=True, capture_output=False, text=True)
|
|
184
|
+
|
|
185
|
+
def _generate_type_stubs(self) -> None:
|
|
186
|
+
root = Path.cwd()
|
|
187
|
+
src_path = root / "src"
|
|
188
|
+
typings_path = root / "typings"
|
|
189
|
+
env = os.environ.copy()
|
|
190
|
+
if not src_path.exists():
|
|
191
|
+
log_error(f"Source directory not found for stubs: {src_path}")
|
|
192
|
+
return
|
|
193
|
+
log_info(f"Generating type stubs for {src_path} -> {typings_path}")
|
|
194
|
+
try:
|
|
195
|
+
generate_type_stubs(str(src_path), str(typings_path), env, quiet=False)
|
|
196
|
+
log_info("✓ Type stubs generated")
|
|
197
|
+
|
|
198
|
+
# Copy generated .pyi into package src so they are included in wheel
|
|
199
|
+
package_root = src_path / "wl_cli"
|
|
200
|
+
stub_root = typings_path / "wl_cli"
|
|
201
|
+
if stub_root.exists():
|
|
202
|
+
for pyi in stub_root.rglob("*.pyi"):
|
|
203
|
+
rel = pyi.relative_to(stub_root)
|
|
204
|
+
dest = package_root / rel
|
|
205
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
206
|
+
shutil.copy2(pyi, dest)
|
|
207
|
+
log_info("✓ Type stubs copied into package source")
|
|
208
|
+
else:
|
|
209
|
+
# Fallback: copy all .pyi under typings preserving structure relative to src
|
|
210
|
+
for pyi in typings_path.rglob("*.pyi"):
|
|
211
|
+
try:
|
|
212
|
+
# attempt to find 'wl_cli' segment
|
|
213
|
+
parts = list(pyi.parts)
|
|
214
|
+
if "wl_cli" in parts:
|
|
215
|
+
idx = parts.index("wl_cli")
|
|
216
|
+
rel = Path(*parts[idx + 1 :])
|
|
217
|
+
dest = package_root / rel
|
|
218
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
219
|
+
shutil.copy2(pyi, dest)
|
|
220
|
+
except Exception:
|
|
221
|
+
continue
|
|
222
|
+
except Exception as e:
|
|
223
|
+
log_error(f"Failed to generate type stubs: {e}")
|
|
224
|
+
|
|
225
|
+
def _cleanup_stubs(self) -> None:
|
|
226
|
+
try:
|
|
227
|
+
root = Path.cwd()
|
|
228
|
+
typings_path = root / "typings"
|
|
229
|
+
if typings_path.exists():
|
|
230
|
+
shutil.rmtree(typings_path, ignore_errors=True)
|
|
231
|
+
log_info("✓ typings directory removed")
|
|
232
|
+
src_path = root / "src"
|
|
233
|
+
if src_path.exists():
|
|
234
|
+
removed = 0
|
|
235
|
+
for p in src_path.rglob("*.pyi"):
|
|
236
|
+
try:
|
|
237
|
+
p.unlink()
|
|
238
|
+
removed += 1
|
|
239
|
+
except Exception:
|
|
240
|
+
pass
|
|
241
|
+
if removed:
|
|
242
|
+
log_info(f"✓ Removed {removed} .pyi files from src")
|
|
243
|
+
except Exception as e:
|
|
244
|
+
log_error(f"Failed to cleanup stubs: {e}")
|
|
245
|
+
|
|
246
|
+
def _cleanup_venv_if_needed(self, is_workspace: bool, created_venv: bool) -> None:
|
|
247
|
+
"""Clean up .venv directory if in uv workspace environment.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
is_workspace: Whether we are in a uv workspace environment
|
|
251
|
+
created_venv: Whether we created a new venv during this build
|
|
252
|
+
"""
|
|
253
|
+
if not is_workspace:
|
|
254
|
+
# In non-workspace environments, preserve the user's existing venv
|
|
255
|
+
return
|
|
256
|
+
|
|
257
|
+
venv_path = Path.cwd() / ".venv"
|
|
258
|
+
if venv_path.exists():
|
|
259
|
+
# Only remove .venv if we created it during this build
|
|
260
|
+
# Preserve user's existing venv if it was already there
|
|
261
|
+
if created_venv:
|
|
262
|
+
try:
|
|
263
|
+
log_info(
|
|
264
|
+
"Removing temporary .venv directory created during build..."
|
|
265
|
+
)
|
|
266
|
+
shutil.rmtree(venv_path, ignore_errors=True)
|
|
267
|
+
log_info("✓ Temporary .venv directory removed")
|
|
268
|
+
except Exception as e:
|
|
269
|
+
log_error(f"Failed to remove .venv directory: {e}")
|
|
270
|
+
else:
|
|
271
|
+
log_info(
|
|
272
|
+
"Preserving existing .venv directory (not created during this build)"
|
|
273
|
+
)
|
|
274
|
+
else:
|
|
275
|
+
log_info("No .venv directory to remove")
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
@click.command(name="builddist", help="Build distribution packages")
|
|
279
|
+
@click.option(
|
|
280
|
+
"--ws-min-roots",
|
|
281
|
+
type=int,
|
|
282
|
+
default=2,
|
|
283
|
+
help="Minimum root packages to consider workspace (uv tree)",
|
|
284
|
+
)
|
|
285
|
+
@click.option(
|
|
286
|
+
"--ws-disable-tree", is_flag=True, default=False, help="Disable uv tree detection"
|
|
287
|
+
)
|
|
288
|
+
@click.option(
|
|
289
|
+
"--ws-disable-init", is_flag=True, default=False, help="Disable uv init detection"
|
|
290
|
+
)
|
|
291
|
+
@click.option(
|
|
292
|
+
"--ws-disable-pyproject",
|
|
293
|
+
is_flag=True,
|
|
294
|
+
default=False,
|
|
295
|
+
help="Disable pyproject workspace detection",
|
|
296
|
+
)
|
|
297
|
+
@click.option(
|
|
298
|
+
"--ws-disable-uv-lock",
|
|
299
|
+
is_flag=True,
|
|
300
|
+
default=False,
|
|
301
|
+
help="Disable uv.lock detection",
|
|
302
|
+
)
|
|
303
|
+
@click.option(
|
|
304
|
+
"--ws-strict",
|
|
305
|
+
is_flag=True,
|
|
306
|
+
default=False,
|
|
307
|
+
help="Fail build on abnormal workspace structure",
|
|
308
|
+
)
|
|
309
|
+
def builddist_command(
|
|
310
|
+
ws_min_roots,
|
|
311
|
+
ws_disable_tree,
|
|
312
|
+
ws_disable_init,
|
|
313
|
+
ws_disable_pyproject,
|
|
314
|
+
ws_disable_uv_lock,
|
|
315
|
+
ws_strict,
|
|
316
|
+
):
|
|
317
|
+
"""Build distribution packages."""
|
|
318
|
+
cmd = BuildDistCommand()
|
|
319
|
+
cmd.execute(
|
|
320
|
+
ws_min_roots=ws_min_roots,
|
|
321
|
+
ws_disable_tree=ws_disable_tree,
|
|
322
|
+
ws_disable_init=ws_disable_init,
|
|
323
|
+
ws_disable_pyproject=ws_disable_pyproject,
|
|
324
|
+
ws_disable_uv_lock=ws_disable_uv_lock,
|
|
325
|
+
ws_strict=ws_strict,
|
|
326
|
+
)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test command for WL Commands.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
|
|
11
|
+
from ...exceptions import CommandError
|
|
12
|
+
from ...utils.logging import log_error, log_info
|
|
13
|
+
from ...utils.output_context import get_output_config
|
|
14
|
+
from ...utils.subprocess import run_command
|
|
15
|
+
from .. import Command
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BuildTestCommand(Command):
|
|
19
|
+
"""Command to build test environment and run unit tests."""
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def name(self) -> str:
|
|
23
|
+
"""Return the command name."""
|
|
24
|
+
return "test"
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def help(self) -> str:
|
|
28
|
+
"""Return the command help text."""
|
|
29
|
+
return "Build test environment and run unit tests"
|
|
30
|
+
|
|
31
|
+
def execute(self, *_: Any, **kwargs: Any) -> None:
|
|
32
|
+
"""Execute the build test command."""
|
|
33
|
+
# Get output configuration
|
|
34
|
+
quiet = kwargs.get("quiet", False)
|
|
35
|
+
verbose = kwargs.get("verbose", False)
|
|
36
|
+
config = get_output_config(override_quiet=quiet, override_verbose=verbose)
|
|
37
|
+
concise = config.concise_mode
|
|
38
|
+
|
|
39
|
+
# Run tests using Python's unittest module directly
|
|
40
|
+
try:
|
|
41
|
+
if not concise:
|
|
42
|
+
log_info("Building test environment and running tests...")
|
|
43
|
+
|
|
44
|
+
# Use -v for verbose output when not concise
|
|
45
|
+
if config.verbose:
|
|
46
|
+
cmd = [
|
|
47
|
+
sys.executable,
|
|
48
|
+
"-m",
|
|
49
|
+
"unittest",
|
|
50
|
+
"discover",
|
|
51
|
+
"-s",
|
|
52
|
+
"tests",
|
|
53
|
+
"-v",
|
|
54
|
+
]
|
|
55
|
+
else:
|
|
56
|
+
cmd = [sys.executable, "-m", "unittest", "discover", "-s", "tests"]
|
|
57
|
+
|
|
58
|
+
# Run tests using python -m unittest discover
|
|
59
|
+
run_command(
|
|
60
|
+
cmd,
|
|
61
|
+
capture_output=False,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if not concise:
|
|
65
|
+
log_info("✓ Tests completed successfully")
|
|
66
|
+
|
|
67
|
+
except subprocess.CalledProcessError as e:
|
|
68
|
+
log_error(f"Failed to run tests: {e}")
|
|
69
|
+
|
|
70
|
+
raise CommandError(f"Test failed with return code {e.returncode}")
|
|
71
|
+
except FileNotFoundError:
|
|
72
|
+
log_error("Python interpreter not found.")
|
|
73
|
+
|
|
74
|
+
raise CommandError("Python interpreter not found")
|
|
75
|
+
except Exception as e:
|
|
76
|
+
log_error(f"Unexpected error during test execution: {e}")
|
|
77
|
+
|
|
78
|
+
raise CommandError(f"Test failed: {e}")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@click.command(name="buildtest", help="Build test environment and run unit tests")
|
|
82
|
+
@click.option(
|
|
83
|
+
"-q", "--quiet", is_flag=True, help="Suppress verbose output (concise mode)"
|
|
84
|
+
)
|
|
85
|
+
@click.option("-v", "--verbose", is_flag=True, help="Force verbose output")
|
|
86
|
+
def buildtest_command(quiet: bool, verbose: bool) -> None:
|
|
87
|
+
"""Build test environment and run unit tests."""
|
|
88
|
+
cmd = BuildTestCommand()
|
|
89
|
+
cmd.execute(quiet=quiet, verbose=verbose)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Clean command module."""
|