Skip to content

feat(selenium-devtools-py): Python Selenium adapter, and a runnable backend entry - #246

Open
vishnuv688 wants to merge 24 commits into
mainfrom
vv/selenium-python-integration
Open

feat(selenium-devtools-py): Python Selenium adapter, and a runnable backend entry#246
vishnuv688 wants to merge 24 commits into
mainfrom
vv/selenium-python-integration

Conversation

@vishnuv688

@vishnuv688 vishnuv688 commented Jul 2, 2026

Copy link
Copy Markdown
Member

What & why

Currently, DevTools supports only TypeScript and JavaScript tests, as it was originally designed for WebdriverIO. With the addition of Selenium support, we are also introducing Python support as a first step, since Selenium supports multiple languages such as Python, Java, JavaScript, and Ruby.

Type of change

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Polish (an improvement to an existing feature)
  • Breaking change (existing behavior changes for users)
  • Documentation
  • Internal (build, CI, dependencies, tooling)

Packages touched

  • shared (types and contracts)
  • core (framework-agnostic capture/reporting)
  • service (WebdriverIO adapter)
  • nightwatch-devtools (Nightwatch adapter)
  • selenium-devtools (Selenium adapter)
  • backend (server)
  • app (UI)
  • script (page-injected runtime)
  • selenium-py-devtools

Notes for reviewers

Screenshots / recordings

image

Fixes #279

Tracked under #277 (Python Selenium adapter). The remaining work is broken out as sub-issues there, one pull request each, so this PR stays scoped to the rename and the backend CLI entry.

Comment thread .github/workflows/python.yml Fixed
@vishnuv688 vishnuv688 changed the title Vv/selenium python integration Selenium python integration Aug 12, 2026
@vishnuv688 vishnuv688 changed the title Selenium python integration feat(selenium-devtools-py): Python Selenium adapter, and a runnable backend entry Aug 14, 2026
@vishnuv688
vishnuv688 marked this pull request as ready for review August 14, 2026 11:46
@greptile-apps

greptile-apps Bot commented Aug 14, 2026

Copy link
Copy Markdown

Greptile Summary

The PR introduces a Python Selenium adapter and a standalone backend CLI entry. The session-lifecycle fix handles sequential driver replacement, but still uses one rotating capture slot for simultaneously active drivers.

  • Adds Python command, metadata, BiDi, DOM snapshot, screencast, terminal, logging, pytest, and lifecycle integration.
  • Adds a runnable devtools-backend entry with CLI argument parsing and package metadata.
  • Adds Python test, release, contract-generation, documentation, and example infrastructure.

Confidence Score: 4/5

The PR is not yet safe to merge because simultaneously active Selenium drivers still overwrite and finalize one another's capture resources when their commands interleave.

The attempted session-state fix supports sequential driver replacement, but each non-skipped command compares against one global armed session and tears down that session's snapshot and screencast before arming another. The reply reports the issue as fixed, but alternating commands between two still-live drivers are a concrete residual case that repeatedly truncates capture and reattaches event handlers.

Files Needing Attention: packages/selenium-devtools-py/src/selenium_devtools/instrumentation.py, packages/selenium-devtools-py/src/selenium_devtools/bidi.py

Important Files Changed

Filename Overview
packages/selenium-devtools-py/src/selenium_devtools/instrumentation.py Implements Selenium command instrumentation and session setup, but rotating one global resource set breaks interleaved live sessions.
packages/selenium-devtools-py/src/selenium_devtools/bidi.py Adds console and network capture whose additive registrations are repeated whenever instrumentation re-arms a previously seen live driver.
packages/selenium-devtools-py/src/selenium_devtools/capturer.py Tracks metadata per session and explicitly attributes finalized screencasts, though upstream resource ownership remains global.
packages/backend/src/server.ts Adds a dedicated backend CLI entry that parses options, starts the server, reports its bound address, and exits on startup failure.
packages/backend/src/server-args.ts Adds isolated parsing for backend port, hostname, and help options.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Driver A command] --> S[Global armed_session A]
  B[Driver B command] --> R1[Finalize A resources]
  R1 --> S2[Global armed_session B]
  C[Next Driver A command] --> R2[Finalize B resources]
  R2 --> S3[Reattach and restart A resources]
Loading

Reviews (2): Last reviewed commit: "fix(selenium-devtools-py): capture every..." | Re-trigger Greptile

Comment on lines +239 to +240
if _state.get("setup_done"):
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Global state mixes Selenium sessions

When a pytest run or plain Python process creates a second WebDriver, the process-wide patch sends its commands through the same capturer while setup_done skips its metadata, BiDi, snapshot, and screencast initialization, causing later sessions to be attributed to the first session and captured incompletely; quitting either driver also clears the shared capture state.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed, and the cause was wider than the one guard. Three separate pieces of state made a second driver in the same process capture incompletely:

  • _state["setup_done"] (a boolean) skipped metadata, BiDi, snapshot and screencast bringup for every session after the first.
  • SessionCapturer._metadata_sent (also a boolean) meant the second session's id never reached the wire, so its commands were attributed to session one.
  • _on_quit tore down the shared recorder and snapshot regardless of which driver quit, and send_screencast keyed on the latest session id, so a rotated video would be filed under the wrong session.

That matters for the most ordinary pytest setup there is, a function-scoped driver fixture, where every test builds and quits a driver.

Fixed by mirroring how the JS adapters already handle this (one capturer per run, per-session bringup, scoped teardown):

  • setup_done becomes armed_session, holding the session id. Bringup re-runs whenever the id changes, and rotating closes out the previous session first: final mutation drain, snapshot dropped, screencast finalized and attributed to the session that produced it.
  • _metadata_sent becomes a set of session ids, so each session announces itself once. The dashboard already keys metadata by sessionId, so both sessions are kept rather than the first being overwritten.
  • _on_quit takes the quitting driver and returns early when another driver is the armed one, then clears armed_session so a driver created later re-arms instead of inheriting the dead session's state.
  • send_screencast accepts an explicit session_id for the already-replaced case.

Five tests added under TestMultipleSessions. Four of them fail against the old guards and pass with the fix, verified by reverting the change locally: per-session metadata, per-session BiDi attach, quitting a stale driver leaving the live one capturing, and quitting the armed driver clearing arming so the next one re-arms. Suite is 145 tests, green on 3.9 and 3.12.

One residual, deliberately not changed here: for a plain script (no test framework), the synthetic suite is still marked complete on the first quit(), so a script that drives two browsers sequentially marks its single suite done early. That only affects the no-framework path and is cosmetic in the tree; worth its own issue rather than widening this PR.

Comment on lines +265 to +271
previous = _state.get("armed_session")
if previous:
# A second driver in the same process. Close out the previous session
# before arming this one, or its video and DOM would be attributed here.
_flush_mutations(capturer)
_state["snapshot"] = None
_finalize_screencast(capturer, previous)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Live sessions replace capture state

If two live WebDrivers issue interleaved commands, every change in session_id finalizes the shared screencast, discards the shared snapshot, and reattaches BiDi handlers for a session that remains active. Alternating commands therefore truncate or discard each session's video and DOM stream and can duplicate its console and network events.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Land the Python adapter (live mode), the naming, and the backend CLI entry

2 participants