Skip to content

Commit 38e222b

Browse files
[py] BiDi Python code generation from CDDL
1 parent 303f43a commit 38e222b

26 files changed

+9464
-3487
lines changed

BIDI_CODEGEN_FINDINGS.md

Lines changed: 487 additions & 0 deletions
Large diffs are not rendered by default.

Bidi-CodeGen-Plan.md

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# Plan: Python BiDi Code Generation via CDDL Parser
2+
3+
**TL;DR**: Create `py/generate_bidi.py` mirroring `py/generate.py` but tailored for CDDL format. Use an existing Python CDDL library (add to `py/requirements.txt`). Download W3C CDDL specs and commit to git alongside CDP PDL files. Integrate via Bazel rule in `py/BUILD.bazel` and `py/private/generate_bidi.bzl`. Start with a pilot module (e.g., `script`) to validate the generator, then expand coverage. Plan for cross-binding extension later.
4+
5+
---
6+
7+
## Steps
8+
9+
### Phase 1: Setup & Infrastructure
10+
11+
1. **Add CDDL library to dependencies**
12+
- Add `cddl` package to [py/requirements.txt](py/requirements.txt)
13+
- Update [py/requirements_lock.txt](py/requirements_lock.txt)
14+
15+
2. **Obtain and commit CDDL spec files**
16+
- Download W3C WebDriver BiDi CDDL specs (local.cddl and remote.cddl if split)
17+
- Store in [common/bidi/spec/](common/bidi/spec/) (mirrors [common/devtools/chromium/](common/devtools/chromium/) pattern for CDP)
18+
- Commit to git with metadata (version, source URL, fetch date)
19+
20+
3. **Create CDDL file label in Bazel**
21+
- Add BUILD.bazel rule in [common/bidi/spec/](common/bidi/spec/) to expose CDDL files as Bazel labels (similar to [common/devtools/chromium/BUILD.bazel](common/devtools/chromium/BUILD.bazel))
22+
23+
### Phase 2: Create Generator Script
24+
25+
1. **Create [py/generate_bidi.py](py/generate_bidi.py)**
26+
- **Input**: CDDL file path (passed via command-line arg)
27+
- **Parsing**:
28+
- Use `cddl` library to parse spec
29+
- Extract BiDi modules (namespaces like `script`, `network`, `browsing_context`)
30+
- Extract Commands (method definitions)
31+
- Extract Events (event definitions)
32+
- Extract Type definitions (data structures, enums, unions)
33+
34+
- **Type Mapping** (`cddl_to_python_type(cddl_type)`):
35+
- `tstr` / `text``str`
36+
- `uint` / `int` / `nint``int`
37+
- `bool``bool`
38+
- `null``None`
39+
- `[+ T]` (array) → `List[T]`
40+
- `{ key: T }` (map) → `Dict[str, T]`
41+
- `A / B` (union) → `Union[A, B]`
42+
43+
- **Code Generation** (using f-strings/templates):
44+
- Header: Selenium license, `# DO NOT EDIT` marker, imports
45+
- Module classes: For each BiDi module, generate a class (e.g., `class Script`)
46+
- Methods: For each command, generate a method using **generator co-routine pattern**:
47+
48+
```python
49+
def evaluate(self, expression: str, target: Any, ...) -> Generator[dict, dict, dict]:
50+
params = {
51+
"expression": expression,
52+
"target": target,
53+
...
54+
}
55+
params = {k: v for k, v in params.items() if v is not None}
56+
return command_builder("script.evaluate", params)
57+
```
58+
59+
- Event classes: Dataclasses with `from_json()` method (similar to CDP pattern)
60+
61+
- **Output**: Write one file per module to [py/selenium/webdriver/common/bidi/generated/](py/selenium/webdriver/common/bidi/generated/) (or `py/selenium/webdriver/common/bidi/` if single module)
62+
63+
### Phase 3: Bazel Integration
64+
65+
1. **Create [py/private/generate_bidi.bzl](py/private/generate_bidi.bzl)**
66+
- Define `generate_bidi()` Bazel rule (mirrors `generate_devtools()` logic)
67+
- Inputs: `cddl_file`, `generator` script, `spec_version`
68+
- Outputs: Generated Python files
69+
- Action: Run `python generate_bidi.py --cddl <input> --output <dir> --version <spec_version>`
70+
71+
2. **Register in [py/BUILD.bazel](py/BUILD.bazel)**
72+
- Load `generate_bidi` rule from `py/private/generate_bidi.bzl`
73+
- Create a `generate_bidi()` target for the pilot module (e.g., `script`)
74+
- Example (around line 590):
75+
76+
```
77+
generate_bidi(
78+
name = "create-bidi-script",
79+
cddl_file = "//common/bidi/spec:local_cddl",
80+
generator = ":generate_bidi",
81+
spec_version = "1.0", # BiDi spec version
82+
)
83+
```
84+
85+
### Phase 4: Validation & Refinement (Pilot)
86+
87+
1. **Choose pilot module** (e.g., `script`)
88+
- Generate code for one module using the new script
89+
- Compare against existing [py/selenium/webdriver/common/bidi/script.py](py/selenium/webdriver/common/bidi/script.py)
90+
- Validate:
91+
- All commands present
92+
- Type signatures match
93+
- Generator co-routine pattern is correct
94+
- No linting errors (run through `black`/`ruff`)
95+
96+
2. **Run generator and test**
97+
- Build Bazel target: `bazel build //py:create-bidi-script`
98+
- Review generated output
99+
- Run existing tests: `bazel test //py/selenium/webdriver/common/bidi/test:script_tests`
100+
- Manual inspection: Ensure generated code is maintainable and adheres to Selenium style
101+
102+
### Phase 5: Plan for Extension
103+
104+
1. **Document findings** (in a follow-up issue/PR doc)
105+
- Note any gaps or special cases in CDDL parsing
106+
- Identify modules to generate next
107+
- Plan rollout strategy (replace manual code vs. shadow validation)
108+
109+
2. **Plan cross-binding extension**
110+
- Once Python generator is stable, create equivalent generators for Java, JavaScript, .NET, Ruby
111+
- Coordinate via AGENTS.md updates in each language directory
112+
113+
---
114+
115+
## Verification
116+
117+
- **Build**: `bazel build //py:create-bidi-script` succeeds
118+
- **Generated code**: Compiles without errors, passes linting
119+
- **Functional equivalence**: Generated `script.py` (or test module) produces same results as manual version
120+
- **Immutability**: Generated files contain `# DO NOT EDIT` header
121+
- **Tests pass**: Existing BiDi tests still pass with generated code
122+
123+
---
124+
125+
## Decisions Made
126+
127+
- **Async pattern**: Generator co-routine (matching existing BiDi code)
128+
- **CDDL parser**: Use existing `cddl` library from PyPI
129+
- **Spec storage**: Download/commit to `common/bidi/spec/` (mirrors CDP pattern)
130+
- **Build system**: Bazel rule (mirrors CDP `generate_devtools()`)
131+
- **Scope**: Pilot single module, validate, then expand
132+
- **Cross-binding**: Deferred until Python implementation is stable
133+
134+
---
135+
136+
## Phase Progress
137+
138+
- [ ] Phase 1: Setup & Infrastructure
139+
- [ ] Phase 2: Create Generator Script
140+
- [ ] Phase 3: Bazel Integration
141+
- [ ] Phase 4: Validation & Refinement (Pilot)
142+
- [ ] Phase 5: Plan for Extension

PHASE_6_INTEGRATION_PLAN.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# Phase 6.3: Integration Test Baseline
2+
3+
## Objective
4+
5+
Establish a baseline test execution against hand-written BiDi code to understand current test coverage and behavior before migrating to generated modules.
6+
7+
## Test Coverage Summary
8+
9+
### BiDi Test Files (11 total, 177 tests)
10+
11+
- `bidi_browser_tests.py` - 16 tests (browser module)
12+
- `bidi_browsing_context_tests.py` - 56 tests (browsingContext module)
13+
- `bidi_emulation_tests.py` - 22 tests (emulation module)
14+
- `bidi_input_tests.py` - 13 tests (input module)
15+
- `bidi_network_tests.py` - 10 tests (network module)
16+
- `bidi_permissions_tests.py` - 7 tests (permissions module, NOT generated)
17+
- `bidi_script_tests.py` - 47 tests (script module)
18+
- `bidi_session_tests.py` - 2 tests (session module)
19+
- `bidi_storage_tests.py` - 0 tests (storage module)
20+
- `bidi_tests.py` - 3 tests (general BiDi tests)
21+
- `bidi_webextension_tests.py` - 1 test (webExtension module)
22+
23+
**Total: 177 tests across 11 files**
24+
25+
### Test Infrastructure
26+
27+
**Bazel Target**: `//py:test-bidi-baseline`
28+
29+
- Runs only BiDi tests with Chrome browser
30+
- Establishes baseline behavior against hand-written code
31+
- Uses `--bidi` flag for WebDriver BiDi protocol
32+
33+
**Usage**:
34+
35+
```bash
36+
bazel test //py:test-bidi-baseline
37+
```
38+
39+
### Modules Tested vs Generated
40+
41+
| Module | Tests | Generated | Status |
42+
|--------|-------|-----------|--------|
43+
| browser | 16 || Ready for comparison |
44+
| browsingContext | 56 || Ready for comparison |
45+
| emulation | 22 || Ready for comparison |
46+
| input | 13 || Ready for comparison |
47+
| network | 10 || Ready for comparison |
48+
| permissions | 7 || Not in W3C spec, hand-written only |
49+
| script | 47 || Ready for comparison |
50+
| session | 2 || Ready for comparison |
51+
| storage | 0 || No tests, but module generated |
52+
| tests | 3 | N/A | General BiDi tests |
53+
| webExtension | 1 || Ready for comparison |
54+
55+
## Baseline Execution (Phase 6.3)
56+
57+
### Step 1: Run Baseline Tests
58+
59+
Execute against hand-written code to establish known-good behavior:
60+
61+
```bash
62+
bazel test //py:test-bidi-baseline -s
63+
```
64+
65+
Expected outcome: All 177 tests pass (or identify known failures)
66+
67+
### Step 2: Document Results
68+
69+
- Test pass/fail counts per module
70+
- Identify any platform-specific issues (xfail markers)
71+
- Document test execution time baseline
72+
- Capture any warnings or deprecations
73+
74+
### Step 3: Capture Output
75+
76+
Creates baseline report:
77+
78+
- Number of passing tests
79+
- Number of xfail (expected failures)
80+
- Number of skipped tests
81+
- Execution time
82+
83+
## Phase 7: Type Definitions
84+
85+
Once baseline is established:
86+
87+
1. Generate type definitions from CDDL
88+
2. Create `types.py` or per-module dataclasses
89+
3. Update generated modules to reference types
90+
4. Prepare for code equivalence testing
91+
92+
## Phase 8: Code Validation
93+
94+
Compare generated code behavior:
95+
96+
1. Run generated modules in test harness
97+
2. Compare output with hand-written baseline
98+
3. Document any behavioral differences
99+
4. Create compatibility mapping if needed
100+
101+
## Phase 9: Migration
102+
103+
Once equivalence is proven:
104+
105+
1. Copy generated modules to source tree (`py/selenium/webdriver/common/bidi/`)
106+
2. Remove hand-written modules
107+
3. Update `//py:bidi` target to use generated code
108+
4. Add `:create-bidi-src` to `common` target data
109+
5. Run full test suite to validate
110+
111+
## Notes
112+
113+
### Known test markers
114+
115+
- `@pytest.mark.xfail_safari` - Tests expected to fail on Safari
116+
- `--bidi` flag enables WebDriver BiDi mode
117+
- Tests require Chrome or Firefox with BiDi support
118+
119+
### Test Dependencies
120+
121+
- WebDriver client implementation
122+
- Selenium Manager for driver provisioning
123+
- Test web server for fixture serving
124+
- Browser with BiDi protocol support
125+
126+
### Permissions Module
127+
128+
Note: `bidi_permissions_tests.py` tests a module (permissions) not in the official W3C BiDi spec. This is Selenium-specific and will remain hand-written.

0 commit comments

Comments
 (0)