cf-xarray 0.9.2__tar.gz → 0.9.3__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 (90) hide show
  1. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.github/workflows/ci.yaml +2 -2
  2. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.github/workflows/pypi.yaml +2 -2
  3. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.pre-commit-config.yaml +5 -5
  4. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/PKG-INFO +2 -2
  5. cf_xarray-0.9.3/cf_xarray/_version.py +1 -0
  6. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/geometry.py +49 -9
  7. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/test_geometry.py +7 -7
  8. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/test_units.py +5 -0
  9. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/units.py +46 -51
  10. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray.egg-info/PKG-INFO +2 -2
  11. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray.egg-info/requires.txt +1 -1
  12. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/units.md +2 -2
  13. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/pyproject.toml +1 -1
  14. cf_xarray-0.9.2/cf_xarray/_version.py +0 -1
  15. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.binder/environment.yml +0 -0
  16. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.deepsource.toml +0 -0
  17. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.github/dependabot.yml +0 -0
  18. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.github/workflows/parse_logs.py +0 -0
  19. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.github/workflows/testpypi-release.yaml +0 -0
  20. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.github/workflows/upstream-dev-ci.yaml +0 -0
  21. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.gitignore +0 -0
  22. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.readthedocs.yml +0 -0
  23. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.tributors +0 -0
  24. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/CITATION.cff +0 -0
  25. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/LICENSE +0 -0
  26. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/README.rst +0 -0
  27. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/__init__.py +0 -0
  28. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/accessor.py +0 -0
  29. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/coding.py +0 -0
  30. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/criteria.py +0 -0
  31. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/datasets.py +0 -0
  32. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/formatting.py +0 -0
  33. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/helpers.py +0 -0
  34. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/options.py +0 -0
  35. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/py.typed +0 -0
  36. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/scripts/make_doc.py +0 -0
  37. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/scripts/print_versions.py +0 -0
  38. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/sgrid.py +0 -0
  39. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/__init__.py +0 -0
  40. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/conftest.py +0 -0
  41. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/test_accessor.py +0 -0
  42. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/test_coding.py +0 -0
  43. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/test_helpers.py +0 -0
  44. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/test_options.py +0 -0
  45. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/test_scripts.py +0 -0
  46. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/utils.py +0 -0
  47. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray.egg-info/SOURCES.txt +0 -0
  48. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray.egg-info/dependency_links.txt +0 -0
  49. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray.egg-info/top_level.txt +0 -0
  50. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/ci/doc.yml +0 -0
  51. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/ci/environment-no-optional-deps.yml +0 -0
  52. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/ci/environment.yml +0 -0
  53. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/ci/upstream-dev-env.yml +0 -0
  54. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/codecov.yml +0 -0
  55. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/2D_bounds_averaged.png +0 -0
  56. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/2D_bounds_error.png +0 -0
  57. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/2D_bounds_nonunique.png +0 -0
  58. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/Makefile +0 -0
  59. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/_static/dataset-diagram-logo.tex +0 -0
  60. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/_static/full-logo.png +0 -0
  61. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/_static/logo.png +0 -0
  62. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/_static/logo.svg +0 -0
  63. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/_static/rich-repr-example.png +0 -0
  64. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/_static/style.css +0 -0
  65. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/api.rst +0 -0
  66. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/bounds.md +0 -0
  67. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/cartopy_rotated_pole.png +0 -0
  68. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/coding.md +0 -0
  69. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/conf.py +0 -0
  70. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/contributing.rst +0 -0
  71. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/coord_axes.md +0 -0
  72. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/custom-criteria.md +0 -0
  73. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/dsg.md +0 -0
  74. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/examples/introduction.ipynb +0 -0
  75. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/faq.md +0 -0
  76. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/flags.md +0 -0
  77. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/geometry.md +0 -0
  78. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/grid_mappings.md +0 -0
  79. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/howtouse.md +0 -0
  80. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/index.rst +0 -0
  81. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/make.bat +0 -0
  82. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/parametricz.md +0 -0
  83. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/plotting.md +0 -0
  84. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/provenance.md +0 -0
  85. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/quickstart.md +0 -0
  86. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/roadmap.rst +0 -0
  87. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/selecting.md +0 -0
  88. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/sgrid_ugrid.md +0 -0
  89. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/whats-new.rst +0 -0
  90. {cf_xarray-0.9.2 → cf_xarray-0.9.3}/setup.cfg +0 -0
@@ -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.4.1
53
+ uses: codecov/codecov-action@v4.5.0
54
54
  with:
55
55
  file: ./coverage.xml
56
56
  flags: unittests
@@ -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.4.1
117
+ uses: codecov/codecov-action@v4.5.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.8.14
75
+ uses: pypa/gh-action-pypi-publish@v1.9.0
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.8.14
99
+ uses: pypa/gh-action-pypi-publish@v1.9.0
100
100
  with:
101
101
  verbose: true
@@ -3,20 +3,20 @@ ci:
3
3
 
4
4
  repos:
5
5
  - repo: https://github.com/asottile/pyupgrade
6
- rev: v3.15.2
6
+ rev: v3.16.0
7
7
  hooks:
8
8
  - id: pyupgrade
9
9
  args: ["--py39-plus"]
10
10
 
11
11
  - repo: https://github.com/astral-sh/ruff-pre-commit
12
12
  # Ruff version.
13
- rev: 'v0.3.5'
13
+ rev: 'v0.5.0'
14
14
  hooks:
15
15
  - id: ruff
16
16
  args: ["--show-fixes", "--fix"]
17
17
 
18
18
  - repo: https://github.com/psf/black-pre-commit-mirror
19
- rev: 24.3.0
19
+ rev: 24.4.2
20
20
  hooks:
21
21
  - id: black
22
22
 
@@ -47,7 +47,7 @@ repos:
47
47
  additional_dependencies: [mdformat==0.7.17]
48
48
 
49
49
  - repo: https://github.com/pre-commit/pre-commit-hooks
50
- rev: v4.5.0
50
+ rev: v4.6.0
51
51
  hooks:
52
52
  - id: trailing-whitespace
53
53
  - id: end-of-file-fixer
@@ -67,7 +67,7 @@ repos:
67
67
  - id: validate-cff
68
68
 
69
69
  - repo: https://github.com/abravalheri/validate-pyproject
70
- rev: v0.16
70
+ rev: v0.18
71
71
  hooks:
72
72
  - id: validate-pyproject
73
73
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cf_xarray
3
- Version: 0.9.2
3
+ Version: 0.9.3
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
@@ -224,7 +224,7 @@ License-File: LICENSE
224
224
  Requires-Dist: xarray
225
225
  Provides-Extra: all
226
226
  Requires-Dist: matplotlib; extra == "all"
227
- Requires-Dist: pint; extra == "all"
227
+ Requires-Dist: pint!=0.24.0,>=0.18; extra == "all"
228
228
  Requires-Dist: shapely; extra == "all"
229
229
  Requires-Dist: regex; extra == "all"
230
230
  Requires-Dist: rich; extra == "all"
@@ -0,0 +1 @@
1
+ __version__ = "0.9.3"
@@ -17,6 +17,19 @@ __all__ = [
17
17
  ]
18
18
 
19
19
 
20
+ # Useful convention language:
21
+ # 1. Whether linked to normal CF space-time coordinates with a nodes attribute or not, inclusion of such coordinates is
22
+ # recommended to maintain backward compatibility with software that has not implemented geometry capabilities.
23
+ # 2. The geometry node coordinate variables must each have an axis attribute whose allowable values are X, Y, and Z.
24
+ # 3. If a coordinates attribute is carried by the geometry container variable or its parent data variable, then those coordinate variables
25
+ # that have a meaningful correspondence with node coordinates are indicated as such by a nodes attribute that names the corresponding node
26
+ # coordinates, but only if the grid_mapping associated the geometry node variables is the same as that of the coordinate variables.
27
+ # If a different grid mapping is used, then the provided coordinates must not have the nodes attribute.
28
+ #
29
+ # Interpretation:
30
+ # 1. node coordinates are exact; the 'normal' coordinates are a reasonable value to use, if you do not know how to interpret the nodes.
31
+
32
+
20
33
  def decode_geometries(encoded: xr.Dataset) -> xr.Dataset:
21
34
  """
22
35
  Decode CF encoded geometries to a numpy object array containing shapely geometries.
@@ -282,6 +295,14 @@ def shapely_to_cf(geometries: xr.DataArray | Sequence, grid_mapping: str | None
282
295
  ----------
283
296
  Please refer to the CF conventions document: http://cfconventions.org/Data/cf-conventions/cf-conventions-1.8/cf-conventions.html#geometries
284
297
  """
298
+
299
+ if isinstance(geometries, xr.DataArray) and grid_mapping is not None:
300
+ raise DeprecationWarning(
301
+ "Explicitly passing `grid_mapping` with DataArray of geometries is deprecated. "
302
+ "Please set a `grid_mapping` attribute on `geometries`, ",
303
+ "and set the grid mapping variable as a coordinate",
304
+ )
305
+
285
306
  # Get all types to call the appropriate translation function.
286
307
  types = {
287
308
  geom.item().geom_type if isinstance(geom, xr.DataArray) else geom.geom_type
@@ -300,19 +321,38 @@ def shapely_to_cf(geometries: xr.DataArray | Sequence, grid_mapping: str | None
300
321
 
301
322
  ds[GEOMETRY_CONTAINER_NAME].attrs.update(coordinates="crd_x crd_y")
302
323
 
324
+ if (
325
+ grid_mapping is None
326
+ and isinstance(geometries, xr.DataArray)
327
+ and (grid_mapping_varname := geometries.attrs.get("grid_mapping"))
328
+ ):
329
+ if grid_mapping_varname in geometries.coords:
330
+ grid_mapping = geometries.coords[grid_mapping_varname].attrs[
331
+ "grid_mapping_name"
332
+ ]
333
+ for name_ in ["x", "y", "crd_x", "crd_y"]:
334
+ ds[name_].attrs["grid_mapping"] = grid_mapping_varname
335
+
303
336
  # Special treatment of selected grid mappings
304
- if grid_mapping == "longitude_latitude":
305
- # Special case for longitude_latitude grid mapping
337
+ if grid_mapping in ["latitude_longitude", "rotated_latitude_longitude"]:
338
+ # Special case for longitude_latitude type grid mappings
306
339
  ds = ds.rename(crd_x="lon", crd_y="lat")
307
- ds.lon.attrs.update(units="degrees_east", standard_name="longitude")
308
- ds.lat.attrs.update(units="degrees_north", standard_name="latitude")
340
+ if grid_mapping == "latitude_longitude":
341
+ ds.lon.attrs.update(units="degrees_east", standard_name="longitude")
342
+ ds.x.attrs.update(units="degrees_east", standard_name="longitude")
343
+ ds.lat.attrs.update(units="degrees_north", standard_name="latitude")
344
+ ds.y.attrs.update(units="degrees_north", standard_name="latitude")
345
+ elif grid_mapping == "rotated_latitude_longitude":
346
+ ds.lon.attrs.update(units="degrees", standard_name="grid_longitude")
347
+ ds.x.attrs.update(units="degrees", standard_name="grid_longitude")
348
+ ds.lat.attrs.update(units="degrees", standard_name="grid_latitude")
349
+ ds.y.attrs.update(units="degrees", standard_name="grid_latitude")
309
350
  ds[GEOMETRY_CONTAINER_NAME].attrs.update(coordinates="lon lat")
310
- ds.x.attrs.update(units="degrees_east", standard_name="longitude")
311
- ds.y.attrs.update(units="degrees_north", standard_name="latitude")
312
351
  elif grid_mapping is not None:
313
- raise NotImplementedError(
314
- f"Only grid mapping longitude_latitude is implemented. Got {grid_mapping}."
315
- )
352
+ ds.crd_x.attrs.update(standard_name="projection_x_coordinate")
353
+ ds.x.attrs.update(standard_name="projection_x_coordinate")
354
+ ds.crd_y.attrs.update(standard_name="projection_y_coordinate")
355
+ ds.y.attrs.update(standard_name="projection_y_coordinate")
316
356
 
317
357
  return ds
318
358
 
@@ -265,8 +265,8 @@ def test_shapely_to_cf(geometry_ds):
265
265
  [
266
266
  in_ds.drop_vars("geometry").isel(index=slice(1, None)),
267
267
  cfxr.shapely_to_cf(
268
- in_ds.geometry.isel(index=slice(1, None)),
269
- grid_mapping="longitude_latitude",
268
+ in_ds.geometry.isel(index=slice(1, None)).data,
269
+ grid_mapping="latitude_longitude",
270
270
  ),
271
271
  ]
272
272
  )
@@ -355,10 +355,11 @@ def test_shapely_to_cf_errors():
355
355
  with pytest.raises(ValueError, match="Mixed geometry types are not supported"):
356
356
  cfxr.shapely_to_cf(geoms)
357
357
 
358
- with pytest.raises(
359
- NotImplementedError, match="Only grid mapping longitude_latitude"
360
- ):
361
- cfxr.shapely_to_cf([Point(4, 5)], grid_mapping="albers_conical_equal_area")
358
+ encoded = cfxr.shapely_to_cf(
359
+ [Point(4, 5)], grid_mapping="albers_conical_equal_area"
360
+ )
361
+ assert encoded["x"].attrs["standard_name"] == "projection_x_coordinate"
362
+ assert encoded["y"].attrs["standard_name"] == "projection_y_coordinate"
362
363
 
363
364
 
364
365
  @requires_shapely
@@ -491,7 +492,6 @@ def test_reshape_unique_geometries(geometry_ds):
491
492
 
492
493
  @requires_shapely
493
494
  def test_encode_decode(geometry_ds, polygon_geometry):
494
-
495
495
  geom_dim_ds = xr.Dataset()
496
496
  geom_dim_ds = geom_dim_ds.assign_coords(
497
497
  xr.Coordinates(
@@ -75,11 +75,16 @@ def test_udunits_power_syntax_parse_units():
75
75
  ("m ** -1", "m-1"),
76
76
  ("m ** 2 / s ** 2", "m2 s-2"),
77
77
  ("m ** 3 / (kg * s ** 2)", "m3 kg-1 s-2"),
78
+ ("", "1"),
78
79
  ),
79
80
  )
80
81
  def test_udunits_format(units, expected):
81
82
  u = ureg.parse_units(units)
83
+ if units == "":
84
+ # The non-shortened dimensionless can only work with recent pint
85
+ pytest.importorskip("pint", minversion="0.24.1")
82
86
 
87
+ assert f"{u:~cf}" == expected
83
88
  assert f"{u:cf}" == expected
84
89
 
85
90
 
@@ -4,62 +4,57 @@ import functools
4
4
  import re
5
5
 
6
6
  import pint
7
- from pint import ( # noqa: F401
8
- DimensionalityError,
9
- UndefinedUnitError,
10
- UnitStrippedWarning,
11
- )
7
+ from packaging.version import Version
12
8
 
13
9
  from .utils import emit_user_level_warning
14
10
 
15
- # from `xclim`'s unit support module with permission of the maintainers
16
- try:
17
11
 
18
- @pint.register_unit_format("cf")
19
- def short_formatter(unit, registry, **options):
20
- """Return a CF-compliant unit string from a `pint` unit.
21
-
22
- Parameters
23
- ----------
24
- unit : pint.UnitContainer
25
- Input unit.
26
- registry : pint.UnitRegistry
27
- the associated registry
28
- **options
29
- Additional options (may be ignored)
30
-
31
- Returns
32
- -------
33
- out : str
34
- Units following CF-Convention, using symbols.
35
- """
36
- import re
37
-
38
- # convert UnitContainer back to Unit
39
- unit = registry.Unit(unit)
40
- # Print units using abbreviations (millimeter -> mm)
41
- s = f"{unit:~D}"
42
-
43
- # Search and replace patterns
44
- pat = r"(?P<inverse>(?:1 )?/ )?(?P<unit>\w+)(?: \*\* (?P<pow>\d))?"
45
-
46
- def repl(m):
47
- i, u, p = m.groups()
48
- p = p or (1 if i else "")
49
- neg = "-" if i else ""
50
-
51
- return f"{u}{neg}{p}"
52
-
53
- out, n = re.subn(pat, repl, s)
54
-
55
- # Remove multiplications
56
- out = out.replace(" * ", " ")
57
- # Delta degrees:
58
- out = out.replace("Δ°", "delta_deg")
59
- return out.replace("percent", "%")
12
+ @pint.register_unit_format("cf")
13
+ def short_formatter(unit, registry, **options):
14
+ """Return a CF-compliant unit string from a `pint` unit.
15
+
16
+ Parameters
17
+ ----------
18
+ unit : pint.UnitContainer
19
+ Input unit.
20
+ registry : pint.UnitRegistry
21
+ The associated registry
22
+ **options
23
+ Additional options (may be ignored)
24
+
25
+ Returns
26
+ -------
27
+ out : str
28
+ Units following CF-Convention, using symbols.
29
+ """
30
+ # pint 0.24.1 gives {"dimensionless": 1} for non-shortened dimensionless units
31
+ # CF uses "1" to denote fractions and dimensionless quantities
32
+ if unit == {"dimensionless": 1} or not unit:
33
+ return "1"
34
+
35
+ # If u is a name, get its symbol (same as pint's "~" pre-formatter)
36
+ # otherwise, assume a symbol (pint should have already raised on invalid units before this)
37
+ unit = pint.util.UnitsContainer(
38
+ {
39
+ registry._get_symbol(u) if u in registry._units else u: exp
40
+ for u, exp in unit.items()
41
+ }
42
+ )
43
+
44
+ # Change in formatter signature in pint 0.24
45
+ if Version(pint.__version__) < Version("0.24"):
46
+ args = (unit.items(),)
47
+ else:
48
+ # Numerators splitted from denominators
49
+ args = (
50
+ ((u, e) for u, e in unit.items() if e >= 0),
51
+ ((u, e) for u, e in unit.items() if e < 0),
52
+ )
53
+
54
+ out = pint.formatter(*args, as_ratio=False, product_fmt=" ", power_fmt="{}{}")
55
+ # To avoid potentiel unicode problems in netCDF. In both cases, this unit is not recognized by udunits
56
+ return out.replace("Δ°", "delta_deg")
60
57
 
61
- except ImportError:
62
- pass
63
58
 
64
59
  # ------
65
60
  # Reused with modification from MetPy under the terms of the BSD 3-Clause License.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cf_xarray
3
- Version: 0.9.2
3
+ Version: 0.9.3
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
@@ -224,7 +224,7 @@ License-File: LICENSE
224
224
  Requires-Dist: xarray
225
225
  Provides-Extra: all
226
226
  Requires-Dist: matplotlib; extra == "all"
227
- Requires-Dist: pint; extra == "all"
227
+ Requires-Dist: pint!=0.24.0,>=0.18; extra == "all"
228
228
  Requires-Dist: shapely; extra == "all"
229
229
  Requires-Dist: regex; extra == "all"
230
230
  Requires-Dist: rich; extra == "all"
@@ -2,7 +2,7 @@ xarray
2
2
 
3
3
  [all]
4
4
  matplotlib
5
- pint
5
+ pint!=0.24.0,>=0.18
6
6
  shapely
7
7
  regex
8
8
  rich
@@ -16,7 +16,7 @@ hide-toc: true
16
16
 
17
17
  The xarray ecosystem supports unit-aware arrays using [pint](https://pint.readthedocs.io) and [pint-xarray](https://pint-xarray.readthedocs.io). Some changes are required to make these packages work well with [UDUNITS format recommended by the CF conventions](http://cfconventions.org/Data/cf-conventions/cf-conventions-1.8/cf-conventions.html#units).
18
18
 
19
- `cf_xarray` makes those recommended changes when you `import cf_xarray.units`. These changes allow pint to parse and format UDUNIT units strings, and add several custom units like `degrees_north` for latitude, `psu` for ocean salinity, etc.
19
+ `cf_xarray` makes those recommended changes when you `import cf_xarray.units`. These changes allow pint to parse and format UDUNIT units strings, and add several custom units like `degrees_north` for latitude, `psu` for ocean salinity, etc. Be aware that pint supports some units that UDUNITS does not recognize but `cf-xarray` will not try to detect them and raise an error. For example, a temperature subtraction returns "delta_degC" units in pint, which does not exist in UDUNITS.
20
20
 
21
21
  ## Formatting units
22
22
 
@@ -27,5 +27,5 @@ from pint import application_registry as ureg
27
27
  import cf_xarray.units
28
28
 
29
29
  u = ureg.Unit("m ** 3 / s ** 2")
30
- f"{u:~cf}"
30
+ f"{u:cf}" # or {u:~cf}, both return the same short format
31
31
  ```
@@ -22,7 +22,7 @@ dependencies = [
22
22
  dynamic = ["version"]
23
23
 
24
24
  [project.optional-dependencies]
25
- all = ["matplotlib", "pint", "shapely", "regex", "rich", "pooch"]
25
+ all = ["matplotlib", "pint >=0.18, !=0.24.0", "shapely", "regex", "rich", "pooch"]
26
26
 
27
27
  [project.urls]
28
28
  homepage = "https://cf-xarray.readthedocs.io"
@@ -1 +0,0 @@
1
- __version__ = "0.9.2"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes