From fdc6baab46a5f5f4dfdb327d37197b54ae4d84ea Mon Sep 17 00:00:00 2001 From: Sean Gillies Date: Wed, 23 Mar 2016 22:17:42 -0600 Subject: [PATCH] Switch to new try/with/except/finally structure Also restructure the errors raised from cpl_errs. Closes #600 --- rasterio/_base.pxd | 2 +- rasterio/_base.pyx | 50 ++++++---- rasterio/_err.pyx | 74 ++++++++++++--- rasterio/_io.pyx | 218 +++++++++++++++++++++++-------------------- rasterio/_warp.pyx | 149 ++++++++++++++++------------- tests/test_err.py | 28 ++++++ tests/test_tags.py | 4 +- tests/test_update.py | 2 +- 8 files changed, 327 insertions(+), 200 deletions(-) create mode 100644 tests/test_err.py diff --git a/rasterio/_base.pxd b/rasterio/_base.pxd index a193da9c..be4ad954 100644 --- a/rasterio/_base.pxd +++ b/rasterio/_base.pxd @@ -21,7 +21,7 @@ cdef class DatasetReader: cdef public object _read cdef object env - cdef void *band(self, int bidx) + cdef void *band(self, int bidx) except NULL cdef void *_osr_from_crs(object crs) diff --git a/rasterio/_base.pyx b/rasterio/_base.pyx index d41b11ec..a874e26f 100644 --- a/rasterio/_base.pyx +++ b/rasterio/_base.pyx @@ -14,9 +14,11 @@ from libc.stdlib cimport malloc, free from rasterio cimport _gdal, _ogr from rasterio._drivers import driver_count, GDALEnv -from rasterio._err import cpl_errs, GDALError +from rasterio._err import ( + cpl_errs, GDALError, CPLE_IllegalArg, CPLE_OpenFailed) from rasterio import dtypes from rasterio.coords import BoundingBox +from rasterio.errors import RasterioIOError from rasterio.transform import Affine from rasterio.enums import ColorInterp, Compression, Interleaving from rasterio.vfs import parse_path, vsi_path @@ -71,10 +73,13 @@ cdef class DatasetReader(object): name_b = path.encode('utf-8') cdef const char *fname = name_b - with cpl_errs: - self._hds = _gdal.GDALOpen(fname, 0) - if self._hds == NULL: - raise ValueError("Null dataset") + + try: + with cpl_errs: + self._hds = _gdal.GDALOpen(fname, 0) + except CPLE_OpenFailed as err: + self.env.stop() + raise RasterioIOError(str(err)) cdef void *drv cdef const char *drv_name @@ -96,14 +101,26 @@ cdef class DatasetReader(object): self._closed = False - cdef void *band(self, int bidx): - if self._hds == NULL: - raise ValueError("Null dataset") - cdef void *hband = _gdal.GDALGetRasterBand(self._hds, bidx) - if hband == NULL: - raise ValueError("Null band") + cdef void *band(self, int bidx) except NULL: + cdef void *hband = NULL + try: + with cpl_errs: + hband = _gdal.GDALGetRasterBand(self._hds, bidx) + except CPLE_IllegalArg as exc: + self.env.stop() + raise IndexError(str(exc)) return hband + def _has_band(self, bidx): + cdef void *hband = NULL + try: + with cpl_errs: + hband = _gdal.GDALGetRasterBand(self._hds, bidx) + except CPLE_IllegalArg: + self.env.stop() + return False + return True + def read_crs(self): cdef char *proj_c = NULL cdef const char * auth_key = NULL @@ -596,14 +613,9 @@ cdef class DatasetReader(object): cdef void *hobj cdef const char *domain_c cdef char **papszStrList - if self._hds == NULL: - raise ValueError("can't read closed raster file") + if bidx > 0: - if bidx not in self.indexes: - raise ValueError("Invalid band index") - hobj = _gdal.GDALGetRasterBand(self._hds, bidx) - if hobj == NULL: - raise ValueError("NULL band") + hobj = self.band(bidx) else: hobj = self._hds if ns: @@ -850,6 +862,8 @@ def tastes_like_gdal(t): cdef void *_osr_from_crs(object crs): + """Returns a reference to memory that must be deallocated + by the caller.""" cdef char *proj_c = NULL cdef void *osr = _gdal.OSRNewSpatialReference(NULL) params = [] diff --git a/rasterio/_err.pyx b/rasterio/_err.pyx index 5d02c860..07638484 100644 --- a/rasterio/_err.pyx +++ b/rasterio/_err.pyx @@ -31,6 +31,8 @@ manager raises a more useful and informative error: from enums import IntEnum +from rasterio.errors import RasterioIOError + # CPL function declarations. cdef extern from "cpl_error.h": @@ -39,17 +41,59 @@ cdef extern from "cpl_error.h": int CPLGetLastErrorType() void CPLErrorReset() + +class CPLError(Exception): + """Base CPL error class""" + pass + + +class CPLE_AppDefined(CPLError): + pass + + +class CPLE_OutOfMemory(CPLError): + pass + + +class CPLE_FileIO(CPLError): + pass + + +class CPLE_OpenFailed(CPLError): + pass + + +class CPLE_IllegalArg(CPLError): + pass + + +class CPLE_NotSupported(CPLError): + pass + + +class CPLE_AssertionFailed(CPLError): + pass + + +class CPLE_NoWriteAccess(CPLError): + pass + + +class CPLE_UserInterrupt(CPLError): + pass + + # Map GDAL error numbers to Python exceptions. exception_map = { - 1: RuntimeError, # CPLE_AppDefined - 2: MemoryError, # CPLE_OutOfMemory - 3: IOError, # CPLE_FileIO - 4: IOError, # CPLE_OpenFailed - 5: TypeError, # CPLE_IllegalArg - 6: ValueError, # CPLE_NotSupported - 7: AssertionError, # CPLE_AssertionFailed - 8: IOError, # CPLE_NoWriteAccess - 9: KeyboardInterrupt, # CPLE_UserInterrupt + 1: CPLE_AppDefined, # + 2: CPLE_OutOfMemory, # + 3: CPLE_FileIO, # + 4: CPLE_OpenFailed, # + 5: CPLE_IllegalArg, # + 6: CPLE_NotSupported, # + 7: CPLE_AssertionFailed, # + 8: CPLE_NoWriteAccess, # + 9: CPLE_UserInterrupt, # 10: ValueError # ObjectNull } @@ -62,11 +106,17 @@ cdef class GDALErrCtxManager: return self def __exit__(self, exc_type=None, exc_val=None, exc_tb=None): - cdef int err_type = CPLGetLastErrorType() - cdef int err_no = CPLGetLastErrorNo() - cdef const char *msg = CPLGetLastErrorMsg() + cdef const char *msg_c = NULL + + err_type = CPLGetLastErrorType() # TODO: warn for err_type 2? if err_type >= 3: + err_no = CPLGetLastErrorNo() + msg_c = CPLGetLastErrorMsg() + msg_b = msg_c + msg = msg_b.decode('utf-8') + msg = msg.replace("`", "'") + msg = msg.replace("\n", " ") raise exception_map[err_no](msg) diff --git a/rasterio/_io.pyx b/rasterio/_io.pyx index 1b5b2e7d..98fbb9a5 100644 --- a/rasterio/_io.pyx +++ b/rasterio/_io.pyx @@ -17,10 +17,10 @@ from rasterio cimport _base, _gdal, _ogr, _io from rasterio._base import ( crop_window, eval_window, window_shape, window_index, tastes_like_gdal) from rasterio._drivers import driver_count, GDALEnv -from rasterio._err import cpl_errs, GDALError +from rasterio._err import cpl_errs, GDALError, CPLE_OpenFailed from rasterio import dtypes from rasterio.coords import BoundingBox -from rasterio.errors import DriverRegistrationError +from rasterio.errors import DriverRegistrationError, RasterioIOError from rasterio.five import text_type, string_types from rasterio.transform import Affine from rasterio.enums import ColorInterp, MaskFlags, Resampling @@ -1288,9 +1288,13 @@ cdef class RasterUpdater(RasterReader): driver_b = self.driver.encode('utf-8') drv_name = driver_b - drv = _gdal.GDALGetDriverByName(drv_name) - if drv == NULL: - raise ValueError("NULL driver for %s", self.driver) + + try: + with cpl_errs: + drv = _gdal.GDALGetDriverByName(drv_name) + except Exception as err: + self.env.stop() + raise DriverRegistrationError(str(err)) # Find the equivalent GDAL data type or raise an exception # We've mapped numpy scalar types to GDAL types so see @@ -1328,12 +1332,16 @@ cdef class RasterUpdater(RasterReader): "Option: %r\n", (k, _gdal.CSLFetchNameValue(options, key_c))) - self._hds = _gdal.GDALCreate( - drv, fname, self.width, self.height, self._count, - gdal_dtype, options) - - if self._hds == NULL: - raise ValueError("NULL dataset") + try: + with cpl_errs: + self._hds = _gdal.GDALCreate( + drv, fname, self.width, self.height, self._count, + gdal_dtype, options) + except Exception as err: + self.env.stop() + if options != NULL: + _gdal.CSLDestroy(options) + raise if self._init_nodata is not None: @@ -1354,10 +1362,12 @@ cdef class RasterUpdater(RasterReader): self.set_crs(self._crs) elif self.mode == 'r+': - with cpl_errs: - self._hds = _gdal.GDALOpen(fname, 1) - if self._hds == NULL: - raise ValueError("NULL dataset") + try: + with cpl_errs: + self._hds = _gdal.GDALOpen(fname, 1) + except CPLE_OpenFailed as err: + self.env.stop() + raise RasterioIOError(str(err)) drv = _gdal.GDALGetDatasetDriver(self._hds) drv_name = _gdal.GDALGetDriverShortName(drv) @@ -1659,14 +1669,8 @@ cdef class RasterUpdater(RasterReader): cdef void *hobj = NULL cdef const char *domain_c = NULL cdef char **papszStrList = NULL - if self._hds == NULL: - raise ValueError("can't read closed raster file") if bidx > 0: - if bidx not in self.indexes: - raise ValueError("Invalid band index") - hobj = _gdal.GDALGetRasterBand(self._hds, bidx) - if hobj == NULL: - raise ValueError("NULL band") + hobj = self.band(bidx) else: hobj = self._hds if ns: @@ -1700,21 +1704,15 @@ cdef class RasterUpdater(RasterReader): cdef void *hBand = NULL cdef void *hTable cdef _gdal.GDALColorEntry color - if self._hds == NULL: - raise ValueError("can't read closed raster file") if bidx > 0: - if bidx not in self.indexes: - raise ValueError("Invalid band index") - hBand = _gdal.GDALGetRasterBand(self._hds, bidx) - if hBand == NULL: - raise ValueError("NULL band") + hBand = self.band(bidx) + # RGB only for now. TODO: the other types. # GPI_Gray=0, GPI_RGB=1, GPI_CMYK=2, GPI_HLS=3 hTable = _gdal.GDALCreateColorTable(1) vals = range(256) for i, rgba in colormap.items(): - if len(rgba) == 4 and self.driver in ('GTiff'): warnings.warn( "This format doesn't support alpha in colormap entries. " @@ -1745,18 +1743,18 @@ cdef class RasterUpdater(RasterReader): specifying a raster subset to write into. """ - cdef void *hband - cdef void *hmask - if self._hds == NULL: - raise ValueError("can't write closed raster file") - hband = _gdal.GDALGetRasterBand(self._hds, 1) - if hband == NULL: - raise ValueError("NULL band mask") - if _gdal.GDALCreateMaskBand(hband, 0x02) != 0: - raise RuntimeError("Failed to create mask") - hmask = _gdal.GDALGetMaskBand(hband) - if hmask == NULL: - raise ValueError("NULL band mask") + cdef void *hband = NULL + cdef void *hmask = NULL + + hband = self.band(1) + if GDALError.success != _gdal.GDALCreateMaskBand(hband, 0x02): + raise RasterioIOError("Failed to create mask.") + + try: + with cpl_errs: + hmask = _gdal.GDALGetMaskBand(hband) + except: + raise log.debug("Created mask band") if window: @@ -1788,23 +1786,20 @@ cdef class RasterUpdater(RasterReader): cdef int *factors_c = NULL cdef const char *resampling_c = NULL - if self._hds == NULL: - raise ValueError("can't write closed raster file") - # Allocate arrays. if factors: factors_c = _gdal.CPLMalloc(len(factors)*sizeof(int)) for i, factor in enumerate(factors): factors_c[i] = factor - - with cpl_errs: - resampling_b = resampling.value.encode('utf-8') - resampling_c = resampling_b - err = _gdal.GDALBuildOverviews(self._hds, resampling_c, - len(factors), factors_c, 0, NULL, NULL, NULL) - - if factors_c != NULL: - _gdal.CPLFree(factors_c) + try: + with cpl_errs: + resampling_b = resampling.value.encode('utf-8') + resampling_c = resampling_b + err = _gdal.GDALBuildOverviews(self._hds, resampling_c, + len(factors), factors_c, 0, NULL, NULL, NULL) + finally: + if factors_c != NULL: + _gdal.CPLFree(factors_c) cdef class InMemoryRaster: @@ -1845,27 +1840,25 @@ cdef class InMemoryRaster: cdef int i = 0 # avoids Cython warning in for loop below cdef const char *srcwkt = NULL cdef void *osr = NULL + cdef void *memdriver = NULL # Several GDAL operations require the array of band IDs as input self.band_ids[0] = 1 - cdef void *memdriver = _gdal.GDALGetDriverByName("MEM") - if memdriver == NULL: - raise DriverRegistrationError( - "MEM driver is not registered.") + try: + with cpl_errs: + memdriver = _gdal.GDALGetDriverByName("MEM") + except: + raise DriverRegistrationError("MEM driver is not registered.") - self.dataset = _gdal.GDALCreate( - memdriver, - "output", - image.shape[1], - image.shape[0], - 1, - <_gdal.GDALDataType>dtypes.dtype_rev[image.dtype.name], - NULL - ) - - if self.dataset == NULL: - raise ValueError("NULL output datasource") + try: + with cpl_errs: + self.dataset = _gdal.GDALCreate( + memdriver, "output", image.shape[1], image.shape[0], + 1, <_gdal.GDALDataType>dtypes.dtype_rev[image.dtype.name], + NULL) + except: + raise if transform is not None: for i in range(6): @@ -1951,14 +1944,19 @@ cdef class IndirectRasterUpdater(RasterUpdater): gdal_dtype = dtypes.dtype_rev.get(tp) else: gdal_dtype = dtypes.dtype_rev.get(self._init_dtype) - self._hds = _gdal.GDALCreate( - memdrv, "temp", self.width, self.height, self._count, - gdal_dtype, NULL) - if self._hds == NULL: - raise ValueError("NULL dataset") + + try: + with cpl_errs: + self._hds = _gdal.GDALCreate( + memdrv, "temp", self.width, self.height, self._count, + gdal_dtype, NULL) + except: + self.env.close() + raise + if self._init_nodata is not None: for i in range(self._count): - hband = _gdal.GDALGetRasterBand(self._hds, i+1) + hband = self.band(i+1) success = _gdal.GDALSetRasterNoDataValue( hband, self._init_nodata) if self._transform: @@ -1967,14 +1965,19 @@ cdef class IndirectRasterUpdater(RasterUpdater): self.set_crs(self._crs) elif self.mode == 'r+': - with cpl_errs: - temp = _gdal.GDALOpen(fname, 0) - if temp == NULL: - raise ValueError("Null dataset") - self._hds = _gdal.GDALCreateCopy( - memdrv, "temp", temp, 1, NULL, NULL, NULL) - if self._hds == NULL: - raise ValueError("NULL dataset") + try: + with cpl_errs: + temp = _gdal.GDALOpen(fname, 0) + except Exception as exc: + raise RasterioIOError(str(exc)) + + try: + with cpl_errs: + self._hds = _gdal.GDALCreateCopy( + memdrv, "temp", temp, 1, NULL, NULL, NULL) + except: + raise + drv = _gdal.GDALGetDatasetDriver(temp) drv_name = _gdal.GDALGetDriverShortName(drv) self.driver = drv_name.decode('utf-8') @@ -2031,16 +2034,19 @@ cdef class IndirectRasterUpdater(RasterUpdater): log.debug( "Option: %r\n", (k, _gdal.CSLFetchNameValue(options, key_c))) - + #self.update_tags(ns='rio_creation_kwds', **kwds) - temp = _gdal.GDALCreateCopy( + try: + with cpl_errs: + temp = _gdal.GDALCreateCopy( drv, fname, self._hds, 1, options, NULL, NULL) - - if options != NULL: - _gdal.CSLDestroy(options) - - if temp != NULL: - _gdal.GDALClose(temp) + except: + raise + finally: + if options != NULL: + _gdal.CSLDestroy(options) + if temp != NULL: + _gdal.GDALClose(temp) def writer(path, mode, **kwargs): @@ -2067,15 +2073,17 @@ def writer(path, mode, **kwargs): # driver. name_b = path.encode('utf-8') fname = name_b - with cpl_errs: - hds = _gdal.GDALOpen(fname, 0) - if hds == NULL: - raise ValueError("NULL dataset") - drv = _gdal.GDALGetDatasetDriver(hds) - drv_name = _gdal.GDALGetDriverShortName(drv) - drv_name_b = drv_name - driver = drv_name_b.decode('utf-8') - _gdal.GDALClose(hds) + try: + with cpl_errs: + hds = _gdal.GDALOpen(fname, 0) + except CPLE_OpenFailed as exc: + raise RasterioIOError(str(exc)) + + drv = _gdal.GDALGetDatasetDriver(hds) + drv_name = _gdal.GDALGetDriverShortName(drv) + drv_name_b = drv_name + driver = drv_name_b.decode('utf-8') + _gdal.GDALClose(hds) if driver == 'GTiff': return RasterUpdater(path, mode) @@ -2091,7 +2099,13 @@ def virtual_file_to_buffer(filename): filename_b = filename if not isinstance(filename, string_types) else filename.encode('utf-8') cfilename = filename_b - buff = _gdal.VSIGetMemFileBuffer(cfilename, &buff_len, 0) + + try: + with cpl_errs: + buff = _gdal.VSIGetMemFileBuffer(cfilename, &buff_len, 0) + except: + raise + n = buff_len log.debug("Buffer length: %d bytes", n) cdef np.uint8_t[:] buff_view = buff diff --git a/rasterio/_warp.pyx b/rasterio/_warp.pyx index e2ad9402..53d88a69 100644 --- a/rasterio/_warp.pyx +++ b/rasterio/_warp.pyx @@ -100,8 +100,15 @@ def _transform_geom( src = _base._osr_from_crs(src_crs) dst = _base._osr_from_crs(dst_crs) - transform = _gdal.OCTNewCoordinateTransformation(src, dst) + try: + with cpl_errs: + transform = _gdal.OCTNewCoordinateTransformation(src, dst) + except: + _gdal.OSRDestroySpatialReference(src) + _gdal.OSRDestroySpatialReference(dst) + raise + # Transform options. val_b = str(antimeridian_offset).encode('utf-8') val_c = val_b @@ -110,22 +117,24 @@ def _transform_geom( if antimeridian_cutting: options = _gdal.CSLSetNameValue(options, "WRAPDATELINE", "YES") - factory = new OGRGeometryFactory() - src_ogr_geom = _features.OGRGeomBuilder().build(geom) - dst_ogr_geom = factory.transformWithOptions( + try: + factory = new OGRGeometryFactory() + src_ogr_geom = _features.OGRGeomBuilder().build(geom) + with cpl_errs: + dst_ogr_geom = factory.transformWithOptions( src_ogr_geom, transform, options) - del factory - g = _features.GeomBuilder().build(dst_ogr_geom) - - _ogr.OGR_G_DestroyGeometry(dst_ogr_geom) - _ogr.OGR_G_DestroyGeometry(src_ogr_geom) - _gdal.OCTDestroyCoordinateTransformation(transform) - if options != NULL: - _gdal.CSLDestroy(options) - _gdal.OSRDestroySpatialReference(src) - _gdal.OSRDestroySpatialReference(dst) + g = _features.GeomBuilder().build(dst_ogr_geom) + finally: + del factory + _ogr.OGR_G_DestroyGeometry(dst_ogr_geom) + _ogr.OGR_G_DestroyGeometry(src_ogr_geom) + _gdal.OCTDestroyCoordinateTransformation(transform) + if options != NULL: + _gdal.CSLDestroy(options) + _gdal.OSRDestroySpatialReference(src) + _gdal.OSRDestroySpatialReference(dst) if precision >= 0: if g['type'] == 'Point': @@ -267,31 +276,39 @@ def _reproject( # source is a masked array src_nodata = source.fill_value - hrdriver = _gdal.GDALGetDriverByName("MEM") - if hrdriver == NULL: + try: + with cpl_errs: + hrdriver = _gdal.GDALGetDriverByName("MEM") + except: raise DriverRegistrationError( "'MEM' driver not found. Check that this call is contained " "in a `with rasterio.drivers()` or `with rasterio.open()` " "block.") - hdsin = _gdal.GDALCreate( + try: + with cpl_errs: + hdsin = _gdal.GDALCreate( hrdriver, "input", cols, rows, src_count, dtypes.dtype_rev[dtype], NULL) - if hdsin == NULL: - raise ValueError("NULL input datasource") + except: + raise _gdal.GDALSetDescription( hdsin, "Temporary source dataset for _reproject()") log.debug("Created temp source dataset") + for i in range(6): gt[i] = src_transform[i] retval = _gdal.GDALSetGeoTransform(hdsin, gt) log.debug("Set transform on temp source dataset: %d", retval) - osr = _base._osr_from_crs(src_crs) - _gdal.OSRExportToWkt(osr, &srcwkt) - _gdal.GDALSetProjection(hdsin, srcwkt) - log.debug("Set CRS on temp source dataset: %s", srcwkt) - _gdal.CPLFree(srcwkt) - _gdal.OSRDestroySpatialReference(osr) + + try: + osr = _base._osr_from_crs(src_crs) + _gdal.OSRExportToWkt(osr, &srcwkt) + _gdal.GDALSetProjection(hdsin, srcwkt) + log.debug("Set CRS on temp source dataset: %s", srcwkt) + finally: + _gdal.CPLFree(srcwkt) + _gdal.OSRDestroySpatialReference(osr) # Copy arrays to the dataset. retval = _io.io_auto(source, hdsin, 1) @@ -315,19 +332,23 @@ def _reproject( if destination.shape[0] != src_count: raise ValueError("Destination's shape is invalid") - hrdriver = _gdal.GDALGetDriverByName("MEM") - if hrdriver == NULL: + try: + with cpl_errs: + hrdriver = _gdal.GDALGetDriverByName("MEM") + except: raise DriverRegistrationError( "'MEM' driver not found. Check that this call is contained " "in a `with rasterio.drivers()` or `with rasterio.open()` " "block.") _, rows, cols = destination.shape - hdsout = _gdal.GDALCreate( - hrdriver, "output", cols, rows, src_count, - dtypes.dtype_rev[np.dtype(destination.dtype).name], NULL) - if hdsout == NULL: - raise ValueError("Failed to create temp destination dataset.") + try: + with cpl_errs: + hdsout = _gdal.GDALCreate( + hrdriver, "output", cols, rows, src_count, + dtypes.dtype_rev[np.dtype(destination.dtype).name], NULL) + except: + raise _gdal.GDALSetDescription( hdsout, "Temporary destination dataset for _reproject()") log.debug("Created temp destination dataset.") @@ -339,16 +360,16 @@ def _reproject( raise ValueError( "Failed to set transform on temp destination dataset.") - osr = _base._osr_from_crs(dst_crs) - _gdal.OSRExportToWkt(osr, &dstwkt) - _gdal.OSRDestroySpatialReference(osr) - log.debug("CRS for temp destination dataset: %s.", dstwkt) - - if not GDALError.success == _gdal.GDALSetProjection(hdsout, dstwkt): - raise ValueError( - "Failed to set projection on temp destination dataset.") - - _gdal.CPLFree(dstwkt) + try: + osr = _base._osr_from_crs(dst_crs) + _gdal.OSRExportToWkt(osr, &dstwkt) + log.debug("CRS for temp destination dataset: %s.", dstwkt) + if not GDALError.success == _gdal.GDALSetProjection( + hdsout, dstwkt): + raise ("Failed to set projection on temp destination dataset.") + finally: + _gdal.OSRDestroySpatialReference(osr) + _gdal.CPLFree(dstwkt) if dst_nodata is None and hasattr(destination, "fill_value"): # destination is a masked array @@ -365,14 +386,18 @@ def _reproject( cdef void *hTransformArg = NULL cdef _gdal.GDALWarpOptions *psWOptions = NULL - hTransformArg = _gdal.GDALCreateGenImgProjTransformer( - hdsin, NULL, hdsout, NULL, - 1, 1000.0, 0) - if hTransformArg == NULL: - raise ValueError("NULL transformer") - log.debug("Created transformer") - - psWOptions = _gdal.GDALCreateWarpOptions() + try: + with cpl_errs: + hTransformArg = _gdal.GDALCreateGenImgProjTransformer( + hdsin, NULL, hdsout, NULL, + 1, 1000.0, 0) + with cpl_errs: + psWOptions = _gdal.GDALCreateWarpOptions() + log.debug("Created transformer and options.") + except: + _gdal.GDALDestroyGenImgProjTransformer(hTransformArg) + _gdal.GDALDestroyWarpOptions(psWOptions) + raise # Note: warp_extras is pointed to different memory locations on every # call to CSLSetNameValue call below, but needs to be set here to @@ -534,22 +559,18 @@ def _calculate_default_transform( with InMemoryRaster( img, transform=transform.to_gdal(), crs=src_crs) as temp: - hTransformArg = _gdal.GDALCreateGenImgProjTransformer( - temp.dataset, NULL, NULL, wkt, 1, 1000.0, 0) - if hTransformArg == NULL: - if wkt != NULL: - _gdal.CPLFree(wkt) - raise ValueError("NULL transformer") - log.debug("Created transformer") - - # geotransform, npixels, and nlines are modified by the - # function called below. try: - if not GDALError.success == _gdal.GDALSuggestedWarpOutput2( + with cpl_errs: + hTransformArg = _gdal.GDALCreateGenImgProjTransformer( + temp.dataset, NULL, NULL, wkt, + 1, 1000.0,0) + with cpl_errs: + result = _gdal.GDALSuggestedWarpOutput2( temp.dataset, _gdal.GDALGenImgProjTransform, hTransformArg, - geotransform, &npixels, &nlines, extent, 0): - raise RuntimeError( - "Failed to compute a suggested warp output.") + geotransform, &npixels, &nlines, extent, 0) + log.debug("Created transformer and warp output.") + except: + raise finally: if wkt != NULL: _gdal.CPLFree(wkt) diff --git a/tests/test_err.py b/tests/test_err.py new file mode 100644 index 00000000..e5ec6f6c --- /dev/null +++ b/tests/test_err.py @@ -0,0 +1,28 @@ +# Testing use of cpl_errs + +import pytest + +import rasterio +from rasterio.errors import RasterioIOError + + +def test_io_error(tmpdir): + with pytest.raises(RasterioIOError) as exc_info: + rasterio.open(str(tmpdir.join('foo.tif'))) + msg = exc_info.value.message + assert msg.startswith("'{0}'".format(tmpdir.join('foo.tif'))) + assert ("does not exist in the file system, and is not recognised as a " + "supported dataset name.") in msg + + +def test_io_error_env(tmpdir): + with rasterio.drivers() as env: + drivers_start = env.drivers() + with pytest.raises(RasterioIOError): + rasterio.open(str(tmpdir.join('foo.tif'))) + assert env.drivers() == drivers_start + + +def test_bogus_band_error(): + with rasterio.open('tests/data/RGB.byte.tif') as src: + assert src._has_band(4) is False diff --git a/tests/test_tags.py b/tests/test_tags.py index 514d068c..e3e80ae1 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -13,7 +13,7 @@ def test_tags_read(): assert src.tags(ns='IMAGE_STRUCTURE') == {'INTERLEAVE': 'PIXEL'} assert src.tags(ns='bogus') == {} assert 'STATISTICS_MAXIMUM' in src.tags(1) - with pytest.raises(ValueError): + with pytest.raises(IndexError): tags = src.tags(4) def test_tags_update(tmpdir): @@ -29,7 +29,7 @@ def test_tags_update(tmpdir): dst.update_tags(a='1', b='2') dst.update_tags(1, c=3) - with pytest.raises(ValueError): + with pytest.raises(IndexError): dst.update_tags(4, d=4) assert dst.tags() == {'a': '1', 'b': '2'} diff --git a/tests/test_update.py b/tests/test_update.py index a097a543..cc15c215 100644 --- a/tests/test_update.py +++ b/tests/test_update.py @@ -15,7 +15,7 @@ def test_update_tags(data): with rasterio.open(tiffname, 'r+') as f: f.update_tags(a='1', b='2') f.update_tags(1, c=3) - with pytest.raises(ValueError): + with pytest.raises(IndexError): f.update_tags(4, d=4) assert f.tags() == {'AREA_OR_POINT': 'Area', 'a': '1', 'b': '2'} assert ('c', '3') in f.tags(1).items()