11from .browser import Browser
2+ from .search_utils import get_command_origin , find_first_defined_env_var
23
34import subprocess
45import platform
56import os
67import shutil
78
9+ ENV_VAR_LOOKUP_TOGGLE = 'HTML2IMAGE_TOGGLE_ENV_VAR_LOOKUP'
810
9- def _find_chrome (user_given_path = None ):
11+ CHROME_EXECUTABLE_ENV_VAR_CANDIDATES = [
12+ 'HTML2IMAGE_CHROME_BIN' ,
13+ 'HTML2IMAGE_CHROME_EXE' ,
14+ 'CHROME_BIN' ,
15+ 'CHROME_EXE' ,
16+ ]
17+
18+
19+ def _find_chrome (user_given_executable = None ):
1020 """ Finds a Chrome executable.
1121
1222 Search Chrome on a given path. If no path given,
1323 try to find Chrome or Chromium-browser on a Windows or Unix system.
1424
25+ Parameters
26+ ----------
27+ - `user_given_executable`: str (optional)
28+ + A filepath leading to a Chrome/ Chromium executable
29+ + Or a filename found in the current working directory
30+ + Or a keyword that executes Chrome/ Chromium, ex:
31+ - 'chromium' on linux systems
32+ - 'chrome' on windows (if typing `start chrome` in a cmd works)
33+
1534 Raises
1635 ------
1736 - `FileNotFoundError`
@@ -23,15 +42,58 @@ def _find_chrome(user_given_path=None):
2342 + Path of the chrome executable on the current machine.
2443 """
2544
26- # TODO when other browsers will be available:
27- # Ensure that the given executable is a chrome one.
45+ # try to find a chrome bin/exe in ENV
46+ path_from_env = find_first_defined_env_var (
47+ env_var_list = CHROME_EXECUTABLE_ENV_VAR_CANDIDATES ,
48+ toggle = ENV_VAR_LOOKUP_TOGGLE
49+ )
2850
29- if user_given_path is not None :
30- if os .path .isfile (user_given_path ):
31- return user_given_path
51+ if path_from_env :
52+ print (
53+ f'Found a potential chrome executable in the { path_from_env } '
54+ f'environment variable:\n { path_from_env } \n '
55+ )
56+ return path_from_env
57+
58+ # if an executable is given, try to use it
59+ if user_given_executable is not None :
60+
61+ # On Windows, we cannot "safely" validate that user_given_executable
62+ # seems to be a chrome executable, as we cannot run it with
63+ # the --version flag.
64+ # https://bugs.chromium.org/p/chromium/issues/detail?id=158372
65+ #
66+ # We thus do the "bare minimum" and check if user_given_executable
67+ # is a file, a filepath, or corresponds to a keyword that can be used
68+ # with the start command, like so: `start user_given_executable`
69+ if platform .system () == 'Windows' :
70+ command_origin = get_command_origin (user_given_executable )
71+ if command_origin :
72+ return command_origin
73+
74+ # cannot validate user_given_executable
75+ raise FileNotFoundError ()
76+
77+ # On a non-Windows OS, we can validate in a basic way that
78+ # user_given_executable leads to a Chrome / Chromium executable,
79+ # or is a command, using the --version flag
3280 else :
33- raise FileNotFoundError ('Could not find chrome in the given path.' )
81+ try :
82+ if 'chrom' in subprocess .check_output (
83+ [user_given_executable , '--version' ]
84+ ).decode ('utf-8' ).lower ():
85+ return user_given_executable
86+ except Exception :
87+ pass
88+
89+ # We got a user_given_executable but couldn't validate it
90+ raise FileNotFoundError (
91+ 'Failed to find a seemingly valid chrome executable '
92+ 'in the given path.'
93+ )
3494
95+ # Executable not in ENV or given by the user, try to find it
96+ # Search for executable on a Windows OS
3597 if platform .system () == 'Windows' :
3698 prefixes = [
3799 os .getenv ('PROGRAMFILES(X86)' ),
@@ -46,45 +108,54 @@ def _find_chrome(user_given_path=None):
46108 if os .path .isfile (path_candidate ):
47109 return path_candidate
48110
111+ # Search for executable on a Linux OS
49112 elif platform .system () == "Linux" :
50113
51- # search google-chrome
52- version_result = subprocess .check_output (
53- ["google-chrome" , "--version" ]
54- )
55-
56- if 'Google Chrome' in str (version_result ):
57- return "google-chrome"
114+ chrome_commands = [
115+ 'chromium' ,
116+ 'chromium-browser' ,
117+ 'chrome' ,
118+ 'google-chrome'
119+ ]
58120
59- # else search chromium-browser
121+ for chrome_command in chrome_commands :
122+ if shutil .which (chrome_command ):
123+ # check the --version for "chrom" ?
124+ return chrome_command
60125
61126 # snap seems to be a special case?
62127 # see https://stackoverflow.com/q/63375327/12182226
63- version_result = subprocess .check_output (
64- ["chromium-browser" , "--version" ]
65- )
66- if 'snap' in str (version_result ):
67- chrome_snap = (
68- '/snap/chromium/current/usr/lib/chromium-browser/chrome'
69- )
70- if os .path .isfile (chrome_snap ):
71- return chrome_snap
72- else :
73- which_result = shutil .which ('chromium-browser' )
74- if which_result is not None and os .path .isfile (which_result ):
75- return which_result
76128
129+ try :
130+ version_result = subprocess .check_output (
131+ ["chromium-browser" , "--version" ]
132+ )
133+ if 'snap' in str (version_result ):
134+ chrome_snap = (
135+ '/snap/chromium/current/usr/lib/chromium-browser/chrome'
136+ )
137+ if os .path .isfile (chrome_snap ):
138+ return chrome_snap
139+ except Exception :
140+ pass
141+
142+ # Search for executable on MacOS
77143 elif platform .system () == "Darwin" :
78144 # MacOS system
79145 chrome_app = (
80146 '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
81147 )
82- version_result = subprocess .check_output (
83- [chrome_app , "--version" ]
84- )
85- if "Google Chrome" in str (version_result ):
86- return chrome_app
87148
149+ try :
150+ version_result = subprocess .check_output (
151+ [chrome_app , "--version" ]
152+ )
153+ if "Google Chrome" in str (version_result ):
154+ return chrome_app
155+ except Exception :
156+ pass
157+
158+ # Couldn't find an executable (or OS not in Windows, Linux or Mac)
88159 raise FileNotFoundError (
89160 'Could not find a Chrome executable on this '
90161 'machine, please specify it yourself.'
@@ -97,7 +168,7 @@ class ChromeHeadless(Browser):
97168
98169 Parameters
99170 ----------
100- - `executable_path ` : str, optional
171+ - `executable ` : str, optional
101172 + Path to a chrome executable.
102173
103174 - `flags` : list of str
@@ -109,8 +180,8 @@ class ChromeHeadless(Browser):
109180 + Whether or not to print the command used to take a screenshot.
110181 """
111182
112- def __init__ (self , executable_path = None , flags = None , print_command = False ):
113- self .executable_path = executable_path
183+ def __init__ (self , executable = None , flags = None , print_command = False ):
184+ self .executable = executable
114185 if not flags :
115186 self .flags = [
116187 '--default-background-color=0' ,
@@ -122,12 +193,12 @@ def __init__(self, executable_path=None, flags=None, print_command=False):
122193 self .print_command = print_command
123194
124195 @property
125- def executable_path (self ):
126- return self ._executable_path
196+ def executable (self ):
197+ return self ._executable
127198
128- @executable_path .setter
129- def executable_path (self , value ):
130- self ._executable_path = _find_chrome (value )
199+ @executable .setter
200+ def executable (self , value ):
201+ self ._executable = _find_chrome (value )
131202
132203 def screenshot (
133204 self ,
@@ -171,7 +242,7 @@ def screenshot(
171242 # command used to launch chrome in
172243 # headless mode and take a screenshot
173244 command = [
174- f'{ self .executable_path } ' ,
245+ f'{ self .executable } ' ,
175246 '--headless' ,
176247 f'--screenshot={ os .path .join (output_path , output_file )} ' ,
177248 f'--window-size={ size [0 ]} ,{ size [1 ]} ' ,
0 commit comments