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.
Files changed (320) hide show
  1. py_wlcli-0.8.0.dist-info/METADATA +139 -0
  2. py_wlcli-0.8.0.dist-info/RECORD +320 -0
  3. py_wlcli-0.8.0.dist-info/WHEEL +4 -0
  4. py_wlcli-0.8.0.dist-info/entry_points.txt +2 -0
  5. py_wlcli-0.8.0.dist-info/licenses/LICENSE +21 -0
  6. wl_cli/__init__.py +27 -0
  7. wl_cli/__main__.py +130 -0
  8. wl_cli/capabilities.py +79 -0
  9. wl_cli/cc/__init__.py +5 -0
  10. wl_cli/cc/checks.py +1019 -0
  11. wl_cli/cc/cli.py +370 -0
  12. wl_cli/cc/mutate.py +1350 -0
  13. wl_cli/cli_click.py +156 -0
  14. wl_cli/commands/__init__.py +84 -0
  15. wl_cli/commands/base.py +80 -0
  16. wl_cli/commands/buildcommands/__init__.py +1 -0
  17. wl_cli/commands/buildcommands/build.py +71 -0
  18. wl_cli/commands/buildcommands/build_utils.py +47 -0
  19. wl_cli/commands/buildcommands/builders/__init__.py +18 -0
  20. wl_cli/commands/buildcommands/builders/cleanup.py +35 -0
  21. wl_cli/commands/buildcommands/builders/coordinator.py +122 -0
  22. wl_cli/commands/buildcommands/builders/environment.py +81 -0
  23. wl_cli/commands/buildcommands/builders/rust_builder.py +39 -0
  24. wl_cli/commands/buildcommands/builders/stubs.py +58 -0
  25. wl_cli/commands/buildcommands/dist.py +326 -0
  26. wl_cli/commands/buildcommands/test.py +89 -0
  27. wl_cli/commands/clean/__init__.py +1 -0
  28. wl_cli/commands/clean/build.py +151 -0
  29. wl_cli/commands/clean/clean.py +294 -0
  30. wl_cli/commands/clean/clean_utils.py +77 -0
  31. wl_cli/commands/clean/core.py +236 -0
  32. wl_cli/commands/clean/dead_code/__init__.py +0 -0
  33. wl_cli/commands/clean/dead_code/dead_code_command.py +70 -0
  34. wl_cli/commands/clean/dead_code/detector.py +161 -0
  35. wl_cli/commands/clean/dead_code/fixer.py +176 -0
  36. wl_cli/commands/clean/dead_code/reporter.py +295 -0
  37. wl_cli/commands/clean/dead_code/utils.py +0 -0
  38. wl_cli/commands/clean/lfs.py +215 -0
  39. wl_cli/commands/clean/python.py +81 -0
  40. wl_cli/commands/clean/rust.py +91 -0
  41. wl_cli/commands/clean/utils/__init__.py +34 -0
  42. wl_cli/commands/clean/venv.py +91 -0
  43. wl_cli/commands/cleanup.py +73 -0
  44. wl_cli/commands/click_base.py +79 -0
  45. wl_cli/commands/config/__init__.py +1 -0
  46. wl_cli/commands/config/config.py +291 -0
  47. wl_cli/commands/debug/__init__.py +6 -0
  48. wl_cli/commands/debug/debug.py +309 -0
  49. wl_cli/commands/debug/debug_cache.py +86 -0
  50. wl_cli/commands/debug/debug_subagent.py +156 -0
  51. wl_cli/commands/debug/output_formatter.py +165 -0
  52. wl_cli/commands/debug/source_hasher.py +92 -0
  53. wl_cli/commands/format/__init__.py +13 -0
  54. wl_cli/commands/format/format_cache_adapter.py +125 -0
  55. wl_cli/commands/format/format_command.py +197 -0
  56. wl_cli/commands/format/format_coordinator.py +191 -0
  57. wl_cli/commands/format/format_executor.py +470 -0
  58. wl_cli/commands/format/format_outcome.py +44 -0
  59. wl_cli/commands/format/format_path_handler.py +1016 -0
  60. wl_cli/commands/format/format_progress.py +62 -0
  61. wl_cli/commands/format/format_reporter.py +31 -0
  62. wl_cli/commands/format/python_formatter.py +506 -0
  63. wl_cli/commands/format/rust_formatter.py +106 -0
  64. wl_cli/commands/gate/__init__.py +1 -0
  65. wl_cli/commands/gate/gate_command.py +862 -0
  66. wl_cli/commands/gate/preflight.py +445 -0
  67. wl_cli/commands/git/__init__.py +1 -0
  68. wl_cli/commands/git/config.py +169 -0
  69. wl_cli/commands/git/git_facts.py +60 -0
  70. wl_cli/commands/git/git_flow.py +86 -0
  71. wl_cli/commands/git/hook.py +112 -0
  72. wl_cli/commands/git/policy.py +216 -0
  73. wl_cli/commands/git/push_update.py +82 -0
  74. wl_cli/commands/initenv/__init__.py +1 -0
  75. wl_cli/commands/initenv/common/__init__.py +23 -0
  76. wl_cli/commands/initenv/common/check_git_bash.py +78 -0
  77. wl_cli/commands/initenv/common/error_handler.py +68 -0
  78. wl_cli/commands/initenv/common/exceptions.py +52 -0
  79. wl_cli/commands/initenv/components/__init__.py +4 -0
  80. wl_cli/commands/initenv/components/cargo_sync.py +240 -0
  81. wl_cli/commands/initenv/components/config_manager.py +106 -0
  82. wl_cli/commands/initenv/components/directory_utils.py +71 -0
  83. wl_cli/commands/initenv/components/git/__init__.py +9 -0
  84. wl_cli/commands/initenv/components/git/branch_creator.py +143 -0
  85. wl_cli/commands/initenv/components/git/git_flow_interface.py +24 -0
  86. wl_cli/commands/initenv/components/git/git_flow_manager.py +159 -0
  87. wl_cli/commands/initenv/components/git/git_lfs_manager.py +272 -0
  88. wl_cli/commands/initenv/components/git/git_utility.py +254 -0
  89. wl_cli/commands/initenv/components/git/gitignore_manager.py +53 -0
  90. wl_cli/commands/initenv/components/git_initializer.py +212 -0
  91. wl_cli/commands/initenv/components/i18n_manager.py +104 -0
  92. wl_cli/commands/initenv/components/log_manager.py +241 -0
  93. wl_cli/commands/initenv/components/project_structure/__init__.py +23 -0
  94. wl_cli/commands/initenv/components/project_structure/config_generator.py +360 -0
  95. wl_cli/commands/initenv/components/project_structure/directory_creator.py +184 -0
  96. wl_cli/commands/initenv/components/project_structure/hook_source.py +110 -0
  97. wl_cli/commands/initenv/components/project_structure/hooks_manager.py +995 -0
  98. wl_cli/commands/initenv/components/project_structure/readme_handler.py +56 -0
  99. wl_cli/commands/initenv/components/project_structure/rust_files_handler.py +30 -0
  100. wl_cli/commands/initenv/components/project_structure/setup_handler.py +103 -0
  101. wl_cli/commands/initenv/components/project_structure/sop_generator.py +73 -0
  102. wl_cli/commands/initenv/components/project_structure/template_copier.py +104 -0
  103. wl_cli/commands/initenv/components/project_structure/template_manager.py +195 -0
  104. wl_cli/commands/initenv/components/pyproject_generator.py +194 -0
  105. wl_cli/commands/initenv/components/rust_initializer.py +145 -0
  106. wl_cli/commands/initenv/components/symlink_cleaner.py +99 -0
  107. wl_cli/commands/initenv/components/symlink_manager.py +52 -0
  108. wl_cli/commands/initenv/components/venv_manager.py +118 -0
  109. wl_cli/commands/initenv/initenv.py +169 -0
  110. wl_cli/commands/initenv/services/__init__.py +11 -0
  111. wl_cli/commands/initenv/services/environment_initializer.py +288 -0
  112. wl_cli/commands/initenv/services/git_lfs_initializer.py +26 -0
  113. wl_cli/commands/initenv/services/project_structure_initializer.py +52 -0
  114. wl_cli/commands/lfs/README.md +221 -0
  115. wl_cli/commands/lfs/__init__.py +5 -0
  116. wl_cli/commands/lfs/cli.py +106 -0
  117. wl_cli/commands/lfs/config.py +164 -0
  118. wl_cli/commands/lfs/hook.py +61 -0
  119. wl_cli/commands/lfs/initializer.py +531 -0
  120. wl_cli/commands/lfs/manager.py +95 -0
  121. wl_cli/commands/lfs/policy.py +165 -0
  122. wl_cli/commands/lfs/watcher.py +294 -0
  123. wl_cli/commands/lint/__init__.py +1 -0
  124. wl_cli/commands/lint/lint_command.py +297 -0
  125. wl_cli/commands/lint/lint_executor.py +409 -0
  126. wl_cli/commands/lint/lint_reporter.py +579 -0
  127. wl_cli/commands/lint/lint_run.py +258 -0
  128. wl_cli/commands/lint/mypy_config.py +90 -0
  129. wl_cli/commands/registry.py +103 -0
  130. wl_cli/commands/self/__init__.py +7 -0
  131. wl_cli/commands/self/common/__init__.py +8 -0
  132. wl_cli/commands/self/common/capabilities.py +30 -0
  133. wl_cli/commands/self/common/config.py +27 -0
  134. wl_cli/commands/self/common/version_cache.py +85 -0
  135. wl_cli/commands/self/handlers/__init__.py +19 -0
  136. wl_cli/commands/self/handlers/base_handler.py +28 -0
  137. wl_cli/commands/self/handlers/install_handler.py +85 -0
  138. wl_cli/commands/self/handlers/status_handler.py +66 -0
  139. wl_cli/commands/self/handlers/uninstall_handler.py +58 -0
  140. wl_cli/commands/self/handlers/update_handler.py +102 -0
  141. wl_cli/commands/self/handlers/version_handler.py +16 -0
  142. wl_cli/commands/self/self_command.py +237 -0
  143. wl_cli/commands/self/services/__init__.py +15 -0
  144. wl_cli/commands/self/services/installation_manager.py +187 -0
  145. wl_cli/commands/self/services/local_installer.py +165 -0
  146. wl_cli/commands/self/services/pypi_updater.py +278 -0
  147. wl_cli/commands/self/services/windows_update.py +174 -0
  148. wl_cli/commands/testcommands/__init__.py +8 -0
  149. wl_cli/commands/testcommands/base_dependency_analyzer.py +198 -0
  150. wl_cli/commands/testcommands/batch_processor.py +278 -0
  151. wl_cli/commands/testcommands/cache_manager.py +1156 -0
  152. wl_cli/commands/testcommands/coverage_analyzer.py +955 -0
  153. wl_cli/commands/testcommands/coverage_cache_manager.py +183 -0
  154. wl_cli/commands/testcommands/progress_manager.py +79 -0
  155. wl_cli/commands/testcommands/python_env_detector.py +207 -0
  156. wl_cli/commands/testcommands/report_generator.py +101 -0
  157. wl_cli/commands/testcommands/single_test_executor.py +250 -0
  158. wl_cli/commands/testcommands/speed_checker.py +118 -0
  159. wl_cli/commands/testcommands/test_command_builder.py +165 -0
  160. wl_cli/commands/testcommands/test_commands.py +786 -0
  161. wl_cli/commands/testcommands/test_reporter.py +181 -0
  162. wl_cli/commands/testcommands/test_result_handler.py +96 -0
  163. wl_cli/commands/testcommands/test_runner.py +1343 -0
  164. wl_cli/commands/testcommands/test_utils.py +197 -0
  165. wl_cli/commands/testcommands/utils/__init__.py +17 -0
  166. wl_cli/commands/testcommands/utils/coverage_parser.py +98 -0
  167. wl_cli/commands/testcommands/utils/env_info.py +36 -0
  168. wl_cli/commands/testcommands/utils/junit_parser.py +75 -0
  169. wl_cli/commands/testcommands/utils/markdown_utils.py +20 -0
  170. wl_cli/commands/testcommands/utils/project_root.py +61 -0
  171. wl_cli/commands/update/__init__.py +9 -0
  172. wl_cli/commands/update/services/__init__.py +21 -0
  173. wl_cli/commands/update/services/backup_manager.py +207 -0
  174. wl_cli/commands/update/services/component_manager.py +83 -0
  175. wl_cli/commands/update/services/conflict_resolver.py +189 -0
  176. wl_cli/commands/update/services/vendors_updater.py +85 -0
  177. wl_cli/commands/update/services/version_tracker.py +127 -0
  178. wl_cli/commands/update/update_command.py +270 -0
  179. wl_cli/commands/update/utils/__init__.py +3 -0
  180. wl_cli/commands/version/__init__.py +9 -0
  181. wl_cli/commands/version/version_command.py +155 -0
  182. wl_cli/commands/workflow.py +477 -0
  183. wl_cli/env/__init__.py +5 -0
  184. wl_cli/env/cli.py +167 -0
  185. wl_cli/env_status.py +157 -0
  186. wl_cli/envelope.py +91 -0
  187. wl_cli/exceptions.py +85 -0
  188. wl_cli/lib/__init__.py +3 -0
  189. wl_cli/lib/rust_utils.py +35 -0
  190. wl_cli/mcp/__init__.py +6 -0
  191. wl_cli/mcp/capabilities.py +19 -0
  192. wl_cli/mcp/cli.py +22 -0
  193. wl_cli/mcp/gitlab.py +553 -0
  194. wl_cli/mcp/k8s.py +344 -0
  195. wl_cli/mcp/protocol.py +159 -0
  196. wl_cli/platform.py +139 -0
  197. wl_cli/quality/__init__.py +18 -0
  198. wl_cli/utils/__init__.py +7 -0
  199. wl_cli/utils/cache.py +491 -0
  200. wl_cli/utils/cache_ledger.py +355 -0
  201. wl_cli/utils/cache_store.py +546 -0
  202. wl_cli/utils/config/__init__.py +27 -0
  203. wl_cli/utils/config/config_loader.py +163 -0
  204. wl_cli/utils/config/core.py +862 -0
  205. wl_cli/utils/config/defaults.py +142 -0
  206. wl_cli/utils/config/env_exporter.py +86 -0
  207. wl_cli/utils/config/env_parser.py +108 -0
  208. wl_cli/utils/config/validation.py +280 -0
  209. wl_cli/utils/error_handler.py +165 -0
  210. wl_cli/utils/file_copier.py +56 -0
  211. wl_cli/utils/file_operations.py +506 -0
  212. wl_cli/utils/language_utils.py +122 -0
  213. wl_cli/utils/lazy_import.py +240 -0
  214. wl_cli/utils/log_levels.py +35 -0
  215. wl_cli/utils/log_rotators.py +74 -0
  216. wl_cli/utils/logging.py +458 -0
  217. wl_cli/utils/output_context.py +142 -0
  218. wl_cli/utils/performance/__init__.py +1 -0
  219. wl_cli/utils/performance/decorators.py +111 -0
  220. wl_cli/utils/performance/exporter.py +38 -0
  221. wl_cli/utils/performance/models.py +16 -0
  222. wl_cli/utils/performance/monitor.py +161 -0
  223. wl_cli/utils/performance_monitor.py +32 -0
  224. wl_cli/utils/platform_adapter.py +131 -0
  225. wl_cli/utils/pre_commit.py +266 -0
  226. wl_cli/utils/pre_commit_config.py +72 -0
  227. wl_cli/utils/root.py +521 -0
  228. wl_cli/utils/root_fallback.py +256 -0
  229. wl_cli/utils/run_evidence.py +236 -0
  230. wl_cli/utils/structured_logger.py +18 -0
  231. wl_cli/utils/structured_logging/__init__.py +19 -0
  232. wl_cli/utils/structured_logging/config.py +337 -0
  233. wl_cli/utils/structured_logging/core.py +177 -0
  234. wl_cli/utils/structured_logging/env.py +56 -0
  235. wl_cli/utils/structured_logging/filtering.py +75 -0
  236. wl_cli/utils/structured_logging/filters.py +55 -0
  237. wl_cli/utils/structured_logging/formatters.py +251 -0
  238. wl_cli/utils/structured_logging/handlers.py +308 -0
  239. wl_cli/utils/structured_logging/logger.py +354 -0
  240. wl_cli/utils/subprocess.py +71 -0
  241. wl_cli/utils/subprocess_utils.py +260 -0
  242. wl_cli/utils/test_utils/__init__.py +5 -0
  243. wl_cli/utils/test_utils/test_filter.py +36 -0
  244. wl_cli/utils/uv_run.py +79 -0
  245. wl_cli/utils/uv_tool.py +117 -0
  246. wl_cli/utils/uv_tree.py +71 -0
  247. wl_cli/utils/uv_workspace.py +174 -0
  248. wl_cli/utils/version/__init__.py +15 -0
  249. wl_cli/utils/version/pypi_version_checker.py +95 -0
  250. wl_cli/utils/version/version_comparator.py +133 -0
  251. wl_cli/utils/version/version_detectors.py +312 -0
  252. wl_cli/utils/version/version_service.py +131 -0
  253. wl_cli/utils/version/version_updater.py +198 -0
  254. wl_cli/utils/wl_dir_updater.py +1440 -0
  255. wl_cli/utils/workspace_detector/__init__.py +28 -0
  256. wl_cli/utils/workspace_detector/core.py +161 -0
  257. wl_cli/utils/workspace_detector/detection_strategies.py +107 -0
  258. wl_cli/utils/workspace_detector/exceptions.py +5 -0
  259. wl_cli/utils/workspace_detector/types.py +17 -0
  260. wl_cli/utils/workspace_detector/venv_resolver.py +84 -0
  261. wl_cli/vendors/config/.codespellrc +2 -0
  262. wl_cli/vendors/config/dependencies.toml +10 -0
  263. wl_cli/vendors/config/mypy.ini +62 -0
  264. wl_cli/vendors/config/pyproject_defaults.toml +28 -0
  265. wl_cli/vendors/config/template_config.toml +24 -0
  266. wl_cli/vendors/config/tool_configs.toml +76 -0
  267. wl_cli/vendors/git/.gitignore +122 -0
  268. wl_cli/vendors/git/.pre-commit-config.yaml +37 -0
  269. wl_cli/vendors/hooks/applypatch-msg.sample +15 -0
  270. wl_cli/vendors/hooks/commit-msg.sample +24 -0
  271. wl_cli/vendors/hooks/fsmonitor-watchman.sample +174 -0
  272. wl_cli/vendors/hooks/post-update.sample +8 -0
  273. wl_cli/vendors/hooks/pre-applypatch.sample +14 -0
  274. wl_cli/vendors/hooks/pre-commit +45 -0
  275. wl_cli/vendors/hooks/pre-commit.legacy +31 -0
  276. wl_cli/vendors/hooks/pre-commit.sample +49 -0
  277. wl_cli/vendors/hooks/pre-merge-commit.sample +13 -0
  278. wl_cli/vendors/hooks/pre-push.sample +60 -0
  279. wl_cli/vendors/hooks/pre-rebase.sample +169 -0
  280. wl_cli/vendors/hooks/pre-receive.sample +24 -0
  281. wl_cli/vendors/hooks/prepare-commit-msg.sample +42 -0
  282. wl_cli/vendors/hooks/push-to-checkout.sample +78 -0
  283. wl_cli/vendors/hooks/sendemail-validate.sample +77 -0
  284. wl_cli/vendors/hooks/unix/applypatch-msg.sample +15 -0
  285. wl_cli/vendors/hooks/unix/check_lockfile.py +58 -0
  286. wl_cli/vendors/hooks/unix/check_lockfile.sh +36 -0
  287. wl_cli/vendors/hooks/unix/commit-msg.sample +24 -0
  288. wl_cli/vendors/hooks/unix/fsmonitor-watchman.sample +174 -0
  289. wl_cli/vendors/hooks/unix/post-update.sample +8 -0
  290. wl_cli/vendors/hooks/unix/pre-applypatch.sample +14 -0
  291. wl_cli/vendors/hooks/unix/pre-commit +190 -0
  292. wl_cli/vendors/hooks/unix/pre-commit.legacy +31 -0
  293. wl_cli/vendors/hooks/unix/pre-commit.py +81 -0
  294. wl_cli/vendors/hooks/unix/pre-commit.sample +49 -0
  295. wl_cli/vendors/hooks/unix/pre-merge-commit.sample +13 -0
  296. wl_cli/vendors/hooks/unix/pre-push +417 -0
  297. wl_cli/vendors/hooks/unix/pre-push.sample +53 -0
  298. wl_cli/vendors/hooks/unix/pre-rebase.sample +169 -0
  299. wl_cli/vendors/hooks/unix/pre-receive.sample +24 -0
  300. wl_cli/vendors/hooks/unix/prepare-commit-msg.sample +42 -0
  301. wl_cli/vendors/hooks/update.sample +128 -0
  302. wl_cli/vendors/hooks/win/work.bat +63 -0
  303. wl_cli/vendors/project/.gitignore +90 -0
  304. wl_cli/vendors/project/.python-version +1 -0
  305. wl_cli/vendors/project/LICENSE +21 -0
  306. wl_cli/vendors/project/README.md +0 -0
  307. wl_cli/vendors/project/pyproject.toml +27 -0
  308. wl_cli/vendors/project/pyproject_no_workspace.toml +16 -0
  309. wl_cli/vendors/project/pyproject_workspace.toml +14 -0
  310. wl_cli/vendors/readme/README.md +120 -0
  311. wl_cli/vendors/rust/Cargo.toml +14 -0
  312. wl_cli/vendors/rust/lib/__init__.py +3 -0
  313. wl_cli/vendors/rust/lib/rust_utils.py +31 -0
  314. wl_cli/vendors/rust/src/lib.rs +13 -0
  315. wl_cli/vendors/rust/src/main.rs +3 -0
  316. wl_cli/vendors/rust/tests/test_rust_fallback.py +40 -0
  317. wl_cli/vendors/sop_doc/git-dev-sop.md +220 -0
  318. wl_cli/vendors/sop_doc/industrial-standard-sop.md +145 -0
  319. wl_cli/vendors/sop_doc/python-sop.md +342 -0
  320. wl_cli/vendors/sop_doc/rust-readme.md +219 -0
@@ -0,0 +1,187 @@
1
+ """
2
+ Installation manager for self update command.
3
+ """
4
+
5
+ import os
6
+ import shutil
7
+ import subprocess
8
+ import sys
9
+
10
+ from ....utils.platform_adapter import PlatformAdapter
11
+
12
+
13
+ class InstallationManager:
14
+ """Manager for handling installation-related functionality."""
15
+
16
+ def prepare_environment(self) -> tuple[str, dict]:
17
+ """
18
+ Prepare environment variables and find uv executable.
19
+ """
20
+
21
+ env = os.environ.copy()
22
+
23
+ if PlatformAdapter.is_windows():
24
+ env["PYTHONIOENCODING"] = "utf-8"
25
+ env["PYTHONLEGACYWINDOWSFSENCODING"] = "1"
26
+
27
+ uv_path = shutil.which("uv")
28
+ if uv_path is None:
29
+ raise FileNotFoundError("uv command not found in PATH")
30
+
31
+ return uv_path, env
32
+
33
+ def uninstall_existing(self, uv_path: str, env: dict) -> None:
34
+ """
35
+ Uninstall existing wl_cli (ignore failure if not installed).
36
+ """
37
+ from wl_cli.utils.logging import log_info_pair
38
+
39
+ log_info_pair("Uninstalling existing wl_cli...", "正在卸载现有wl_cli...")
40
+
41
+ uninstall_result = subprocess.run(
42
+ [uv_path, "tool", "uninstall", "py_wlcli"],
43
+ check=False,
44
+ capture_output=True,
45
+ text=True,
46
+ encoding="utf-8" if PlatformAdapter.is_windows() else None,
47
+ env=env,
48
+ )
49
+
50
+ if uninstall_result.returncode != 0:
51
+ log_info_pair(
52
+ "Note: wl_cli was not installed or failed to uninstall. Continuing with installation.",
53
+ "注意:wl_cli未安装或卸载失败。继续安装。",
54
+ )
55
+ else:
56
+ log_info_pair(
57
+ "Successfully uninstalled existing wl_cli.", "成功卸载现有wl_cli。"
58
+ )
59
+
60
+ def is_file_in_use_error(self, error_msg: str) -> bool:
61
+ """
62
+ Check if the error message indicates a file in use error on Windows.
63
+ """
64
+ return any(
65
+ keyword in error_msg
66
+ for keyword in [
67
+ "Access is denied",
68
+ "无法访问文件",
69
+ "The process cannot access the file",
70
+ ]
71
+ )
72
+
73
+ def detect_installation_method(self) -> str:
74
+ """
75
+ Detect how the current wl command is installed.
76
+
77
+ Returns:
78
+ str: Installation method - "uv_tool", "uv_pip", "pip", or "unknown"
79
+ """
80
+ from wl_cli.utils.logging import log_info_pair
81
+ from wl_cli.utils.uv_tool import is_running_in_uv_tool
82
+
83
+ if is_running_in_uv_tool():
84
+ log_info_pair(
85
+ "Detected installation method: uv tool", "检测到安装方式: uv tool"
86
+ )
87
+ return "uv_tool"
88
+
89
+ try:
90
+ import importlib.metadata
91
+
92
+ dist = importlib.metadata.distribution("py_wlcli")
93
+ log_info_pair(f"Detected distribution: {dist}", f"检测到发行版: {dist}")
94
+
95
+ log_info_pair(
96
+ "Detected installation method: uv pip", "检测到安装方式: uv pip"
97
+ )
98
+ return "uv_pip"
99
+ except (importlib.metadata.PackageNotFoundError, Exception) as e:
100
+ log_info_pair(
101
+ f"Warning: Could not detect installation method: {e}",
102
+ f"警告: 无法检测安装方式: {e}",
103
+ )
104
+ return "unknown"
105
+
106
+ def uninstall(self, uv_path: str, env: dict) -> None:
107
+ """
108
+ Uninstall wl command using the appropriate method.
109
+
110
+ Args:
111
+ uv_path (str): Path to the uv executable.
112
+ env (dict): Environment variables to use for uninstallation.
113
+ """
114
+ from wl_cli.utils.logging import log_info, log_info_pair
115
+
116
+ install_method = self.detect_installation_method()
117
+
118
+ try:
119
+ if install_method == "uv_tool":
120
+ cmd = [uv_path, "tool", "uninstall", "py_wlcli"]
121
+ log_info(f"Running: {' '.join(cmd)}")
122
+ subprocess.run(
123
+ cmd,
124
+ check=True,
125
+ capture_output=False,
126
+ text=True,
127
+ encoding="utf-8" if PlatformAdapter.is_windows() else None,
128
+ env=env,
129
+ )
130
+ elif install_method == "uv_pip":
131
+ cmd = [uv_path, "pip", "uninstall", "py_wlcli"]
132
+ log_info(f"Running: {' '.join(cmd)}")
133
+ subprocess.run(
134
+ cmd,
135
+ check=True,
136
+ capture_output=False,
137
+ text=True,
138
+ encoding="utf-8" if PlatformAdapter.is_windows() else None,
139
+ env=env,
140
+ )
141
+ elif install_method == "pip":
142
+ cmd = [sys.executable, "-m", "pip", "uninstall", "-y", "py_wlcli"]
143
+ log_info(f"Running: {' '.join(cmd)}")
144
+ subprocess.run(
145
+ cmd,
146
+ check=True,
147
+ capture_output=False,
148
+ text=True,
149
+ encoding="utf-8" if PlatformAdapter.is_windows() else None,
150
+ env=env,
151
+ )
152
+ else:
153
+ # Try multiple uninstall methods as fallback
154
+ log_info_pair(
155
+ "Trying multiple uninstall methods as fallback...",
156
+ "尝试多种卸载方法作为回退...",
157
+ )
158
+
159
+ # Try uv tool uninstall
160
+ tool_result = subprocess.run(
161
+ [uv_path, "tool", "uninstall", "py_wlcli"],
162
+ check=False,
163
+ capture_output=True,
164
+ text=True,
165
+ encoding="utf-8" if PlatformAdapter.is_windows() else None,
166
+ env=env,
167
+ )
168
+
169
+ # Try pip uninstall
170
+ pip_result = subprocess.run(
171
+ [uv_path, "pip", "uninstall", "py_wlcli"],
172
+ check=False,
173
+ capture_output=True,
174
+ text=True,
175
+ encoding="utf-8" if PlatformAdapter.is_windows() else None,
176
+ env=env,
177
+ )
178
+
179
+ if tool_result.returncode != 0 and pip_result.returncode != 0:
180
+ raise Exception(
181
+ f"Failed to uninstall: uv tool error: {tool_result.stderr}, pip error: {pip_result.stderr}"
182
+ )
183
+
184
+ log_info_pair("Successfully uninstalled wl command!", "成功卸载wl命令!")
185
+ except subprocess.CalledProcessError as e:
186
+ log_info_pair(f"Failed to uninstall: {e}", f"卸载失败: {e}")
187
+ raise
@@ -0,0 +1,165 @@
1
+ """
2
+ Local installer for self update command.
3
+ """
4
+
5
+ import subprocess
6
+
7
+ from wl_cli.utils.logging import log_info, log_info_pair
8
+
9
+ from ....utils.platform_adapter import PlatformAdapter
10
+ from .installation_manager import InstallationManager
11
+ from .windows_update import WindowsUpdate
12
+
13
+
14
+ class LocalInstaller:
15
+ """Installer for local installation of the wl command."""
16
+
17
+ def install(self, uv_path: str, env: dict) -> None:
18
+ """
19
+ Install the current local code (replicates reinstall.bat functionality).
20
+
21
+ Args:
22
+ uv_path (str): Path to the uv executable.
23
+ env (dict): Environment variables to use for the installation.
24
+ """
25
+ log_info_pair("Installing local wl_cli...", "正在安装本地wl_cli...")
26
+
27
+ installation_manager = InstallationManager()
28
+ installation_manager.uninstall_existing(uv_path, env)
29
+
30
+ log_info_pair(
31
+ "Installing current directory in editable mode...",
32
+ "正在以可编辑模式安装当前目录...",
33
+ )
34
+
35
+ install_cmd = [uv_path, "tool", "install", "--editable", "."]
36
+ log_info(f"Running: {' '.join(install_cmd)}")
37
+
38
+ try:
39
+ subprocess.run(
40
+ install_cmd,
41
+ check=True,
42
+ capture_output=False,
43
+ text=True,
44
+ encoding="utf-8" if PlatformAdapter.is_windows() else None,
45
+ env=env,
46
+ )
47
+ log_info_pair(
48
+ "Local installation completed successfully!", "本地安装成功完成!"
49
+ )
50
+ except subprocess.CalledProcessError as e:
51
+ log_info_pair(f"Local installation failed: {e}", f"本地安装失败: {e}")
52
+ self._try_alternative_installation(uv_path, env)
53
+
54
+ def _try_alternative_installation(self, uv_path: str, env: dict) -> None:
55
+ """
56
+ Try alternative installation method if primary fails.
57
+
58
+ Args:
59
+ uv_path (str): Path to the uv executable.
60
+ env (dict): Environment variables to use for the installation.
61
+ """
62
+ log_info_pair(
63
+ "Trying alternative installation method...", "尝试备选安装方法..."
64
+ )
65
+
66
+ alternative_cmd = [
67
+ uv_path,
68
+ "tool",
69
+ "install",
70
+ "--editable",
71
+ ".",
72
+ "--python-preference",
73
+ "only-system",
74
+ ]
75
+ log_info(f"Running: {' '.join(alternative_cmd)}")
76
+
77
+ try:
78
+ subprocess.run(
79
+ alternative_cmd,
80
+ check=True,
81
+ capture_output=False,
82
+ text=True,
83
+ encoding="utf-8" if PlatformAdapter.is_windows() else None,
84
+ env=env,
85
+ )
86
+ log_info_pair(
87
+ "Local installation completed successfully with alternative method!",
88
+ "使用备选方法本地安装成功完成!",
89
+ )
90
+ except subprocess.CalledProcessError as alt_e:
91
+ log_info_pair(
92
+ f"Alternative installation also failed: {alt_e}",
93
+ f"备选安装方法也失败: {alt_e}",
94
+ )
95
+ raise
96
+
97
+ def run_installation_attempt(
98
+ self,
99
+ uv_path: str,
100
+ env: dict,
101
+ timeout_duration: int,
102
+ use_alternative: bool = False,
103
+ ) -> bool:
104
+ """
105
+ Run a single installation attempt (primary or alternative).
106
+ Returns True if installation succeeded, False otherwise.
107
+ """
108
+ if use_alternative:
109
+ cmd = [
110
+ uv_path,
111
+ "tool",
112
+ "install",
113
+ "--editable",
114
+ ".",
115
+ "--python-preference",
116
+ "only-system",
117
+ ]
118
+ log_info_pair(
119
+ "Trying alternative installation method...", "尝试备选安装方法..."
120
+ )
121
+ else:
122
+ cmd = [uv_path, "tool", "install", "--editable", "."]
123
+
124
+ log_info(f"Running: {' '.join(cmd)}")
125
+ try:
126
+ subprocess.run(
127
+ cmd,
128
+ check=True,
129
+ capture_output=False,
130
+ text=True,
131
+ encoding="utf-8" if PlatformAdapter.is_windows() else None,
132
+ env=env,
133
+ timeout=timeout_duration,
134
+ )
135
+ return True
136
+ except subprocess.TimeoutExpired:
137
+ log_info_pair(
138
+ f"{'Alternative' if use_alternative else 'Primary'} installation timed out after {timeout_duration} seconds.",
139
+ f"{'备选' if use_alternative else '主'}安装方法在{timeout_duration}秒后超时。",
140
+ )
141
+ return False
142
+ except subprocess.CalledProcessError as e:
143
+ log_info_pair(
144
+ f"{'Alternative' if use_alternative else 'Primary'} installation failed: {e}",
145
+ f"{'备选' if use_alternative else '主'}安装方法失败: {e}",
146
+ )
147
+ installation_manager = InstallationManager()
148
+ if (
149
+ PlatformAdapter.is_windows()
150
+ and installation_manager.is_file_in_use_error(str(e))
151
+ ):
152
+ windows_update = WindowsUpdate()
153
+ windows_update.handle_access_error(uv_path)
154
+ return False
155
+ if use_alternative:
156
+ raise
157
+ return False
158
+ except Exception as e:
159
+ log_info_pair(
160
+ f"Unexpected error during {'alternative' if use_alternative else 'primary'} installation: {e}",
161
+ f"{'备选' if use_alternative else '主'}安装过程中发生意外错误: {e}",
162
+ )
163
+ if use_alternative:
164
+ raise
165
+ return False
@@ -0,0 +1,278 @@
1
+ """
2
+ PyPI updater for self update command.
3
+ """
4
+
5
+ import subprocess
6
+ import sys
7
+
8
+ from wl_cli.commands.self.common.version_cache import VersionCache
9
+ from wl_cli.commands.self.services.installation_manager import (
10
+ InstallationManager,
11
+ )
12
+
13
+ from ....utils.platform_adapter import PlatformAdapter
14
+
15
+
16
+ class PypiUpdater:
17
+ """Updater for updating the wl command from PyPI."""
18
+
19
+ def __init__(self) -> None:
20
+ """Initialize PypiUpdater with cache."""
21
+ self._cache_timeout: int = 300
22
+ # When _cache_timeout is changed from outside, the VersionCache
23
+ # still uses the original default, but that's OK since cache entry
24
+ # expiration is checked per entry when accessed
25
+ self._version_cache = VersionCache(default_timeout=self._cache_timeout)
26
+ # Allow overriding cache timeout from outside for configuration
27
+
28
+ def update(self, uv_path: str, env: dict) -> None:
29
+ """
30
+ Update wl command from PyPI using the same installation method as current.
31
+
32
+ Args:
33
+ uv_path (str): Path to the uv executable.
34
+ env (dict): Environment variables to use for the update.
35
+ """
36
+
37
+ from wl_cli.utils.logging import log_info, log_info_pair
38
+ from wl_cli.utils.version.pypi_version_checker import PyPIVersionChecker
39
+ from wl_cli.utils.version.version_comparator import VersionComparator
40
+ from wl_cli.utils.version.version_detectors import VersionDetector
41
+
42
+ installation_manager = InstallationManager()
43
+ install_method = installation_manager.detect_installation_method()
44
+
45
+ detector = VersionDetector()
46
+ checker = PyPIVersionChecker()
47
+ comparator = VersionComparator()
48
+
49
+ current_version = detector.get_current_version(comparator)
50
+ log_info_pair(
51
+ f"Current local version: {current_version}",
52
+ f"当前本地版本: {current_version}",
53
+ )
54
+
55
+ # Get PyPI version with caching
56
+ def fetch_pypi_version():
57
+ return checker._get_pypi_version()
58
+
59
+ pypi_version = self._version_cache.get_or_set(
60
+ "pypi_version", fetch_pypi_version
61
+ )
62
+ if pypi_version is None:
63
+ log_info_pair(
64
+ "Failed to get PyPI version. Skipping update.",
65
+ "无法获取PyPI版本。跳过更新。",
66
+ )
67
+ return
68
+ else:
69
+ if self._version_cache.get("pypi_version") is not None:
70
+ log_info_pair(
71
+ "Using cached PyPI version information", "使用缓存的PyPI版本信息"
72
+ )
73
+
74
+ if pypi_version is None:
75
+ log_info_pair(
76
+ "Failed to get PyPI version. Skipping update.",
77
+ "无法获取PyPI版本。跳过更新。",
78
+ )
79
+ return
80
+
81
+ log_info_pair(
82
+ f"Latest PyPI version: {pypi_version}", f"最新PyPI版本: {pypi_version}"
83
+ )
84
+
85
+ if not comparator.is_version_greater(pypi_version, current_version):
86
+ log_info_pair(
87
+ f"Local version {current_version} is already up to date with PyPI version {pypi_version}.",
88
+ f"本地版本 {current_version} 已经与PyPI版本 {pypi_version} 保持最新。",
89
+ )
90
+ return
91
+
92
+ log_info_pair(
93
+ f"Updating from version {current_version} to {pypi_version}...",
94
+ f"正在从版本 {current_version} 更新到 {pypi_version}...",
95
+ )
96
+
97
+ try:
98
+ if install_method == "uv_tool":
99
+ cmd = [uv_path, "tool", "install", "py_wlcli"]
100
+ log_info(f"Running: {' '.join(cmd)}")
101
+ subprocess.run(
102
+ cmd,
103
+ check=True,
104
+ capture_output=False,
105
+ text=True,
106
+ encoding="utf-8" if PlatformAdapter.is_windows() else None,
107
+ env=env,
108
+ )
109
+ elif install_method == "uv_pip":
110
+ cmd = [
111
+ uv_path,
112
+ "pip",
113
+ "install",
114
+ "--upgrade",
115
+ "--system",
116
+ "py_wlcli",
117
+ ]
118
+ log_info(f"Running: {' '.join(cmd)}")
119
+ subprocess.run(
120
+ cmd,
121
+ check=True,
122
+ capture_output=False,
123
+ text=True,
124
+ encoding="utf-8" if PlatformAdapter.is_windows() else None,
125
+ env=env,
126
+ )
127
+ elif install_method == "pip":
128
+ cmd = [
129
+ sys.executable,
130
+ "-m",
131
+ "pip",
132
+ "install",
133
+ "--upgrade",
134
+ "py_wlcli",
135
+ ]
136
+ log_info(f"Running: {' '.join(cmd)}")
137
+ subprocess.run(
138
+ cmd,
139
+ check=True,
140
+ capture_output=False,
141
+ text=True,
142
+ encoding="utf-8" if PlatformAdapter.is_windows() else None,
143
+ env=env,
144
+ )
145
+ else:
146
+ log_info_pair(
147
+ f"Unknown installation method: {install_method}, falling back to uv tool install.",
148
+ f"未知安装方法: {install_method},回退到uv tool安装。",
149
+ )
150
+ cmd = [uv_path, "tool", "install", "py_wlcli"]
151
+ log_info(f"Running: {' '.join(cmd)}")
152
+ subprocess.run(
153
+ cmd,
154
+ check=True,
155
+ capture_output=False,
156
+ text=True,
157
+ encoding="utf-8" if PlatformAdapter.is_windows() else None,
158
+ env=env,
159
+ )
160
+
161
+ log_info_pair("Successfully updated from PyPI!", "成功从PyPI更新!")
162
+ except subprocess.CalledProcessError as e:
163
+ log_info_pair(f"Failed to update from PyPI: {e}", f"从PyPI更新失败: {e}")
164
+ raise
165
+
166
+ def install_version(
167
+ self, uv_path: str, env: dict, version: str | None = None
168
+ ) -> None:
169
+ """
170
+ Install a specific version of wl command.
171
+
172
+ Args:
173
+ uv_path (str): Path to the uv executable.
174
+ env (dict): Environment variables to use for the installation.
175
+ version (str): Version to install (e.g., "0.2.48" or "latest").
176
+ """
177
+ from wl_cli.utils.logging import log_info, log_info_pair
178
+
179
+ installation_manager = InstallationManager()
180
+ install_method = installation_manager.detect_installation_method()
181
+
182
+ try:
183
+ if version == "latest" or version is None:
184
+ # Install latest version
185
+ package_spec = "py_wlcli"
186
+ else:
187
+ # Install specific version
188
+ package_spec = f"py_wlcli=={version}"
189
+
190
+ if install_method == "uv_tool":
191
+ cmd = [uv_path, "tool", "install", package_spec]
192
+ log_info(f"Running: {' '.join(cmd)}")
193
+ subprocess.run(
194
+ cmd,
195
+ check=True,
196
+ capture_output=False,
197
+ text=True,
198
+ encoding="utf-8" if PlatformAdapter.is_windows() else None,
199
+ env=env,
200
+ )
201
+ elif install_method == "uv_pip":
202
+ cmd = [
203
+ uv_path,
204
+ "pip",
205
+ "install",
206
+ "--system",
207
+ package_spec,
208
+ ]
209
+ log_info(f"Running: {' '.join(cmd)}")
210
+ subprocess.run(
211
+ cmd,
212
+ check=True,
213
+ capture_output=False,
214
+ text=True,
215
+ encoding="utf-8" if PlatformAdapter.is_windows() else None,
216
+ env=env,
217
+ )
218
+ elif install_method == "pip":
219
+ cmd = [
220
+ sys.executable,
221
+ "-m",
222
+ "pip",
223
+ "install",
224
+ package_spec,
225
+ ]
226
+ log_info(f"Running: {' '.join(cmd)}")
227
+ subprocess.run(
228
+ cmd,
229
+ check=True,
230
+ capture_output=False,
231
+ text=True,
232
+ encoding="utf-8" if PlatformAdapter.is_windows() else None,
233
+ env=env,
234
+ )
235
+ else:
236
+ log_info_pair(
237
+ f"Unknown installation method: {install_method}, falling back to uv tool install.",
238
+ f"未知安装方法: {install_method},回退到uv tool安装。",
239
+ )
240
+ cmd = [uv_path, "tool", "install", package_spec]
241
+ log_info(f"Running: {' '.join(cmd)}")
242
+ subprocess.run(
243
+ cmd,
244
+ check=True,
245
+ capture_output=False,
246
+ text=True,
247
+ encoding="utf-8" if PlatformAdapter.is_windows() else None,
248
+ env=env,
249
+ )
250
+
251
+ log_info_pair(
252
+ f"Successfully installed {package_spec}!", f"成功安装 {package_spec}!"
253
+ )
254
+ except subprocess.CalledProcessError as e:
255
+ log_info_pair(f"Failed to install version: {e}", f"安装版本失败: {e}")
256
+ raise
257
+
258
+ def get_latest_version(self) -> str:
259
+ """
260
+ Get the latest version from PyPI.
261
+
262
+ Returns:
263
+ str: Latest version string from PyPI.
264
+ """
265
+ from wl_cli.utils.version.pypi_version_checker import PyPIVersionChecker
266
+
267
+ # Get PyPI version with caching
268
+ def fetch_pypi_version():
269
+ checker = PyPIVersionChecker()
270
+ return checker._get_pypi_version()
271
+
272
+ version: str | None = self._version_cache.get_or_set(
273
+ "pypi_version", fetch_pypi_version
274
+ )
275
+
276
+ if version is None:
277
+ raise Exception("Failed to get latest version from PyPI")
278
+ return version