pypecdp
Fully async Chrome DevTools Protocol over POSIX pipes with a high-level Browser/Tab/Elem API for Python 3.12+ on Linux.
Chrome automation using --remote-debugging-pipe (no websockets, no ports, just pipes) with bundled CDP protocol classes.
Inspired by playwright-python, python-cdp and nodriver.
Documentation
Full documentation is auto-generated and published at: https://sohaib17.github.io/pypecdp/
Features
- Fully Async: Built from ground up with asyncio for concurrent operations
- Fast: Direct pipe communication via file descriptors - no websockets, no network overhead
- Minimal dependencies: Only one dependency (
deprecated) - lightweight and easy to install - Secure: Browser only communicates over local pipes, no open ports accessible to other processes
- No zombies: No risk of orphaned Chrome processes if code crashes - automatic lifecycle management
- Linux focused: Leverages POSIX pipes and process management
Install
pip install pypecdp
Install Chromium if needed:
# Ubuntu/Debian
sudo apt-get install chromium-browser
# Fedora
sudo dnf install chromium
# Arch
sudo pacman -S chromium
Quick Start
import asyncio
from pypecdp import Browser
async def main():
# Launch browser
browser = await Browser.start(
chrome_path="chromium",
headless=True
)
# Navigate to a URL (creates/reuses a tab)
tab = await browser.navigate("https://example.com")
# Select and interact with elements
h1 = await tab.wait_for_elem("h1")
if h1:
text = await h1.text()
print(f"Page heading: {text}")
# Evaluate JavaScript
result = await tab.eval("document.title")
print(f"Title: {result.value}")
# Close browser
await browser.close()
asyncio.run(main())
Usage Guide
Browser Management
from pypecdp import Browser, Config
# Simple start
browser = await Browser.start(chrome_path="chromium", headless=True)
# Advanced configuration
config = Config(
chrome_path="/usr/bin/google-chrome",
user_data_dir="/tmp/chrome-profile",
clean_data_dir=False, # Preserve existing profile data
headless=True,
extra_args=["--no-sandbox", "--disable-gpu"],
env={"LANG": "en_US.UTF-8"}
)
browser = await Browser.start(config=config)
# Close browser
await browser.close()
Note: By default, clean_data_dir=True which removes any existing user data directory before starting. Set it to False to preserve cookies, cache, and other browser state between runs.
Element Interactions
# Finding and clicking elements
button = await tab.wait_for_elem("button.submit")
if button:
await button.click()
# Clicking elements that cause navigation
link = await tab.wait_for_elem('a[href="/next-page"]')
if link:
# click() returns the top-level Tab after navigation
current_tab = await link.click()
if current_tab:
# Wait for the new page to load
await current_tab.wait_for_event(cdp.page.LoadEventFired, timeout=10.0)
print(f"Navigated to: {current_tab.url}")
Customization via Inheritance
pypecdp is designed to be easily extended through OOP inheritance:
from pypecdp import Browser, Tab, Elem
class MyElem(Elem):
async def click_and_wait(self, timeout=10.0):
"""Click and wait for page load."""
tab = await self.click()
if tab:
await tab.wait_for_event(cdp.page.LoadEventFired, timeout=timeout)
return tab
class MyTab(Tab):
elem_class = MyElem # Use custom Elem class
async def search(self, query):
"""Custom search method."""
search_box = await self.wait_for_elem("#search")
await search_box.type(query)
class MyBrowser(Browser):
tab_class = MyTab # Use custom Tab class
# Use your custom classes
browser = await MyBrowser.start()
tab = await browser.navigate("https://example.com")
# tab is now MyTab instance, elements are MyElem instances
See example/customize_pypecdp.py for working example.
Event Handlers
from pypecdp import cdp
# Tab-level events (requires domain enable!)
await tab.send(cdp.runtime.enable()) # Required for runtime events!
async def on_console(event):
print(f"Console {event.type_}: {event.args}")
tab.on(cdp.runtime.ConsoleAPICalled, on_console)
# Browser-level events
async def on_target_created(event):
info = event.target_info
print(f"Target created: {info.type_} - {info.url}")
browser.on(cdp.target.TargetCreated, on_target_created)
Logging
pypecdp uses Python's standard logging module. Configure via environment variables:
# Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
export PYPECDP_LOGLEVEL=DEBUG
# Set custom logger name
export PYPECDP_LOGGER=myapp.browser
Or configure the logger directly in Python:
from pypecdp import logger
import logging
# Set log level
logger.setLevel(logging.DEBUG)
# Add custom handler
handler = logging.FileHandler("pypecdp.log")
logger.addHandler(handler)
Error Handling
try:
browser = await Browser.start()
tab = await browser.navigate("https://example.com")
# Your automation code
elem = await tab.wait_for_elem("button")
if elem:
await elem.click()
result = await tab.eval("document.title")
except ReferenceError as e:
# Element's tab is no longer available (closed/detached)
print(f"Target Error: {e}")
except RuntimeError as e:
# CDP protocol errors
print(f"CDP Error: {e}")
except ConnectionError as e:
# Connection lost
print(f"Connection Error: {e}")
except Exception as e:
# Other errors
print(f"Error: {e}")
finally:
# Always cleanup
await browser.close()
Requirements
- Python 3.12+
- Linux (uses POSIX pipes and
preexec_fn) - Chromium or Google Chrome
Links
License
MIT License - See LICENSE file for details.
Contributing
Contributions welcome! This project aims to provide a clean, type-safe interface to Chrome automation on Linux.
Release files for pypecdp 0.6.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pypecdp-0.6.3.tar.gz | 293.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pypecdp-0.6.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size:599.5 kB
Release files / pypecdp-0.6.3.tar.gz
| Download URL | pypecdp-0.6.3.tar.gz |
|---|---|
| Size | 293.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
546e23680fe711d3c1cf5be85519a502f700737dbea53e7f9cea4a026a45201c
|
|
BLAKE2b-256 checksum How to use checksums |
695678e48428329b4e935b2aa80256b02130613f65f19888f45d54230f6779a9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.
Transparency logRelease files / pypecdp-0.6.3-py3-none-any.whl
| Download URL | pypecdp-0.6.3-py3-none-any.whl |
|---|---|
| Size | 306.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
578123850cb4587eb3fb519f17d0b7417638b1151f604934b61baf1fe7898c7e
|
|
BLAKE2b-256 checksum How to use checksums |
f52d4a815ab31d9e564f705c4ad3dacbdc742fbee22117a5dcc2478c5229f0d7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.
Transparency log