Skip to content

Commit 863e490

Browse files
author
Joe Hamman
authored
OrderedDict --> dict, some python3.5 cleanup too (#3389)
* OrderedDict --> dict, some python3.5 cleanup too * respond to part of @shoyer's review * fix set attr syntax on netcdf4 vars * fix typing errors * update whats new and todo comments * Typing annotations * Typing annotations * Fix regression * More substantial changes * More polish * Typing annotations * Rerun notebooks
1 parent 6851e3e commit 863e490

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+485
-638
lines changed

doc/data-structures.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ multi-dimensional array. It has several key properties:
2323
- ``coords``: a dict-like container of arrays (*coordinates*) that label each
2424
point (e.g., 1-dimensional arrays of numbers, datetime objects or
2525
strings)
26-
- ``attrs``: an ``OrderedDict`` to hold arbitrary metadata (*attributes*)
26+
- ``attrs``: :py:class:`dict` to hold arbitrary metadata (*attributes*)
2727

2828
xarray uses ``dims`` and ``coords`` to enable its core metadata aware operations.
2929
Dimensions provide names that xarray uses instead of the ``axis`` argument found
@@ -32,10 +32,10 @@ alignment, building on the functionality of the ``index`` found on a pandas
3232
:py:class:`~pandas.DataFrame` or :py:class:`~pandas.Series`.
3333

3434
DataArray objects also can have a ``name`` and can hold arbitrary metadata in
35-
the form of their ``attrs`` property (an ordered dictionary). Names and
36-
attributes are strictly for users and user-written code: xarray makes no attempt
37-
to interpret them, and propagates them only in unambiguous cases (see FAQ,
38-
:ref:`approach to metadata`).
35+
the form of their ``attrs`` property. Names and attributes are strictly for
36+
users and user-written code: xarray makes no attempt to interpret them, and
37+
propagates them only in unambiguous cases
38+
(see FAQ, :ref:`approach to metadata`).
3939

4040
.. _creating a dataarray:
4141

@@ -222,7 +222,7 @@ to access any variable in a dataset, datasets have four key properties:
222222
- ``data_vars``: a dict-like container of DataArrays corresponding to variables
223223
- ``coords``: another dict-like container of DataArrays intended to label points
224224
used in ``data_vars`` (e.g., arrays of numbers, datetime objects or strings)
225-
- ``attrs``: an ``OrderedDict`` to hold arbitrary metadata
225+
- ``attrs``: :py:class:`dict` to hold arbitrary metadata
226226

227227
The distinction between whether a variables falls in data or coordinates
228228
(borrowed from `CF conventions`_) is mostly semantic, and you can probably get

doc/whats-new.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ Breaking changes
4646
It was unused and doesn't make sense for a Variable.
4747
(:pull:`3375`) by `Guido Imperiale <https://github.com/crusaderky>`_.
4848

49+
- Remove internal usage of `collections.OrderedDict`. After dropping support for
50+
Python <=3.5, most uses of `OrderedDict` in Xarray were no longer necessary. We
51+
have removed the internal use of the `OrderedDict` in favor of Python's builtin
52+
`dict` object which is now ordered itself. This change will be most obvious when
53+
interacting with the `attrs` property on the Dataset and DataArray objects.
54+
55+
(:issue:`3380`, :issue:`3389`). By `Joe Hamman <https://github.com/jhamman>`_.
56+
4957
New functions/methods
5058
~~~~~~~~~~~~~~~~~~~~~
5159

examples/xarray_multidimensional_coords.ipynb

Lines changed: 27 additions & 27 deletions
Large diffs are not rendered by default.

examples/xarray_seasonal_means.ipynb

Lines changed: 7 additions & 7 deletions
Large diffs are not rendered by default.

xarray/backends/cfgrib_.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from .. import Variable
44
from ..core import indexing
5-
from ..core.utils import Frozen, FrozenOrderedDict
5+
from ..core.utils import Frozen, FrozenDict
66
from .common import AbstractDataStore, BackendArray
77
from .locks import SerializableLock, ensure_lock
88

@@ -55,7 +55,7 @@ def open_store_variable(self, name, var):
5555
return Variable(var.dimensions, data, var.attributes, encoding)
5656

5757
def get_variables(self):
58-
return FrozenOrderedDict(
58+
return FrozenDict(
5959
(k, self.open_store_variable(k, v)) for k, v in self.ds.variables.items()
6060
)
6161

xarray/backends/common.py

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
import time
33
import traceback
44
import warnings
5-
from collections import OrderedDict
65
from collections.abc import Mapping
76

87
import numpy as np
98

109
from ..conventions import cf_encoder
1110
from ..core import indexing
1211
from ..core.pycompat import dask_array_type
13-
from ..core.utils import FrozenOrderedDict, NdimSizeLenMixin
12+
from ..core.utils import FrozenDict, NdimSizeLenMixin
1413

1514
# Create a logger object, but don't add any handlers. Leave that to user code.
1615
logger = logging.getLogger(__name__)
@@ -120,10 +119,10 @@ def load(self):
120119
This function will be called anytime variables or attributes
121120
are requested, so care should be taken to make sure its fast.
122121
"""
123-
variables = FrozenOrderedDict(
122+
variables = FrozenDict(
124123
(_decode_variable_name(k), v) for k, v in self.get_variables().items()
125124
)
126-
attributes = FrozenOrderedDict(self.get_attrs())
125+
attributes = FrozenDict(self.get_attrs())
127126
return variables, attributes
128127

129128
@property
@@ -230,12 +229,8 @@ def encode(self, variables, attributes):
230229
attributes : dict-like
231230
232231
"""
233-
variables = OrderedDict(
234-
[(k, self.encode_variable(v)) for k, v in variables.items()]
235-
)
236-
attributes = OrderedDict(
237-
[(k, self.encode_attribute(v)) for k, v in attributes.items()]
238-
)
232+
variables = {k: self.encode_variable(v) for k, v in variables.items()}
233+
attributes = {k: self.encode_attribute(v) for k, v in attributes.items()}
239234
return variables, attributes
240235

241236
def encode_variable(self, v):
@@ -361,7 +356,7 @@ def set_dimensions(self, variables, unlimited_dims=None):
361356

362357
existing_dims = self.get_dimensions()
363358

364-
dims = OrderedDict()
359+
dims = {}
365360
for v in unlimited_dims: # put unlimited_dims first
366361
dims[v] = None
367362
for v in variables.values():
@@ -385,10 +380,6 @@ def encode(self, variables, attributes):
385380
# All NetCDF files get CF encoded by default, without this attempting
386381
# to write times, for example, would fail.
387382
variables, attributes = cf_encoder(variables, attributes)
388-
variables = OrderedDict(
389-
[(k, self.encode_variable(v)) for k, v in variables.items()]
390-
)
391-
attributes = OrderedDict(
392-
[(k, self.encode_attribute(v)) for k, v in attributes.items()]
393-
)
383+
variables = {k: self.encode_variable(v) for k, v in variables.items()}
384+
attributes = {k: self.encode_attribute(v) for k, v in attributes.items()}
394385
return variables, attributes

xarray/backends/h5netcdf_.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import functools
2-
from collections import OrderedDict
32

43
import numpy as np
54

65
from .. import Variable
76
from ..core import indexing
8-
from ..core.utils import FrozenOrderedDict
7+
from ..core.utils import FrozenDict
98
from .common import WritableCFDataStore
109
from .file_manager import CachingFileManager
1110
from .locks import HDF5_LOCK, combine_locks, ensure_lock, get_write_lock
@@ -49,7 +48,7 @@ def _read_attributes(h5netcdf_var):
4948
# GH451
5049
# to ensure conventions decoding works properly on Python 3, decode all
5150
# bytes attributes to strings
52-
attrs = OrderedDict()
51+
attrs = {}
5352
for k, v in h5netcdf_var.attrs.items():
5453
if k not in ["_FillValue", "missing_value"]:
5554
v = maybe_decode_bytes(v)
@@ -153,12 +152,12 @@ def open_store_variable(self, name, var):
153152
return Variable(dimensions, data, attrs, encoding)
154153

155154
def get_variables(self):
156-
return FrozenOrderedDict(
155+
return FrozenDict(
157156
(k, self.open_store_variable(k, v)) for k, v in self.ds.variables.items()
158157
)
159158

160159
def get_attrs(self):
161-
return FrozenOrderedDict(_read_attributes(self.ds))
160+
return FrozenDict(_read_attributes(self.ds))
162161

163162
def get_dimensions(self):
164163
return self.ds.dimensions

xarray/backends/memory.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import copy
2-
from collections import OrderedDict
32

43
import numpy as np
54

@@ -16,8 +15,8 @@ class InMemoryDataStore(AbstractWritableDataStore):
1615
"""
1716

1817
def __init__(self, variables=None, attributes=None):
19-
self._variables = OrderedDict() if variables is None else variables
20-
self._attributes = OrderedDict() if attributes is None else attributes
18+
self._variables = {} if variables is None else variables
19+
self._attributes = {} if attributes is None else attributes
2120

2221
def get_attrs(self):
2322
return self._attributes
@@ -26,7 +25,7 @@ def get_variables(self):
2625
return self._variables
2726

2827
def get_dimensions(self):
29-
dims = OrderedDict()
28+
dims = {}
3029
for v in self._variables.values():
3130
for d, s in v.dims.items():
3231
dims[d] = s

xarray/backends/netCDF4_.py

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import functools
22
import operator
3-
from collections import OrderedDict
43
from contextlib import suppress
54

65
import numpy as np
76

87
from .. import Variable, coding
98
from ..coding.variables import pop_to
109
from ..core import indexing
11-
from ..core.utils import FrozenOrderedDict, is_remote_uri
10+
from ..core.utils import FrozenDict, is_remote_uri
1211
from .common import (
1312
BackendArray,
1413
WritableCFDataStore,
@@ -274,25 +273,6 @@ def _is_list_of_strings(value):
274273
return False
275274

276275

277-
def _set_nc_attribute(obj, key, value):
278-
if _is_list_of_strings(value):
279-
# encode as NC_STRING if attr is list of strings
280-
try:
281-
obj.setncattr_string(key, value)
282-
except AttributeError:
283-
# Inform users with old netCDF that does not support
284-
# NC_STRING that we can't serialize lists of strings
285-
# as attrs
286-
msg = (
287-
"Attributes which are lists of strings are not "
288-
"supported with this version of netCDF. Please "
289-
"upgrade to netCDF4-python 1.2.4 or greater."
290-
)
291-
raise AttributeError(msg)
292-
else:
293-
obj.setncattr(key, value)
294-
295-
296276
class NetCDF4DataStore(WritableCFDataStore):
297277
"""Store for reading and writing data via the Python-NetCDF4 library.
298278
@@ -388,7 +368,7 @@ def ds(self):
388368
def open_store_variable(self, name, var):
389369
dimensions = var.dimensions
390370
data = indexing.LazilyOuterIndexedArray(NetCDF4ArrayWrapper(name, self))
391-
attributes = OrderedDict((k, var.getncattr(k)) for k in var.ncattrs())
371+
attributes = {k: var.getncattr(k) for k in var.ncattrs()}
392372
_ensure_fill_value_valid(data, attributes)
393373
# netCDF4 specific encoding; save _FillValue for later
394374
encoding = {}
@@ -415,17 +395,17 @@ def open_store_variable(self, name, var):
415395
return Variable(dimensions, data, attributes, encoding)
416396

417397
def get_variables(self):
418-
dsvars = FrozenOrderedDict(
398+
dsvars = FrozenDict(
419399
(k, self.open_store_variable(k, v)) for k, v in self.ds.variables.items()
420400
)
421401
return dsvars
422402

423403
def get_attrs(self):
424-
attrs = FrozenOrderedDict((k, self.ds.getncattr(k)) for k in self.ds.ncattrs())
404+
attrs = FrozenDict((k, self.ds.getncattr(k)) for k in self.ds.ncattrs())
425405
return attrs
426406

427407
def get_dimensions(self):
428-
dims = FrozenOrderedDict((k, len(v)) for k, v in self.ds.dimensions.items())
408+
dims = FrozenDict((k, len(v)) for k, v in self.ds.dimensions.items())
429409
return dims
430410

431411
def get_encoding(self):
@@ -442,7 +422,11 @@ def set_dimension(self, name, length, is_unlimited=False):
442422
def set_attribute(self, key, value):
443423
if self.format != "NETCDF4":
444424
value = encode_nc3_attr_value(value)
445-
_set_nc_attribute(self.ds, key, value)
425+
if _is_list_of_strings(value):
426+
# encode as NC_STRING if attr is list of strings
427+
self.ds.setncattr_string(key, value)
428+
else:
429+
self.ds.setncattr(key, value)
446430

447431
def encode_variable(self, variable):
448432
variable = _force_native_endianness(variable)
@@ -494,10 +478,7 @@ def prepare_variable(
494478
fill_value=fill_value,
495479
)
496480

497-
for k, v in attrs.items():
498-
# set attributes one-by-one since netCDF4<1.0.10 can't handle
499-
# OrderedDict as the input to setncatts
500-
_set_nc_attribute(nc4_var, k, v)
481+
nc4_var.setncatts(attrs)
501482

502483
target = NetCDF4ArrayWrapper(name, self)
503484

xarray/backends/netcdf3.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import unicodedata
2-
from collections import OrderedDict
32

43
import numpy as np
54

@@ -70,7 +69,7 @@ def encode_nc3_attr_value(value):
7069

7170

7271
def encode_nc3_attrs(attrs):
73-
return OrderedDict([(k, encode_nc3_attr_value(v)) for k, v in attrs.items()])
72+
return {k: encode_nc3_attr_value(v) for k, v in attrs.items()}
7473

7574

7675
def encode_nc3_variable(var):

xarray/backends/pseudonetcdf_.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
from collections import OrderedDict
2-
31
import numpy as np
42

53
from .. import Variable
64
from ..core import indexing
7-
from ..core.utils import Frozen, FrozenOrderedDict
5+
from ..core.utils import Frozen, FrozenDict
86
from .common import AbstractDataStore, BackendArray
97
from .file_manager import CachingFileManager
108
from .locks import HDF5_LOCK, NETCDFC_LOCK, combine_locks, ensure_lock
@@ -65,11 +63,11 @@ def ds(self):
6563

6664
def open_store_variable(self, name, var):
6765
data = indexing.LazilyOuterIndexedArray(PncArrayWrapper(name, self))
68-
attrs = OrderedDict((k, getattr(var, k)) for k in var.ncattrs())
66+
attrs = {k: getattr(var, k) for k in var.ncattrs()}
6967
return Variable(var.dimensions, data, attrs)
7068

7169
def get_variables(self):
72-
return FrozenOrderedDict(
70+
return FrozenDict(
7371
(k, self.open_store_variable(k, v)) for k, v in self.ds.variables.items()
7472
)
7573

xarray/backends/pydap_.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from .. import Variable
44
from ..core import indexing
55
from ..core.pycompat import integer_types
6-
from ..core.utils import Frozen, FrozenOrderedDict, is_dict_like
6+
from ..core.utils import Frozen, FrozenDict, is_dict_like
77
from .common import AbstractDataStore, BackendArray, robust_getitem
88

99

@@ -83,7 +83,7 @@ def open_store_variable(self, var):
8383
return Variable(var.dimensions, data, _fix_attributes(var.attributes))
8484

8585
def get_variables(self):
86-
return FrozenOrderedDict(
86+
return FrozenDict(
8787
(k, self.open_store_variable(self.ds[k])) for k in self.ds.keys()
8888
)
8989

xarray/backends/pynio_.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from .. import Variable
44
from ..core import indexing
5-
from ..core.utils import Frozen, FrozenOrderedDict
5+
from ..core.utils import Frozen, FrozenDict
66
from .common import AbstractDataStore, BackendArray
77
from .file_manager import CachingFileManager
88
from .locks import HDF5_LOCK, NETCDFC_LOCK, SerializableLock, combine_locks, ensure_lock
@@ -66,7 +66,7 @@ def open_store_variable(self, name, var):
6666
return Variable(var.dimensions, data, var.attributes)
6767

6868
def get_variables(self):
69-
return FrozenOrderedDict(
69+
return FrozenDict(
7070
(k, self.open_store_variable(k, v)) for k, v in self.ds.variables.items()
7171
)
7272

xarray/backends/rasterio_.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import os
22
import warnings
3-
from collections import OrderedDict
43

54
import numpy as np
65

@@ -244,7 +243,7 @@ def open_rasterio(filename, parse_coordinates=None, chunks=None, cache=None, loc
244243
if cache is None:
245244
cache = chunks is None
246245

247-
coords = OrderedDict()
246+
coords = {}
248247

249248
# Get bands
250249
if riods.count < 1:
@@ -276,7 +275,7 @@ def open_rasterio(filename, parse_coordinates=None, chunks=None, cache=None, loc
276275
)
277276

278277
# Attributes
279-
attrs = dict()
278+
attrs = {}
280279
# Affine transformation matrix (always available)
281280
# This describes coefficients mapping pixel coordinates to CRS
282281
# For serialization store as tuple of 6 floats, the last row being

0 commit comments

Comments
 (0)