Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: export build_launch_options helper for composing with existing Playwright instance#255

Open
mvanhorn wants to merge 2 commits into
CloakHQ:main from
mvanhorn:feat/153-python-build-launch-options
Open

feat: export build_launch_options helper for composing with existing Playwright instance #255
mvanhorn wants to merge 2 commits into
CloakHQ:main from
mvanhorn:feat/153-python-build-launch-options

Conversation

@mvanhorn

@mvanhorn mvanhorn commented May 17, 2026

Copy link
Copy Markdown

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 of launch() 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 a playwright param 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 a playwright= kwarg can layer on later if preferred - it composes with this helper.

Testing

  • 124 unit tests pass: test_build_args, test_config, test_proxy, test_backend, test_geoip, test_extension_loading.
  • py_compile clean across cloakbrowser/browser.py, cloakbrowser/__init__.py, examples/external_playwright.py.
  • build_launch_options() returns executable_path, headless, args, ignore_default_args; adds a proxy dict only when a proxy URL is passed.
  • launch() is refactored to delegate to build_launch_options(), so the existing call path produces identical kwargs for pw.chromium.launch().
  • The example at examples/external_playwright.py shows the sync_playwright() + build_launch_options() pattern from the maintainer reply.

Scope kept to the sync path. Async launch_async, launch_context, launch_persistent_context are untouched and can follow as a separate PR if useful.

Fixes #153

...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).

Copy link
Copy Markdown
Contributor

Thanks for the PR, @mvanhorn! This addresses #153 nicely.

Two things before we can merge:

  1. Missing humanize_browser() helper — The JS equivalent (feat(js): export composable launch helpers #244 ) exports both buildLaunchOptions and humanizeBrowser. For parity, this should also export a humanize_browser(browser, ...) so users can apply humanize to their own Playwright instance.

  2. launch_async() still has duplicated logiclaunch() now delegates to build_launch_options(), but launch_async() still has its own copy of the same arg-building code. It should delegate to build_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.

Copy link
Copy Markdown
Author

Thanks for the careful review. Both items addressed in 17c85c3:

  1. Exported humanize_browser() and humanize_browser_async(). Callers who launched their own Playwright instance (e.g. via build_launch_options()) can now apply the stealth humanize patches with humanize_browser(browser, preset='default', config=None). The sync and async variants stay split because patch_browser and patch_browser_async are themselves separate. launch(humanize=True) and launch_async(humanize=True) now delegate to these helpers, so the patch logic stays single-sourced.

  2. launch_async() delegates to build_launch_options(). The duplicated geoip/proxy/webrtc/build_args block is gone from launch_async. It now mirrors launch() exactly: build the opts dict, log args count, await pw.chromium.launch(**opts). Dropped the # noqa: C901 since complexity is back under threshold.

  3. Tests. Added tests/test_build_launch_options.py covering: build_launch_options schema (executable_path, ignore_default_args, timezone/locale flags merged into args), both humanize helpers (with patch_browser / patch_browser_async mocked so the test doesn't need a real Browser), and launch_async delegation (asserts build_launch_options is called with forwarded kwargs and pw.chromium.launch is awaited with the returned dict). Focused suite passes; full pytest requires playwright installed 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Function to launch browser with a pre-existent playwright context

AltStyle によって変換されたページ (->オリジナル) /