diff --git a/docs/superpowers/plans/2026-07-07-wabot-modernization.md b/docs/superpowers/plans/2026-07-07-wabot-modernization.md index 66def02..9b58327 100644 --- a/docs/superpowers/plans/2026-07-07-wabot-modernization.md +++ b/docs/superpowers/plans/2026-07-07-wabot-modernization.md @@ -50,7 +50,7 @@ wabot/ (repo root, branch feature/modernization │ │ # ReattachingRemote, attach │ ├── fields.py # PageObject, TextField, SelectField, CheckField, NullField │ ├── page.py # Page (element maps, click, forms, alerts, waits) -│ ├── browser.py # Browser facade +│ ├── _browser.py # Browser facade (underscored to avoid shadowing browser()) │ └── screenshot.py # save_full_page (firefox raw cmd / chromium stitch) ├── tests/ │ ├── unit/ # test_pacing.py, test_sessions.py, test_hosts.py, @@ -2442,10 +2442,10 @@ git commit -m "feat: full-page screenshots (firefox raw command, chromium PIL st --- -### Task 11: `browser.py` — the Browser facade +### Task 11: `_browser.py` — the Browser facade **Files:** -- Create: `src/wabot/browser.py` +- Create: `src/wabot/_browser.py` - Test: `tests/unit/test_browser.py` Naming note: the spec says "`reset_good_status()` retained"; under the approved free-API-redesign decision it is renamed to the cleaner `reset()` (same semantics: clears the broken-state flag). This is the only intentional naming deviation from the spec. @@ -2461,7 +2461,7 @@ from unittest.mock import MagicMock import pytest from selenium.common.exceptions import WebDriverException -from wabot.browser import Browser +from wabot._browser import Browser from wabot.pacing import HumanPacing, NoPacing from wabot.page import Page @@ -2581,7 +2581,7 @@ class TestQuit: driver.quit.assert_called_once_with() def test_quit_stops_managed_service(self, monkeypatch): - import wabot.browser as browser_mod + import wabot._browser as browser_mod stopped = [] monkeypatch.setattr(browser_mod, "stop_service", lambda pid: stopped.append(pid)) @@ -2593,7 +2593,7 @@ class TestQuit: store.remove.assert_called_once_with("s1") def test_quit_removes_record_even_if_driver_quit_raises(self, monkeypatch): - import wabot.browser as browser_mod + import wabot._browser as browser_mod monkeypatch.setattr(browser_mod, "stop_service", lambda pid: None) driver, store = MagicMock(), MagicMock() @@ -2612,7 +2612,7 @@ Expected: FAIL — `ModuleNotFoundError: No module named 'wabot.browser'` - [ ] **Step 3: Write the implementation** -`src/wabot/browser.py`: +`src/wabot/_browser.py`: ```python """Browser: the facade consumers drive. @@ -2751,7 +2751,7 @@ Expected: 19 passed - [ ] **Step 5: Commit** ```bash -git add src/wabot/browser.py tests/unit/test_browser.py +git add src/wabot/_browser.py tests/unit/test_browser.py git commit -m "feat: Browser facade with working refuse-after-exception guard" ``` @@ -3029,7 +3029,7 @@ from datetime import datetime, timezone from selenium import webdriver from selenium.common.exceptions import WebDriverException -from .browser import Browser +from ._browser import Browser from .fields import CheckField, NullField, PageObject, SelectField, TextField from .hosts import ( ExternalServer, @@ -3878,7 +3878,7 @@ Tooling: uv (`uv sync`), pytest (`uv run pytest`; real-browser suite via Selenium 4 (pinned <5: session reattach relies on stable 4.x internals). ``` -In the Architecture section: `BrowserProxy` → `Browser` (`src/wabot/browser.py`), add `pacing.py` (HumanPacing/NoPacing policies), and replace the dill/pickle description with: sessions persist as JSON records `(executor_url, session_id)` in `platformdirs.user_data_dir("wabot")/sessions.json`; `hosts.py` spawns detached driver services (`ManagedService`) or connects to external ones (`ExternalServer`); `ReattachingRemote.start_session` adopts the saved session id. Delete the Gotchas bullets about `LOGGER.trace`, undeclared deps, and star exports (all fixed); keep the Gotcha that external hosts must outlive Python and add: geckodriver reports `ready:false` while its single session is active, so liveness checks ignore the `ready` flag. +In the Architecture section: `BrowserProxy` → `Browser` (`src/wabot/_browser.py`), add `pacing.py` (HumanPacing/NoPacing policies), and replace the dill/pickle description with: sessions persist as JSON records `(executor_url, session_id)` in `platformdirs.user_data_dir("wabot")/sessions.json`; `hosts.py` spawns detached driver services (`ManagedService`) or connects to external ones (`ExternalServer`); `ReattachingRemote.start_session` adopts the saved session id. Delete the Gotchas bullets about `LOGGER.trace`, undeclared deps, and star exports (all fixed); keep the Gotcha that external hosts must outlive Python and add: geckodriver reports `ready:false` while its single session is active, so liveness checks ignore the `ready` flag. - [ ] **Step 3: Full verification** diff --git a/src/wabot/__init__.py b/src/wabot/__init__.py index 2d53a7d..9aa32b1 100644 --- a/src/wabot/__init__.py +++ b/src/wabot/__init__.py @@ -18,7 +18,7 @@ from datetime import datetime, timezone from selenium import webdriver from selenium.common.exceptions import WebDriverException -from .browser import Browser +from ._browser import Browser from .fields import CheckField, NullField, PageObject, SelectField, TextField from .hosts import ( ExternalServer, diff --git a/src/wabot/browser.py b/src/wabot/_browser.py similarity index 100% rename from src/wabot/browser.py rename to src/wabot/_browser.py diff --git a/tests/unit/test_browser.py b/tests/unit/test_browser.py index eae72e9..c6cab80 100644 --- a/tests/unit/test_browser.py +++ b/tests/unit/test_browser.py @@ -4,7 +4,7 @@ from unittest.mock import MagicMock import pytest from selenium.common.exceptions import WebDriverException -from wabot.browser import Browser +from wabot._browser import Browser from wabot.pacing import HumanPacing, NoPacing from wabot.page import Page @@ -124,15 +124,7 @@ class TestQuit: driver.quit.assert_called_once_with() def test_quit_stops_managed_service(self, monkeypatch): - import sys - - # NOTE(Task 12 wiring fix): `import wabot.browser as browser_mod` would - # resolve to `wabot.browser` the FUNCTION (the public API added in - # Task 12 shadows the submodule attribute of the same name on the - # `wabot` package once `wabot/__init__.py` defines `def browser(...)`). - # sys.modules keeps the real submodule reachable regardless of what - # the package's own `browser` attribute is rebound to. - browser_mod = sys.modules["wabot.browser"] + import wabot._browser as browser_mod stopped = [] monkeypatch.setattr(browser_mod, "stop_service", lambda pid: stopped.append(pid)) @@ -144,10 +136,7 @@ class TestQuit: store.remove.assert_called_once_with("s1") def test_quit_removes_record_even_if_driver_quit_raises(self, monkeypatch): - import sys - - # see NOTE in test_quit_stops_managed_service above - browser_mod = sys.modules["wabot.browser"] + import wabot._browser as browser_mod monkeypatch.setattr(browser_mod, "stop_service", lambda pid: None) driver, store = MagicMock(), MagicMock()