Skip to content

Generalise support for netcdf load+save of "special" attributes #6566

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Jul 23, 2025

Conversation

pp-mo
Copy link
Member

@pp-mo pp-mo commented Jul 16, 2025

Closes SciTools/iris-grib#596 and SciTools/iris-grib#674

Ties together the handling of STASH, GRIB_PARAM and "ukmo__process_flags" attributes,
which all require special handling on netcdf load+save.

By design, should preserve existing behaviour.
But I have tried to generalise the handling,
( and also rationalise behaviour a wee bit, at least for "ukmo__process_flags" -- though that one is quite obscure ).

TODO:

  • ?probably? ditch the @staticmethod + use self more
  • make sure all types also support the "plain" string formats (as found in the files) inside Iris, as well as the 'convenience' forms
    • e.g. in place of a STASH object, can have just the string like "m01s02i123" (str form) or "STASH(model=...)" (repr form) .
    • ... and similar for the other 2 types
  • testing (TBD) ...

(but first, just let's see if it breaks anything I haven't already tried)

Copy link

codecov bot commented Jul 16, 2025

Codecov Report

Attention: Patch coverage is 90.62500% with 12 lines in your changes missing coverage. Please review.

Project coverage is 89.93%. Comparing base (41876b4) to head (1080a1b).

Files with missing lines Patch % Lines
lib/iris/fileformats/netcdf/_attribute_handlers.py 86.04% 12 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6566      +/-   ##
==========================================
- Coverage   89.94%   89.93%   -0.02%     
==========================================
  Files          90       91       +1     
  Lines       24302    24394      +92     
  Branches     4546     4554       +8     
==========================================
+ Hits        21859    21939      +80     
- Misses       1674     1686      +12     
  Partials      769      769              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@pp-mo pp-mo added the benchmark_this Request that this pull request be benchmarked to check if it introduces performance shifts label Jul 16, 2025
Copy link
Contributor

⏱️ Performance Benchmark Report: 71c7bc4

Performance shifts

Full benchmark results

Benchmarks that have stayed the same:

| Change   | Before [13e3d9f3]    | After [71c7bc41]    |   Ratio | Benchmark (Parameter)                                                                       |
|----------|----------------------|---------------------|---------|---------------------------------------------------------------------------------------------|
|          | 20.8±0.2ms           | 21.0±0.3ms          |    1.01 | aggregate_collapse.Aggregation.time_aggregated_by_COUNT(False)                              |
|          | 52.7±0.5ms           | 52.4±0.9ms          |    0.99 | aggregate_collapse.Aggregation.time_aggregated_by_COUNT(True)                               |
|          | 35.3±0.6ms           | 35.0±0.5ms          |    0.99 | aggregate_collapse.Aggregation.time_aggregated_by_FAST_PERCENTILE(False)                    |
|          | 170±3ms              | 167±1ms             |    0.98 | aggregate_collapse.Aggregation.time_aggregated_by_FAST_PERCENTILE(True)                     |
|          | 22.8±0.3ms           | 22.9±0.3ms          |    1    | aggregate_collapse.Aggregation.time_aggregated_by_GMEAN(False)                              |
|          | 31.9±0.3ms           | 32.0±0.5ms          |    1.01 | aggregate_collapse.Aggregation.time_aggregated_by_GMEAN(True)                               |
|          | 23.2±0.3ms           | 23.0±0.4ms          |    0.99 | aggregate_collapse.Aggregation.time_aggregated_by_HMEAN(False)                              |
|          | 31.8±0.2ms           | 31.9±0.2ms          |    1    | aggregate_collapse.Aggregation.time_aggregated_by_HMEAN(True)                               |
|          | 20.8±0.3ms           | 20.7±0.4ms          |    0.99 | aggregate_collapse.Aggregation.time_aggregated_by_MAX(False)                                |
|          | 44.7±0.9ms           | 44.7±0.6ms          |    1    | aggregate_collapse.Aggregation.time_aggregated_by_MAX(True)                                 |
|          | 126±1ms              | 125±0.8ms           |    0.99 | aggregate_collapse.Aggregation.time_aggregated_by_MAX_RUN(False)                            |
|          | 127±2ms              | 127±2ms             |    1    | aggregate_collapse.Aggregation.time_aggregated_by_MAX_RUN(True)                             |
|          | 22.0±0.3ms           | 21.9±0.3ms          |    1    | aggregate_collapse.Aggregation.time_aggregated_by_MEAN(False)                               |
|          | 48.8±0.8ms           | 47.9±0.7ms          |    0.98 | aggregate_collapse.Aggregation.time_aggregated_by_MEAN(True)                                |
|          | 23.5±0.2ms           | 23.0±0.2ms          |    0.98 | aggregate_collapse.Aggregation.time_aggregated_by_MEDIAN(False)                             |
|          | 58.6±1ms             | 57.6±0.7ms          |    0.98 | aggregate_collapse.Aggregation.time_aggregated_by_MEDIAN(True)                              |
|          | 21.2±0.2ms           | 20.6±0.1ms          |    0.97 | aggregate_collapse.Aggregation.time_aggregated_by_MIN(False)                                |
|          | 44.7±0.8ms           | 44.9±0.5ms          |    1    | aggregate_collapse.Aggregation.time_aggregated_by_MIN(True)                                 |
|          | 1.07±0.01s           | 1.07±0.01s          |    1    | aggregate_collapse.Aggregation.time_aggregated_by_PEAK(False)                               |
|          | 1.06±0.01s           | 1.06±0.01s          |    1    | aggregate_collapse.Aggregation.time_aggregated_by_PEAK(True)                                |
|          | 209±2ms              | 206±3ms             |    0.99 | aggregate_collapse.Aggregation.time_aggregated_by_PERCENTILE(False)                         |
|          | 341±6ms              | 340±8ms             |    1    | aggregate_collapse.Aggregation.time_aggregated_by_PERCENTILE(True)                          |
|          | 22.2±0.1ms           | 21.8±0.3ms          |    0.98 | aggregate_collapse.Aggregation.time_aggregated_by_PROPORTION(False)                         |
|          | 30.4±0.4ms           | 30.2±0.4ms          |    0.99 | aggregate_collapse.Aggregation.time_aggregated_by_PROPORTION(True)                          |
|          | 22.6±0.1ms           | 22.2±0.2ms          |    0.98 | aggregate_collapse.Aggregation.time_aggregated_by_RMS(False)                                |
|          | 59.5±1ms             | 59.6±1ms            |    1    | aggregate_collapse.Aggregation.time_aggregated_by_RMS(True)                                 |
|          | 22.9±0.2ms           | 22.7±0.3ms          |    1    | aggregate_collapse.Aggregation.time_aggregated_by_STD_DEV(False)                            |
|          | 61.7±0.7ms           | 61.8±1ms            |    1    | aggregate_collapse.Aggregation.time_aggregated_by_STD_DEV(True)                             |
|          | 23.0±0.2ms           | 22.7±0.2ms          |    0.99 | aggregate_collapse.Aggregation.time_aggregated_by_VARIANCE(False)                           |
|          | 57.8±1ms             | 58.1±1ms            |    1.01 | aggregate_collapse.Aggregation.time_aggregated_by_VARIANCE(True)                            |
|          | 7.88±0.07ms          | 7.80±0.07ms         |    0.99 | aggregate_collapse.Aggregation.time_collapsed_by_COUNT(False)                               |
|          | 22.0±0.7ms           | 22.1±0.6ms          |    1    | aggregate_collapse.Aggregation.time_collapsed_by_COUNT(True)                                |
|          | 19.9±0.2ms           | 20.2±0.2ms          |    1.02 | aggregate_collapse.Aggregation.time_collapsed_by_FAST_PERCENTILE(False)                     |
|          | 124±2ms              | 126±1ms             |    1.01 | aggregate_collapse.Aggregation.time_collapsed_by_FAST_PERCENTILE(True)                      |
|          | 8.26±0.1ms           | 8.21±0.1ms          |    0.99 | aggregate_collapse.Aggregation.time_collapsed_by_GMEAN(False)                               |
|          | 20.4±0.7ms           | 19.9±0.6ms          |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_GMEAN(True)                                |
|          | 8.29±0.09ms          | 8.26±0.08ms         |    1    | aggregate_collapse.Aggregation.time_collapsed_by_HMEAN(False)                               |
|          | 20.7±0.6ms           | 20.7±0.6ms          |    1    | aggregate_collapse.Aggregation.time_collapsed_by_HMEAN(True)                                |
|          | 7.76±0.1ms           | 7.73±0.09ms         |    1    | aggregate_collapse.Aggregation.time_collapsed_by_MAX(False)                                 |
|          | 20.7±0.6ms           | 20.9±0.6ms          |    1.01 | aggregate_collapse.Aggregation.time_collapsed_by_MAX(True)                                  |
|          | 24.3±0.3ms           | 24.4±0.3ms          |    1    | aggregate_collapse.Aggregation.time_collapsed_by_MAX_RUN(False)                             |
|          | 34.6±0.5ms           | 34.8±0.6ms          |    1.01 | aggregate_collapse.Aggregation.time_collapsed_by_MAX_RUN(True)                              |
|          | 8.16±0.2ms           | 8.07±0.2ms          |    0.99 | aggregate_collapse.Aggregation.time_collapsed_by_MEAN(False)                                |
|          | 21.0±0.6ms           | 21.3±0.5ms          |    1.02 | aggregate_collapse.Aggregation.time_collapsed_by_MEAN(True)                                 |
|          | 9.22±0.07ms          | 9.06±0.1ms          |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_MEDIAN(False)                              |
|          | 23.1±0.5ms           | 23.5±0.7ms          |    1.02 | aggregate_collapse.Aggregation.time_collapsed_by_MEDIAN(True)                               |
|          | 7.70±0.06ms          | 7.74±0.08ms         |    1.01 | aggregate_collapse.Aggregation.time_collapsed_by_MIN(False)                                 |
|          | 20.4±0.6ms           | 20.9±0.4ms          |    1.02 | aggregate_collapse.Aggregation.time_collapsed_by_MIN(True)                                  |
|          | 532±2ms              | 531±4ms             |    1    | aggregate_collapse.Aggregation.time_collapsed_by_PEAK(False)                                |
|          | 545±5ms              | 543±3ms             |    1    | aggregate_collapse.Aggregation.time_collapsed_by_PEAK(True)                                 |
|          | 45.4±0.5ms           | 44.6±0.5ms          |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_PERCENTILE(False)                          |
|          | 133±0.9ms            | 133±0.6ms           |    1    | aggregate_collapse.Aggregation.time_collapsed_by_PERCENTILE(True)                           |
|          | 8.03±0.1ms           | 8.21±0.07ms         |    1.02 | aggregate_collapse.Aggregation.time_collapsed_by_PROPORTION(False)                          |
|          | 20.2±0.4ms           | 19.8±0.7ms          |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_PROPORTION(True)                           |
|          | 8.11±0.08ms          | 8.15±0.08ms         |    1.01 | aggregate_collapse.Aggregation.time_collapsed_by_RMS(False)                                 |
|          | 22.5±0.6ms           | 22.6±0.7ms          |    1    | aggregate_collapse.Aggregation.time_collapsed_by_RMS(True)                                  |
|          | 8.30±0.05ms          | 8.27±0.1ms          |    1    | aggregate_collapse.Aggregation.time_collapsed_by_STD_DEV(False)                             |
|          | 22.2±0.6ms           | 22.4±0.6ms          |    1.01 | aggregate_collapse.Aggregation.time_collapsed_by_STD_DEV(True)                              |
|          | 8.37±0.06ms          | 8.38±0.1ms          |    1    | aggregate_collapse.Aggregation.time_collapsed_by_VARIANCE(False)                            |
|          | 21.8±0.6ms           | 22.0±0.4ms          |    1.01 | aggregate_collapse.Aggregation.time_collapsed_by_VARIANCE(True)                             |
|          | 22.7±0.3ms           | 22.8±0.2ms          |    1    | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_MEAN(False)                     |
|          | 82.2±1ms             | 82.3±0.8ms          |    1    | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_MEAN(True)                      |
|          | 22.5±0.2ms           | 22.3±0.3ms          |    0.99 | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_RMS(False)                      |
|          | 95.8±1ms             | 93.5±1ms            |    0.98 | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_RMS(True)                       |
|          | 21.4±0.2ms           | 21.2±0.3ms          |    0.99 | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_SUM(False)                      |
|          | 55.9±0.8ms           | 55.4±0.6ms          |    0.99 | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_SUM(True)                       |
|          | 8.42±0.2ms           | 8.45±0.1ms          |    1    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_MEAN(False)                      |
|          | 27.4±0.6ms           | 27.1±0.8ms          |    0.99 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_MEAN(True)                       |
|          | 8.26±0.1ms           | 8.22±0.1ms          |    0.99 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_RMS(False)                       |
|          | 28.6±0.7ms           | 29.3±0.8ms          |    1.02 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_RMS(True)                        |
|          | 8.05±0.1ms           | 8.07±0.09ms         |    1    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_SUM(False)                       |
|          | 23.1±0.8ms           | 22.6±0.7ms          |    0.98 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_SUM(True)                        |
|          | 220±3ms              | 217±2ms             |    0.98 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_WPERCENTILE(False)               |
|          | 276±3ms              | 278±3ms             |    1.01 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_WPERCENTILE(True)                |
|          | 1.14±0.02ms          | 1.14±0.01ms         |    1    | cube.CubeCreation.time_create(False, 'construct')                                           |
|          | 398±9μs              | 394±3μs             |    0.99 | cube.CubeCreation.time_create(False, 'instantiate')                                         |
|          | 1.97±0.03ms          | 1.94±0.02ms         |    0.99 | cube.CubeCreation.time_create(True, 'construct')                                            |
|          | 1.36±0.01ms          | 1.37±0.01ms         |    1.01 | cube.CubeCreation.time_create(True, 'instantiate')                                          |
|          | 91.1±1ms             | 92.6±0.9ms          |    1.02 | cube.CubeEquality.time_equality(False, False, 'all_equal')                                  |
|          | 25.0±1ms             | 24.6±2ms            |    0.98 | cube.CubeEquality.time_equality(False, False, 'coord_inequality')                           |
|          | 106±2ms              | 105±1ms             |    0.99 | cube.CubeEquality.time_equality(False, False, 'data_inequality')                            |
|          | 17.5±0.2μs           | 17.3±0.2μs          |    0.99 | cube.CubeEquality.time_equality(False, False, 'metadata_inequality')                        |
|          | 97.1±0.8ms           | 95.0±0.9ms          |    0.98 | cube.CubeEquality.time_equality(False, True, 'all_equal')                                   |
|          | 27.8±0.5ms           | 27.5±0.3ms          |    0.99 | cube.CubeEquality.time_equality(False, True, 'coord_inequality')                            |
|          | 109±2ms              | 108±1ms             |    0.99 | cube.CubeEquality.time_equality(False, True, 'data_inequality')                             |
|          | 17.5±0.3μs           | 17.5±0.2μs          |    1    | cube.CubeEquality.time_equality(False, True, 'metadata_inequality')                         |
|          | 178±1ms              | 176±2ms             |    0.99 | cube.CubeEquality.time_equality(True, False, 'all_equal')                                   |
|          | 66.5±1ms             | 66.5±0.9ms          |    1    | cube.CubeEquality.time_equality(True, False, 'coord_inequality')                            |
|          | 206±1ms              | 207±2ms             |    1.01 | cube.CubeEquality.time_equality(True, False, 'data_inequality')                             |
|          | 52.1±0.2μs           | 52.1±0.5μs          |    1    | cube.CubeEquality.time_equality(True, False, 'metadata_inequality')                         |
|          | 254±5ms              | 253±3ms             |    0.99 | cube.CubeEquality.time_equality(True, True, 'all_equal')                                    |
|          | 142±3ms              | 140±2ms             |    0.98 | cube.CubeEquality.time_equality(True, True, 'coord_inequality')                             |
|          | 285±2ms              | 283±4ms             |    0.99 | cube.CubeEquality.time_equality(True, True, 'data_inequality')                              |
|          | 361±4μs              | 366±3μs             |    1.01 | cube.CubeEquality.time_equality(True, True, 'metadata_inequality')                          |
|          | 782±20μs             | 776±4μs             |    0.99 | import_iris.Iris.time__concatenate                                                          |
|          | 181±3μs              | 183±2μs             |    1.01 | import_iris.Iris.time__constraints                                                          |
|          | 114±4μs              | 115±1μs             |    1    | import_iris.Iris.time__data_manager                                                         |
|          | 91.0±0.8μs           | 92.1±0.4μs          |    1.01 | import_iris.Iris.time__deprecation                                                          |
|          | 165±2μs              | 161±2μs             |    0.98 | import_iris.Iris.time__lazy_data                                                            |
|          | 881±6μs              | 881±20μs            |    1    | import_iris.Iris.time__merge                                                                |
|          | 74.6±0.4μs           | 74.3±2μs            |    1    | import_iris.Iris.time__representation                                                       |
|          | 606±7μs              | 609±6μs             |    1    | import_iris.Iris.time_analysis                                                              |
|          | 139±0.7μs            | 140±2μs             |    1.01 | import_iris.Iris.time_analysis__area_weighted                                               |
|          | 107±2μs              | 106±1μs             |    0.99 | import_iris.Iris.time_analysis__grid_angles                                                 |
|          | 247±3μs              | 246±3μs             |    1    | import_iris.Iris.time_analysis__interpolation                                               |
|          | 190±5μs              | 190±1μs             |    1    | import_iris.Iris.time_analysis__regrid                                                      |
|          | 110±2μs              | 111±2μs             |    1.01 | import_iris.Iris.time_analysis__scipy_interpolate                                           |
|          | 136±2μs              | 134±2μs             |    0.99 | import_iris.Iris.time_analysis_calculus                                                     |
|          | 322±1μs              | 319±4μs             |    0.99 | import_iris.Iris.time_analysis_cartography                                                  |
|          | 89.9±1μs             | 92.1±0.7μs          |    1.02 | import_iris.Iris.time_analysis_geomerty                                                     |
|          | 209±2μs              | 210±1μs             |    1    | import_iris.Iris.time_analysis_maths                                                        |
|          | 92.6±0.9μs           | 94.2±2μs            |    1.02 | import_iris.Iris.time_analysis_stats                                                        |
|          | 172±2μs              | 171±0.9μs           |    1    | import_iris.Iris.time_analysis_trajectory                                                   |
|          | 313±3μs              | 314±2μs             |    1    | import_iris.Iris.time_aux_factory                                                           |
|          | 79.9±2μs             | 80.7±1μs            |    1.01 | import_iris.Iris.time_common                                                                |
|          | 165±2μs              | 161±2μs             |    0.98 | import_iris.Iris.time_common_lenient                                                        |
|          | 1.33±0.02ms          | 1.34±0.01ms         |    1.01 | import_iris.Iris.time_common_metadata                                                       |
|          | 168±2μs              | 168±1μs             |    1    | import_iris.Iris.time_common_mixin                                                          |
|          | 1.15±0ms             | 1.15±0.02ms         |    1.01 | import_iris.Iris.time_common_resolve                                                        |
|          | 196±2μs              | 197±0.8μs           |    1.01 | import_iris.Iris.time_config                                                                |
|          | 125±1μs              | 127±0.9μs           |    1.02 | import_iris.Iris.time_coord_categorisation                                                  |
|          | 373±2μs              | 374±7μs             |    1    | import_iris.Iris.time_coord_systems                                                         |
|          | 746±4μs              | 750±3μs             |    1.01 | import_iris.Iris.time_coords                                                                |
|          | 674±4μs              | 676±5μs             |    1    | import_iris.Iris.time_cube                                                                  |
|          | 235±1μs              | 237±2μs             |    1.01 | import_iris.Iris.time_exceptions                                                            |
|          | 74.9±0.8μs           | 74.7±1μs            |    1    | import_iris.Iris.time_experimental                                                          |
|          | 181±1μs              | 181±2μs             |    1    | import_iris.Iris.time_fileformats                                                           |
|          | 251±3μs              | 253±3μs             |    1.01 | import_iris.Iris.time_fileformats__ff                                                       |
|          | 2.56±0.03ms          | 2.58±0.04ms         |    1.01 | import_iris.Iris.time_fileformats__ff_cross_references                                      |
|          | 75.1±0.8μs           | 75.3±2μs            |    1    | import_iris.Iris.time_fileformats__pp_lbproc_pairs                                          |
|          | 112±1μs              | 112±1μs             |    1.01 | import_iris.Iris.time_fileformats_abf                                                       |
|          | 424±5μs              | 423±4μs             |    1    | import_iris.Iris.time_fileformats_cf                                                        |
|          | 4.68±0.03ms          | 4.71±0.04ms         |    1.01 | import_iris.Iris.time_fileformats_dot                                                       |
|          | 71.3±0.2μs           | 71.2±0.7μs          |    1    | import_iris.Iris.time_fileformats_name                                                      |
|          | 248±2μs              | 247±1μs             |    0.99 | import_iris.Iris.time_fileformats_name_loaders                                              |
|          | 114±1μs              | 114±0.8μs           |    1    | import_iris.Iris.time_fileformats_netcdf                                                    |
|          | 119±0.6μs            | 121±0.7μs           |    1.01 | import_iris.Iris.time_fileformats_nimrod                                                    |
|          | 209±1μs              | 209±2μs             |    1    | import_iris.Iris.time_fileformats_nimrod_load_rules                                         |
|          | 804±10μs             | 779±4μs             |    0.97 | import_iris.Iris.time_fileformats_pp                                                        |
|          | 174±2μs              | 177±3μs             |    1.01 | import_iris.Iris.time_fileformats_pp_load_rules                                             |
|          | 136±2μs              | 136±0.6μs           |    1    | import_iris.Iris.time_fileformats_pp_save_rules                                             |
|          | 534±5μs              | 540±8μs             |    1.01 | import_iris.Iris.time_fileformats_rules                                                     |
|          | 221±5μs              | 221±4μs             |    1    | import_iris.Iris.time_fileformats_structured_array_identification                           |
|          | 79.8±0.1μs           | 81.9±0.3μs          |    1.03 | import_iris.Iris.time_fileformats_um                                                        |
|          | 154±3μs              | 155±5μs             |    1    | import_iris.Iris.time_fileformats_um__fast_load                                             |
|          | 139±3μs              | 136±2μs             |    0.98 | import_iris.Iris.time_fileformats_um__fast_load_structured_fields                           |
|          | 71.7±0.7μs           | 73.5±1μs            |    1.03 | import_iris.Iris.time_fileformats_um__ff_replacement                                        |
|          | 77.5±0.3μs           | 78.2±2μs            |    1.01 | import_iris.Iris.time_fileformats_um__optimal_array_structuring                             |
|          | 941±9μs              | 940±5μs             |    1    | import_iris.Iris.time_fileformats_um_cf_map                                                 |
|          | 135±1μs              | 138±2μs             |    1.02 | import_iris.Iris.time_io                                                                    |
|          | 173±2μs              | 172±3μs             |    0.99 | import_iris.Iris.time_io_format_picker                                                      |
|          | 209±2μs              | 209±2μs             |    1    | import_iris.Iris.time_iris                                                                  |
|          | 125±0.7μs            | 126±0.4μs           |    1    | import_iris.Iris.time_iterate                                                               |
|          | 8.02±0.07ms          | 7.99±0.06ms         |    1    | import_iris.Iris.time_palette                                                               |
|          | 1.74±0.02ms          | 1.75±0.01ms         |    1.01 | import_iris.Iris.time_plot                                                                  |
|          | 217±1μs              | 216±1μs             |    1    | import_iris.Iris.time_quickplot                                                             |
|          | 2.09±0.05ms          | 2.13±0.03ms         |    1.02 | import_iris.Iris.time_std_names                                                             |
|          | 1.80±0.01ms          | 1.81±0.01ms         |    1    | import_iris.Iris.time_symbols                                                               |
|          | 261±9ms              | 262±9ms             |    1.01 | import_iris.Iris.time_tests                                                                 |
|          | 249±1μs              | 251±4μs             |    1.01 | import_iris.Iris.time_third_party_cartopy                                                   |
|          | 5.08±0.05ms          | 5.03±0.02ms         |    0.99 | import_iris.Iris.time_third_party_cf_units                                                  |
|          | 118±1μs              | 117±1μs             |    1    | import_iris.Iris.time_third_party_cftime                                                    |
|          | 2.71±0.01ms          | 2.71±0.01ms         |    1    | import_iris.Iris.time_third_party_matplotlib                                                |
|          | 1.30±0ms             | 1.30±0.01ms         |    1    | import_iris.Iris.time_third_party_numpy                                                     |
|          | 168±2μs              | 167±2μs             |    1    | import_iris.Iris.time_third_party_scipy                                                     |
|          | 98.8±0.7μs           | 98.6±0.5μs          |    1    | import_iris.Iris.time_time                                                                  |
|          | 345±8μs              | 347±2μs             |    1    | import_iris.Iris.time_util                                                                  |
|          | 71.9±1μs             | 71.4±0.9μs          |    0.99 | iterate.IZip.time_izip                                                                      |
|          | 9.92±0.03ms          | 10.0±0.06ms         |    1.01 | load.LoadAndRealise.time_load((1280, 960, 5), False, 'FF')                                  |
|          | 16.2±0.8ms           | 16.0±0.2ms          |    0.99 | load.LoadAndRealise.time_load((1280, 960, 5), False, 'NetCDF')                              |
|          | 9.94±0.06ms          | 9.94±0.04ms         |    1    | load.LoadAndRealise.time_load((1280, 960, 5), False, 'PP')                                  |
|          | 9.85±0.04ms          | 9.93±0.05ms         |    1.01 | load.LoadAndRealise.time_load((1280, 960, 5), True, 'FF')                                   |
|          | 13.4±0.2ms           | 13.3±0.1ms          |    0.99 | load.LoadAndRealise.time_load((1280, 960, 5), True, 'NetCDF')                               |
|          | 9.89±0.06ms          | 9.96±0.06ms         |    1.01 | load.LoadAndRealise.time_load((1280, 960, 5), True, 'PP')                                   |
|          | 1.49±0.01s           | 1.48±0.01s          |    0.99 | load.LoadAndRealise.time_load((2, 2, 1000), False, 'FF')                                    |
|          | 12.2±0.1ms           | 12.3±0.1ms          |    1.01 | load.LoadAndRealise.time_load((2, 2, 1000), False, 'NetCDF')                                |
|          | 1.52±0.01s           | 1.51±0.01s          |    0.99 | load.LoadAndRealise.time_load((2, 2, 1000), False, 'PP')                                    |
|          | 1.51±0.01s           | 1.49±0.01s          |    0.99 | load.LoadAndRealise.time_load((2, 2, 1000), True, 'FF')                                     |
|          | 12.2±0.08ms          | 12.1±0.1ms          |    1    | load.LoadAndRealise.time_load((2, 2, 1000), True, 'NetCDF')                                 |
|          | 1.51±0s              | 1.51±0.01s          |    1    | load.LoadAndRealise.time_load((2, 2, 1000), True, 'PP')                                     |
|          | 5.26±0.02ms          | 5.32±0.03ms         |    1.01 | load.LoadAndRealise.time_load((50, 50, 2), False, 'FF')                                     |
|          | 12.0±0.07ms          | 11.9±0.09ms         |    0.99 | load.LoadAndRealise.time_load((50, 50, 2), False, 'NetCDF')                                 |
|          | 5.25±0.02ms          | 5.33±0.04ms         |    1.01 | load.LoadAndRealise.time_load((50, 50, 2), False, 'PP')                                     |
|          | 5.32±0.04ms          | 5.27±0.02ms         |    0.99 | load.LoadAndRealise.time_load((50, 50, 2), True, 'FF')                                      |
|          | 12.0±0.1ms           | 12.0±0.08ms         |    1    | load.LoadAndRealise.time_load((50, 50, 2), True, 'NetCDF')                                  |
|          | 5.26±0.08ms          | 5.42±0.1ms          |    1.03 | load.LoadAndRealise.time_load((50, 50, 2), True, 'PP')                                      |
|          | 22.8±2ms             | 22.9±1ms            |    1.01 | load.LoadAndRealise.time_realise((1280, 960, 5), False, 'FF')                               |
|          | 23.6±0.5ms           | 23.9±0.5ms          |    1.01 | load.LoadAndRealise.time_realise((1280, 960, 5), False, 'NetCDF')                           |
|          | 12.1±1ms             | 11.3±1ms            |    0.93 | load.LoadAndRealise.time_realise((1280, 960, 5), False, 'PP')                               |
|          | 28.4±1ms             | 28.5±1ms            |    1    | load.LoadAndRealise.time_realise((1280, 960, 5), True, 'FF')                                |
|          | 83.1±0.8ms           | 83.0±0.5ms          |    1    | load.LoadAndRealise.time_realise((1280, 960, 5), True, 'NetCDF')                            |
|          | 28.0±1ms             | 27.8±1ms            |    0.99 | load.LoadAndRealise.time_realise((1280, 960, 5), True, 'PP')                                |
|          | 598±2ms              | 603±4ms             |    1.01 | load.LoadAndRealise.time_realise((2, 2, 1000), False, 'FF')                                 |
|          | 3.27±0.07ms          | 3.38±0.09ms         |    1.03 | load.LoadAndRealise.time_realise((2, 2, 1000), False, 'NetCDF')                             |
|          | 602±3ms              | 603±3ms             |    1    | load.LoadAndRealise.time_realise((2, 2, 1000), False, 'PP')                                 |
|          | 608±2ms              | 611±2ms             |    1    | load.LoadAndRealise.time_realise((2, 2, 1000), True, 'FF')                                  |
|          | 3.34±0.08ms          | 3.48±0.1ms          |    1.04 | load.LoadAndRealise.time_realise((2, 2, 1000), True, 'NetCDF')                              |
|          | 603±2ms              | 610±2ms             |    1.01 | load.LoadAndRealise.time_realise((2, 2, 1000), True, 'PP')                                  |
|          | 2.11±0.03ms          | 2.04±0.04ms         |    0.96 | load.LoadAndRealise.time_realise((50, 50, 2), False, 'FF')                                  |
|          | 3.37±0.1ms           | 3.40±0.09ms         |    1.01 | load.LoadAndRealise.time_realise((50, 50, 2), False, 'NetCDF')                              |
|          | 2.01±0.07ms          | 2.01±0.08ms         |    1    | load.LoadAndRealise.time_realise((50, 50, 2), False, 'PP')                                  |
|          | 2.08±0.07ms          | 2.02±0.03ms         |    0.97 | load.LoadAndRealise.time_realise((50, 50, 2), True, 'FF')                                   |
|          | 3.40±0.1ms           | 3.41±0.1ms          |    1    | load.LoadAndRealise.time_realise((50, 50, 2), True, 'NetCDF')                               |
|          | 2.00±0.06ms          | 2.03±0.07ms         |    1.01 | load.LoadAndRealise.time_realise((50, 50, 2), True, 'PP')                                   |
|          | 337±2ms              | 337±1ms             |    1    | load.ManyCubes.time_many_cube_load                                                          |
|          | 89.4±1ms             | 88.9±1ms            |    0.99 | load.ManyVars.time_many_var_load                                                            |
|          | 9.97±0.02ms          | 10.0±0.04ms         |    1    | load.STASHConstraint.time_stash_constraint((1280, 960, 5), 'FF')                            |
|          | 10.0±0.06ms          | 10.1±0.08ms         |    1.01 | load.STASHConstraint.time_stash_constraint((1280, 960, 5), 'PP')                            |
|          | 1.51±0.01s           | 1.51±0.01s          |    1    | load.STASHConstraint.time_stash_constraint((2, 2, 1000), 'FF')                              |
|          | 1.52±0.01s           | 1.52±0.01s          |    1    | load.STASHConstraint.time_stash_constraint((2, 2, 1000), 'PP')                              |
|          | 5.33±0.02ms          | 5.35±0.02ms         |    1    | load.STASHConstraint.time_stash_constraint((2, 2, 2), 'FF')                                 |
|          | 5.32±0.06ms          | 5.32±0.03ms         |    1    | load.STASHConstraint.time_stash_constraint((2, 2, 2), 'PP')                                 |
|          | 8.95±0.02ms          | 8.95±0.1ms          |    1    | load.StructuredFF.time_structured_load((1280, 960, 5), False)                               |
|          | 5.58±0.03ms          | 5.59±0.02ms         |    1    | load.StructuredFF.time_structured_load((1280, 960, 5), True)                                |
|          | 1.47±0.01s           | 1.48±0.01s          |    1.01 | load.StructuredFF.time_structured_load((2, 2, 1000), False)                                 |
|          | 415±3ms              | 415±3ms             |    1    | load.StructuredFF.time_structured_load((2, 2, 1000), True)                                  |
|          | 4.36±0.07ms          | 4.39±0.06ms         |    1.01 | load.StructuredFF.time_structured_load((2, 2, 2), False)                                    |
|          | 4.21±0.02ms          | 4.24±0.05ms         |    1.01 | load.StructuredFF.time_structured_load((2, 2, 2), True)                                     |
|          | 163±1ms              | 168±2ms             |    1.03 | load.TimeConstraint.time_time_constraint(20, 'FF')                                          |
|          | 15.1±0.2ms           | 15.4±0.2ms          |    1.02 | load.TimeConstraint.time_time_constraint(20, 'NetCDF')                                      |
|          | 165±0.5ms            | 165±1ms             |    1    | load.TimeConstraint.time_time_constraint(20, 'PP')                                          |
|          | 32.8±0.1ms           | 32.7±0.3ms          |    1    | load.TimeConstraint.time_time_constraint(3, 'FF')                                           |
|          | 14.8±0.1ms           | 15.1±0.1ms          |    1.02 | load.TimeConstraint.time_time_constraint(3, 'NetCDF')                                       |
|          | 32.8±0.2ms           | 32.7±0.6ms          |    1    | load.TimeConstraint.time_time_constraint(3, 'PP')                                           |
|          | 18.9±0.4ms           | 18.2±0.2ms          |    0.96 | load.ugrid.BasicLoading.time_load_file(1)                                                   |
|          | 48.5±0.5ms           | 48.2±0.7ms          |    0.99 | load.ugrid.BasicLoading.time_load_file(200000)                                              |
|          | 8.91±0.1ms           | 8.89±0.2ms          |    1    | load.ugrid.BasicLoading.time_load_mesh(1)                                                   |
|          | 16.8±0.4ms           | 16.5±0.4ms          |    0.98 | load.ugrid.BasicLoading.time_load_mesh(200000)                                              |
|          | 18.6±0.5ms           | 18.9±0.3ms          |    1.02 | load.ugrid.BasicLoadingTime.time_load_file(1)                                               |
|          | 19.2±0.6ms           | 19.0±0.4ms          |    0.99 | load.ugrid.BasicLoadingTime.time_load_file(200000)                                          |
|          | 8.74±0.1ms           | 8.83±0.3ms          |    1.01 | load.ugrid.BasicLoadingTime.time_load_mesh(1)                                               |
|          | 11.5±0.2ms           | 11.5±0.3ms          |    1    | load.ugrid.BasicLoadingTime.time_load_mesh(200000)                                          |
|          | 20.6±0.2ms           | 20.9±0.2ms          |    1.02 | load.ugrid.Callback.time_load_file_callback(1)                                              |
|          | 59.7±0.3ms           | 60.3±0.4ms          |    1.01 | load.ugrid.Callback.time_load_file_callback(200000)                                         |
|          | 20.3±0.5ms           | 20.7±0.3ms          |    1.02 | load.ugrid.CallbackTime.time_load_file_callback(1)                                          |
|          | 22.1±0.2ms           | 22.1±0.4ms          |    1    | load.ugrid.CallbackTime.time_load_file_callback(200000)                                     |
|          | 3.52±0.1ms           | 3.48±0.1ms          |    0.99 | load.ugrid.DataRealisation.time_realise_data(10000)                                         |
|          | 6.63±0.2ms           | 6.40±0.1ms          |    0.96 | load.ugrid.DataRealisation.time_realise_data(200000)                                        |
|          | 39.6±2ms             | 37.0±0.9ms          |    0.93 | load.ugrid.DataRealisationTime.time_realise_data(10000)                                     |
|          | 761±9ms              | 763±5ms             |    1    | load.ugrid.DataRealisationTime.time_realise_data(200000)                                    |
|          | 1.39±0.02s           | 1.37±0.03s          |    0.98 | merge_concat.Concatenate.time_concatenate(False)                                            |
|          | 427±10ms             | 422±7ms             |    0.99 | merge_concat.Concatenate.time_concatenate(True)                                             |
|          | 2.42±0G              | 2.42±0G             |    1    | merge_concat.Concatenate.tracemalloc_concatenate(False)                                     |
|          | 110±2M               | 110±5M              |    1    | merge_concat.Concatenate.tracemalloc_concatenate(True)                                      |
|          | 39.2±3ms             | 39.4±2ms            |    1    | merge_concat.Merge.time_merge                                                               |
|          | 126±0.03M            | 126±0.03M           |    1    | merge_concat.Merge.tracemalloc_merge                                                        |
|          | 366±4ns              | 369±1ns             |    1.01 | mesh.utils.regions_combine.CombineRegionsComputeRealData.time_compute_data(50)              |
|          | 194±1ms              | 195±1ms             |    1.01 | mesh.utils.regions_combine.CombineRegionsComputeRealData.time_compute_data(500)             |
|          | 771±0.5k             | 771±0.4k            |    1    | mesh.utils.regions_combine.CombineRegionsComputeRealData.tracemalloc_compute_data(50)       |
|          | 60.2±0M              | 60.2±0M             |    1    | mesh.utils.regions_combine.CombineRegionsComputeRealData.tracemalloc_compute_data(500)      |
|          | 18.4±0.2ms           | 18.9±0.2ms          |    1.03 | mesh.utils.regions_combine.CombineRegionsCreateCube.time_create_combined_cube(50)           |
|          | 21.8±0.5ms           | 21.7±0.3ms          |    1    | mesh.utils.regions_combine.CombineRegionsCreateCube.time_create_combined_cube(500)          |
|          | 1.27±0.04M           | 1.27±0.04M          |    1    | mesh.utils.regions_combine.CombineRegionsCreateCube.tracemalloc_create_combined_cube(50)    |
|          | 25±0.04M             | 25±0.04M            |    1    | mesh.utils.regions_combine.CombineRegionsCreateCube.tracemalloc_create_combined_cube(500)   |
|          | 119±0.7ms            | 118±1ms             |    1    | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.time_stream_file2file(50)         |
|          | 577±7ms              | 578±3ms             |    1    | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.time_stream_file2file(500)        |
|          | 1.46±0.03M           | 1.48±0.02M          |    1.01 | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.tracemalloc_stream_file2file(50)  |
|          | 96.5±0.02M           | 96.5±0.03M          |    1    | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.tracemalloc_stream_file2file(500) |
|          | 78.4±0.7ms           | 77.1±0.5ms          |    0.98 | mesh.utils.regions_combine.CombineRegionsSaveData.time_save(50)                             |
|          | 534±3ms              | 534±2ms             |    1    | mesh.utils.regions_combine.CombineRegionsSaveData.time_save(500)                            |
|          | 1.41±0.04M           | 1.41±0.02M          |    1    | mesh.utils.regions_combine.CombineRegionsSaveData.tracemalloc_save(50)                      |
|          | 96.4±0.03M           | 96.5±0.02M          |    1    | mesh.utils.regions_combine.CombineRegionsSaveData.tracemalloc_save(500)                     |
|          | 2.1752849999999997   | 2.1752849999999997  |    1    | mesh.utils.regions_combine.CombineRegionsSaveData.track_filesize_saved(50)                  |
|          | 216.01528499999998   | 216.01528499999998  |    1    | mesh.utils.regions_combine.CombineRegionsSaveData.track_filesize_saved(500)                 |
|          | 6.67±0.03ms          | 6.84±0.1ms          |    1.03 | plot.AuxSort.time_aux_sort                                                                  |
|          | 79.0±1ms             | 77.1±4ms            |    0.98 | regridding.CurvilinearRegridding.time_regrid_pic                                            |
|          | 136±3M               | 136±3M              |    1    | regridding.CurvilinearRegridding.tracemalloc_regrid_pic                                     |
|          | 105±6ms              | 104±6ms             |    0.99 | regridding.HorizontalChunkedRegridding.time_regrid_area_w                                   |
|          | 62.9±5ms             | 63.3±1ms            |    1.01 | regridding.HorizontalChunkedRegridding.time_regrid_area_w_new_grid                          |
|          | 107±0.08M            | 107±0.09M           |    1    | regridding.HorizontalChunkedRegridding.tracemalloc_regrid_area_w                            |
|          | 147±0.04M            | 147±0.04M           |    1    | regridding.HorizontalChunkedRegridding.tracemalloc_regrid_area_w_new_grid                   |
|          | 4.65±0.03ms          | 4.62±0.08ms         |    0.99 | save.NetcdfSave.time_netcdf_save_cube(50, False)                                            |
|          | 83.9±1ms             | 84.3±0.5ms          |    1    | save.NetcdfSave.time_netcdf_save_cube(50, True)                                             |
|          | 41.1±0.5ms           | 41.2±0.3ms          |    1    | save.NetcdfSave.time_netcdf_save_cube(600, False)                                           |
|          | 473±4ms              | 477±3ms             |    1.01 | save.NetcdfSave.time_netcdf_save_cube(600, True)                                            |
|          | 86.3±0.9ns           | 86.4±0.8ns          |    1    | save.NetcdfSave.time_netcdf_save_mesh(50, False)                                            |
|          | 64.0±1ms             | 63.9±0.9ms          |    1    | save.NetcdfSave.time_netcdf_save_mesh(50, True)                                             |
|          | 87.7±0.7ns           | 88.3±1ns            |    1.01 | save.NetcdfSave.time_netcdf_save_mesh(600, False)                                           |
|          | 414±3ms              | 415±3ms             |    1    | save.NetcdfSave.time_netcdf_save_mesh(600, True)                                            |
|          | 31.7±0.3k            | 31.8±0.4k           |    1    | save.NetcdfSave.tracemalloc_netcdf_save(50, False)                                          |
|          | 1.86±0.2M            | 1.9±0.2M            |    1.02 | save.NetcdfSave.tracemalloc_netcdf_save(50, True)                                           |
|          | 31.6±0.3k            | 31.8±0.3k           |    1.01 | save.NetcdfSave.tracemalloc_netcdf_save(600, False)                                         |
|          | 225±20M              | 208±20M             |    0.92 | save.NetcdfSave.tracemalloc_netcdf_save(600, True)                                          |
|          | 38.9±0.2ms           | 38.8±0.4ms          |    1    | stats.PearsonR.time_lazy                                                                    |
|          | 9.33±0.1ms           | 9.26±0.2ms          |    0.99 | stats.PearsonR.time_real                                                                    |
|          | 29.4±0.8M            | 29.4±1M             |    1    | stats.PearsonR.tracemalloc_lazy                                                             |
|          | 18.3±0.01M           | 18.3±0.01M          |    1    | stats.PearsonR.tracemalloc_real                                                             |
|          | 25.1±0.3ms           | 25.2±0.5ms          |    1    | trajectory.TrajectoryInterpolation.time_trajectory_linear                                   |
|          | 61.0±0.5ms           | 61.6±0.3ms          |    1.01 | trajectory.TrajectoryInterpolation.time_trajectory_nearest                                  |
|          | 17.6±0.02M           | 17.6±0.02M          |    1    | trajectory.TrajectoryInterpolation.tracemalloc_trajectory_linear                            |
|          | 7.75±0.02M           | 7.75±0.02M          |    1    | trajectory.TrajectoryInterpolation.tracemalloc_trajectory_nearest                           |

Generated by GHA run 16323043207

@pp-mo pp-mo force-pushed the nc_attribute_handlers branch from afa11e5 to 9d90853 Compare July 21, 2025 14:13
@pp-mo
Copy link
Member Author

pp-mo commented Jul 22, 2025

Review Notes : changes to consider

The code changes here provoked review of some older behaviour decisions.
As a result there are some small corners where I decided it was worth it to change behaviour slightly.

Here's a summary, which may not be entirely complete:

overall

  • suitable string values are now supported as well as objects, for GRIB_PARAM and STASH (stash already was)
  • GRIB_PARAM now reads+writes to netcdf (this was the main goal)
  • invalid (i.e. untranslatable) attributes in iris now all give warnings on write -- but a value is always written
  • invalid (i.e. untranslatable) attributes in file now all give warnings on read -- but a value is always read

STASH

  • an MSI string is also accepted (as before)
  • a warning is now issued if the STASH string is invalid on write (but it doesn't stop you).
    • a failed translation on write creates a file "STASH" attribute == does not translate to "um_stash_source"
    • [[a failed translation on read creates an iris "um_stash_source" attribute -- N.B. THIS IS NOT A CHANGE, but it might be a problem?]]

ukmo__process_flags

  • only a tuple of strings is now accepted, anything else gives an error on write
    • Previously, a string would be accepted, and broken into characters on save, and reconstructed as a tuple of single-char strings.
      • E.G. "abc" (iris) --> "a b c" (file) --> ("a", "b", "c") (iris again)
  • an empty string (file attribute) now produces an empty tuple (iris attribute) and vice versa
    • I.E. "" <--> () : Previously "" <-> ("",)
  • a string section of "<EMPTY>" (in file) now translates to/from an empty string (in iris).
    • E.G. "a <EMPTY> b" <-> ("a", "", "b")
    • [[this is really just for completeness, and could be removed]]

GRIB_PARAM

  • now saves the repr form of the GRIBCode to netcdf, and retrieves
  • string also works
    • (the translation is very free, just expects 4 decimals)

@pp-mo
Copy link
Member Author

pp-mo commented Jul 22, 2025

Uncertainty remaining

  • include iris_grib in testing : this now seems hard to avoid
  • not now sure that STASH behaviours on failure (new or old) quite make sense
    • because in these cases we can write a STASH and/or read a um_stash_source,
      when read- or written-back there is possibility of conflct (not allowed for in present code)

@pp-mo pp-mo force-pushed the nc_attribute_handlers branch from 78d96e8 to 7226b36 Compare July 22, 2025 00:22
@pp-mo pp-mo marked this pull request as ready for review July 22, 2025 10:32
@bjlittle bjlittle self-requested a review July 22, 2025 10:57
@bjlittle bjlittle self-assigned this Jul 22, 2025
@bjlittle bjlittle moved this to 🚀 In Progress in 🦋 Iris 3.13.0 Jul 22, 2025
@pp-mo
Copy link
Member Author

pp-mo commented Jul 22, 2025

Update:

include iris_grib in testing : this now seems hard to avoid

I'm now thinking that, without any further changes here, I can just import the tests which are here skipped due to iris-grib not being installed, and run them in the iris-grib repo instead.
See : SciTools/iris-grib#677

Does that sound like a good plan ?

Copy link
Member

@bjlittle bjlittle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pp-mo Looks good.

You've had to grapple with some serious sketchy historical code that's evolved over time. Unifying it should make it more understandable and maintainable.

I can't see any obvious behaviour changes that will upset users, so I'm happy to roll with what you've suggested and we can see whether there are any user edge cases that may require a patch or rebuffing.

Thanks for adding the additional test coverage 🤩💯

Only a few minors to service 👍

@pp-mo pp-mo force-pushed the nc_attribute_handlers branch from 1c43257 to 1080a1b Compare July 23, 2025 12:41
@pp-mo
Copy link
Member Author

pp-mo commented Jul 23, 2025

Thanks @bjlittle
I think I've covered all the review suggestions so far.
Please take another look.

@bjlittle bjlittle self-requested a review July 23, 2025 14:21
Copy link
Member

@bjlittle bjlittle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pp-mo It's lookin' good 💯

Just need a whatsnew entry and then we're good to roll 👍

@bjlittle bjlittle self-requested a review July 23, 2025 15:51
@bjlittle bjlittle merged commit e2236be into SciTools:main Jul 23, 2025
21 checks passed
@github-project-automation github-project-automation bot moved this from 👀 In Review to 🏁 Done in 🦋 Iris 3.13.0 Jul 23, 2025
@pp-mo pp-mo deleted the nc_attribute_handlers branch July 23, 2025 17:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
benchmark_this Request that this pull request be benchmarked to check if it introduces performance shifts
Projects
Status: Done
Status: 🏁 Done
Development

Successfully merging this pull request may close these issues.

"GRIB_PARAM" attributes are not saving to netcdf
2 participants