perrygeo/python-rasterstats

Yield exceptions

alpha-beta-soup opened this issue · 1 comments

I have hundreds of features to loop over, for which I'm using the gen_zonal_stats function to get a generator for iteration. Rarely, one of these features will be invalid for some reason. Currently the generator throws an exception for this, specifically the fsrc = rast.read(bounds=geom_bounds) line within the feature loop. This is uncaught, which means that the iterator will also start throwing StopIteration.

What I'd prefer is to have the generator yield an exception, so I can handle it: by logging some information, and then continuing to the next feature rather than aborting the generation.

e.g. something like

zs = gen_zonal_stats(*args, **kwargs)
i = 0
while True:
    try:
        zone = next(zs)
        if isinstance(zone, Exception):
            raise zone
    except ValueError:
        LOGGER.error(f'Error getting zonal stats for zone {i}', exc_info=True)
    except StopIteration:
        break
    else:
        do_something(zone)
    finally:
        i += 1

This is only possible if exceptions are yielded from within gen_zonal_stats; currently the isinstance(zone, Exception) is irrelvant, so after the ValueError is caught, the subsequent next(zs) call will throw StopIteration, so it's not possible to loop through the complete set of input features.

This is the same issue: https://stackoverflow.com/questions/11366064/handle-an-exception-thrown-in-a-generator

The contract made by rasterstats is that you get stats for all your vector input features or, if not, you get an Exception. This proposed change to the API of the gen_zonal_stats function and would not be backwards compatible. Happy to review a PR is you could implement it without changing any existing functions. But as written above, with the generator returning either stats or an exception, this would break lots of existing code.

An alternate approach to consider that can be accomplished through the current API: iterate though the features manually, call zonal_stats on each and catching and handling any exceptions that arise in your application. Gives you more explicit control and shouldn't be too much additional overhead.