From d5082fab739cdb687ebf9cf142113086defd5f51 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Thu, 27 Aug 2020 20:49:26 -0500 Subject: [PATCH] Fix both broken weather modules (#789) * Fix both broken weather modules The Weather.com changed its API again, this fixes that as well as fixing a bug that caused the weather conditions to always show as "None". For Weather Underground, the module had been broken for some time due to the discontinuation of their API. The module has been rewritten to use the same API calls that the website itself uses. * Fix double-click browser launch in wunderground module * Add example of longer location_code for weather.com --- i3pystatus/weather/__init__.py | 74 +++---- i3pystatus/weather/weathercom.py | 22 +- i3pystatus/weather/wunderground.py | 323 ++++++++++++----------------- 3 files changed, 174 insertions(+), 245 deletions(-) diff --git a/i3pystatus/weather/__init__.py b/i3pystatus/weather/__init__.py index 6c244282..405e4c5b 100644 --- a/i3pystatus/weather/__init__.py +++ b/i3pystatus/weather/__init__.py @@ -2,7 +2,7 @@ import re import threading import time -from urllib.request import urlopen +from urllib.request import Request, urlopen from i3pystatus import SettingsBase, IntervalModule, formatp from i3pystatus.core.util import user_open, internet, require @@ -12,32 +12,37 @@ class WeatherBackend(SettingsBase): settings = () @require(internet) - def api_request(self, url): + def http_request(self, url, headers=None): + req = Request(url, headers=headers or {}) + with urlopen(req) as content: + try: + content_type = dict(content.getheaders())['Content-Type'] + charset = re.search(r'charset=(.*)', content_type).group(1) + except AttributeError: + charset = 'utf-8' + return content.read().decode(charset) + + @require(internet) + def api_request(self, url, headers=None): self.logger.debug('Making API request to %s', url) try: - with urlopen(url) as content: - try: - content_type = dict(content.getheaders())['Content-Type'] - charset = re.search(r'charset=(.*)', content_type).group(1) - except AttributeError: - charset = 'utf-8' - response_json = content.read().decode(charset).strip() - if not response_json: - self.logger.debug('JSON response from %s was blank', url) - return {} - try: - response = json.loads(response_json) - except json.decoder.JSONDecodeError as exc: - self.logger.error('Error loading JSON: %s', exc) - self.logger.debug('JSON text that failed to load: %s', - response_json) - return {} - self.logger.log(5, 'API response: %s', response) - error = self.check_response(response) - if error: - self.logger.error('Error in JSON response: %s', error) - return {} - return response + response_json = self.http_request(url, headers=headers).strip() + if not response_json: + self.logger.debug('JSON response from %s was blank', url) + return {} + try: + response = json.loads(response_json) + except json.decoder.JSONDecodeError as exc: + self.logger.error('Error loading JSON: %s', exc) + self.logger.debug('JSON text that failed to load: %s', + response_json) + return {} + self.logger.log(5, 'API response: %s', response) + error = self.check_response(response) + if error: + self.logger.error('Error in JSON response: %s', error) + return {} + return response except Exception as exc: self.logger.error( 'Failed to make API request to %s. Exception follows:', url, @@ -45,8 +50,8 @@ def api_request(self, url): ) return {} - def check_response(response): - raise NotImplementedError + def check_response(self, response): + return False class Weather(IntervalModule): @@ -99,21 +104,6 @@ class Weather(IntervalModule): syntax to conditionally show the value of the **update_error** config value when the backend encounters an error during an update. - The extended string format syntax also comes in handy for the - :py:mod:`weathercom <.weather.weathercom>` backend, which at a certain - point in the afternoon will have a blank ``{high_temp}`` value. Using the - following snippet in your format string will only display the high - temperature information if it is not blank: - - :: - - {current_temp}{temp_unit}[ Hi: {high_temp}] Lo: {low_temp}[ {update_error}] - - Brackets are evaluated from the outside-in, so the fact that the only - formatter in the outer block (``{high_temp}``) is empty would keep the - inner block from being evaluated at all, and entire block would not be - displayed. - See the following links for usage examples for the available weather backends: diff --git a/i3pystatus/weather/weathercom.py b/i3pystatus/weather/weathercom.py index 1e33abfb..566983e3 100644 --- a/i3pystatus/weather/weathercom.py +++ b/i3pystatus/weather/weathercom.py @@ -7,8 +7,6 @@ from i3pystatus.core.util import internet, require from i3pystatus.weather import WeatherBackend -USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0' - class WeathercomHTMLParser(HTMLParser): ''' @@ -16,6 +14,7 @@ class WeathercomHTMLParser(HTMLParser): through some other source at runtime and added as