1

I need to catch when one of my dependencies throws a specific ValueError and deal with that a certain way, and otherwise re-raise the error. I don't find any recent questions that deal with this in a way that's Python 3 compliant, and that deals with cases where the only thing distinguishing errors returned is the string message.

This post is probably the closest: Python: Catching specific exception

Something like this-- catch specific HTTP error in python --won't work because I'm not using a dependency that also supplies specific codes like an HTTP error would have.

Here's my attempt:

try:
    spect, freq_bins, time_bins = spect_maker.make(syl_audio,
                                                   self.sampFreq)
except ValueError as err:
    if str(err) == 'window is longer than input signal':
        warnings.warn('Segment {0} in {1} with label {2} '
                      'not long enough for window function'
                      ' set with current spect_params.\n'
                      'spect will be set to nan.')
        spect, freq_bins, time_bins = (np.nan,
                                       np.nan,
                                       np.nan)
    else:
        raise

If it matters, the dependency is scipy and I need to catch when the spectrogram fails for a specific reason (the segment I'm taking a spectrogram of is shorter than the window function).

I realize my approach is fragile because it depends on the error string not changing, but the error string is the only thing that distinguishes it from other ValueErrors returned by the same function. So I plan to have a unit test to defend myself against that.

NickleDave
  • 372
  • 2
  • 18
  • what's wrong with this approach? perhaps I missed it but I'm not sure what you're asking. – Stael Jul 04 '17 at 16:07
  • A better approach would be to catch the ValueError at a lower level, to raise a custom exception. Is `spect_maker.make` a method of your code ? – Adrien Matissart Jul 04 '17 at 16:10
  • @Stael I'm asking "what's wrong this approach" because I can't find any specific SO questions, random blog posts, sections in the Python docs, etc. that deal with it even though I feel like it would be a common issue. Maybe I'm not using the right search terms – NickleDave Jul 04 '17 at 16:26
  • @AdrienMatissart yes spect_maker.make is a method of my code. I thought about raising a custom exception. In that case I would just catch that custom exception here, right? Not sure what the other benefits are. The custom exception itself would still be fragile but I guess there would be separation of concerns? I.e. I wouldn't have a long hard-to-read list of try-catch + if statements in this current method – NickleDave Jul 04 '17 at 16:30
  • Ideally if you can restrict the block that may raise this specific error, you would not need to check the error string. But we would need more details about what this method does. – Adrien Matissart Jul 04 '17 at 16:35
  • Not sure if you're asking, but: the top-level method loops through a list of segments from an audio file and makes spectrograms from them. The spect_maker.make does the actual spectrogram making using parameters that were set when initializing the spect_maker object. So I guess I should write the custom exceptions inside the spect_maker class. – NickleDave Jul 04 '17 at 16:44
  • It's standard practice to define custom Exception classes in modules to riase module specific errors. The custom class doesn't have to do anything special or different. It's much easier to test for class than to parse a message (and less fragile). Also check the practice in unittest files. – hpaulj Jul 04 '17 at 16:52

1 Answers1

0

Ok, so based on other people's comments, I'm guessing it should be something like this:

# lower-level module
class CustomError(Exception):
    pass

# in method
Class Thing:
    def __init__(prop1):
        self.prop1 = prop1

    def method(self,element):
        try:
            dependency.function(element,self.prop1)
        except ValueError as err:
            if str(err) == 'specific ValueError':
                raise CustomError
            else:
                raise # re-raise ValueError because string not recognized

# back in higher-level module
thing = lowerlevelmodule.Thing(prop1)
for element in list_of_stuff:
    try:
        output = thing.method(element)
    except CustomError:
        output = None
        warnings.warn('set output to None for {} because CustomError'.
                       format(element))
NickleDave
  • 372
  • 2
  • 18