From 451f7205c888e3d7abb11e0d87730a26cc235f6b Mon Sep 17 00:00:00 2001 From: Shahram Najm Date: Wed, 19 Jun 2019 19:37:04 +0100 Subject: [PATCH 1/5] Latest changes --- builder.py | 5 ++-- gribapi/grib_api.h | 44 +++++++++++++++++++++++++++++++++++ gribapi/grib_api_prototypes.h | 3 --- gribapi/gribapi.py | 27 +++++++++++---------- 4 files changed, 62 insertions(+), 17 deletions(-) diff --git a/builder.py b/builder.py index 75fffcd..27da67a 100644 --- a/builder.py +++ b/builder.py @@ -1,16 +1,16 @@ import logging import cffi +import sys ffibuilder = cffi.FFI() ffibuilder.set_source( "gribapi._bindings", - '#include "grib_api_internal.h"\n#include ', + '#include ', libraries=["eccodes"], ) ffibuilder.cdef( open("gribapi/grib_api.h").read() + - open("gribapi/grib_api_prototypes.h").read() + open("gribapi/eccodes.h").read() ) @@ -19,3 +19,4 @@ ffibuilder.compile(verbose=True) except Exception: logging.exception("can't compile ecCodes bindings") + sys.exit(1) diff --git a/gribapi/grib_api.h b/gribapi/grib_api.h index cdadfa8..217849d 100644 --- a/gribapi/grib_api.h +++ b/gribapi/grib_api.h @@ -99,6 +99,8 @@ typedef struct grib_context grib_context; */ typedef struct grib_iterator grib_iterator; +typedef struct grib_nearest grib_nearest; + /*! Grib keys iterator. Iterator over keys. \ingroup keys_iterator */ @@ -386,6 +388,48 @@ int grib_iterator_next(grib_iterator *i, double* lat,double* lon,double* value); */ int grib_iterator_delete(grib_iterator *i); +/*! +* \brief Create a new nearest from a handle, using current geometry . +* +* \param h : the handle from which the iterator will be created +* \param error : error code +* \return the new nearest, NULL if no nearest can be created +*/ +grib_nearest* grib_nearest_new(grib_handle* h, int* error); + +/** +* Find the 4 nearest points of a latitude longitude point. +* The flags are provided to speed up the process of searching. If you are +* sure that the point you are asking for is not changing from a call +* to another you can use GRIB_NEAREST_SAME_POINT. The same is valid for +* the grid. Flags can be used together doing a bitwise OR. +* The distances are given in kilometres. +* +* @param nearest : nearest structure +* @param h : handle from which geography and data values are taken +* @param inlat : latitude of the point to search for +* @param inlon : longitude of the point to search for +* @param flags : GRIB_NEAREST_SAME_POINT, GRIB_NEAREST_SAME_GRID +* @param outlats : returned array of latitudes of the nearest points +* @param outlons : returned array of longitudes of the nearest points +* @param values : returned array of data values of the nearest points +* @param distances : returned array of distances from the nearest points +* @param indexes : returned array of indexes of the nearest points +* @param len : size of the arrays +* @return 0 if OK, integer value on error +*/ +int grib_nearest_find(grib_nearest *nearest,grib_handle* h,double inlat,double inlon, + unsigned long flags,double* outlats,double* outlons, + double* values,double* distances,int* indexes,size_t *len); + +/** +* Frees an nearest from memory +* +* @param nearest : the nearest +* @return 0 if OK, integer value on error +*/ +int grib_nearest_delete(grib_nearest *nearest); + /** * Find the nearest point of a set of points whose latitudes and longitudes * are given in the inlats, inlons arrays respectively. diff --git a/gribapi/grib_api_prototypes.h b/gribapi/grib_api_prototypes.h index 2be6391..6fc54ac 100644 --- a/gribapi/grib_api_prototypes.h +++ b/gribapi/grib_api_prototypes.h @@ -1,7 +1,4 @@ - /* grib_handle.c */ grib_handle *grib_new_from_file(grib_context *c, FILE *f, int headers_only, int *error); grib_handle *gts_new_from_file(grib_context *c, FILE *f, int *error); grib_handle *metar_new_from_file(grib_context *c, FILE *f, int *error); - -int parse_keyval_string(const char *grib_tool, char *arg, int values_required, int default_type, grib_values values[], int *count); diff --git a/gribapi/gribapi.py b/gribapi/gribapi.py index 3ab660e..7b616a2 100644 --- a/gribapi/gribapi.py +++ b/gribapi/gribapi.py @@ -214,11 +214,11 @@ def gts_new_from_file(fileobj, headers_only=False): @brief Load in memory a GTS message from a file. The message can be accessed through its id and will be available\n - until @ref grib_release is called.\n + until @ref codes_release is called.\n @param fileobj python file object @param headers_only whether or not to load the message with the headers only - @return id of the GTS loaded in memory + @return id of the GTS loaded in memory or None @exception GribInternalError """ #err, h = err_last(lib.gts_new_from_file)(ffi.NULL, fileobj) @@ -241,11 +241,11 @@ def metar_new_from_file(fileobj, headers_only=False): @brief Load in memory a METAR message from a file. The message can be accessed through its id and will be available\n - until @ref grib_release is called.\n + until @ref codes_release is called.\n @param fileobj python file object @param headers_only whether or not to load the message with the headers only - @return id of the METAR loaded in memory + @return id of the METAR loaded in memory or None @exception GribInternalError """ #err, h = err_last(lib.metar_new_from_file)(ffi.NULL, fileobj) @@ -268,14 +268,14 @@ def codes_new_from_file(fileobj, product_kind, headers_only=False): @brief Load in memory a message from a file for a given product. The message can be accessed through its id and will be available\n - until @ref grib_release is called.\n + until @ref codes_release is called.\n \b Examples: \ref get_product_kind.py "get_product_kind.py" @param fileobj python file object @param product_kind one of CODES_PRODUCT_GRIB, CODES_PRODUCT_BUFR, CODES_PRODUCT_METAR or CODES_PRODUCT_GTS @param headers_only whether or not to load the message with the headers only - @return id of the message loaded in memory + @return id of the message loaded in memory or None @exception GribInternalError """ if product_kind == CODES_PRODUCT_GRIB: @@ -297,13 +297,13 @@ def any_new_from_file(fileobj, headers_only=False): @brief Load in memory a message from a file. The message can be accessed through its id and will be available\n - until @ref grib_release is called.\n + until @ref codes_release is called.\n \b Examples: \ref grib_get_keys.py "grib_get_keys.py" @param fileobj python file object @param headers_only whether or not to load the message with the headers only - @return id of the message loaded in memory + @return id of the message loaded in memory or None @exception GribInternalError """ err, h = err_last(lib.codes_handle_new_from_file)(ffi.NULL, fileobj, CODES_PRODUCT_ANY) @@ -325,13 +325,13 @@ def bufr_new_from_file(fileobj, headers_only=False): @brief Load in memory a BUFR message from a file. The message can be accessed through its id and will be available\n - until @ref grib_release is called.\n + until @ref codes_release is called.\n \b Examples: \ref bufr_get_keys.py "bufr_get_keys.py" @param fileobj python file object @param headers_only whether or not to load the message with the headers only - @return id of the BUFR loaded in memory + @return id of the BUFR loaded in memory or None @exception GribInternalError """ err, h = err_last(lib.codes_handle_new_from_file)(ffi.NULL, fileobj, CODES_PRODUCT_BUFR) @@ -353,7 +353,7 @@ def grib_new_from_file(fileobj, headers_only=False): @brief Load in memory a GRIB message from a file. The message can be accessed through its gribid and will be available\n - until @ref grib_release is called.\n + until @ref codes_release is called.\n The message can be loaded headers only by using the headers_only argument. Default is to have the headers only option set to off (False). If set to on (True), @@ -366,7 +366,7 @@ def grib_new_from_file(fileobj, headers_only=False): @param fileobj python file object @param headers_only whether or not to load the message with the headers only - @return id of the grib loaded in memory + @return id of the grib loaded in memory or None @exception GribInternalError """ #err, h = err_last(lib.grib_new_from_file)(ffi.NULL, fileobj, headers_only) @@ -2060,6 +2060,9 @@ def grib_get_api_version(): def div(v, d): return (v / d, v % d) + if not lib: + raise Exception("Could not load the ecCodes library!") + v = lib.grib_get_api_version() v, revision = div(v, 100) v, minor = div(v, 100) From edf40c0b53d3810f650aec248e312f3b2539b5e8 Mon Sep 17 00:00:00 2001 From: Shahram Najm Date: Wed, 19 Jun 2019 19:40:17 +0100 Subject: [PATCH 2/5] Latest changes --- gribapi/grib_api.h | 1 + 1 file changed, 1 insertion(+) diff --git a/gribapi/grib_api.h b/gribapi/grib_api.h index 217849d..0d4b79d 100644 --- a/gribapi/grib_api.h +++ b/gribapi/grib_api.h @@ -797,6 +797,7 @@ int grib_is_defined(grib_handle* h, const char* key); int grib_set_missing(grib_handle* h, const char* key); int grib_get_message_size ( grib_handle* h,size_t* size); +int parse_keyval_string(const char *grib_tool, char *arg, int values_required, int default_type, grib_values values[], int *count); /*! \defgroup errors Error codes Error codes returned by the grib_api functions. From e20d21bbb27b4c8e387ff4b275b12cdcb1ca1145 Mon Sep 17 00:00:00 2001 From: Shahram Najm Date: Fri, 5 Jul 2019 16:45:49 +0100 Subject: [PATCH 3/5] Fix PEP8 style issues and README file updates --- CHANGELOG.rst | 8 +++++--- README.rst | 8 +++----- gribapi/bindings.py | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c1c6e91..ebb40b5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,11 +2,13 @@ Changelog for eccodes-python ============================ -0.9.2 (unreleased) +0.9.2 ------------------ -- Nothing changed yet. - +- All ecCodes tests now pass +- Simplify the xx_new_from_file calls +- Fix for grib_set_string_array +- Use ECCODES_DIR to locate the library 0.9.1 (2019-06-06) ------------------ diff --git a/README.rst b/README.rst index 3324447..2e1578e 100644 --- a/README.rst +++ b/README.rst @@ -16,8 +16,7 @@ Limitations: - *CFFI* ABI level, in-line mode is almost twice as slow as the original *ecCodes* bindings, - only experimental support for the much faster *CFFI* API level, out-of-line mode, - *PyPI* binary packages do not include *ecCodes*, -- incomplete documentation, for now, -- no Windows support, for now. +- Microsoft Windows support is untested. Installation @@ -71,15 +70,14 @@ Fast bindings ------------- To test the much faster *CFFI* API level, out-of-line mode you need the *ecCodes* -source tree available, because we need two include files ``grib_api_internal.h`` -and ``grib_api_prototypes.h`` that are not installed by default. +header files. Then you need to clone the repo in the same folder as your *ecCodes* source tree, make a ``pip`` development install and custom compile the binary bindings:: $ git clone https://github.com/ecmwf/eccodes-python $ cd eccodes-python $ pip install -e . - $ CPPFLAGS=-I../eccodes/src python builder.py + $ python builder.py To revert back to ABI level, in-line more just remove the compiled bindings:: diff --git a/gribapi/bindings.py b/gribapi/bindings.py index a705dbb..52e7cee 100644 --- a/gribapi/bindings.py +++ b/gribapi/bindings.py @@ -25,7 +25,7 @@ import cffi -__version__ = '0.9.2.dev0' +__version__ = '0.9.2' LOG = logging.getLogger(__name__) From 57119727747a0ffd75793a1237db8946e70cde7e Mon Sep 17 00:00:00 2001 From: Shahram Najm Date: Mon, 8 Jul 2019 11:20:52 +0100 Subject: [PATCH 4/5] Style --- gribapi/gribapi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gribapi/gribapi.py b/gribapi/gribapi.py index 7b616a2..96ff9b1 100644 --- a/gribapi/gribapi.py +++ b/gribapi/gribapi.py @@ -221,7 +221,7 @@ def gts_new_from_file(fileobj, headers_only=False): @return id of the GTS loaded in memory or None @exception GribInternalError """ - #err, h = err_last(lib.gts_new_from_file)(ffi.NULL, fileobj) + # err, h = err_last(lib.gts_new_from_file)(ffi.NULL, fileobj) err, h = err_last(lib.codes_handle_new_from_file)(ffi.NULL, fileobj, CODES_PRODUCT_GTS) if err: if err == lib.GRIB_END_OF_FILE: @@ -248,7 +248,7 @@ def metar_new_from_file(fileobj, headers_only=False): @return id of the METAR loaded in memory or None @exception GribInternalError """ - #err, h = err_last(lib.metar_new_from_file)(ffi.NULL, fileobj) + # err, h = err_last(lib.metar_new_from_file)(ffi.NULL, fileobj) err, h = err_last(lib.codes_handle_new_from_file)(ffi.NULL, fileobj, CODES_PRODUCT_METAR) if err: if err == lib.GRIB_END_OF_FILE: @@ -369,7 +369,7 @@ def grib_new_from_file(fileobj, headers_only=False): @return id of the grib loaded in memory or None @exception GribInternalError """ - #err, h = err_last(lib.grib_new_from_file)(ffi.NULL, fileobj, headers_only) + # err, h = err_last(lib.grib_new_from_file)(ffi.NULL, fileobj, headers_only) err, h = err_last(lib.codes_handle_new_from_file)(ffi.NULL, fileobj, CODES_PRODUCT_GRIB) if err: if err == lib.GRIB_END_OF_FILE: From a93e4b89be73cc90dff8d6d0ade1c005d8fa9288 Mon Sep 17 00:00:00 2001 From: Shahram Najm Date: Mon, 8 Jul 2019 11:35:09 +0100 Subject: [PATCH 5/5] Use RuntimeError --- gribapi/gribapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gribapi/gribapi.py b/gribapi/gribapi.py index 96ff9b1..0967f86 100644 --- a/gribapi/gribapi.py +++ b/gribapi/gribapi.py @@ -2061,7 +2061,7 @@ def div(v, d): return (v / d, v % d) if not lib: - raise Exception("Could not load the ecCodes library!") + raise RuntimeError("Could not load the ecCodes library!") v = lib.grib_get_api_version() v, revision = div(v, 100)