Repository: ecmwf/earthkit-data
Pull request #603 (2025-01-30 08:21:21)

feat: support string format specification for output file path template
# Purpose
Earthkit data currently allows specifying an output file name template such as `"output/{date}-{time}-{step}.grib2"`. In some cases, users might want to add some format specifiers, for instance zero-padding for the `step` value, like so: `"output/{date}-{time}-{step:03}.grib2"`. 

# Code changes
After the regex groups are extracted from the template string, split them with `:` and take the first element to get the values from the handles. 

FussyDuck: [![CLA assistant check](https://bol-claassistant.ecmwf.int:443/pull/badge/not_signed)](https://bol-claassistant.ecmwf.int:443/ecmwf/earthkit-data?pullRequest=603) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://bol-claassistant.ecmwf.int:443/ecmwf/earthkit-data?pullRequest=603) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://bol-claassistant.ecmwf.int:443/check/ecmwf/earthkit-data?pullRequest=603) it.</sub>
frazane: I didn't find any pre-existing tests for the output filename formatting so I didn't add any myself to test this new feature, because I am not sure about the best way to do it. Suggestions are welcome!
sandorkertesz: Thank you for this improvement. I have just added a test for the GRIB filename output pattern. I suggest you sync the latest `develop` code and add tests for your modifications in:

`tests/grib/test_grib_output.py/test_grib_output_filename_pattern()`

I hope it makes sense.
codecov-commenter: ## [Codecov](https://app.codecov.io/gh/ecmwf/earthkit-data/pull/603?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecmwf) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 90.28%. Comparing base [(`c481cef`)](https://app.codecov.io/gh/ecmwf/earthkit-data/commit/c481cefbf8115029912162084009a3727d5d6436?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecmwf) to head [(`cb9fdbd`)](https://app.codecov.io/gh/ecmwf/earthkit-data/commit/cb9fdbd4a485ac31928ba3b8d39d719f2a2f733b?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecmwf).
> Report is 1 commits behind head on develop.

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##           develop     #603      +/-   ##
===========================================
+ Coverage    90.27%   90.28%   +0.01%     
===========================================
  Files          153      153              
  Lines        11552    11565      +13     
  Branches       544      546       +2     
===========================================
+ Hits         10429    10442      +13     
  Misses         953      953              
  Partials       170      170              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/ecmwf/earthkit-data/pull/603?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecmwf).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecmwf).


Patch:
diff --git a/src/earthkit/data/readers/grib/output.py b/src/earthkit/data/readers/grib/output.py
index 429dda7..3b9e988 100644
--- a/src/earthkit/data/readers/grib/output.py
+++ b/src/earthkit/data/readers/grib/output.py
@@ -451,7 +451,8 @@ class GribOutput:
             return self.fileobj, None
 
         if self.split_output:
-            path = self.filename.format(**{k: handle.get(k) for k in self.split_output})
+            keys = {k.split(":")[0]: handle.get(k.split(":")[0]) for k in self.split_output}
+            path = self.filename.format(**keys)
         else:
             path = self.filename
 


Test patch:
diff --git a/tests/grib/test_grib_output.py b/tests/grib/test_grib_output.py
index 008bb2b..0744bc4 100644
--- a/tests/grib/test_grib_output.py
+++ b/tests/grib/test_grib_output.py
@@ -332,6 +332,7 @@ def test_grib_output_field_template(array):
         ("{shortName}", {"t": 2, "u": 2, "v": 2}),
         ("{shortName}_{level}", {"t_1000": 1, "t_850": 1, "u_1000": 1, "u_850": 1, "v_1000": 1, "v_850": 1}),
         ("{date}_{time}_{step}", {"20180801_1200_0": 6}),
+        ("{date}_{time}_{step:03}", {"20180801_1200_000": 6}),
     ],
 )
 def test_grib_output_filename_pattern(pattern, expected_value):