I live in China, behind the infamous Great Firewall of China, and I use VPNs.
A simple observation is that while the VPN is connected, I can access www.google.com. And if the VPN isn’t connected, then I cannot access Google. So I can check if I have an active VPN connection by accessing Google.
My ISP really loves to disconnect my VPN, and so I have routinely check if I have an active VPN connection, and I have already found a way to programmatically do this.
I am connected to the VPN right now, and if I do the following:
import requests google = requests.get('https://www.google.com', timeout=3) print(google.status_code == 200)
Everything is fine.
But if I don’t have an active VPN connection, then all hell breaks loose.
I do this check precisely because my connection will be disconnected, and I need a function to return a
False when it happens, but
requests really loves to throw exceptions, it stops the execution of my script, and the exceptions come one after another:
... ReadTimeoutError: HTTPSConnectionPool(host='www.google.com', port=443): Read timed out. (read timeout=3) During handling of the above exception, another exception occurred: ReadTimeout Traceback (most recent call last) ...
I have imported a bunch of exceptions just so
requests doesn’t panic and stop my script when VPN is disconnected:
import requests from requests.exceptions import ConnectionError, ConnectTimeout, ReadTimeout, Timeout from socket import gaierror from requests.packages.urllib3.exceptions import MaxRetryError, NewConnectionError, ReadTimeoutError def google_accessible(): try: google = requests.get('https://www.google.com', timeout=3) if google.status_code == 200: return True except (ConnectionError, ConnectTimeout, gaierror, MaxRetryError, NewConnectionError, ReadTimeout, ReadTimeoutError, TimeoutError): pass return False
I thought I caught all exceptions previously, but that isn’t the case because I failed to catch the above exceptions (
I know I can use
except Exception to catch them all, but that would catch exceptions that aren’t intended to be caught, and I would rather let those exceptions stop the execution than risking bugs.
How do I use minimal number of exceptions to catch all exceptions that are VERY likely to occur when a request failed?
I think it would be better to use
requests.exception module. The hierarchy is the following:
builtins.OSError(builtins.Exception) RequestException # <- Use this top level exception ChunkedEncodingError ConnectionError ConnectTimeout(ConnectionError, Timeout) ProxyError SSLError ContentDecodingError(RequestException, urllib3.exceptions.HTTPError) HTTPError InvalidHeader(RequestException, builtins.ValueError) InvalidJSONError JSONDecodeError(InvalidJSONError, json.decoder.JSONDecodeError) InvalidSchema(RequestException, builtins.ValueError) InvalidURL(RequestException, builtins.ValueError) InvalidProxyURL MissingSchema(RequestException, builtins.ValueError) RetryError StreamConsumedError(RequestException, builtins.TypeError) Timeout ReadTimeout TooManyRedirects URLRequired UnrewindableBodyError builtins.Warning(builtins.Exception) RequestsWarning FileModeWarning(RequestsWarning, builtins.DeprecationWarning) RequestsDependencyWarning
So you can do:
from requests.exception import RequestException def google_accessible(): try: google = requests.get('https://www.google.com', timeout=3) if google.status_code == 200: return True except RequestException: pass return False