Python: Re-displaying errors together after being ignored - python

I am using a method that raises a KeyError. I would like to run that through a for loop and then display all these errors together at once when it's done with the loop. Is that possible ?
I am doing a Try except as this post indicates in order to ignore the KeyError(s).
bad_list = []
for i in range (10):
try:
bad_list.append(checkermethod(i)) #checker method raises TypeError when called
except KeyError:
continue
if bad_list:
raise KeyError('\n'.join(bad_list))
I am trying to catch these errors by appending a list, but it's always empty, so it's not really appending anything. This makes sense since I am actually ignoring the errors, but is there any idea on how to do that in another way?

I found the solution:
bad_list = []
for i in range (10):
try:
checkermethod(i) #checker method raises TypeError when called
except KeyError as e:
bad_list.append(e.args[0])
if bad_list:
raise KeyError('\n'.join(bad_list))

Related

Try-except with NameError and TypeError

Can you please help me with the following. I am trying to catch two exceptions: 1) TypeError and 2)NameError. I use the following code below that estimates the average:
def calculate_average(number_list):
try:
if type(number_list) is not list:
raise ValueError("You should pass list to this function")
except ValueError as err:
print(err)
return
try:
average = sum(number_list)/len(number_list)
except TypeError:
print('List should contain numbers')
return
except NameError:
print('List should contain numbers')
return
return average
The code works fine for:
print(calculate_average([1, 2, 3]))
print(calculate_average([1, 2, 'a']))
But when I use:
print(calculate_average([1, 2, a]))
I have the following error that was supposed to be captured by except:
NameError: name 'a' is not defined
Can you please help me with understanding the issue? (I use Spyder)
The NameError on a is raised in the calling scope, not when you attempt to use number_list. You would need to catch it there:
try:
print(calculate_average([1, 2, a]))
except NameError:
print("Variable not defined")
However, you shouldn't be catching NameErrors at all. When they arise in testing, you should figure out what undefined name you are trying to use, and make sure it is defined. Like most exceptions, this isn't intended for flow control or dealing with easily fixed problems at runtime.
Rather than littering your code with run-time type check, consider using type hints and static typecheckers like mypy to catch code that would produce a TypeError at runtime.
# list[int] might be too restrictive, but this is a simplified
# example
def calculate_average(number_list: list[int]):
average = sum(number_list)/len(number_list)
return average
They only error left here that mypy wouldn't catch is the attempt to divide by zero if you pass an empty list. That you can check for and handle. You can raise a ValueError, or just decide that the average of an empty list is 0 by definition.
def calculate_average(number_list: list[int]):
if not number_list:
# raise ValueError("Cannot average an empty list")
return 0
return sum(number_list)/len(number_list)
This is preferable to
try:
return sum(number_list)/len(number_list)
except ZeroDivisionError:
...
because it anticipates the problem before you go to the trouble of calling sum and len.
In spyder, if you look in the trail of recent tracebacks, the error is raised by site-package ...lib\site-packages\spyder_kernels\py3compat.py", line 356, in compat_exec(code, globals, locals) , as NameError: name 'a' is not defined since it searches for a variable declaration of a in the script (which is absent), before executing the function.

re-raising exception doesn't work as expected

I have this rather intricate try except block:
try:
self.sorting = sys.argv[1]
try:
test_sorting_var = int(self.sorting)
if test_sorting_var < 1:
print "Sorting column number not valid."
raise ValueError
else:
self.sorting = test_sorting_var
except ValueError:
print "There's a problem with the sorting value provided"
print "Either the column doesn't exists or the column number is invalid"
print "Please try a different sorting value or omit it."
sys.exit(1)
except:
if self.sorting not in self.output_table.column_headers:
print "Sorting column name not valid."
raise ValueError
except:
pass
Basically I'm checking:
If there's a sys.argv[1]
If so, try it as int, and see if it's less than 1
If int fails, test it as string
In both 2+3, if the tests don't succeed, I'm raising a ValueError that should be caught in the except ValueError block and it does as expected:
Sorting column number not valid.
There's a problem with the sorting value provided
Either the column doesn't exists or the column number is invalid
Please try a different sorting value or omit it.
BUT! The sys.exit(1) is not invoked and the program just continues.
How can I fix it and even make it more readable?
In the last two lines you catch any exception:
except:
pass
This includes the exception SystemExit, which is raised by sys.exit.
To fix this, only catch exceptions deriving from Exception, which
SystemExit does not:
except Exception:
pass
In general, it's (almost) never a good idea to do a bare except, always catch Exception, or if possible, something more specific.
The builtint sys.exit() raises a SystemExit-Exception. As you are catching any type of exception when you don't define the Exception to catch (except: without an Exception Type) the SystemExit gets also caught. Ultimately the function will run until the last line where you wrote pass.
Best thing to do is to always catch specific Exceptions and never ever catch all Exceptions with an except:.
Furthermore you should put the check if self.sorting is not in self.output_table.column_headers outside the try catch where you check for a valid self.sorting.
From the documentation for sys.exit:
Exit from Python. This is implemented by raising the SystemExit exception, so cleanup actions specified by finally clauses of try statements are honored, and it is possible to intercept the exit attempt at an outer level.
This means that the outer try except loop is catching the SystemExit exception and causing it to pass. You can add this exception to the outer block and call it again.
I think I would do something like this:
import sys
def MyClass(object):
def method(self, argv, ...):
# ...
if len(argv) < 2:
raise RuntimeError("Usage: {} <sorting column>".format(argv[0]))
sorting = argv[1]
try:
self.sorting = int(sorting)
except ValueError:
try:
self.sorting = self.output_table.column_headers.index(sorting)
except ValueError:
raise ValueError("Invalid sorting column '{}'.".format(sorting))
# ...
try:
# ...
obj.method(sys.argv, ...)
except Exception as e:
sys.exit(e.message)
It's okay to ask for forgiveness instead of permission when it makes things easier (for example to parse a number), but if you need to make sure if sys.argv has enough elements just check it, it will make the program flow clearer.
Avoid using sys.exit within regular code, try to use it only in the outermost levels. For the most part, it is generally better to let exceptions bubble up and catch them at top level or let them crash the program if necessary.
Do make use of exception parameters to store error information, you can decide at a later point whether to print the error, log it, show it in a popup, ...
Instead of using sys.argv directly from within a class, you can pass it as an argument to the method/constructor, it will make the code easier to test and more flexible towards the future.

Catching Numpy All-Nan Slice

I have an issue trying to catch a Python exception:
File "/usr/lib/python2.7/dist-packages/numpy/lib/nanfunctions.py",
line 427, in nanargmax
raise ValueError("All-NaN slice encountered") ValueError: All-NaN slice encountered
The error appears with this code when effectively the slice contains All-NaN. However, I want to catch that situation and handle it.
with warnings.catch_warnings():
warnings.filterwarnings('error')
try:
action = np.nanargmax(self.Q[state])
except Warning as e:
print "error"
sys.exit(0)
I expect to print the word error, however, the try-except statement is ignored. Any help, please?
You should change except Warning as e to except ValueError as e.
This is because the ValueError class is not a subclass of the Warning class. Alternatively, you could catch any Exception with except Exception as e since all exceptions are a subclass of the Exception class, but best practice is to be as precise as possible with the exceptions that you catch.

Else not raising errors in try-except-else

I'm working with a special try-except-else block in python where I try to fetch an value and if I fail I try to create it to use later.
However, the creation process is not perfect (it actually fetches from the internet and might fail for several reasons) and I noticed that when this fails it is not raised. The only information I get is that the value is not defined
NameError: name 'value' is not defined
Since the error can be many, I would very much appreciate to see the full trackback for the error in the else clause
Please note: all tries can raise KeyError and the instantiation process cannot raise this error, it's always something else. Here is a sample code:
try:
value = _list[key]
except KeyError:
try:
number = kwargs['number'] # A number might be parsed via kwargs
except KeyError:
clean_everything()
value = None
raise KeyError('Value not found! ' +
'Need a number to create new instance!')
else:
value = Value(number=number) # This instantiation can raise other errors!
_list[homeTeam] = _listnumber[number] = value # Update lists for future reference.
finally:
print value
Anyone got any ideas on why the else clause is not raising? Is there a better way to write this?
Thanks,
EDIT: Added treatment for value inside the nested try.
This is leading me to believe that nested tries doesn't work as the nested errors will only be raised after the outer-most finally.
i.e. inner errors are raised only after the outer try is completed.
This might lead to a new question: how can I properly raise an error inside an except clause?
Trying to access value without first defining it indeed raises NameError. If KeyError is raised because key is not in _list, then value will never get defined in the try block. Because you redefine value in both of the except blocks, this should be OK there. However, your finally block does not redefine value before accessing it, so you would get a NameError there in the following cases:
value = _list[key] raises an error other than KeyError (e.g. NameError when _list is not defined)
clean_everything() raises any error
value = Value(number=number) raises any error
You can define value before the try block so that it is always defined no matter what happens inside the try block.

Get around raising IndexError

My code is as follows :
for p in qs:
set = None
try:
set = p.property.property_locations.all()
except IndexError:
pass
if set:
Problem is that when set is none it still throws IndexError from this part of django.db.models.query:
try:
qs = self._clone()
qs.query.set_limits(k, k + 1)
return list(qs)[0]
except self.model.DoesNotExist, e:
raise IndexError(e.args)
How to stop system from throwing this error and continuing to next element in for loop ?
In any case, there are two mistakes in your code:
set is a builtin (as you can see from SO's syntax highlighting), so by giving your variable that name you're shadowing the builtin for no purpose, which is at least bad practice, and likely to cause issues later in the code.
The canonical way to check if set is not None is by writing: if set is not None
Better yet, the canonical way to write that code snippet is:
try:
[code that might raise an exception]
except Error:
[code that handles the exception]
else:
[code that executes when no exception was raised]
(substitute Error for the actual exception, of course)
That way you don't even have to check 'set' at that point.

Categories

Resources