From d7917d402c12c7fe9c951da886147eae28ea2cb0 Mon Sep 17 00:00:00 2001 From: stephan192 Date: Sat, 25 Apr 2020 21:36:00 +0200 Subject: [PATCH] ADD: Input validation ADD: Flake8 and pydocstyle conformity --- CHANGELOG.md | 5 +++ dwdwfsapi/__init__.py | 6 ++-- dwdwfsapi/core.py | 12 +++---- dwdwfsapi/weatherwarnings.py | 64 ++++++++++++++--------------------- setup.py | 12 ++++--- tests/test_weatherwarnings.py | 37 ++++++++++---------- 6 files changed, 65 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fc7e10..cee14bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.0.2 (2020-04-25) +### Added +- Input validation +- Flake8 and pydocstyle conformity + ## 1.0.1 (2020-04-20) ### Fixed - DwdWeatherWarningsAPI: Exception handling when input data is None diff --git a/dwdwfsapi/__init__.py b/dwdwfsapi/__init__.py index ed91cc2..0307d34 100644 --- a/dwdwfsapi/__init__.py +++ b/dwdwfsapi/__init__.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -""" -Python client to retrieve data provided by DWD via their geoserver WFS API -""" +"""Python client to retrieve data provided by DWD via their WFS API.""" -from .weatherwarnings import DwdWeatherWarningsAPI +from .weatherwarnings import DwdWeatherWarningsAPI # noqa: F401 diff --git a/dwdwfsapi/core.py b/dwdwfsapi/core.py index 088fdea..be74f14 100644 --- a/dwdwfsapi/core.py +++ b/dwdwfsapi/core.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- """ -Collection of the core functions needed to communicate with the geoserver -operated by the Deutscher Wetterdienst (DWD) +Collection of the core functions needed to communicate with the geoserver. + +The geoserver is operated by the Deutscher Wetterdienst (DWD). https://maps.dwd.de + """ import urllib.parse @@ -16,9 +18,7 @@ def query_dwd(**kwargs): - """ - Retrive data from DWD server. - """ + """Retrive data from DWD server.""" # Make all keys lowercase and escape all values kwargs = {k.lower(): urllib.parse.quote(v) for k, v in kwargs.items()} @@ -50,5 +50,5 @@ def query_dwd(**kwargs): if resp.status_code != 200: return None return resp.json() - except: # pylint: disable=bare-except + except: # pylint: disable=bare-except # noqa: E722 return None diff --git a/dwdwfsapi/weatherwarnings.py b/dwdwfsapi/weatherwarnings.py index 4fc67cb..7002939 100644 --- a/dwdwfsapi/weatherwarnings.py +++ b/dwdwfsapi/weatherwarnings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- -""" -Python client to retrieve weather warnings from Deutscher Wetterdienst (DWD) -""" +"""Python client to retrieve weather warnings from DWD.""" # pylint: disable=c-extension-no-member import datetime @@ -11,10 +9,7 @@ def convert_warning_data(data_in): - """ - Convert the data received from DWD - """ - + """Convert the data received from DWD.""" # pylint: disable=too-many-branches # pylint: disable=too-many-statements @@ -46,19 +41,19 @@ def convert_warning_data(data_in): if "onset" in data_in: try: data_out["start_time"] = ciso8601.parse_datetime(data_in["onset"]) - except: # pylint: disable=bare-except + except: # pylint: disable=bare-except # noqa: E722 data_out["start_time"] = None if "expires" in data_in: try: data_out["end_time"] = ciso8601.parse_datetime(data_in["expires"]) - except: # pylint: disable=bare-except + except: # pylint: disable=bare-except # noqa: E722 data_out["end_time"] = None if "event" in data_in: data_out["event"] = data_in["event"] if "ec_ii" in data_in: try: data_out["event_code"] = int(data_in["ec_ii"]) - except: # pylint: disable=bare-except + except: # pylint: disable=bare-except # noqa: E722 data_out["event_code"] = 0 if "headline" in data_in: data_out["headline"] = data_in["headline"] @@ -72,10 +67,11 @@ def convert_warning_data(data_in): data_out["level"] = weather_severity_mapping[ data_in["severity"].lower() ] - except: # pylint: disable=bare-except + except: # pylint: disable=bare-except # noqa: E722 data_out["level"] = 0 if "parametername" in data_in and "parametervalue" in data_in: - # Depending on the query the keys and values are either seperated by , or ; + # Depending on the query the keys and values are either seperated + # by , or ; try: if "," in data_in["parametername"]: keys = data_in["parametername"].split(",") @@ -84,14 +80,14 @@ def convert_warning_data(data_in): keys = data_in["parametername"].split(";") values = data_in["parametervalue"].split(";") data_out["parameters"] = dict(zip(keys, values)) - except: # pylint: disable=bare-except + except: # pylint: disable=bare-except # noqa: E722 data_out["parameters"] = None if "ec_area_color" in data_in: try: colors = data_in["ec_area_color"].split(" ") data_out["color"] = f"#{int(colors[0]):02x}{int(colors[1]):02x}" data_out["color"] += f"{int(colors[2]):02x}" - except: # pylint: disable=bare-except + except: # pylint: disable=bare-except # noqa: E722 data_out["color"] = "#000000" return data_out @@ -99,7 +95,7 @@ def convert_warning_data(data_in): class DwdWeatherWarningsAPI: """ - Class for retrieving weather warnings from DWD + Class for retrieving weather warnings from DWD. Attributes: ----------- @@ -148,7 +144,7 @@ class DwdWeatherWarningsAPI: def __init__(self, identifier): """ - Init DWD weather warnings + Init DWD weather warnings. Parameters ---------- @@ -170,20 +166,19 @@ def __init__(self, identifier): self.expected_warning_level = None self.expected_warnings = None - self.__generate_query(identifier) + # Identifier must be either integer or string + if not isinstance(identifier, (int, str)): + return + self.__generate_query(identifier) self.update() def __bool__(self): - """ - Returns the data_valid attribute - """ + """Return the data_valid attribute.""" return self.data_valid def __len__(self): - """ - Returns the sum of current and expected warnings - """ + """Return the sum of current and expected warnings.""" if self.data_valid: length = len(self.current_warnings) + len(self.expected_warnings) else: @@ -191,9 +186,7 @@ def __len__(self): return length def __str__(self): - """ - Returns a short overview about the actual status - """ + """Return a short overview about the actual status.""" if self.data_valid: retval = f"{len(self.current_warnings)} current and" retval += f" {len(self.expected_warnings)} expected warnings" @@ -203,9 +196,7 @@ def __str__(self): return retval def update(self): - """ - Update data by querying DWD server and parsing result - """ + """Update data by querying DWD server and parsing result.""" if self.__query is None: return @@ -222,9 +213,7 @@ def update(self): self.expected_warnings = None def __generate_query(self, identifier): - """ - Determine the warning region to which the identifier belongs - """ + """Determine the warning region to which the identifier belongs.""" weather_warnings_query_mapping = { "dwd:Warngebiete_Gemeinden": "dwd:Warnungen_Gemeinden", "dwd:Warngebiete_Kreise": "dwd:Warnungen_Landkreise", @@ -251,7 +240,8 @@ def __generate_query(self, identifier): self.warncell_name = result["features"][0]["properties"][ "NAME" ] - # More than one match found. Can only happen if search is done by name. + # More than one match found. Can only happen if search is + # done by name. if result["numberReturned"] > 1: self.warncell_name += " (not unique used ID)!" @@ -270,9 +260,7 @@ def __generate_query(self, identifier): break def __parse_result(self, json_obj): - """ - Parse the retrieved data - """ + """Parse the retrieved data.""" try: current_maxlevel = 0 expected_maxlevel = 0 @@ -284,7 +272,7 @@ def __parse_result(self, json_obj): self.last_update = ciso8601.parse_datetime( json_obj["timeStamp"] ) - except: # pylint: disable=bare-except + except: # pylint: disable=bare-except # noqa: E722 self.last_update = datetime.datetime.now( datetime.timezone.utc ) @@ -325,7 +313,7 @@ def __parse_result(self, json_obj): self.expected_warnings = expected_warnings self.data_valid = True - except: # pylint: disable=bare-except + except: # pylint: disable=bare-except # noqa: E722 self.data_valid = False self.last_update = None self.current_warning_level = None diff --git a/setup.py b/setup.py index 9c6d9a0..bc5f197 100644 --- a/setup.py +++ b/setup.py @@ -1,20 +1,21 @@ # -*- coding: utf-8 -*- -""" -Setup for dwdwfsapi package -""" +"""Setup for dwdwfsapi package.""" from setuptools import setup, find_packages # Package meta-data NAME = "dwdwfsapi" -DESCRIPTION = "Python client to retrieve data provided by DWD via their geoserver WFS API" +DESCRIPTION = ( + "Python client to retrieve data provided by DWD via their geoserver " + "WFS API" +) KEYWORDS = "dwd ows wfs deutscher wetterdienst" URL = "https://github.com/stephan192/dwdwfsapi" EMAIL = "stephan192@outlook.com" AUTHOR = "stephan192" REQUIRES_PYTHON = ">=3.6" -VERSION = "1.0.1" +VERSION = "1.0.2" # Define required packages REQUIRES = ["requests>=2.23.0,<3", "ciso8601>=2.1.3,<3", "urllib3>=1.25.8,<2"] @@ -36,6 +37,7 @@ packages=find_packages(), install_requires=REQUIRES, keywords=KEYWORDS, + license="MIT", classifiers=[ "Development Status :: 5 - Production/Stable", "Programming Language :: Python :: 3", diff --git a/tests/test_weatherwarnings.py b/tests/test_weatherwarnings.py index 191bb7b..e21b54d 100644 --- a/tests/test_weatherwarnings.py +++ b/tests/test_weatherwarnings.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -""" -Tests for dwdwfsapi weatherwarnings module -""" +"""Tests for dwdwfsapi weatherwarnings module.""" import datetime from dwdwfsapi import DwdWeatherWarningsAPI @@ -23,9 +21,7 @@ def test_city(): - """ - Test a city warncell - """ + """Test a city warncell.""" dwd = DwdWeatherWarningsAPI(WARNCELL_ID_CITY) assert dwd.data_valid assert dwd.warncell_id == WARNCELL_ID_CITY @@ -42,9 +38,7 @@ def test_city(): def test_county(): - """ - Test a county warncell - """ + """Test a county warncell.""" dwd = DwdWeatherWarningsAPI(WARNCELL_NAME_COUNTY) assert dwd.data_valid assert dwd.warncell_id == WARNCELL_ID_COUNTY @@ -61,9 +55,7 @@ def test_county(): def test_lake(): - """ - Test a lake warncell - """ + """Test a lake warncell.""" dwd = DwdWeatherWarningsAPI(WARNCELL_ID_LAKE) assert dwd.data_valid assert dwd.warncell_id == WARNCELL_ID_LAKE @@ -80,9 +72,7 @@ def test_lake(): def test_coast(): - """ - Test a coast warncell - """ + """Test a coast warncell.""" dwd = DwdWeatherWarningsAPI(WARNCELL_NAME_COAST) assert dwd.data_valid assert dwd.warncell_id == WARNCELL_ID_COAST @@ -99,9 +89,7 @@ def test_coast(): def test_sea(): - """ - Test a sea warncell - """ + """Test a sea warncell.""" dwd = DwdWeatherWarningsAPI(WARNCELL_ID_SEA) assert dwd.data_valid assert dwd.warncell_id == WARNCELL_ID_SEA @@ -115,3 +103,16 @@ def test_sea(): assert MIN_WARNING_LEVEL <= dwd.expected_warning_level <= MAX_WARNING_LEVEL assert isinstance(dwd.current_warnings, list) assert isinstance(dwd.expected_warnings, list) + + +def test_wrong_input(): + """Test an invalid input.""" + dwd = DwdWeatherWarningsAPI(None) + assert not dwd.data_valid + assert dwd.warncell_id is None + assert dwd.warncell_name is None + assert dwd.last_update is None + assert dwd.current_warning_level is None + assert dwd.expected_warning_level is None + assert dwd.current_warnings is None + assert dwd.expected_warnings is None