From b69193b9e39a9ed6ecb4d532c88551d6f40e4ee1 Mon Sep 17 00:00:00 2001 From: YourGitHubUsername Date: Sun, 2 Aug 2026 23:11:00 +0530 Subject: [PATCH 1/2] Refactor Slice B exception handlers to use specific Playwright errors (#46) - Narrowed broad `except Exception:` blocks in lines 653-943 of `moon_extract.py`. - Locally imported `PlaywrightError` and `PlaywrightTimeoutError` inside `on_route` and `_extract_datanodes_on_context` to preserve deferred Playwright initialization. - Replaced broad swallows in popups/page close `finally` blocks with `(PlaywrightError, Exception)`. - Updated `_cdp_alive` to catch specific urllib/JSON errors instead of a broad Exception. --- moon_extract.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/moon_extract.py b/moon_extract.py index cf9c6fd..7903f18 100644 --- a/moon_extract.py +++ b/moon_extract.py @@ -626,6 +626,8 @@ async def _extract_datanodes_on_context(context, url: str, unopened gate, or an unsolved Turnstile. Called by extract_datanodes(), which owns lane acquisition — this half owns only the page-level flow. """ + from playwright.async_api import Error as PlaywrightError, TimeoutError as PlaywrightTimeoutError + page = await context.new_page() captured = asyncio.Event() holder: list[str] = [] @@ -649,6 +651,7 @@ def _take(candidate: str) -> bool: return False async def on_route(route): + from playwright.async_api import Error as PlaywrightError req = route.request u, rt = req.url, req.resource_type try: @@ -662,8 +665,8 @@ async def on_route(route): await route.abort() return await route.continue_() - except Exception: - pass + except (PlaywrightError, Exception): + pass # Ignore route abort/continue race conditions during request interception await page.route("**/*", on_route) # A same-tab navigation, a popup, or a real download event also carries the @@ -722,8 +725,8 @@ def _on_popup(pop): try: await page.wait_for_load_state("domcontentloaded", timeout=25000) - except Exception: - pass + except (PlaywrightError, PlaywrightTimeoutError): + pass # Ignore page navigation/load timeout; proceed with DOM evaluation. if await _dn_eval(page, DN_DEAD_JS, default=False): return None, None @@ -751,8 +754,8 @@ def _on_popup(pop): ad = my_popups.pop() try: await ad.close() - except Exception: - pass + except (PlaywrightError, Exception): + pass # Ignore error if ad popup page is already closed or destroyed. last_sweep = now st = await _dn_eval(page, DN_STEP2_JS) @@ -803,7 +806,8 @@ def _on_popup(pop): try: await page.reload(wait_until="domcontentloaded", timeout=20000) await asyncio.sleep(1.0) - except Exception: + except (PlaywrightError, PlaywrightTimeoutError): + # Return None on failure to reload page after Turnstile hard-fail. return None, None else: return None, None @@ -819,17 +823,18 @@ def _on_popup(pop): cookies_str = "; ".join(f"{c['name']}={c['value']}" for c in await context.cookies()) except Exception as e: + # Catch and log unexpected extraction errors during datanodes flow. _d("datanodes:", type(e).__name__, str(e)[:120]) finally: for ad in my_popups: try: await ad.close() - except Exception: - pass + except (PlaywrightError, Exception): + pass # Ignore error if ad popup page was already closed or detached. try: await page.close() - except Exception: - pass + except (PlaywrightError, Exception): + pass # Ignore error if datanodes page was already closed. return file_url, cookies_str @@ -965,7 +970,8 @@ def _cdp_alive(port: int) -> str | None: try: with urllib.request.urlopen(f"http://127.0.0.1:{port}/json/version", timeout=1.5) as r: return json.load(r).get("webSocketDebuggerUrl") - except Exception: + except (urllib.error.URLError, OSError, json.JSONDecodeError, KeyError): + # Handle connection failures or invalid JSON when Chrome CDP port is inactive. return None From c4d7ca6be2f46b038321576f984200a982dc0713 Mon Sep 17 00:00:00 2001 From: YourGitHubUsername Date: Sun, 2 Aug 2026 23:42:55 +0530 Subject: [PATCH 2/2] Fix exception narrowings and urllib.error import per review --- moon_extract.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/moon_extract.py b/moon_extract.py index 7903f18..1ff3a6b 100644 --- a/moon_extract.py +++ b/moon_extract.py @@ -651,7 +651,6 @@ def _take(candidate: str) -> bool: return False async def on_route(route): - from playwright.async_api import Error as PlaywrightError req = route.request u, rt = req.url, req.resource_type try: @@ -665,7 +664,7 @@ async def on_route(route): await route.abort() return await route.continue_() - except (PlaywrightError, Exception): + except PlaywrightError: pass # Ignore route abort/continue race conditions during request interception await page.route("**/*", on_route) @@ -754,7 +753,7 @@ def _on_popup(pop): ad = my_popups.pop() try: await ad.close() - except (PlaywrightError, Exception): + except PlaywrightError: pass # Ignore error if ad popup page is already closed or destroyed. last_sweep = now @@ -829,11 +828,11 @@ def _on_popup(pop): for ad in my_popups: try: await ad.close() - except (PlaywrightError, Exception): + except PlaywrightError: pass # Ignore error if ad popup page was already closed or detached. try: await page.close() - except (PlaywrightError, Exception): + except PlaywrightError: pass # Ignore error if datanodes page was already closed. return file_url, cookies_str @@ -877,6 +876,7 @@ async def extract_datanodes(browser, url: str, import shutil import subprocess import sys +import urllib.error import urllib.request CDP_PORT = int(os.environ.get("MOON_CDP_PORT", "9222"))