fix: hide cmhub curl download window

This commit is contained in:
chengma
2026-07-08 17:49:54 +08:00
parent 6171b7cd92
commit e25d695ce7
3 changed files with 36 additions and 2 deletions
+16
View File
@@ -1452,6 +1452,21 @@ def _find_system_curl():
return ""
def _subprocess_hidden_window_kwargs():
if os.name != "nt":
return {}
creationflags = getattr(subprocess, "CREATE_NO_WINDOW", 0)
if creationflags:
return {"creationflags": creationflags}
startupinfo_cls = getattr(subprocess, "STARTUPINFO", None)
if startupinfo_cls is None:
return {}
startupinfo = startupinfo_cls()
startupinfo.dwFlags |= getattr(subprocess, "STARTF_USESHOWWINDOW", 1)
startupinfo.wShowWindow = getattr(subprocess, "SW_HIDE", 0)
return {"startupinfo": startupinfo}
def _download_cmhub_image_with_curl(
url,
connect_timeout,
@@ -1501,6 +1516,7 @@ def _download_cmhub_image_with_curl(
timeout=max(2, int(connect_timeout) + int(read_timeout) + 10),
check=False,
shell=False,
**_subprocess_hidden_window_kwargs(),
)
except (OSError, subprocess.TimeoutExpired) as exc:
raise AIError("下载 cmhub 图片失败: curl 执行失败") from exc
+5 -1
View File
@@ -3,7 +3,7 @@ id: T-561
title: cmhub curl 下载隐藏 Windows 黑窗
phase: 7
deps: [T-548, T-524]
status: TODO
status: DONE
created: 2026-07-08
---
@@ -46,3 +46,7 @@ T-548 后,②AI生成在 Windows 下默认优先调用系统 `curl.exe` 下载
## 执行记录
- 2026-07-08:`app/ai.py` 新增 `_subprocess_hidden_window_kwargs()`,Windows 下优先使用 `subprocess.CREATE_NO_WINDOW` 隐藏 curl 子进程控制台窗口;缺少该常量时兜底 `STARTUPINFO + STARTF_USESHOWWINDOW + SW_HIDE`;非 Windows 返回空参数。
- 2026-07-08:`_download_cmhub_image_with_curl()` 调用 `subprocess.run()` 时传入隐藏窗口 kwargs,保留 `shell=False`、`-K` 临时配置文件、`--noproxy`、超时、大小上限、stderr/stdout 捕获和 requests fallback 语义。
- 2026-07-08:更新 `tests/test_ai.py`,覆盖 Windows curl 调用携带隐藏窗口参数、URL 不在 argv、`shell=False` 保持,以及非 Windows 不加隐藏窗口参数。
- 验证通过:`py -3.10 -m unittest tests.test_ai.AITests.test_cmhub_image_download_uses_curl_without_url_in_argv tests.test_ai.AITests.test_cmhub_curl_hidden_window_kwargs_are_windows_only tests.test_ai.AITests.test_cmhub_image_download_falls_back_to_requests_when_curl_fails tests.test_ai.AITests.test_cmhub_image_download_auto_without_curl_uses_requests`、`python -m ruff check app tests main.py`。
+15 -1
View File
@@ -725,12 +725,15 @@ class AITests(TempDirMixin, unittest.TestCase):
self.assertIn("--noproxy", args)
self.assertEqual("*", args[args.index("--noproxy") + 1])
self.assertFalse(kwargs["shell"])
self.assertEqual(0x08000000, kwargs["creationflags"])
output_path = args[args.index("--output") + 1]
with open(output_path, "wb") as fh:
fh.write(generated_png)
return SimpleNamespace(returncode=0, stdout=b"", stderr=b"")
with mock.patch("app.ai._find_system_curl", return_value=r"C:\Windows\System32\curl.exe"), \
with mock.patch("app.ai.os.name", "nt"), \
mock.patch("app.ai.subprocess.CREATE_NO_WINDOW", 0x08000000, create=True), \
mock.patch("app.ai._find_system_curl", return_value=r"C:\Windows\System32\curl.exe"), \
mock.patch("app.ai.subprocess.run", side_effect=fake_run), \
mock.patch("app.ai.socket.getaddrinfo", return_value=public_dns):
image_bytes = ai._download_cmhub_image(
@@ -744,6 +747,17 @@ class AITests(TempDirMixin, unittest.TestCase):
self.assertEqual(generated_png, image_bytes)
self.assertEqual(1, len(calls))
def test_cmhub_curl_hidden_window_kwargs_are_windows_only(self):
with mock.patch("app.ai.os.name", "nt"), \
mock.patch("app.ai.subprocess.CREATE_NO_WINDOW", 0x08000000, create=True):
self.assertEqual(
{"creationflags": 0x08000000},
ai._subprocess_hidden_window_kwargs(),
)
with mock.patch("app.ai.os.name", "posix"):
self.assertEqual({}, ai._subprocess_hidden_window_kwargs())
def test_cmhub_image_download_skips_curl_for_private_url(self):
with mock.patch("app.ai._find_system_curl", return_value=r"C:\Windows\System32\curl.exe"), \
mock.patch("app.ai.subprocess.run") as run: