Skip to content

Commit

Permalink
Move exception squashing to _err.pyx
Browse files Browse the repository at this point in the history
  • Loading branch information
theroggy committed Nov 14, 2024
1 parent 5203244 commit f76fc56
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 15 deletions.
4 changes: 2 additions & 2 deletions pyogrio/_err.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ cdef void *check_pointer(void *ptr) except NULL

cdef class ErrorHandler:
cdef object error_stack
cdef int check_int(self, int retval) except -1
cdef void *check_pointer(self, void *ptr) except NULL
cdef int check_int(self, int retval, bint squash_errors) except -1
cdef void *check_pointer(self, void *ptr, bint squash_errors) except NULL
55 changes: 53 additions & 2 deletions pyogrio/_err.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -305,14 +305,28 @@ cdef class ErrorHandler:
def __init__(self, error_stack=None):
self.error_stack = error_stack or {}

cdef int check_int(self, int err) except -1:
cdef int check_int(self, int err, bint squash_errors) except -1:
"""Check the CPLErr (int) value returned by a GDAL/OGR function.
If `err` is a nonzero value, an exception inheriting from
CPLE_BaseError is raised.
When a non-fatal GDAL/OGR error was captured in the error stack, the
exception raised will be customized appropriately. Otherwise, a
CPLError is raised.
Parameters
----------
err : int
The CPLErr returned by a GDAL/OGR function.
squash_errors : bool
True to squash all errors captured to one error with the exception type of
the last error and all error messages concatenated.
Returns
-------
int
The `err` input parameter if it is zero. Otherwise an exception is raised.
"""
if err:
stack = self.error_stack.get()
Expand All @@ -323,20 +337,46 @@ cdef class ErrorHandler:
if stack:
last = stack.pop()
if last is not None:
if squash_errors:
# Concatenate all error messages, and raise a single exception
errmsg = str(last)
inner = last.__cause__
while inner is not None:
errmsg = f"{errmsg}; {inner}"
inner = inner.__cause__

raise type(last)(-1, -1, errmsg)

raise last

else:
raise CPLError(CE_Failure, err, "Unspecified OGR / GDAL error")

return err

cdef void *check_pointer(self, void *ptr) except NULL:
cdef void *check_pointer(self, void *ptr, bint squash_errors) except NULL:
"""Check the pointer returned by a GDAL/OGR function.
If `ptr` is `NULL`, an exception inheriting from CPLE_BaseError is
raised.
When a non-fatal GDAL/OGR error was captured in the error stack, the
exception raised will be customized appropriately. Otherwise, a
NullPointerError is raised.
Parameters
----------
ptr : pointer
The pointer returned by a GDAL/OGR function.
squash_errors : bool
True to squash all errors captured to one error with the exception type of
the last error and all error messages concatenated.
Returns
-------
pointer
The `ptr` input parameter if it is not `NULL`. Otherwise an exception is
raised.
"""
if ptr == NULL:
stack = self.error_stack.get()
Expand All @@ -347,7 +387,18 @@ cdef class ErrorHandler:
if stack:
last = stack.pop()
if last is not None:
if squash_errors:
# Concatenate all error messages, and raise a single exception
errmsg = str(last)
inner = last.__cause__
while inner is not None:
errmsg = f"{errmsg}; {inner}"
inner = inner.__cause__

raise type(last)(-1, -1, errmsg)

raise last

else:
raise NullPointerError(-1, -1, "NULL pointer error")

Expand Down
15 changes: 4 additions & 11 deletions pyogrio/_io.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -210,28 +210,21 @@ cdef void* ogr_open(const char* path_c, int mode, char** options) except NULL:
# instead of raising an error
with capture_errors() as errors:
ogr_dataset = GDALOpenEx(path_c, flags, NULL, <const char *const *>options, NULL)
return errors.check_pointer(ogr_dataset)
return errors.check_pointer(ogr_dataset, True)

except NullPointerError:
raise DataSourceError(
f"Failed to open dataset (mode={mode}): {path_c.decode('utf-8')}"
) from None

except CPLE_BaseError as exc:
# If there are inner exceptions, append their errmsg to the errmsg
errmsg = str(exc)
inner = exc.__cause__
while inner is not None:
errmsg = f"{errmsg}; {inner}"
inner = inner.__cause__

if exc.errmsg.endswith("a supported file format."):
if " not recognized as a supported file format." in exc.errmsg:
raise DataSourceError(
f"{errmsg}; It might help to specify the correct driver explicitly by "
f"{exc.errmsg}; It might help to specify the correct driver explicitly by "
"prefixing the file path with '<DRIVER>:', e.g. 'CSV:path'."
) from None

raise DataSourceError(errmsg) from None
raise DataSourceError(exc.errmsg) from None


cdef ogr_close(GDALDatasetH ogr_dataset):
Expand Down

0 comments on commit f76fc56

Please sign in to comment.