diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index aab1cacd52..3531be2432 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -45,6 +45,10 @@ repos: hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.812' + rev: 'v0.990' hooks: - id: mypy + additional_dependencies: + - 'types-PyYAML' + - 'types-pkg_resources' + - 'types-requests' diff --git a/environment.yml b/environment.yml index c219e6a5a4..21795e34b2 100644 --- a/environment.yml +++ b/environment.yml @@ -14,6 +14,7 @@ dependencies: - esmpy!=8.1.0 # see github.com/ESMValGroup/ESMValCore/issues/1208 - geopy - iris>=3.2.1 + - mypy>=0.990 - nested-lookup - netcdf4!=1.6.1 # github.com/ESMValGroup/ESMValCore/issues/1723 - pandas diff --git a/esmvalcore/_task.py b/esmvalcore/_task.py index 7fc7bf893b..9e158408a0 100644 --- a/esmvalcore/_task.py +++ b/esmvalcore/_task.py @@ -15,6 +15,7 @@ from multiprocessing import Pool from pathlib import Path, PosixPath from shutil import which +from typing import Optional import psutil import yaml @@ -709,7 +710,7 @@ def get_independent(self) -> 'TaskSet': independent_tasks.add(task) return independent_tasks - def run(self, max_parallel_tasks: int = None) -> None: + def run(self, max_parallel_tasks: Optional[int] = None) -> None: """Run tasks. Parameters diff --git a/esmvalcore/cmor/table.py b/esmvalcore/cmor/table.py index e6a76c0767..f4db69f2a3 100644 --- a/esmvalcore/cmor/table.py +++ b/esmvalcore/cmor/table.py @@ -3,6 +3,8 @@ Read variable information from CMOR 2 and CMOR 3 tables and make it easily available for the other components of ESMValTool """ +from __future__ import annotations + import copy import errno import glob diff --git a/esmvalcore/config/_config_validators.py b/esmvalcore/config/_config_validators.py index 26990c49e7..63e33968d0 100644 --- a/esmvalcore/config/_config_validators.py +++ b/esmvalcore/config/_config_validators.py @@ -1,4 +1,6 @@ """List of config validators.""" +from __future__ import annotations + import logging import os.path import warnings @@ -243,7 +245,7 @@ def validate_diagnostics( } -def deprecate(func, variable, version: str = None): +def deprecate(func, variable, version: Optional[str] = None): """Wrap function to mark variables to be deprecated. This will give a warning if the function will be/has been deprecated. diff --git a/esmvalcore/config/_logging.py b/esmvalcore/config/_logging.py index 0831618fe3..674fad953d 100644 --- a/esmvalcore/config/_logging.py +++ b/esmvalcore/config/_logging.py @@ -5,7 +5,7 @@ import os import time from pathlib import Path -from typing import Union +from typing import Optional, Union import yaml @@ -27,8 +27,10 @@ def _purge_file_handlers(cfg: dict) -> None: ] -def _get_log_files(cfg: dict, - output_dir: Union[os.PathLike, str] = None) -> list: +def _get_log_files( + cfg: dict, + output_dir: Optional[Union[os.PathLike, str]] = None, +) -> list: """Initialize log files for the file handlers.""" log_files = [] @@ -59,9 +61,11 @@ def _update_stream_level(cfg: dict, level=None): handler['level'] = level.upper() -def configure_logging(cfg_file: Union[os.PathLike, str] = None, - output_dir: Union[os.PathLike, str] = None, - console_log_level: str = None) -> list: +def configure_logging( + cfg_file: Optional[Union[os.PathLike, str]] = None, + output_dir: Optional[Union[os.PathLike, str]] = None, + console_log_level: Optional[str] = None, +) -> list: """Configure logging. Parameters diff --git a/esmvalcore/experimental/recipe.py b/esmvalcore/experimental/recipe.py index c237164ed6..eb68d58655 100644 --- a/esmvalcore/experimental/recipe.py +++ b/esmvalcore/experimental/recipe.py @@ -100,7 +100,11 @@ def _load(self, session: Session) -> RecipeEngine: config_user=config_user, recipe_file=self.path) - def run(self, task: str = None, session: Session = None): + def run( + self, + task: Optional[str] = None, + session: Optional[Session] = None, + ): """Run the recipe. This function loads the recipe into the ESMValCore recipe format diff --git a/esmvalcore/experimental/recipe_metadata.py b/esmvalcore/experimental/recipe_metadata.py index e8842a5fe2..1801e92ca2 100644 --- a/esmvalcore/experimental/recipe_metadata.py +++ b/esmvalcore/experimental/recipe_metadata.py @@ -1,5 +1,7 @@ """API for recipe metadata.""" +from typing import Optional + import pybtex from pybtex.database.input import bibtex @@ -23,7 +25,7 @@ class Contributor: ORCID url """ - def __init__(self, name: str, institute: str, orcid: str = None): + def __init__(self, name: str, institute: str, orcid: Optional[str] = None): self.name = name self.institute = institute self.orcid = orcid diff --git a/esmvalcore/experimental/recipe_output.py b/esmvalcore/experimental/recipe_output.py index 7ed357595b..40de3150f5 100644 --- a/esmvalcore/experimental/recipe_output.py +++ b/esmvalcore/experimental/recipe_output.py @@ -255,7 +255,7 @@ class OutputFile(): kind: Optional[str] = None - def __init__(self, path: str, attributes: dict = None): + def __init__(self, path: str, attributes: Optional[dict] = None): if not attributes: attributes = {} @@ -291,7 +291,7 @@ def references(self) -> tuple: self._references = tuple(Reference.from_tag(tag) for tag in tags) return self._references - def _get_derived_path(self, append: str, suffix: str = None): + def _get_derived_path(self, append: str, suffix: Optional[str] = None): """Return path of related files. Parameters @@ -325,7 +325,12 @@ def provenance_xml_file(self): return self._get_derived_path('_provenance', '.xml') @classmethod - def create(cls, path: str, attributes: dict = None) -> 'OutputFile': + def create( + cls, + path: str, + attributes: + Optional[dict] = None, + ) -> 'OutputFile': """Construct new instances of OutputFile. Chooses a derived class if suitable. diff --git a/esmvalcore/experimental/utils.py b/esmvalcore/experimental/utils.py index 2926053dee..3c2b1f003a 100644 --- a/esmvalcore/experimental/utils.py +++ b/esmvalcore/experimental/utils.py @@ -3,7 +3,7 @@ import os import re from pathlib import Path -from typing import Pattern, Tuple, Union +from typing import Optional, Pattern, Tuple, Union from esmvalcore.config._diagnostics import DIAGNOSTICS @@ -41,7 +41,7 @@ def find(self, query: Pattern[str]): return matches -def get_all_recipes(subdir: str = None) -> list: +def get_all_recipes(subdir: Optional[str] = None) -> list: """Return a list of all available recipes. Parameters diff --git a/setup.cfg b/setup.cfg index 88fee9d4fc..6ea564b2a6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -34,7 +34,6 @@ include_trailing_comma = true [mypy] # see mypy.readthedocs.io/en/stable/command_line.html -implicit_optional = True ignore_missing_imports = True files = esmvalcore, tests diff --git a/setup.py b/setup.py index 528d34d0d7..25e996e274 100755 --- a/setup.py +++ b/setup.py @@ -70,6 +70,7 @@ 'pytest-xdist', 'ESMValTool_sample_data==0.0.3', # MyPy library stubs + 'mypy>=0.990', 'types-requests', 'types-pkg_resources', 'types-PyYAML', diff --git a/tests/sample_data/multimodel_statistics/test_multimodel.py b/tests/sample_data/multimodel_statistics/test_multimodel.py index 485328d85b..480169dc61 100644 --- a/tests/sample_data/multimodel_statistics/test_multimodel.py +++ b/tests/sample_data/multimodel_statistics/test_multimodel.py @@ -4,6 +4,7 @@ import platform from itertools import groupby from pathlib import Path +from typing import Optional import iris import numpy as np @@ -69,7 +70,7 @@ def fix_metadata(cubes): cube.coord('air_pressure').bounds = None -def preprocess_data(cubes, time_slice: dict = None): +def preprocess_data(cubes, time_slice: Optional[dict] = None): """Regrid the data to the first cube and optional time-slicing.""" # Increase TEST_REVISION anytime you make changes to this function. if time_slice: @@ -92,7 +93,8 @@ def get_cache_key(value): """Get a cache key that is hopefully unique enough for unpickling. If this doesn't avoid problems with unpickling the cached data, - manually clean the pytest cache with the command `pytest --cache-clear`. + manually clean the pytest cache with the command `pytest --cache- + clear`. """ py_version = platform.python_version() return (f'{value}_iris-{iris.__version__}_'