Skip to content

Commit e949c41

Browse files
authored
chore(get-modflow): workaround broken local CA trust store (#2806)
Motivated by CI errors caused by unreliably provisioned GitHub Actions runners
1 parent 78ab37d commit e949c41

1 file changed

Lines changed: 27 additions & 2 deletions

File tree

flopy/utils/get_modflow.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import json
1212
import os
1313
import shutil
14+
import ssl
1415
import sys
1516
import tempfile
1617
import urllib
@@ -94,6 +95,30 @@ def get_request(url, params={}):
9495
return urllib.request.Request(url, headers=headers)
9596

9697

98+
def urlopen(request, timeout=10, quiet=False):
99+
"""Open a URL, working around a broken/incomplete local CA trust store.
100+
101+
Some Python installs (e.g. a freshly provisioned Homebrew Python) resolve
102+
``ssl.create_default_context()`` to a CA bundle that isn't populated,
103+
causing HTTPS requests to fail verification even though the certificate
104+
is fine. If that happens and certifi is already installed, retry once
105+
using certifi's CA bundle.
106+
"""
107+
try:
108+
return urllib.request.urlopen(request, timeout=timeout)
109+
except urllib.error.URLError as err:
110+
if not isinstance(err.reason, ssl.SSLCertVerificationError):
111+
raise
112+
try:
113+
import certifi
114+
except ImportError:
115+
raise
116+
if not quiet:
117+
print("certificate verification failed, retrying with certifi")
118+
context = ssl.create_default_context(cafile=certifi.where())
119+
return urllib.request.urlopen(request, timeout=timeout, context=context)
120+
121+
97122
def get_releases(owner=None, repo=None, quiet=False, per_page=None) -> List[str]:
98123
"""Get list of available releases."""
99124
owner = default_owner if owner is None else owner
@@ -111,7 +136,7 @@ def get_releases(owner=None, repo=None, quiet=False, per_page=None) -> List[str]
111136
while True:
112137
num_tries += 1
113138
try:
114-
with urllib.request.urlopen(request, timeout=10) as resp:
139+
with urlopen(request, timeout=10, quiet=quiet) as resp:
115140
result = resp.read()
116141
break
117142
except urllib.error.HTTPError as err:
@@ -153,7 +178,7 @@ def get_release(owner=None, repo=None, tag="latest", quiet=False) -> dict:
153178
while True:
154179
num_tries += 1
155180
try:
156-
with urllib.request.urlopen(request, timeout=10) as resp:
181+
with urlopen(request, timeout=10, quiet=quiet) as resp:
157182
result = resp.read()
158183
remaining = resp.headers.get("x-ratelimit-remaining", None)
159184
if remaining and int(remaining) <= 10:

0 commit comments

Comments
 (0)