-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
feat: export build_launch_options helper for composing with existing Playwright instance#255
feat: export build_launch_options helper for composing with existing Playwright instance #255mvanhorn wants to merge 2 commits into
Conversation
...Playwright Closes CloakHQ#153. Adds build_launch_options(headless, proxy, args, stealth_args, timezone, locale, geoip, extension_paths, **kwargs) which returns the kwargs dict that launch() forwards to playwright.chromium.launch(). Callers can now drive their own sync_playwright() instance and pass pw.chromium.launch(**opts), instead of copy-pasting the body of launch() per release. launch() is refactored to delegate to build_launch_options so behavior stays identical. Adds examples/external_playwright.py showing the maintainer reply's pattern (own Playwright + build_launch_options + pw.chromium.launch).
Cloak-HQ
commented
May 17, 2026
Thanks for the PR, @mvanhorn! This addresses #153 nicely.
Two things before we can merge:
-
Missing
humanize_browser()helper — The JS equivalent (feat(js): export composable launch helpers #244 ) exports bothbuildLaunchOptionsandhumanizeBrowser. For parity, this should also export ahumanize_browser(browser, ...)so users can apply humanize to their own Playwright instance. -
launch_async()still has duplicated logic —launch()now delegates tobuild_launch_options(), butlaunch_async()still has its own copy of the same arg-building code. It should delegate tobuild_launch_options()too, otherwise they'll diverge over time.
Let us know if you'd like to update, or we can handle it on our end.
...a build_launch_options Adds humanize_browser() and humanize_browser_async() helpers so callers who run their own Playwright instance can apply stealth humanize patches. launch_async() now delegates to build_launch_options() to match launch(), eliminating the duplicated geoip/proxy/webrtc/build_args block.
mvanhorn
commented
May 19, 2026
Thanks for the careful review. Both items addressed in 17c85c3:
-
Exported
humanize_browser()andhumanize_browser_async(). Callers who launched their own Playwright instance (e.g. viabuild_launch_options()) can now apply the stealth humanize patches withhumanize_browser(browser, preset='default', config=None). The sync and async variants stay split becausepatch_browserandpatch_browser_asyncare themselves separate.launch(humanize=True)andlaunch_async(humanize=True)now delegate to these helpers, so the patch logic stays single-sourced. -
launch_async()delegates tobuild_launch_options(). The duplicated geoip/proxy/webrtc/build_args block is gone fromlaunch_async. It now mirrorslaunch()exactly: build the opts dict, log args count,await pw.chromium.launch(**opts). Dropped the# noqa: C901since complexity is back under threshold. -
Tests. Added
tests/test_build_launch_options.pycovering:build_launch_optionsschema (executable_path, ignore_default_args, timezone/locale flags merged into args), both humanize helpers (withpatch_browser/patch_browser_asyncmocked so the test doesn't need a real Browser), andlaunch_asyncdelegation (assertsbuild_launch_optionsis called with forwarded kwargs andpw.chromium.launchis awaited with the returned dict). Focused suite passes; fullpytestrequiresplaywrightinstalled which I didn't add as a dev dep here.
Let me know if you'd prefer a single humanize_browser() that auto-detects sync vs async by inspecting the Browser object instead of the two-name form; happy to switch.
Summary
Users who run their own
sync_playwright()context can now pull CloakBrowser's launch args into it with one call instead of copying the body oflaunch()each release.Why this matters
In #153, @chrisemke described maintaining scrapers that share a single Playwright instance across multiple stealth backends, and asked whether
launch()could accept aplaywrightparam or expose a lower-level helper. The maintainer reply asked the same question.This PR takes the lower-level-helper route to keep the existing
launch()signature stable. Adding aplaywright=kwarg can layer on later if preferred - it composes with this helper.Testing
test_build_args,test_config,test_proxy,test_backend,test_geoip,test_extension_loading.py_compileclean acrosscloakbrowser/browser.py,cloakbrowser/__init__.py,examples/external_playwright.py.build_launch_options()returnsexecutable_path,headless,args,ignore_default_args; adds aproxydict only when a proxy URL is passed.launch()is refactored to delegate tobuild_launch_options(), so the existing call path produces identical kwargs forpw.chromium.launch().examples/external_playwright.pyshows thesync_playwright() + build_launch_options()pattern from the maintainer reply.Scope kept to the sync path. Async
launch_async,launch_context,launch_persistent_contextare untouched and can follow as a separate PR if useful.Fixes #153