From d138ed13f6a2719dc54806fcc9cd86d67dc60cb0 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Wed, 20 Dec 2023 11:19:58 +0100 Subject: [PATCH 1/4] Call the Python version of import_array() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new approach follows this example: The C version is a macro with control flow that Cython does not know anything about. It introduces a C type error. This fixes a build failure with some compilers: src/_geoslib.c: In function ‘__pyx_pymod_exec__geoslib’: src/_geoslib.c:8755:3: error: returning ‘void *’ from a function with return type ‘int’ makes integer from pointer without a cast 8755 | import_array(); | ^~~~~~~~~~~~ --- packages/basemap/src/_geoslib.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/basemap/src/_geoslib.pyx b/packages/basemap/src/_geoslib.pyx index e078b4f30..74be404f6 100644 --- a/packages/basemap/src/_geoslib.pyx +++ b/packages/basemap/src/_geoslib.pyx @@ -1,5 +1,6 @@ import sys import numpy +cimport numpy as cnp __version__ = "0.3" @@ -20,10 +21,9 @@ cdef extern from "numpy/arrayobject.h": cdef int flags npy_intp PyArray_SIZE(ndarray arr) npy_intp PyArray_ISCONTIGUOUS(ndarray arr) - void import_array() # Initialize numpy -import_array() +cnp.import_array() # GENERAL NOTES: # From 73326b40bed70e7b06a089d99968861e143ce9e9 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Wed, 20 Dec 2023 11:24:52 +0100 Subject: [PATCH 2/4] Change GEOSGeom to GEOSGeometry and GEOSCoordSeq to GEOSCoordSequence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The C types GEOSGeom and GEOSCoordSeq are themselves pointer types. The underlying struct typedefs are called GEOSGeometry and GEOSCoordSequence. This fixes C compilation errors with future compilers: src/_geoslib.c:6918:28: error: passing argument 1 of ‘GEOSCoordSeq_setY’ from incompatible pointer type 6918 | (void)(GEOSCoordSeq_setY(__pyx_v_cs, 0, __pyx_v_dy)); | ^~~~~~~~~~ | | | GEOSCoordSequence ** {aka struct GEOSCoordSeq_t **} /usr/include/geos_c.h:2194:58: note: expected ‘GEOSCoordSequence *’ {aka ‘struct GEOSCoordSeq_t *’} but argument is of type ‘GEOSCoordSequence **’ {aka ‘struct GEOSCoordSeq_t **’} 2194 | extern int GEOS_DLL GEOSCoordSeq_setY(GEOSCoordSequence* s, | ~~~~~~~~~~~~~~~~~~~^ --- packages/basemap/src/_geoslib.pyx | 128 +++++++++++++++--------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/packages/basemap/src/_geoslib.pyx b/packages/basemap/src/_geoslib.pyx index 74be404f6..a5671262c 100644 --- a/packages/basemap/src/_geoslib.pyx +++ b/packages/basemap/src/_geoslib.pyx @@ -47,9 +47,9 @@ cdef extern from "geos_c.h": GEOS_MULTIPOLYGON GEOS_GEOMETRYCOLLECTION GEOS_VERSION_MAJOR - ctypedef struct GEOSGeom: + ctypedef struct GEOSGeometry: pass - ctypedef struct GEOSCoordSeq: + ctypedef struct GEOSCoordSequence: pass # Cython 3: Next ctypedef needs "noexcept" declaration unless # the compiler directive "legacy_implicit_noexcept" is used @@ -58,55 +58,55 @@ cdef extern from "geos_c.h": char *GEOSversion() void initGEOS(GEOSMessageHandler notice_function, GEOSMessageHandler error_function) void finishGEOS() - GEOSCoordSeq *GEOSCoordSeq_create(unsigned int size, unsigned int dims) - void GEOSCoordSeq_destroy(GEOSCoordSeq* s) - int GEOSCoordSeq_setX(GEOSCoordSeq* s,unsigned int idx, double val) - int GEOSCoordSeq_setY(GEOSCoordSeq* s,unsigned int idx, double val) - int GEOSCoordSeq_getX(GEOSCoordSeq* s, unsigned int idx, double *val) - int GEOSCoordSeq_getY(GEOSCoordSeq* s, unsigned int idx, double *val) - GEOSGeom *GEOSUnion(GEOSGeom* g1, GEOSGeom* g2) - GEOSGeom *GEOSUnaryUnion(GEOSGeom* g1) - GEOSGeom *GEOSEnvelope(GEOSGeom* g1) - GEOSGeom *GEOSConvexHull(GEOSGeom* g1) - GEOSGeom *GEOSGeom_createPoint(GEOSCoordSeq* s) - GEOSGeom *GEOSGeom_createLineString(GEOSCoordSeq* s) - GEOSGeom *GEOSGeom_createPolygon(GEOSGeom* shell, GEOSGeom** holes, unsigned int nholes) - GEOSGeom *GEOSGeom_createLinearRing(GEOSCoordSeq* s) - void GEOSGeom_destroy(GEOSGeom* g) + GEOSCoordSequence *GEOSCoordSeq_create(unsigned int size, unsigned int dims) + void GEOSCoordSeq_destroy(GEOSCoordSequence* s) + int GEOSCoordSeq_setX(GEOSCoordSequence* s,unsigned int idx, double val) + int GEOSCoordSeq_setY(GEOSCoordSequence* s,unsigned int idx, double val) + int GEOSCoordSeq_getX(GEOSCoordSequence* s, unsigned int idx, double *val) + int GEOSCoordSeq_getY(GEOSCoordSequence* s, unsigned int idx, double *val) + GEOSGeometry *GEOSUnion(GEOSGeometry* g1, GEOSGeometry* g2) + GEOSGeometry *GEOSUnaryUnion(GEOSGeometry* g1) + GEOSGeometry *GEOSEnvelope(GEOSGeometry* g1) + GEOSGeometry *GEOSConvexHull(GEOSGeometry* g1) + GEOSGeometry *GEOSGeom_createPoint(GEOSCoordSequence* s) + GEOSGeometry *GEOSGeom_createLineString(GEOSCoordSequence* s) + GEOSGeometry *GEOSGeom_createPolygon(GEOSGeometry* shell, GEOSGeometry** holes, unsigned int nholes) + GEOSGeometry *GEOSGeom_createLinearRing(GEOSCoordSequence* s) + void GEOSGeom_destroy(GEOSGeometry* g) # Topology operations - return NULL on exception. - GEOSGeom *GEOSIntersection(GEOSGeom* g1, GEOSGeom* g2) - GEOSGeom *GEOSSimplify(GEOSGeom* g1, double tolerance) - GEOSGeom *GEOSBuffer(GEOSGeom* g1, double width, int quadsegs) - GEOSGeom *GEOSTopologyPreserveSimplify(GEOSGeom* g1, double tolerance) + GEOSGeometry *GEOSIntersection(GEOSGeometry* g1, GEOSGeometry* g2) + GEOSGeometry *GEOSSimplify(GEOSGeometry* g1, double tolerance) + GEOSGeometry *GEOSBuffer(GEOSGeometry* g1, double width, int quadsegs) + GEOSGeometry *GEOSTopologyPreserveSimplify(GEOSGeometry* g1, double tolerance) # Binary/Unary predicate - return 2 on exception, 1 on true, 0 on false - char GEOSIntersects(GEOSGeom* g1, GEOSGeom* g2) - char GEOSWithin(GEOSGeom* g1, GEOSGeom* g2) - char GEOSContains(GEOSGeom* g1, GEOSGeom* g2) - char GEOSisEmpty(GEOSGeom* g1) - char GEOSisValid(GEOSGeom* g1) - char GEOSisSimple(GEOSGeom* g1) - char GEOSisRing(GEOSGeom* g1) + char GEOSIntersects(GEOSGeometry* g1, GEOSGeometry* g2) + char GEOSWithin(GEOSGeometry* g1, GEOSGeometry* g2) + char GEOSContains(GEOSGeometry* g1, GEOSGeometry* g2) + char GEOSisEmpty(GEOSGeometry* g1) + char GEOSisValid(GEOSGeometry* g1) + char GEOSisSimple(GEOSGeometry* g1) + char GEOSisRing(GEOSGeometry* g1) # Geometry info - char *GEOSGeomType(GEOSGeom* g1) - int GEOSGeomTypeId(GEOSGeom* g1) + char *GEOSGeomType(GEOSGeometry* g1) + int GEOSGeomTypeId(GEOSGeometry* g1) # Functions: Return 0 on exception, 1 otherwise - int GEOSArea(GEOSGeom* g1, double *area) - int GEOSLength(GEOSGeom* g1, double *length) + int GEOSArea(GEOSGeometry* g1, double *area) + int GEOSLength(GEOSGeometry* g1, double *length) # returns -1 on error and 1 for non-multi geoms - int GEOSGetNumGeometries(GEOSGeom* g1) + int GEOSGetNumGeometries(GEOSGeometry* g1) # Return NULL on exception, Geometry must be a Collection. # Returned object is a pointer to internal storage: # it must NOT be destroyed directly. - GEOSGeom *GEOSGetGeometryN(GEOSGeom* g, int n) - int GEOSGetNumInteriorRings(GEOSGeom* g1) + GEOSGeometry *GEOSGetGeometryN(GEOSGeometry* g, int n) + int GEOSGetNumInteriorRings(GEOSGeometry* g1) # Return NULL on exception, Geometry must be a Polygon. # Returned object is a pointer to internal storage: # it must NOT be destroyed directly. - GEOSGeom *GEOSGetExteriorRing(GEOSGeom* g) + GEOSGeometry *GEOSGetExteriorRing(GEOSGeometry* g) # Return NULL on exception. # Geometry must be a LineString, LinearRing or Point. - GEOSCoordSeq *GEOSGeom_getCoordSeq(GEOSGeom* g) - int GEOSCoordSeq_getSize(GEOSCoordSeq *s, unsigned int *size) + GEOSCoordSequence *GEOSGeom_getCoordSeq(GEOSGeometry* g) + int GEOSCoordSeq_getSize(GEOSCoordSequence *s, unsigned int *size) # Cython 3: Next cdef needs "noexcept" declaration unless # the compiler directive "legacy_implicit_noexcept" is used @@ -145,7 +145,7 @@ __geos_major_version__ = GEOS_VERSION_MAJOR initGEOS(notice_h, error_h) cdef class BaseGeometry: - cdef GEOSGeom *_geom + cdef GEOSGeometry *_geom cdef unsigned int _npts cdef public object boundary @@ -161,8 +161,8 @@ cdef class BaseGeometry: return PyBytes_FromString(GEOSGeomType(self._geom)) def within(self, BaseGeometry geom): - cdef GEOSGeom *g1 - cdef GEOSGeom *g2 + cdef GEOSGeometry *g1 + cdef GEOSGeometry *g2 cdef char answer g1 = self._geom g2 = geom._geom @@ -173,10 +173,10 @@ cdef class BaseGeometry: return False def union(self, BaseGeometry geom): - cdef GEOSGeom *g1 - cdef GEOSGeom *g2 - cdef GEOSGeom *g3 - cdef GEOSGeom *gout + cdef GEOSGeometry *g1 + cdef GEOSGeometry *g2 + cdef GEOSGeometry *g3 + cdef GEOSGeometry *gout cdef int numgeoms, i, typeid g1 = self._geom g2 = geom._geom @@ -206,9 +206,9 @@ cdef class BaseGeometry: return p def simplify(self, tol): - cdef GEOSGeom *g1 - cdef GEOSGeom *g3 - cdef GEOSGeom *gout + cdef GEOSGeometry *g1 + cdef GEOSGeometry *g3 + cdef GEOSGeometry *gout cdef double tolerance cdef int numgeoms, i, typeid g1 = self._geom @@ -239,9 +239,9 @@ cdef class BaseGeometry: return p def fix(self): - cdef GEOSGeom *g1 - cdef GEOSGeom *g3 - cdef GEOSGeom *gout + cdef GEOSGeometry *g1 + cdef GEOSGeometry *g3 + cdef GEOSGeometry *gout cdef int numgeoms, i, typeid g1 = self._geom g3 = GEOSBuffer(g1, 0., 0) @@ -270,8 +270,8 @@ cdef class BaseGeometry: return p def intersects(self, BaseGeometry geom): - cdef GEOSGeom *g1 - cdef GEOSGeom *g2 + cdef GEOSGeometry *g1 + cdef GEOSGeometry *g2 cdef char answer g1 = self._geom g2 = geom._geom @@ -282,10 +282,10 @@ cdef class BaseGeometry: return False def intersection(self, BaseGeometry geom): - cdef GEOSGeom *g1 - cdef GEOSGeom *g2 - cdef GEOSGeom *g3 - cdef GEOSGeom *gout + cdef GEOSGeometry *g1 + cdef GEOSGeometry *g2 + cdef GEOSGeometry *g3 + cdef GEOSGeometry *gout cdef char answer cdef int numgeoms, i, typeid g1 = self._geom @@ -341,8 +341,8 @@ cdef class Polygon(BaseGeometry): cdef unsigned int M, m, i cdef double dx, dy cdef double *bbuffer - cdef GEOSCoordSeq *cs - cdef GEOSGeom *lr + cdef GEOSCoordSequence *cs + cdef GEOSGeometry *lr @@ -396,7 +396,7 @@ cdef class Polygon(BaseGeometry): cdef class LineString(BaseGeometry): def __init__(self, ndarray b): cdef double dx, dy - cdef GEOSCoordSeq *cs + cdef GEOSCoordSequence *cs cdef int i, M cdef double *bbuffer @@ -429,7 +429,7 @@ cdef class Point(BaseGeometry): cdef public x,y def __init__(self, b): cdef double dx, dy - cdef GEOSCoordSeq *cs + cdef GEOSCoordSequence *cs # Create a coordinate sequence cs = GEOSCoordSeq_create(1, 2) dx = b[0]; dy = b[1] @@ -439,9 +439,9 @@ cdef class Point(BaseGeometry): self._npts = 1 self.boundary = b -cdef _get_coords(GEOSGeom *geom): - cdef GEOSCoordSeq *cs - cdef GEOSGeom *lr +cdef _get_coords(GEOSGeometry *geom): + cdef GEOSCoordSequence *cs + cdef GEOSGeometry *lr cdef unsigned int i, M cdef double dx, dy cdef ndarray b From 0d90252ac0218eabcb333212af2faa2f159abe85 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Wed, 20 Dec 2023 11:27:38 +0100 Subject: [PATCH 3/4] Fix const correctness issues This avoids lots of C compiler warnings. --- packages/basemap/src/_geoslib.pyx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/basemap/src/_geoslib.pyx b/packages/basemap/src/_geoslib.pyx index a5671262c..d6d6f4826 100644 --- a/packages/basemap/src/_geoslib.pyx +++ b/packages/basemap/src/_geoslib.pyx @@ -105,8 +105,8 @@ cdef extern from "geos_c.h": GEOSGeometry *GEOSGetExteriorRing(GEOSGeometry* g) # Return NULL on exception. # Geometry must be a LineString, LinearRing or Point. - GEOSCoordSequence *GEOSGeom_getCoordSeq(GEOSGeometry* g) - int GEOSCoordSeq_getSize(GEOSCoordSequence *s, unsigned int *size) + GEOSCoordSequence *GEOSGeom_getCoordSeq(const GEOSGeometry* g) + int GEOSCoordSeq_getSize(const GEOSCoordSequence *s, unsigned int *size) # Cython 3: Next cdef needs "noexcept" declaration unless # the compiler directive "legacy_implicit_noexcept" is used @@ -176,7 +176,7 @@ cdef class BaseGeometry: cdef GEOSGeometry *g1 cdef GEOSGeometry *g2 cdef GEOSGeometry *g3 - cdef GEOSGeometry *gout + cdef const GEOSGeometry *gout cdef int numgeoms, i, typeid g1 = self._geom g2 = geom._geom @@ -208,7 +208,7 @@ cdef class BaseGeometry: def simplify(self, tol): cdef GEOSGeometry *g1 cdef GEOSGeometry *g3 - cdef GEOSGeometry *gout + cdef const GEOSGeometry *gout cdef double tolerance cdef int numgeoms, i, typeid g1 = self._geom @@ -241,7 +241,7 @@ cdef class BaseGeometry: def fix(self): cdef GEOSGeometry *g1 cdef GEOSGeometry *g3 - cdef GEOSGeometry *gout + cdef const GEOSGeometry *gout cdef int numgeoms, i, typeid g1 = self._geom g3 = GEOSBuffer(g1, 0., 0) @@ -285,7 +285,7 @@ cdef class BaseGeometry: cdef GEOSGeometry *g1 cdef GEOSGeometry *g2 cdef GEOSGeometry *g3 - cdef GEOSGeometry *gout + cdef const GEOSGeometry *gout cdef char answer cdef int numgeoms, i, typeid g1 = self._geom @@ -439,9 +439,9 @@ cdef class Point(BaseGeometry): self._npts = 1 self.boundary = b -cdef _get_coords(GEOSGeometry *geom): - cdef GEOSCoordSequence *cs - cdef GEOSGeometry *lr +cdef _get_coords(const GEOSGeometry *geom): + cdef const GEOSCoordSequence *cs + cdef const GEOSGeometry *lr cdef unsigned int i, M cdef double dx, dy cdef ndarray b From c181b790282a3215fcb3aa2d06d25c7960a83e19 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Wed, 20 Dec 2023 11:33:06 +0100 Subject: [PATCH 4/4] Fix type error in initGEOS() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function pointers passed to initGEOS() did not have the correct type. A proper fix needs support in Cython, which is currently missing. This fixes a build failure with Clang 16 and GCC 14. src/_geoslib.c: In function ‘__pyx_pymod_exec__geoslib’: src/_geoslib.c:8803:12: error: passing argument 1 of ‘initGEOS’ from incompatible pointer type 8803 | initGEOS(__pyx_f_8_geoslib_notice_h, __pyx_f_8_geoslib_error_h); | ^~~~~~~~~~~~~~~~~~~~~~~~~~ | | | void (*)(char *, char *) In file included from src/_geoslib.c:1219: /usr/include/geos_c.h:2074:24: note: expected ‘GEOSMessageHandler’ {aka ‘void (*)(const char *, ...)’} but argument is of type ‘void (*)(char *, char *)’ 2074 | GEOSMessageHandler notice_function, | ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~ src/_geoslib.c:8803:40: error: passing argument 2 of ‘initGEOS’ from incompatible pointer type 8803 | initGEOS(__pyx_f_8_geoslib_notice_h, __pyx_f_8_geoslib_error_h); | ^~~~~~~~~~~~~~~~~~~~~~~~~ | | | void (*)(char *, char *) /usr/include/geos_c.h:2075:24: note: expected ‘GEOSMessageHandler’ {aka ‘void (*)(const char *, ...)’} but argument is of type ‘void (*)(char *, char *)’ 2075 | GEOSMessageHandler error_function); | ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~ --- packages/basemap/src/_geoslib.pyx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/basemap/src/_geoslib.pyx b/packages/basemap/src/_geoslib.pyx index d6d6f4826..d92071b63 100644 --- a/packages/basemap/src/_geoslib.pyx +++ b/packages/basemap/src/_geoslib.pyx @@ -54,7 +54,7 @@ cdef extern from "geos_c.h": # Cython 3: Next ctypedef needs "noexcept" declaration unless # the compiler directive "legacy_implicit_noexcept" is used # ("noexcept" syntax supported since Cython 0.29.31). - ctypedef void (*GEOSMessageHandler)(char *fmt, char *list) + ctypedef void (*GEOSMessageHandler)(const char *fmt, ...) char *GEOSversion() void initGEOS(GEOSMessageHandler notice_function, GEOSMessageHandler error_function) void finishGEOS() @@ -111,7 +111,7 @@ cdef extern from "geos_c.h": # Cython 3: Next cdef needs "noexcept" declaration unless # the compiler directive "legacy_implicit_noexcept" is used # ("noexcept" syntax supported since Cython 0.29.31). -cdef void notice_h(char *fmt, char*msg): +cdef void notice_h(const char *fmt, ...): pass #format = PyBytes_FromString(fmt) #message = PyBytes_FromString(msg) @@ -124,7 +124,9 @@ cdef void notice_h(char *fmt, char*msg): # Cython 3: Next cdef needs "noexcept" declaration unless # the compiler directive "legacy_implicit_noexcept" is used # ("noexcept" syntax supported since Cython 0.29.31). -cdef void error_h(char *fmt, char*msg): +# FIXME: The type should be: error_h(const char *fmt, ...), but +# Cython does not currently support varargs functions. +cdef void error_h(const char *fmt, char*msg): format = PyBytes_FromString(fmt) message = PyBytes_FromString(msg) try: @@ -142,7 +144,7 @@ __geos_major_version__ = GEOS_VERSION_MAJOR # raise ValueError('version 2.2.3 of the geos library is required') # intialize GEOS (parameters are notice and error function callbacks). -initGEOS(notice_h, error_h) +initGEOS(notice_h, error_h) cdef class BaseGeometry: cdef GEOSGeometry *_geom