xarray-annotated 0.5.2__py3-none-any.whl → 0.6.0__py3-none-any.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.
@@ -1,4 +1,4 @@
1
- """xarray-annotated: validate xarray DataArray properties declared via ``typing.Annotated``.
1
+ """xarray-annotated: validate DataArray properties declared via ``typing.Annotated``.
2
2
 
3
3
  Annotation is the unifying technology: a declared property is read off an
4
4
  ``Annotated[DataArray, ...]`` hint and validated at runtime. Each kind of
@@ -14,7 +14,6 @@ from collections.abc import Callable
14
14
  from typing import (
15
15
  Annotated,
16
16
  Any,
17
- TypeVar,
18
17
  Union,
19
18
  get_args,
20
19
  get_origin,
@@ -24,8 +23,6 @@ from typing import (
24
23
 
25
24
  import xarray as xr
26
25
 
27
- _T = TypeVar("_T")
28
-
29
26
 
30
27
  def unwrap_annotated(hint: Any) -> Any:
31
28
  """Return the underlying type of an `Annotated` hint, else the hint itself.
@@ -44,30 +41,40 @@ def unwrap_annotated(hint: Any) -> Any:
44
41
 
45
42
 
46
43
  def _is_dataarray_type(tp: Any) -> bool:
47
- """Return whether `tp` is `DataArray` (possibly wrapped in a Union).
44
+ """Return whether `tp` is `DataArray` (possibly wrapped or in a Union).
48
45
 
49
46
  Accepts a bare `DataArray` as well as `DataArray | None` /
50
47
  `Optional[DataArray]` (and any other union that includes `DataArray`), so an
51
48
  optional DataArray parameter can still carry a declaration. Anything else
52
49
  (scalars, `str`, `bool`, `Dataset`, …) is not a declaration-bearing type.
53
50
 
51
+ Also accepts any *array-type wrapper* that exposes the underlying array class
52
+ as an `array_type` attribute — notably jaxtyping's `Float[DataArray, "t x"]`,
53
+ which occupies the base-type slot of an `Annotated` hint rather than competing
54
+ with it. This is a duck-typed check: jaxtyping is never imported, and any
55
+ other library following the same convention composes for free. See
56
+ `docs/guides/jaxtyping.md`.
57
+
54
58
  Args:
55
59
  tp: A type annotation to inspect.
56
60
 
57
61
  Returns:
58
- `True` if `tp` is or contains `DataArray`.
62
+ `True` if `tp` is, wraps, or contains `DataArray`.
59
63
  """
60
64
  if tp is xr.DataArray:
61
65
  return True
66
+ inner = getattr(tp, "array_type", None)
67
+ if isinstance(inner, type) and issubclass(inner, xr.DataArray):
68
+ return True
62
69
  if get_origin(tp) in (Union, types.UnionType):
63
70
  return any(_is_dataarray_type(arg) for arg in get_args(tp))
64
71
  return False
65
72
 
66
73
 
67
- def walk_signature(
74
+ def walk_signature[T](
68
75
  func: object,
69
- extract: Callable[[Any], _T | None],
70
- ) -> tuple[dict[str, _T], dict[str, _T] | _T | None]:
76
+ extract: Callable[[Any], T | None],
77
+ ) -> tuple[dict[str, T], dict[str, T] | T | None]:
71
78
  """Extract per-parameter and return declarations from a function's hints.
72
79
 
73
80
  Reads `get_type_hints(func, include_extras=True)` and maps each hint through
@@ -98,7 +105,7 @@ def walk_signature(
98
105
  if (payload := extract(hint)) is not None
99
106
  }
100
107
 
101
- output: dict[str, _T] | _T | None
108
+ output: dict[str, T] | T | None
102
109
  if is_typeddict(ret):
103
110
  ret_hints = get_type_hints(ret, include_extras=True)
104
111
  output = {
@@ -19,7 +19,6 @@ live here.
19
19
 
20
20
  import os
21
21
  from collections.abc import Callable
22
- from typing import TypeVar
23
22
 
24
23
  #: Master switch default. Validation is on unless explicitly disabled.
25
24
  DEFAULT_ENABLED: bool = True
@@ -35,8 +34,6 @@ _FALSEY: frozenset[str] = frozenset({"0", "false", "no", "off"})
35
34
  #: Process-wide override for the master switch, shared across all domains.
36
35
  _process_enabled: bool | None = None
37
36
 
38
- _T = TypeVar("_T")
39
-
40
37
 
41
38
  class _Unset:
42
39
  """Sentinel distinguishing 'argument omitted' from 'explicitly set to None'."""
@@ -48,12 +45,12 @@ class _Unset:
48
45
  _UNSET = _Unset()
49
46
 
50
47
 
51
- def _resolve(
48
+ def _resolve[T](
52
49
  env_var: str,
53
- process_value: _T | None,
54
- default: _T,
55
- parse: Callable[[str], _T],
56
- ) -> _T:
50
+ process_value: T | None,
51
+ default: T,
52
+ parse: Callable[[str], T],
53
+ ) -> T:
57
54
  """Resolve one axis: env var (parsed) → process override → default."""
58
55
  env = os.environ.get(env_var)
59
56
  if env:
@@ -13,8 +13,9 @@ Declare several at once inside ``Annotated`` and apply them with ``declare_schem
13
13
  from xarray_annotated.schema import declare_schema, Dims, Dtype
14
14
 
15
15
  @declare_schema
16
- def f(x: Annotated[xr.DataArray, Dims("time", "x"), Dtype("float64")]) -> xr.DataArray:
17
- ...
16
+ def f(
17
+ x: Annotated[xr.DataArray, Dims("time", "x"), Dtype("float64")],
18
+ ) -> xr.DataArray: ...
18
19
  """
19
20
 
20
21
  from ._annotations import (
@@ -48,7 +48,7 @@ class Dims:
48
48
  _names: tuple[str, ...]
49
49
  _ordered: bool
50
50
  _on_mismatch: OnMismatch | None
51
- __slots__ = ("_names", "_ordered", "_on_mismatch")
51
+ __slots__ = ("_names", "_on_mismatch", "_ordered")
52
52
 
53
53
  def __init__(
54
54
  self,
@@ -20,11 +20,11 @@ from dataclasses import dataclass
20
20
  from typing import Literal
21
21
 
22
22
  from .._config import (
23
+ _UNSET,
23
24
  DEFAULT_ENABLED,
24
25
  ENABLED_ENV_VAR,
25
26
  _resolve,
26
27
  _Unset,
27
- _UNSET,
28
28
  get_enabled_override,
29
29
  resolve_enabled,
30
30
  set_enabled_override,
@@ -100,8 +100,8 @@ def get_policy() -> Policy:
100
100
 
101
101
  def set_policy(
102
102
  *,
103
- enabled: bool | None | _Unset = _UNSET,
104
- on_mismatch: OnMismatch | None | _Unset = _UNSET,
103
+ enabled: bool | _Unset | None = _UNSET,
104
+ on_mismatch: OnMismatch | _Unset | None = _UNSET,
105
105
  ) -> None:
106
106
  """Set process-wide schema-policy overrides for one or more axes.
107
107
 
@@ -125,8 +125,8 @@ def set_policy(
125
125
  @contextmanager
126
126
  def policy(
127
127
  *,
128
- enabled: bool | None | _Unset = _UNSET,
129
- on_mismatch: OnMismatch | None | _Unset = _UNSET,
128
+ enabled: bool | _Unset | None = _UNSET,
129
+ on_mismatch: OnMismatch | _Unset | None = _UNSET,
130
130
  ):
131
131
  """Temporarily override schema-policy axes, restoring them on exit.
132
132
 
@@ -44,8 +44,8 @@ def declare_schema(
44
44
  default). When the policy is **disabled** the wrapper is a total no-op.
45
45
 
46
46
  Usable bare (`@declare_schema`) or called (`@declare_schema(on_mismatch=...)`).
47
- Every marker declaration is validated **at decoration time** (`assert_valid_schema`),
48
- so a malformed declaration fails fast at import.
47
+ Every marker declaration is validated **at decoration time**
48
+ (`assert_valid_schema`), so a malformed declaration fails fast at import.
49
49
 
50
50
  Args:
51
51
  func: The function to decorate (when used bare); `None` when parametrised.
@@ -91,7 +91,8 @@ def declare_schema(
91
91
  if not pol.enabled:
92
92
  # Master switch off: a total no-op (no validation at all).
93
93
  return fn(*args, **kwargs)
94
- # Per-call default severity (a marker's own override still wins in check_schema).
94
+ # Per-call default severity (a marker's own override still wins
95
+ # inside check_schema).
95
96
  eff = on_mismatch if on_mismatch is not None else pol.on_mismatch
96
97
  if input_markers:
97
98
  bound = sig.bind_partial(*args, **kwargs)
@@ -16,7 +16,9 @@ the classic resample footgun of a series landing on Wednesdays::
16
16
  from xarray_annotated.temporal import declare_freq, Freq
17
17
 
18
18
  @declare_freq
19
- def weekly_mean(x: Annotated[xr.DataArray, Freq("D")]) -> Annotated[xr.DataArray, Freq("W-SUN")]:
19
+ def weekly_mean(
20
+ x: Annotated[xr.DataArray, Freq("D")],
21
+ ) -> Annotated[xr.DataArray, Freq("W-SUN")]:
20
22
  return x.resample(time="W-SUN").mean()
21
23
 
22
24
  ``freq_compatible`` is the same comparison with no array in hand, for a build-time
@@ -55,7 +55,7 @@ class Freq:
55
55
  _dim: str | None
56
56
  _anchored: bool | None
57
57
  _on_mismatch: OnMismatch | None
58
- __slots__ = ("_freq", "_dim", "_anchored", "_on_mismatch")
58
+ __slots__ = ("_anchored", "_dim", "_freq", "_on_mismatch")
59
59
 
60
60
  def __init__(
61
61
  self,
@@ -24,11 +24,11 @@ from dataclasses import dataclass
24
24
  from typing import Literal
25
25
 
26
26
  from .._config import (
27
+ _UNSET,
27
28
  DEFAULT_ENABLED,
28
29
  ENABLED_ENV_VAR,
29
30
  _resolve,
30
31
  _Unset,
31
- _UNSET,
32
32
  get_enabled_override,
33
33
  resolve_enabled,
34
34
  set_enabled_override,
@@ -127,9 +127,9 @@ def get_policy() -> Policy:
127
127
 
128
128
  def set_policy(
129
129
  *,
130
- enabled: bool | None | _Unset = _UNSET,
131
- on_mismatch: OnMismatch | None | _Unset = _UNSET,
132
- on_uninferable: OnUninferable | None | _Unset = _UNSET,
130
+ enabled: bool | _Unset | None = _UNSET,
131
+ on_mismatch: OnMismatch | _Unset | None = _UNSET,
132
+ on_uninferable: OnUninferable | _Unset | None = _UNSET,
133
133
  ) -> None:
134
134
  """Set process-wide temporal-policy overrides for one or more axes.
135
135
 
@@ -158,9 +158,9 @@ def set_policy(
158
158
  @contextmanager
159
159
  def policy(
160
160
  *,
161
- enabled: bool | None | _Unset = _UNSET,
162
- on_mismatch: OnMismatch | None | _Unset = _UNSET,
163
- on_uninferable: OnUninferable | None | _Unset = _UNSET,
161
+ enabled: bool | _Unset | None = _UNSET,
162
+ on_mismatch: OnMismatch | _Unset | None = _UNSET,
163
+ on_uninferable: OnUninferable | _Unset | None = _UNSET,
164
164
  ):
165
165
  """Temporarily override temporal-policy axes, restoring them on exit.
166
166
 
@@ -425,13 +425,13 @@ def _apply_output_quantified(
425
425
  return da
426
426
  if equal:
427
427
  return da
428
- if on_output == "strict":
429
- if units_compatible(label, declared):
430
- raise ValueError(
431
- f"{prefix}output {name!r}: unit {label!r} differs from declared "
432
- f"{declared!r} and on_output='strict' requires them to match"
433
- )
434
- # Fall through: the conversion below raises DimensionalityError for us.
428
+ # Only *compatible* units raise here; an incompatible label falls through to the
429
+ # conversion below, which raises DimensionalityError for us.
430
+ if on_output == "strict" and units_compatible(label, declared):
431
+ raise ValueError(
432
+ f"{prefix}output {name!r}: unit {label!r} differs from declared "
433
+ f"{declared!r} and on_output='strict' requires them to match"
434
+ )
435
435
  try:
436
436
  return da.pint.to(declared)
437
437
  except PintExceptionGroup as group:
@@ -31,11 +31,11 @@ from dataclasses import dataclass
31
31
  from typing import Literal
32
32
 
33
33
  from .._config import (
34
+ _UNSET,
34
35
  DEFAULT_ENABLED,
35
36
  ENABLED_ENV_VAR,
36
37
  _resolve,
37
38
  _Unset,
38
- _UNSET,
39
39
  get_enabled_override,
40
40
  resolve_enabled,
41
41
  set_enabled_override,
@@ -152,10 +152,10 @@ def get_policy() -> Policy:
152
152
 
153
153
  def set_policy(
154
154
  *,
155
- enabled: bool | None | _Unset = _UNSET,
156
- on_missing: OnMissing | None | _Unset = _UNSET,
157
- on_inexact: OnInexact | None | _Unset = _UNSET,
158
- on_output: OnOutput | None | _Unset = _UNSET,
155
+ enabled: bool | _Unset | None = _UNSET,
156
+ on_missing: OnMissing | _Unset | None = _UNSET,
157
+ on_inexact: OnInexact | _Unset | None = _UNSET,
158
+ on_output: OnOutput | _Unset | None = _UNSET,
159
159
  ) -> None:
160
160
  """Set process-wide policy overrides for one or more axes.
161
161
 
@@ -189,10 +189,10 @@ def set_policy(
189
189
  @contextmanager
190
190
  def policy(
191
191
  *,
192
- enabled: bool | None | _Unset = _UNSET,
193
- on_missing: OnMissing | None | _Unset = _UNSET,
194
- on_inexact: OnInexact | None | _Unset = _UNSET,
195
- on_output: OnOutput | None | _Unset = _UNSET,
192
+ enabled: bool | _Unset | None = _UNSET,
193
+ on_missing: OnMissing | _Unset | None = _UNSET,
194
+ on_inexact: OnInexact | _Unset | None = _UNSET,
195
+ on_output: OnOutput | _Unset | None = _UNSET,
196
196
  ):
197
197
  """Temporarily override policy axes, restoring all of them on exit.
198
198
 
@@ -199,5 +199,6 @@ def declare_units(
199
199
  return wrapper
200
200
 
201
201
  # @declare_units → func is the decorated function;
202
- # @declare_units(on_missing=...) → func is None, return the parametrised decorator.
202
+ # @declare_units(on_missing=...) → func is None, return the parametrised
203
+ # decorator.
203
204
  return decorate if func is None else decorate(func)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xarray-annotated
3
- Version: 0.5.2
3
+ Version: 0.6.0
4
4
  Summary: Run-time validation of xarray DataArray properties (units, dims/coords/dtype) declared via typing.Annotated
5
5
  Keywords: xarray,pint,units,validation,annotations
6
6
  Author: jmarshrossney
@@ -78,10 +78,12 @@ It assumes a unit (pascals) and says so in the docstring but doesn't check or en
78
78
  ```python
79
79
  import xarray as xr
80
80
 
81
+
81
82
  def altitude(p: xr.DataArray) -> xr.DataArray:
82
83
  """Barometric altitude. Assumes p is in Pa."""
83
84
  return 44330.0 * (1.0 - (p / 101325.0) ** 0.1903)
84
85
 
86
+
85
87
  p = xr.DataArray([1013.0, 1000.0], dims=["time"], attrs={"units": "hPa"})
86
88
  altitude(p)
87
89
  ```
@@ -104,6 +106,7 @@ import xarray as xr
104
106
  from xarray_annotated.schema import declare_schema, Dims
105
107
  from xarray_annotated.units import declare_units, Unit
106
108
 
109
+
107
110
  @declare_units
108
111
  @declare_schema
109
112
  def altitude(
@@ -112,6 +115,7 @@ def altitude(
112
115
  """Barometric altitude."""
113
116
  return 44330.0 * (1.0 - (p / 101325.0) ** 0.1903)
114
117
 
118
+
115
119
  p = xr.DataArray([1013.0, 1000.0], dims=["time"], attrs={"units": "hPa"})
116
120
  altitude(p)
117
121
  ```
@@ -0,0 +1,26 @@
1
+ xarray_annotated/__init__.py,sha256=ht5hPD7C3sg_TBCP6V2b3hzckytcL5wrbH4y9eet8Ok,2033
2
+ xarray_annotated/_annotations.py,sha256=PTr693OmIxBqFT_77cAscO-uT0FDrkGK42Rdl3ipzQs,4611
3
+ xarray_annotated/_config.py,sha256=JkSP9GIjVy45IUnQXCvY1qg1FoO49vHwTUNjSUB_U8o,3375
4
+ xarray_annotated/_reader.py,sha256=UBmneo6B8aAeK0xWTVy6zDMdm-PaoDH5Pi7hF28UHKA,4857
5
+ xarray_annotated/_writer.py,sha256=BsEUVK3YfZi8QEf8Hz31pYkfO8-WRBP7fcMNbpPCak0,4172
6
+ xarray_annotated/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ xarray_annotated/schema/__init__.py,sha256=Podg0xG-oAP2m6M0YftRr1ETV7SKZzyxzi5NC6yfagM,1489
8
+ xarray_annotated/schema/_annotations.py,sha256=1_slbAJvmo0GXPuecGqXgKLuVFqQCq8bpKzQqtQDU0c,8723
9
+ xarray_annotated/schema/_check.py,sha256=oU5Ayx4nDr-VBlnhmj2n46-AEyrHp_wJj17BlJTxq9U,9017
10
+ xarray_annotated/schema/_config.py,sha256=LSuhTkVpk3yPM7xGaOWPqZIlFnDI1MNTSy25V4oiJug,4736
11
+ xarray_annotated/schema/_decorator.py,sha256=s_AlTKIBFNhiBFRC_K6LkkGeXuZMmxMmJNQZ6sKgvY4,5657
12
+ xarray_annotated/temporal/__init__.py,sha256=QyLAb0OQDBdoj-89YQ84Ct5aETufrxfgxBi5c28o2FM,1797
13
+ xarray_annotated/temporal/_annotations.py,sha256=gO0Ne_Ui7LTCyE33cklJPBQY18bBRxhaW3W4okNIceY,5757
14
+ xarray_annotated/temporal/_check.py,sha256=4Xn8YkxkevSFSMXS_Uq_loLpB1UiGu1GgH-YKIoBII0,13253
15
+ xarray_annotated/temporal/_config.py,sha256=GnEkAiRV2QfQO3J776HKTWKJyGigh1mT9Ck0u1SCiU8,6642
16
+ xarray_annotated/temporal/_decorator.py,sha256=t70bNlOkEprCA-8rhy4ZAgb-_kqRzKOMbf4h4df2dm4,5930
17
+ xarray_annotated/units/__init__.py,sha256=rzFW1KdrCydAnul5pQJW4aRpR-BmINF0b7Ul-g0C_Rs,1018
18
+ xarray_annotated/units/_annotations.py,sha256=dfKEzxTJ4s5yaXI8ypT0gQtyIDXZl6yzmi_CeciIzk8,6516
19
+ xarray_annotated/units/_check.py,sha256=IdJT6jZ1JWApNGeMDMw7iyt-ukntJdRCHpKeyoquy5o,25636
20
+ xarray_annotated/units/_config.py,sha256=O0P1RpTIVocDmJinpSa4RMP6ULKWw-GGo0E_ZaGjfSU,8739
21
+ xarray_annotated/units/_decorator.py,sha256=2lo4JjNAE08LemLffDpZp0DhWyKiwbygVWhOzQYCITs,9477
22
+ xarray_annotated/units/_registry.py,sha256=SGzhwLNmCGoR92UobeUpMxKMft8IodyyXWe9h6pZSQQ,3313
23
+ xarray_annotated-0.6.0.dist-info/licenses/LICENSE,sha256=YhhIjDcfuWhp2dZ8e2W8H6k-HAKceHoWiG6zciVguvo,1074
24
+ xarray_annotated-0.6.0.dist-info/WHEEL,sha256=EmLkUISDECbcUx3FMCYOqokNOJqNp2r0d4mJzjErvvs,80
25
+ xarray_annotated-0.6.0.dist-info/METADATA,sha256=lePQQHHeuCZbaWI-CziyicQGSAd6O0uW3tw5Y-D7JAk,6245
26
+ xarray_annotated-0.6.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.12.1
2
+ Generator: uv 0.12.3
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,26 +0,0 @@
1
- xarray_annotated/__init__.py,sha256=1vO0qejeRzcUS-zqDIU1UMkBtd2HnZaIyAZMgzsI2iE,2040
2
- xarray_annotated/_annotations.py,sha256=yRcx9LpLZgmHm_5dg2li-tPEGJLMokujRaIRXCNHZns,4071
3
- xarray_annotated/_config.py,sha256=H0vKNY7VNJ1rw_4Ymzu2vIOGo1luJLTlGp0e6UzhVpk,3423
4
- xarray_annotated/_reader.py,sha256=UBmneo6B8aAeK0xWTVy6zDMdm-PaoDH5Pi7hF28UHKA,4857
5
- xarray_annotated/_writer.py,sha256=BsEUVK3YfZi8QEf8Hz31pYkfO8-WRBP7fcMNbpPCak0,4172
6
- xarray_annotated/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- xarray_annotated/schema/__init__.py,sha256=0i4hIqpHJG_FXGzoymjOqgRmuT_iBJZQNhAAzr_bOPE,1482
8
- xarray_annotated/schema/_annotations.py,sha256=sD-vpRKz_eThak-Uti7lSlRKA4JSnh-9HFbbTzzdbQI,8723
9
- xarray_annotated/schema/_check.py,sha256=oU5Ayx4nDr-VBlnhmj2n46-AEyrHp_wJj17BlJTxq9U,9017
10
- xarray_annotated/schema/_config.py,sha256=0HLAvUIIsZ-eI5XGce-xu8D7SmEO7zdoExQI1jKRB4Q,4736
11
- xarray_annotated/schema/_decorator.py,sha256=joRCMs3gSHEV8sst02topZyxz2acHtYNGjPhm9hQ0Mw,5639
12
- xarray_annotated/temporal/__init__.py,sha256=KW5nbDDe8SFzA7sQfUIPUd9xfbf53fEyJLVwG-sxQnQ,1782
13
- xarray_annotated/temporal/_annotations.py,sha256=gDr3fG0qj3KqgrafR2VWiAWGYsOweUpVoj-Q2pQ5ZYk,5757
14
- xarray_annotated/temporal/_check.py,sha256=4Xn8YkxkevSFSMXS_Uq_loLpB1UiGu1GgH-YKIoBII0,13253
15
- xarray_annotated/temporal/_config.py,sha256=uP3Ywc1PRcqWPRScEQdh3p1OcwU8lIzFCVMXoQyV38g,6642
16
- xarray_annotated/temporal/_decorator.py,sha256=t70bNlOkEprCA-8rhy4ZAgb-_kqRzKOMbf4h4df2dm4,5930
17
- xarray_annotated/units/__init__.py,sha256=rzFW1KdrCydAnul5pQJW4aRpR-BmINF0b7Ul-g0C_Rs,1018
18
- xarray_annotated/units/_annotations.py,sha256=dfKEzxTJ4s5yaXI8ypT0gQtyIDXZl6yzmi_CeciIzk8,6516
19
- xarray_annotated/units/_check.py,sha256=OHl-gemlWVaO_WcwtbDtC5joUgyFaAKTlpRb73md9kw,25590
20
- xarray_annotated/units/_config.py,sha256=8_rnIf5bCTLFxxgApN1QYacEo-0Fcfe32c2bzrLh52M,8739
21
- xarray_annotated/units/_decorator.py,sha256=zoVIPIrChR5g8xPgRRMOXv6tnUyJo2o1F_ZMiDby0Hw,9471
22
- xarray_annotated/units/_registry.py,sha256=SGzhwLNmCGoR92UobeUpMxKMft8IodyyXWe9h6pZSQQ,3313
23
- xarray_annotated-0.5.2.dist-info/licenses/LICENSE,sha256=YhhIjDcfuWhp2dZ8e2W8H6k-HAKceHoWiG6zciVguvo,1074
24
- xarray_annotated-0.5.2.dist-info/WHEEL,sha256=lrO5MD1WVAWzcbNy_L2BwtfPrcM3KfUFpbKYXLkJX4A,80
25
- xarray_annotated-0.5.2.dist-info/METADATA,sha256=qJx0mdO5RheqHZpMz_CRlPhwV2Io-rE8wNKsK8JJiO8,6241
26
- xarray_annotated-0.5.2.dist-info/RECORD,,