pycontrails 0.57.0__cp312-cp312-win_amd64.whl → 0.59.0__cp312-cp312-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pycontrails might be problematic. Click here for more details.

Files changed (40) hide show
  1. pycontrails/_version.py +3 -3
  2. pycontrails/core/aircraft_performance.py +1 -1
  3. pycontrails/core/cache.py +2 -2
  4. pycontrails/core/fleet.py +2 -7
  5. pycontrails/core/flight.py +2 -7
  6. pycontrails/core/interpolation.py +45 -67
  7. pycontrails/core/met.py +62 -37
  8. pycontrails/core/polygon.py +3 -3
  9. pycontrails/core/rgi_cython.cp312-win_amd64.pyd +0 -0
  10. pycontrails/core/vector.py +3 -8
  11. pycontrails/datalib/_met_utils/metsource.py +4 -7
  12. pycontrails/datalib/ecmwf/common.py +2 -2
  13. pycontrails/datalib/ecmwf/hres.py +2 -2
  14. pycontrails/datalib/ecmwf/ifs.py +1 -1
  15. pycontrails/datalib/ecmwf/model_levels.py +1 -1
  16. pycontrails/datalib/gfs/gfs.py +1 -1
  17. pycontrails/datalib/goes.py +10 -3
  18. pycontrails/datalib/gruan.py +343 -0
  19. pycontrails/datalib/himawari/header_struct.py +1 -1
  20. pycontrails/datalib/himawari/himawari.py +24 -7
  21. pycontrails/datalib/leo_utils/sentinel_metadata.py +9 -9
  22. pycontrails/ext/synthetic_flight.py +2 -2
  23. pycontrails/models/cocip/cocip_uncertainty.py +1 -1
  24. pycontrails/models/cocip/contrail_properties.py +1 -1
  25. pycontrails/models/cocip/output_formats.py +1 -1
  26. pycontrails/models/cocipgrid/cocip_grid.py +3 -3
  27. pycontrails/models/dry_advection.py +1 -1
  28. pycontrails/models/extended_k15.py +4 -4
  29. pycontrails/models/humidity_scaling/humidity_scaling.py +2 -2
  30. pycontrails/models/ps_model/ps_grid.py +2 -2
  31. pycontrails/models/sac.py +1 -1
  32. pycontrails/models/tau_cirrus.py +1 -1
  33. pycontrails/physics/thermo.py +4 -4
  34. pycontrails/utils/iteration.py +1 -1
  35. {pycontrails-0.57.0.dist-info → pycontrails-0.59.0.dist-info}/METADATA +5 -6
  36. {pycontrails-0.57.0.dist-info → pycontrails-0.59.0.dist-info}/RECORD +40 -39
  37. {pycontrails-0.57.0.dist-info → pycontrails-0.59.0.dist-info}/WHEEL +0 -0
  38. {pycontrails-0.57.0.dist-info → pycontrails-0.59.0.dist-info}/licenses/LICENSE +0 -0
  39. {pycontrails-0.57.0.dist-info → pycontrails-0.59.0.dist-info}/licenses/NOTICE +0 -0
  40. {pycontrails-0.57.0.dist-info → pycontrails-0.59.0.dist-info}/top_level.txt +0 -0
pycontrails/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.57.0'
32
- __version_tuple__ = version_tuple = (0, 57, 0)
31
+ __version__ = version = '0.59.0'
32
+ __version_tuple__ = version_tuple = (0, 59, 0)
33
33
 
34
- __commit_id__ = commit_id = 'g7b8b60b87'
34
+ __commit_id__ = commit_id = 'g3ff518b95'
@@ -550,7 +550,7 @@ class AircraftPerformance(Model):
550
550
  if self.met is None:
551
551
  cond = np.isnan(u) & np.isnan(v)
552
552
  else:
553
- met_level_max = self.met.data["level"][-1].item() # type: ignore[union-attr]
553
+ met_level_max = self.met.data["level"][-1].item()
554
554
  cond = self.source.level > met_level_max
555
555
 
556
556
  # We DON'T overwrite the original u and v arrays already attached to the source
pycontrails/core/cache.py CHANGED
@@ -189,7 +189,7 @@ class DiskCacheStore(CacheStore):
189
189
  self,
190
190
  cache_dir: str | pathlib.Path | None = None,
191
191
  allow_clear: bool = False,
192
- ):
192
+ ) -> None:
193
193
  if cache_dir is None:
194
194
  # Avoid unnecessary import of platformdirs (called in _get_user_cache_dir)
195
195
  cache_dir = os.getenv("PYCONTRAILS_CACHE_DIR") or _get_user_cache_dir()
@@ -461,7 +461,7 @@ class GCPCacheStore(CacheStore):
461
461
  timeout: int = 300,
462
462
  show_progress: bool = False,
463
463
  chunk_size: int = 64 * 262144,
464
- ):
464
+ ) -> None:
465
465
  try:
466
466
  from google.cloud import storage
467
467
  except ModuleNotFoundError as e:
pycontrails/core/fleet.py CHANGED
@@ -5,12 +5,7 @@ from __future__ import annotations
5
5
  import sys
6
6
  import warnings
7
7
  from collections.abc import Iterable
8
- from typing import Any, NoReturn
9
-
10
- if sys.version_info >= (3, 11):
11
- from typing import Self
12
- else:
13
- from typing_extensions import Self
8
+ from typing import Any, NoReturn, Self
14
9
 
15
10
  if sys.version_info >= (3, 12):
16
11
  from typing import override
@@ -122,7 +117,7 @@ class Fleet(Flight):
122
117
  # Set default fl_attrs if not provided
123
118
  fl_attrs = fl_attrs or {}
124
119
  for flight_id in groups.index:
125
- fl_attrs.setdefault(flight_id, {}) # type: ignore[call-overload]
120
+ fl_attrs.setdefault(flight_id, {})
126
121
 
127
122
  extra = fl_attrs.keys() - groups.index
128
123
  if extra:
@@ -6,18 +6,13 @@ import enum
6
6
  import logging
7
7
  import sys
8
8
  import warnings
9
- from typing import TYPE_CHECKING, Any, NoReturn
9
+ from typing import TYPE_CHECKING, Any, NoReturn, Self
10
10
 
11
11
  if sys.version_info >= (3, 12):
12
12
  from typing import override
13
13
  else:
14
14
  from typing_extensions import override
15
15
 
16
- if sys.version_info >= (3, 11):
17
- from typing import Self
18
- else:
19
- from typing_extensions import Self
20
-
21
16
 
22
17
  import numpy as np
23
18
  import numpy.typing as npt
@@ -2138,7 +2133,7 @@ def segment_rocd(
2138
2133
  T_correction[:-1] = (air_temperature[:-1] + air_temperature[1:]) / (T_isa[:-1] + T_isa[1:])
2139
2134
  T_correction[-1] = np.nan
2140
2135
 
2141
- return T_correction * out # type: ignore[return-value]
2136
+ return T_correction * out
2142
2137
 
2143
2138
 
2144
2139
  def _resample_to_freq_or_time(
@@ -26,57 +26,74 @@ class PycontrailsRegularGridInterpolator(scipy.interpolate.RegularGridInterpolat
26
26
 
27
27
  This class is a thin wrapper around the
28
28
  :class:`scipy.interpolate.RegularGridInterpolator` in order to make typical
29
- ``pycontrails`` use-cases more efficient.
29
+ pycontrails linear interpolation use-cases more performant:
30
30
 
31
- #. Avoid ``RegularGridInterpolator`` constructor validation. In :func:`interp`,
32
- parameters are carefully crafted to fit into the intended form, thereby making
33
- validation unnecessary.
31
+ #. Avoid ``RegularGridInterpolator`` constructor validation when ``method="linear"``.
32
+ In :func:`interp`, parameters are carefully crafted to fit into the intended form,
33
+ thereby making validation unnecessary.
34
34
  #. Override the :meth:`_evaluate_linear` method with a faster implementation. See
35
- the :meth:`_evaluate_linear` docstring for more information.
35
+ the :meth:`_evaluate_linear` docstring for more information.
36
36
 
37
- This class should not be used directly. Instead, use the :func:`interp` function.
37
+ **This class should not be used directly. Instead, use the** :func:`interp` **function.**
38
38
 
39
39
  .. versionchanged:: 0.40.0
40
40
 
41
41
  The :meth:`_evaluate_linear` method now uses a Cython implementation. The dtype
42
42
  of the output is now consistent with the dtype of the underlying :attr:`values`
43
43
 
44
+ .. versionchanged:: 0.58.0
45
+
46
+ Any ``method`` other than ``"linear"`` now uses the
47
+ :class:`scipy.interpolate.RegularGridInterpolator` implementation. This
48
+ allows for greater flexibility in the ``method`` parameter.
49
+
44
50
  Parameters
45
51
  ----------
46
52
  points : tuple[npt.NDArray[np.floating], ...]
47
53
  Coordinates of the grid points.
48
54
  values : npt.NDArray[np.floating]
49
55
  Grid values. The shape of this array must be compatible with the
50
- coordinates. An error is raised if the dtype is not ``np.float32``
51
- or ``np.float64``.
56
+ coordinates.
52
57
  method : str
53
58
  Passed into :class:`scipy.interpolate.RegularGridInterpolator`
54
59
  bounds_error : bool
55
60
  Passed into :class:`scipy.interpolate.RegularGridInterpolator`
56
61
  fill_value : float | np.float64 | None
57
62
  Passed into :class:`scipy.interpolate.RegularGridInterpolator`
63
+
64
+ See Also
65
+ --------
66
+ scipy.interpolate.RegularGridInterpolator
67
+ interp
58
68
  """
59
69
 
60
70
  def __init__(
61
71
  self,
62
72
  points: tuple[npt.NDArray[np.floating], ...],
63
73
  values: npt.NDArray[np.floating],
74
+ *,
64
75
  method: str,
65
76
  bounds_error: bool,
66
77
  fill_value: float | np.float64 | None,
67
- ):
68
- if values.dtype not in (np.float32, np.float64):
69
- msg = f"values must be a float array, not {values.dtype}"
70
- raise ValueError(msg)
71
-
78
+ ) -> None:
79
+ if method != "linear" or values.dtype not in (np.float32, np.float64):
80
+ # Slow path: use parent class
81
+ super().__init__(
82
+ points,
83
+ values,
84
+ method=method,
85
+ bounds_error=bounds_error,
86
+ fill_value=fill_value,
87
+ )
88
+ return
89
+
90
+ # Fast path: no validation
72
91
  self.grid = points
73
92
  self.values = values
74
- # TODO: consider supporting updated tensor-product spline methods
75
- # see https://github.com/scipy/scipy/releases/tag/v1.13.0
76
- self.method = _pick_method(scipy.__version__, method)
93
+ self.method = method
77
94
  self.bounds_error = bounds_error
78
95
  self.fill_value = fill_value
79
- self._spline = None
96
+ self._spline = None # XXX: setting private attribute on RGI
80
97
 
81
98
  def _prepare_xi_simple(self, xi: npt.NDArray[np.floating]) -> npt.NDArray[np.bool_]:
82
99
  """Run looser version of :meth:`_prepare_xi`.
@@ -103,7 +120,7 @@ class PycontrailsRegularGridInterpolator(scipy.interpolate.RegularGridInterpolat
103
120
 
104
121
  return np.zeros(xi.shape[0], dtype=bool)
105
122
 
106
- return self._find_out_of_bounds(xi.T)
123
+ return self._find_out_of_bounds(xi.T) # XXX: calling private method on RGI
107
124
 
108
125
  def __call__(
109
126
  self, xi: npt.NDArray[np.floating], method: str | None = None
@@ -130,7 +147,7 @@ class PycontrailsRegularGridInterpolator(scipy.interpolate.RegularGridInterpolat
130
147
  return super().__call__(xi, method)
131
148
 
132
149
  out_of_bounds = self._prepare_xi_simple(xi)
133
- xi_indices, norm_distances = rgi_cython.find_indices(self.grid, xi.T)
150
+ xi_indices, norm_distances = self._find_indices(xi.T) # XXX: calling private method on RGI
134
151
 
135
152
  out = self._evaluate_linear(xi_indices, norm_distances)
136
153
  return self._set_out_of_bounds(out, out_of_bounds)
@@ -223,45 +240,6 @@ class PycontrailsRegularGridInterpolator(scipy.interpolate.RegularGridInterpolat
223
240
  raise ValueError(msg)
224
241
 
225
242
 
226
- def _pick_method(scipy_version: str, method: str) -> str:
227
- """Select an interpolation method.
228
-
229
- For scipy versions 1.13.0 and later, fall back on legacy implementations
230
- of tensor-product spline methods. The default implementations in 1.13.0
231
- and later are incompatible with this class.
232
-
233
- Parameters
234
- ----------
235
- scipy_version : str
236
- scipy version (major.minor.patch)
237
-
238
- method : str
239
- Interpolation method. Passed into :class:`scipy.interpolate.RegularGridInterpolator`
240
- as-is unless ``scipy_version`` is 1.13.0 or later and ``method`` is ``"slinear"``,
241
- ``"cubic"``, or ``"quintic"``. In this case, ``"_legacy"`` is appended to ``method``.
242
-
243
- Returns
244
- -------
245
- str
246
- Interpolation method adjusted for compatibility with this class.
247
- """
248
- if method == "linear":
249
- return method
250
-
251
- try:
252
- version = scipy_version.split(".")
253
- major = int(version[0])
254
- minor = int(version[1])
255
- except (IndexError, ValueError) as exc:
256
- msg = f"Failed to parse major and minor version from {scipy_version}"
257
- raise ValueError(msg) from exc
258
-
259
- reimplemented_methods = ["slinear", "cubic", "quintic"]
260
- if major > 1 or ((major == 1 and minor >= 13) and method in reimplemented_methods):
261
- return method + "_legacy"
262
- return method
263
-
264
-
265
243
  def _floatize_time(
266
244
  time: npt.NDArray[np.datetime64], offset: np.datetime64
267
245
  ) -> npt.NDArray[np.floating]:
@@ -431,7 +409,7 @@ def interp(
431
409
  Include ``indices`` and ``return_indices`` experimental parameters.
432
410
  Currently, nan values in ``longitude``, ``latitude``, ``level``, or ``time``
433
411
  are always propagated through to the output, regardless of ``bounds_error``.
434
- In other words, a ValueError for an out of bounds coordinate is only raised
412
+ In other words, a ``ValueError`` for an out of bounds coordinate is only raised
435
413
  if a non-nan value is out of bounds.
436
414
 
437
415
  .. versionchanged:: 0.40.0
@@ -450,8 +428,8 @@ def interp(
450
428
  In particular, the dimensions of ``da`` must be ``longitude``, ``latitude``,
451
429
  ``level``, and ``time``. The three spatial dimensions must be monotonically
452
430
  increasing with ``float64`` dtype. The ``time`` dimension must be
453
- monotonically increasing with ``datetime64`` dtype.
454
- Assumed to be cheap to load into memory (:attr:`xr.DataArray.values` is
431
+ monotonically increasing with :class:`numpy.datetime64` dtype.
432
+ Assumed to be cheap to load into memory (:attr:`xarray.DataArray.values` is
455
433
  used without hesitation).
456
434
  method : str
457
435
  Passed into :class:`scipy.interpolate.RegularGridInterpolator`.
@@ -464,7 +442,7 @@ def interp(
464
442
  ``coords``.
465
443
  indices : tuple | None, optional
466
444
  Experimental. Provide intermediate artifacts computed by
467
- :meth:``scipy.interpolate.RegularGridInterpolator._find_indices`
445
+ :meth:`scipy.interpolate.RegularGridInterpolator._find_indices`
468
446
  to avoid redundant computation. If known and provided, this can speed
469
447
  up interpolation by avoiding an unnecessary call to ``_find_indices``.
470
448
  By default, None. Must be used precisely.
@@ -480,9 +458,9 @@ def interp(
480
458
 
481
459
  See Also
482
460
  --------
483
- - :meth:`MetDataArray.interpolate`
484
- - :func:`scipy.interpolate.interpn`
485
- - :class:`scipy.interpolate.RegularGridInterpolator`
461
+ pycontrails.MetDataArray.interpolate
462
+ scipy.interpolate.interpn
463
+ scipy.interpolate.RegularGridInterpolator
486
464
  """
487
465
  if localize:
488
466
  coords = {"longitude": longitude, "latitude": latitude, "level": level, "time": time}
@@ -579,7 +557,7 @@ def _linear_interp_with_indices(
579
557
  if indices is None:
580
558
  assert xi is not None, "xi must be provided if indices is None"
581
559
  out_of_bounds = interp._prepare_xi_simple(xi)
582
- xi_indices, norm_distances = rgi_cython.find_indices(interp.grid, xi.T)
560
+ xi_indices, norm_distances = interp._find_indices(xi.T)
583
561
  indices = RGIArtifacts(xi_indices, norm_distances, out_of_bounds)
584
562
 
585
563
  out = interp._evaluate_linear(indices.xi_indices, indices.norm_distances)
@@ -606,7 +584,7 @@ class EmissionsProfileInterpolator:
606
584
 
607
585
  This class simply wraps :func:`numpy.interp` with fixed values for the
608
586
  ``xp`` and ``fp`` arguments. Unlike :class:`xarray.DataArray` interpolation,
609
- the `numpy.interp` automatically clips values outside the range of the
587
+ the :func:`numpy.interp` automatically clips values outside the range of the
610
588
  ``xp`` array.
611
589
 
612
590
  Parameters
pycontrails/core/met.py CHANGED
@@ -26,15 +26,11 @@ from typing import (
26
26
  Any,
27
27
  Generic,
28
28
  Literal,
29
+ Self,
29
30
  TypeVar,
30
31
  overload,
31
32
  )
32
33
 
33
- if sys.version_info >= (3, 11):
34
- from typing import Self
35
- else:
36
- from typing_extensions import Self
37
-
38
34
  if sys.version_info >= (3, 12):
39
35
  from typing import override
40
36
  else:
@@ -211,14 +207,22 @@ class MetBase(ABC, Generic[XArrayType]):
211
207
  Raises
212
208
  ------
213
209
  ValueError
214
- If one of the coordinates is not sorted.
210
+ If one of the coordinates is not sorted or contains duplicate values.
215
211
  """
216
212
  indexes = self.indexes
217
- if not np.all(np.diff(indexes["time"]) > np.timedelta64(0, "ns")):
218
- raise ValueError("Coordinate 'time' not sorted. Instantiate with 'copy=True'.")
219
- for coord in self.dim_order[:3]: # exclude time, the 4th dimension
220
- if not np.all(np.diff(indexes[coord]) > 0.0):
221
- raise ValueError(f"Coordinate '{coord}' not sorted. Instantiate with 'copy=True'.")
213
+ for coord in self.dim_order:
214
+ arr = indexes[coord]
215
+ d = np.diff(arr)
216
+ zero = np.zeros((), dtype=d.dtype) # ensure same dtype
217
+
218
+ if np.any(d <= zero):
219
+ if np.any(d == zero):
220
+ msg = f"Coordinate '{coord}' contains duplicate values."
221
+ else:
222
+ msg = f"Coordinate '{coord}' not sorted."
223
+
224
+ msg += " Instantiate with 'copy=True'."
225
+ raise ValueError(msg)
222
226
 
223
227
  def _validate_transpose(self) -> None:
224
228
  """Check that data is transposed according to :attr:`dim_order`."""
@@ -271,6 +275,10 @@ class MetBase(ABC, Generic[XArrayType]):
271
275
  Auxiliary coordinates (altitude and air_pressure) are now cast to the same
272
276
  dtype as the underlying grid data.
273
277
 
278
+ .. versionchanged:: 0.58.0
279
+
280
+ Duplicate dimension values are dropped, keeping the first occurrence.
281
+
274
282
 
275
283
  Parameters
276
284
  ----------
@@ -297,6 +305,18 @@ class MetBase(ABC, Generic[XArrayType]):
297
305
  # sortby to ensure each coordinate has ascending order
298
306
  self.data = self.data.sortby(list(self.dim_order), ascending=True)
299
307
 
308
+ # Drop any duplicated dimension values
309
+ indexes = self.indexes
310
+ for coord in self.dim_order:
311
+ arr = indexes[coord]
312
+ d = np.diff(arr)
313
+ zero = np.zeros((), dtype=d.dtype) # ensure same dtype
314
+
315
+ if np.any(d == zero):
316
+ # Remove duplicates
317
+ filt = np.r_[True, d > zero] # prepend True keeps the first occurrence
318
+ self.data = self.data.isel({coord: filt})
319
+
300
320
  if not self.is_wrapped:
301
321
  # Ensure longitude is contained in interval [-180, 180)
302
322
  # If longitude has value at 180, we might not want to shift it?
@@ -334,8 +354,7 @@ class MetBase(ABC, Generic[XArrayType]):
334
354
  def hash(self) -> str:
335
355
  """Generate a unique hash for this met instance.
336
356
 
337
- Note this is not as robust as it could be since `repr`
338
- cuts off.
357
+ Note this is not as robust as it could be since :func:`repr` cuts off.
339
358
 
340
359
  Returns
341
360
  -------
@@ -666,7 +685,7 @@ class MetBase(ABC, Generic[XArrayType]):
666
685
  class MetDataset(MetBase):
667
686
  """Meteorological dataset with multiple variables.
668
687
 
669
- Composition around xr.Dataset to enforce certain
688
+ Composition around :class:`xarray.Dataset` to enforce certain
670
689
  variables and dimensions for internal usage
671
690
 
672
691
  Parameters
@@ -678,17 +697,17 @@ class MetDataset(MetBase):
678
697
  Defaults to None.
679
698
  wrap_longitude : bool, optional
680
699
  Wrap data along the longitude dimension. If True, duplicate and shift longitude
681
- values (ie, -180 -> 180) to ensure that the longitude dimension covers the entire
700
+ values (ie, ``-180 -> 180``) to ensure that the longitude dimension covers the entire
682
701
  interval ``[-180, 180]``. Defaults to False.
683
702
  copy : bool, optional
684
703
  Copy data on construction. Defaults to True.
685
704
  attrs : dict[str, Any], optional
686
- Attributes to add to :attr:`data.attrs`. Defaults to None.
687
- Generally, pycontrails :class:`Models` may use the following attributes:
705
+ Attributes to add to :attr:`data.attrs`. Defaults to None. Generally, pycontrails
706
+ :class:`pycontrails.core.models.Models` may use the following attributes:
688
707
 
689
- - ``provider``: Name of the data provider (e.g. "ECMWF").
690
- - ``dataset``: Name of the dataset (e.g. "ERA5").
691
- - ``product``: Name of the product type (e.g. "reanalysis").
708
+ - ``provider``: Name of the data provider (e.g. ``"ECMWF"``).
709
+ - ``dataset``: Name of the dataset (e.g. ``"ERA5"``).
710
+ - ``product``: Name of the product type (e.g. ``"reanalysis"``).
692
711
 
693
712
  **attrs_kwargs : Any
694
713
  Keyword arguments to add to :attr:`data.attrs`. Defaults to None.
@@ -792,7 +811,7 @@ class MetDataset(MetBase):
792
811
  return MetDataArray._from_fastpath(da)
793
812
 
794
813
  def get(self, key: str, default_value: Any = None) -> Any:
795
- """Shortcut to :meth:`data.get(k, v)` method.
814
+ """Shortcut to :meth:`xarray.Dataset.get` method.
796
815
 
797
816
  Parameters
798
817
  ----------
@@ -869,7 +888,7 @@ class MetDataset(MetBase):
869
888
 
870
889
  See Also
871
890
  --------
872
- - :meth:`xarray.Dataset.update`
891
+ xarray.Dataset.update
873
892
  """
874
893
  other = other or {}
875
894
  other.update(kwargs)
@@ -937,8 +956,8 @@ class MetDataset(MetBase):
937
956
  Returns
938
957
  -------
939
958
  list[str]
940
- List of met keys verified in MetDataset.
941
- Returns an empty list if any MetVariable is missing.
959
+ List of met keys verified in :class:`MetDataset`.
960
+ Returns an empty list if any :class:`MetVariable` is missing.
942
961
 
943
962
  Raises
944
963
  ------
@@ -1087,7 +1106,7 @@ class MetDataset(MetBase):
1087
1106
  out[key] = da.values.ravel() # type: ignore[index]
1088
1107
 
1089
1108
  if transfer_attrs:
1090
- out.attrs.update(self.attrs) # type: ignore[arg-type]
1109
+ out.attrs.update(self.attrs)
1091
1110
 
1092
1111
  return out
1093
1112
 
@@ -1121,12 +1140,12 @@ class MetDataset(MetBase):
1121
1140
 
1122
1141
  @property
1123
1142
  def provider_attr(self) -> str:
1124
- """Look up the 'provider' attribute with a custom error message.
1143
+ """Look up the ``"provider"`` attribute with a custom error message.
1125
1144
 
1126
1145
  Returns
1127
1146
  -------
1128
1147
  str
1129
- Provider of the data. If not one of 'ECMWF' or 'NCEP',
1148
+ Provider of the data. If not one of ``"ECMWF"`` or ``"NCEP"``,
1130
1149
  a warning is issued.
1131
1150
  """
1132
1151
  supported = ("ECMWF", "NCEP")
@@ -1135,13 +1154,13 @@ class MetDataset(MetBase):
1135
1154
 
1136
1155
  @property
1137
1156
  def dataset_attr(self) -> str:
1138
- """Look up the 'dataset' attribute with a custom error message.
1157
+ """Look up the ``"dataset"`` attribute with a custom error message.
1139
1158
 
1140
1159
  Returns
1141
1160
  -------
1142
1161
  str
1143
- Dataset of the data. If not one of 'ERA5', 'HRES', 'IFS',
1144
- or 'GFS', a warning is issued.
1162
+ Dataset of the data. If not one of ``"ERA5"``, ``"HRES"``, ``"IFS"``,
1163
+ or ``"GFS"``, a warning is issued.
1145
1164
  """
1146
1165
  supported = ("ERA5", "HRES", "IFS", "GFS")
1147
1166
  examples = {
@@ -1153,13 +1172,13 @@ class MetDataset(MetBase):
1153
1172
 
1154
1173
  @property
1155
1174
  def product_attr(self) -> str:
1156
- """Look up the 'product' attribute with a custom error message.
1175
+ """Look up the ``"product"`` attribute with a custom error message.
1157
1176
 
1158
1177
  Returns
1159
1178
  -------
1160
1179
  str
1161
- Product of the data. If not one of 'forecast', 'ensemble', or 'reanalysis',
1162
- a warning is issued.
1180
+ Product of the data. If not one of ``"forecast"``, ``"ensemble"``,
1181
+ or ``"reanalysis"``, a warning is issued.
1163
1182
 
1164
1183
  """
1165
1184
  supported = ("reanalysis", "forecast", "ensemble")
@@ -1188,6 +1207,7 @@ class MetDataset(MetBase):
1188
1207
 
1189
1208
  By default, this method returns a new :class:`MetDataset` instead
1190
1209
  of renaming in place. To retain the old behavior, set ``inplace=True``.
1210
+ The ``inplace`` behavior is deprecated and will be removed in a future release.
1191
1211
 
1192
1212
  Parameters
1193
1213
  ----------
@@ -1204,6 +1224,11 @@ class MetDataset(MetBase):
1204
1224
  data_renamed = standardize_variables(self.data, variables)
1205
1225
 
1206
1226
  if inplace:
1227
+ warnings.warn(
1228
+ "The inplace behavior is deprecated and will be removed in a future release. ",
1229
+ DeprecationWarning,
1230
+ stacklevel=2,
1231
+ )
1207
1232
  self.data = data_renamed
1208
1233
  return None
1209
1234
 
@@ -1300,7 +1325,7 @@ class MetDataset(MetBase):
1300
1325
  coords: dict[str, np.ndarray] = {}
1301
1326
  for key, val in input_data.items():
1302
1327
  dtype = "datetime64[ns]" if key == "time" else COORD_DTYPE
1303
- arr: np.ndarray = np.asarray(val, dtype=dtype) # type: ignore[call-overload]
1328
+ arr: np.ndarray = np.asarray(val, dtype=dtype)
1304
1329
 
1305
1330
  if arr.ndim == 0:
1306
1331
  arr = arr.reshape(1)
@@ -1889,7 +1914,7 @@ class MetDataArray(MetBase):
1889
1914
  if not self.binary:
1890
1915
  raise NotImplementedError("proportion method is only implemented for binary fields")
1891
1916
 
1892
- return self.data.sum().values.item() / self.data.count().values.item() # type: ignore[operator]
1917
+ return self.data.sum().values.item() / self.data.count().values.item()
1893
1918
 
1894
1919
  def find_edges(self) -> Self:
1895
1920
  """Find edges of regions.
@@ -2598,9 +2623,9 @@ def _extract_2d_arr_and_altitude(
2598
2623
  except KeyError:
2599
2624
  altitude = None
2600
2625
  else:
2601
- altitude = round(altitude) # type: ignore[call-overload]
2626
+ altitude = round(altitude)
2602
2627
 
2603
- return arr, altitude # type: ignore[return-value]
2628
+ return arr, altitude
2604
2629
 
2605
2630
 
2606
2631
  def downselect(data: XArrayType, bbox: tuple[float, ...]) -> XArrayType:
@@ -238,7 +238,7 @@ def _contours_to_polygons(
238
238
  latitude=latitude,
239
239
  precision=precision,
240
240
  buffer=buffer,
241
- i=child_i, # type: ignore[arg-type]
241
+ i=child_i,
242
242
  )
243
243
 
244
244
  candidate = shapely.Polygon(polygon.exterior, [h.exterior for h in holes])
@@ -354,11 +354,11 @@ def find_multipolygon(
354
354
  return shapely.MultiPolygon()
355
355
 
356
356
  assert len(hierarchy) == 1
357
- hierarchy = hierarchy[0] # type: ignore[index]
357
+ hierarchy = hierarchy[0]
358
358
 
359
359
  polygons = _contours_to_polygons(
360
360
  contours, # type: ignore[arg-type]
361
- hierarchy, # type: ignore[arg-type]
361
+ hierarchy,
362
362
  min_area,
363
363
  convex_hull,
364
364
  epsilon,
@@ -8,12 +8,7 @@ import logging
8
8
  import sys
9
9
  import warnings
10
10
  from collections.abc import Generator, Iterable, Iterator, Sequence
11
- from typing import Any, overload
12
-
13
- if sys.version_info >= (3, 11):
14
- from typing import Self
15
- else:
16
- from typing_extensions import Self
11
+ from typing import Any, Self, overload
17
12
 
18
13
  if sys.version_info >= (3, 12):
19
14
  from typing import override
@@ -315,7 +310,7 @@ class VectorDataset: # noqa: PLW1641
315
310
  # Set attributes: always shallow copy
316
311
  # -----------------------------------
317
312
 
318
- self.attrs = AttrDict(attrs or {}) # type: ignore[arg-type]
313
+ self.attrs = AttrDict(attrs or {})
319
314
  self.attrs.update(attrs_kwargs)
320
315
 
321
316
  @classmethod
@@ -1389,7 +1384,7 @@ class GeoVectorDataset(VectorDataset):
1389
1384
  ):
1390
1385
  keys = *self.required_keys, "altitude"
1391
1386
  self.data = VectorDataDict(_empty_vector_dict(keys))
1392
- self.attrs = AttrDict(attrs or {}) # type: ignore[arg-type]
1387
+ self.attrs = AttrDict(attrs or {})
1393
1388
  self.attrs.update(attrs_kwargs)
1394
1389
  return
1395
1390
 
@@ -175,16 +175,13 @@ def parse_pressure_levels(
175
175
 
176
176
  out = arr.tolist()
177
177
  if supported is None:
178
- return out # type: ignore[return-value]
178
+ return out
179
179
 
180
- if missing := set(out).difference(supported): # type: ignore[arg-type]
181
- msg = (
182
- f"Pressure levels {sorted(missing)} are not supported. " # type: ignore[type-var]
183
- f"Supported levels: {supported}"
184
- )
180
+ if missing := set(out).difference(supported):
181
+ msg = f"Pressure levels {sorted(missing)} are not supported. Supported levels: {supported}"
185
182
  raise ValueError(msg)
186
183
 
187
- return out # type: ignore[return-value]
184
+ return out
188
185
 
189
186
 
190
187
  def parse_variables(variables: VariableInput, supported: list[MetVariable]) -> list[MetVariable]:
@@ -61,14 +61,14 @@ class ECMWFAPI(metsource.MetDataSource):
61
61
 
62
62
  # downselect times
63
63
  if not self.timesteps:
64
- self.timesteps = ds["time"].values.astype("datetime64[ns]").tolist() # type: ignore[assignment]
64
+ self.timesteps = ds["time"].values.astype("datetime64[ns]").tolist()
65
65
  else:
66
66
  try:
67
67
  ds = ds.sel(time=self.timesteps)
68
68
  except KeyError as exc:
69
69
  # this snippet shows the missing times for convenience
70
70
  np_timesteps = {np.datetime64(t, "ns") for t in self.timesteps}
71
- missing_times = sorted(np_timesteps.difference(ds["time"].values)) # type: ignore[type-var]
71
+ missing_times = sorted(np_timesteps.difference(ds["time"].values))
72
72
  msg = f"Input dataset is missing time coordinates {[str(t) for t in missing_times]}"
73
73
  raise KeyError(msg) from exc
74
74