Raising a "null" exception in Python - python

I am trying to write a function that does error checking in the following way.
def check_var_type(var, var_name, type):
t = type(var)
if t is type or t is list:
return None
else:
return TypeError(
f"Invalid {var_name} type '{t.__module__}.{t.__name__}', "
f"'{float.__module__}.{float.__name__}' or "
f"'{list.__module__}.{list.__name__}' expected.")
my_var = 1
raise check(my_var, 'my_var', float)
My expectation of Python's raise command was that if I pass None to it, it would simply ignore it and raise no exception. The response, however, was:
TypeError: exceptions must derive from BaseException
I then had a look at Python's built-in exception types to see if something like Nonthing, NoError, etc. exists. No luck though.
I can, of course raise the exception in the check_var_type function, but I don't want to, since this would add an extra line to the stack trace.
So, my (perhaps silly) question is: How can I use raise to not raise an exception? :-)
Thanks

If you don't want to raise an exception, don't call raise (whose sole job IS to raise an exception).
Perhaps what you really want is to call raise from inside check_var_type when you actually do want to raise an exception, and just use return when you don't.
An alternative might be to leave check_var_type as is, but wrap the call to it in an if that one raises the exception returned when an exception is returned.

Related

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.

raise Exception or raise Exception()

I need to raise an exception. In my specific case NotImplementedError.
What is the difference between
Raise NotImplementedError and raise NotImplementedError().
Which is considered a better practice? Why?
There is no difference between raise X and raise X(). It's better to use the second form and pass a message like raise RuntimeError('bad argument'). If there is no useful message like your case I go with first syntax. It's a matter of taste.
The first form is a remaining of old style raising (not valid in Python3):
raise X, 'a'
is the same as
raise X('a')

Which Standard Exception Class to Use For Protocol Violations?

According to a given protocol (which I cannot change, only implement), some function initialize_foo() is supposed to be called only once:
def initialize_foo():
"""
...
Note:
You must call this function exactly once.
"""
I would like to recognize a protocol abuse where it is called twice, and raise an exception:
_foo_initialized = False
def initialize_foo():
"""
...
Note:
You must call this function exactly once.
"""
if _foo_initialized:
raise <what>?
...
_foo_initialized = True
The problem is what class's object to raise. Looking at the standard exceptions, I can't find anything to subclass except Exception, which seems too general.
What is the general practice in this case?
I'd use RuntimeError.
It is often used for that sort of stuff, even in the standard library. You can find an example very similar to your use case in the warnings module:
if self._entered:
raise RuntimeError("Cannot enter %r twice" % self)
Another example is in threading:
if self._started.is_set():
raise RuntimeError("threads can only be started once")
You can also consider raising an ad-hoc exception (possibly a subclass of RuntimeError) if that error is supposed to be caught and if you feel that RuntimeError may be ambiguous.
I would recommend you to subclass a warning, instead of having an exception, since I have a feeling that a lot of times you'd rather continue running after this happens.

Returning error string from a function in python

I have a class function in Python that either returns a success or a failure, but in case of a failure I want it to send a specific error string back. I have 3 approaches in mind:
Pass in an variable error_msg to the function originally set to None and in case of an error, it gets set to the error string. eg:
if !(foo(self, input, error_msg)):
print "no error"
else:
print error_msg
Return a tuple containing a bool and error_msg from the function.
I raise an exception in case of an error and catch it in the calling code. But since I don't see exceptions being used often in the codebase I am working on, so was not too sure about taking this approach.
What is the Pythonic way of doing this?
Create your own exception and raise that instead:
class MyValidationError(Exception):
pass
def my_function():
if not foo():
raise MyValidationError("Error message")
return 4
You can then call your function as:
try:
result = my_function()
except MyValidationError as exception:
# handle exception here and get error message
print exception.message
This style is called EAFP ("Easier to ask for forgiveness than permission") which means that you write the code as normal, raise exceptions when something goes wrong and handle that later:
This common Python
coding style assumes the existence of valid keys or attributes and
catches exceptions if the assumption proves false. This clean and fast
style is characterized by the presence of many try and except
statements. The technique contrasts with the LBYL style common to many
other languages such as C.
Raise an error:
if foo(self, input, error_msg):
raise SomethingError("You broke it")
And handle it:
try:
something()
except SomethingError as e:
print str(e)
It's the Pythonic approach and the most readable.
Returning a tuple like (12, None) may seem like a good solution, but it's hard to keep track of what each method returns if you're not consistent. Returning two different data types is even worse, as it will probably break code that assumes a constant data type.

How to use "raise" keyword in Python [duplicate]

This question already has answers here:
Manually raising (throwing) an exception in Python
(11 answers)
Closed 4 years ago.
I have read the official definition of "raise", but I still don't quite understand what it does.
In simplest terms, what is "raise"?
Example usage would help.
It has two purposes.
jackcogdill has given the first one:
It's used for raising your own errors.
if something:
raise Exception('My error!')
The second is to reraise the current exception in an exception handler, so that it can be handled further up the call stack.
try:
generate_exception()
except SomeException as e:
if not can_handle(e):
raise
handle_exception(e)
raise without any arguments is a special use of python syntax. It means get the exception and re-raise it. If this usage it could have been called reraise.
raise
From The Python Language Reference:
If no expressions are present, raise re-raises the last exception that
was active in the current scope.
If raise is used alone without any argument is strictly used for reraise-ing. If done in the situation that is not at a reraise of another exception, the following error is shown:
RuntimeError: No active exception to reraise
It's used for raising errors.
if something:
raise Exception('My error!')
Some examples here
Besides raise Exception("message") and raise Python 3 introduced a new form, raise Exception("message") from e. It's called exception chaining, it allows you to preserve the original exception (the root cause) with its traceback.
It's very similar to inner exceptions from C#.
More info:
https://www.python.org/dev/peps/pep-3134/
You can use it to raise errors as part of error-checking:
if (a < b):
raise ValueError()
Or handle some errors, and then pass them on as part of error-handling:
try:
f = open('file.txt', 'r')
except IOError:
# do some processing here
# and then pass the error on
raise
raise causes an exception to be raised. Some other languages use the verb 'throw' instead.
It's intended to signal an error situation; it flags that the situation is exceptional to the normal flow.
Raised exceptions can be caught again by code 'upstream' (a surrounding block, or a function earlier on the stack) to handle it, using a try, except combination.

Categories

Resources