13
13
from time import sleep
14
14
from typing import Dict , List , Optional
15
15
16
+ import browsers
16
17
import requests
17
18
from requests import Response
18
19
from selenium import webdriver
39
40
40
41
41
42
class ChromeDriverManager :
42
- def get_chrome_driver (self , path_to_cache_dir : str ):
43
+ def get_chrome_driver (self , path_to_cache_dir : str ) -> str :
44
+ """Return path to downloaded chromedriver."""
43
45
chrome_version = self .get_chrome_version ()
44
46
45
47
# If Web Driver Manager cannot detect Chrome, it returns None.
@@ -104,13 +106,24 @@ def get_chrome_driver(self, path_to_cache_dir: str):
104
106
105
107
@staticmethod
106
108
def _download_chromedriver (
107
- chrome_major_version ,
109
+ chrome_major_version : str ,
108
110
os_type : str ,
109
- path_to_driver_cache_dir ,
110
- path_to_cached_chrome_driver ,
111
- ):
111
+ path_to_driver_cache_dir : str ,
112
+ path_to_cached_chrome_driver : str ,
113
+ ) -> str :
112
114
url = "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json"
113
- response = ChromeDriverManager .send_http_get_request (url ).json ()
115
+ response = ChromeDriverManager .send_http_get_request (url )
116
+ if response is None :
117
+ raise RuntimeError (
118
+ "Could not download known-good-versions-with-downloads.json"
119
+ )
120
+
121
+ response = response .json ()
122
+ if response is None :
123
+ raise RuntimeError (
124
+ "Could not parse known-good-versions-with-downloads.json"
125
+ )
126
+ assert isinstance (response , dict )
114
127
115
128
matching_versions = [
116
129
item
@@ -143,6 +156,11 @@ def _download_chromedriver(
143
156
)
144
157
response = ChromeDriverManager .send_http_get_request (driver_url )
145
158
159
+ if response is None :
160
+ raise Exception (
161
+ f"Could not download ChromeDriver from { driver_url } "
162
+ )
163
+
146
164
Path (path_to_driver_cache_dir ).mkdir (parents = True , exist_ok = True )
147
165
zip_path = os .path .join (path_to_driver_cache_dir , "chromedriver.zip" )
148
166
print ( # noqa: T201
@@ -160,7 +178,7 @@ def _download_chromedriver(
160
178
return path_to_cached_chrome_driver
161
179
162
180
@staticmethod
163
- def send_http_get_request (url , params = None , ** kwargs ) -> Response :
181
+ def send_http_get_request (url , params = None , ** kwargs ) -> Optional [ Response ] :
164
182
last_error : Optional [Exception ] = None
165
183
for attempt in range (1 , 4 ):
166
184
print ( # noqa: T201
@@ -180,9 +198,10 @@ def send_http_get_request(url, params=None, **kwargs) -> Response:
180
198
f"html2print: "
181
199
f"failed to get response for URL: { url } with error: { last_error } "
182
200
)
201
+ return None
183
202
184
203
@staticmethod
185
- def get_chrome_version ():
204
+ def get_chrome_version () -> str :
186
205
# Special case: GitHub Actions macOS CI machines have both
187
206
# Google Chrome for Testing and normal Google Chrome installed, and
188
207
# sometimes their versions are of different major version families.
@@ -225,7 +244,7 @@ def get_chrome_version():
225
244
)
226
245
227
246
os_manager = OperationSystemManager (os_type = None )
228
- version = os_manager .get_browser_version_from_os (ChromeType .GOOGLE )
247
+ version = str ( os_manager .get_browser_version_from_os (ChromeType .GOOGLE ) )
229
248
return version
230
249
231
250
@@ -312,30 +331,35 @@ def create_webdriver(
312
331
) -> webdriver .Chrome :
313
332
print ("html2print: creating ChromeDriver service." , flush = True ) # noqa: T201
314
333
if chromedriver is None :
315
- path_to_chrome = ChromeDriverManager ().get_chrome_driver (
334
+ path_to_chromedriver = ChromeDriverManager ().get_chrome_driver (
316
335
path_to_cache_dir
317
336
)
318
337
else :
319
- path_to_chrome = chromedriver
320
- print (f"html2print: ChromeDriver available at path: { path_to_chrome } " ) # noqa: T201
338
+ path_to_chromedriver = chromedriver
339
+ print (f"html2print: ChromeDriver available at path: { path_to_chromedriver } " ) # noqa: T201
321
340
322
341
if debug :
323
342
service = Service (
324
- path_to_chrome , log_output = PATH_TO_CHROME_DRIVER_DEBUG_LOG
343
+ path_to_chromedriver , log_output = PATH_TO_CHROME_DRIVER_DEBUG_LOG
325
344
)
326
345
else :
327
- service = Service (path_to_chrome )
346
+ service = Service (path_to_chromedriver )
347
+
348
+ path_to_chrome = ""
349
+ if platform .system () == "Windows" :
350
+ path_to_chrome = browsers .get ("chrome" )["path" ]
351
+ print (f"html2print: Chrome available at path: { path_to_chrome } " ) # noqa: T201
328
352
329
353
webdriver_options = Options ()
354
+ webdriver_options .binary_location = path_to_chrome
330
355
webdriver_options .add_argument ("start-maximized" )
331
356
webdriver_options .add_argument ("disable-infobars" )
332
357
# Doesn't seem to be needed.
333
358
# webdriver_options.add_argument('--disable-gpu') # noqa: ERA001
334
359
webdriver_options .add_argument ("--disable-extensions" )
335
- webdriver_options .add_argument ("--headless=chrome" )
336
- # FIXME: This is not nice but otherwise it does not work in Ubuntu 24-based Docker image.
337
- # https://github.com/SeleniumHQ/selenium/issues/15327#issuecomment-2689287561
338
- webdriver_options .add_argument ("--no-sandbox" )
360
+ # Use --headless=new, as it seems to be more stable on Windows (available since Chrome 109).
361
+ # see https://www.selenium.dev/blog/2023/headless-is-going-away/
362
+ webdriver_options .add_argument ("--headless=new" )
339
363
340
364
# The Chrome option --disable-dev-shm-usage disables the use of /dev/shm
341
365
# (shared memory) for temporary storage in Chrome.
0 commit comments