Skip to content

Commit

Permalink
Merge branch 'main' into open-dtree-kwargs
Browse files Browse the repository at this point in the history
  • Loading branch information
dcherian authored Jul 1, 2024
2 parents 563b5db + 90e4486 commit 8e643c6
Show file tree
Hide file tree
Showing 27 changed files with 249 additions and 78 deletions.
4 changes: 2 additions & 2 deletions ci/requirements/all-but-dask.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ dependencies:
- netcdf4
- numba
- numbagg
- numpy
- numpy<2
- packaging
- pandas
- pint>=0.22
- pip
- pydap
# - pydap
- pytest
- pytest-cov
- pytest-env
Expand Down
2 changes: 1 addition & 1 deletion ci/requirements/doc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ dependencies:
- nbsphinx
- netcdf4>=1.5
- numba
- numpy>=1.21
- numpy>=1.21,<2
- packaging>=21.3
- pandas>=1.4,!=2.1.0
- pooch
Expand Down
4 changes: 2 additions & 2 deletions ci/requirements/environment-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ dependencies:
- netcdf4
- numba
- numbagg
- numpy
- numpy<2
- packaging
- pandas
# - pint>=0.22
- pip
- pre-commit
- pydap
# - pydap
- pytest
- pytest-cov
- pytest-env
Expand Down
4 changes: 2 additions & 2 deletions ci/requirements/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ dependencies:
- numba
- numbagg
- numexpr
- numpy
- numpy<2
- opt_einsum
- packaging
- pandas
Expand All @@ -35,7 +35,7 @@ dependencies:
- pooch
- pre-commit
- pyarrow # pandas raises a deprecation warning without this, breaking doctests
- pydap
# - pydap
- pytest
- pytest-cov
- pytest-env
Expand Down
13 changes: 12 additions & 1 deletion doc/whats-new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Performance

- Allow chunking for arrays with duplicated dimension names (:issue:`8759`, :pull:`9099`).
By `Martin Raspaud <https://github.com/mraspaud>`_.
- Extract the source url from fsspec objects (:issue:`9142`, :pull:`8923`).
By `Justus Magin <https://github.com/keewis>`_.

Breaking changes
~~~~~~~~~~~~~~~~
Expand All @@ -46,7 +48,16 @@ Deprecations

Bug fixes
~~~~~~~~~

- Make :py:func:`testing.assert_allclose` work with numpy 2.0 (:issue:`9165`, :pull:`9166`).
By `Pontus Lurcock <https://github.com/pont-us>`_.
- Allow diffing objects with array attributes on variables (:issue:`9153`, :pull:`9169`).
By `Justus Magin <https://github.com/keewis>`_.
- Promote floating-point numeric datetimes before decoding (:issue:`9179`, :pull:`9182`).
By `Justus Magin <https://github.com/keewis>`_.
- Fiy static typing of tolerance arguments by allowing `str` type (:issue:`8892`, :pull:`9194`).
By `Michael Niklas <https://github.com/headtr1ck>`_.
- Dark themes are now properly detected for ``html[data-theme=dark]``-tags (:pull:`9200`).
By `Dieter Werthmüller <https://github.com/prisae>`_.

Documentation
~~~~~~~~~~~~~
Expand Down
17 changes: 17 additions & 0 deletions properties/test_properties.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import pytest

pytest.importorskip("hypothesis")

from hypothesis import given

import xarray as xr
import xarray.testing.strategies as xrst


@given(attrs=xrst.simple_attrs)
def test_assert_identical(attrs):
v = xr.Variable(dims=(), data=0, attrs=attrs)
xr.testing.assert_identical(v, v.copy(deep=True))

ds = xr.Dataset(attrs=attrs)
xr.testing.assert_identical(ds, ds.copy(deep=True))
7 changes: 5 additions & 2 deletions xarray/backends/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,11 @@ def _dataset_from_backend_dataset(
ds.set_close(backend_ds._close)

# Ensure source filename always stored in dataset object
if "source" not in ds.encoding and isinstance(filename_or_obj, (str, os.PathLike)):
ds.encoding["source"] = _normalize_path(filename_or_obj)
if "source" not in ds.encoding:
path = getattr(filename_or_obj, "path", filename_or_obj)

if isinstance(path, (str, os.PathLike)):
ds.encoding["source"] = _normalize_path(path)

return ds

Expand Down
4 changes: 2 additions & 2 deletions xarray/backends/file_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ class CachingFileManager(FileManager):
FileManager.close(), which ensures that closed files are removed from the
cache as well.
Example usage:
Example usage::
manager = FileManager(open, 'example.txt', mode='w')
f = manager.acquire()
f.write(...)
manager.close() # ensures file is closed
Note that as long as previous files are still cached, acquiring a file
multiple times from the same FileManager is essentially free:
multiple times from the same FileManager is essentially free::
f1 = manager.acquire()
f2 = manager.acquire()
Expand Down
2 changes: 2 additions & 0 deletions xarray/coding/times.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,8 @@ def _decode_datetime_with_pandas(
# timedelta64 value, and therefore would raise an error in the lines above.
if flat_num_dates.dtype.kind in "iu":
flat_num_dates = flat_num_dates.astype(np.int64)
elif flat_num_dates.dtype.kind in "f":
flat_num_dates = flat_num_dates.astype(np.float64)

# Cast input ordinals to integers of nanoseconds because pd.to_timedelta
# works much faster when dealing with integers (GH 1399).
Expand Down
6 changes: 3 additions & 3 deletions xarray/core/alignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def __init__(
exclude_dims: str | Iterable[Hashable] = frozenset(),
exclude_vars: Iterable[Hashable] = frozenset(),
method: str | None = None,
tolerance: int | float | Iterable[int | float] | None = None,
tolerance: float | Iterable[float] | str | None = None,
copy: bool = True,
fill_value: Any = dtypes.NA,
sparse: bool = False,
Expand Down Expand Up @@ -965,7 +965,7 @@ def reindex(
obj: T_Alignable,
indexers: Mapping[Any, Any],
method: str | None = None,
tolerance: int | float | Iterable[int | float] | None = None,
tolerance: float | Iterable[float] | str | None = None,
copy: bool = True,
fill_value: Any = dtypes.NA,
sparse: bool = False,
Expand Down Expand Up @@ -1004,7 +1004,7 @@ def reindex_like(
obj: T_Alignable,
other: Dataset | DataArray,
method: str | None = None,
tolerance: int | float | Iterable[int | float] | None = None,
tolerance: float | Iterable[float] | str | None = None,
copy: bool = True,
fill_value: Any = dtypes.NA,
) -> T_Alignable:
Expand Down
8 changes: 4 additions & 4 deletions xarray/core/dataarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -1909,7 +1909,7 @@ def reindex_like(
other: T_DataArrayOrSet,
*,
method: ReindexMethodOptions = None,
tolerance: int | float | Iterable[int | float] | None = None,
tolerance: float | Iterable[float] | str | None = None,
copy: bool = True,
fill_value=dtypes.NA,
) -> Self:
Expand All @@ -1936,7 +1936,7 @@ def reindex_like(
- backfill / bfill: propagate next valid index value backward
- nearest: use nearest valid index value
tolerance : optional
tolerance : float | Iterable[float] | str | None, default: None
Maximum distance between original and new labels for inexact
matches. The values of the index at the matching locations must
satisfy the equation ``abs(index[indexer] - target) <= tolerance``.
Expand Down Expand Up @@ -2096,7 +2096,7 @@ def reindex(
indexers: Mapping[Any, Any] | None = None,
*,
method: ReindexMethodOptions = None,
tolerance: float | Iterable[float] | None = None,
tolerance: float | Iterable[float] | str | None = None,
copy: bool = True,
fill_value=dtypes.NA,
**indexers_kwargs: Any,
Expand Down Expand Up @@ -2126,7 +2126,7 @@ def reindex(
- backfill / bfill: propagate next valid index value backward
- nearest: use nearest valid index value
tolerance : float | Iterable[float] | None, default: None
tolerance : float | Iterable[float] | str | None, default: None
Maximum distance between original and new labels for inexact
matches. The values of the index at the matching locations must
satisfy the equation ``abs(index[indexer] - target) <= tolerance``.
Expand Down
8 changes: 4 additions & 4 deletions xarray/core/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -3499,7 +3499,7 @@ def reindex_like(
self,
other: T_Xarray,
method: ReindexMethodOptions = None,
tolerance: int | float | Iterable[int | float] | None = None,
tolerance: float | Iterable[float] | str | None = None,
copy: bool = True,
fill_value: Any = xrdtypes.NA,
) -> Self:
Expand All @@ -3526,7 +3526,7 @@ def reindex_like(
- "backfill" / "bfill": propagate next valid index value backward
- "nearest": use nearest valid index value
tolerance : optional
tolerance : float | Iterable[float] | str | None, default: None
Maximum distance between original and new labels for inexact
matches. The values of the index at the matching locations must
satisfy the equation ``abs(index[indexer] - target) <= tolerance``.
Expand Down Expand Up @@ -3569,7 +3569,7 @@ def reindex(
self,
indexers: Mapping[Any, Any] | None = None,
method: ReindexMethodOptions = None,
tolerance: int | float | Iterable[int | float] | None = None,
tolerance: float | Iterable[float] | str | None = None,
copy: bool = True,
fill_value: Any = xrdtypes.NA,
**indexers_kwargs: Any,
Expand All @@ -3594,7 +3594,7 @@ def reindex(
- "backfill" / "bfill": propagate next valid index value backward
- "nearest": use nearest valid index value
tolerance : optional
tolerance : float | Iterable[float] | str | None, default: None
Maximum distance between original and new labels for inexact
matches. The values of the index at the matching locations must
satisfy the equation ``abs(index[indexer] - target) <= tolerance``.
Expand Down
11 changes: 6 additions & 5 deletions xarray/core/datatree.py
Original file line number Diff line number Diff line change
Expand Up @@ -1314,11 +1314,12 @@ def match(self, pattern: str) -> DataTree:
... }
... )
>>> dt.match("*/B")
DataTree('None', parent=None)
├── DataTree('a')
│ └── DataTree('B')
└── DataTree('b')
└── DataTree('B')
<xarray.DataTree>
Group: /
├── Group: /a
│ └── Group: /a/B
└── Group: /b
└── Group: /b/B
"""
matching_nodes = {
node.path: node.ds
Expand Down
11 changes: 6 additions & 5 deletions xarray/core/datatree_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,12 @@ def __init__(self):
>>> s0a = DataTree(name="sub0A", parent=s0)
>>> s1 = DataTree(name="sub1", parent=root)
>>> print(RenderDataTree(root))
DataTree('root', parent=None)
├── DataTree('sub0')
│ ├── DataTree('sub0B')
│ └── DataTree('sub0A')
└── DataTree('sub1')
<xarray.DataTree 'root'>
Group: /
├── Group: /sub0
│ ├── Group: /sub0/sub0B
│ └── Group: /sub0/sub0A
└── Group: /sub1
"""
super().__init__("\u2502 ", "\u251c\u2500\u2500 ", "\u2514\u2500\u2500 ")

Expand Down
33 changes: 17 additions & 16 deletions xarray/core/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,12 @@ def _diff_mapping_repr(
a_indexes=None,
b_indexes=None,
):
def compare_attr(a, b):
if is_duck_array(a) or is_duck_array(b):
return array_equiv(a, b)
else:
return a == b

def extra_items_repr(extra_keys, mapping, ab_side, kwargs):
extra_repr = [
summarizer(k, mapping[k], col_width, **kwargs[k]) for k in extra_keys
Expand Down Expand Up @@ -801,11 +807,7 @@ def extra_items_repr(extra_keys, mapping, ab_side, kwargs):
is_variable = True
except AttributeError:
# compare attribute value
if is_duck_array(a_mapping[k]) or is_duck_array(b_mapping[k]):
compatible = array_equiv(a_mapping[k], b_mapping[k])
else:
compatible = a_mapping[k] == b_mapping[k]

compatible = compare_attr(a_mapping[k], b_mapping[k])
is_variable = False

if not compatible:
Expand All @@ -821,7 +823,11 @@ def extra_items_repr(extra_keys, mapping, ab_side, kwargs):

attrs_to_print = set(a_attrs) ^ set(b_attrs)
attrs_to_print.update(
{k for k in set(a_attrs) & set(b_attrs) if a_attrs[k] != b_attrs[k]}
{
k
for k in set(a_attrs) & set(b_attrs)
if not compare_attr(a_attrs[k], b_attrs[k])
}
)
for m in (a_mapping, b_mapping):
attr_s = "\n".join(
Expand Down Expand Up @@ -1023,20 +1029,21 @@ def diff_datatree_repr(a: DataTree, b: DataTree, compat):

def _single_node_repr(node: DataTree) -> str:
"""Information about this node, not including its relationships to other nodes."""
node_info = f"DataTree('{node.name}')"

if node.has_data or node.has_attrs:
ds_info = "\n" + repr(node.ds)
else:
ds_info = ""
return node_info + ds_info
return f"Group: {node.path}{ds_info}"


def datatree_repr(dt: DataTree):
"""A printable representation of the structure of this entire tree."""
renderer = RenderDataTree(dt)

lines = []
name_info = "" if dt.name is None else f" {dt.name!r}"
header = f"<xarray.DataTree{name_info}>"

lines = [header]
for pre, fill, node in renderer:
node_repr = _single_node_repr(node)

Expand All @@ -1051,12 +1058,6 @@ def datatree_repr(dt: DataTree):
else:
lines.append(f"{fill}{' ' * len(renderer.style.vertical)}{line}")

# Tack on info about whether or not root node has a parent at the start
first_line = lines[0]
parent = f'"{dt.parent.name}"' if dt.parent is not None else "None"
first_line_with_parent = first_line[:-1] + f", parent={parent})"
lines[0] = first_line_with_parent

return "\n".join(lines)


Expand Down
19 changes: 10 additions & 9 deletions xarray/core/iterators.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,16 @@ class LevelOrderIter(Iterator):
>>> i = DataTree(name="i", parent=g)
>>> h = DataTree(name="h", parent=i)
>>> print(f)
DataTree('f', parent=None)
├── DataTree('b')
│ ├── DataTree('a')
│ └── DataTree('d')
│ ├── DataTree('c')
│ └── DataTree('e')
└── DataTree('g')
└── DataTree('i')
└── DataTree('h')
<xarray.DataTree 'f'>
Group: /
├── Group: /b
│ ├── Group: /b/a
│ └── Group: /b/d
│ ├── Group: /b/d/c
│ └── Group: /b/d/e
└── Group: /g
└── Group: /g/i
└── Group: /g/i/h
>>> [node.name for node in LevelOrderIter(f)]
['f', 'b', 'g', 'a', 'd', 'i', 'c', 'e', 'h']
>>> [node.name for node in LevelOrderIter(f, maxlevel=3)]
Expand Down
Loading

0 comments on commit 8e643c6

Please sign in to comment.