1111import json
1212import os
1313import shutil
14+ import ssl
1415import sys
1516import tempfile
1617import 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+
97122def 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