From 154344e94a1bc2ee0739da1e613e8afb6522a238 Mon Sep 17 00:00:00 2001 From: Mathew Sir Guest the best Date: Sun, 12 Jul 2026 15:37:56 -0600 Subject: [PATCH] refactor: rename sessions submodule to _sessions to avoid shadowing sessions() wabot/__init__.py defines def sessions(...) and also had a sessions.py submodule, so wabot.sessions (attribute) resolved to the function, shadowing the module for import wabot.sessions as m style access. This is the same class of collision that browser.py -> _browser.py already fixed. Renaming to _sessions.py removes the ambiguity and lets docs/api.rst drop the :no-index: Sphinx workaround in favor of plain autoclass directives on the public SessionRecord/SessionStore re-exports. --- docs/api.rst | 6 ++-- .../plans/2026-07-07-wabot-modernization.md | 29 ++++++++++--------- src/wabot/__init__.py | 2 +- src/wabot/{sessions.py => _sessions.py} | 0 tests/integration/conftest.py | 2 +- tests/integration/test_persistence.py | 2 +- tests/unit/test_api.py | 2 +- tests/unit/test_sessions.py | 4 +-- 8 files changed, 26 insertions(+), 21 deletions(-) rename src/wabot/{sessions.py => _sessions.py} (100%) diff --git a/docs/api.rst b/docs/api.rst index 75e7310..8007b85 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -16,9 +16,11 @@ API reference .. automodule:: wabot.pacing :members: -.. automodule:: wabot.sessions +.. autoclass:: wabot.SessionRecord + :members: + +.. autoclass:: wabot.SessionStore :members: - :no-index: .. automodule:: wabot.hosts :members: ExternalServer, ManagedService, attach, service_alive diff --git a/docs/superpowers/plans/2026-07-07-wabot-modernization.md b/docs/superpowers/plans/2026-07-07-wabot-modernization.md index 1ba359d..15dc6a9 100644 --- a/docs/superpowers/plans/2026-07-07-wabot-modernization.md +++ b/docs/superpowers/plans/2026-07-07-wabot-modernization.md @@ -44,7 +44,7 @@ wabot/ (repo root, branch feature/modernization ├── src/wabot/ │ ├── __init__.py # public API: browser(), sessions(), destroy(), re-exports │ ├── pacing.py # NoPacing / HumanPacing -│ ├── sessions.py # SessionRecord, SessionStore +│ ├── _sessions.py # SessionRecord, SessionStore (underscored to avoid shadowing sessions()) │ ├── hosts.py # build_options, service_alive, ExternalServer, │ │ # ManagedService, find_driver_binary, stop_service, │ │ # ReattachingRemote, attach @@ -291,10 +291,10 @@ git commit -m "feat: pacing policies (HumanPacing default behavior, NoPacing for ``` --- -### Task 3: `sessions.py` — SessionRecord + SessionStore +### Task 3: `_sessions.py` — SessionRecord + SessionStore **Files:** -- Create: `src/wabot/sessions.py` +- Create: `src/wabot/_sessions.py` - Test: `tests/unit/test_sessions.py` - [ ] **Step 1: Write the failing tests** @@ -304,7 +304,7 @@ git commit -m "feat: pacing policies (HumanPacing default behavior, NoPacing for ```python from datetime import datetime, timedelta, timezone -from wabot.sessions import SessionRecord, SessionStore +from wabot._sessions import SessionRecord, SessionStore def make_record(name="scraper1", created_at=None): @@ -367,7 +367,7 @@ class TestSessionStore: assert store.get("scraper1") is not None def test_default_path_is_under_platformdirs(self): - from wabot.sessions import default_store_path + from wabot._sessions import default_store_path assert default_store_path().name == "sessions.json" assert "wabot" in str(default_store_path()) @@ -432,11 +432,11 @@ class TestSessionStore: - [ ] **Step 2: Run tests to verify they fail** Run: `uv run pytest tests/unit/test_sessions.py -v` -Expected: FAIL — `ModuleNotFoundError: No module named 'wabot.sessions'` +Expected: FAIL — `ModuleNotFoundError: No module named 'wabot._sessions'` - [ ] **Step 3: Write the implementation** -`src/wabot/sessions.py`: +`src/wabot/_sessions.py`: ```python """Persistence of reattachable browser sessions as plain JSON. @@ -573,7 +573,7 @@ Expected: 14 passed - [ ] **Step 5: Commit** ```bash -git add src/wabot/sessions.py tests/unit/test_sessions.py +git add src/wabot/_sessions.py tests/unit/test_sessions.py git commit -m "feat: JSON SessionStore with stale eviction (replaces pickle persistence)" ``` @@ -2773,7 +2773,7 @@ from unittest.mock import MagicMock import pytest import wabot -from wabot.sessions import SessionRecord, SessionStore +from wabot import SessionRecord, SessionStore @pytest.fixture @@ -3042,7 +3042,7 @@ from .hosts import ( ) from .page import Page from .pacing import HumanPacing, NoPacing -from .sessions import SessionRecord, SessionStore +from ._sessions import SessionRecord, SessionStore __all__ = [ "Browser", @@ -3282,7 +3282,7 @@ def site_url(): @pytest.fixture def store(tmp_path): - from wabot.sessions import SessionStore + from wabot import SessionStore return SessionStore(path=tmp_path / "sessions.json") ``` @@ -3422,7 +3422,7 @@ REATTACH_SCRIPT = textwrap.dedent( """ import json, sys import wabot - from wabot.sessions import SessionStore + from wabot import SessionStore store = SessionStore(path=sys.argv[1]) bot = wabot.browser(session=sys.argv[2], pacing=wabot.NoPacing(), store=store) @@ -3807,7 +3807,10 @@ API reference .. automodule:: wabot.pacing :members: -.. automodule:: wabot.sessions +.. autoclass:: wabot.SessionRecord + :members: + +.. autoclass:: wabot.SessionStore :members: .. automodule:: wabot.hosts diff --git a/src/wabot/__init__.py b/src/wabot/__init__.py index 9aa32b1..84f3e00 100644 --- a/src/wabot/__init__.py +++ b/src/wabot/__init__.py @@ -19,6 +19,7 @@ from selenium import webdriver from selenium.common.exceptions import WebDriverException from ._browser import Browser +from ._sessions import SessionRecord, SessionStore from .fields import CheckField, NullField, PageObject, SelectField, TextField from .hosts import ( ExternalServer, @@ -31,7 +32,6 @@ from .hosts import ( ) from .pacing import HumanPacing, NoPacing from .page import Page -from .sessions import SessionRecord, SessionStore __all__ = [ "Browser", diff --git a/src/wabot/sessions.py b/src/wabot/_sessions.py similarity index 100% rename from src/wabot/sessions.py rename to src/wabot/_sessions.py diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 7a32652..b959dbe 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -36,6 +36,6 @@ def site_url(): @pytest.fixture def store(tmp_path): - from wabot.sessions import SessionStore + from wabot import SessionStore return SessionStore(path=tmp_path / "sessions.json") diff --git a/tests/integration/test_persistence.py b/tests/integration/test_persistence.py index e5739a0..cd7d3ba 100644 --- a/tests/integration/test_persistence.py +++ b/tests/integration/test_persistence.py @@ -15,7 +15,7 @@ REATTACH_SCRIPT = textwrap.dedent( """ import json, sys import wabot - from wabot.sessions import SessionStore + from wabot import SessionStore store = SessionStore(path=sys.argv[1]) bot = wabot.browser(session=sys.argv[2], pacing=wabot.NoPacing(), store=store) diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index 50893b4..28df3d6 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -3,7 +3,7 @@ from unittest.mock import MagicMock import pytest import wabot -from wabot.sessions import SessionRecord, SessionStore +from wabot import SessionRecord, SessionStore @pytest.fixture diff --git a/tests/unit/test_sessions.py b/tests/unit/test_sessions.py index c29433d..cb4e5ab 100644 --- a/tests/unit/test_sessions.py +++ b/tests/unit/test_sessions.py @@ -1,6 +1,6 @@ from datetime import datetime, timedelta, timezone -from wabot.sessions import SessionRecord, SessionStore +from wabot._sessions import SessionRecord, SessionStore def make_record(name="scraper1", created_at=None): @@ -63,7 +63,7 @@ class TestSessionStore: assert store.get("scraper1") is not None def test_default_path_is_under_platformdirs(self): - from wabot.sessions import default_store_path + from wabot._sessions import default_store_path assert default_store_path().name == "sessions.json" assert "wabot" in str(default_store_path())