This implementation addresses the GitHub issue about extending trio-chrome-devtools-protocol with higher-level utility functions and classes for common browser automation tasks, inspired by Puppeteer/Pyppeteer.
Rather than creating a separate trio-puppeteer package, the utilities are integrated directly into the main trio_cdp package as a util module. This approach was chosen because:
- Lightweight: The utilities are thin wrappers around CDP commands
- No External Dependencies: Everything uses native CDP, no JavaScript injection
- Tight Integration: Direct access to session and connection objects
- Simplicity: Users don't need to install/manage a separate package
Contains three main classes and utility functions:
Provides keyboard input simulation:
down(key, text=None)- Press key downup(key)- Release keypress(key, delay=0)- Complete key press (down + up)type(text, delay=0)- Type a string character by character
Example:
keyboard = Keyboard(session)
await keyboard.type("Hello, World!")
await keyboard.press("Enter")Provides mouse action simulation:
move(x, y, steps=1)- Move mouse with optional smooth interpolationclick(x, y, button='left', click_count=1, delay=0)- Click at positiondown(button='left', click_count=1)- Mouse button downup(button='left', click_count=1)- Mouse button up
Example:
mouse = Mouse(session)
await mouse.move(100, 200, steps=10) # Smooth movement
await mouse.click(100, 200)Represents a handle to a DOM element with convenient interaction methods:
click(button='left', click_count=1, delay=0)- Click the elementtype(text, delay=0)- Focus and type into elementget_attribute(name)- Get HTML attribute valueget_property(name)- Get JavaScript property valueget_text_content()- Extract text content
Example:
input_field = await query_selector(session, 'input[name="email"]')
if input_field:
await input_field.type('user@example.com')query_selector(session, selector, node_id=None)- Find first matching elementquery_selector_all(session, selector, node_id=None)- Find all matching elementswait_for_selector(session, selector, timeout=30, visible=False)- Wait for element
Example:
# Find and interact with elements
button = await query_selector(session, 'button.submit')
if button:
await button.click()
# Wait for dynamic content
result = await wait_for_selector(session, '.result', timeout=10, visible=True)- docs/utilities.rst - Comprehensive documentation for all utilities
- examples/form_interaction.py - Example showing form interaction
- examples/keyboard_mouse.py - Example demonstrating keyboard/mouse usage
- tests/test_util.py - Unit tests for utility functions
- validate_utilities.py - Validation script to verify module structure
- README.md - Added utilities section with examples
- docs/index.rst - Added utilities to documentation table of contents
- trio_cdp/init.py - Export util module
- Pure CDP: No JavaScript injection, all interactions use native CDP commands
- Async-First: Fully compatible with Trio's async/await patterns
- Lightweight: Minimal abstractions, close to underlying CDP
- Type-Safe: Complete type hints for IDE support
- Composable: Small, focused utilities that work well together
- Optional: Core CDP functionality remains available; utilities are opt-in
- Intuitive API: Familiar patterns for anyone coming from Puppeteer
- Less Boilerplate: Common tasks simplified with high-level methods
- Type Safety: Full IDE support with autocomplete and type checking
- Pure Python: No JavaScript knowledge required
- Maintains Philosophy: Stays true to lightweight, CDP-focused approach
- No Breaking Changes: Completely additive, existing code unaffected
- Extensible: Users can easily add custom utilities following same patterns
- Well-Documented: Comprehensive docs and examples
Fixed generator/generate.py to handle typing.Optional type hints, which was preventing regeneration of CDP bindings with newer Python versions.
Regenerated all CDP binding code to be compatible with chrome-devtools-protocol==0.4.0, resolving import errors with the generated code.
- Unit Tests: Comprehensive test suite in
tests/test_util.py - Validation Script:
validate_utilities.pyverifies all classes and methods exist - Code Quality: Passed CodeQL security scan with 0 alerts
- Examples: Two working examples demonstrate real-world usage
Here's a complete example showing the utilities in action:
import trio
from trio_cdp import open_cdp, page, target
from trio_cdp.util import query_selector, wait_for_selector, Keyboard
async def automate_form(cdp_url):
async with open_cdp(cdp_url) as conn:
# Get a target
targets = await target.get_targets()
target_id = targets[0].target_id
async with conn.open_session(target_id) as session:
# Navigate
await page.enable()
await page.navigate('https://example.com/form')
# Wait for and fill form
name_field = await wait_for_selector(session, 'input[name="name"]', timeout=10)
if name_field:
await name_field.type('John Doe')
# Use keyboard for submission
keyboard = Keyboard(session)
await keyboard.press('Enter')Potential additions that maintain the same design philosophy:
- Page utilities: Screenshot helpers, PDF generation utilities
- Network utilities: Request interception helpers, mock response utilities
- Cookie utilities: Easy cookie management
- Dialog utilities: Alert/prompt/confirm handlers
- File upload: File chooser utilities
Each would follow the same pattern: lightweight wrappers around CDP commands with convenient async interfaces.
This implementation successfully extends trio-chrome-devtools-protocol with higher-level utilities while maintaining the library's core principles of being lightweight, pure-CDP, and Trio-native. The utilities provide a more intuitive interface for common automation tasks without sacrificing the power and flexibility of the underlying CDP protocol.