climarraykit 0.2.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.
@@ -0,0 +1,41 @@
1
+ # climarraykit Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ---
6
+
7
+ ## [0.2.0] - 2026-03-31
8
+
9
+ ### Changed (0.2.0)
10
+
11
+ #### **Package Dependencies** (changing; 0.2.0)
12
+
13
+ - **NumPy / Pandas:** require **`numpy>=2.2.3`** and **`pandas>=2.2.3`** in **`pyproject.toml`**, **`requirements.txt`**, and **`recipe/meta.yaml`**.
14
+
15
+ ---
16
+
17
+ ## [0.1.0] - 2026-03-30
18
+
19
+ ### Added (0.1.0)
20
+
21
+ - Bootstrap dedicated package metadata:
22
+ - `pyproject.toml`
23
+ - `LICENSE`
24
+ - `MANIFEST.in`
25
+ - `requirements.txt`
26
+ - `requirements-dev.txt`
27
+ - `README.md`
28
+ - package `climarraykit/`
29
+
30
+ - Introduce initial xarray-focused module surface:
31
+ - `climarraykit.file_utils`
32
+ - `climarraykit.patterns`
33
+ - `climarraykit.conversions`
34
+ - `climarraykit.data_manipulation`
35
+ - `climarraykit.xarray_obj_handler`
36
+
37
+ - Provide a non-breaking migration path by preserving the legacy logic and API shape while
38
+ enabling import migration towards `climarraykit`.
39
+
40
+ - Add `rename_xarray_dimension` to `climarraykit.patterns` for robust xarray dimension
41
+ renaming (shared by downstream packages such as `statflow` climatology helpers).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 climarraykit
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ include README.md
2
+ include LICENSE
3
+ include CHANGELOG.md
4
+ include requirements.txt
5
+ include requirements-dev.txt
6
+ recursive-include climarraykit *.py
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.4
2
+ Name: climarraykit
3
+ Version: 0.2.0
4
+ Summary: Climate-focused xarray toolkit for NetCDF/GRIB handling, coordinate discovery, and dataset serialisation
5
+ Author-email: Jon Ander Gabantxo <jagabantxo@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 climarraykit
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/EusDancerDev/climarraykit
29
+ Project-URL: Documentation, https://github.com/EusDancerDev/climarraykit#readme
30
+ Project-URL: Repository, https://github.com/EusDancerDev/climarraykit.git
31
+ Project-URL: Bug Reports, https://github.com/EusDancerDev/climarraykit/issues
32
+ Keywords: xarray,climate,netcdf,grib,meteorology,climatology
33
+ Classifier: Development Status :: 3 - Alpha
34
+ Classifier: Intended Audience :: Science/Research
35
+ Classifier: Programming Language :: Python :: 3
36
+ Classifier: Programming Language :: Python :: 3.10
37
+ Classifier: Programming Language :: Python :: 3.11
38
+ Classifier: Programming Language :: Python :: 3.12
39
+ Classifier: Operating System :: OS Independent
40
+ Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
41
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
42
+ Requires-Python: >=3.10
43
+ Description-Content-Type: text/markdown
44
+ License-File: LICENSE
45
+ Requires-Dist: numpy>=2.2.3
46
+ Requires-Dist: pandas>=2.2.3
47
+ Requires-Dist: xarray>=2024.2.0
48
+ Requires-Dist: filewise>=3.13.0
49
+ Requires-Dist: pygenutils>=16.4.0
50
+ Requires-Dist: paramlib>=3.4.10
51
+ Provides-Extra: grib
52
+ Requires-Dist: cfgrib>=0.9.10; extra == "grib"
53
+ Provides-Extra: regrid
54
+ Requires-Dist: xesmf>=0.8.0; extra == "regrid"
55
+ Provides-Extra: dev
56
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
57
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
58
+ Requires-Dist: black>=23.0.0; extra == "dev"
59
+ Requires-Dist: isort>=5.0.0; extra == "dev"
60
+ Requires-Dist: flake8>=6.0.0; extra == "dev"
61
+ Dynamic: license-file
62
+
63
+ # climarraykit
64
+
65
+ `climarraykit` is a dedicated xarray-centric toolkit for climate and weather data workflows.
66
+
67
+ It is introduced to decouple heavy xarray/netCDF functionality from broader utility packages
68
+ where those dependencies are not always needed.
69
+
70
+ ## Scope
71
+
72
+ - netCDF file scanning and integrity checks
73
+ - coordinate and dimension discovery utilities
74
+ - GRIB-to-netCDF conversion helpers
75
+ - xarray Dataset/DataArray creation and serialisation helpers
76
+ - climate-oriented xarray data manipulation helpers
77
+
78
+ ## Transitional compatibility
79
+
80
+ This initial release preserves behaviour by exposing the same module-level APIs as the
81
+ legacy `filewise.xarray_utils` package while enabling downstream projects to migrate imports
82
+ to `climarraykit`.
83
+
84
+ ---
85
+
86
+ Current version: **0.2.0** (see [CHANGELOG.md](CHANGELOG.md)).
@@ -0,0 +1,24 @@
1
+ # climarraykit
2
+
3
+ `climarraykit` is a dedicated xarray-centric toolkit for climate and weather data workflows.
4
+
5
+ It is introduced to decouple heavy xarray/netCDF functionality from broader utility packages
6
+ where those dependencies are not always needed.
7
+
8
+ ## Scope
9
+
10
+ - netCDF file scanning and integrity checks
11
+ - coordinate and dimension discovery utilities
12
+ - GRIB-to-netCDF conversion helpers
13
+ - xarray Dataset/DataArray creation and serialisation helpers
14
+ - climate-oriented xarray data manipulation helpers
15
+
16
+ ## Transitional compatibility
17
+
18
+ This initial release preserves behaviour by exposing the same module-level APIs as the
19
+ legacy `filewise.xarray_utils` package while enabling downstream projects to migrate imports
20
+ to `climarraykit`.
21
+
22
+ ---
23
+
24
+ Current version: **0.2.0** (see [CHANGELOG.md](CHANGELOG.md)).
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ __version__ = "0.2.0"
5
+
6
+ __all__ = [
7
+ "conversions",
8
+ "data_manipulation",
9
+ "file_utils",
10
+ "patterns",
11
+ "xarray_obj_handler",
12
+ ]
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ #----------------#
5
+ # Import modules #
6
+ #----------------#
7
+
8
+ import xarray as xr
9
+ from pathlib import Path
10
+
11
+ #------------------------#
12
+ # Import project modules #
13
+ #------------------------#
14
+
15
+ from climarraykit.xarray_obj_handler import _save_ds_as_nc
16
+ from paramlib.global_parameters import CLIMATE_FILE_EXTENSIONS
17
+ from pygenutils.arrays_and_lists.conversions import flatten_to_string
18
+ from pygenutils.arrays_and_lists.data_manipulation import flatten_list
19
+ from pygenutils.operative_systems.os_operations import exit_info, run_system_command
20
+ from pygenutils.strings.string_handler import (
21
+ find_substring_index,
22
+ get_obj_specs,
23
+ modify_obj_specs
24
+ )
25
+
26
+ #------------------#
27
+ # Define functions #
28
+ #------------------#
29
+
30
+ # Xarray objects #
31
+ #----------------#
32
+
33
+ def grib2nc(
34
+ grib_file_list: str | list[str],
35
+ on_shell: bool = False,
36
+ option_str: str | None = None,
37
+ capture_output: bool = False,
38
+ return_output_name: bool = False,
39
+ encoding: str = "utf-8",
40
+ shell: bool = True) -> None:
41
+ """
42
+ Converts a GRIB file or list of GRIB files to netCDF format. The conversion
43
+ can be executed either via shell commands or programmatically using xarray.
44
+
45
+ Parameters
46
+ ----------
47
+ grib_file_list : str | list[str]
48
+ The file path(s) of the GRIB file(s) to be converted.
49
+ on_shell : bool, optional
50
+ If True, the conversion will be handled through shell commands using
51
+ the 'grib_to_netcdf' tool. If False, the conversion will be done
52
+ programmatically using xarray.
53
+ option_str : str, optional
54
+ Additional options to pass to the shell command for 'grib_to_netcdf'.
55
+ This parameter is only used if 'on_shell' is set to True.
56
+ capture_output : bool, optional
57
+ Whether to capture the command output. Default is False.
58
+ return_output_name : bool, optional
59
+ Whether to return file descriptor names. Default is False.
60
+ encoding : str, optional
61
+ Encoding to use when decoding command output. Default is "utf-8".
62
+ shell : bool, optional
63
+ Whether to execute the command through the shell. Default is True.
64
+
65
+ Returns
66
+ -------
67
+ None
68
+ Converts the GRIB file(s) to netCDF format and saves the output
69
+ netCDF file(s) in the same directory as the GRIB files.
70
+
71
+ Raises
72
+ ------
73
+ TypeError
74
+ If grib_file_list is not str or list of str.
75
+ ValueError
76
+ If any GRIB file path is invalid or empty.
77
+ FileNotFoundError
78
+ If any GRIB file doesn't exist.
79
+
80
+ Notes
81
+ -----
82
+ - When 'on_shell' is True, the function builds and runs a shell command
83
+ that calls the 'grib_to_netcdf' tool, with optional flags.
84
+ - When 'on_shell' is False, xarray is used to directly open the GRIB file
85
+ and convert it to netCDF format.
86
+ - The function will prompt for input in the case of multiple GRIB files if
87
+ 'on_shell' is True.
88
+ """
89
+
90
+ # Parameter validation
91
+ if not isinstance(grib_file_list, (str, list)):
92
+ raise TypeError("grib_file_list must be a string or list of strings")
93
+
94
+ # Flatten nested lists for defensive programming
95
+ if isinstance(grib_file_list, list):
96
+ grib_file_list = flatten_list(grib_file_list)
97
+
98
+ # Validate all items are strings
99
+ if not all(isinstance(item, str) for item in grib_file_list):
100
+ raise TypeError("All items in grib_file_list must be strings")
101
+
102
+ # Check for empty strings
103
+ if not all(item.strip() for item in grib_file_list):
104
+ raise ValueError("All GRIB file paths must be non-empty strings")
105
+ else:
106
+ # Single string validation
107
+ if not isinstance(grib_file_list, str) or not grib_file_list.strip():
108
+ raise ValueError("GRIB file path must be a non-empty string")
109
+
110
+ # Check file existence
111
+ files_to_check = [grib_file_list] if isinstance(grib_file_list, str) else grib_file_list
112
+ for grib_file in files_to_check:
113
+ if not Path(grib_file).exists():
114
+ raise FileNotFoundError(f"GRIB file not found: {grib_file}")
115
+
116
+ # Check if file has expected GRIB extension
117
+ if not any(grib_file.lower().endswith(ext.lower()) for ext in ['.grib', '.grb', '.grib2', '.grb2']):
118
+ print(f"Warning: File {grib_file} may not be a GRIB file based on extension")
119
+
120
+ # Shell-based conversion #
121
+ #-#-#-#-#-#-#-#-#-#-#-#-#
122
+
123
+ if on_shell:
124
+ # Handle single GRIB file
125
+ if isinstance(grib_file_list, str):
126
+ nc_file_new = modify_obj_specs(grib_file_list, "ext", EXTENSIONS[0])
127
+
128
+ # Handle list of GRIB files
129
+ else:
130
+ grib_allfile_info_str = flatten_to_string(grib_file_list)
131
+
132
+ # Prompt user for the netCDF file name without extension
133
+ nc_file_new_noext = input("Please introduce a name "
134
+ "for the netCDF file, "
135
+ "WITHOUT THE EXTENSION: ")
136
+
137
+ # Validate the file name using RegEx
138
+ allowed_minimum_char_idx = find_substring_index(nc_file_new_noext,
139
+ REGEX_GRIB2NC,
140
+ advanced_search=True)
141
+
142
+ while allowed_minimum_char_idx == -1:
143
+ print("Invalid file name.\nIt can contain alphanumeric characters, "
144
+ "as well as the following non-word characters: {. _ -}")
145
+ nc_file_new_noext = input("Please introduce a valid name: ")
146
+ allowed_minimum_char_idx = find_substring_index(nc_file_new_noext,
147
+ REGEX_GRIB2NC,
148
+ advanced_search=True)
149
+
150
+ # Modify the file name to have the .nc extension
151
+ nc_file_new = modify_obj_specs(nc_file_new_noext,
152
+ obj2modify="ext",
153
+ new_obj=EXTENSIONS[0])
154
+
155
+ # Construct the shell command for conversion
156
+ grib2nc_template = "grib_to_netcdf "
157
+ if option_str:
158
+ grib2nc_template += f"{option_str} "
159
+ grib2nc_template += f"-o {nc_file_new} {grib_allfile_info_str}"
160
+
161
+ # Execute the shell command
162
+ try:
163
+ process_exit_info = run_system_command(
164
+ grib2nc_template,
165
+ capture_output=capture_output,
166
+ return_output_name=return_output_name,
167
+ encoding=encoding,
168
+ shell=shell
169
+ )
170
+ # Call exit_info with parameters based on capture_output
171
+ exit_info(
172
+ process_exit_info,
173
+ check_stdout=True,
174
+ check_stderr=True,
175
+ check_return_code=True
176
+ )
177
+ except Exception as e:
178
+ raise RuntimeError(f"Shell command execution failed: {e}")
179
+
180
+ # Programmatic conversion #
181
+ #-#-#-#-#-#-#-#-#-#-#-#-#-#
182
+
183
+ else:
184
+ # Ensure grib_file_list is a list
185
+ if isinstance(grib_file_list, str):
186
+ grib_file_list = [grib_file_list]
187
+
188
+ # Convert each GRIB file in the list to netCDF
189
+ for grib_file in grib_file_list:
190
+ try:
191
+ grib_file_noext = get_obj_specs(grib_file, "name_noext", EXTENSIONS[0])
192
+ ds = xr.open_dataset(grib_file, engine="cfgrib")
193
+ _save_ds_as_nc(ds, grib_file_noext)
194
+ print(f"Successfully converted {grib_file} to netCDF format")
195
+ except Exception as e:
196
+ print(f"Error converting {grib_file}: {e}")
197
+ raise
198
+
199
+
200
+ #--------------------------#
201
+ # Parameters and constants #
202
+ #--------------------------#
203
+
204
+ # Valid file extensions #
205
+ EXTENSIONS = CLIMATE_FILE_EXTENSIONS[::3]
206
+
207
+ # RegEx control for GRIB-to-netCDF single file name #
208
+ REGEX_GRIB2NC = r"^[a-zA-Z\d\._-]$"