From 7361b0654fa362e130754daf7f87d076998df266 Mon Sep 17 00:00:00 2001 From: host Date: Sat, 7 Nov 2026 19:42:07 +0000 Subject: [PATCH] =?UTF-8?q?fix(chv):=20=5Fload=5Fplaywright=20=E9=A1=BB?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=20sync=5Fplaywright()=20=E5=AE=9E=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 返回函数本身则无 __enter__,真实启动路径从未跑通过(P2 假实现 掩盖;2026-11-08 构建 #8 驱动 live 抓到)。回归测试钉死该契约。 --- chv-guest/chv-browser.py | 7 +++++-- cloud/tests/test_chv_opt_stage3.py | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/chv-guest/chv-browser.py b/chv-guest/chv-browser.py index fc88071..1866e26 100644 --- a/chv-guest/chv-browser.py +++ b/chv-guest/chv-browser.py @@ -181,7 +181,10 @@ def truncate_a11y(node, max_nodes=MAX_A11Y_NODES, def _load_playwright(): - """懒加载 playwright;缺失时报明错(不静默假装有浏览器)。""" + """懒加载 playwright;缺失时报明错(不静默假装有浏览器)。 + 返回 sync_playwright() 的上下文管理器实例(须调用, + 不能只返回 sync_playwright 函数本身——函数没有 __enter__, + 2026-11-08 构建 #8 驱动 live 抓到:真实路径从未跑通过)。""" os.environ.setdefault("PLAYWRIGHT_BROWSERS_PATH", PLAYWRIGHT_BROWSERS_PATH) try: @@ -190,7 +193,7 @@ def _load_playwright(): raise RuntimeError( "chv-browser: playwright 未装(%s),浏览器子代理不可用" % e) - return sync_playwright + return sync_playwright() def _fetch_cdp_version(port, timeout=5.0): diff --git a/cloud/tests/test_chv_opt_stage3.py b/cloud/tests/test_chv_opt_stage3.py index 9af1b6b..4fc0516 100644 --- a/cloud/tests/test_chv_opt_stage3.py +++ b/cloud/tests/test_chv_opt_stage3.py @@ -234,6 +234,28 @@ class ScreenshotTest(unittest.TestCase): class CdpAndStatusTest(unittest.TestCase): + def test_load_playwright_returns_context_manager(self): + # 2026-11-08 live 根因:_load_playwright 返回 sync_playwright + # 函数本身(无 __enter__),真实启动路径从未跑通过。 + import sys + import types + + class FakeCM: + def __enter__(self): + return self + + def __exit__(self, *exc): + return False + + fake_mod = types.ModuleType("playwright.sync_api") + fake_mod.sync_playwright = lambda: FakeCM() + with mock.patch.dict(sys.modules, + {"playwright.sync_api": fake_mod}): + cm = BROWSER._load_playwright() + self.assertTrue(hasattr(cm, "__enter__")) + self.assertTrue(hasattr(cm, "__exit__")) + self.assertIsInstance(cm, FakeCM) + def test_cdp_returns_websocket_url(self): with tempfile.TemporaryDirectory() as tmp: agent, _c, _h = _agent([FakePage()], tmp)