Skip to content

Commit 788fe0d

Browse files
committed
Merge develop to master
2 parents ac08ddf + afe1010 commit 788fe0d

File tree

11 files changed

+160
-46
lines changed

11 files changed

+160
-46
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ on:
44
# Trigger the workflow on push to master or develop, except tag creation
55
push:
66
branches:
7-
- 'master'
8-
- 'develop'
7+
- "master"
8+
- "develop"
99
tags-ignore:
10-
- '**'
10+
- "**"
1111

1212
# Trigger the workflow on pull request
1313
pull_request: ~
@@ -27,6 +27,7 @@ jobs:
2727
uses: ecmwf-actions/downstream-ci/.github/workflows/downstream-ci.yml@main
2828
with:
2929
eccodes-python: ecmwf/eccodes-python@${{ github.event.pull_request.head.sha || github.sha }}
30+
codecov_upload: true
3031
secrets: inherit
3132

3233
# Build downstream packages on HPC

CHANGELOG.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
Changelog for eccodes-python
33
============================
44

5+
1.7.0 (2024-02-26)
6+
--------------------
7+
- ECC-1761: Add function to extract message offsets and sizes
8+
- ECC-1742: Add function to clone only the meta-data of a message
9+
510
1.6.1 (2023-10-02)
611
--------------------
712

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Features:
88

99
- reads and writes GRIB 1 and 2 files,
1010
- reads and writes BUFR 3 and 4 files,
11-
- supports all modern versions of Python 3.11, 3.10, 3.9, 3.8 and PyPy3,
11+
- supports all modern versions of Python and PyPy3,
1212
- works on most *Linux* distributions and *MacOS*, the *ecCodes* C-library
1313
is the only system dependency,
1414
- PyPI package can be installed without compiling,

docs/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Features:
1515

1616
- reads and writes GRIB 1 and 2 files,
1717
- reads and writes BUFR 3 and 4 files,
18-
- supports all modern versions of Python 3.11, 3.10, 3.9, 3.8 and PyPy3,
18+
- supports all modern versions of Python and PyPy3,
1919
- works on most *Linux* distributions and *MacOS*, the *ecCodes* C-library is the only system dependency,
2020
- PyPI package can be installed without compiling,
2121
at the cost of being twice as slow as the original *ecCodes* module,

eccodes/eccodes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
codes_definition_path,
4444
codes_dump,
4545
codes_extract_offsets,
46+
codes_extract_offsets_sizes,
4647
codes_get_gaussian_latitudes,
4748
codes_get_library_path,
4849
codes_get_version_info,
@@ -245,6 +246,7 @@
245246
"codes_count_in_file",
246247
"codes_definition_path",
247248
"codes_extract_offsets",
249+
"codes_extract_offsets_sizes",
248250
"codes_get_api_version",
249251
"codes_get_array",
250252
"codes_get_double_array",

gribapi/bindings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
import cffi
2323

24-
__version__ = "1.6.1"
24+
__version__ = "1.7.0"
2525

2626
LOG = logging.getLogger(__name__)
2727

gribapi/eccodes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ void codes_bufr_multi_element_constant_arrays_on(codes_context* c);
2222
void codes_bufr_multi_element_constant_arrays_off(codes_context* c);
2323
int codes_bufr_extract_headers_malloc(codes_context* c, const char* filename, codes_bufr_header** result, int* num_messages, int strict_mode);
2424
int codes_extract_offsets_malloc(codes_context* c, const char* filename, ProductKind product, long int** offsets, int* num_messages, int strict_mode);
25+
int codes_extract_offsets_sizes_malloc(codes_context* c, const char* filename, ProductKind product, long int** offsets, size_t** sizes, int* num_messages, int strict_mode);
2526
int codes_bufr_key_is_header(const codes_handle* h, const char* key, int* err);
2627
int codes_bufr_key_is_coordinate(const codes_handle* h, const char* key, int* err);
2728

gribapi/errors.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ class InvalidIndexError(GribInternalError):
192192

193193

194194
class InvalidGribError(GribInternalError):
195-
"""Invalid grib id."""
195+
"""Invalid GRIB id."""
196196

197197

198198
class InvalidFileError(GribInternalError):

gribapi/grib_api.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ struct grib_values {
3838
int has_value;
3939
int equal;
4040
grib_values* next;
41-
} ;
41+
};
4242

4343
typedef struct grib_handle grib_handle;
4444
typedef struct grib_multi_handle grib_multi_handle;
@@ -74,7 +74,8 @@ int grib_count_in_file(grib_context* c, FILE* f,int* n);
7474
grib_handle* grib_handle_new_from_file(grib_context* c, FILE* f, int* error);
7575
grib_handle* grib_handle_new_from_message_copy(grib_context* c, const void* data, size_t data_len);
7676
grib_handle* grib_handle_new_from_samples (grib_context* c, const char* sample_name);
77-
grib_handle* grib_handle_clone(const grib_handle* h) ;
77+
grib_handle* grib_handle_clone(const grib_handle* h);
78+
grib_handle* grib_handle_clone_headers_only(const grib_handle* h);
7879
int grib_handle_delete(grib_handle* h);
7980
grib_multi_handle* grib_multi_handle_new(grib_context* c);
8081
int grib_multi_handle_append(grib_handle* h,int start_section,grib_multi_handle* mh);
@@ -125,7 +126,7 @@ void grib_dump_content(const grib_handle* h, FILE* out, const char* mode, unsign
125126
grib_context* grib_context_get_default(void);
126127
void grib_context_delete(grib_context* c);
127128

128-
void grib_gts_header_on(grib_context* c) ;
129+
void grib_gts_header_on(grib_context* c);
129130
void grib_gts_header_off(grib_context* c);
130131
void grib_gribex_mode_on(grib_context* c);
131132
void grib_gribex_mode_off(grib_context* c);

gribapi/gribapi.py

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,13 @@ def wrapper(*args):
162162
def get_handle(msgid):
163163
h = ffi.cast("grib_handle*", msgid)
164164
if h == ffi.NULL:
165-
raise errors.InvalidGribError(f"get_handle: Bad message ID {msgid}")
165+
raise errors.NullHandleError(f"get_handle: Bad message ID {msgid}")
166166
return h
167167

168168

169169
def put_handle(handle):
170170
if handle == ffi.NULL:
171-
raise errors.InvalidGribError(f"put_handle: Bad message ID {handle}")
171+
raise errors.NullHandleError("put_handle: Bad message ID (handle is NULL)")
172172
return int(ffi.cast("size_t", handle))
173173

174174

@@ -752,12 +752,10 @@ def grib_iterator_next(iterid):
752752
lat_p = ffi.new("double*")
753753
lon_p = ffi.new("double*")
754754
value_p = ffi.new("double*")
755-
err = lib.grib_iterator_next(iterh, lat_p, lon_p, value_p)
756-
if err == 0:
755+
retval = lib.grib_iterator_next(iterh, lat_p, lon_p, value_p)
756+
if retval == 0:
757+
# No more data available. End of iteration
757758
return []
758-
elif err < 0:
759-
GRIB_CHECK(err)
760-
return None
761759
else:
762760
return (lat_p[0], lon_p[0], value_p[0])
763761

@@ -803,8 +801,7 @@ def grib_keys_iterator_next(iterid):
803801
"""
804802
kih = get_grib_keys_iterator(iterid)
805803
res = lib.grib_keys_iterator_next(kih)
806-
if res < 0:
807-
GRIB_CHECK(res)
804+
# res is 0 or 1
808805
return res
809806

810807

@@ -887,8 +884,7 @@ def codes_bufr_keys_iterator_next(iterid):
887884
"""
888885
bki = get_bufr_keys_iterator(iterid)
889886
res = lib.codes_bufr_keys_iterator_next(bki)
890-
if res < 0:
891-
GRIB_CHECK(res)
887+
# res is 0 or 1
892888
return res
893889

894890

@@ -1121,23 +1117,29 @@ def codes_bufr_copy_data(msgid_src, msgid_dst):
11211117

11221118

11231119
@require(msgid_src=int)
1124-
def grib_clone(msgid_src):
1120+
def grib_clone(msgid_src, headers_only=False):
11251121
r"""
11261122
@brief Create a copy of a message.
11271123
11281124
Create a copy of a given message (\em msgid_src) resulting in a new
11291125
message in memory (\em msgid_dest) identical to the original one.
1126+
If the headers_only option is enabled, the clone will not contain
1127+
the Bitmap and Data sections
11301128
11311129
\b Examples: \ref grib_clone.py "grib_clone.py"
11321130
1133-
@param msgid_src id of message to be cloned
1134-
@return id of clone
1131+
@param msgid_src id of message to be cloned
1132+
@param headers_only whether or not to clone the message with the headers only
1133+
@return id of clone
11351134
@exception CodesInternalError
11361135
"""
11371136
h_src = get_handle(msgid_src)
1138-
h_dest = lib.grib_handle_clone(h_src)
1137+
if headers_only:
1138+
h_dest = lib.grib_handle_clone_headers_only(h_src)
1139+
else:
1140+
h_dest = lib.grib_handle_clone(h_src)
11391141
if h_dest == ffi.NULL:
1140-
raise errors.InvalidGribError("clone failed")
1142+
raise errors.MessageInvalidError("clone failed")
11411143
return put_handle(h_dest)
11421144

11431145

@@ -2381,7 +2383,7 @@ def grib_new_from_message(message):
23812383
message = message.encode(ENC)
23822384
h = lib.grib_handle_new_from_message_copy(ffi.NULL, message, len(message))
23832385
if h == ffi.NULL:
2384-
raise errors.InvalidGribError("new_from_message failed")
2386+
raise errors.MessageInvalidError("new_from_message failed")
23852387
return put_handle(h)
23862388

23872389

@@ -2555,10 +2557,10 @@ def codes_extract_offsets(filepath, product_kind, is_strict=True):
25552557
"""
25562558
@brief Message offset extraction
25572559
2558-
@param filepath path of input file
2559-
@product_kind one of CODES_PRODUCT_GRIB, CODES_PRODUCT_BUFR, CODES_PRODUCT_ANY or CODES_PRODUCT_GTS
2560-
@param is_strict if True, fail as soon as any invalid message is encountered
2561-
@return a generator that yields offsets (each offset is an integer)
2560+
@param filepath path of input file
2561+
@param product_kind one of CODES_PRODUCT_GRIB, CODES_PRODUCT_BUFR, CODES_PRODUCT_ANY or CODES_PRODUCT_GTS
2562+
@param is_strict if True, fail as soon as any invalid message is encountered
2563+
@return a generator that yields offsets (as integers)
25622564
@exception CodesInternalError
25632565
"""
25642566
context = lib.grib_context_get_default()
@@ -2579,6 +2581,42 @@ def codes_extract_offsets(filepath, product_kind, is_strict=True):
25792581
i += 1
25802582

25812583

2584+
def codes_extract_offsets_sizes(filepath, product_kind, is_strict=True):
2585+
"""
2586+
@brief Message offset and size extraction
2587+
2588+
@param filepath path of input file
2589+
@param product_kind one of CODES_PRODUCT_GRIB, CODES_PRODUCT_BUFR, CODES_PRODUCT_ANY or CODES_PRODUCT_GTS
2590+
@param is_strict if True, fail as soon as any invalid message is encountered
2591+
@return a generator that yields lists of pairs of offsets and sizes (as integers)
2592+
@exception CodesInternalError
2593+
"""
2594+
context = lib.grib_context_get_default()
2595+
offsets_p = ffi.new("long int**")
2596+
sizes_p = ffi.new("size_t**")
2597+
num_message_p = ffi.new("int*")
2598+
2599+
err = lib.codes_extract_offsets_sizes_malloc(
2600+
context,
2601+
filepath.encode(ENC),
2602+
product_kind,
2603+
offsets_p,
2604+
sizes_p,
2605+
num_message_p,
2606+
is_strict,
2607+
)
2608+
GRIB_CHECK(err)
2609+
2610+
num_messages = num_message_p[0]
2611+
offsets = offsets_p[0]
2612+
sizes = sizes_p[0]
2613+
2614+
i = 0
2615+
while i < num_messages:
2616+
yield (offsets[i], sizes[i])
2617+
i += 1
2618+
2619+
25822620
# -------------------------------
25832621
# EXPERIMENTAL FEATURES
25842622
# -------------------------------

0 commit comments

Comments
 (0)