diff --git a/lib/iris/_constraints.py b/lib/iris/_constraints.py index 3b212bf6f2..e0fcc7b737 100644 --- a/lib/iris/_constraints.py +++ b/lib/iris/_constraints.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2017, Met Office +# (C) British Crown Copyright 2010 - 2019, Met Office # # This file is part of Iris. # @@ -23,7 +23,10 @@ from six.moves import (filter, input, map, range, zip) # noqa import six -import collections +try: # Python 3 + from collections.abc import Iterable, Mapping +except: # Python 2.7 + from collections import Iterable, Mapping import operator import numpy as np @@ -98,7 +101,7 @@ def __init__(self, name=None, cube_func=None, coord_values=None, **kwargs): raise TypeError('cube_func must be None or callable, got %r' % cube_func) if not (coord_values is None or isinstance(coord_values, - collections.Mapping)): + Mapping)): raise TypeError('coord_values must be None or a ' 'collections.Mapping, got %r' % coord_values) @@ -258,7 +261,7 @@ def extract(self, cube): try_quick = False if callable(self._coord_thing): call_func = self._coord_thing - elif (isinstance(self._coord_thing, collections.Iterable) and + elif (isinstance(self._coord_thing, Iterable) and not isinstance(self._coord_thing, (six.string_types, iris.coords.Cell))): desired_values = list(self._coord_thing) diff --git a/lib/iris/analysis/__init__.py b/lib/iris/analysis/__init__.py index 0a5265e9ab..bfb9f9b5d5 100644 --- a/lib/iris/analysis/__init__.py +++ b/lib/iris/analysis/__init__.py @@ -50,7 +50,11 @@ from six.moves import (filter, input, map, range, zip) # noqa import six -import collections +from collections import OrderedDict +try: # Python 3 + from collections.abc import Iterable +except: # Python 2.7 + from collections import Iterable from functools import wraps import dask.array as da @@ -715,7 +719,7 @@ def post_process(self, collapsed_cube, data_result, coords, **kwargs): names = [coord.name() for coord in coords] coord_name = '{}_over_{}'.format(self.name(), '_'.join(names)) - if not isinstance(points, collections.Iterable): + if not isinstance(points, Iterable): points = [points] # Decorate a collapsed cube with a scalar additive coordinate @@ -764,7 +768,7 @@ def aggregate_shape(self, **kwargs): points = kwargs[self._args[0]] shape = () - if not isinstance(points, collections.Iterable): + if not isinstance(points, Iterable): points = [points] points = np.array(points) @@ -1089,7 +1093,7 @@ def _percentile(data, axis, percent, fast_percentile_method=False, # Ensure to unflatten any leading dimensions. if shape: - if not isinstance(percent, collections.Iterable): + if not isinstance(percent, Iterable): percent = [percent] percent = np.array(percent) # Account for the additive dimension. @@ -1207,7 +1211,7 @@ def _weighted_percentile(data, axis, weights, percent, returned=False, # Ensure to unflatten any leading dimensions. if shape: - if not isinstance(percent, collections.Iterable): + if not isinstance(percent, Iterable): percent = [percent] percent = np.array(percent) # Account for the additive dimension. @@ -1877,10 +1881,10 @@ def __init__(self, groupby_coords, shared_coords=None): self.coords = [] self._groupby_coords = [] self._shared_coords = [] - self._slices_by_key = collections.OrderedDict() + self._slices_by_key = OrderedDict() self._stop = None # Ensure group-by coordinates are iterable. - if not isinstance(groupby_coords, collections.Iterable): + if not isinstance(groupby_coords, Iterable): raise TypeError('groupby_coords must be a ' '`collections.Iterable` type.') @@ -1891,7 +1895,7 @@ def __init__(self, groupby_coords, shared_coords=None): # coordinates. if shared_coords is not None: # Ensure shared coordinates are iterable. - if not isinstance(shared_coords, collections.Iterable): + if not isinstance(shared_coords, Iterable): raise TypeError('shared_coords must be a ' '`collections.Iterable` type.') # Add valid shared coordinates. diff --git a/lib/iris/coords.py b/lib/iris/coords.py index 6487b3f7e6..05c9920654 100644 --- a/lib/iris/coords.py +++ b/lib/iris/coords.py @@ -24,7 +24,11 @@ import six from abc import ABCMeta, abstractproperty -import collections +from collections import namedtuple +try: # Python 3 + from collections.abc import Iterator +except: # Python 2.7 + from collections import Iterator import copy from itertools import chain from six.moves import zip_longest @@ -48,10 +52,10 @@ from iris.util import points_step -class CoordDefn(collections.namedtuple('CoordDefn', - ['standard_name', 'long_name', - 'var_name', 'units', - 'attributes', 'coord_system'])): +class CoordDefn(namedtuple('CoordDefn', + ['standard_name', 'long_name', + 'var_name', 'units', + 'attributes', 'coord_system'])): """ Criterion for identifying a specific type of :class:`DimCoord` or :class:`AuxCoord` based on its metadata. @@ -86,11 +90,11 @@ def _sort_key(defn): return _sort_key(self) < _sort_key(other) -class CoordExtent(collections.namedtuple('_CoordExtent', ['name_or_coord', - 'minimum', - 'maximum', - 'min_inclusive', - 'max_inclusive'])): +class CoordExtent(namedtuple('_CoordExtent', ['name_or_coord', + 'minimum', + 'maximum', + 'min_inclusive', + 'max_inclusive'])): """Defines a range of values for a coordinate.""" def __new__(cls, name_or_coord, minimum, maximum, @@ -139,8 +143,8 @@ def __new__(cls, name_or_coord, minimum, maximum, # Private named tuple class for coordinate groups. -_GroupbyItem = collections.namedtuple('GroupbyItem', - 'groupby_point, groupby_slice') +_GroupbyItem = namedtuple('GroupbyItem', + 'groupby_point, groupby_slice') def _get_2d_coord_bound_grid(bounds): @@ -184,7 +188,7 @@ def _get_2d_coord_bound_grid(bounds): return result -class Cell(collections.namedtuple('Cell', ['point', 'bound'])): +class Cell(namedtuple('Cell', ['point', 'bound'])): """ An immutable representation of a single cell of a coordinate, including the sample point and/or boundary position. @@ -2315,7 +2319,7 @@ def xml_element(self, doc): # See Coord.cells() for the description/context. -class _CellIterator(collections.Iterator): +class _CellIterator(Iterator): def __init__(self, coord): self._coord = coord if coord.ndim != 1: @@ -2331,7 +2335,7 @@ def __next__(self): # See ExplicitCoord._group() for the description/context. -class _GroupIterator(collections.Iterator): +class _GroupIterator(Iterator): def __init__(self, points): self._points = points self._start = 0 diff --git a/lib/iris/cube.py b/lib/iris/cube.py index 81152a1293..3f21c1a8cf 100644 --- a/lib/iris/cube.py +++ b/lib/iris/cube.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2018, Met Office +# (C) British Crown Copyright 2010 - 2019, Met Office # # This file is part of Iris. # @@ -24,7 +24,19 @@ from six.moves import (filter, input, map, range, zip) # noqa import six -import collections +from collections import namedtuple, OrderedDict +try: # Python 3 + from collections.abc import (Iterable, + Container, + Mapping, + MutableMapping, + Iterator) +except: # Python 2.7 + from collections import (Iterable, + Container, + Mapping, + MutableMapping, + Iterator) import copy from copy import deepcopy import datetime @@ -58,13 +70,13 @@ __all__ = ['Cube', 'CubeList', 'CubeMetadata'] -class CubeMetadata(collections.namedtuple('CubeMetadata', - ['standard_name', - 'long_name', - 'var_name', - 'units', - 'attributes', - 'cell_methods'])): +class CubeMetadata(namedtuple('CubeMetadata', + ['standard_name', + 'long_name', + 'var_name', + 'units', + 'attributes', + 'cell_methods'])): """ Represents the phenomenon metadata for a single :class:`Cube`. @@ -495,7 +507,7 @@ def concatenate_cube(self, check_aux_coords=True): raise ValueError("can't concatenate an empty CubeList") names = [cube.metadata.name() for cube in self] - unique_names = list(collections.OrderedDict.fromkeys(names)) + unique_names = list(OrderedDict.fromkeys(names)) if len(unique_names) == 1: res = iris._concatenate.concatenate( self, error_on_mismatch=True, @@ -629,7 +641,7 @@ def _is_single_item(testee): """ return (isinstance(testee, six.string_types) or - not isinstance(testee, collections.Iterable)) + not isinstance(testee, Iterable)) class Cube(CFVariableMixin): @@ -952,7 +964,7 @@ def _check_multi_dim_metadata(self, metadata, data_dims): # Convert to a tuple of integers if data_dims is None: data_dims = tuple() - elif isinstance(data_dims, collections.Container): + elif isinstance(data_dims, Container): data_dims = tuple(int(d) for d in data_dims) else: data_dims = (int(data_dims),) @@ -1064,7 +1076,7 @@ def _add_unique_dim_coord(self, dim_coord, data_dim): raise ValueError('The dim_coord may not be an AuxCoord instance.') # Convert data_dim to a single integer - if isinstance(data_dim, collections.Container): + if isinstance(data_dim, Container): if len(data_dim) != 1: raise ValueError('The supplied data dimension must be a' ' single number.') @@ -1366,7 +1378,7 @@ def coords(self, name_or_coord=None, standard_name=None, if guess_axis(coord_) == axis] if attributes is not None: - if not isinstance(attributes, collections.Mapping): + if not isinstance(attributes, Mapping): msg = 'The attributes keyword was expecting a dictionary ' \ 'type, but got a %s instead.' % type(attributes) raise ValueError(msg) @@ -1396,7 +1408,7 @@ def attr_filter(coord_): self.coord_dims(coord_)] if dimensions is not None: - if not isinstance(dimensions, collections.Container): + if not isinstance(dimensions, Container): dimensions = [dimensions] dimensions = tuple(dimensions) coords_and_factories = [coord_ for coord_ in coords_and_factories @@ -3185,7 +3197,7 @@ def collapsed(self, coords, aggregator, **kwargs): for coord in coords] # Remove duplicate dimensions. - new_dims = collections.OrderedDict.fromkeys( + new_dims = OrderedDict.fromkeys( d for dim in dims_to_collapse for d in dim) # Reverse the dimensions so the order can be maintained when # reshaping the data. @@ -3753,7 +3765,7 @@ def regrid(self, grid, scheme): return regridder(self) -class ClassDict(collections.MutableMapping, object): +class ClassDict(MutableMapping, object): """ A mapping that stores objects keyed on their superclasses and their names. @@ -3839,7 +3851,7 @@ def sorted_axes(axes): # See Cube.slice() for the definition/context. -class _SliceIterator(collections.Iterator): +class _SliceIterator(Iterator): def __init__(self, cube, dims_index, requested_dims, ordered): self._cube = cube diff --git a/lib/iris/fileformats/cf.py b/lib/iris/fileformats/cf.py index f2fc895d1c..4c5166542a 100644 --- a/lib/iris/fileformats/cf.py +++ b/lib/iris/fileformats/cf.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2018, Met Office +# (C) British Crown Copyright 2010 - 2019, Met Office # # This file is part of Iris. # @@ -30,7 +30,11 @@ import six from abc import ABCMeta, abstractmethod -from collections import Iterable, MutableMapping + +try: # Python 3 + from collections.abc import Iterable, MutableMapping +except: # Python 2.7 + from collections import Iterable, MutableMapping import os import re import warnings diff --git a/lib/iris/io/format_picker.py b/lib/iris/io/format_picker.py index 1eafcefe1b..e2c46b15bb 100644 --- a/lib/iris/io/format_picker.py +++ b/lib/iris/io/format_picker.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2015, Met Office +# (C) British Crown Copyright 2010 - 2019, Met Office # # This file is part of Iris. # @@ -25,7 +25,7 @@ import matplotlib.pyplot as plt fagent = fp.FormatAgent() png_spec = fp.FormatSpecification('PNG image', fp.MagicNumber(8), - 0x89504E470D0A1A0A, + 0x89504E470D0A1A0A, handler=lambda filename: plt.imread(filename), priority=5 ) @@ -55,7 +55,10 @@ from six.moves import (filter, input, map, range, zip) # noqa import six -import collections +try: # Python 3 + from collections.abc import Callable +except: # Python 2.7 + from collections import Callable import functools import os import struct @@ -126,12 +129,12 @@ def get_spec(self, basename, buffer_obj): if buffer_obj is not None and buffer_obj.tell() != 0: # reset the buffer if tell != 0 buffer_obj.seek(0) - + element_cache[repr(fmt_elem)] = \ fmt_elem.get_element(basename, buffer_obj) # If we have a callable object, then call it and tests its result, otherwise test using basic equality - if isinstance(fmt_elem_value, collections.Callable): + if isinstance(fmt_elem_value, Callable): matches = fmt_elem_value(element_cache[repr(fmt_elem)]) elif element_cache[repr(fmt_elem)] == fmt_elem_value: matches = True @@ -255,14 +258,14 @@ def __init__(self, requires_fh=True): """ self.requires_fh = requires_fh - + def get_element(self, basename, file_handle): """Called when identifying the element of a file that this FileElement is representing.""" raise NotImplementedError("get_element must be defined in a subclass") - + def __hash__(self): return hash(repr(self)) - + def __repr__(self): return '{}()'.format(self.__class__.__name__) diff --git a/lib/iris/iterate.py b/lib/iris/iterate.py index 9828d8670d..9a01c54991 100644 --- a/lib/iris/iterate.py +++ b/lib/iris/iterate.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2015, Met Office +# (C) British Crown Copyright 2010 - 2019, Met Office # # This file is part of Iris. # @@ -22,7 +22,10 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -import collections +try: # Python 3 + from collections.abc import Iterator +except: # Python 2.7 + from collections import Iterator import itertools import warnings @@ -167,7 +170,7 @@ def izip(*cubes, **kwargs): coords_by_cube) -class _ZipSlicesIterator(collections.Iterator): +class _ZipSlicesIterator(Iterator): """ Extension to _SlicesIterator (see cube.py) to support iteration over a collection of cubes in step. diff --git a/lib/iris/tests/test_merge.py b/lib/iris/tests/test_merge.py index 1ca86e6929..1e8cb08646 100644 --- a/lib/iris/tests/test_merge.py +++ b/lib/iris/tests/test_merge.py @@ -26,7 +26,10 @@ # import iris tests first so that some things can be initialised before importing anything else import iris.tests as tests -from collections import Iterable +try: # Python 3 + from collections.abc import Iterable +except: # Python 2.7 + from collections import Iterable import datetime import itertools import numpy as np diff --git a/lib/iris/util.py b/lib/iris/util.py index b0f00c3b52..2502c19f8d 100644 --- a/lib/iris/util.py +++ b/lib/iris/util.py @@ -23,8 +23,11 @@ from six.moves import (filter, input, map, range, zip) # noqa import six +try: # Python 3 + from collections.abc import Hashable +except: # Python 2.7 + from collections import Hashable import abc -import collections from contextlib import contextmanager import copy import functools @@ -835,7 +838,7 @@ def __new__(cls, name, bases, namespace): @functools.total_ordering class _OrderedHashable(six.with_metaclass(_MetaOrderedHashable, - collections.Hashable)): + Hashable)): """ Convenience class for creating "immutable", hashable, and ordered classes.