uxarray 2026.9.0__tar.gz → 2026.10.0__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.
- {uxarray-2026.9.0 → uxarray-2026.10.0}/MANIFEST.in +1 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/PKG-INFO +28 -25
- uxarray-2026.10.0/pyproject.toml +117 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/core/accessors.py +3 -1
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/core/aggregation.py +16 -24
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/core/api.py +33 -10
- uxarray-2026.10.0/uxarray/core/arithmetic.py +258 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/core/dataarray.py +233 -102
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/core/dataset.py +83 -40
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/core/gradient.py +6 -5
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/core/utils.py +20 -5
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/core/zonal.py +4 -1
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/cross_sections/sample.py +3 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/errors.py +7 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/grid/angles.py +1 -1
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/grid/arcs.py +23 -13
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/grid/area.py +6 -2
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/grid/bounds.py +6 -5
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/grid/connectivity.py +5 -5
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/grid/coordinates.py +7 -5
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/grid/dual.py +1 -1
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/grid/geometry.py +49 -23
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/grid/grid.py +108 -49
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/grid/integrate.py +8 -4
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/grid/neighbors.py +246 -196
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/grid/point_in_face.py +112 -49
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/grid/slice.py +6 -2
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/grid/utils.py +13 -7
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/io/_esmf.py +32 -6
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/io/_geopandas.py +2 -1
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/io/_healpix.py +3 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/io/_scrip.py +323 -28
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/io/_ugrid.py +61 -2
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/plot/accessor.py +10 -2
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/plot/matplotlib.py +2 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/plot/utils.py +6 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/remap/bilinear.py +1 -1
- uxarray-2026.10.0/uxarray/utils/__init__.py +0 -0
- uxarray-2026.10.0/uxarray/utils/coords.py +212 -0
- uxarray-2026.10.0/uxarray/utils/imports.py +366 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/utils/numba_math.py +10 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray.egg-info/PKG-INFO +28 -25
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray.egg-info/SOURCES.txt +3 -0
- uxarray-2026.10.0/uxarray.egg-info/requires.txt +40 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray.egg-info/scm_file_list.json +20 -1
- uxarray-2026.10.0/uxarray.egg-info/scm_version.json +8 -0
- uxarray-2026.9.0/pyproject.toml +0 -89
- uxarray-2026.9.0/uxarray/utils/coords.py +0 -86
- uxarray-2026.9.0/uxarray.egg-info/requires.txt +0 -34
- uxarray-2026.9.0/uxarray.egg-info/scm_version.json +0 -8
- {uxarray-2026.9.0 → uxarray-2026.10.0}/.codecov.yml +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/.git_archival.txt +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/.gitattributes +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/CITATION.cff +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/CODE_OF_CONDUCT.md +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/CONTRIBUTING.md +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/INSTALLATION.md +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/LICENSE +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/README.md +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/build.sh +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/setup.cfg +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/setup.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/__init__.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/constants.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/conventions/__init__.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/conventions/descriptors.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/conventions/ugrid.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/core/__init__.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/cross_sections/__init__.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/cross_sections/dataarray_accessor.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/cross_sections/grid_accessor.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/formatting_html.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/grid/__init__.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/grid/intersections.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/grid/validation.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/io/__init__.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/io/_delaunay.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/io/_exodus.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/io/_fesom2.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/io/_geos.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/io/_icon.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/io/_mpas.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/io/_structured.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/io/_topology.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/io/_vertices.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/io/_voronoi.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/io/utils.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/plot/__init__.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/plot/constants.py +0 -0
- /uxarray-2026.9.0/uxarray/utils/__init__.py → /uxarray-2026.10.0/uxarray/py.typed +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/remap/__init__.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/remap/accessor.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/remap/apply_weights.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/remap/inverse_distance_weighted.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/remap/nearest_neighbor.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/remap/spatial_coords_remap.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/remap/structured.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/remap/utils.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/remap/weights.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/remap/yac.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/subset/__init__.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/subset/dataarray_accessor.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/subset/grid_accessor.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/tutorial/__init__.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/tutorial/registry.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/utils/computing.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray/utils/warnings.py +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray.egg-info/dependency_links.txt +0 -0
- {uxarray-2026.9.0 → uxarray-2026.10.0}/uxarray.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: uxarray
|
|
3
|
-
Version: 2026.
|
|
3
|
+
Version: 2026.10.0
|
|
4
4
|
Summary: Xarray extension for unstructured climate and global weather data analysis and visualization.
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -220,38 +220,41 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
220
220
|
Requires-Python: >=3.11
|
|
221
221
|
Description-Content-Type: text/markdown
|
|
222
222
|
License-File: LICENSE
|
|
223
|
-
Requires-Dist: antimeridian
|
|
224
|
-
Requires-Dist:
|
|
225
|
-
Requires-Dist:
|
|
226
|
-
Requires-Dist: datashader
|
|
227
|
-
Requires-Dist: geoviews
|
|
228
|
-
Requires-Dist: holoviews
|
|
229
|
-
Requires-Dist: matplotlib<3.11
|
|
230
|
-
Requires-Dist: matplotlib-inline
|
|
231
|
-
Requires-Dist: netcdf4
|
|
223
|
+
Requires-Dist: antimeridian>=0.3.0
|
|
224
|
+
Requires-Dist: dask[dataframe]>=2024.12.0
|
|
225
|
+
Requires-Dist: netcdf4>=1.7.0
|
|
232
226
|
Requires-Dist: numba>=0.63
|
|
233
|
-
Requires-Dist: numpy
|
|
234
|
-
Requires-Dist: pandas
|
|
235
|
-
Requires-Dist:
|
|
236
|
-
Requires-Dist: requests
|
|
237
|
-
Requires-Dist: scikit-learn
|
|
227
|
+
Requires-Dist: numpy>=2.0
|
|
228
|
+
Requires-Dist: pandas>=2.2.2
|
|
229
|
+
Requires-Dist: scikit-learn>=1.4.2
|
|
238
230
|
Requires-Dist: scipy
|
|
239
|
-
Requires-Dist: shapely
|
|
240
|
-
Requires-Dist:
|
|
241
|
-
Requires-Dist:
|
|
242
|
-
|
|
243
|
-
Requires-Dist:
|
|
244
|
-
Requires-Dist: healpix
|
|
245
|
-
Requires-Dist: polars
|
|
246
|
-
Requires-Dist: pyproj
|
|
231
|
+
Requires-Dist: shapely>=2.0.6
|
|
232
|
+
Requires-Dist: xarray>=2024.10.0
|
|
233
|
+
Requires-Dist: polars>=0.20.4
|
|
234
|
+
Provides-Extra: all
|
|
235
|
+
Requires-Dist: uxarray[dev,geo,viz]; extra == "all"
|
|
247
236
|
Provides-Extra: complete
|
|
248
|
-
Requires-Dist: uxarray[dev]; extra == "complete"
|
|
237
|
+
Requires-Dist: uxarray[dev,geo,viz]; extra == "complete"
|
|
249
238
|
Provides-Extra: dev
|
|
239
|
+
Requires-Dist: asv; extra == "dev"
|
|
240
|
+
Requires-Dist: pooch; extra == "dev"
|
|
250
241
|
Requires-Dist: pre_commit; extra == "dev"
|
|
251
242
|
Requires-Dist: pytest; extra == "dev"
|
|
252
243
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
253
244
|
Requires-Dist: ruff; extra == "dev"
|
|
254
|
-
|
|
245
|
+
Provides-Extra: geo
|
|
246
|
+
Requires-Dist: cartopy>=0.23; extra == "geo"
|
|
247
|
+
Requires-Dist: geopandas>=1.0; extra == "geo"
|
|
248
|
+
Requires-Dist: healpix>=2024.2; extra == "geo"
|
|
249
|
+
Requires-Dist: pyproj; extra == "geo"
|
|
250
|
+
Requires-Dist: spatialpandas>=0.4.10; extra == "geo"
|
|
251
|
+
Provides-Extra: viz
|
|
252
|
+
Requires-Dist: cartopy<0.26,>=0.23; extra == "viz"
|
|
253
|
+
Requires-Dist: datashader; extra == "viz"
|
|
254
|
+
Requires-Dist: matplotlib<3.11,>=3.8.4; extra == "viz"
|
|
255
|
+
Requires-Dist: geoviews>=1.11.0; extra == "viz"
|
|
256
|
+
Requires-Dist: holoviews>=1.19.0; extra == "viz"
|
|
257
|
+
Requires-Dist: hvplot>=0.8.3; extra == "viz"
|
|
255
258
|
Dynamic: license-file
|
|
256
259
|
|
|
257
260
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
classifiers=[
|
|
3
|
+
"Operating System :: OS Independent",
|
|
4
|
+
"Intended Audience :: Science/Research",
|
|
5
|
+
"Topic :: Scientific/Engineering",
|
|
6
|
+
"Programming Language :: Python",
|
|
7
|
+
"Programming Language :: Python :: 3",
|
|
8
|
+
"Programming Language :: Python :: 3.11",
|
|
9
|
+
"Programming Language :: Python :: 3.12",
|
|
10
|
+
"Programming Language :: Python :: 3.13",
|
|
11
|
+
"Programming Language :: Python :: 3.14",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
dynamic = ["version"]
|
|
15
|
+
keywords = ["unstructured grids", "xarray", "ugrid", "mpas", "exodus", "scrip", "fesom", "icon", "esmf"]
|
|
16
|
+
description = "Xarray extension for unstructured climate and global weather data analysis and visualization."
|
|
17
|
+
license = {file = "LICENSE"}
|
|
18
|
+
name = "uxarray"
|
|
19
|
+
readme = "README.md"
|
|
20
|
+
requires-python = ">=3.11"
|
|
21
|
+
|
|
22
|
+
# minimal dependencies start
|
|
23
|
+
# Lower bounds are added only for a known failure with an older release; the
|
|
24
|
+
# comment on each says what breaks. The "Minimum Dependencies" CI workflow
|
|
25
|
+
# tests the oldest versions these bounds allow and opens an issue when they
|
|
26
|
+
# fail, which is how new bounds get found.
|
|
27
|
+
dependencies = [
|
|
28
|
+
"antimeridian>=0.3.0", # fix_polygon(fix_winding=...) is new in 0.3.0.
|
|
29
|
+
"dask[dataframe]>=2024.12.0", # older dd.from_dask_array() fails on empty arrays (empty SCRIP grids).
|
|
30
|
+
"netcdf4>=1.7.0", # older releases are built against numpy 1 and fail to import under numpy 2.
|
|
31
|
+
"numba>=0.63", # 0.63 is the first release supporting Python 3.14 (3.10 <= py < 3.15). See #1561.
|
|
32
|
+
"numpy>=2.0", # np.asin() is new in 2.0.
|
|
33
|
+
"pandas>=2.2.2", # older releases without a numpy<2 cap fail to import under numpy 2.
|
|
34
|
+
"scikit-learn>=1.4.2", # older releases without a numpy<2 cap fail to import under numpy 2.
|
|
35
|
+
"scipy",
|
|
36
|
+
"shapely>=2.0.6", # 2.0 API (shapely.polygons()); older 2.0.x fail in create_collection under numpy 2.
|
|
37
|
+
"xarray>=2024.10.0", # older releases break resample() and dask-backed weighted_mean().
|
|
38
|
+
"polars>=0.20.4", # DataFrame.with_row_index() is new in 0.20.4.
|
|
39
|
+
]
|
|
40
|
+
# minimal dependencies end
|
|
41
|
+
|
|
42
|
+
[project.optional-dependencies]
|
|
43
|
+
all = ["uxarray[dev,geo,viz]"]
|
|
44
|
+
complete = ["uxarray[dev,geo,viz]"] # alias to "all"
|
|
45
|
+
dev = [
|
|
46
|
+
'asv',
|
|
47
|
+
'pooch',
|
|
48
|
+
'pre_commit',
|
|
49
|
+
'pytest',
|
|
50
|
+
'pytest-cov',
|
|
51
|
+
'ruff',
|
|
52
|
+
]
|
|
53
|
+
geo = [
|
|
54
|
+
"cartopy>=0.23", # in both geo and viz because it is needed by to_geodataframe() and by plotting routines.
|
|
55
|
+
# (0.23 is the first release that works with numpy 2.)
|
|
56
|
+
"geopandas>=1.0", # 1.0 is the first release requiring pyogrio, the file-IO backend read_file needs. See #1693.
|
|
57
|
+
"healpix>=2024.2", # older releases are built against numpy 1 and fail to import under numpy 2.
|
|
58
|
+
"pyproj",
|
|
59
|
+
"spatialpandas>=0.4.10", # PolygonArray.from_exterior_coords() is new in 0.4.10.
|
|
60
|
+
]
|
|
61
|
+
viz = [
|
|
62
|
+
"cartopy>=0.23,<0.26", # cartopy 0.26.0 breaks some geoviews plots; see issue #1780.
|
|
63
|
+
# (cartopy pin intentionally here but not in "geo" extras because geoviews is only in "viz".)
|
|
64
|
+
"datashader",
|
|
65
|
+
"matplotlib>=3.8.4,<3.11", # 3.11.0 breaks some plots (see issue #1542); older releases without a numpy<2 cap fail under numpy 2.
|
|
66
|
+
"geoviews>=1.11.0", # older releases use np.NaN, removed in numpy 2. No upper pin; newer should support any cartopy; see holoviz/geoviews#884.
|
|
67
|
+
"holoviews>=1.19.0", # older releases import panel.depends.param_value_if_widget, which current panel removed.
|
|
68
|
+
"hvplot>=0.8.3", # older releases use param's .params(), removed in param 2.
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
[project.urls]
|
|
72
|
+
Documentation = "https://uxarray.readthedocs.io/"
|
|
73
|
+
Source = "https://github.com/UXARRAY/uxarray"
|
|
74
|
+
Tracker = "https://github.com/UXARRAY/uxarray/issues"
|
|
75
|
+
# please keep [project.urls] immediately after [project.optional-dependencies],
|
|
76
|
+
# or, if rearranging sections, make sure to edit installation.rst accordingly.
|
|
77
|
+
|
|
78
|
+
[build-system]
|
|
79
|
+
build-backend = "setuptools.build_meta"
|
|
80
|
+
requires = [
|
|
81
|
+
"setuptools>=60",
|
|
82
|
+
"setuptools-scm>=8",
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
[tool.setuptools]
|
|
86
|
+
packages = ["uxarray"]
|
|
87
|
+
|
|
88
|
+
[tool.setuptools.package-data]
|
|
89
|
+
# PEP 561 marker. setuptools >= 69.0 adds py.typed to a wheel on its own
|
|
90
|
+
# (_IMPLICIT_DATA_FILES in setuptools/command/build_py.py), but the build
|
|
91
|
+
# requirement below is setuptools >= 60, and on 68.2.2 the marker is dropped
|
|
92
|
+
# from the wheel unless it is declared here. Keep this until that floor is
|
|
93
|
+
# raised past 69.
|
|
94
|
+
uxarray = ["py.typed"]
|
|
95
|
+
|
|
96
|
+
[tool.setuptools_scm]
|
|
97
|
+
fallback_version = "9999"
|
|
98
|
+
|
|
99
|
+
[tool.ruff]
|
|
100
|
+
extend-exclude = ["test","benchmarks"]
|
|
101
|
+
|
|
102
|
+
[tool.ruff.lint]
|
|
103
|
+
extend-select = ["I"]
|
|
104
|
+
extend-ignore = ["E731"]
|
|
105
|
+
|
|
106
|
+
[tool.ruff.lint.isort]
|
|
107
|
+
known-first-party = ["uxarray"]
|
|
108
|
+
|
|
109
|
+
[tool.ruff.lint.per-file-ignores]
|
|
110
|
+
"docs/*" = ["E402", "F401"]
|
|
111
|
+
"uxarray/plot/*" = ["E402", "F401"]
|
|
112
|
+
|
|
113
|
+
[tool.ruff.format]
|
|
114
|
+
docstring-code-format = true
|
|
115
|
+
|
|
116
|
+
[tool.pytest.ini_options]
|
|
117
|
+
testpaths = ["test"] # (intentionally excludes test_optional_deps)
|
|
@@ -119,7 +119,9 @@ class BaseAccessor:
|
|
|
119
119
|
|
|
120
120
|
def _process_result(self, result):
|
|
121
121
|
"""Process method results to preserve uxgrid. To be overridden by subclasses."""
|
|
122
|
-
raise NotImplementedError(
|
|
122
|
+
raise NotImplementedError(
|
|
123
|
+
f"_process_result, for BaseAccessor subclass {type(self)}"
|
|
124
|
+
)
|
|
123
125
|
|
|
124
126
|
# Delegation for common dunder methods
|
|
125
127
|
def __iter__(self):
|
|
@@ -25,8 +25,8 @@ def _uxda_grid_aggregate(uxda, destination, aggregation, **kwargs):
|
|
|
25
25
|
UxDataArray."""
|
|
26
26
|
if destination is None:
|
|
27
27
|
raise ValueError(
|
|
28
|
-
"
|
|
29
|
-
)
|
|
28
|
+
f"Missing destination (got destination=None) in topological_{aggregation})."
|
|
29
|
+
) # e.g. aggregation will be something like "mean", "min", "any", ...
|
|
30
30
|
|
|
31
31
|
if uxda._node_centered():
|
|
32
32
|
# aggregation of a node-centered data variable
|
|
@@ -36,8 +36,8 @@ def _uxda_grid_aggregate(uxda, destination, aggregation, **kwargs):
|
|
|
36
36
|
return _node_to_edge_aggregation(uxda, aggregation, kwargs)
|
|
37
37
|
else:
|
|
38
38
|
raise DataCenteringError(
|
|
39
|
-
f"
|
|
40
|
-
f"
|
|
39
|
+
f"Node-centered data requires destination='face' or 'edge'; "
|
|
40
|
+
f"got destination={destination!r}, during topological_{aggregation}."
|
|
41
41
|
)
|
|
42
42
|
|
|
43
43
|
elif uxda._edge_centered():
|
|
@@ -45,29 +45,17 @@ def _uxda_grid_aggregate(uxda, destination, aggregation, **kwargs):
|
|
|
45
45
|
raise NotImplementedError(
|
|
46
46
|
"Aggregation of edge-centered data variables is not yet supported."
|
|
47
47
|
)
|
|
48
|
-
# if destination == "node":
|
|
49
|
-
# pass
|
|
50
|
-
# elif destination == "face":
|
|
51
|
-
# pass
|
|
52
|
-
# else:
|
|
53
|
-
# raise ValueError("TODO: )
|
|
54
48
|
|
|
55
49
|
elif uxda._face_centered():
|
|
56
50
|
# aggregation of a face-centered data variable
|
|
57
51
|
raise NotImplementedError(
|
|
58
52
|
"Aggregation of face-centered data variables is not yet supported."
|
|
59
53
|
)
|
|
60
|
-
# if destination == "node":
|
|
61
|
-
# pass
|
|
62
|
-
# elif destination == "edge":
|
|
63
|
-
# pass
|
|
64
|
-
# else:
|
|
65
|
-
# raise ValueError("TODO: ")
|
|
66
54
|
|
|
67
55
|
else:
|
|
68
56
|
raise DataCenteringError(
|
|
69
|
-
"
|
|
70
|
-
"
|
|
57
|
+
f"topological_{aggregation} expected data mapped to nodes, edges, or faces; "
|
|
58
|
+
f"got array with array.data_mapping={uxda.data_mapping!r} and array.dims={uxda.dims!r}."
|
|
71
59
|
)
|
|
72
60
|
|
|
73
61
|
|
|
@@ -77,8 +65,8 @@ def _node_to_face_aggregation(uxda, aggregation, aggregation_func_kwargs):
|
|
|
77
65
|
|
|
78
66
|
if not uxda._node_centered():
|
|
79
67
|
raise DataCenteringError(
|
|
80
|
-
f"
|
|
81
|
-
f"{uxda.
|
|
68
|
+
f"Expected node-centered data; got array with array.data_mapping={uxda.data_mapping!r} "
|
|
69
|
+
f"and array.dims={uxda.dims!r}, during node-to-face aggregation."
|
|
82
70
|
)
|
|
83
71
|
|
|
84
72
|
if isinstance(uxda.data, np.ndarray):
|
|
@@ -92,7 +80,9 @@ def _node_to_face_aggregation(uxda, aggregation, aggregation_func_kwargs):
|
|
|
92
80
|
uxda, NUMPY_AGGREGATIONS[aggregation], aggregation_func_kwargs
|
|
93
81
|
)
|
|
94
82
|
else:
|
|
95
|
-
raise TypeError
|
|
83
|
+
raise TypeError(
|
|
84
|
+
f"Expected numpy or dask array; got type(uxda.data)={type(uxda.data)}."
|
|
85
|
+
)
|
|
96
86
|
|
|
97
87
|
|
|
98
88
|
def _node_to_face_kernel(
|
|
@@ -206,8 +196,8 @@ def _node_to_edge_aggregation(uxda, aggregation, aggregation_func_kwargs):
|
|
|
206
196
|
|
|
207
197
|
if not uxda._node_centered():
|
|
208
198
|
raise DataCenteringError(
|
|
209
|
-
f"
|
|
210
|
-
f"{uxda.
|
|
199
|
+
f"Expected node-centered data; got array with array.data_mapping={uxda.data_mapping!r} "
|
|
200
|
+
f"and array.dims={uxda.dims!r}, during node-to-edge aggregation."
|
|
211
201
|
)
|
|
212
202
|
|
|
213
203
|
aggregation_func = NUMPY_AGGREGATIONS[aggregation]
|
|
@@ -223,7 +213,9 @@ def _node_to_edge_aggregation(uxda, aggregation, aggregation_func_kwargs):
|
|
|
223
213
|
uxda, aggregation_func, aggregation_func_kwargs
|
|
224
214
|
)
|
|
225
215
|
else:
|
|
226
|
-
raise TypeError
|
|
216
|
+
raise TypeError(
|
|
217
|
+
f"Expected numpy or dask array; got type(uxda.data)={type(uxda.data)}."
|
|
218
|
+
)
|
|
227
219
|
|
|
228
220
|
|
|
229
221
|
def _node_to_edge_kernel(
|
|
@@ -108,8 +108,14 @@ def open_grid(
|
|
|
108
108
|
if os.path.isfile(nod2d_path) and os.path.isfile(elem2d_path):
|
|
109
109
|
grid = Grid.from_dataset(grid_filename_or_obj)
|
|
110
110
|
else:
|
|
111
|
+
_missing = []
|
|
112
|
+
if not os.path.isfile(nod2d_path):
|
|
113
|
+
_missing.append("'nod2d.out'")
|
|
114
|
+
if not os.path.isfile(elem2d_path):
|
|
115
|
+
_missing.append("'elem2d.out'")
|
|
111
116
|
raise FileNotFoundError(
|
|
112
|
-
|
|
117
|
+
"open_grid(directory) expects FESOM2 ASCII dataset with 'nod2d.out' and 'elem2d.out' files, but "
|
|
118
|
+
f"got directory={os.path.abspath(grid_filename_or_obj)!r}, which is missing {' and '.join(_missing)}."
|
|
113
119
|
)
|
|
114
120
|
|
|
115
121
|
elif isinstance(grid_filename_or_obj, dict):
|
|
@@ -190,6 +196,12 @@ def open_multigrid(
|
|
|
190
196
|
mask_ds = xr.open_dataset(mask_filename)
|
|
191
197
|
mask_ds_opened = True
|
|
192
198
|
|
|
199
|
+
# human-readable str telling what was provided to open_multigrid(). Useful for error messages.
|
|
200
|
+
if isinstance(grid_filename_or_obj, (str, os.PathLike)):
|
|
201
|
+
_provided_input_str = f"file, {os.path.abspath(grid_filename_or_obj)!r}"
|
|
202
|
+
else:
|
|
203
|
+
_provided_input_str = type(grid_filename_or_obj).__name__
|
|
204
|
+
|
|
193
205
|
try:
|
|
194
206
|
active_value_map: Mapping[str, MaskValue] | None = (
|
|
195
207
|
mask_active_value if isinstance(mask_active_value, Mapping) else None
|
|
@@ -238,8 +250,8 @@ def open_multigrid(
|
|
|
238
250
|
if format_type == "single_scrip":
|
|
239
251
|
if gridnames is not None and "grid" not in gridnames:
|
|
240
252
|
raise ValueError(
|
|
241
|
-
f"Requested grids {gridnames} not found
|
|
242
|
-
"
|
|
253
|
+
f"Requested grids (gridnames={gridnames}) not found in the provided {_provided_input_str}, "
|
|
254
|
+
"in open_multigrid(). Only 'grid' is available in single-grid SCRIP files."
|
|
243
255
|
)
|
|
244
256
|
grid_ds_ugrid, source_dims_dict = _read_scrip(grid_ds)
|
|
245
257
|
return {
|
|
@@ -251,7 +263,9 @@ def open_multigrid(
|
|
|
251
263
|
}
|
|
252
264
|
|
|
253
265
|
if not grids_dict:
|
|
254
|
-
raise GridInvalidError(
|
|
266
|
+
raise GridInvalidError(
|
|
267
|
+
f"Failed to detect any grids in the provided {_provided_input_str}, in open_multigrid()."
|
|
268
|
+
)
|
|
255
269
|
|
|
256
270
|
available_grids = list(grids_dict.keys())
|
|
257
271
|
|
|
@@ -267,7 +281,8 @@ def open_multigrid(
|
|
|
267
281
|
for name in requested:
|
|
268
282
|
if name not in grids_dict:
|
|
269
283
|
raise ValueError(
|
|
270
|
-
f"
|
|
284
|
+
f"open_multigrid() grid '{name}' not found in the provided {_provided_input_str}, "
|
|
285
|
+
f"in open_multigrid(). Available grids: {available_grids}"
|
|
271
286
|
)
|
|
272
287
|
grids_to_load.append(name)
|
|
273
288
|
|
|
@@ -295,9 +310,14 @@ def open_multigrid(
|
|
|
295
310
|
active_indices = np.flatnonzero(active_mask)
|
|
296
311
|
grid = grid.isel(n_face=active_indices)
|
|
297
312
|
else:
|
|
313
|
+
_provided_mask_str = (
|
|
314
|
+
f"file, {os.path.abspath(mask_filename)!r}"
|
|
315
|
+
if isinstance(mask_filename, (str, os.PathLike))
|
|
316
|
+
else type(mask_filename).__name__
|
|
317
|
+
)
|
|
298
318
|
warn(
|
|
299
|
-
f"Mask variable
|
|
300
|
-
f"
|
|
319
|
+
f"Mask variable {mask_var!r} not found in the provided mask {_provided_mask_str}. "
|
|
320
|
+
f"Grid {grid_name!r} will be returned without masking."
|
|
301
321
|
)
|
|
302
322
|
|
|
303
323
|
loaded_grids[grid_name] = grid
|
|
@@ -428,7 +448,8 @@ def open_dataset(
|
|
|
428
448
|
if isinstance(grid_filename_or_obj, (str, os.PathLike)):
|
|
429
449
|
if os.path.isdir(grid_filename_or_obj):
|
|
430
450
|
raise ValueError(
|
|
431
|
-
"ux.open_dataset() with a single directory
|
|
451
|
+
"ux.open_dataset(arg0) with no other arguments and arg0 a single directory is not supported, "
|
|
452
|
+
f"but got arg0 indicating path to directory: {os.path.abspath(grid_filename_or_obj)!r}."
|
|
432
453
|
"Supply a path to a grid file instead. Directory-based grids (e.g. a "
|
|
433
454
|
"FESOM2 ASCII grid) are only recognized when a separate data file is "
|
|
434
455
|
"also provided, i.e. ux.open_dataset(grid_directory, data_file)."
|
|
@@ -442,8 +463,10 @@ def open_dataset(
|
|
|
442
463
|
elif isinstance(grid_filename_or_obj, xr.Dataset):
|
|
443
464
|
ds = grid_filename_or_obj
|
|
444
465
|
else:
|
|
445
|
-
raise
|
|
446
|
-
"
|
|
466
|
+
raise TypeError(
|
|
467
|
+
"Expected grid_filename_or_obj to be a file path or xarray.Dataset when filename_or_obj "
|
|
468
|
+
f"is not provided, but got type(grid_filename_or_obj)={type(grid_filename_or_obj)}, "
|
|
469
|
+
"in ux.open_dataset(grid_filename_or_obj, filename_or_obj=None)."
|
|
447
470
|
)
|
|
448
471
|
|
|
449
472
|
uxgrid, _ = _get_grid(ds, chunks, chunk_grid, use_dual, grid_kwargs, **kwargs)
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: ensures arithmetic for uxarray objects behaves as expected,
|
|
3
|
+
e.g. checks for compatible uxgrids (if more than one input has a uxgrid),
|
|
4
|
+
and ensure appropriate result types (e.g. xr.DataArray + UxDataArray --> UxDataArray).
|
|
5
|
+
|
|
6
|
+
Includes solutions for issues #1685, #1695, #1718:
|
|
7
|
+
(1685) Uxarray types dropped during ufuncs
|
|
8
|
+
Solved by appropriately defining __array_ufunc__.
|
|
9
|
+
(1695) Uxarray types dropped during math with xarrays, depending on order of operations
|
|
10
|
+
Solved by explicitly overriding dunder methods like __mul__,
|
|
11
|
+
so Python's default behavior for dunder methods and subclasses triggers
|
|
12
|
+
(which just requires `uxarray_obj.__mul__ is not xarray_obj.__mul__`)
|
|
13
|
+
and dispatches to uxarray's methods instead of xarray's methods.
|
|
14
|
+
(The overridden methods just call super()'s method, because they go to _binary_op,
|
|
15
|
+
which goes to uxarray's _binary_op(), which handles types appropriately.
|
|
16
|
+
_binary_op() already handles types appropriately because it uses
|
|
17
|
+
_replace() for DataArrays, which UxDataArray already overrides, and it uses
|
|
18
|
+
_calculate_binary_op() for Datasets, which UxDataset overrides.)
|
|
19
|
+
(1718) Math with multiple uxarray objects with different grids silently succeeds,
|
|
20
|
+
when data dims are otherwise compatible
|
|
21
|
+
Solved by comparing uxgrids during _binary_op() if both inputs have a uxgrid,
|
|
22
|
+
and by comparing uxgrids of all inputs during __array_ufunc__,
|
|
23
|
+
if multiple inputs to __array_ufunc__ have a "uxgrid" attr.
|
|
24
|
+
(The former fixes, e.g., objA + objB; the latter fixes, e.g., np.add(objA, objB))
|
|
25
|
+
|
|
26
|
+
Note:
|
|
27
|
+
Grids with n_face==1 are intentionally excluded from automatic comparisons,
|
|
28
|
+
because including them would breaks workflows operating on single faces, like:
|
|
29
|
+
arr.isel(n_face=0) < arr.isel(n_face=7)
|
|
30
|
+
The downside of not fixing this, though, is that the result's grid will match n_face==0,
|
|
31
|
+
silently dropping the n_face==7 information (as in issue #1718).
|
|
32
|
+
If isel() is ever updated to drop n_face dimension from arr.isel(n_face=0) instead,
|
|
33
|
+
should update the relevant code inside _binary_op and __array_ufunc__ below.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
import operator
|
|
37
|
+
|
|
38
|
+
import xarray as xr
|
|
39
|
+
|
|
40
|
+
from uxarray.errors import GridsMismatchError
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class UxSupportsArithmetic:
|
|
44
|
+
"""Handles arithmetic overrides for uxarray objects as needed,
|
|
45
|
+
to ensure they behave as expected. E.g.:
|
|
46
|
+
|
|
47
|
+
- check for compatible uxgrids (if more than one input has a uxgrid),
|
|
48
|
+
- ensure appropriate result types (e.g. xr.DataArray + UxDataArray --> UxDataArray).
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
Expects to be placed in inheritance order *before* xarray classes,
|
|
52
|
+
e.g. UxDataArray(UxSupportsArithmetic, xr.DataArray),
|
|
53
|
+
not xr.DataArray(UxSupportsArithmetic, UxDataArray),
|
|
54
|
+
so that super() will resolve to methods from xarray.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
# If strict type-checking gets turned on, it might complain about super() calls here.
|
|
58
|
+
# The fix would probably be to define a typing.Protocol parent class to inherit from.
|
|
59
|
+
|
|
60
|
+
__slots__ = ()
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def uxgrid(self):
|
|
64
|
+
"""(UxSupportsArithmetic expects subclasses to implement uxgrid)"""
|
|
65
|
+
raise NotImplementedError(f"{type(self).__name__}.uxgrid")
|
|
66
|
+
# below, checks for "has a uxgrid" are written as `isinstance(obj, UxSupportsArithmetic)`,
|
|
67
|
+
# rather than checking for a uxgrid attribute, because the latter would be too permissive,
|
|
68
|
+
# e.g. hasattr(xr.DataArray(7, coords={"uxgrid": "anything"}), "uxgrid") is True.
|
|
69
|
+
|
|
70
|
+
def _binary_op(self, other, f, reflexive=False, **kw_super):
|
|
71
|
+
"""returns f(self, other) (or f(other, self), if `reflexive`) for f a binary operation,
|
|
72
|
+
such as adding or multiplying. Like super()._binary_op, except that
|
|
73
|
+
if `other` has a uxgrid, first ensure it is compatible with self.uxgrid.
|
|
74
|
+
"""
|
|
75
|
+
self._raise_if_grids_incompatible_during(other, f)
|
|
76
|
+
return super()._binary_op(other, f, reflexive=reflexive, **kw_super)
|
|
77
|
+
|
|
78
|
+
def _inplace_binary_op(self, other, f):
|
|
79
|
+
"""returns f(self, other) for f a binary in-place operation,
|
|
80
|
+
such as A+=B. Like super()._inplace_binary_op, except that
|
|
81
|
+
if `other` has a uxgrid, first ensure it is compatible with self.uxgrid.
|
|
82
|
+
"""
|
|
83
|
+
self._raise_if_grids_incompatible_during(other, f)
|
|
84
|
+
return super()._inplace_binary_op(other, f)
|
|
85
|
+
|
|
86
|
+
def _raise_if_grids_incompatible_during(self, other, f):
|
|
87
|
+
"""raise GridsMismatchError if other has a uxgrid which is incompatible with self.
|
|
88
|
+
f is only used for error message, as f.__name__.
|
|
89
|
+
|
|
90
|
+
Grids are considered "compatible" here if they compare as equal,
|
|
91
|
+
OR if either grid has n_face==1 (to avoid breaking "scalar-like" workflows,
|
|
92
|
+
because isel(n_face=int) maybe should provide scalar, but currently doesn't).
|
|
93
|
+
"""
|
|
94
|
+
if isinstance(other, UxSupportsArithmetic):
|
|
95
|
+
if (
|
|
96
|
+
(self.uxgrid.n_face > 1)
|
|
97
|
+
and (other.uxgrid.n_face > 1)
|
|
98
|
+
and (self.uxgrid != other.uxgrid)
|
|
99
|
+
):
|
|
100
|
+
raise GridsMismatchError(
|
|
101
|
+
f"A.uxgrid != B.uxgrid during binary operation {f.__name__!r}, "
|
|
102
|
+
f"with type(A)={type(self).__name__}, type(B)={type(other).__name__}. "
|
|
103
|
+
f"(Got A.uxgrid.sizes={self.uxgrid.sizes}; B.uxgrid.sizes={other.uxgrid.sizes}.)"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
|
|
107
|
+
"""Like super().__array_ufunc__, except that if multiple inputs have a uxgrid,
|
|
108
|
+
ensure they are all compatible, and if any outputs are xr.DataArray or xr.Dataset,
|
|
109
|
+
convert to the appropriate uxarray type (UxDataArray or UxDataset).
|
|
110
|
+
|
|
111
|
+
Grids are considered "compatible" here if they compare as equal,
|
|
112
|
+
OR if they have n_face==1 (to avoid breaking "scalar-like" workflows,
|
|
113
|
+
because isel(n_face=int) maybe should provide scalar, but currently doesn't).
|
|
114
|
+
"""
|
|
115
|
+
from uxarray.core.dataarray import UxDataArray
|
|
116
|
+
from uxarray.core.dataset import UxDataset
|
|
117
|
+
|
|
118
|
+
uxgrids = [
|
|
119
|
+
(i, obj.uxgrid)
|
|
120
|
+
for i, obj in enumerate(inputs)
|
|
121
|
+
if isinstance(obj, UxSupportsArithmetic)
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
# when checking for equality, and when deciding which grid to attach to result,
|
|
125
|
+
# will pretend that any grids with n_face==1 are scalars (see above).
|
|
126
|
+
nonscalar_grids = [(i, grid) for i, grid in uxgrids if grid.n_face > 1]
|
|
127
|
+
|
|
128
|
+
if len(nonscalar_grids) == 0:
|
|
129
|
+
# no nonscalar grids; keep first grid.
|
|
130
|
+
# At least one input is guaranteed to have a uxgrid, otherwise numpy
|
|
131
|
+
# would not have delegated to this method during the ufunc call.
|
|
132
|
+
j_ref, grid_ref = uxgrids[0]
|
|
133
|
+
else:
|
|
134
|
+
# At least one nonscalar grid; output grid should match it.
|
|
135
|
+
j_ref, grid_ref = nonscalar_grids[0]
|
|
136
|
+
# check that all nonscalar uxgrids are compatible:
|
|
137
|
+
if len(nonscalar_grids) > 1:
|
|
138
|
+
for j, grid in nonscalar_grids[1:]:
|
|
139
|
+
if grid != grid_ref:
|
|
140
|
+
raise GridsMismatchError(
|
|
141
|
+
f"Multiple inputs to {ufunc.__name__!r} have incompatible uxgrids. "
|
|
142
|
+
f"Got inputs[{j}].uxgrid != inputs[{j_ref}].uxgrid, for inputs "
|
|
143
|
+
f"with types: type(inputs[{j}])={type(inputs[j])}, "
|
|
144
|
+
f"type(inputs[{j_ref}])={type(inputs[j_ref])}."
|
|
145
|
+
)
|
|
146
|
+
result = super().__array_ufunc__(ufunc, method, *inputs, **kwargs)
|
|
147
|
+
|
|
148
|
+
# convert any xr.DataArray / xr.Dataset outputs to appropriate uxarray type:
|
|
149
|
+
_was_tuple = isinstance(result, tuple)
|
|
150
|
+
result = list(result) if _was_tuple else [result]
|
|
151
|
+
for i, obj in enumerate(result):
|
|
152
|
+
if isinstance(obj, xr.DataArray):
|
|
153
|
+
result[i] = UxDataArray(obj, uxgrid=grid_ref)
|
|
154
|
+
elif isinstance(obj, xr.Dataset):
|
|
155
|
+
result[i] = UxDataset(obj, uxgrid=grid_ref)
|
|
156
|
+
result = tuple(result) if _was_tuple else result[0]
|
|
157
|
+
return result
|
|
158
|
+
|
|
159
|
+
# shadow dunder methods to ensure that, in case of ops with both uxarray and xarray,
|
|
160
|
+
# Python's default behavior for dunder methods and subclasses triggers.
|
|
161
|
+
# E.g., shadowing __add__ & __radd__ ensures that xarr + uxarr goes to uxarr.__radd__(xarr),
|
|
162
|
+
# instead of xarr.__add__(uxarr), so the result can actually be a UxAarray.
|
|
163
|
+
# (Just calling super()'s method is sufficient because it goes to self._binary_op,
|
|
164
|
+
# which goes to uxarray's _binary_op because self will be the uxarray object.)
|
|
165
|
+
|
|
166
|
+
def __add__(self, other):
|
|
167
|
+
return super().__add__(other)
|
|
168
|
+
|
|
169
|
+
def __radd__(self, other):
|
|
170
|
+
return super().__radd__(other)
|
|
171
|
+
|
|
172
|
+
def __sub__(self, other):
|
|
173
|
+
return super().__sub__(other)
|
|
174
|
+
|
|
175
|
+
def __rsub__(self, other):
|
|
176
|
+
return super().__rsub__(other)
|
|
177
|
+
|
|
178
|
+
def __mul__(self, other):
|
|
179
|
+
return super().__mul__(other)
|
|
180
|
+
|
|
181
|
+
def __rmul__(self, other):
|
|
182
|
+
return super().__rmul__(other)
|
|
183
|
+
|
|
184
|
+
def __pow__(self, other):
|
|
185
|
+
return super().__pow__(other)
|
|
186
|
+
|
|
187
|
+
def __rpow__(self, other):
|
|
188
|
+
return super().__rpow__(other)
|
|
189
|
+
|
|
190
|
+
def __truediv__(self, other):
|
|
191
|
+
return super().__truediv__(other)
|
|
192
|
+
|
|
193
|
+
def __rtruediv__(self, other):
|
|
194
|
+
return super().__rtruediv__(other)
|
|
195
|
+
|
|
196
|
+
def __floordiv__(self, other):
|
|
197
|
+
return super().__floordiv__(other)
|
|
198
|
+
|
|
199
|
+
def __rfloordiv__(self, other):
|
|
200
|
+
return super().__rfloordiv__(other)
|
|
201
|
+
|
|
202
|
+
def __mod__(self, other):
|
|
203
|
+
return super().__mod__(other)
|
|
204
|
+
|
|
205
|
+
def __rmod__(self, other):
|
|
206
|
+
return super().__rmod__(other)
|
|
207
|
+
|
|
208
|
+
def __and__(self, other):
|
|
209
|
+
return super().__and__(other)
|
|
210
|
+
|
|
211
|
+
def __rand__(self, other):
|
|
212
|
+
return super().__rand__(other)
|
|
213
|
+
|
|
214
|
+
def __or__(self, other):
|
|
215
|
+
return super().__or__(other)
|
|
216
|
+
|
|
217
|
+
def __ror__(self, other):
|
|
218
|
+
return super().__ror__(other)
|
|
219
|
+
|
|
220
|
+
def __xor__(self, other):
|
|
221
|
+
return super().__xor__(other)
|
|
222
|
+
|
|
223
|
+
def __rxor__(self, other):
|
|
224
|
+
return super().__rxor__(other)
|
|
225
|
+
|
|
226
|
+
def __lshift__(self, other):
|
|
227
|
+
return super().__lshift__(other)
|
|
228
|
+
|
|
229
|
+
def __rlshift__(self, other):
|
|
230
|
+
# (this isn't in super() for some reason, so use _binary_op directly)
|
|
231
|
+
return self._binary_op(other, operator.lshift, reflexive=True)
|
|
232
|
+
|
|
233
|
+
def __rshift__(self, other):
|
|
234
|
+
return super().__rshift__(other)
|
|
235
|
+
|
|
236
|
+
def __rrshift__(self, other):
|
|
237
|
+
# (this isn't in super() for some reason, so use _binary_op directly)
|
|
238
|
+
return self._binary_op(other, operator.rshift, reflexive=True)
|
|
239
|
+
|
|
240
|
+
# comparisons
|
|
241
|
+
|
|
242
|
+
def __lt__(self, other):
|
|
243
|
+
return super().__lt__(other)
|
|
244
|
+
|
|
245
|
+
def __le__(self, other):
|
|
246
|
+
return super().__le__(other)
|
|
247
|
+
|
|
248
|
+
def __gt__(self, other):
|
|
249
|
+
return super().__gt__(other)
|
|
250
|
+
|
|
251
|
+
def __ge__(self, other):
|
|
252
|
+
return super().__ge__(other)
|
|
253
|
+
|
|
254
|
+
def __eq__(self, other):
|
|
255
|
+
return super().__eq__(other)
|
|
256
|
+
|
|
257
|
+
def __ne__(self, other):
|
|
258
|
+
return super().__ne__(other)
|