Skip to content

Commit

Permalink
relax null checking
Browse files Browse the repository at this point in the history
Fixes #19

Now a GDALError is only thrown in `checknull` if both the pointer is null and the last error type is either CE_Failure or CE_Fatal, as suggested by Even Rouault.

The error handler that we register is given the same behavior (throw on failure & fatal), instead of previously always raising an error when called.

At the same time this restructures and tests the error handling a bit better.
  • Loading branch information
visr committed Jun 24, 2018
1 parent 7994d06 commit 62cf848
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 28 deletions.
2 changes: 1 addition & 1 deletion src/GDAL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function __init__()
check_deps()

# register custom error handler
funcptr = cfunction(gdaljl_errorhandler, Ptr{Void}, (UInt32, Cint, Ptr{UInt8}))
funcptr = cfunction(gdaljl_errorhandler, Ptr{Void}, (CPLErr, Cint, Cstring))
ccall(ferrh, Ptr{Void}, (Ptr{Void},), funcptr)

# get GDAL version number
Expand Down
47 changes: 21 additions & 26 deletions src/error.jl
Original file line number Diff line number Diff line change
@@ -1,41 +1,36 @@

errortype = Dict{UInt32, AbstractString}(
0 => "None",
1 => "Debug",
2 => "Warning",
3 => "Failure",
4 => "Fatal"
)

struct GDALError <: Exception
class::AbstractString
code::Int
msg::AbstractString
class::CPLErr
code::Cint
msg::String
end

function GDALError()
class = getlasterrortype()
code = getlasterrorno()
msg = getlasterrormsg()
GDALError(class, code, msg)
end

GDALError() = GDALError("", 0, "")
GDALError(msg::AbstractString) = GDALError("", 0, msg)
const throw_class = (CE_Failure, CE_Fatal)

function Base.showerror(io::IO, err::GDALError)
if err == GDALError()
print(io, "GDALError")
elseif err.class == "" && err.code == 0
print(io, "GDALError\n\t$(err.msg)")
else
print(io, "GDALError ($(err.class), code $(err.code)):\n\t$(err.msg)")
end
err = string("GDALError (", err.class, ", code ", err.code, "):\n\t", err.msg)
println(io, err)
end

function gdaljl_errorhandler(errtype::UInt32, errno::Cint, errmsg::Ptr{UInt8})
throw(GDALError(errortype[errtype], errno, unsafe_string(errmsg)))
# it currently never returns but cfunction needs a guaranteed return type
# return NULL to come back to the default behavior
function gdaljl_errorhandler(class::CPLErr, errno::Cint, errmsg::Cstring)
# function signature needs to match the one in __init__, and the signature
# of the callback for a custom error handler in the GDAL docs
if class in throw_class
throw(GDALError(class, errno, unsafe_string(errmsg)))
end
return C_NULL
end

function checknull(ptr)
if ptr == C_NULL
throw(GDALError("GDAL returned nothing"))
if ptr == C_NULL && getlasterrortype() in throw_class
throw(GDALError())
end
ptr
end
9 changes: 9 additions & 0 deletions test/gdal_utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ ds_point = GDAL.openex("data/point.geojson", GDAL.GDAL_OF_VECTOR, C_NULL, C_NULL
# GDALInfo
# infooptionsnew checks if the options are valid
@test_throws GDAL.GDALError GDAL.infooptionsnew(["-novalidoption"], C_NULL)
# check not only that a GDALError is thrown, but also its contents
try
GDAL.infooptionsnew(["-novalidoption"], C_NULL)
catch err
@test err.class === GDAL.CE_Failure
@test err.code === Cint(6)
@test err.msg == "Unknown option name '-novalidoption'"
end

options = GDAL.infooptionsnew(["-checksum"], C_NULL)
infostr = GDAL.info(ds_small, options)
@test contains(infostr, "Checksum=50054")
Expand Down
4 changes: 4 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ info(GDAL.ogrgetdrivercount(), " OGR drivers found")
@test GDAL.getdrivercount() > 0
@test GDAL.ogrgetdrivercount() > 0

# test if the registered error handler also handles CE_Fatal
@test_throws GDAL.GDALError GDAL.emergencyerror("we've got a problem")
GDAL.errorreset()

# throw errors on non existing files
@test_throws GDAL.GDALError GDAL.open("NonExistent", GDAL.GA_ReadOnly)
# if a driver is not found, the C API returns a null
Expand Down
4 changes: 3 additions & 1 deletion test/tutorial_raster.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ gotmin, gotmax = Ref(Cint(-1)), Ref(Cint(-1))
@test gotmin[] == gotmax[] == 0

@test GDAL.getoverviewcount(band) == 0
@test_throws GDAL.GDALError GDAL.getrastercolortable(band)
# note that if there is no raster color table, no error (C_None) is set by GDAL
# so there is no GDALError thrown here
@test GDAL.getrastercolortable(band) == C_NULL


# Reading Raster Data
Expand Down

0 comments on commit 62cf848

Please sign in to comment.