eccodes 2.40.1__tar.gz → 2.42.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.
Files changed (45) hide show
  1. {eccodes-2.40.1 → eccodes-2.42.0}/CHANGELOG.rst +15 -0
  2. {eccodes-2.40.1 → eccodes-2.42.0}/Makefile +1 -1
  3. {eccodes-2.40.1 → eccodes-2.42.0}/PKG-INFO +18 -3
  4. {eccodes-2.40.1 → eccodes-2.42.0}/ci/requirements-docs.txt +3 -3
  5. {eccodes-2.40.1 → eccodes-2.42.0}/eccodes/eccodes.py +2 -0
  6. {eccodes-2.40.1 → eccodes-2.42.0}/eccodes/highlevel/__init__.py +1 -1
  7. {eccodes-2.40.1 → eccodes-2.42.0}/eccodes/highlevel/message.py +20 -2
  8. {eccodes-2.40.1 → eccodes-2.42.0}/eccodes/highlevel/reader.py +21 -11
  9. {eccodes-2.40.1 → eccodes-2.42.0}/eccodes.egg-info/PKG-INFO +18 -3
  10. {eccodes-2.40.1 → eccodes-2.42.0}/gribapi/__init__.py +2 -2
  11. {eccodes-2.40.1 → eccodes-2.42.0}/gribapi/bindings.py +1 -1
  12. {eccodes-2.40.1 → eccodes-2.42.0}/gribapi/grib_api.h +1 -0
  13. {eccodes-2.40.1 → eccodes-2.42.0}/gribapi/gribapi.py +25 -2
  14. {eccodes-2.40.1 → eccodes-2.42.0}/setup.py +0 -1
  15. {eccodes-2.40.1 → eccodes-2.42.0}/tests/test_eccodes.py +25 -0
  16. {eccodes-2.40.1 → eccodes-2.42.0}/tests/test_highlevel.py +9 -1
  17. {eccodes-2.40.1 → eccodes-2.42.0}/tox.ini +1 -1
  18. {eccodes-2.40.1 → eccodes-2.42.0}/.dockerignore +0 -0
  19. {eccodes-2.40.1 → eccodes-2.42.0}/CONTRIBUTING.rst +0 -0
  20. {eccodes-2.40.1 → eccodes-2.42.0}/Dockerfile +0 -0
  21. {eccodes-2.40.1 → eccodes-2.42.0}/LICENSE +0 -0
  22. {eccodes-2.40.1 → eccodes-2.42.0}/MANIFEST.in +0 -0
  23. {eccodes-2.40.1 → eccodes-2.42.0}/README.rst +0 -0
  24. {eccodes-2.40.1 → eccodes-2.42.0}/builder.py +0 -0
  25. {eccodes-2.40.1 → eccodes-2.42.0}/ci/requirements-dev.txt +0 -0
  26. {eccodes-2.40.1 → eccodes-2.42.0}/ci/requirements-docs.in +0 -0
  27. {eccodes-2.40.1 → eccodes-2.42.0}/ci/requirements-tests.in +0 -0
  28. {eccodes-2.40.1 → eccodes-2.42.0}/ci/requirements-tests.txt +0 -0
  29. {eccodes-2.40.1 → eccodes-2.42.0}/docs/_static/.gitkeep +0 -0
  30. {eccodes-2.40.1 → eccodes-2.42.0}/docs/conf.py +0 -0
  31. {eccodes-2.40.1 → eccodes-2.42.0}/docs/index.rst +0 -0
  32. {eccodes-2.40.1 → eccodes-2.42.0}/eccodes/__init__.py +0 -0
  33. {eccodes-2.40.1 → eccodes-2.42.0}/eccodes/__main__.py +0 -0
  34. {eccodes-2.40.1 → eccodes-2.42.0}/eccodes.egg-info/SOURCES.txt +0 -0
  35. {eccodes-2.40.1 → eccodes-2.42.0}/eccodes.egg-info/dependency_links.txt +0 -0
  36. {eccodes-2.40.1 → eccodes-2.42.0}/eccodes.egg-info/requires.txt +0 -0
  37. {eccodes-2.40.1 → eccodes-2.42.0}/eccodes.egg-info/top_level.txt +0 -0
  38. {eccodes-2.40.1 → eccodes-2.42.0}/eccodes.egg-info/zip-safe +0 -0
  39. {eccodes-2.40.1 → eccodes-2.42.0}/gribapi/eccodes.h +0 -0
  40. {eccodes-2.40.1 → eccodes-2.42.0}/gribapi/errors.py +0 -0
  41. {eccodes-2.40.1 → eccodes-2.42.0}/setup.cfg +0 -0
  42. {eccodes-2.40.1 → eccodes-2.42.0}/tests/sample-data/era5-levels-members.grib +0 -0
  43. {eccodes-2.40.1 → eccodes-2.42.0}/tests/sample-data/tiggelam_cnmc_sfc.grib2 +0 -0
  44. {eccodes-2.40.1 → eccodes-2.42.0}/tests/test_20_main.py +0 -0
  45. {eccodes-2.40.1 → eccodes-2.42.0}/tests/test_20_messages.py +0 -0
@@ -2,6 +2,21 @@
2
2
  Changelog for eccodes-python
3
3
  ============================
4
4
 
5
+ 2.42.0 (2025-06-25)
6
+ --------------------
7
+
8
+ - ECC-2081: Allow memoryview as input to codes_new_from_message
9
+ - ECC-2086: GRIB: getting the bitmap using up all the memory
10
+ - ECC-2072: high-level 'get' function should return default value if key is not implemented
11
+
12
+
13
+ 2.41.0 (2025-04-10)
14
+ --------------------
15
+
16
+ - ECC-2034: GRIB encoding: Data quality checks via the API
17
+ - GitHub pull request #124: Add basic BUFR support (high-level interface)
18
+
19
+
5
20
  2.40.1 (2025-03-17)
6
21
  --------------------
7
22
 
@@ -2,7 +2,7 @@
2
2
  PACKAGE := eccodes-python
3
3
  IMAGE := $(PACKAGE)-image
4
4
  MODULE := eccodes
5
- PYTHONS := python3.9 python3.8 pypy3
5
+ PYTHONS := python3.9 pypy3
6
6
  PYTHON := python
7
7
 
8
8
  PYTESTFLAGS_TEST := -v --flakes --doctest-glob '*.rst' --cov=$(MODULE) --cov-report=html --cache-clear
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: eccodes
3
- Version: 2.40.1
3
+ Version: 2.42.0
4
4
  Summary: Python interface to the ecCodes GRIB and BUFR decoder/encoder
5
5
  Home-page: https://github.com/ecmwf/eccodes-python
6
6
  Author: European Centre for Medium-Range Weather Forecasts (ECMWF)
@@ -10,7 +10,6 @@ Keywords: ecCodes GRIB BUFR
10
10
  Classifier: Development Status :: 4 - Beta
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: License :: OSI Approved :: Apache Software License
13
- Classifier: Programming Language :: Python :: 3.8
14
13
  Classifier: Programming Language :: Python :: 3.9
15
14
  Classifier: Programming Language :: Python :: 3.10
16
15
  Classifier: Programming Language :: Python :: 3.11
@@ -31,6 +30,7 @@ Dynamic: description
31
30
  Dynamic: home-page
32
31
  Dynamic: keywords
33
32
  Dynamic: license
33
+ Dynamic: license-file
34
34
  Dynamic: requires-dist
35
35
  Dynamic: summary
36
36
 
@@ -210,6 +210,21 @@ does it submit to any jurisdiction.
210
210
  Changelog for eccodes-python
211
211
  ============================
212
212
 
213
+ 2.42.0 (2025-06-25)
214
+ --------------------
215
+
216
+ - ECC-2081: Allow memoryview as input to codes_new_from_message
217
+ - ECC-2086: GRIB: getting the bitmap using up all the memory
218
+ - ECC-2072: high-level 'get' function should return default value if key is not implemented
219
+
220
+
221
+ 2.41.0 (2025-04-10)
222
+ --------------------
223
+
224
+ - ECC-2034: GRIB encoding: Data quality checks via the API
225
+ - GitHub pull request #124: Add basic BUFR support (high-level interface)
226
+
227
+
213
228
  2.40.1 (2025-03-17)
214
229
  --------------------
215
230
 
@@ -18,7 +18,7 @@ idna==3.7
18
18
  # via requests
19
19
  imagesize==1.1.0
20
20
  # via sphinx
21
- jinja2==3.1.5
21
+ jinja2==3.1.6
22
22
  # via sphinx
23
23
  markupsafe==2.1.5
24
24
  # via jinja2
@@ -32,7 +32,7 @@ pytest-runner==4.4
32
32
  # via -r requirements-docs.in
33
33
  pytz==2018.9
34
34
  # via babel
35
- requests==2.32.2
35
+ requests==2.32.4
36
36
  # via sphinx
37
37
  six==1.12.0
38
38
  # via
@@ -44,5 +44,5 @@ sphinx==1.8.5
44
44
  # via -r requirements-docs.in
45
45
  sphinxcontrib-websupport==1.1.0
46
46
  # via sphinx
47
- urllib3==1.26.19
47
+ urllib3==2.4.0
48
48
  # via requests
@@ -132,6 +132,7 @@ from gribapi import grib_no_fail_on_wrong_length as codes_no_fail_on_wrong_lengt
132
132
  from gribapi import grib_release as codes_release
133
133
  from gribapi import grib_set as codes_set
134
134
  from gribapi import grib_set_array as codes_set_array
135
+ from gribapi import grib_set_data_quality_checks as codes_set_data_quality_checks
135
136
  from gribapi import grib_set_debug as codes_set_debug
136
137
  from gribapi import grib_set_definitions_path as codes_set_definitions_path
137
138
  from gribapi import grib_set_double as codes_set_double
@@ -348,6 +349,7 @@ __all__ = [
348
349
  "codes_samples_path",
349
350
  "codes_dump",
350
351
  "codes_set_array",
352
+ "codes_set_data_quality_checks",
351
353
  "codes_set_debug",
352
354
  "codes_set_definitions_path",
353
355
  "codes_set_double_array",
@@ -1,2 +1,2 @@
1
- from .message import GRIBMessage, Message # noqa
1
+ from .message import BUFRMessage, GRIBMessage, Message # noqa
2
2
  from .reader import FileReader, MemoryReader, StreamReader # noqa
@@ -17,7 +17,7 @@ def raise_keyerror(name):
17
17
  """Make operations on a key raise a KeyError if not found"""
18
18
  try:
19
19
  yield
20
- except eccodes.KeyValueNotFoundError:
20
+ except (eccodes.KeyValueNotFoundError, eccodes.FunctionNotImplementedError):
21
21
  raise KeyError(name)
22
22
 
23
23
 
@@ -33,7 +33,7 @@ class Message:
33
33
 
34
34
  def copy(self):
35
35
  """Create a copy of the current message"""
36
- return Message(eccodes.codes_clone(self._handle))
36
+ return self.__class__(eccodes.codes_clone(self._handle))
37
37
 
38
38
  def __copy__(self):
39
39
  return self.copy()
@@ -242,6 +242,24 @@ class Message:
242
242
  return eccodes.codes_get_message(self._handle)
243
243
 
244
244
 
245
+ class BUFRMessage(Message):
246
+ def __init__(self, handle):
247
+ super().__init__(handle)
248
+
249
+ def pack(self):
250
+ """Pack the underlying data"""
251
+ self.set("pack", 1, check_values=False)
252
+
253
+ def unpack(self):
254
+ """Unpack the underlying data"""
255
+ self.set("unpack", 1, check_values=False)
256
+
257
+ @classmethod
258
+ def from_samples(cls, name):
259
+ """Create a message from a sample"""
260
+ return cls(eccodes.codes_bufr_new_from_samples(name))
261
+
262
+
245
263
  class GRIBMessage(Message):
246
264
  def __init__(self, handle):
247
265
  super().__init__(handle)
@@ -2,12 +2,22 @@ import eccodes
2
2
  import gribapi
3
3
  from gribapi import ffi
4
4
 
5
- from .message import GRIBMessage
5
+ from .message import BUFRMessage, GRIBMessage
6
+
7
+ _MSG_CLASSES = {
8
+ eccodes.CODES_PRODUCT_GRIB: GRIBMessage,
9
+ eccodes.CODES_PRODUCT_BUFR: BUFRMessage,
10
+ }
6
11
 
7
12
 
8
13
  class ReaderBase:
9
- def __init__(self):
14
+ def __init__(self, kind=eccodes.CODES_PRODUCT_GRIB):
10
15
  self._peeked = None
16
+ self._kind = kind
17
+ cls = _MSG_CLASSES.get(kind)
18
+ if cls is None:
19
+ raise ValueError(f"Unsupported product type {kind}")
20
+ self._msg_class = cls
11
21
 
12
22
  def __iter__(self):
13
23
  return self
@@ -20,7 +30,7 @@ class ReaderBase:
20
30
  handle = self._next_handle()
21
31
  if handle is None:
22
32
  raise StopIteration
23
- return GRIBMessage(handle)
33
+ return self._msg_class(handle)
24
34
 
25
35
  def _next_handle(self):
26
36
  raise NotImplementedError
@@ -36,19 +46,19 @@ class ReaderBase:
36
46
  if self._peeked is None:
37
47
  handle = self._next_handle()
38
48
  if handle is not None:
39
- self._peeked = GRIBMessage(handle)
49
+ self._peeked = self._msg_class(handle)
40
50
  return self._peeked
41
51
 
42
52
 
43
53
  class FileReader(ReaderBase):
44
54
  """Read messages from a file"""
45
55
 
46
- def __init__(self, path):
47
- super().__init__()
56
+ def __init__(self, path, kind=eccodes.CODES_PRODUCT_GRIB):
57
+ super().__init__(kind=kind)
48
58
  self.file = open(path, "rb")
49
59
 
50
60
  def _next_handle(self):
51
- return eccodes.codes_new_from_file(self.file, eccodes.CODES_PRODUCT_GRIB)
61
+ return eccodes.codes_new_from_file(self.file, self._kind)
52
62
 
53
63
  def __enter__(self):
54
64
  self.file.__enter__()
@@ -61,8 +71,8 @@ class FileReader(ReaderBase):
61
71
  class MemoryReader(ReaderBase):
62
72
  """Read messages from memory"""
63
73
 
64
- def __init__(self, buf):
65
- super().__init__()
74
+ def __init__(self, buf, kind=eccodes.CODES_PRODUCT_GRIB):
75
+ super().__init__(kind=kind)
66
76
  self.buf = buf
67
77
 
68
78
  def _next_handle(self):
@@ -128,14 +138,14 @@ def codes_new_from_stream(stream):
128
138
  class StreamReader(ReaderBase):
129
139
  """Read messages from a stream (an object with a ``read`` method)"""
130
140
 
131
- def __init__(self, stream):
141
+ def __init__(self, stream, kind=eccodes.CODES_PRODUCT_GRIB):
132
142
  if cstd is None:
133
143
  raise OSError("This feature is not supported on Windows")
134
144
  if pyread_callback is None:
135
145
  raise OSError(
136
146
  "This feature cannot be used because the OS prevents allocating write+execute memory"
137
147
  )
138
- super().__init__()
148
+ super().__init__(kind=kind)
139
149
  self.stream = stream
140
150
 
141
151
  def _next_handle(self):
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: eccodes
3
- Version: 2.40.1
3
+ Version: 2.42.0
4
4
  Summary: Python interface to the ecCodes GRIB and BUFR decoder/encoder
5
5
  Home-page: https://github.com/ecmwf/eccodes-python
6
6
  Author: European Centre for Medium-Range Weather Forecasts (ECMWF)
@@ -10,7 +10,6 @@ Keywords: ecCodes GRIB BUFR
10
10
  Classifier: Development Status :: 4 - Beta
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: License :: OSI Approved :: Apache Software License
13
- Classifier: Programming Language :: Python :: 3.8
14
13
  Classifier: Programming Language :: Python :: 3.9
15
14
  Classifier: Programming Language :: Python :: 3.10
16
15
  Classifier: Programming Language :: Python :: 3.11
@@ -31,6 +30,7 @@ Dynamic: description
31
30
  Dynamic: home-page
32
31
  Dynamic: keywords
33
32
  Dynamic: license
33
+ Dynamic: license-file
34
34
  Dynamic: requires-dist
35
35
  Dynamic: summary
36
36
 
@@ -210,6 +210,21 @@ does it submit to any jurisdiction.
210
210
  Changelog for eccodes-python
211
211
  ============================
212
212
 
213
+ 2.42.0 (2025-06-25)
214
+ --------------------
215
+
216
+ - ECC-2081: Allow memoryview as input to codes_new_from_message
217
+ - ECC-2086: GRIB: getting the bitmap using up all the memory
218
+ - ECC-2072: high-level 'get' function should return default value if key is not implemented
219
+
220
+
221
+ 2.41.0 (2025-04-10)
222
+ --------------------
223
+
224
+ - ECC-2034: GRIB encoding: Data quality checks via the API
225
+ - GitHub pull request #124: Add basic BUFR support (high-level interface)
226
+
227
+
213
228
  2.40.1 (2025-03-17)
214
229
  --------------------
215
230
 
@@ -14,8 +14,8 @@ from .gribapi import * # noqa
14
14
  from .gribapi import __version__, lib
15
15
 
16
16
  # The minimum recommended version for the ecCodes package
17
- min_recommended_version_str = "2.38.0"
18
- min_recommended_version_int = 23800
17
+ min_recommended_version_str = "2.39.0"
18
+ min_recommended_version_int = 23900
19
19
 
20
20
  if lib.grib_get_api_version() < min_recommended_version_int:
21
21
  import warnings
@@ -23,7 +23,7 @@ import sys
23
23
 
24
24
  import cffi
25
25
 
26
- __version__ = "2.40.1"
26
+ __version__ = "2.42.0"
27
27
 
28
28
  LOG = logging.getLogger(__name__)
29
29
 
@@ -133,6 +133,7 @@ void grib_gribex_mode_on(grib_context* c);
133
133
  void grib_gribex_mode_off(grib_context* c);
134
134
  void grib_context_set_definitions_path(grib_context* c, const char* path);
135
135
  void grib_context_set_debug(grib_context* c, int mode);
136
+ void grib_context_set_data_quality_checks(grib_context* c, int val);
136
137
  void grib_context_set_samples_path(grib_context* c, const char* path);
137
138
  void grib_multi_support_on(grib_context* c);
138
139
  void grib_multi_support_off(grib_context* c);
@@ -20,7 +20,7 @@ Numpy is a package used for scientific computing in Python and an efficient cont
20
20
 
21
21
  @em Requirements:
22
22
 
23
- - Python 3.8 or higher
23
+ - Python 3.9 or higher
24
24
  - NumPy
25
25
 
26
26
  """
@@ -2044,6 +2044,10 @@ def grib_get_array(msgid, key, ktype=None):
2044
2044
  if ktype is None:
2045
2045
  ktype = grib_get_native_type(msgid, key)
2046
2046
 
2047
+ # ECC-2086
2048
+ if ktype is bytes and key == "bitmap":
2049
+ return grib_get_long_array(msgid, key)
2050
+
2047
2051
  result = None
2048
2052
  if ktype is int:
2049
2053
  result = grib_get_long_array(msgid, key)
@@ -2389,7 +2393,7 @@ def grib_get_message(msgid):
2389
2393
  return fixed_length_buffer[:]
2390
2394
 
2391
2395
 
2392
- @require(message=(bytes, str))
2396
+ @require(message=(bytes, str, memoryview))
2393
2397
  def grib_new_from_message(message):
2394
2398
  """
2395
2399
  @brief Create a handle from a message in memory.
@@ -2402,8 +2406,12 @@ def grib_new_from_message(message):
2402
2406
  @return msgid of the newly created message
2403
2407
  @exception CodesInternalError
2404
2408
  """
2409
+ if isinstance(message, memoryview):
2410
+ message = ffi.from_buffer(message)
2411
+
2405
2412
  if isinstance(message, str):
2406
2413
  message = message.encode(ENC)
2414
+
2407
2415
  h = lib.grib_handle_new_from_message_copy(ffi.NULL, message, len(message))
2408
2416
  if h == ffi.NULL:
2409
2417
  raise errors.MessageInvalidError("new_from_message failed")
@@ -2438,6 +2446,21 @@ def grib_set_debug(dmode):
2438
2446
  lib.grib_context_set_debug(context, dmode)
2439
2447
 
2440
2448
 
2449
+ @require(val=int)
2450
+ def grib_set_data_quality_checks(val):
2451
+ """
2452
+ @brief Enable/Disable GRIB data quality checks
2453
+
2454
+ @param val 0, 1 or 2
2455
+ 0 -> disable data quality checks
2456
+ 1 -> failure results in an error
2457
+ 2 -> failure results in a warning
2458
+ """
2459
+ assert val == 0 or val == 1 or val == 2
2460
+ context = lib.grib_context_get_default()
2461
+ lib.grib_context_set_data_quality_checks(context, val)
2462
+
2463
+
2441
2464
  @require(defs_path=str)
2442
2465
  def grib_set_definitions_path(defs_path):
2443
2466
  """
@@ -113,7 +113,6 @@ setuptools.setup(
113
113
  "Development Status :: 4 - Beta",
114
114
  "Intended Audience :: Developers",
115
115
  "License :: OSI Approved :: Apache Software License",
116
- "Programming Language :: Python :: 3.8",
117
116
  "Programming Language :: Python :: 3.9",
118
117
  "Programming Language :: Python :: 3.10",
119
118
  "Programming Language :: Python :: 3.11",
@@ -55,6 +55,14 @@ def test_codes_set_debug():
55
55
  eccodes.codes_set_debug(0)
56
56
 
57
57
 
58
+ def test_codes_set_data_quality_checks():
59
+ if eccodes.codes_get_api_version(int) < 24100:
60
+ pytest.skip("ecCodes version too old")
61
+ eccodes.codes_set_data_quality_checks(2)
62
+ eccodes.codes_set_data_quality_checks(1)
63
+ eccodes.codes_set_data_quality_checks(0)
64
+
65
+
58
66
  def test_codes_set_definitions_path():
59
67
  eccodes.codes_set_definitions_path(eccodes.codes_definition_path())
60
68
 
@@ -179,6 +187,22 @@ def test_new_from_message():
179
187
  # eccodes.codes_release(newgid)
180
188
 
181
189
 
190
+ def test_new_from_message_memoryview():
191
+ # ECC-2081
192
+ fpath = get_sample_fullpath("gg_sfc_grib1.tmpl")
193
+ if fpath is None:
194
+ return
195
+ with open(fpath, "rb") as f:
196
+ data = f.read()
197
+ mv = memoryview(data)
198
+ assert len(mv) == 26860
199
+ newgid = eccodes.codes_new_from_message(mv)
200
+ assert eccodes.codes_get(newgid, "packingType") == "grid_simple"
201
+ assert eccodes.codes_get(newgid, "gridType") == "reduced_gg"
202
+ assert eccodes.codes_get(newgid, "totalLength") == 26860
203
+ eccodes.codes_release(newgid)
204
+
205
+
182
206
  def test_gts_header():
183
207
  eccodes.codes_gts_header(True)
184
208
  eccodes.codes_gts_header(False)
@@ -615,6 +639,7 @@ def test_grib_set_bitmap():
615
639
  assert eccodes.codes_get(gid, "numberOfDataPoints") == 100
616
640
  assert eccodes.codes_get(gid, "numberOfCodedValues") == 98
617
641
  assert eccodes.codes_get(gid, "numberOfMissing") == 2
642
+ eccodes.codes_get_array(gid, "bitmap")
618
643
  eccodes.codes_release(gid)
619
644
 
620
645
 
@@ -44,6 +44,9 @@ def test_message_get():
44
44
  assert message["gridType:int"] == 0
45
45
  with pytest.raises(KeyError):
46
46
  message["invalid"]
47
+ with pytest.raises(KeyError):
48
+ message["gridSpec"]
49
+ assert message.get("gridSpec", dummy_default) is dummy_default
47
50
  # keys set as MISSING
48
51
  assert message.is_missing(known_missing)
49
52
  assert message.get(known_missing) is None
@@ -166,8 +169,13 @@ def test_write_message(tmp_path):
166
169
  assert np.all(message1.data == message2.data)
167
170
 
168
171
 
169
- def test_message_from_samples():
172
+ def test_grib_message_from_samples():
170
173
  message = eccodes.GRIBMessage.from_samples("regular_ll_sfc_grib2")
171
174
  assert message["edition"] == 2
172
175
  assert message["gridType"] == "regular_ll"
173
176
  assert message["levtype"] == "sfc"
177
+
178
+
179
+ def test_bufr_message_from_samples():
180
+ message = eccodes.BUFRMessage.from_samples("BUFR4")
181
+ assert message["edition"] == 4
@@ -12,7 +12,7 @@ deps = -r{toxinidir}/ci/requirements-docs.txt
12
12
  commands = sphinx-build -W -b html docs build/sphinx/html
13
13
 
14
14
  [testenv:qc]
15
- basepython = python3.8
15
+ basepython = python3.9
16
16
  # needed for pytest-cov
17
17
  usedevelop = true
18
18
  commands = pytest -v --flakes --pep8 --mccabe --cov=eccodes --doctest-glob="*.rst" --cov-report=html --cache-clear --basetemp={envtmpdir} {posargs}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes