Skip to content

Commit a9cbf80

Browse files
committed
Merge remote-tracking branch 'upstream/master' into update_release_guide
* upstream/master: combine keep_attrs and combine_attrs in apply_ufunc (pydata#5041) Explained what a deprecation cycle is (pydata#5289) Code cleanup (pydata#5234) FacetGrid docstrings (pydata#5293) Add whats new for dataset interpolation with non-numerics (pydata#5297) Allow dataset interpolation with different datatypes (pydata#5008) Flexible indexes: add Index base class and xindexes properties (pydata#5102) pre-commit: autoupdate hook versions (pydata#5280) convert the examples for apply_ufunc to doctest (pydata#5279) fix the new whatsnew section Ensure `HighLevelGraph` layers are `Layer` instances (pydata#5271)
2 parents c8c8d99 + 751f76a commit a9cbf80

Some content is hidden

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

66 files changed

+1543
-945
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ repos:
1313
- id: isort
1414
# https://github.com/python/black#version-control-integration
1515
- repo: https://github.com/psf/black
16-
rev: 21.4b2
16+
rev: 21.5b0
1717
hooks:
1818
- id: black
1919
- repo: https://github.com/keewis/blackdoc
2020
rev: v0.3.3
2121
hooks:
2222
- id: blackdoc
2323
- repo: https://gitlab.com/pycqa/flake8
24-
rev: 3.9.1
24+
rev: 3.9.2
2525
hooks:
2626
- id: flake8
2727
# - repo: https://github.com/Carreau/velin

doc/api.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,7 @@ Faceting
846846
plot.FacetGrid
847847
plot.FacetGrid.add_colorbar
848848
plot.FacetGrid.add_legend
849+
plot.FacetGrid.add_quiverkey
849850
plot.FacetGrid.map
850851
plot.FacetGrid.map_dataarray
851852
plot.FacetGrid.map_dataarray_line

doc/contributing.rst

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -379,10 +379,34 @@ with ``git commit --no-verify``.
379379
Backwards Compatibility
380380
~~~~~~~~~~~~~~~~~~~~~~~
381381

382-
Please try to maintain backward compatibility. *xarray* has growing number of users with
382+
Please try to maintain backwards compatibility. *xarray* has a growing number of users with
383383
lots of existing code, so don't break it if at all possible. If you think breakage is
384-
required, clearly state why as part of the pull request. Also, be careful when changing
385-
method signatures and add deprecation warnings where needed.
384+
required, clearly state why as part of the pull request.
385+
386+
Be especially careful when changing function and method signatures, because any change
387+
may require a deprecation warning. For example, if your pull request means that the
388+
argument ``old_arg`` to ``func`` is no longer valid, instead of simply raising an error if
389+
a user passes ``old_arg``, we would instead catch it:
390+
391+
.. code-block:: python
392+
393+
def func(new_arg, old_arg=None):
394+
if old_arg is not None:
395+
from warnings import warn
396+
397+
warn(
398+
"`old_arg` has been deprecated, and in the future will raise an error."
399+
"Please use `new_arg` from now on.",
400+
DeprecationWarning,
401+
)
402+
403+
# Still do what the user intended here
404+
405+
This temporary check would then be removed in a subsequent version of xarray.
406+
This process of first warning users before actually breaking their code is known as a
407+
"deprecation cycle", and makes changes significantly easier to handle both for users
408+
of xarray, and for developers of other libraries that depend on xarray.
409+
386410

387411
.. _contributing.ci:
388412

doc/whats-new.rst

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,19 @@ What's New
1414
1515
np.random.seed(123456)
1616
17-
.. _whats-new.{0.18.1}:
17+
.. _whats-new.0.18.1:
1818

19-
v{0.18.1} (unreleased)
20-
---------------------
19+
v0.18.1 (unreleased)
20+
--------------------
2121

2222
New Features
2323
~~~~~~~~~~~~
24-
24+
- allow passing ``combine_attrs`` strategy names to the ``keep_attrs`` parameter of
25+
:py:func:`apply_ufunc` (:pull:`5041`)
26+
By `Justus Magin <https://github.com/keewis>`_.
27+
- :py:meth:`Dataset.interp` now allows interpolation with non-numerical datatypes,
28+
such as booleans, instead of dropping them. (:issue:`4761` :pull:`5008`).
29+
By `Jimmy Westling <https://github.com/illviljan>`_.
2530

2631
Breaking changes
2732
~~~~~~~~~~~~~~~~
@@ -40,12 +45,18 @@ Documentation
4045

4146
- Updated the release guide for developers. Now accounts for actions that are automated via github
4247
actions. Pull (:pull:`5274`).
48+
- Explanation of deprecation cycles and how to implement them added to contributors
49+
guide. (:pull:`5289`)
4350
By `Tom Nicholas <https://github.com/TomNicholas>`_.
4451

4552

4653
Internal Changes
4754
~~~~~~~~~~~~~~~~
4855

56+
- Explicit indexes refactor: add an ``xarray.Index`` base class and
57+
``Dataset.xindexes`` / ``DataArray.xindexes`` properties. Also rename
58+
``PandasIndexAdapter`` to ``PandasIndex``, which now inherits from
59+
``xarray.Index`` (:pull:`5102`). By `Benoit Bovy <https://github.com/benbovy>`_.
4960

5061
.. _whats-new.0.18.0:
5162

@@ -272,7 +283,6 @@ Internal Changes
272283
(:pull:`5188`), (:pull:`5191`).
273284
By `Maximilian Roos <https://github.com/max-sixty>`_.
274285

275-
276286
.. _whats-new.0.17.0:
277287

278288
v0.17.0 (24 Feb 2021)

xarray/backends/api.py

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,11 @@ def _get_default_engine_netcdf():
102102

103103
def _get_default_engine(path: str, allow_remote: bool = False):
104104
if allow_remote and is_remote_uri(path):
105-
engine = _get_default_engine_remote_uri()
105+
return _get_default_engine_remote_uri()
106106
elif path.endswith(".gz"):
107-
engine = _get_default_engine_gz()
107+
return _get_default_engine_gz()
108108
else:
109-
engine = _get_default_engine_netcdf()
110-
return engine
109+
return _get_default_engine_netcdf()
111110

112111

113112
def _validate_dataset_names(dataset):
@@ -282,7 +281,7 @@ def _chunk_ds(
282281

283282
mtime = _get_mtime(filename_or_obj)
284283
token = tokenize(filename_or_obj, mtime, engine, chunks, **extra_tokens)
285-
name_prefix = "open_dataset-%s" % token
284+
name_prefix = f"open_dataset-{token}"
286285

287286
variables = {}
288287
for name, var in backend_ds.variables.items():
@@ -295,8 +294,7 @@ def _chunk_ds(
295294
name_prefix=name_prefix,
296295
token=token,
297296
)
298-
ds = backend_ds._replace(variables)
299-
return ds
297+
return backend_ds._replace(variables)
300298

301299

302300
def _dataset_from_backend_dataset(
@@ -308,12 +306,10 @@ def _dataset_from_backend_dataset(
308306
overwrite_encoded_chunks,
309307
**extra_tokens,
310308
):
311-
if not (isinstance(chunks, (int, dict)) or chunks is None):
312-
if chunks != "auto":
313-
raise ValueError(
314-
"chunks must be an int, dict, 'auto', or None. "
315-
"Instead found %s. " % chunks
316-
)
309+
if not isinstance(chunks, (int, dict)) and chunks not in {None, "auto"}:
310+
raise ValueError(
311+
f"chunks must be an int, dict, 'auto', or None. Instead found {chunks}."
312+
)
317313

318314
_protect_dataset_variables_inplace(backend_ds, cache)
319315
if chunks is None:
@@ -331,9 +327,8 @@ def _dataset_from_backend_dataset(
331327
ds.set_close(backend_ds._close)
332328

333329
# Ensure source filename always stored in dataset object (GH issue #2550)
334-
if "source" not in ds.encoding:
335-
if isinstance(filename_or_obj, str):
336-
ds.encoding["source"] = filename_or_obj
330+
if "source" not in ds.encoding and isinstance(filename_or_obj, str):
331+
ds.encoding["source"] = filename_or_obj
337332

338333
return ds
339334

@@ -515,7 +510,6 @@ def open_dataset(
515510
**decoders,
516511
**kwargs,
517512
)
518-
519513
return ds
520514

521515

@@ -1015,8 +1009,8 @@ def to_netcdf(
10151009
elif engine != "scipy":
10161010
raise ValueError(
10171011
"invalid engine for creating bytes with "
1018-
"to_netcdf: %r. Only the default engine "
1019-
"or engine='scipy' is supported" % engine
1012+
f"to_netcdf: {engine!r}. Only the default engine "
1013+
"or engine='scipy' is supported"
10201014
)
10211015
if not compute:
10221016
raise NotImplementedError(
@@ -1037,7 +1031,7 @@ def to_netcdf(
10371031
try:
10381032
store_open = WRITEABLE_STORES[engine]
10391033
except KeyError:
1040-
raise ValueError("unrecognized engine for to_netcdf: %r" % engine)
1034+
raise ValueError(f"unrecognized engine for to_netcdf: {engine!r}")
10411035

10421036
if format is not None:
10431037
format = format.upper()
@@ -1049,9 +1043,8 @@ def to_netcdf(
10491043
autoclose = have_chunks and scheduler in ["distributed", "multiprocessing"]
10501044
if autoclose and engine == "scipy":
10511045
raise NotImplementedError(
1052-
"Writing netCDF files with the %s backend "
1053-
"is not currently supported with dask's %s "
1054-
"scheduler" % (engine, scheduler)
1046+
f"Writing netCDF files with the {engine} backend "
1047+
f"is not currently supported with dask's {scheduler} scheduler"
10551048
)
10561049

10571050
target = path_or_file if path_or_file is not None else BytesIO()
@@ -1061,7 +1054,7 @@ def to_netcdf(
10611054
kwargs["invalid_netcdf"] = invalid_netcdf
10621055
else:
10631056
raise ValueError(
1064-
"unrecognized option 'invalid_netcdf' for engine %s" % engine
1057+
f"unrecognized option 'invalid_netcdf' for engine {engine}"
10651058
)
10661059
store = store_open(target, mode, format, group, **kwargs)
10671060

@@ -1203,7 +1196,7 @@ def save_mfdataset(
12031196
Data variables:
12041197
a (time) float64 0.0 0.02128 0.04255 0.06383 ... 0.9574 0.9787 1.0
12051198
>>> years, datasets = zip(*ds.groupby("time.year"))
1206-
>>> paths = ["%s.nc" % y for y in years]
1199+
>>> paths = [f"{y}.nc" for y in years]
12071200
>>> xr.save_mfdataset(datasets, paths)
12081201
"""
12091202
if mode == "w" and len(set(paths)) < len(paths):
@@ -1215,7 +1208,7 @@ def save_mfdataset(
12151208
if not isinstance(obj, Dataset):
12161209
raise TypeError(
12171210
"save_mfdataset only supports writing Dataset "
1218-
"objects, received type %s" % type(obj)
1211+
f"objects, received type {type(obj)}"
12191212
)
12201213

12211214
if groups is None:

xarray/backends/cfgrib_.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,7 @@ def get_dimensions(self):
9090

9191
def get_encoding(self):
9292
dims = self.get_dimensions()
93-
encoding = {"unlimited_dims": {k for k, v in dims.items() if v is None}}
94-
return encoding
93+
return {"unlimited_dims": {k for k, v in dims.items() if v is None}}
9594

9695

9796
class CfgribfBackendEntrypoint(BackendEntrypoint):

xarray/backends/common.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,8 @@ def robust_getitem(array, key, catch=Exception, max_retries=6, initial_delay=500
6969
base_delay = initial_delay * 2 ** n
7070
next_delay = base_delay + np.random.randint(base_delay)
7171
msg = (
72-
"getitem failed, waiting %s ms before trying again "
73-
"(%s tries remaining). Full traceback: %s"
74-
% (next_delay, max_retries - n, traceback.format_exc())
72+
f"getitem failed, waiting {next_delay} ms before trying again "
73+
f"({max_retries - n} tries remaining). Full traceback: {traceback.format_exc()}"
7574
)
7675
logger.debug(msg)
7776
time.sleep(1e-3 * next_delay)
@@ -336,7 +335,7 @@ def set_dimensions(self, variables, unlimited_dims=None):
336335
if dim in existing_dims and length != existing_dims[dim]:
337336
raise ValueError(
338337
"Unable to update size for existing dimension"
339-
"%r (%d != %d)" % (dim, length, existing_dims[dim])
338+
f"{dim!r} ({length} != {existing_dims[dim]})"
340339
)
341340
elif dim not in existing_dims:
342341
is_unlimited = dim in unlimited_dims

xarray/backends/h5netcdf_.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@
3737
class H5NetCDFArrayWrapper(BaseNetCDF4Array):
3838
def get_array(self, needs_lock=True):
3939
ds = self.datastore._acquire(needs_lock)
40-
variable = ds.variables[self.variable_name]
41-
return variable
40+
return ds.variables[self.variable_name]
4241

4342
def __getitem__(self, key):
4443
return indexing.explicit_indexing_adapter(
@@ -102,7 +101,7 @@ def __init__(self, manager, group=None, mode=None, lock=HDF5_LOCK, autoclose=Fal
102101
if group is None:
103102
root, group = find_root_and_group(manager)
104103
else:
105-
if not type(manager) is h5netcdf.File:
104+
if type(manager) is not h5netcdf.File:
106105
raise ValueError(
107106
"must supply a h5netcdf.File if the group "
108107
"argument is provided"
@@ -233,11 +232,9 @@ def get_dimensions(self):
233232
return self.ds.dimensions
234233

235234
def get_encoding(self):
236-
encoding = {}
237-
encoding["unlimited_dims"] = {
238-
k for k, v in self.ds.dimensions.items() if v is None
235+
return {
236+
"unlimited_dims": {k for k, v in self.ds.dimensions.items() if v is None}
239237
}
240-
return encoding
241238

242239
def set_dimension(self, name, length, is_unlimited=False):
243240
if is_unlimited:
@@ -266,9 +263,9 @@ def prepare_variable(
266263
"h5netcdf does not yet support setting a fill value for "
267264
"variable-length strings "
268265
"(https://github.com/shoyer/h5netcdf/issues/37). "
269-
"Either remove '_FillValue' from encoding on variable %r "
266+
f"Either remove '_FillValue' from encoding on variable {name!r} "
270267
"or set {'dtype': 'S1'} in encoding to use the fixed width "
271-
"NC_CHAR type." % name
268+
"NC_CHAR type."
272269
)
273270

274271
if dtype is str:

xarray/backends/locks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def locked(self):
167167
return any(lock.locked for lock in self.locks)
168168

169169
def __repr__(self):
170-
return "CombinedLock(%r)" % list(self.locks)
170+
return f"CombinedLock({list(self.locks)!r})"
171171

172172

173173
class DummyLock:

0 commit comments

Comments
 (0)