feat: public config helpers for third-party integrations (#25)

Adds invisible_playwright.config module with:
- get_default_stealth_prefs(seed, *, pin, locale, timezone,
  extra_prefs, humanize, virtual_display) -> dict
- get_default_args() -> list

Both also re-exported at the package root alongside the existing
InvisiblePlaywright. ensure_binary is also re-exported there for
parity with the cloakbrowser.download.ensure_binary integration
pattern that downstream projects (Skyvern PR #5340, crawlee-python
PR #1794, agno PR #8129) already expect.

These helpers let third-party fetchers (changedetection.io plugins,
Crawlee BrowserPool subclasses, agno toolkits) drive
playwright.firefox.launch(executable_path=..., firefox_user_prefs=...)
themselves without depending on the InvisiblePlaywright context
manager owning the lifecycle. Same seed semantics, same humanize
toggle, same extra_prefs overlay as the existing wrapper.

Tests: tests/unit/test_config_public.py adds 14 unit tests covering
deterministic seed, locale/timezone/pin/extra_prefs/humanize
variations, and round-trip via the public namespace. Full unit suite
(392 tests) stays green.

Backwards compatible: InvisiblePlaywright surface is unchanged.
BINARY_VERSION stays at firefox-7. Python-only release.
This commit is contained in:
Federico
2026-05-28 17:05:22 -07:00
committed by GitHub
parent 929da150bc
commit ee0fe57ced
6 changed files with 267 additions and 3 deletions
+19
View File
@@ -172,6 +172,25 @@ invisible_playwright version # wrapper and binary versions
invisible_playwright clear-cache # remove all cached binaries
```
## Public API for downstream integrations
When you're building a third-party fetcher (a Crawlee `BrowserPool` subclass, a changedetection.io plugin, an agno toolkit, a Skyvern backend) and need to own the browser lifecycle yourself, use the public helpers instead of `InvisiblePlaywright`:
```python
from playwright.async_api import async_playwright
from invisible_playwright import ensure_binary, get_default_stealth_prefs
async with async_playwright() as p:
browser = await p.firefox.launch(
executable_path=str(ensure_binary()),
firefox_user_prefs=get_default_stealth_prefs(seed=42),
)
```
`get_default_stealth_prefs(seed, *, pin, locale, timezone, extra_prefs, humanize, virtual_display)` returns the same dict that `InvisiblePlaywright(seed=..., locale=..., ...)` would inject. Same deterministic seed semantics, same humanize toggle, same `extra_prefs` overlay. `ensure_binary()` downloads the patched Firefox on first call and returns its absolute path.
For everyday Python usage the `InvisiblePlaywright` context manager is still the recommended entry point.
## Related projects
invisible_playwright takes a different angle than the major Firefox-hardening projects but stands on their shoulders: