diff --git a/ci/requirements-py36.yml b/ci/requirements-py36.yml index 2986dc33adb..311e4a275a8 100644 --- a/ci/requirements-py36.yml +++ b/ci/requirements-py36.yml @@ -14,7 +14,7 @@ dependencies: - pytest-cov - pytest-env - coveralls - - flake8 + - pycodestyle - numpy - pandas - scipy @@ -32,3 +32,4 @@ dependencies: - lxml - pip: - cfgrib>=0.9.2 + - mypy==0.650 diff --git a/ci/requirements-py37.yml b/ci/requirements-py37.yml index afd18e37a75..1a98e6b285c 100644 --- a/ci/requirements-py37.yml +++ b/ci/requirements-py37.yml @@ -29,3 +29,4 @@ dependencies: - pydap - pip: - cfgrib>=0.9.2 + - mypy==0.650 diff --git a/doc/contributing.rst b/doc/contributing.rst index ceba81d9319..da9c89234a3 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -345,19 +345,26 @@ the more common ``PEP8`` issues: - passing arguments should have spaces after commas, e.g. ``foo(arg1, arg2, kw1='bar')`` :ref:`Continuous Integration ` will run -the `flake8 `_ tool +the `pycodestyle `_ tool and report any stylistic errors in your code. Therefore, it is helpful before submitting code to run the check yourself:: - flake8 + pycodestyle xarray -If you install `isort `_ and -`flake8-isort `_, this will also show -any errors from incorrectly sorted imports. These aren't currently enforced in -CI. To automatically sort imports, you can run:: +Other recommended but optional tools for checking code quality (not currently +enforced in CI): - isort -y +- `mypy `_ performs static type checking, which can + make it easier to catch bugs. Please run ``mypy xarray`` if you annotate any + code with `type hints `_. +- `flake8 `_ includes a few more automated + checks than those enforced by pycodestyle. +- `isort `_ will highlight + incorrectly sorted imports. ``isort -y`` will automatically fix them. See + also `flake8-isort `_. +Note that your code editor probably supports extensions that can show results +of these checks inline as you type. Backwards Compatibility ~~~~~~~~~~~~~~~~~~~~~~~ @@ -365,8 +372,7 @@ Backwards Compatibility Please try to maintain backward compatibility. *xarray* has growing number of users with lots of existing code, so don't break it if at all possible. If you think breakage is required, clearly state why as part of the pull request. Also, be careful when changing -method signatures and add deprecation warnings where needed. Also, add the deprecated -sphinx directive to the deprecated functions or methods. +method signatures and add deprecation warnings where needed. .. _contributing.ci: diff --git a/setup.cfg b/setup.cfg index 21ba5ee8dec..c80ff300a60 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,6 +20,64 @@ default_section=THIRDPARTY known_first_party=xarray multi_line_output=4 +# Most of the numerical computing stack doesn't have type annotations yet. +[mypy-bottleneck.*] +ignore_missing_imports = True +[mypy-cdms2.*] +ignore_missing_imports = True +[mypy-cf_units.*] +ignore_missing_imports = True +[mypy-cfgrib.*] +ignore_missing_imports = True +[mypy-cftime.*] +ignore_missing_imports = True +[mypy-dask.*] +ignore_missing_imports = True +[mypy-distributed.*] +ignore_missing_imports = True +[mypy-h5netcdf.*] +ignore_missing_imports = True +[mypy-h5py.*] +ignore_missing_imports = True +[mypy-iris.*] +ignore_missing_imports = True +[mypy-matplotlib.*] +ignore_missing_imports = True +[mypy-Nio.*] +ignore_missing_imports = True +[mypy-numpy.*] +ignore_missing_imports = True +[mypy-netCDF4.*] +ignore_missing_imports = True +[mypy-netcdftime.*] +ignore_missing_imports = True +[mypy-pandas.*] +ignore_missing_imports = True +[mypy-PseudoNetCDF.*] +ignore_missing_imports = True +[mypy-pydap.*] +ignore_missing_imports = True +[mypy-pytest.*] +ignore_missing_imports = True +[mypy-rasterio.*] +ignore_missing_imports = True +[mypy-scipy.*] +ignore_missing_imports = True +[mypy-seaborn.*] +ignore_missing_imports = True +[mypy-toolz.*] +ignore_missing_imports = True +[mypy-zarr.*] +ignore_missing_imports = True + +# written by versioneer +[mypy-xarray._version] +ignore_errors = True +# version spanning code is hard to type annotate (and most of this module will +# be going away soon anyways) +[mypy-xarray.core.pycompat] +ignore_errors = True + [versioneer] VCS = git style = pep440 diff --git a/xarray/backends/file_manager.py b/xarray/backends/file_manager.py index d329f9e734f..d0efba86bf9 100644 --- a/xarray/backends/file_manager.py +++ b/xarray/backends/file_manager.py @@ -1,5 +1,6 @@ import contextlib import threading +from typing import Any, Dict import warnings from ..core import utils @@ -13,7 +14,7 @@ assert FILE_CACHE.maxsize, 'file cache must be at least size one' -REF_COUNTS = {} +REF_COUNTS = {} # type: Dict[Any, int] _DEFAULT_MODE = utils.ReprObject('') diff --git a/xarray/backends/locks.py b/xarray/backends/locks.py index 6c135fd1240..bca27a0bbc1 100644 --- a/xarray/backends/locks.py +++ b/xarray/backends/locks.py @@ -1,5 +1,6 @@ import multiprocessing import threading +from typing import Any, MutableMapping import weakref try: @@ -20,7 +21,7 @@ NETCDFC_LOCK = SerializableLock() -_FILE_LOCKS = weakref.WeakValueDictionary() +_FILE_LOCKS = weakref.WeakValueDictionary() # type: MutableMapping[Any, threading.Lock] # noqa def _get_threaded_lock(key): diff --git a/xarray/coding/cftime_offsets.py b/xarray/coding/cftime_offsets.py index f8e1cfa6718..98571c9a995 100644 --- a/xarray/coding/cftime_offsets.py +++ b/xarray/coding/cftime_offsets.py @@ -43,6 +43,7 @@ import re from datetime import timedelta from functools import partial +from typing import ClassVar, Optional import numpy as np @@ -74,7 +75,7 @@ def get_date_type(calendar): class BaseCFTimeOffset(object): - _freq = None + _freq = None # type: ClassVar[str] def __init__(self, n=1): if not isinstance(n, int): @@ -254,9 +255,9 @@ def onOffset(self, date): class YearOffset(BaseCFTimeOffset): - _freq = None - _day_option = None - _default_month = None + _freq = None # type: ClassVar[str] + _day_option = None # type: ClassVar[str] + _default_month = None # type: ClassVar[int] def __init__(self, n=1, month=None): BaseCFTimeOffset.__init__(self, n) diff --git a/xarray/coding/variables.py b/xarray/coding/variables.py index d5963b0e94f..d8453a95fad 100644 --- a/xarray/coding/variables.py +++ b/xarray/coding/variables.py @@ -1,6 +1,7 @@ """Coders for individual Variable objects.""" from __future__ import absolute_import, division, print_function +from typing import Any import warnings from functools import partial @@ -126,11 +127,12 @@ def pop_to(source, dest, key, name=None): return value -def _apply_mask(data, # type: np.ndarray - encoded_fill_values, # type: list - decoded_fill_value, # type: Any - dtype, # type: Any - ): # type: np.ndarray +def _apply_mask( + data: np.ndarray, + encoded_fill_values: list, + decoded_fill_value: Any, + dtype: Any, +) -> np.ndarray: """Mask all matching values in a NumPy arrays.""" data = np.asarray(data, dtype=dtype) condition = False diff --git a/xarray/core/alignment.py b/xarray/core/alignment.py index 33902abaf3e..789bea90b55 100644 --- a/xarray/core/alignment.py +++ b/xarray/core/alignment.py @@ -31,7 +31,7 @@ def _get_joiner(join): raise ValueError('invalid value for join: %s' % join) -_DEFAULT_EXCLUDE = frozenset() +_DEFAULT_EXCLUDE = frozenset() # type: frozenset def align(*objects, **kwargs): diff --git a/xarray/core/common.py b/xarray/core/common.py index 923d30aad11..d272115f492 100644 --- a/xarray/core/common.py +++ b/xarray/core/common.py @@ -24,7 +24,7 @@ def wrapped_func(self, dim=None, axis=None, skipna=None, return self.reduce(func, dim, axis, skipna=skipna, allow_lazy=True, **kwargs) else: - def wrapped_func(self, dim=None, axis=None, + def wrapped_func(self, dim=None, axis=None, # type: ignore **kwargs): return self.reduce(func, dim, axis, allow_lazy=True, **kwargs) @@ -56,7 +56,7 @@ def wrapped_func(self, dim=None, skipna=None, numeric_only=numeric_only, allow_lazy=True, **kwargs) else: - def wrapped_func(self, dim=None, **kwargs): + def wrapped_func(self, dim=None, **kwargs): # type: ignore return self.reduce(func, dim, numeric_only=numeric_only, allow_lazy=True, **kwargs) diff --git a/xarray/core/computation.py b/xarray/core/computation.py index 7998cc4f72f..bf9ab56bbb4 100644 --- a/xarray/core/computation.py +++ b/xarray/core/computation.py @@ -8,6 +8,10 @@ import operator from collections import Counter from distutils.version import LooseVersion +from typing import ( + AbstractSet, Any, Dict, Iterable, List, Mapping, Union, Tuple, + TYPE_CHECKING, TypeVar +) import numpy as np @@ -16,8 +20,11 @@ from .merge import expand_and_merge_variables from .pycompat import OrderedDict, basestring, dask_array_type from .utils import is_dict_like +from .variable import Variable +if TYPE_CHECKING: + from .dataset import Dataset -_DEFAULT_FROZEN_SET = frozenset() +_DEFAULT_FROZEN_SET = frozenset() # type: frozenset _NO_FILL_VALUE = utils.ReprObject('') _DEFAULT_NAME = utils.ReprObject('') _JOINS_WITHOUT_FILL_VALUES = frozenset({'inner', 'exact'}) @@ -111,8 +118,7 @@ def to_gufunc_string(self): return str(alt_signature) -def result_name(objects): - # type: List[object] -> Any +def result_name(objects: list) -> Any: # use the same naming heuristics as pandas: # https://github.com/blaze/blaze/issues/458#issuecomment-51936356 names = {getattr(obj, 'name', _DEFAULT_NAME) for obj in objects} @@ -138,10 +144,10 @@ def _get_coord_variables(args): def build_output_coords( - args, # type: list - signature, # type: _UFuncSignature - exclude_dims=frozenset(), # type: set -): + args: list, + signature: _UFuncSignature, + exclude_dims: AbstractSet = frozenset(), +) -> 'List[OrderedDict[Any, Variable]]': """Build output coordinates for an operation. Parameters @@ -159,7 +165,6 @@ def build_output_coords( ------- OrderedDict of Variable objects with merged coordinates. """ - # type: (...) -> List[OrderedDict[Any, Variable]] input_coords = _get_coord_variables(args) if exclude_dims: @@ -220,8 +225,7 @@ def apply_dataarray_ufunc(func, *args, **kwargs): return out -def ordered_set_union(all_keys): - # type: List[Iterable] -> Iterable +def ordered_set_union(all_keys: List[Iterable]) -> Iterable: result_dict = OrderedDict() for keys in all_keys: for key in keys: @@ -229,8 +233,7 @@ def ordered_set_union(all_keys): return result_dict.keys() -def ordered_set_intersection(all_keys): - # type: List[Iterable] -> Iterable +def ordered_set_intersection(all_keys: List[Iterable]) -> Iterable: intersection = set(all_keys[0]) for keys in all_keys[1:]: intersection.intersection_update(keys) @@ -284,9 +287,9 @@ def _as_variables_or_variable(arg): def _unpack_dict_tuples( result_vars, # type: Mapping[Any, Tuple[Variable]] - num_outputs, # type: int + num_outputs, # type: int ): - # type: (...) -> Tuple[Dict[Any, Variable]] + # type: (...) -> Tuple[Dict[Any, Variable], ...] out = tuple(OrderedDict() for _ in range(num_outputs)) for name, values in result_vars.items(): for value, results_dict in zip(values, out): @@ -438,8 +441,11 @@ def apply_groupby_ufunc(func, *args): return combined -def unified_dim_sizes(variables, exclude_dims=frozenset()): - # type: Iterable[Variable] -> OrderedDict[Any, int] +def unified_dim_sizes( + variables: Iterable[Variable], + exclude_dims: AbstractSet = frozenset(), +) -> 'OrderedDict[Any, int]': + dim_sizes = OrderedDict() for var in variables: @@ -460,11 +466,9 @@ def unified_dim_sizes(variables, exclude_dims=frozenset()): SLICE_NONE = slice(None) -# A = TypeVar('A', numpy.ndarray, dask.array.Array) - def broadcast_compat_data(variable, broadcast_dims, core_dims): - # type: (Variable[A], tuple, tuple) -> A + # type: (Variable, tuple, tuple) -> Any data = variable.data old_dims = variable.dims diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index a63b63b45bf..f27958b1c77 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -771,7 +771,8 @@ def __deepcopy__(self, memo=None): return self.copy(deep=True) # mutable objects should not be hashable - __hash__ = None + # https://github.com/python/mypy/issues/4266 + __hash__ = None # type: ignore @property def chunks(self): diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 29178c9b13c..2caf45ce954 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -6,6 +6,7 @@ from collections import Mapping, defaultdict from distutils.version import LooseVersion from numbers import Number +from typing import Any, Dict, List, Set, Tuple, Union import numpy as np import pandas as pd @@ -124,14 +125,14 @@ def merge_indexes( Not public API. Used in Dataset and DataArray set_index methods. """ - vars_to_replace = {} - vars_to_remove = [] + vars_to_replace = {} # Dict[Any, Variable] + vars_to_remove = [] # type: list for dim, var_names in indexes.items(): if isinstance(var_names, basestring): var_names = [var_names] - names, labels, levels = [], [], [] + names, labels, levels = [], [], [] # type: (list, list, list) current_index_variable = variables.get(dim) for n in var_names: @@ -195,7 +196,7 @@ def split_indexes( if isinstance(dims_or_levels, basestring): dims_or_levels = [dims_or_levels] - dim_levels = defaultdict(list) + dim_levels = defaultdict(list) # type: Dict[Any, list] dims = [] for k in dims_or_levels: if k in level_coords: @@ -1005,7 +1006,8 @@ def __delitem__(self, key): self._coord_names.discard(key) # mutable objects should not be hashable - __hash__ = None + # https://github.com/python/mypy/issues/4266 + __hash__ = None # type: ignore def _all_compat(self, other, compat_str): """Helper function for equals and identical""" @@ -1683,7 +1685,8 @@ def relevant_keys(mapping): if any(d in indexer_dims for d in v.dims)] coords = relevant_keys(self.coords) - indexers = [(k, np.asarray(v)) for k, v in iteritems(indexers)] + indexers = [(k, np.asarray(v)) # type: ignore + for k, v in iteritems(indexers)] indexers_dict = dict(indexers) non_indexed_dims = set(self.dims) - indexer_dims non_indexed_coords = set(self.coords) - set(coords) @@ -1693,9 +1696,9 @@ def relevant_keys(mapping): for k, v in indexers: if k not in self.dims: raise ValueError("dimension %s does not exist" % k) - if v.dtype.kind != 'i': + if v.dtype.kind != 'i': # type: ignore raise TypeError('Indexers must be integers') - if v.ndim != 1: + if v.ndim != 1: # type: ignore raise ValueError('Indexers must be 1 dimensional') # all the indexers should have the same length diff --git a/xarray/core/dtypes.py b/xarray/core/dtypes.py index a2f11728b4d..00ff7958183 100644 --- a/xarray/core/dtypes.py +++ b/xarray/core/dtypes.py @@ -42,29 +42,6 @@ def __eq__(self, other): ] -@functools.total_ordering -class AlwaysGreaterThan(object): - def __gt__(self, other): - return True - - def __eq__(self, other): - return isinstance(other, type(self)) - - -@functools.total_ordering -class AlwaysLessThan(object): - def __lt__(self, other): - return True - - def __eq__(self, other): - return isinstance(other, type(self)) - - -# Equivalence to np.inf (-np.inf) for object-type -INF = AlwaysGreaterThan() -NINF = AlwaysLessThan() - - def maybe_promote(dtype): """Simpler equivalent of pandas.core.common._maybe_promote diff --git a/xarray/core/duck_array_ops.py b/xarray/core/duck_array_ops.py index b02eb4e899b..54fd8881a56 100644 --- a/xarray/core/duck_array_ops.py +++ b/xarray/core/duck_array_ops.py @@ -6,9 +6,9 @@ from __future__ import absolute_import, division, print_function import contextlib +from functools import partial import inspect import warnings -from functools import partial import numpy as np import pandas as pd @@ -21,8 +21,8 @@ import dask.array as dask_array from . import dask_array_compat except ImportError: - dask_array = None - dask_array_compat = None + dask_array = None # type: ignore + dask_array_compat = None # type: ignore def _dask_or_eager_func(name, eager_module=np, dask_module=dask_array, @@ -43,10 +43,10 @@ def f(*args, **kwargs): (e, requires_dask)) else: wrapped = getattr(eager_module, name) - return wrapped(*args, ** kwargs) + return wrapped(*args, **kwargs) else: - def f(data, *args, **kwargs): - return getattr(eager_module, name)(data, *args, **kwargs) + def f(*args, **kwargs): + return getattr(eager_module, name)(*args, **kwargs) return f diff --git a/xarray/core/groupby.py b/xarray/core/groupby.py index ec8329d6805..aa8ced5adab 100644 --- a/xarray/core/groupby.py +++ b/xarray/core/groupby.py @@ -630,7 +630,7 @@ def wrapped_func(self, dim=DEFAULT_DIMS, axis=None, skipna=None, return self.reduce(func, dim, axis, keep_attrs=keep_attrs, skipna=skipna, allow_lazy=True, **kwargs) else: - def wrapped_func(self, dim=DEFAULT_DIMS, axis=None, + def wrapped_func(self, dim=DEFAULT_DIMS, axis=None, # type: ignore keep_attrs=None, **kwargs): return self.reduce(func, dim, axis, keep_attrs=keep_attrs, allow_lazy=True, **kwargs) @@ -748,7 +748,7 @@ def wrapped_func(self, dim=DEFAULT_DIMS, skipna=skipna, numeric_only=numeric_only, allow_lazy=True, **kwargs) else: - def wrapped_func(self, dim=DEFAULT_DIMS, + def wrapped_func(self, dim=DEFAULT_DIMS, # type: ignore **kwargs): return self.reduce(func, dim, numeric_only=numeric_only, allow_lazy=True, diff --git a/xarray/core/merge.py b/xarray/core/merge.py index 2a5e7acbb25..637a9cbda7f 100644 --- a/xarray/core/merge.py +++ b/xarray/core/merge.py @@ -1,11 +1,18 @@ from __future__ import absolute_import, division, print_function +from typing import ( + Any, Dict, List, Mapping, Optional, Set, Tuple, TYPE_CHECKING, Union, +) + import pandas as pd from .alignment import deep_align from .pycompat import OrderedDict, basestring from .utils import Frozen -from .variable import as_variable, assert_unique_multiindex_level_names +from .variable import ( + Variable, as_variable, assert_unique_multiindex_level_names) +if TYPE_CHECKING: + from .dataset import Dataset PANDAS_TYPES = (pd.Series, pd.DataFrame, pd.Panel) @@ -145,13 +152,13 @@ def merge_variables( # variables appear merged = OrderedDict() - for name, variables in lookup.items(): + for name, var_list in lookup.items(): if name in priority_vars: # one of these arguments (e.g., the first for in-place arithmetic # or the second for Dataset.update) takes priority merged[name] = priority_vars[name] else: - dim_variables = [var for var in variables if (name,) == var.dims] + dim_variables = [var for var in var_list if (name,) == var.dims] if dim_variables: # if there are dimension coordinates, these must be equal (or # identical), and they take priority over non-dimension @@ -159,7 +166,7 @@ def merge_variables( merged[name] = unique_variable(name, dim_variables, dim_compat) else: try: - merged[name] = unique_variable(name, variables, compat) + merged[name] = unique_variable(name, var_list, compat) except MergeError: if compat != 'minimal': # we need more than "minimal" compatibility (for which @@ -236,8 +243,8 @@ def determine_coords(list_of_variable_dicts): from .dataarray import DataArray from .dataset import Dataset - coord_names = set() - noncoord_names = set() + coord_names = set() # type: set + noncoord_names = set() # type: set for variables in list_of_variable_dicts: if isinstance(variables, Dataset): diff --git a/xarray/core/missing.py b/xarray/core/missing.py index 5624d9b5092..ff0e63801bc 100644 --- a/xarray/core/missing.py +++ b/xarray/core/missing.py @@ -3,6 +3,7 @@ import warnings from collections import Iterable from functools import partial +from typing import Any, Dict import numpy as np import pandas as pd @@ -18,8 +19,8 @@ class BaseInterpolator(object): '''gerneric interpolator class for normalizing interpolation methods''' - cons_kwargs = {} - call_kwargs = {} + cons_kwargs = {} # type: Dict[str, Any] + call_kwargs = {} # type: Dict[str, Any] f = None method = None diff --git a/xarray/core/utils.py b/xarray/core/utils.py index e961426195e..085eaaa5ed1 100644 --- a/xarray/core/utils.py +++ b/xarray/core/utils.py @@ -488,7 +488,7 @@ def __repr__(self): class ReprObject(object): """Object that prints as the given value, for use with sentinel values.""" - def __init__(self, value): # type: str + def __init__(self, value: str): self._value = value def __repr__(self): diff --git a/xarray/core/variable.py b/xarray/core/variable.py index 48acc8edff9..8bd7225efc3 100644 --- a/xarray/core/variable.py +++ b/xarray/core/variable.py @@ -4,6 +4,7 @@ import itertools from collections import defaultdict from datetime import timedelta +from typing import Tuple, Type import numpy as np import pandas as pd @@ -28,7 +29,8 @@ NON_NUMPY_SUPPORTED_ARRAY_TYPES = ( indexing.ExplicitlyIndexed, pd.Index) + dask_array_type -BASIC_INDEXING_TYPES = integer_types + (slice,) +# https://github.com/python/mypy/issues/224 +BASIC_INDEXING_TYPES = integer_types + (slice,) # type: ignore class MissingDimensionsError(ValueError): @@ -414,6 +416,10 @@ def dims(self): """ return self._dims + @dims.setter + def dims(self, value): + self._dims = self._parse_dimensions(value) + def _parse_dimensions(self, dims): if isinstance(dims, basestring): dims = (dims,) @@ -424,10 +430,6 @@ def _parse_dimensions(self, dims): % (dims, self.ndim)) return dims - @dims.setter - def dims(self, value): - self._dims = self._parse_dimensions(value) - def _item_key_to_tuple(self, key): if utils.is_dict_like(key): return tuple(key.get(dim, slice(None)) for dim in self.dims) @@ -816,7 +818,8 @@ def __deepcopy__(self, memo=None): return self.copy(deep=True) # mutable objects should not be hashable - __hash__ = None + # https://github.com/python/mypy/issues/4266 + __hash__ = None # type: ignore @property def chunks(self): @@ -1801,7 +1804,8 @@ def load(self): # data is already loaded into memory for IndexVariable return self - @Variable.data.setter + # https://github.com/python/mypy/issues/1465 + @Variable.data.setter # type: ignore def data(self, data): Variable.data.fset(self, data) if not isinstance(self._data, PandasIndexAdapter): diff --git a/xarray/tests/__init__.py b/xarray/tests/__init__.py index 52345396ffa..58f76596822 100644 --- a/xarray/tests/__init__.py +++ b/xarray/tests/__init__.py @@ -6,6 +6,7 @@ from distutils import version import re import importlib +from unittest import mock import numpy as np from numpy.testing import assert_array_equal # noqa: F401 @@ -25,12 +26,6 @@ # old location, for pandas < 0.20 from pandas.util.testing import assert_frame_equal # noqa: F401 - -try: - from unittest import mock -except ImportError: - import mock # noqa: F401 - # import mpl and change the backend before other mpl imports try: import matplotlib as mpl diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py index 57e875bb563..d3c8599b21b 100644 --- a/xarray/tests/test_backends.py +++ b/xarray/tests/test_backends.py @@ -4,10 +4,12 @@ import itertools import math import os.path +from pathlib import Path import pickle import shutil import sys import tempfile +from typing import Optional import warnings from io import BytesIO @@ -47,14 +49,6 @@ except ImportError: pass -try: - from pathlib import Path -except ImportError: - try: - from pathlib2 import Path - except ImportError: - pass - ON_WINDOWS = sys.platform == 'win32' @@ -172,8 +166,8 @@ class NetCDF3Only(object): class DatasetIOBase(object): - engine = None - file_format = None + engine = None # type: Optional[str] + file_format = None # type: Optional[str] def create_store(self): raise NotImplementedError diff --git a/xarray/tests/test_backends_file_manager.py b/xarray/tests/test_backends_file_manager.py index 9c4c1cf815c..4405454e216 100644 --- a/xarray/tests/test_backends_file_manager.py +++ b/xarray/tests/test_backends_file_manager.py @@ -1,6 +1,7 @@ import gc import pickle import threading +from unittest import mock import pytest @@ -8,11 +9,6 @@ from xarray.backends.lru_cache import LRUCache from xarray.core.options import set_options -try: - from unittest import mock -except ImportError: - import mock # noqa: F401 - @pytest.fixture(params=[1, 2, 3, None]) def file_cache(request): diff --git a/xarray/tests/test_backends_lru_cache.py b/xarray/tests/test_backends_lru_cache.py index 03eb6dcf208..d64d718f2f7 100644 --- a/xarray/tests/test_backends_lru_cache.py +++ b/xarray/tests/test_backends_lru_cache.py @@ -1,8 +1,4 @@ -try: - from unittest import mock -except ImportError: - import mock # noqa: F401 - +from unittest import mock import pytest from xarray.backends.lru_cache import LRUCache diff --git a/xarray/tests/test_cftime_offsets.py b/xarray/tests/test_cftime_offsets.py index dfb46df21e3..b9d2cf520a8 100644 --- a/xarray/tests/test_cftime_offsets.py +++ b/xarray/tests/test_cftime_offsets.py @@ -141,7 +141,7 @@ def test_to_offset_sub_annual(freq, expected): @pytest.mark.parametrize(('month_int', 'month_label'), - list(_MONTH_ABBREVIATIONS.items()) + [('', '')]) + list(_MONTH_ABBREVIATIONS.items()) + [(0, '')]) @pytest.mark.parametrize('multiple', [None, 2]) @pytest.mark.parametrize('offset_str', ['AS', 'A']) def test_to_offset_annual(month_label, month_int, multiple, offset_str): diff --git a/xarray/tests/test_cftimeindex.py b/xarray/tests/test_cftimeindex.py index 271cacb5ca0..3c4fc67c5eb 100644 --- a/xarray/tests/test_cftimeindex.py +++ b/xarray/tests/test_cftimeindex.py @@ -384,7 +384,7 @@ def test_resample_error(da): SEL_STRING_OR_LIST_TESTS = { 'string': '0001', - 'string-slice': slice('0001-01-01', '0001-12-30'), + 'string-slice': slice('0001-01-01', '0001-12-30'), # type: ignore 'bool-list': [True, True, False, False] } diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py index e7e091efa4c..e55caf1bf13 100644 --- a/xarray/tests/test_dataset.py +++ b/xarray/tests/test_dataset.py @@ -5,6 +5,7 @@ import warnings from copy import copy, deepcopy from io import StringIO +import pickle from textwrap import dedent import numpy as np @@ -26,10 +27,6 @@ raises_regex, requires_bottleneck, requires_dask, requires_scipy, source_ndarray) -try: - import cPickle as pickle -except ImportError: - import pickle try: import dask.array as da except ImportError: diff --git a/xarray/tests/test_extensions.py b/xarray/tests/test_extensions.py index ffefa78aa34..608ec798ca1 100644 --- a/xarray/tests/test_extensions.py +++ b/xarray/tests/test_extensions.py @@ -1,16 +1,13 @@ from __future__ import absolute_import, division, print_function +import pickle + import pytest import xarray as xr from . import raises_regex -try: - import cPickle as pickle -except ImportError: - import pickle - @xr.register_dataset_accessor('example_accessor') @xr.register_dataarray_accessor('example_accessor')