Skip to content

[py] generate the internal BiDi protocol layer from the shared binding-neutral schema#17761

Draft
titusfortner wants to merge 11 commits into
SeleniumHQ:trunkfrom
titusfortner:bidi-gen-py
Draft

[py] generate the internal BiDi protocol layer from the shared binding-neutral schema#17761
titusfortner wants to merge 11 commits into
SeleniumHQ:trunkfrom
titusfortner:bidi-gen-py

Conversation

@titusfortner

@titusfortner titusfortner commented Jul 10, 2026

Copy link
Copy Markdown
Member

🔗 Related Issues

💥 What does this PR do?

  • Adds a new internal selenium.webdriver._bidi package with code generated from the CDDL via a shared binding-neutral schema
  • Adds hand-written serialization runtime and transport seam
  • This code is additive; nothing has been re-implemented to use it yet.
  • The generated files are checked in here only so the output's shape can be reviewed — the final PR will generate them at build time rather than commit them (see Considerations).

🔧 Implementation Notes

  • The underscore namespace marks it unsupported and outside the deprecation policy; nothing is publicly exposed on the driver. The existing common/bidi code and its CDDL generator are left untouched — retiring or migrating that is out of scope.
  • Because the shared schema is already resolved (types, unions, nullability):
    • the generated methods take precisely-typed arguments, argument values are validated before being sent over the socket, and inbound results are self-validating typed objects rather than raw dicts
    • no per-construct special-casing in the generator via manifests is required
  • Records are frozen/immutable
  • Inbound validation is strict now — types, required-presence, and enum/discriminator values are all checked. The intent is to relax reactively if a real browser payload ever needs it, rather than start lenient.
  • One Transport is built per session and owned by the WebDriver (imported lazily so non-BiDi sessions don't load the generated package)
  • Regenerate the output with bazel run //py:generate-bidi-protocol. The files are committed here for review only; a follow-up will move generation to build time (see Considerations).
  • Code is properly linted & type checked

🤖 AI assistance

  • No substantial AI assistance used
  • AI assisted (complete below)
    • Tool(s): Claude Code
    • What was generated: the generator, serialization runtime, transport seam, and tests — authored and reviewed iteratively
    • I reviewed all AI output and can explain the change

💡 Additional Considerations

  • Is this the right namespace and the right way to namespace this code?
  • This draft checks in the generated output only to make its shape reviewable. Per feedback that generated code shouldn't be committed, once the shape is agreed this PR will be redone to generate at build time (not checked in). Staying in draft until then — feedback wanted on the output shape.
  • Should we relax inbound validation from the beginning? (this code is very strict with intent to relax as necessary during implementation phase)
  • Any deprecations and re-implementations will be managed in follow-up PRs

🔄 Types of changes

  • New feature (non-breaking change which adds functionality and tests!)

@selenium-ci selenium-ci added C-py Python Bindings B-build Includes scripting, bazel and CI integrations labels Jul 10, 2026
@titusfortner titusfortner marked this pull request as ready for review July 10, 2026 15:10
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Add generated internal Python BiDi protocol layer (selenium.webdriver._bidi)

✨ Enhancement 🧪 Tests ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Adds an internal selenium.webdriver._bidi package generated from the shared BiDi schema.
• Introduces a strict (de)serialization runtime plus a websocket Transport/Domain seam.
• Adds generator + drift verification target and wires verification into CI.
• Adds unit + browser round-trip tests; no existing code migrated to use it yet.
Diagram

graph TD
  Schema[("Shared BiDi schema.json")] --> Gen["generate_bidi_protocol.py"] --> Pkg["selenium.webdriver._bidi"]
  Verify["verify_bidi_protocol.py"] -."diffs output".-> Pkg
  CI{{"CI unit-tests"}} --> Verify
  Driver(["Remote WebDriver"]) --> Transport["_bidi Transport"]
  Pkg --> Domain["Generated Domains"]
  Domain --> Transport --> WS{{"WebSocketConnection"}}
  Domain --> Runtime["serialization runtime"]
  subgraph Legend
    direction LR
    _mod["Module/File"] ~~~ _svc(["Runtime component"]) ~~~ _ext{{"External input"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Build-only generation (do not check in generated code)
  • ➕ Eliminates large generated diffs from reviews/history
  • ➕ Avoids drift by construction (always generated during build)
  • ➖ Harder for reviewers to inspect the actual produced API surface
  • ➖ Debugging/triage becomes harder without committed sources
  • ➖ Conflicts with current check-in + CI-verify strategy used to stage adoption
2. Lenient inbound validation from day one
  • ➕ More resilient to browser quirks during early adoption
  • ➕ May reduce churn if browsers send slightly off-schema payloads
  • ➖ Pushes schema mismatches to later failures (harder debugging)
  • ➖ Reduces confidence that the schema and generator match real payloads
3. Keep existing CDDL-based Python generator and evolve it
  • ➕ Reuses existing Python BiDi implementation work
  • ➕ Potentially less new runtime code
  • ➖ Does not achieve shared binding-neutral schema alignment goal
  • ➖ Likely requires more per-construct special casing/manifests than the normalized shared schema approach

Recommendation: The PR’s check-in generator + CI drift verification is the right staging strategy for a large, mechanical surface: it makes the generated API reviewable and debuggable while preventing hand-edits/staleness. The strict-first inbound validation is also a good default while this remains internal/unused; relaxing can be driven by concrete browser payloads once higher-level facades start consuming it.

Files changed (26) +7206 / -2

Enhancement (19) +6352 / -0
generate_bidi_protocol.pyAdd schema-driven generator for the internal Python BiDi layer +1003/-0

Add schema-driven generator for the internal Python BiDi layer

• Adds the Python generator that reads the binding-neutral 'schema.json', builds an IR for enums/records/unions/commands/events, and renders ruff-compatible Python modules into 'selenium/webdriver/_bidi/' for check-in.

py/generate_bidi_protocol.py

__init__.pyGenerate internal package entrypoint and domain exports +61/-0

Generate internal package entrypoint and domain exports

• Defines the internal '_bidi' package, marks it unsupported/internal, and re-exports generated domain classes via '__all__'.

py/selenium/webdriver/_bidi/init.py

bluetooth.pyGenerate bluetooth domain types and commands +505/-0

Generate bluetooth domain types and commands

• Adds generated enums/records/unions and the 'Bluetooth' domain command surface from the shared schema.

py/selenium/webdriver/_bidi/bluetooth.py

browser.pyGenerate browser domain types and commands +244/-0

Generate browser domain types and commands

• Adds generated types and the 'Browser' domain commands from the shared schema.

py/selenium/webdriver/_bidi/browser.py

browsing_context.pyGenerate browsingContext domain types and commands +693/-0

Generate browsingContext domain types and commands

• Adds generated browsingContext records/unions/enums and the 'BrowsingContext' domain methods (e.g., 'create', 'get_tree') used by new tests.

py/selenium/webdriver/_bidi/browsing_context.py

emulation.pyGenerate emulation domain types and commands +413/-0

Generate emulation domain types and commands

• Adds generated types and the 'Emulation' domain command surface from the shared schema.

py/selenium/webdriver/_bidi/emulation.py

input.pyGenerate input domain types and commands +321/-0

Generate input domain types and commands

• Adds generated input types/unions and the 'Input' domain commands from the shared schema.

py/selenium/webdriver/_bidi/input.py

log.pyGenerate log domain types and commands +112/-0

Generate log domain types and commands

• Adds generated log domain types and command surface from the shared schema.

py/selenium/webdriver/_bidi/log.py

network.pyGenerate network domain types and commands +746/-0

Generate network domain types and commands

• Adds generated network records/unions/enums and the 'Network' domain command surface used in unit tests for enum/discriminator validation.

py/selenium/webdriver/_bidi/network.py

permissions.pyGenerate permissions domain types and commands +77/-0

Generate permissions domain types and commands

• Adds generated types and the 'Permissions' domain command surface from the shared schema.

py/selenium/webdriver/_bidi/permissions.py

script.pyGenerate script domain types and commands +993/-0

Generate script domain types and commands

• Adds generated script types (e.g., RemoteValue/EvaluateResult unions) and the 'Script' domain command surface from the shared schema.

py/selenium/webdriver/_bidi/script.py

serialization.pyAdd Record/Union/UNSET serialization runtime with strict validation +364/-0

Add Record/Union/UNSET serialization runtime with strict validation

• Introduces the hand-written runtime used by all generated modules: immutable 'Record' with wire metadata, 'Union' dispatch/build logic, 'UNSET' sentinel, registry-based ref resolution to avoid import cycles, and strict inbound validation for required fields, nullability, primitives, enums, lists, and discriminators.

py/selenium/webdriver/_bidi/serialization.py

session.pyGenerate session domain types and commands +299/-0

Generate session domain types and commands

• Adds generated session records/unions/enums (capabilities/proxy/user prompt handler shapes) and the 'Session' domain command surface from the shared schema.

py/selenium/webdriver/_bidi/session.py

speculation.pyGenerate speculation domain types and commands +55/-0

Generate speculation domain types and commands

• Adds generated types and the 'Speculation' domain command surface from the shared schema.

py/selenium/webdriver/_bidi/speculation.py

storage.pyGenerate storage domain types and commands +182/-0

Generate storage domain types and commands

• Adds generated types and the 'Storage' domain command surface from the shared schema.

py/selenium/webdriver/_bidi/storage.py

transport.pyAdd websocket transport seam and generated-domain base class +88/-0

Add websocket transport seam and generated-domain base class

• Adds 'Transport.execute()' that speaks the existing WebSocketConnection coroutine protocol without re-camelCasing, and a 'Domain' base that reuses a WebDriver-owned shared transport (or accepts a standalone Transport).

py/selenium/webdriver/_bidi/transport.py

user_agent_client_hints.pyGenerate userAgentClientHints domain types and commands +91/-0

Generate userAgentClientHints domain types and commands

• Adds generated types and the 'UserAgentClientHints' domain command surface from the shared schema.

py/selenium/webdriver/_bidi/user_agent_client_hints.py

web_extension.pyGenerate webExtension domain types and commands +98/-0

Generate webExtension domain types and commands

• Adds generated types and the 'WebExtension' domain command surface from the shared schema.

py/selenium/webdriver/_bidi/web_extension.py

webdriver.pyStore one shared BiDi Transport on the driver (lazy import) +7/-0

Store one shared BiDi Transport on the driver (lazy import)

• Adds a '_bidi_transport' attribute, initializes it in '_start_bidi()' via a lazy import of '_bidi.transport.Transport', and clears it on 'quit()' alongside websocket teardown.

py/selenium/webdriver/remote/webdriver.py

Other (7) +854 / -2
ci-python.ymlRun verify-bidi-protocol in the unit-tests CI job +1/-1

Run verify-bidi-protocol in the unit-tests CI job

• Extends the Bazel unit test invocation to include '//py:verify-bidi-protocol', ensuring generated '_bidi' files cannot drift in CI.

.github/workflows/ci-python.yml

BUILD.bazelAdd generator, protocol library, and verify test Bazel targets +37/-1

Add generator, protocol library, and verify test Bazel targets

• Introduces 'py_binary(generate-bidi-protocol)', 'py_library(bidi_protocol)' for the generated '_bidi' package, and 'py_test(verify-bidi-protocol)'; wires the package into 'py_package' and ensures feature tests can import the generated layer.

py/BUILD.bazel

bidi_protocol_tests.pyAdd browser round-trip tests for the generated '_bidi' layer +64/-0

Add browser round-trip tests for the generated '_bidi' layer

• Adds end-to-end tests that execute generated browsingContext commands against a real browser and assert typed results (including nested record/list shapes) deserialize correctly.

py/test/selenium/webdriver/common/bidi_protocol_tests.py

bidi_protocol_command_tests.pyAdd unit tests for generated command methods and argument validation +151/-0

Add unit tests for generated command methods and argument validation

• Uses a stand-in connection/Transport to assert generated domain methods validate enum/discriminator arguments before sending, build correct wire frames, and parse typed result objects.

py/test/unit/selenium/webdriver/common/bidi_protocol_command_tests.py

bidi_serialization_tests.pyAdd unit tests for the serialization runtime strictness and dispatch +396/-0

Add unit tests for the serialization runtime strictness and dispatch

• Tests UNSET behavior, required/nullable handling, strict primitive checks (including integer-vs-float), enum validation, list/scalar shape enforcement, extensible record behavior, and union dispatch/build semantics (including fallback and bare-scalar arms).

py/test/unit/selenium/webdriver/common/bidi_serialization_tests.py

bidi_transport_tests.pyAdd unit tests for Transport.execute and Domain transport acquisition +150/-0

Add unit tests for Transport.execute and Domain transport acquisition

• Tests the websocket coroutine contract for command frames, raw vs typed results, Domain’s ability to reuse a driver’s shared transport or trigger '_start_bidi()', and error behavior when neither is available.

py/test/unit/selenium/webdriver/common/bidi_transport_tests.py

verify_bidi_protocol.pyAdd generator drift verification test runner +55/-0

Add generator drift verification test runner

• Re-renders all modules from the schema and compares them to checked-in '_bidi' files, failing with a clear regeneration message when stale/edited.

py/verify_bidi_protocol.py

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a new internal Python WebDriver BiDi protocol layer under selenium.webdriver._bidi, generated from the shared binding-neutral BiDi schema, plus a small handwritten runtime (serialization + transport seam) and Bazel/CI wiring to keep checked-in generated files in sync.

Changes:

  • Introduces the generated internal _bidi protocol package (domains, types, commands/events metadata) and a handwritten runtime (serialization.py, transport.py).
  • Wires RemoteWebDriver to create a per-session _bidi_transport lazily when BiDi is started.
  • Adds unit + end-to-end tests, plus a verify-bidi-protocol Bazel target and CI step to detect drift/hand-edits.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
py/verify_bidi_protocol.py Verifies checked-in _bidi files match generator output.
py/test/unit/selenium/webdriver/common/bidi_transport_tests.py Unit coverage for the handwritten transport/domain seam.
py/test/unit/selenium/webdriver/common/bidi_serialization_tests.py Unit coverage for strict (de)serialization runtime rules.
py/test/unit/selenium/webdriver/common/bidi_protocol_command_tests.py Exercises generated command surfaces over a stand-in transport.
py/test/selenium/webdriver/common/bidi_protocol_tests.py Browser-level sanity checks that generated types round-trip against a real BiDi session.
py/selenium/webdriver/remote/webdriver.py Adds _bidi_transport lifecycle + lazy import/creation in _start_bidi().
py/selenium/webdriver/_bidi/__init__.py Generated package initializer exporting domain classes.
py/selenium/webdriver/_bidi/serialization.py Handwritten serialization runtime used by generated records/unions (plus registry).
py/selenium/webdriver/_bidi/transport.py Handwritten websocket seam (Transport) and base domain (Domain).
py/selenium/webdriver/_bidi/browsing_context.py Generated browsingContext domain (types + commands + event metadata).
py/selenium/webdriver/_bidi/bluetooth.py Generated bluetooth domain (types + commands + event metadata).
py/selenium/webdriver/_bidi/browser.py Generated browser domain (types + commands).
py/selenium/webdriver/_bidi/emulation.py Generated emulation domain (types + commands).
py/selenium/webdriver/_bidi/input.py Generated input domain (types + commands + event metadata).
py/selenium/webdriver/_bidi/log.py Generated log domain (types + event metadata).
py/selenium/webdriver/_bidi/network.py Generated network domain (types + commands + event metadata).
py/selenium/webdriver/_bidi/permissions.py Generated permissions domain (types + commands).
py/selenium/webdriver/_bidi/script.py Generated script domain (types + commands + event metadata).
py/selenium/webdriver/_bidi/session.py Generated session domain (types + commands).
py/selenium/webdriver/_bidi/speculation.py Generated speculation domain (types + event metadata).
py/selenium/webdriver/_bidi/storage.py Generated storage domain (types + commands).
py/selenium/webdriver/_bidi/user_agent_client_hints.py Generated userAgentClientHints domain (types + commands).
py/selenium/webdriver/_bidi/web_extension.py Generated webExtension domain (types + commands).
py/BUILD.bazel Adds generator target, _bidi library, and verify test; packages _bidi.
.github/workflows/ci-python.yml Runs //py:verify-bidi-protocol in CI alongside unit tests.

Comment thread py/selenium/webdriver/_bidi/serialization.py
Comment thread py/selenium/webdriver/_bidi/serialization.py
Comment thread py/selenium/webdriver/_bidi/serialization.py
@qodo-code-review

qodo-code-review Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 17 rules

Grey Divider


Action required

1. Union scalar bypass ✗ Dismissed 🐞 Bug ≡ Correctness
Description
Union.from_json() returns any non-dict payload unchanged, so union-typed fields can silently
accept unexpected scalar values even when the union has no scalar arm. This weakens the PR’s strict
inbound validation because _read_scalar() routes union refs to Union.from_json() without
enforcing object-ness.
Code

py/selenium/webdriver/_bidi/serialization.py[R300-306]

+    @classmethod
+    def from_json(cls, payload: Any) -> Any:
+        # A bare scalar arm (e.g. input.Origin's "viewport") has no object to
+        # dispatch on, so it is returned unchanged.
+        if not isinstance(payload, dict):
+            return payload
+        variant = cls._select(payload)
Evidence
_read_scalar() allows non-dict values for union refs and delegates to Union.from_json(), and
Union.from_json() immediately returns non-dict values unchanged—so scalars bypass union validation
entirely.

py/selenium/webdriver/_bidi/serialization.py[262-277]
py/selenium/webdriver/_bidi/serialization.py[300-312]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`Union.from_json()` currently accepts any non-dict payload by returning it unchanged. This allows malformed payloads (scalar instead of object) to pass through for any union-ref field.

## Issue Context
`_read_scalar()` explicitly skips the "must be dict" check for unions and calls `klass.from_json(raw)`. With the current `Union.from_json` early return, that means *all* unions accept scalars, not just those that legitimately have scalar arms.

## Fix Focus Areas
- py/selenium/webdriver/_bidi/serialization.py[262-277]
- py/selenium/webdriver/_bidi/serialization.py[300-312]

## Implementation direction
- Make scalar acceptance opt-in per union (e.g., `_ALLOW_SCALAR: bool` or `_SCALAR_PRIMITIVE: str|None`), defaulting to rejecting non-dict payloads.
- Have the generator set this flag only for unions that truly include a scalar arm (and optionally validate the scalar type/value when enabled).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Presence keys mismatch ✗ Dismissed 🐞 Bug ≡ Correctness
Description
The generated script.RemoteReference union uses a presence key sharedId (wire name), but
Union.build() checks presence against Python kwargs keys; because the corresponding Python field
is shared_id, RemoteReference.build(shared_id=...) cannot match a variant and will raise
BiDiSerializationError. This makes outbound construction via Union.build() incorrect for this
union (and any similar presence union where wire keys differ from Python names).
Code

py/selenium/webdriver/_bidi/script.py[R817-820]

+@register("script.RemoteReference")
+class RemoteReference(Union):
+    _PRESENCE = (("script.SharedReference", ("sharedId",)), ("script.RemoteObjectReference", ("handle",)))
+
Evidence
RemoteReference presence selection is keyed on sharedId, but the generated record field name is
shared_id; Union._outbound() checks kwargs.get(key) for each presence key, so a call providing
shared_id=... will never satisfy the sharedId presence check and the union cannot be built via
build().

py/selenium/webdriver/_bidi/script.py[817-823]
py/selenium/webdriver/_bidi/script.py[309-324]
py/selenium/webdriver/_bidi/serialization.py[353-364]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`Union.build()` uses `_PRESENCE` keys to check `kwargs.get(key)`, but `script.RemoteReference._PRESENCE` contains the wire key `sharedId` while the Python field/kwargs name is `shared_id`. This prevents selecting the SharedReference arm when building outbound values.

## Issue Context
- Inbound selection wants wire keys (e.g., `sharedId` in the payload).
- Outbound selection wants Python keys (e.g., `shared_id` in kwargs).
Today these are forced to share one `_PRESENCE` table, which breaks when they differ.

## Fix Focus Areas
- py/selenium/webdriver/_bidi/serialization.py[353-364]
- py/selenium/webdriver/_bidi/script.py[817-820]

## Implementation direction
- Introduce separate presence tables for inbound vs outbound (e.g., `_PRESENCE_WIRE` and `_PRESENCE_FIELDS`), and have `_select()` use wire keys while `_outbound()` uses python field names.
- Update the generator to emit both sets for unions that use presence selection (or emit python names and have `_select()` also accept wire-name equivalents via a mapping).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. _command() missing return annotation ✓ Resolved 📘 Rule violation ✧ Quality
Description
The newly added generator helper _command() has no explicit return type annotation, violating the
requirement that new function signatures include return annotations. This reduces type-checking
coverage and can cause inconsistent typing across the new BiDi transport seam.
Code

py/selenium/webdriver/_bidi/transport.py[R37-45]

+def _command(method: str, params: dict):
+    """A command_builder coroutine: yield the wire frame, return the raw result.
+
+    ``WebSocketConnection.execute`` speaks this protocol (``next`` then ``send``).
+    Params arrive already wire-shaped (from ``Record.as_json``), so the connection's
+    dataclass encoder passes them through untouched — no double camelCasing.
+    """
+    result = yield {"method": method, "params": params}
+    return result
Evidence
PR Compliance ID 337802 requires explicit type annotations on new function signatures, including
return types. The added _command() definition has parameter annotations but omits the -> ...
return annotation.

Rule 337802: Require type annotations on new function and method signatures
py/selenium/webdriver/_bidi/transport.py[37-45]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`py/selenium/webdriver/_bidi/transport.py` introduces `_command()` without an explicit return type annotation.

## Issue Context
Compliance requires all new/modified Python function and method signatures to include parameter annotations and an explicit return type annotation. `_command()` is a generator-style coroutine used with `WebSocketConnection.execute`, so its signature should still declare a return type (e.g., a `Generator[...]` type, or at minimum `-> Any`).

## Fix Focus Areas
- py/selenium/webdriver/_bidi/transport.py[37-45]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Import-order type failures ✗ Dismissed 🐞 Bug ☼ Reliability
Description
Cross-domain refs (e.g. browsingContext.ElementClipRectangle referencing script.SharedReference)
rely on the global registry, but domain modules don’t import their dependencies at runtime;
importing a single domain module can therefore fail at (de)serialization time with `unknown BiDi
type ... (module not imported?)`. This is an import-order dependent runtime failure (package-level
selenium.webdriver._bidi import avoids it by importing all domains).
Code

py/selenium/webdriver/_bidi/browsing_context.py[R192-196]

+@register("browsingContext.ElementClipRectangle")
+@dataclass(frozen=True)
+class ElementClipRectangle(Record):
+    element: SharedReference = field(metadata=meta("element", required=True, ref="script.SharedReference"))
+    type: str = field(default="element", init=False, metadata=meta("type", required=True, fixed="element"))
Evidence
The generated record contains a ref to script.SharedReference, but the module doesn’t import
script; if script hasn’t been imported elsewhere, resolve('script.SharedReference') raises
with an explicit “module not imported?” message.

py/selenium/webdriver/_bidi/browsing_context.py[23-30]
py/selenium/webdriver/_bidi/browsing_context.py[192-197]
py/selenium/webdriver/_bidi/serialization.py[132-137]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Some generated types reference schema names in other domain modules via `ref=...`, but the registry is only populated when those other modules have been imported. If users import only one domain module (not the package `selenium.webdriver._bidi`), deserialization of cross-domain refs can fail.

## Issue Context
This is primarily an import-order hazard. Package-level import (`import selenium.webdriver._bidi`) imports all domain modules and registers all types, but direct module imports do not.

## Fix Focus Areas
- py/selenium/webdriver/_bidi/serialization.py[132-137]
- py/selenium/webdriver/_bidi/browsing_context.py[192-197]
- py/selenium/webdriver/_bidi/browsing_context.py[23-30]

## Implementation direction
- Option A (runtime): Enhance `resolve()` to attempt a lazy import based on the schema name prefix before failing (e.g., importing `selenium.webdriver._bidi.script` when resolving `script.*`).
- Option B (generator): Emit minimal runtime imports for referenced domains (with care to avoid cycles), or provide a documented/obvious "load all" initializer that callers must invoke.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

5. Verifier misses extra files 🐞 Bug ⚙ Maintainability
Description
verify_bidi_protocol.py only checks that files produced by the generator match the committed
copies; it never fails if _bidi/ contains extra .py files not produced by the generator. This
can allow removed/renamed generated modules to linger unnoticed.
Code

py/verify_bidi_protocol.py[R36-41]

+    rendered = gen.render_all(schema_path)
+    stale = [
+        name
+        for name, contents in rendered.items()
+        if not (bidi_dir / name).exists() or (bidi_dir / name).read_text() != contents
+    ]
Evidence
The script computes stale solely from rendered.items() and never compares the on-disk directory
listing against the rendered set, so extra files are ignored.

py/verify_bidi_protocol.py[32-41]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The verification step does not detect extra files present on disk but not produced by `render_all()`, so stale generated modules can persist.

## Issue Context
This is a drift-guard completeness gap: it catches mismatches for expected outputs but not unexpected leftovers.

## Fix Focus Areas
- py/verify_bidi_protocol.py[36-41]

## Implementation direction
- After computing `rendered`, list `bidi_dir/*.py` (and possibly exclude hand-written runtime files like `serialization.py`, `transport.py`) and fail if any generated-looking file is not in `rendered.keys()`.
- Print the unexpected file list alongside stale mismatches.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread py/selenium/webdriver/_bidi/transport.py Outdated
Comment thread py/selenium/webdriver/_bidi/script.py
Comment thread py/selenium/webdriver/_bidi/serialization.py
Comment thread py/selenium/webdriver/_bidi/browsing_context.py
Comment thread py/verify_bidi_protocol.py Outdated
Comment on lines +36 to +41
rendered = gen.render_all(schema_path)
stale = [
name
for name, contents in rendered.items()
if not (bidi_dir / name).exists() or (bidi_dir / name).read_text() != contents
]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Informational

5. Verifier misses extra files 🐞 Bug ⚙ Maintainability

verify_bidi_protocol.py only checks that files produced by the generator match the committed
copies; it never fails if _bidi/ contains extra .py files not produced by the generator. This
can allow removed/renamed generated modules to linger unnoticed.
Agent Prompt
## Issue description
The verification step does not detect extra files present on disk but not produced by `render_all()`, so stale generated modules can persist.

## Issue Context
This is a drift-guard completeness gap: it catches mismatches for expected outputs but not unexpected leftovers.

## Fix Focus Areas
- py/verify_bidi_protocol.py[36-41]

## Implementation direction
- After computing `rendered`, list `bidi_dir/*.py` (and possibly exclude hand-written runtime files like `serialization.py`, `transport.py`) and fail if any generated-looking file is not in `rendered.keys()`.
- Print the unexpected file list alongside stale mismatches.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@titusfortner titusfortner marked this pull request as draft July 10, 2026 15:21
# bazel run //py:generate-bidi-protocol


from __future__ import annotations

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should not be checking in generated code

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.

My original thought was to check it in until we used it to implement the user facing code, but we can change it to build only now after we've agreed that this is the right shape for the generated code.

Do you have any concerns with the conventions or naming of the output?

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

Labels

B-build Includes scripting, bazel and CI integrations C-py Python Bindings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants