raise None

Terry Reedy tjreedy at udel.edu
Wed Dec 30 23:00:16 EST 2015


On 12/30/2015 7:09 PM, Steven D'Aprano wrote:
> I have a lot of functions that perform the same argument checking each time:
>
> def spam(a, b):
>      if condition(a) or condition(b): raise TypeError
>      if other_condition(a) or something_else(b): raise ValueError
>      if whatever(a): raise SomethingError
>      ...
>
> def eggs(a, b):
>      if condition(a) or condition(b): raise TypeError
>      if other_condition(a) or something_else(b): raise ValueError
>      if whatever(a): raise SomethingError
>      ...
>
>
> Since the code is repeated, I naturally pull it out into a function:
>
> def _validate(a, b):
>      if condition(a) or condition(b): raise TypeError
>      if other_condition(a) or something_else(b): raise ValueError
>      if whatever(a): raise SomethingError
>
> def spam(a, b):
>      _validate(a, b)
>      ...
>
> def eggs(a, b):
>      _validate(a, b)
>      ...
>
>
> But when the argument checking fails, the traceback shows the error
> occurring in _validate, not eggs or spam. (Naturally, since that is where
> the exception is raised.) That makes the traceback more confusing than it
> need be.
>
> So I can change the raise to return in the _validate function:
>
> def _validate(a, b):
>      if condition(a) or condition(b): return TypeError
>      if other_condition(a) or something_else(b): return ValueError
>      if whatever(a): return SomethingError
>
>
> and then write spam and eggs like this:
>
> def spam(a, b):
>      ex = _validate(a, b)
>      if ex is not None: raise ex
>      ...
>
>
> It's not much of a gain though. I save an irrelevant level in the traceback,
> but only at the cost of an extra line of code everywhere I call the
> argument checking function.

It is nicer than the similar standard idiom

try:
     _validate(a, b)
except Exception as e:
     raise e from None

If you could compute a reduced traceback, by copying one, then this 
might work (based on Chris' idea.

def _validate(a, b):
     ex = None
     if condition(a) or condition(b): ex = TypeError
     elif other_condition(a) or something_else(b): ex = ValueError
     elif whatever(a): ex = SomethingError
     if ex:
         try: 1/0
         except ZeroDivisionError as err: tb = err.__traceback__
         tb = <copy of tb with this frame omitted>
         raise ex.with_traceback(tb)

> But suppose we allowed "raise None" to do nothing. Then I could rename
> _validate to _if_error and write this:
>
> def spam(a, b):
>      raise _if_error(a, b)
>      ...
>
>
> and have the benefits of "Don't Repeat Yourself" without the unnecessary,
> and misleading, extra level in the traceback.
>
> Obviously this doesn't work now, since raise None is an error, but if it did
> work, what do you think?

Perhaps a bit too magical, but maybe not.

-- 
Terry Jan Reedy




More information about the Python-list mailing list