diff --git a/hadcrut5_bars.py b/hadcrut5_bars.py index 065d3fb..88f45ed 100755 --- a/hadcrut5_bars.py +++ b/hadcrut5_bars.py @@ -6,13 +6,15 @@ Display a bar plot of the HadCRUT5 Global temperature dataset. """ +import argparse + import matplotlib.pyplot as plt import matplotlib.colors from hadcrut5lib import argparser, HadCRUT5 -def parse_args(): +def parse_args() -> argparse.Namespace: """This function parses and return arguments passed in""" descr = "Parse and plot the HadCRUT5 temperature datasets" examples = [ @@ -49,7 +51,8 @@ def parse_args(): return parser.parse_args() -def plotbar(period, outfile, verbose): +# mypy: disable-error-code="misc, attr-defined" +def plotbar(period: str, outfile: str, verbose: bool): """ Create a bar plot for the specified period and diplay it or save it to file if outfile is set @@ -81,7 +84,8 @@ def major_formatter(x, pos): plt.style.use("dark_background") _, ax = plt.subplots() - cmap = plt.cm.jet # or plt.cm.bwr # pylint: disable=no-member + # pylint: disable=no-member + cmap = plt.cm.jet # or plt.cm.bwr norm = matplotlib.colors.Normalize(vmin=-1, vmax=max(mean)) colors = cmap(norm(mean)) diff --git a/hadcrut5_plot.py b/hadcrut5_plot.py index 324ad41..d52d5ab 100755 --- a/hadcrut5_plot.py +++ b/hadcrut5_plot.py @@ -6,7 +6,9 @@ Display a plot of the HadCRUT5 temperature dataset. """ +import argparse from math import trunc +from typing import List import matplotlib as mpl import matplotlib.pyplot as plt @@ -15,7 +17,7 @@ from hadcrut5lib import argparser, HadCRUT5 -def parse_args(): +def parse_args() -> argparse.Namespace: """This function parses and return arguments passed in""" descr = "Parse and plot the HadCRUT5 temperature datasets" examples = [ @@ -99,17 +101,17 @@ def parse_args(): return parser.parse_args() -def dataset_current_anomaly(temperatures): +def dataset_current_anomaly(temperatures: List[float]) -> float: """Return the current anomaly""" return temperatures[-1] -def dataset_max_anomaly(temperatures): +def dataset_max_anomaly(temperatures: List[float]) -> float: """Return the maximum anomaly with respect to 'temperatures'""" return np.max(temperatures) -def dataset_smoother(years, temperatures, chunksize): +def dataset_smoother(years: List[int | float], temperatures: List[float], chunksize: int): """Make the lines smoother by using {chunksize}-year means""" data_range = range((len(years) + chunksize - 1) // chunksize) subset_years = [years[i * chunksize] for i in data_range] @@ -120,7 +122,7 @@ def dataset_smoother(years, temperatures, chunksize): return subset_years, subset_temperatures -def plotline(hc5, chunksize, annotate, outfile): +def plotline(hc5: HadCRUT5, chunksize: int, annotate: int, outfile: str): """ Create a plot for the specified period and arguments and diplay it or save it to file if outfile is set diff --git a/hadcrut5_stripe.py b/hadcrut5_stripe.py index e0220d7..9b9790a 100755 --- a/hadcrut5_stripe.py +++ b/hadcrut5_stripe.py @@ -6,6 +6,9 @@ Display a stripe image of the HadCRUT5 Global temperature dataset. """ +import argparse +from typing import List + import matplotlib.pyplot as plt from matplotlib.collections import PatchCollection from matplotlib.colors import ListedColormap @@ -14,7 +17,7 @@ from hadcrut5lib import argparser, HadCRUT5 -def parse_args(): +def parse_args() -> argparse.Namespace: """This function parses and return arguments passed in""" descr = "Parse and plot a stripe image of the HadCRUT5 temperature datasets" examples = [ @@ -59,7 +62,7 @@ def parse_args(): return parser.parse_args() -def plotstripe(region, outfile, labels, verbose): +def plotstripe(region: str, outfile: str, labels: bool, verbose: bool): """ Create a stripe plot for the specified period and diplay it or save it to file if outfile is set @@ -74,7 +77,7 @@ def plotstripe(region, outfile, labels, verbose): years = hc5.dataset_years() yfirst, ylast = years[0], years[-1] - yrange = ylast - yfirst + yrange = int(ylast - yfirst) regions_switch = { "global": hc5.GLOBAL_REGION, @@ -134,7 +137,7 @@ def plotstripe(region, outfile, labels, verbose): ) ticks = [0, 0.2, 0.4, 0.6, 0.8, 1] - xlabels = [round(yfirst + x * yrange) for x in ticks] + xlabels: List[str] = [str(round(yfirst + x * yrange)) for x in ticks] plt.xticks(ticks, xlabels, fontweight="bold", fontsize=12) else: ax.get_xaxis().set_visible(False) diff --git a/hadcrut5lib.py b/hadcrut5lib.py index 8abb981..746c522 100755 --- a/hadcrut5lib.py +++ b/hadcrut5lib.py @@ -11,6 +11,7 @@ import json import logging import sys +from typing import Dict, List, Mapping, Tuple import numpy as np import requests @@ -28,7 +29,7 @@ __status__ = "stable" -def copyleft(descr): +def copyleft(descr: str) -> str: """Print the Copyright message and License""" return ( f"{descr} v{__version__} ({__status__})\n" @@ -36,7 +37,7 @@ def copyleft(descr): ) -def argparser(descr, examples): +def argparser(descr: str, examples: List[str]) -> argparse.ArgumentParser: """Return a new ArgumentParser object""" return argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, @@ -49,16 +50,16 @@ def argparser(descr, examples): class HadCRUT5: """Class for parsing and plotting HadCRUT5 datasets""" - # current dataset version _DATASET_VERSION = "5.0.2.0" + """current dataset version""" - # list of all the available data types _DEFAULT_DATATYPE = "annual" _VALID_DATATYPES = [_DEFAULT_DATATYPE, "monthly"] + """list of all the available data types""" - # list of all the valid periods _DEFAULT_PERIOD = "1961-1990" _VALID_PERIODS = [_DEFAULT_PERIOD, "1850-1900", "1880-1920"] + """list of all the valid periods""" GLOBAL_REGION = "Global" NORTHERN_REGION = "Northern Hemisphere" @@ -114,10 +115,10 @@ def datasets_download(self): if self._enable_southern: self._wget_dataset_file(self._southern_hemisphere_filename) - def datasets_load(self): + def datasets_load(self) -> None: """Load all the netCDFv4 datasets""" - def dataset_load(dataset_filename): + def dataset_load(dataset_filename: str) -> Dict: """Load the data provided by the netCDFv4 file 'dataset_filename'""" dataset = nc_Dataset(dataset_filename) return { @@ -126,7 +127,7 @@ def dataset_load(dataset_filename): "variables": dataset.variables, } - def dataset_metadata_dump(dataset_name, dataset): + def dataset_metadata_dump(dataset_name: str, dataset) -> None: metadata = dataset["metadata"] self.logging_debug( (f'Metadata for "{dataset_name}" dataset:\n{json.dumps(metadata, indent=2)}'), @@ -145,14 +146,14 @@ def dataset_metadata_dump(dataset_name, dataset): self._datasets[region] = dataset_load(self._southern_hemisphere_filename) dataset_metadata_dump(region, self._datasets[region]) - def datasets_normalize(self): + def datasets_normalize(self) -> None: """ Normalize the temperature means to the required time period. Set _datasets_normalized with a tuple containing lower, mean, and upper temperatures for every enabled region """ - def normalization_value(temperatures): + def normalization_value(temperatures: List[float]) -> float: """ Return the value to be substracted to temperatures in order to obtain a mean-centered dataset for the required period @@ -176,7 +177,7 @@ def normalization_value(temperatures): raise ValueError(f'Unsupported period "{self._period}"') self.logging_debug("The mean anomaly in {self._period} is about {norm_temp:.8f}°C") - return norm_temp + return float(norm_temp) for region, data in self._datasets.items(): mean = data["variables"]["tas_mean"] @@ -196,26 +197,26 @@ def normalization_value(temperatures): f"normalized dataset ({region}): mean \\\n{np.array(mean) - norm_temp}" ) - def datasets_regions(self): + def datasets_regions(self) -> Mapping[str, object]: """Return the dataset regions set by the user at command-line""" return self._datasets.keys() - def logging(self, message): + def logging(self, message: str) -> None: """Print a message""" logging.info(message) - def logging_debug(self, message): + def logging_debug(self, message: str) -> None: """Print a message when in verbose mode only""" if self._verbose: logging.info(message) - def _hadcrut5_data_url(self, filename): + def _hadcrut5_data_url(self, filename: str) -> str: site = "https://www.metoffice.gov.uk" path = f"/hadobs/hadcrut5/data/HadCRUT.{self._DATASET_VERSION}/analysis/diagnostics/" url = f"{site}{path}{filename}" return url - def _wget_dataset_file(self, filename): + def _wget_dataset_file(self, filename: str) -> None: """Download a netCDFv4 HadCRUT5 file if not already found locally""" try: with open(filename, encoding="utf-8"): @@ -240,19 +241,19 @@ def _wget_dataset_file(self, filename): handle.write(block) @property - def dataset_datatype(self): + def dataset_datatype(self) -> str: """Return the datatype string""" return self._datatype @property - def dataset_history(self): + def dataset_history(self) -> str: """Return the datatype history from metadata""" # The datasets have all the same length so choose the first one region = list(self._datasets.keys())[0] metadata = self._datasets[region]["metadata"] return metadata.get("history") - def dataset_normalized_data(self, region): + def dataset_normalized_data(self, region) -> Tuple[List[float], List[float], List[float]]: """Return the dataset data normalized for the specified region""" lower = self._datasets_normalized[region]["lower"] mean = self._datasets_normalized[region]["mean"] @@ -260,11 +261,11 @@ def dataset_normalized_data(self, region): return (lower, mean, upper) @property - def dataset_period(self): + def dataset_period(self) -> str: """Return the dataset period as a string""" return self._period - def dataset_years(self): + def dataset_years(self) -> List[int|float]: """ Return an array of years corresponding of the loaded datasets. If the original dataset packages monthly data, the returning vector @@ -279,7 +280,7 @@ def dataset_years(self): return years @property - def is_monthly_dataset(self): + def is_monthly_dataset(self) -> bool: """ Return True if the loaded dataset provide monthly data, False otherwise