cf-xarray 0.9.5__tar.gz → 0.10.1__tar.gz

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.
Files changed (95) hide show
  1. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/.github/workflows/ci.yaml +5 -5
  2. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/.github/workflows/pypi.yaml +2 -2
  3. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/.github/workflows/upstream-dev-ci.yaml +1 -1
  4. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/.pre-commit-config.yaml +8 -20
  5. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/.readthedocs.yml +2 -1
  6. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/CITATION.cff +4 -0
  7. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/PKG-INFO +3 -3
  8. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/__init__.py +1 -1
  9. cf_xarray-0.10.1/cf_xarray/_version.py +1 -0
  10. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/accessor.py +34 -53
  11. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/coding.py +6 -3
  12. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/datasets.py +7 -7
  13. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/formatting.py +2 -2
  14. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/geometry.py +11 -2
  15. cf_xarray-0.10.1/cf_xarray/groupers.py +34 -0
  16. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/options.py +0 -1
  17. cf_xarray-0.10.1/cf_xarray/parametric.py +821 -0
  18. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/scripts/make_doc.py +3 -1
  19. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/sgrid.py +2 -1
  20. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/tests/__init__.py +1 -2
  21. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/tests/test_accessor.py +18 -47
  22. cf_xarray-0.10.1/cf_xarray/tests/test_groupers.py +45 -0
  23. cf_xarray-0.10.1/cf_xarray/tests/test_parametric.py +615 -0
  24. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/utils.py +9 -1
  25. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray.egg-info/PKG-INFO +3 -3
  26. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray.egg-info/SOURCES.txt +4 -0
  27. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray.egg-info/requires.txt +1 -1
  28. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/ci/doc.yml +1 -1
  29. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/api.rst +8 -1
  30. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/bounds.md +1 -1
  31. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/coord_axes.md +2 -2
  32. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/custom-criteria.md +1 -1
  33. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/flags.md +32 -0
  34. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/parametricz.md +1 -1
  35. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/selecting.md +3 -3
  36. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/sgrid_ugrid.md +1 -1
  37. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/units.md +2 -2
  38. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/whats-new.rst +1 -3
  39. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/pyproject.toml +11 -9
  40. cf_xarray-0.9.5/cf_xarray/_version.py +0 -1
  41. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/.binder/environment.yml +0 -0
  42. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/.deepsource.toml +0 -0
  43. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/.github/dependabot.yml +0 -0
  44. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/.github/release.yml +0 -0
  45. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/.github/workflows/parse_logs.py +0 -0
  46. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/.github/workflows/testpypi-release.yaml +0 -0
  47. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/.gitignore +0 -0
  48. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/.tributors +0 -0
  49. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/LICENSE +0 -0
  50. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/README.rst +0 -0
  51. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/criteria.py +0 -0
  52. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/helpers.py +0 -0
  53. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/py.typed +0 -0
  54. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/scripts/print_versions.py +0 -0
  55. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/tests/conftest.py +0 -0
  56. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/tests/test_coding.py +0 -0
  57. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/tests/test_geometry.py +0 -0
  58. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/tests/test_helpers.py +0 -0
  59. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/tests/test_options.py +0 -0
  60. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/tests/test_scripts.py +0 -0
  61. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/tests/test_units.py +0 -0
  62. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray/units.py +0 -0
  63. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray.egg-info/dependency_links.txt +0 -0
  64. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/cf_xarray.egg-info/top_level.txt +0 -0
  65. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/ci/environment-no-optional-deps.yml +0 -0
  66. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/ci/environment.yml +0 -0
  67. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/ci/upstream-dev-env.yml +0 -0
  68. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/codecov.yml +0 -0
  69. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/2D_bounds_averaged.png +0 -0
  70. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/2D_bounds_error.png +0 -0
  71. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/2D_bounds_nonunique.png +0 -0
  72. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/Makefile +0 -0
  73. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/_static/dataset-diagram-logo.tex +0 -0
  74. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/_static/full-logo.png +0 -0
  75. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/_static/logo.png +0 -0
  76. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/_static/logo.svg +0 -0
  77. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/_static/rich-repr-example.png +0 -0
  78. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/_static/style.css +0 -0
  79. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/cartopy_rotated_pole.png +0 -0
  80. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/coding.md +0 -0
  81. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/conf.py +0 -0
  82. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/contributing.rst +0 -0
  83. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/dsg.md +0 -0
  84. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/examples/introduction.ipynb +0 -0
  85. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/faq.md +0 -0
  86. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/geometry.md +0 -0
  87. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/grid_mappings.md +0 -0
  88. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/howtouse.md +0 -0
  89. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/index.rst +0 -0
  90. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/make.bat +0 -0
  91. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/plotting.md +0 -0
  92. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/provenance.md +0 -0
  93. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/quickstart.md +0 -0
  94. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/doc/roadmap.rst +0 -0
  95. {cf_xarray-0.9.5 → cf_xarray-0.10.1}/setup.cfg +0 -0
@@ -36,7 +36,7 @@ jobs:
36
36
  run: |
37
37
  echo "PYTHON_VERSION=${{ matrix.python-version }}" >> $GITHUB_ENV
38
38
  - name: Set up conda environment
39
- uses: mamba-org/setup-micromamba@v1
39
+ uses: mamba-org/setup-micromamba@v2
40
40
  with:
41
41
  environment-file: ci/environment.yml
42
42
  environment-name: cf_xarray_test
@@ -50,7 +50,7 @@ jobs:
50
50
  run: |
51
51
  pytest -n auto --cov=./ --cov-report=xml
52
52
  - name: Upload code coverage to Codecov
53
- uses: codecov/codecov-action@v4.5.0
53
+ uses: codecov/codecov-action@v5.4.0
54
54
  with:
55
55
  file: ./coverage.xml
56
56
  flags: unittests
@@ -70,7 +70,7 @@ jobs:
70
70
  # need to fetch all tags to get a correct version
71
71
  fetch-depth: 0 # fetch all branches and tags
72
72
  - name: Set up conda environment
73
- uses: mamba-org/setup-micromamba@v1
73
+ uses: mamba-org/setup-micromamba@v2
74
74
  with:
75
75
  environment-file: ci/environment-no-optional-deps.yml
76
76
  environment-name: cf_xarray_test
@@ -97,7 +97,7 @@ jobs:
97
97
  with:
98
98
  fetch-depth: 0 # Fetch all history for all branches and tags.
99
99
  - name: Set up conda environment
100
- uses: mamba-org/setup-micromamba@v1
100
+ uses: mamba-org/setup-micromamba@v2
101
101
  with:
102
102
  environment-file: ci/environment.yml
103
103
  environment-name: cf_xarray_test
@@ -114,7 +114,7 @@ jobs:
114
114
  run: |
115
115
  python -m mypy --install-types --non-interactive --cobertura-xml-report mypy_report cf_xarray/
116
116
  - name: Upload mypy coverage to Codecov
117
- uses: codecov/codecov-action@v4.5.0
117
+ uses: codecov/codecov-action@v5.4.0
118
118
  with:
119
119
  file: mypy_report/cobertura.xml
120
120
  flags: mypy
@@ -72,7 +72,7 @@ jobs:
72
72
 
73
73
  - name: Publish package to TestPyPI
74
74
  if: github.event_name == 'push'
75
- uses: pypa/gh-action-pypi-publish@v1.9.0
75
+ uses: pypa/gh-action-pypi-publish@v1.12.4
76
76
  with:
77
77
  password: ${{ secrets.TESTPYPI_TOKEN }}
78
78
  repository_url: https://test.pypi.org/legacy/
@@ -96,6 +96,6 @@ jobs:
96
96
  name: releases
97
97
  path: dist
98
98
  - name: Publish package to PyPI
99
- uses: pypa/gh-action-pypi-publish@v1.9.0
99
+ uses: pypa/gh-action-pypi-publish@v1.12.4
100
100
  with:
101
101
  verbose: true
@@ -38,7 +38,7 @@ jobs:
38
38
  run: |
39
39
  echo "PYTHON_VERSION=${{ matrix.python-version }}" >> $GITHUB_ENV
40
40
  - name: Set up conda environment
41
- uses: mamba-org/setup-micromamba@v1
41
+ uses: mamba-org/setup-micromamba@v2
42
42
  with:
43
43
  environment-file: ci/upstream-dev-env.yml
44
44
  environment-name: cf_xarray_test
@@ -3,22 +3,18 @@ ci:
3
3
 
4
4
  repos:
5
5
  - repo: https://github.com/asottile/pyupgrade
6
- rev: v3.17.0
6
+ rev: v3.19.1
7
7
  hooks:
8
8
  - id: pyupgrade
9
9
  args: ["--py310-plus"]
10
10
 
11
11
  - repo: https://github.com/astral-sh/ruff-pre-commit
12
12
  # Ruff version.
13
- rev: 'v0.6.1'
13
+ rev: 'v0.9.3'
14
14
  hooks:
15
15
  - id: ruff
16
- args: ["--show-fixes", "--fix"]
17
-
18
- - repo: https://github.com/psf/black-pre-commit-mirror
19
- rev: 24.8.0
20
- hooks:
21
- - id: black
16
+ args: ["--fix", "--show-fixes"]
17
+ - id: ruff-format
22
18
 
23
19
  - repo: https://github.com/rstcheck/rstcheck
24
20
  rev: v6.2.4
@@ -28,7 +24,7 @@ repos:
28
24
  args: ['--config', 'pyproject.toml']
29
25
 
30
26
  - repo: https://github.com/executablebooks/mdformat
31
- rev: 0.7.17
27
+ rev: 0.7.21
32
28
  hooks:
33
29
  - id: mdformat
34
30
  additional_dependencies:
@@ -36,10 +32,8 @@ repos:
36
32
  - mdformat-myst
37
33
 
38
34
  - repo: https://github.com/nbQA-dev/nbQA
39
- rev: 1.8.7
35
+ rev: 1.9.1
40
36
  hooks:
41
- - id: nbqa-black
42
- - id: nbqa-ruff
43
37
  - id: nbqa
44
38
  entry: nbqa mdformat
45
39
  name: nbqa-mdformat
@@ -47,7 +41,7 @@ repos:
47
41
  additional_dependencies: [mdformat==0.7.17]
48
42
 
49
43
  - repo: https://github.com/pre-commit/pre-commit-hooks
50
- rev: v4.6.0
44
+ rev: v5.0.0
51
45
  hooks:
52
46
  - id: trailing-whitespace
53
47
  - id: end-of-file-fixer
@@ -55,19 +49,13 @@ repos:
55
49
  - id: check-yaml
56
50
  - id: debug-statements
57
51
 
58
- - repo: https://github.com/keewis/blackdoc
59
- rev: v0.3.9
60
- hooks:
61
- - id: blackdoc
62
- files: .+\.py$
63
-
64
52
  - repo: https://github.com/citation-file-format/cff-converter-python
65
53
  rev: "44e8fc9"
66
54
  hooks:
67
55
  - id: validate-cff
68
56
 
69
57
  - repo: https://github.com/abravalheri/validate-pyproject
70
- rev: v0.19
58
+ rev: v0.23
71
59
  hooks:
72
60
  - id: validate-pyproject
73
61
 
@@ -1,5 +1,6 @@
1
1
  version: 2
2
-
2
+ sphinx:
3
+ configuration: doc/conf.py
3
4
  build:
4
5
  os: ubuntu-lts-latest
5
6
  tools:
@@ -83,6 +83,10 @@ authors:
83
83
  - family-names: Haëck
84
84
  given-names: Clément
85
85
  affiliation: Laboratoire d'Océanographie et du Climat (LOCEAN), Paris
86
+ - family-names: Boutte
87
+ given-names: Jason
88
+ orcid: 'https://orcid.org/0009-0009-3996-3772'
89
+ affiliation: Lawrence Livermore National Laboratory
86
90
  identifiers:
87
91
  - type: doi
88
92
  value: 10.5281/zenodo.4749735
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: cf_xarray
3
- Version: 0.9.5
3
+ Version: 0.10.1
4
4
  Summary: A convenience wrapper for using CF attributes on xarray objects
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -220,7 +220,7 @@ Classifier: Programming Language :: Python :: 3.12
220
220
  Requires-Python: >=3.10
221
221
  Description-Content-Type: text/x-rst
222
222
  License-File: LICENSE
223
- Requires-Dist: xarray
223
+ Requires-Dist: xarray>=2022.03.0
224
224
  Provides-Extra: all
225
225
  Requires-Dist: matplotlib; extra == "all"
226
226
  Requires-Dist: pint!=0.24.0,>=0.18; extra == "all"
@@ -9,6 +9,6 @@ from .helpers import bounds_to_vertices, vertices_to_bounds # noqa
9
9
  from .options import set_options # noqa
10
10
  from .utils import _get_version
11
11
 
12
- from . import geometry # noqa
12
+ from . import geometry, groupers # noqa
13
13
 
14
14
  __version__ = _get_version()
@@ -0,0 +1 @@
1
+ __version__ = "0.10.1"
@@ -18,7 +18,6 @@ from typing import (
18
18
  Any,
19
19
  Literal,
20
20
  TypeVar,
21
- Union,
22
21
  cast,
23
22
  overload,
24
23
  )
@@ -31,7 +30,7 @@ from xarray.core.resample import Resample
31
30
  from xarray.core.rolling import Coarsen, Rolling
32
31
  from xarray.core.weighted import Weighted
33
32
 
34
- from . import sgrid
33
+ from . import parametric, sgrid
35
34
  from .criteria import (
36
35
  _DSG_ROLES,
37
36
  _GEOMETRY_TYPES,
@@ -89,7 +88,7 @@ ATTRS["time"] = ATTRS["T"]
89
88
  ATTRS["vertical"] = ATTRS["Z"]
90
89
 
91
90
  # Type for Mapper functions
92
- Mapper = Callable[[Union[DataArray, Dataset], Hashable], list[Hashable]]
91
+ Mapper = Callable[[DataArray | Dataset, Hashable], list[Hashable]]
93
92
 
94
93
  # Type for decorators
95
94
  F = TypeVar("F", bound=Callable[..., Any])
@@ -538,7 +537,7 @@ def _get_coords(obj: DataArray | Dataset, key: Hashable) -> list[Hashable]:
538
537
  def _variables(func: F) -> F:
539
538
  @functools.wraps(func)
540
539
  def wrapper(obj: DataArray | Dataset, key: Hashable) -> list[DataArray]:
541
- return [obj[k] for k in func(obj, key)] # type: ignore[misc]
540
+ return [obj[k] for k in func(obj, key)]
542
541
 
543
542
  return cast(F, wrapper)
544
543
 
@@ -681,7 +680,7 @@ def _getattr(
681
680
  None,
682
681
  ):
683
682
  raise AttributeError(
684
- f"{obj.__class__.__name__+'.cf'!r} object has no attribute {attr!r}"
683
+ f"{obj.__class__.__name__ + '.cf'!r} object has no attribute {attr!r}"
685
684
  ) from None
686
685
  raise AttributeError(
687
686
  f"{attr!r} is not a valid attribute on the underlying xarray object."
@@ -1150,9 +1149,10 @@ def create_flag_dict(da) -> Mapping[Hashable, FlagParam]:
1150
1149
  )
1151
1150
 
1152
1151
  flag_params = tuple(
1153
- FlagParam(mask, value) for mask, value in zip(flag_masks, flag_values)
1152
+ FlagParam(mask, value)
1153
+ for mask, value in zip(flag_masks, flag_values, strict=False)
1154
1154
  )
1155
- return dict(zip(flag_meanings, flag_params))
1155
+ return dict(zip(flag_meanings, flag_params, strict=False))
1156
1156
 
1157
1157
 
1158
1158
  class CFAccessor:
@@ -1368,13 +1368,17 @@ class CFAccessor:
1368
1368
  kwargs: dict[str, Any] | None = None,
1369
1369
  ):
1370
1370
  if coords is not None:
1371
- if isinstance(coords, (Hashable, DataArray)):
1371
+ if isinstance(coords, Hashable | DataArray):
1372
1372
  coords_iter: Iterable[Hashable | DataArray] = [coords]
1373
1373
  else:
1374
1374
  coords_iter = coords
1375
1375
  coords = [
1376
1376
  apply_mapper(
1377
- [_single(_get_coords)], self._obj, v, error=False, default=[v] # type: ignore[arg-type]
1377
+ [_single(_get_coords)], # type:ignore[arg-type]
1378
+ self._obj,
1379
+ v,
1380
+ error=False,
1381
+ default=[v],
1378
1382
  )[0]
1379
1383
  for v in coords_iter
1380
1384
  ]
@@ -1385,7 +1389,11 @@ class CFAccessor:
1385
1389
  reduce_dims_iter = list(reduce_dims)
1386
1390
  reduce_dims = [
1387
1391
  apply_mapper(
1388
- [_single(_get_dims)], self._obj, v, error=False, default=[v] # type: ignore[arg-type]
1392
+ [_single(_get_dims)], # type:ignore[arg-type]
1393
+ self._obj,
1394
+ v,
1395
+ error=False,
1396
+ default=[v],
1389
1397
  )[0]
1390
1398
  for v in reduce_dims_iter
1391
1399
  ]
@@ -2754,18 +2762,13 @@ class CFDatasetAccessor(CFAccessor):
2754
2762
  """
2755
2763
  ds = self._obj
2756
2764
 
2757
- requirements = {
2758
- "ocean_s_coordinate_g1": {"depth_c", "depth", "s", "C", "eta"},
2759
- "ocean_s_coordinate_g2": {"depth_c", "depth", "s", "C", "eta"},
2760
- "ocean_sigma_coordinate": {"sigma", "eta", "depth"},
2761
- }
2762
-
2763
2765
  allterms = self.formula_terms
2766
+
2764
2767
  for dim in allterms:
2765
2768
  if prefix is None:
2766
- assert (
2767
- outnames is not None
2768
- ), "if prefix is None, outnames must be provided"
2769
+ assert outnames is not None, (
2770
+ "if prefix is None, outnames must be provided"
2771
+ )
2769
2772
  # set outnames here
2770
2773
  try:
2771
2774
  zname = outnames[dim]
@@ -2782,6 +2785,7 @@ class CFDatasetAccessor(CFAccessor):
2782
2785
  suffix = dim.split("_")
2783
2786
  zname = f"{prefix}_" + "_".join(suffix[1:])
2784
2787
 
2788
+ # never touched, if standard name is missing it's not included in allterms
2785
2789
  if "standard_name" not in ds[dim].attrs:
2786
2790
  continue
2787
2791
  stdname = ds[dim].attrs["standard_name"]
@@ -2790,46 +2794,23 @@ class CFDatasetAccessor(CFAccessor):
2790
2794
  terms = {}
2791
2795
  for key, value in allterms[dim].items():
2792
2796
  if value not in ds:
2797
+ # is this ever hit, if variable is missing it's missing in decoded allterms
2793
2798
  raise KeyError(
2794
2799
  f"Variable {value!r} is required to decode coordinate for {dim!r}"
2795
2800
  " but it is absent in the Dataset."
2796
2801
  )
2797
- terms[key] = ds[value]
2798
-
2799
- absent_terms = requirements[stdname] - set(terms)
2800
- if absent_terms:
2801
- raise KeyError(f"Required terms {absent_terms} absent in dataset.")
2802
-
2803
- if stdname == "ocean_s_coordinate_g1":
2804
- # S(k,j,i) = depth_c * s(k) + (depth(j,i) - depth_c) * C(k)
2805
- S = (
2806
- terms["depth_c"] * terms["s"]
2807
- + (terms["depth"] - terms["depth_c"]) * terms["C"]
2808
- )
2809
-
2810
- # z(n,k,j,i) = S(k,j,i) + eta(n,j,i) * (1 + S(k,j,i) / depth(j,i))
2811
- ztemp = S + terms["eta"] * (1 + S / terms["depth"])
2802
+ # keys should be case insensitive
2803
+ terms[key.lower()] = ds[value]
2812
2804
 
2813
- elif stdname == "ocean_s_coordinate_g2":
2814
- # make sure all necessary terms are present in terms
2815
- # (depth_c * s(k) + depth(j,i) * C(k)) / (depth_c + depth(j,i))
2816
- S = (terms["depth_c"] * terms["s"] + terms["depth"] * terms["C"]) / (
2817
- terms["depth_c"] + terms["depth"]
2818
- )
2819
-
2820
- # z(n,k,j,i) = eta(n,j,i) + (eta(n,j,i) + depth(j,i)) * S(k,j,i)
2821
- ztemp = terms["eta"] + (terms["eta"] + terms["depth"]) * S
2822
-
2823
- elif stdname == "ocean_sigma_coordinate":
2824
- # z(n,k,j,i) = eta(n,j,i) + sigma(k)*(depth(j,i)+eta(n,j,i))
2825
- ztemp = terms["eta"] + terms["sigma"] * (terms["depth"] + terms["eta"])
2826
-
2827
- else:
2805
+ try:
2806
+ transform = parametric.TRANSFORM_FROM_STDNAME[stdname]
2807
+ except KeyError:
2808
+ # Should occur since stdname is check before
2828
2809
  raise NotImplementedError(
2829
- f"Coordinate function for {stdname!r} not implemented yet. Contributions welcome!"
2830
- )
2810
+ f"Coordinate function for {stdname!r} not implmented yet. Contributions welcome!"
2811
+ ) from None
2831
2812
 
2832
- ds.coords[zname] = ztemp
2813
+ ds.coords[zname] = transform.from_terms(terms)
2833
2814
 
2834
2815
 
2835
2816
  @xr.register_dataarray_accessor("cf")
@@ -3024,7 +3005,7 @@ class CFDataArrayAccessor(CFAccessor):
3024
3005
  x = self._obj.astype("i")
3025
3006
  bit_comp = x & bit_mask
3026
3007
 
3027
- for i, (flag, value) in enumerate(zip(flags_reduced, values)):
3008
+ for i, (flag, value) in enumerate(zip(flags_reduced, values, strict=False)):
3028
3009
  bit = bit_comp.isel(_mask=i)
3029
3010
  if value is not None:
3030
3011
  out[flag] = bit == value
@@ -46,7 +46,7 @@ def encode_multi_index_as_compress(ds, idxnames=None):
46
46
  encoded = ds.reset_index(idxnames)
47
47
  for idxname in idxnames:
48
48
  mindex = ds.indexes[idxname]
49
- coords = dict(zip(mindex.names, mindex.levels))
49
+ coords = dict(zip(mindex.names, mindex.levels, strict=False))
50
50
  encoded.update(coords)
51
51
  for c in coords:
52
52
  encoded[c].attrs = ds[c].attrs
@@ -112,13 +112,16 @@ def decode_compress_to_multi_index(encoded, idxnames=None):
112
112
 
113
113
  variables = {
114
114
  dim: encoded[dim].isel({dim: xr.Variable(data=index, dims=idxname)})
115
- for dim, index in zip(names, indices)
115
+ for dim, index in zip(names, indices, strict=False)
116
116
  }
117
117
  decoded = decoded.assign_coords(variables).set_xindex(
118
118
  names, PandasMultiIndex
119
119
  )
120
120
  except ImportError:
121
- arrays = [encoded[dim].data[index] for dim, index in zip(names, indices)]
121
+ arrays = [
122
+ encoded[dim].data[index]
123
+ for dim, index in zip(names, indices, strict=False)
124
+ ]
122
125
  mindex = pd.MultiIndex.from_arrays(arrays, names=names)
123
126
  decoded.coords[idxname] = mindex
124
127
 
@@ -21,15 +21,14 @@ ds_with_tuple = airds.rename({"air": (1, 2, 3)})
21
21
 
22
22
  # POM dataset
23
23
  pomds = xr.Dataset()
24
+ # fmt: off
24
25
  pomds["sigma"] = (
25
- # fmt: off
26
26
  "sigma",
27
27
  [-0.983333, -0.95 , -0.916667, -0.883333, -0.85 , -0.816667,
28
28
  -0.783333, -0.75 , -0.716667, -0.683333, -0.65 , -0.616667,
29
29
  -0.583333, -0.55 , -0.516667, -0.483333, -0.45 , -0.416667,
30
30
  -0.383333, -0.35 , -0.316667, -0.283333, -0.25 , -0.216667,
31
31
  -0.183333, -0.15 , -0.116667, -0.083333, -0.05 , -0.016667],
32
- # fmt: on
33
32
  {
34
33
  "units": "sigma_level",
35
34
  "long_name": "Sigma Stretched Vertical Coordinate at Nodes",
@@ -38,6 +37,7 @@ pomds["sigma"] = (
38
37
  "formula_terms": "sigma: sigma eta: zeta depth: depth",
39
38
  }
40
39
  )
40
+ # fmt: on
41
41
  pomds["depth"] = 175.0
42
42
  pomds["zeta"] = ("ocean_time", [-0.155356, -0.127435])
43
43
 
@@ -109,15 +109,14 @@ multiple["v2"] = (("x2", "y2"), np.ones((10, 5)) * 15)
109
109
 
110
110
 
111
111
  romsds = xr.Dataset()
112
+ # fmt: off
112
113
  romsds["s_rho"] = (
113
- # fmt: off
114
114
  "s_rho",
115
115
  [-0.983333, -0.95 , -0.916667, -0.883333, -0.85 , -0.816667,
116
116
  -0.783333, -0.75 , -0.716667, -0.683333, -0.65 , -0.616667,
117
117
  -0.583333, -0.55 , -0.516667, -0.483333, -0.45 , -0.416667,
118
118
  -0.383333, -0.35 , -0.316667, -0.283333, -0.25 , -0.216667,
119
119
  -0.183333, -0.15 , -0.116667, -0.083333, -0.05 , -0.016667],
120
- # fmt: on
121
120
  {
122
121
  "long_name": "S-coordinate at RHO-points",
123
122
  "valid_min": -1.0,
@@ -125,13 +124,14 @@ romsds["s_rho"] = (
125
124
  "standard_name": "ocean_s_coordinate_g2",
126
125
  "formula_terms": "s: s_rho C: Cs_r eta: zeta depth: h depth_c: hc",
127
126
  "field": "s_rho, scalar",
128
- }
127
+ },
129
128
  )
129
+ # fmt: on
130
130
  romsds.coords["hc"] = 20.0
131
131
  romsds.coords["h"] = 603.9
132
132
  romsds.coords["Vtransform"] = 2.0
133
+ # fmt: off
133
134
  romsds.coords["Cs_r"] = (
134
- # fmt: off
135
135
  "s_rho",
136
136
  [-9.33010396e-01, -8.09234736e-01, -6.98779853e-01, -6.01008926e-01,
137
137
  -5.15058562e-01, -4.39938913e-01, -3.74609181e-01, -3.18031817e-01,
@@ -141,8 +141,8 @@ romsds.coords["Cs_r"] = (
141
141
  -2.53860004e-02, -1.95414261e-02, -1.46880431e-02, -1.06952600e-02,
142
142
  -7.45515186e-03, -4.87981407e-03, -2.89916971e-03, -1.45919898e-03,
143
143
  -5.20560097e-04, -5.75774004e-05],
144
- # fmt: on
145
144
  )
145
+ # fmt: on
146
146
  romsds["zeta"] = ("ocean_time", [-0.155356, -0.127435])
147
147
  romsds["temp"] = (
148
148
  ("ocean_time", "s_rho"),
@@ -268,14 +268,14 @@ def _format_flags(accessor, rich):
268
268
  table.add_column("Value", justify="right")
269
269
  table.add_column("Bits", justify="center")
270
270
 
271
- for val, bit, key in zip(value_text, bit_text, flag_dict):
271
+ for val, bit, key in zip(value_text, bit_text, flag_dict, strict=False):
272
272
  table.add_row(_format_cf_name(key, rich), val, bit)
273
273
 
274
274
  return table
275
275
 
276
276
  else:
277
277
  rows = []
278
- for val, bit, key in zip(value_text, bit_text, flag_dict):
278
+ for val, bit, key in zip(value_text, bit_text, flag_dict, strict=False):
279
279
  rows.append(
280
280
  f"{TAB}{_format_cf_name(key, rich):>{key_width}}: {TAB} {val} {bit}"
281
281
  )
@@ -4,6 +4,7 @@ import copy
4
4
  from collections import ChainMap
5
5
  from collections.abc import Hashable, Sequence
6
6
  from dataclasses import dataclass
7
+ from typing import TYPE_CHECKING
7
8
 
8
9
  import numpy as np
9
10
  import pandas as pd
@@ -21,6 +22,9 @@ __all__ = [
21
22
  ]
22
23
 
23
24
 
25
+ if TYPE_CHECKING:
26
+ from shapely import MultiPoint, Point
27
+
24
28
  # Useful convention language:
25
29
  # 1. Whether linked to normal CF space-time coordinates with a nodes attribute or not, inclusion of such coordinates is
26
30
  # recommended to maintain backward compatibility with software that has not implemented geometry capabilities.
@@ -324,7 +328,7 @@ def encode_geometries(ds: xr.Dataset):
324
328
  # TODO: this is incomplete. It works for vector data cubes where one of the geometry vars
325
329
  # is a dimension coordinate.
326
330
  if name in var.dims:
327
- var = var.copy()
331
+ var = var.copy(deep=False)
328
332
  var._attrs = copy.deepcopy(var._attrs)
329
333
  var.attrs["geometry"] = container_name
330
334
  # The grid_mapping and coordinates attributes can be carried by the geometry container
@@ -547,7 +551,11 @@ def cf_to_shapely(ds: xr.Dataset, *, container: Hashable = GEOMETRY_CONTAINER_NA
547
551
  return geometries.rename("geometry")
548
552
 
549
553
 
550
- def points_to_cf(pts: xr.DataArray | Sequence, *, names: GeometryNames | None = None):
554
+ def points_to_cf(
555
+ pts: xr.DataArray | Sequence[Point | MultiPoint],
556
+ *,
557
+ names: GeometryNames | None = None,
558
+ ):
551
559
  """Get a list of points (shapely.geometry.[Multi]Point) and return a CF-compliant geometry dataset.
552
560
 
553
561
  Parameters
@@ -563,6 +571,7 @@ def points_to_cf(pts: xr.DataArray | Sequence, *, names: GeometryNames | None =
563
571
  """
564
572
  from shapely.geometry import MultiPoint
565
573
 
574
+ pts_: Sequence[Point | MultiPoint]
566
575
  if isinstance(pts, xr.DataArray):
567
576
  # TODO: Fix this hardcoding
568
577
  if pts.ndim != 1:
@@ -0,0 +1,34 @@
1
+ from dataclasses import dataclass
2
+
3
+ import numpy as np
4
+ import pandas as pd
5
+ from xarray.groupers import EncodedGroups, UniqueGrouper
6
+
7
+
8
+ @dataclass
9
+ class FlagGrouper(UniqueGrouper):
10
+ def factorize(self, group) -> EncodedGroups:
11
+ if "flag_values" not in group.attrs or "flag_meanings" not in group.attrs:
12
+ raise ValueError(
13
+ "FlagGrouper can only be used with flag variables that have"
14
+ "`flag_values` and `flag_meanings` specified in attrs."
15
+ )
16
+
17
+ values = np.array(group.attrs["flag_values"])
18
+ full_index = pd.Index(group.attrs["flag_meanings"].split(" "))
19
+
20
+ self.labels = values
21
+
22
+ # TODO: we could optimize here, since `group` is already factorized,
23
+ # but there are subtleties. For example, the attrs must be up to date,
24
+ # any value that is not in flag_values will cause an error, etc.
25
+ ret = super().factorize(group)
26
+
27
+ ret.codes.attrs.pop("flag_values")
28
+ ret.codes.attrs.pop("flag_meanings")
29
+
30
+ return EncodedGroups(
31
+ codes=ret.codes,
32
+ full_index=full_index,
33
+ group_indices=ret.group_indices,
34
+ )
@@ -38,7 +38,6 @@ class set_options: # numpydoc ignore=PR01,PR02
38
38
  >>> ds = xr.Dataset({"elev": np.arange(1000)})
39
39
  >>> with cf_xarray.set_options(custom_criteria=my_custom_criteria):
40
40
  ... xr.testing.assert_identical(ds["elev"], ds.cf["ssh"])
41
- ...
42
41
 
43
42
  Or to set global options:
44
43