|
2 | 2 |
|
3 | 3 | import os
|
4 | 4 | import platform
|
| 5 | +import subprocess |
5 | 6 | import sys
|
6 | 7 | from pathlib import Path
|
7 | 8 |
|
8 | 9 | if platform.system() == "Windows":
|
9 | 10 | import msvcrt
|
10 | 11 |
|
11 | 12 | from choreographer._channels import Pipe
|
12 |
| -from choreographer._sys_utils import get_browser_path |
| 13 | +from choreographer._sys_utils import TmpDirectory, get_browser_path |
13 | 14 |
|
| 15 | +from ._chrome_constants import chrome_names, typical_chrome_paths |
14 | 16 |
|
15 |
| -class Chromium: |
16 |
| - def __init__(self, channel): |
17 |
| - self._channel = channel |
18 |
| - if not isinstance(channel, Pipe): |
19 |
| - raise NotImplementedError("Websocket style channels not implemented yet") |
| 17 | +chromium_wrapper_path = Path(__file__).resolve().parent / "chromium_wrapper.py" |
| 18 | + |
| 19 | + |
| 20 | +def _is_exe(path): |
| 21 | + try: |
| 22 | + return os.access(path, os.X_OK) |
| 23 | + except: # noqa: E722 bare except ok, weird errors, best effort. |
| 24 | + return False |
20 | 25 |
|
21 |
| - # where do we get user data dir |
22 |
| - def get_cli(self, temp_dir, **kwargs): |
23 |
| - gpu_enabled = kwargs.pop("with_gpu", False) |
24 |
| - headless = kwargs.pop("headless", True) |
25 |
| - sandbox = kwargs.pop("with_sandbox", False) |
| 26 | + |
| 27 | +class Chromium: |
| 28 | + def __init__(self, channel, **kwargs): |
| 29 | + self.gpu_enabled = kwargs.pop("with_gpu", False) |
| 30 | + self.headless = kwargs.pop("headless", True) |
| 31 | + self.sandbox_enabled = kwargs.pop("with_sandbox", False) |
| 32 | + self.path = kwargs.pop("path", None) |
| 33 | + self._tmp_dir_path = kwargs.pop("tmp_dir", None) |
| 34 | + self.skip_local = bool( |
| 35 | + "ubuntu" in platform.version().lower() and self.enable_sandbox, |
| 36 | + ) |
26 | 37 | if kwargs:
|
27 | 38 | raise RuntimeError(
|
28 | 39 | "Chromium.get_cli() received " f"invalid args: {kwargs.keys()}",
|
29 | 40 | )
|
30 |
| - path = get_browser_path() |
31 |
| - if not path: |
32 |
| - raise RuntimeError("Browser not found.") |
33 | 41 |
|
34 |
| - chromium_wrapper_path = Path(__file__).resolve().parent / "chromium_wrapper.py" |
| 42 | + if not self.path: |
| 43 | + self.path = get_browser_path(chrome_names, skip_local=self.skip_local) |
| 44 | + if not self.path: |
| 45 | + # do typical chrome paths |
| 46 | + for candidate in typical_chrome_paths: |
| 47 | + if _is_exe(candidate): |
| 48 | + self.path = candidate |
| 49 | + break |
| 50 | + if not self.path: |
| 51 | + raise RuntimeError( |
| 52 | + "Browser not found. You can use get_chrome(), " |
| 53 | + "please see documentation.", |
| 54 | + ) |
| 55 | + self._channel = channel |
| 56 | + if not isinstance(channel, Pipe): |
| 57 | + raise NotImplementedError("Websocket style channels not implemented yet") |
| 58 | + |
| 59 | + self.tmp_dir = TmpDirectory( |
| 60 | + path=self._tmp_dir_path, |
| 61 | + sneak="snap" in self.path, |
| 62 | + ) |
| 63 | + |
| 64 | + def get_popen_args(self): |
| 65 | + args = {} |
| 66 | + # need to check pipe |
| 67 | + if platform.system() == "Windows": |
| 68 | + args["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP |
| 69 | + args["close_fds"] = False |
| 70 | + else: |
| 71 | + args["close_fds"] = True |
| 72 | + if isinstance(self.channel, Pipe): |
| 73 | + args["stdin"] = self.channel.from_choreo_to_external |
| 74 | + args["stdout"] = self.channel.from_external_to_choreo |
| 75 | + |
| 76 | + def get_cli(self): |
35 | 77 | if platform.system() != "Windows":
|
36 | 78 | cli = [
|
37 | 79 | sys.executable,
|
38 | 80 | chromium_wrapper_path,
|
39 |
| - path, |
| 81 | + self.path, |
40 | 82 | ]
|
41 | 83 | else:
|
42 | 84 | cli = [
|
43 |
| - path, |
| 85 | + self.path, |
44 | 86 | ]
|
45 | 87 |
|
46 | 88 | cli.extend(
|
47 | 89 | [
|
48 | 90 | "--disable-breakpad",
|
49 | 91 | "--allow-file-access-from-files",
|
50 | 92 | "--enable-logging=stderr",
|
51 |
| - f"--user-data-dir={temp_dir}", |
| 93 | + f"--user-data-dir={self.tmp_dir.name}", |
52 | 94 | "--no-first-run",
|
53 | 95 | "--enable-unsafe-swiftshader",
|
54 | 96 | ],
|
55 | 97 | )
|
56 |
| - if not gpu_enabled: |
| 98 | + if not self.gpu_enabled: |
57 | 99 | cli.append("--disable-gpu")
|
58 |
| - if headless: |
| 100 | + if self.headless: |
59 | 101 | cli.append("--headless")
|
60 |
| - if not sandbox: |
| 102 | + if not self.sandbox_enabled: |
61 | 103 | cli.append("--no-sandbox")
|
62 | 104 |
|
63 | 105 | if isinstance(self._channel, Pipe):
|
64 | 106 | cli.append("--remote-debugging-pipe")
|
65 | 107 | if platform.system() == "Windows":
|
66 |
| - _inheritable = True |
67 | 108 | w_handle = msvcrt.get_osfhandle(self._channel.from_choreo_to_external)
|
68 | 109 | r_handle = msvcrt.get_osfhandle(self._channel.from_external_to_choreo)
|
| 110 | + _inheritable = True |
69 | 111 | os.set_handle_inheritable(w_handle, _inheritable)
|
70 | 112 | os.set_handle_inheritable(r_handle, _inheritable)
|
71 | 113 | cli += [
|
72 | 114 | f"--remote-debugging-io-pipes={r_handle!s},{w_handle!s}",
|
73 | 115 | ]
|
| 116 | + return cli |
74 | 117 |
|
| 118 | + def get_env(): |
| 119 | + return os.environ().copy() |
75 | 120 |
|
76 |
| -def get_env(): |
77 |
| - return os.environ().copy() |
| 121 | + def clean(): |
| 122 | + raise ValueError("Look at tempdir") |
0 commit comments