Skip to content

Commit 0b3697f

Browse files
committed
html2print: minor cleanups on windows
1 parent 84481a9 commit 0b3697f

5 files changed

Lines changed: 53 additions & 18 deletions

File tree

.gitattributes

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Mark the file as binary, so that Git won't not convert the line endings.
2+
# Otherwise, the file becomes unreadable for bash inside Docker (on Windows platforms).
3+
entrypoint.sh -text
4+

.github/workflows/ci-windows.yml

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,7 @@ jobs:
2828
shell: powershell
2929

3030
- name: Check Chrome Version
31-
run: '& "C:\Program Files\Google\Chrome\Application\chrome.exe" --version'
32-
shell: powershell
33-
34-
- name: Add Chrome to PATH
35-
run: |
36-
$chromePath = "C:\Program Files\Google\Chrome\Application"
37-
echo "Adding $chromePath to PATH"
38-
echo "$chromePath" | Out-File -Append -Encoding utf8 $env:GITHUB_PATH
39-
shell: powershell
40-
41-
- name: Verify Chrome Installation
42-
run: chrome --version
31+
run: '(Get-Item "C:\Program Files\Google\Chrome\Application\chrome.exe").VersionInfo'
4332
shell: powershell
4433

4534
- name: Upgrade pip

html2print/html2print.py

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,14 @@
3939

4040
class ChromeDriverManager:
4141
def get_chrome_driver(self, path_to_cache_dir: str) -> str:
42-
chrome_version: Optional[str] = self.get_chrome_version()
42+
chrome_version: Optional[str] = None
43+
44+
if platform.system() == "Windows":
45+
import browsers
46+
47+
chrome_version = browsers.get("chrome")["version"]
48+
else:
49+
chrome_version = self.get_chrome_version()
4350

4451
# If Web Driver Manager cannot detect Chrome, it returns None.
4552
if chrome_version is None:
@@ -109,7 +116,18 @@ def _download_chromedriver(
109116
path_to_cached_chrome_driver: str,
110117
) -> str:
111118
url = "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json"
112-
response = ChromeDriverManager.send_http_get_request(url).json()
119+
response = ChromeDriverManager.send_http_get_request(url)
120+
if response is None:
121+
raise RuntimeError(
122+
"Could not download known-good-versions-with-downloads.json"
123+
)
124+
125+
response = response.json()
126+
if response is None:
127+
raise RuntimeError(
128+
"Could not parse known-good-versions-with-downloads.json"
129+
)
130+
assert isinstance(response, dict)
113131

114132
matching_versions = [
115133
item
@@ -118,7 +136,7 @@ def _download_chromedriver(
118136
]
119137

120138
if not matching_versions:
121-
raise Exception(
139+
raise RuntimeError(
122140
f"No compatible ChromeDriver found for Chrome version {chrome_major_version}"
123141
)
124142

@@ -142,6 +160,11 @@ def _download_chromedriver(
142160
)
143161
response = ChromeDriverManager.send_http_get_request(driver_url)
144162

163+
if response is None:
164+
raise RuntimeError(
165+
f"Could not download ChromeDriver from {driver_url}"
166+
)
167+
145168
Path(path_to_driver_cache_dir).mkdir(parents=True, exist_ok=True)
146169
zip_path = os.path.join(path_to_driver_cache_dir, "chromedriver.zip")
147170
print( # noqa: T201
@@ -179,6 +202,9 @@ def send_http_get_request(url: str) -> Response:
179202
f"html2print: "
180203
f"failed to get response for URL: {url} with error: {last_error}"
181204
)
205+
raise RuntimeError(
206+
f"GET request failed after 3 attempts: {url}"
207+
) from last_error
182208

183209
@staticmethod
184210
def get_chrome_version() -> Optional[str]:
@@ -328,14 +354,24 @@ def create_webdriver(
328354
service = Service(path_to_chrome)
329355

330356
webdriver_options = Options()
357+
# Workaround for Windows: chrome is not typically found in the PATH, so need to supply the exact binary location manually
358+
if platform.system() == "Windows":
359+
import browsers
360+
361+
chrome_binary_location = browsers.get("chrome")["path"]
362+
webdriver_options.binary_location = chrome_binary_location
331363
webdriver_options.add_argument("start-maximized")
332364
webdriver_options.add_argument("disable-infobars")
333365
# Doesn't seem to be needed.
334366
# webdriver_options.add_argument('--disable-gpu') # noqa: ERA001
335367
webdriver_options.add_argument("--disable-extensions")
336-
webdriver_options.add_argument("--headless=chrome")
337-
# FIXME: This is not nice but otherwise it does not work in Ubuntu 24-based Docker image.
338-
# https://github.com/SeleniumHQ/selenium/issues/15327#issuecomment-2689287561
368+
# Use --headless=new, as it seems to be more stable on Windows (available since Chrome 109).
369+
# see https://www.selenium.dev/blog/2023/headless-is-going-away/
370+
webdriver_options.add_argument("--headless=new")
371+
# Docker disables some syscalls that are required for Chrome's sandbox to work.
372+
# https://stackoverflow.com/questions/68855734/how-to-setup-chrome-sandbox-on-docker-container.
373+
# See also https://github.com/SeleniumHQ/selenium/issues/15327#issuecomment-2689287561.
374+
# We prefer isolation of the container over isolation of tabs within Chrome, and thus disable the sandbox.
339375
webdriver_options.add_argument("--no-sandbox")
340376

341377
# The Chrome option --disable-dev-shm-usage disables the use of /dev/shm

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ dependencies = [
5454

5555
# requests is used by HTML2PDF_HTTPClient.
5656
"requests",
57+
58+
# To detect the browers path (needed on Windows)
59+
'pybrowsers; platform_system == "Windows"',
5760
]
5861

5962
[project.optional-dependencies]

tests/integration/lit.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,6 @@ config.suffixes = ['.itest', '.c']
2323
config.is_windows = lit_config.isWindows
2424
if not lit_config.isWindows:
2525
config.available_features.add('PLATFORM_IS_NOT_WINDOWS')
26+
27+
# In Linux CI, $HOME is required for Chrome as it needs to access things in ~/.local
28+
config.environment['HOME'] = os.environ.get('HOME', '/tmp')

0 commit comments

Comments
 (0)