[py] generate the internal BiDi protocol layer from the shared binding-neutral schema - #17761
[py] generate the internal BiDi protocol layer from the shared binding-neutral schema#17761titusfortner wants to merge 14 commits into
Conversation
PR Summary by Qodo[py] Generate internal BiDi protocol layer from the shared binding-neutral schema
AI Description
Diagram
High-Level Assessment
Files changed (41)
|
There was a problem hiding this comment.
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
_bidiprotocol package (domains, types, commands/events metadata) and a handwritten runtime (serialization.py,transport.py). - Wires
RemoteWebDriverto create a per-session_bidi_transportlazily when BiDi is started. - Adds unit + end-to-end tests, plus a
verify-bidi-protocolBazel 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. |
Code Review by Qodo
1.
|
|
The direction of this looks ok so far but need to see how this will be used as that code isn't here |
0760b9a to
ae297f2
Compare
|
PR has a lot of unnecessary files in it now in case you're not aware |
|
@AutomatedTester yes, to work with the latest schema it pulled in #17784 which should merge soon |
6057552 to
a227d86
Compare
@AutomatedTester to clarify, the intention is that our users will write this code if they need it. The usage in selenium will be limited to re-implementing current API and whatever additional facades we agree to, so I can't give examples for any of that yet, bu to give a better view of what usage looks like, I ported all the existing integration tests to use this code. py/test/selenium/webdriver/common/_bidi/. (instantiates A few concrete comparisons
# convenience method — dict in, raw dicts out
nodes = driver.browsing_context.locate_nodes(context=cid, locator={"type": "css", "value": "div.content"})
name = nodes[0]["value"]["localName"]
# generated classes — typed locator in, typed nodes out
nodes = BrowsingContext(driver).locate_nodes(context=cid, locator=CssLocator(value="div.content")).nodes
name = nodes[0].value.local_name
# convenience method — flat bool + folder
driver.browser.set_download_behavior(allowed=True, destination_folder=folder)
# generated classes — pass the typed variant
Browser(driver).set_download_behavior(DownloadBehaviorAllowed(destination_folder=folder)) |
5899a33 to
2b06fe9
Compare
Code Review by Qodo
1.
|
|
Code review by qodo was updated up to the latest commit 23d0038 |
|
Code review by qodo was updated up to the latest commit 17fa362 |
|
Code review by qodo was updated up to the latest commit b00ecea |
|
Code review by qodo was updated up to the latest commit de4a355 |
…require fields outbound
…d warnings per record
de4a355 to
e9d89f8
Compare
|
Code review by qodo was updated up to the latest commit e9d89f8 |
|
Code review by qodo was updated up to the latest commit cecf9d9 |
|
Code review by qodo was updated up to the latest commit 39c38a2 |
|
Code review by qodo was updated up to the latest commit 1d705ae |
🔗 Related Issues
objectOnly,extensible,scalar); extensibility follows the spec'sextensiblemarker (see [bidi] mark BiDi types extensible per spec and update Ruby handling #17853), not the retiredpreserveExtras.💥 What does this PR do?
selenium.webdriver.common._bidipackage, generated at build time from the shared binding-neutral schema (projected once from the CDDL). The domain modules are not checked in.Domainbase, and the package__init__. These four are the only tracked files in the package; the domain modules are generated and gitignored.✅ Compliance with the behavioral contract (#17786)
The layer conforms to all three decisions in the ADR. Validation lives at the boundaries and the
value objects are permissive, so the two directions differ. Each behavior is backed by a runtime
test in
bidi_serialization_tests.py(the ADR is runtime-not-declaration).presence, primitive types (a fractional value is rejected for an
integerfield), andnullable-constant literals are checked before a frame goes out (
Record.__post_init__,Union.build,Record.as_json, which errors on an unset required field); an extension may notshadow a declared field. A caller mistake is a local
BiDiSerializationError, not a round-trip.(
Transport.execute), never turned into a local serialization failure. A corrupt value errors(null in a non-nullable field, wrong primitive, cardinality mismatch, non-object where an object is
required, unknown enum/union member, a constant that is not its literal-or-null). A missing
required field is tolerated — warned and left omitted (
UNSET, distinct from null) — so a clientgenerated from one spec revision keeps working against a browser on another. An undeclared
property on an extensible type is spec-sanctioned and preserved in the type's extras map silently;
on a non-extensible type it is a deviation, warned and dropped.
strict_inbound()escalates thegenuine deviations (missing required, undeclared-on-closed) to errors — never a valid extensible extra.
objects (enums for closed vocabularies, a distinct type per union variant branched on by type);
Python's native ints give full numeric precision; names mirror the spec; received values are
represented as they arrived (no normalization).
🔧 Implementation Notes
common/bidicode and its CDDL generator are left untouched — retiring or migrating that is out of scope.common/_bidi/; onlyserialization.py,transport.py,domain.py, and__init__.pyare hand-written and tracked. Regenerate the checked-out tree for local development withbazel run //py:generate-bidi-protocol.common/alongside the existing generatedcommon/bidiandcommon/devtoolspackages, matching the established structure.strict_inbound()opts into erroring on the deviations). This lets a client built from one spec revision keep working against a browser on another.Transport; the driver owns only the sharedWebSocketConnection(where the id counter and callbacks live). Nothing on the driver imports the generated package, so non-BiDi sessions never load it.Domainbase (the class each generated domain subclasses) lives in its owncommon/_bidi/domain.pymodule.🤖 AI assistance
💡 Additional Considerations
🔄 Types of changes