1313from time import sleep
1414from typing import Dict , List , Optional
1515
16+ import browsers
1617import requests
1718from requests import Response
1819from selenium import webdriver
3940
4041
4142class 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."""
4345 chrome_version = self .get_chrome_version ()
4446
4547 # If Web Driver Manager cannot detect Chrome, it returns None.
@@ -104,13 +106,24 @@ def get_chrome_driver(self, path_to_cache_dir: str):
104106
105107 @staticmethod
106108 def _download_chromedriver (
107- chrome_major_version ,
109+ chrome_major_version : str ,
108110 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 :
112114 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 )
114127
115128 matching_versions = [
116129 item
@@ -143,6 +156,11 @@ def _download_chromedriver(
143156 )
144157 response = ChromeDriverManager .send_http_get_request (driver_url )
145158
159+ if response is None :
160+ raise Exception (
161+ f"Could not download ChromeDriver from { driver_url } "
162+ )
163+
146164 Path (path_to_driver_cache_dir ).mkdir (parents = True , exist_ok = True )
147165 zip_path = os .path .join (path_to_driver_cache_dir , "chromedriver.zip" )
148166 print ( # noqa: T201
@@ -160,7 +178,7 @@ def _download_chromedriver(
160178 return path_to_cached_chrome_driver
161179
162180 @staticmethod
163- def send_http_get_request (url , params = None , ** kwargs ) -> Response :
181+ def send_http_get_request (url , params = None , ** kwargs ) -> Optional [ Response ] :
164182 last_error : Optional [Exception ] = None
165183 for attempt in range (1 , 4 ):
166184 print ( # noqa: T201
@@ -180,6 +198,7 @@ def send_http_get_request(url, params=None, **kwargs) -> Response:
180198 f"html2print: "
181199 f"failed to get response for URL: { url } with error: { last_error } "
182200 )
201+ return None
183202
184203 @staticmethod
185204 def get_chrome_version ():
@@ -312,30 +331,35 @@ def create_webdriver(
312331) -> webdriver .Chrome :
313332 print ("html2print: creating ChromeDriver service." , flush = True ) # noqa: T201
314333 if chromedriver is None :
315- path_to_chrome = ChromeDriverManager ().get_chrome_driver (
334+ path_to_chrome_driver = ChromeDriverManager ().get_chrome_driver (
316335 path_to_cache_dir
317336 )
318337 else :
319- path_to_chrome = chromedriver
320- print (f"html2print: ChromeDriver available at path: { path_to_chrome } " ) # noqa: T201
338+ path_to_chrome_driver = chromedriver
339+ print (f"html2print: ChromeDriver available at path: { path_to_chrome_driver } " ) # noqa: T201
321340
322341 if debug :
323342 service = Service (
324- path_to_chrome , log_output = PATH_TO_CHROME_DRIVER_DEBUG_LOG
343+ path_to_chrome_driver , log_output = PATH_TO_CHROME_DRIVER_DEBUG_LOG
325344 )
326345 else :
327- service = Service (path_to_chrome )
346+ service = Service (path_to_chrome_driver )
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
328352
329353 webdriver_options = Options ()
354+ webdriver_options .binary_location = path_to_chrome
330355 webdriver_options .add_argument ("start-maximized" )
331356 webdriver_options .add_argument ("disable-infobars" )
332357 # Doesn't seem to be needed.
333358 # webdriver_options.add_argument('--disable-gpu') # noqa: ERA001
334359 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" )
339363
340364 # The Chrome option --disable-dev-shm-usage disables the use of /dev/shm
341365 # (shared memory) for temporary storage in Chrome.
@@ -447,10 +471,10 @@ def main():
447471 args .cache_dir if args .cache_dir is not None else DEFAULT_CACHE_DIR
448472 )
449473
450- path_to_chrome = ChromeDriverManager ().get_chrome_driver (
474+ path_to_chrome_driver = ChromeDriverManager ().get_chrome_driver (
451475 path_to_cache_dir
452476 )
453- print (f"html2print: ChromeDriver available at path: { path_to_chrome } " ) # noqa: T201
477+ print (f"html2print: ChromeDriver available at path: { path_to_chrome_driver } " ) # noqa: T201
454478 sys .exit (0 )
455479
456480 elif args .command == "print" :
0 commit comments