33import atexit
44import base64
55import os .path
6+ import platform
67import sys
8+ import zipfile
79from datetime import datetime
810from pathlib import Path
9- from shutil import copy
1011from time import sleep
1112from typing import Dict , List , Optional
1213
1516from selenium import webdriver
1617from selenium .webdriver .chrome .options import Options
1718from selenium .webdriver .chrome .service import Service
18- from webdriver_manager .chrome import ChromeDriverManager
19- from webdriver_manager .core .download_manager import WDMDownloadManager
20- from webdriver_manager .core .driver import Driver
21- from webdriver_manager .core .driver_cache import DriverCacheManager
22- from webdriver_manager .core .file_manager import FileManager
23- from webdriver_manager .core .http import HttpClient
24- from webdriver_manager .core .os_manager import OperationSystemManager
19+ from webdriver_manager .core .os_manager import ChromeType , OperationSystemManager
2520
2621__version__ = "0.0.8"
2722
3934sys .stdout = open (sys .stdout .fileno (), mode = "w" , encoding = "utf8" , closefd = False )
4035
4136
42- class HTML2Print_HTTPClient (HttpClient ):
43- def get (self , url , params = None , ** kwargs ) -> Response :
44- last_error : Optional [Exception ] = None
45- for attempt in range (1 , 3 ):
46- print ( # noqa: T201
47- f"html2print: sending GET request attempt { attempt } : { url } "
48- )
49- try :
50- return requests .get (url , params , timeout = (5 , 5 ), ** kwargs )
51- except requests .exceptions .ConnectTimeout as connect_timeout_ :
52- last_error = connect_timeout_
53- except requests .exceptions .ReadTimeout as read_timeout_ :
54- last_error = read_timeout_
55- except Exception as exception_ :
56- raise AssertionError (
57- "html2print: unknown exception" , exception_
58- ) from None
37+ class ChromeDriverManager :
38+ def get_chrome_driver (self , path_to_cache_dir : str ):
39+ chrome_version = self .get_chrome_version ()
40+ chrome_major_version = chrome_version .split ("." )[0 ]
41+
5942 print ( # noqa: T201
60- f"html2print: "
61- f"failed to get response for URL: { url } with error: { last_error } "
43+ f"html2print: Installed Chrome version: { chrome_version } "
6244 )
6345
46+ system_map = {
47+ "Windows" : "win32" ,
48+ "Darwin" : "mac-arm64"
49+ if platform .machine () == "arm64"
50+ else "mac-x64" ,
51+ "Linux" : "linux64" ,
52+ }
53+ os_type = system_map [platform .system ()]
54+ is_windows = platform .system () == "Windows"
6455
65- class HTML2Print_CacheManager (DriverCacheManager ):
66- def __init__ (self , file_manager : FileManager , path_to_cache_dir : str ):
67- super ().__init__ (file_manager = file_manager )
68- self .path_to_cache_dir : str = path_to_cache_dir
69-
70- def find_driver (self , driver : Driver ):
71- path_to_cached_chrome_driver_dir = os .path .join (
72- self .path_to_cache_dir , "chromedriver"
73- )
74-
75- os_type = self .get_os_type ()
76- browser_type = driver .get_browser_type ()
77- browser_version = self ._os_system_manager .get_browser_version_from_os (
78- browser_type
56+ print ( # noqa: T201
57+ f"html2print: OS system: { platform .system ()} , OS type: { os_type } ."
7958 )
80- assert browser_version is not None , browser_version
8159
8260 path_to_cached_chrome_driver_dir = os .path .join (
83- path_to_cached_chrome_driver_dir , browser_version , os_type
61+ path_to_cache_dir , chrome_major_version
8462 )
8563 path_to_cached_chrome_driver = os .path .join (
86- path_to_cached_chrome_driver_dir , "chromedriver"
64+ path_to_cached_chrome_driver_dir ,
65+ f"chromedriver-{ os_type } " ,
66+ "chromedriver" ,
8767 )
68+ if is_windows :
69+ path_to_cached_chrome_driver += ".exe"
70+
8871 if os .path .isfile (path_to_cached_chrome_driver ):
8972 print ( # noqa: T201
9073 f"html2print: ChromeDriver exists in the local cache: "
@@ -95,25 +78,103 @@ def find_driver(self, driver: Driver):
9578 f"html2print: ChromeDriver does not exist in the local cache: "
9679 f"{ path_to_cached_chrome_driver } "
9780 )
98- path_to_downloaded_chrome_driver = super ().find_driver (driver )
99- if path_to_downloaded_chrome_driver is None :
100- print ( # noqa: T201
101- f"html2print: could not get a downloaded ChromeDriver: "
102- f"{ path_to_cached_chrome_driver } "
81+
82+ path_to_downloaded_chrome_driver = self ._download_chromedriver (
83+ chrome_major_version ,
84+ os_type ,
85+ path_to_cached_chrome_driver_dir ,
86+ path_to_cached_chrome_driver ,
87+ )
88+ assert os .path .isfile (path_to_downloaded_chrome_driver )
89+ os .chmod (path_to_downloaded_chrome_driver , 0o755 )
90+
91+ return path_to_downloaded_chrome_driver
92+
93+ @staticmethod
94+ def _download_chromedriver (
95+ chrome_major_version ,
96+ os_type : str ,
97+ path_to_driver_cache_dir ,
98+ path_to_cached_chrome_driver ,
99+ ):
100+ url = "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json"
101+ response = ChromeDriverManager .send_http_get_request (url ).json ()
102+
103+ matching_versions = [
104+ item
105+ for item in response ["versions" ]
106+ if item ["version" ].startswith (chrome_major_version )
107+ ]
108+
109+ if not matching_versions :
110+ raise Exception (
111+ f"No compatible ChromeDriver found for Chrome version { chrome_major_version } "
112+ )
113+
114+ latest_version = matching_versions [- 1 ]
115+
116+ driver_url : str
117+ chrome_downloadable_versions = latest_version ["downloads" ][
118+ "chromedriver"
119+ ]
120+ for chrome_downloadable_version_ in chrome_downloadable_versions :
121+ if chrome_downloadable_version_ ["platform" ] == os_type :
122+ driver_url = chrome_downloadable_version_ ["url" ]
123+ break
124+ else :
125+ raise RuntimeError (
126+ f"Could not find a downloadable URL from downloadable versions: { chrome_downloadable_versions } "
103127 )
104- return None
105128
106129 print ( # noqa: T201
107- f"html2print: saving chromedriver to StrictDoc's local cache: "
108- f"{ path_to_downloaded_chrome_driver } -> { path_to_cached_chrome_driver } "
130+ f"html2print: downloading ChromeDriver from: { driver_url } "
109131 )
110- Path (path_to_cached_chrome_driver_dir ).mkdir (
111- parents = True , exist_ok = True
132+ response = ChromeDriverManager .send_http_get_request (driver_url )
133+
134+ Path (path_to_driver_cache_dir ).mkdir (parents = True , exist_ok = True )
135+ zip_path = os .path .join (path_to_driver_cache_dir , "chromedriver.zip" )
136+ print ( # noqa: T201
137+ f"html2print: saving downloaded ChromeDriver to path: { zip_path } "
112138 )
113- copy (path_to_downloaded_chrome_driver , path_to_cached_chrome_driver )
139+ with open (zip_path , "wb" ) as file :
140+ file .write (response .content )
141+
142+ with zipfile .ZipFile (zip_path , "r" ) as zip_ref :
143+ zip_ref .extractall (path_to_driver_cache_dir )
114144
145+ print ( # noqa: T201
146+ f"html2print: ChromeDriver downloaded to: { path_to_cached_chrome_driver } "
147+ )
115148 return path_to_cached_chrome_driver
116149
150+ @staticmethod
151+ def send_http_get_request (url , params = None , ** kwargs ) -> Response :
152+ last_error : Optional [Exception ] = None
153+ for attempt in range (1 , 4 ):
154+ print ( # noqa: T201
155+ f"html2print: sending GET request attempt { attempt } : { url } "
156+ )
157+ try :
158+ return requests .get (url , params , timeout = (5 , 5 ), ** kwargs )
159+ except requests .exceptions .ConnectTimeout as connect_timeout_ :
160+ last_error = connect_timeout_
161+ except requests .exceptions .ReadTimeout as read_timeout_ :
162+ last_error = read_timeout_
163+ except Exception as exception_ :
164+ raise AssertionError (
165+ "html2print: unknown exception" , exception_
166+ ) from None
167+ print ( # noqa: T201
168+ f"html2print: "
169+ f"failed to get response for URL: { url } with error: { last_error } "
170+ )
171+
172+ @staticmethod
173+ def get_chrome_version ():
174+ os_manager = OperationSystemManager (os_type = None )
175+ version = os_manager .get_browser_version_from_os (ChromeType .GOOGLE )
176+ return version
177+
117178
118179def get_inches_from_millimeters (mm : float ) -> float :
119180 return mm / 25.4
@@ -190,23 +251,12 @@ class Done(Exception):
190251 return data
191252
192253
193- def get_chrome_driver (path_to_cache_dir : str ) -> str :
194- cache_manager = HTML2Print_CacheManager (
195- file_manager = FileManager (os_system_manager = OperationSystemManager ()),
196- path_to_cache_dir = path_to_cache_dir ,
197- )
198-
199- http_client = HTML2Print_HTTPClient ()
200- download_manager = WDMDownloadManager (http_client )
201- path_to_chrome = ChromeDriverManager (
202- download_manager = download_manager , cache_manager = cache_manager
203- ).install ()
204- return path_to_chrome
205-
206-
207254def create_webdriver (chromedriver : Optional [str ], path_to_cache_dir : str ):
255+ print ("html2print: creating ChromeDriver service." , flush = True ) # noqa: T201
208256 if chromedriver is None :
209- path_to_chrome = get_chrome_driver (path_to_cache_dir )
257+ path_to_chrome = ChromeDriverManager ().get_chrome_driver (
258+ path_to_cache_dir
259+ )
210260 else :
211261 path_to_chrome = chromedriver
212262 print (f"html2print: ChromeDriver available at path: { path_to_chrome } " ) # noqa: T201
@@ -295,22 +345,20 @@ def main():
295345 path_to_cache_dir : str
296346 if args .command == "get_driver" :
297347 path_to_cache_dir = (
298- args .cache_dir
299- if args .cache_dir is not None
300- else (DEFAULT_CACHE_DIR )
348+ args .cache_dir if args .cache_dir is not None else DEFAULT_CACHE_DIR
301349 )
302350
303- path_to_chrome = get_chrome_driver (path_to_cache_dir )
351+ path_to_chrome = ChromeDriverManager ().get_chrome_driver (
352+ path_to_cache_dir
353+ )
304354 print (f"html2print: ChromeDriver available at path: { path_to_chrome } " ) # noqa: T201
305355 sys .exit (0 )
306356
307357 elif args .command == "print" :
308358 paths : List [str ] = args .paths
309359
310360 path_to_cache_dir = (
311- args .cache_dir
312- if args .cache_dir is not None
313- else (DEFAULT_CACHE_DIR )
361+ args .cache_dir if args .cache_dir is not None else DEFAULT_CACHE_DIR
314362 )
315363 driver = create_webdriver (args .chromedriver , path_to_cache_dir )
316364
0 commit comments