rustwright 0.1.0__cp38-abi3-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. rustwright/__init__.py +56 -0
  2. rustwright/__main__.py +5 -0
  3. rustwright/_backend.py +58 -0
  4. rustwright/_compat/__init__.py +83 -0
  5. rustwright/_compat/cloakbrowser/__init__.py +92 -0
  6. rustwright/_compat/patchright/__init__.py +2 -0
  7. rustwright/_compat/patchright/__main__.py +5 -0
  8. rustwright/_compat/patchright/_impl/__init__.py +1 -0
  9. rustwright/_compat/patchright/_impl/_errors.py +4 -0
  10. rustwright/_compat/patchright/async_api.py +2 -0
  11. rustwright/_compat/patchright/pytest_plugin.py +2 -0
  12. rustwright/_compat/patchright/sync_api.py +2 -0
  13. rustwright/_compat/playwright/__init__.py +2 -0
  14. rustwright/_compat/playwright/__main__.py +5 -0
  15. rustwright/_compat/playwright/_impl/__init__.py +2 -0
  16. rustwright/_compat/playwright/_impl/_errors.py +4 -0
  17. rustwright/_compat/playwright/async_api.py +2 -0
  18. rustwright/_compat/playwright/pytest_plugin.py +1 -0
  19. rustwright/_compat/playwright/sync_api.py +2 -0
  20. rustwright/_compat/pytest_playwright/__init__.py +3 -0
  21. rustwright/_compat/pytest_playwright/py.typed +1 -0
  22. rustwright/_compat/pytest_playwright/pytest_playwright.py +57 -0
  23. rustwright/_devices.py +1034 -0
  24. rustwright/_rustwright.pyd +0 -0
  25. rustwright/async_api.py +5745 -0
  26. rustwright/cli.py +1538 -0
  27. rustwright/pytest_plugin.py +409 -0
  28. rustwright/sync_api.py +29198 -0
  29. rustwright-0.1.0.dist-info/METADATA +239 -0
  30. rustwright-0.1.0.dist-info/RECORD +33 -0
  31. rustwright-0.1.0.dist-info/WHEEL +4 -0
  32. rustwright-0.1.0.dist-info/entry_points.txt +4 -0
  33. rustwright-0.1.0.dist-info/licenses/LICENSE +21 -0
rustwright/__init__.py ADDED
@@ -0,0 +1,56 @@
1
+ """Rust-backed browser automation with a Playwright-shaped Python API."""
2
+
3
+ from ._compat import disable_playwright_compat, enable_playwright_compat
4
+ from .sync_api import (
5
+ Browser,
6
+ BrowserContext,
7
+ BrowserType,
8
+ BackendMarker,
9
+ ConsoleMessage,
10
+ ElementHandle,
11
+ Error,
12
+ Download,
13
+ FileChooser,
14
+ Frame,
15
+ FrameLocator,
16
+ JSHandle,
17
+ Locator,
18
+ Page,
19
+ Playwright,
20
+ Response,
21
+ TargetClosedError,
22
+ TimeoutError,
23
+ Video,
24
+ backend_marker,
25
+ expect,
26
+ sync_playwright,
27
+ )
28
+ from .async_api import async_playwright
29
+
30
+ __all__ = [
31
+ "Browser",
32
+ "BrowserContext",
33
+ "BrowserType",
34
+ "BackendMarker",
35
+ "ConsoleMessage",
36
+ "Download",
37
+ "ElementHandle",
38
+ "Error",
39
+ "FileChooser",
40
+ "Frame",
41
+ "FrameLocator",
42
+ "JSHandle",
43
+ "Locator",
44
+ "Page",
45
+ "Playwright",
46
+ "Response",
47
+ "TargetClosedError",
48
+ "TimeoutError",
49
+ "Video",
50
+ "backend_marker",
51
+ "disable_playwright_compat",
52
+ "enable_playwright_compat",
53
+ "expect",
54
+ "async_playwright",
55
+ "sync_playwright",
56
+ ]
rustwright/__main__.py ADDED
@@ -0,0 +1,5 @@
1
+ from .cli import main
2
+
3
+
4
+ if __name__ == "__main__":
5
+ raise SystemExit(main(program="rustwright"))
rustwright/_backend.py ADDED
@@ -0,0 +1,58 @@
1
+ from __future__ import annotations
2
+
3
+ from importlib import metadata
4
+ from pathlib import Path
5
+ from typing import Optional, TypedDict
6
+
7
+ from . import _rustwright
8
+
9
+
10
+ class BackendMarker(TypedDict):
11
+ implementation: str
12
+ package: str
13
+ version: str
14
+ api_package: str
15
+ api_module: str
16
+ replacement_backend: bool
17
+ runtime: str
18
+ runtime_module: str
19
+ runtime_module_file: Optional[str]
20
+ transport: str
21
+ transport_protocol: str
22
+ cdp_first: bool
23
+ python_playwright_driver: bool
24
+ playwright_driver: str
25
+
26
+
27
+ def _version() -> str:
28
+ try:
29
+ return metadata.version("rustwright")
30
+ except metadata.PackageNotFoundError:
31
+ return "0.1.0+local"
32
+
33
+
34
+ def backend_marker(api_module: str | None = None) -> BackendMarker:
35
+ """Return JSON-safe evidence that this API is backed by Rustwright CDP."""
36
+
37
+ module_name = api_module or "rustwright"
38
+ api_package = module_name.split(".", 1)[0]
39
+ runtime_file = getattr(_rustwright, "__file__", None)
40
+ return {
41
+ "implementation": "rustwright",
42
+ "package": "rustwright",
43
+ "version": _version(),
44
+ "api_package": api_package,
45
+ "api_module": module_name,
46
+ "replacement_backend": True,
47
+ "runtime": "rust-pyo3-extension",
48
+ "runtime_module": _rustwright.__name__,
49
+ "runtime_module_file": str(Path(runtime_file).resolve()) if runtime_file else None,
50
+ "transport": "raw-cdp",
51
+ "transport_protocol": "Chrome DevTools Protocol",
52
+ "cdp_first": True,
53
+ "python_playwright_driver": False,
54
+ "playwright_driver": "none",
55
+ }
56
+
57
+
58
+ __all__ = ["BackendMarker", "backend_marker"]
@@ -0,0 +1,83 @@
1
+ """Explicit opt-in Playwright/Patchright/Cloakbrowser import compatibility."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib
6
+ import sys
7
+ from types import ModuleType
8
+ from typing import Optional
9
+
10
+
11
+ _ALIASES = (
12
+ ("playwright", "rustwright._compat.playwright"),
13
+ ("playwright.__main__", "rustwright._compat.playwright.__main__"),
14
+ ("playwright._impl", "rustwright._compat.playwright._impl"),
15
+ ("playwright._impl._errors", "rustwright._compat.playwright._impl._errors"),
16
+ ("playwright.async_api", "rustwright._compat.playwright.async_api"),
17
+ ("playwright.pytest_plugin", "rustwright._compat.playwright.pytest_plugin"),
18
+ ("playwright.sync_api", "rustwright._compat.playwright.sync_api"),
19
+ ("patchright", "rustwright._compat.patchright"),
20
+ ("patchright.__main__", "rustwright._compat.patchright.__main__"),
21
+ ("patchright._impl", "rustwright._compat.patchright._impl"),
22
+ ("patchright._impl._errors", "rustwright._compat.patchright._impl._errors"),
23
+ ("patchright.async_api", "rustwright._compat.patchright.async_api"),
24
+ ("patchright.pytest_plugin", "rustwright._compat.patchright.pytest_plugin"),
25
+ ("patchright.sync_api", "rustwright._compat.patchright.sync_api"),
26
+ ("cloakbrowser", "rustwright._compat.cloakbrowser"),
27
+ ("pytest_playwright", "rustwright._compat.pytest_playwright"),
28
+ ("pytest_playwright.pytest_playwright", "rustwright._compat.pytest_playwright.pytest_playwright"),
29
+ )
30
+
31
+ _PREVIOUS_MODULES: dict[str, Optional[ModuleType]] = {}
32
+ _ENABLED = False
33
+
34
+
35
+ def _set_parent_attribute(module_name: str, module: ModuleType) -> None:
36
+ parent_name, _, child_name = module_name.rpartition(".")
37
+ if not parent_name:
38
+ return
39
+ parent = sys.modules.get(parent_name)
40
+ if parent is not None:
41
+ setattr(parent, child_name, module)
42
+
43
+
44
+ def enable_playwright_compat() -> None:
45
+ """Enable legacy Playwright-compatible import names for this Python process.
46
+
47
+ After this is called, subsequent imports such as ``playwright.sync_api`` or
48
+ ``patchright.async_api`` resolve to Rustwright's compatibility shims.
49
+ """
50
+
51
+ global _ENABLED
52
+ if _ENABLED:
53
+ return
54
+
55
+ loaded_modules = [(alias_name, importlib.import_module(target_name)) for alias_name, target_name in _ALIASES]
56
+ for alias_name, module in loaded_modules:
57
+ _PREVIOUS_MODULES[alias_name] = sys.modules.get(alias_name)
58
+ sys.modules[alias_name] = module
59
+ _set_parent_attribute(alias_name, module)
60
+
61
+ _ENABLED = True
62
+
63
+
64
+ def disable_playwright_compat() -> None:
65
+ """Undo aliases installed by :func:`enable_playwright_compat`."""
66
+
67
+ global _ENABLED
68
+ if not _ENABLED:
69
+ return
70
+
71
+ for alias_name, _target_name in _ALIASES:
72
+ previous = _PREVIOUS_MODULES.get(alias_name)
73
+ if previous is None:
74
+ sys.modules.pop(alias_name, None)
75
+ else:
76
+ sys.modules[alias_name] = previous
77
+ _set_parent_attribute(alias_name, previous)
78
+
79
+ _PREVIOUS_MODULES.clear()
80
+ _ENABLED = False
81
+
82
+
83
+ __all__ = ["disable_playwright_compat", "enable_playwright_compat"]
@@ -0,0 +1,92 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ from rustwright.async_api import async_playwright
8
+ from rustwright.sync_api import sync_playwright
9
+
10
+ _VALID_BACKENDS = {"playwright", "patchright"}
11
+
12
+
13
+ def _resolve_backend(backend: Any = None) -> str:
14
+ value = backend if backend is not None else os.environ.get("CLOAKBROWSER_BACKEND", "playwright")
15
+ if not isinstance(value, str):
16
+ raise ValueError(f"Invalid cloakbrowser backend type: {type(value).__name__}")
17
+ normalized = value.strip().lower()
18
+ if normalized not in _VALID_BACKENDS:
19
+ raise ValueError(f"Invalid cloakbrowser backend: {value!r}. Must be one of {sorted(_VALID_BACKENDS)}")
20
+ return normalized
21
+
22
+
23
+ def _normalize_proxy(proxy: Any) -> Any:
24
+ if isinstance(proxy, str):
25
+ return {"server": proxy}
26
+ return proxy
27
+
28
+
29
+ def _normalize_common_options(options: dict[str, Any], *, persistent: bool) -> dict[str, Any]:
30
+ normalized = dict(options)
31
+ _resolve_backend(normalized.pop("backend", None))
32
+ normalized.pop("geoip", None)
33
+ normalized.pop("humanize", None)
34
+
35
+ if "proxy" in normalized:
36
+ normalized["proxy"] = _normalize_proxy(normalized["proxy"])
37
+
38
+ if persistent and "timezone" in normalized and "timezone_id" not in normalized:
39
+ normalized["timezone_id"] = normalized.pop("timezone")
40
+ else:
41
+ normalized.pop("timezone", None)
42
+
43
+ return normalized
44
+
45
+
46
+ def ensure_binary(*, force: bool = False, **_: Any) -> str:
47
+ from rustwright.cli import _chromium_executable_path, _download_chromium
48
+
49
+ executable = _chromium_executable_path()
50
+ if executable and not force:
51
+ return executable
52
+ result = _download_chromium(force=force, dry_run=False)
53
+ return str(result["executable"])
54
+
55
+
56
+ def launch(**kwargs: Any) -> Any:
57
+ manager = sync_playwright()
58
+ playwright = manager.start()
59
+ return playwright.chromium.launch(**_normalize_common_options(kwargs, persistent=False))
60
+
61
+
62
+ async def launch_async(**kwargs: Any) -> Any:
63
+ manager = async_playwright()
64
+ playwright = await manager.start()
65
+ return await playwright.chromium.launch(**_normalize_common_options(kwargs, persistent=False))
66
+
67
+
68
+ def launch_persistent_context(user_data_dir: str | Path, **kwargs: Any) -> Any:
69
+ manager = sync_playwright()
70
+ playwright = manager.start()
71
+ return playwright.chromium.launch_persistent_context(
72
+ user_data_dir,
73
+ **_normalize_common_options(kwargs, persistent=True),
74
+ )
75
+
76
+
77
+ async def launch_persistent_context_async(user_data_dir: str | Path, **kwargs: Any) -> Any:
78
+ manager = async_playwright()
79
+ playwright = await manager.start()
80
+ return await playwright.chromium.launch_persistent_context(
81
+ user_data_dir,
82
+ **_normalize_common_options(kwargs, persistent=True),
83
+ )
84
+
85
+
86
+ __all__ = [
87
+ "ensure_binary",
88
+ "launch",
89
+ "launch_async",
90
+ "launch_persistent_context",
91
+ "launch_persistent_context_async",
92
+ ]
@@ -0,0 +1,2 @@
1
+ from rustwright.sync_api import * # noqa: F401,F403
2
+
@@ -0,0 +1,5 @@
1
+ from rustwright.cli import main
2
+
3
+
4
+ if __name__ == "__main__":
5
+ raise SystemExit(main(program="patchright"))
@@ -0,0 +1,4 @@
1
+ from rustwright.sync_api import Error, TargetClosedError, TimeoutError
2
+
3
+ __all__ = ["Error", "TargetClosedError", "TimeoutError"]
4
+
@@ -0,0 +1,2 @@
1
+ from rustwright.async_api import * # noqa: F401,F403
2
+ from rustwright.async_api import __all__ as __all__
@@ -0,0 +1,2 @@
1
+ from rustwright.pytest_plugin import * # noqa: F401,F403
2
+
@@ -0,0 +1,2 @@
1
+ from rustwright.sync_api import * # noqa: F401,F403
2
+ from rustwright.sync_api import __all__ as __all__
@@ -0,0 +1,2 @@
1
+ from rustwright.sync_api import * # noqa: F401,F403
2
+
@@ -0,0 +1,5 @@
1
+ from rustwright.cli import main
2
+
3
+
4
+ if __name__ == "__main__":
5
+ raise SystemExit(main(program="playwright"))
@@ -0,0 +1,2 @@
1
+ """Private Playwright compatibility namespace used by legacy integrations."""
2
+
@@ -0,0 +1,4 @@
1
+ from rustwright.sync_api import Error, TargetClosedError, TimeoutError
2
+
3
+ __all__ = ["Error", "TargetClosedError", "TimeoutError"]
4
+
@@ -0,0 +1,2 @@
1
+ from rustwright.async_api import * # noqa: F401,F403
2
+ from rustwright.async_api import __all__ as __all__
@@ -0,0 +1 @@
1
+ from rustwright.pytest_plugin import * # noqa: F401,F403
@@ -0,0 +1,2 @@
1
+ from rustwright.sync_api import * # noqa: F401,F403
2
+ from rustwright.sync_api import __all__ as __all__
@@ -0,0 +1,3 @@
1
+ from .pytest_playwright import CreateContextCallback
2
+
3
+ __all__ = ["CreateContextCallback"]
@@ -0,0 +1,57 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import Any, Dict, Literal, Optional, Pattern, Protocol, Sequence, Union
5
+
6
+ from rustwright.pytest_plugin import * # noqa: F401,F403
7
+ from rustwright.sync_api import (
8
+ BrowserContext,
9
+ Geolocation,
10
+ HttpCredentials,
11
+ ProxySettings,
12
+ StorageState,
13
+ ViewportSize,
14
+ )
15
+
16
+
17
+ class CreateContextCallback(Protocol):
18
+ def __call__(
19
+ self,
20
+ viewport: Optional[ViewportSize] = None,
21
+ screen: Optional[ViewportSize] = None,
22
+ no_viewport: Optional[bool] = None,
23
+ ignore_https_errors: Optional[bool] = None,
24
+ java_script_enabled: Optional[bool] = None,
25
+ bypass_csp: Optional[bool] = None,
26
+ user_agent: Optional[str] = None,
27
+ locale: Optional[str] = None,
28
+ timezone_id: Optional[str] = None,
29
+ geolocation: Optional[Geolocation] = None,
30
+ permissions: Optional[Sequence[str]] = None,
31
+ extra_http_headers: Optional[Dict[str, str]] = None,
32
+ offline: Optional[bool] = None,
33
+ http_credentials: Optional[HttpCredentials] = None,
34
+ device_scale_factor: Optional[float] = None,
35
+ is_mobile: Optional[bool] = None,
36
+ has_touch: Optional[bool] = None,
37
+ color_scheme: Optional[Literal["dark", "light", "no-preference", "no-override", "null"]] = None,
38
+ reduced_motion: Optional[Literal["reduce", "no-preference", "no-override", "null"]] = None,
39
+ forced_colors: Optional[Literal["active", "none", "no-override", "null"]] = None,
40
+ contrast: Optional[Literal["no-preference", "more", "no-override"]] = None,
41
+ accept_downloads: Optional[bool] = None,
42
+ default_browser_type: Optional[str] = None,
43
+ proxy: Optional[ProxySettings] = None,
44
+ record_har_path: Optional[Union[str, Path]] = None,
45
+ record_har_omit_content: Optional[bool] = None,
46
+ record_video_dir: Optional[Union[str, Path]] = None,
47
+ record_video_size: Optional[ViewportSize] = None,
48
+ storage_state: Optional[Union[StorageState, str, Path]] = None,
49
+ base_url: Optional[str] = None,
50
+ strict_selectors: Optional[bool] = None,
51
+ service_workers: Optional[Literal["allow", "block"]] = None,
52
+ record_har_url_filter: Optional[Union[str, Pattern[str]]] = None,
53
+ record_har_mode: Optional[Literal["full", "minimal"]] = None,
54
+ record_har_content: Optional[Literal["attach", "embed", "omit"]] = None,
55
+ client_certificates: Optional[list[Any]] = None,
56
+ ) -> BrowserContext:
57
+ ...