Pass a dictionary in try/except clause - python

I have a use case that requires passing a dictionary in a try/exception clause in Python 3.x
The error message can be accessed as a string using str() function, but I can't figure out who to get it as a dictionary.
try:
raise RuntimeError({'a':2})
except Exception as e:
error = e
print(error['a'])
e is a RuntimeError object and I can't find any method that returns the message in its original format.

Exceptions store their init args in an "args" attribute:
try:
raise RuntimeError({'a':2})
except Exception as e:
(the_dict,) = e.args
print(the_dict["a"])
That being said, if you want an exception type which has a structured key/value context associated, it would be best to define your own custom exception subclass for this purpose rather than re-use the standard library's RuntimeError directly. That's because if you catch such an exception and attempt to unpack the dictionary context, you would need to detect and handle your RuntimeError instances differently from RuntimeError instances that the standard library may have raised. Using a different type entirely will make it much cleaner and easier to distinguish these two cases in your code.

Related

How to raise exception within a try/except block properly

right now I have a problem where I want to raise a specific TypeError if there is one. However, what ends up happening is the interpreter sees the first error, and then in the middle of handling it it raises the other one as well saying "During handling of the above exception, another exception occurred:"
this is what I have
def function(dictionary)
try:
value = max(dictionary.values())
except TypeError:
raise TypeError("some error")
I plug in the following into the shell:
function({1:'a', 2:3})
How can I approach this?
If you want to discard the exception context, you can explicitly discard it using from None, e.g.:
try:
value = max(dictionary.values())
except TypeError:
raise TypeError("some error") from None
That said, it's usually best to leave the context in place; the only time you'll see it is if the exception is uncaught and the default logging occurs, or you try to log the exception (e.g. with logger.exception). That additional information is often useful, especially for extremely broad exception types like TypeError and ValueError (where you intend to catch specific known subtypes, and unexpectedly catch one caused in a completely different way).
To be clear, this only works on Python 3, but then, exception context chaining only exists on Python 3; on Python 2, the context is lost automatically.
Since you are raising the exception while handling it, the exception is sent back to the caller function.
If you just want to handle it and print the error and move on with rest of the execution, you can do sth like this
except TypeError as t:
print ("Error", t)

Using try exception/catch in the method definition or in the calling method?

Which of the following snippet codes is common?
#1:
def foo():
try:
pass # Some process
except Exception as e:
print(e)
foo()
#2:
def foo():
pass # Some process
try:
foo()
except Exception as e:
print(e)
It depends on what foo does, and the type of Exception, i'd say.
Should the caller handle it or should the method?
For instance, consider the following example:
def try_get_value(registry, key):
try:
return registry[key]
except KeyError:
return None
This function will attempt to fetch a value from a dictionary using its key. If the value is not there, it should return None.
The method should handle KeyError, because it needs to return None when this happens, so as to comply with its expected behavior. (It's the method's responsability to catch this error)
But think of other exception types, such as TypeError (e.g., if the registry is not a dict).
Why should our method handle that? That's the caller mess-up. He should handle that, and he should worry about that.
Besides, what can our method do if we get such Exception? There's no way we can handle that from this scope.
try_get_value has one simple task: to get a value from the registry (a default one if there is none). It's not responsible for the caller breaking the rules.
So we don't catch TypeError because it's not our responsability.
Hence, the caller's code may look like something like this:
try:
value = try_get_value(reg, 'some_key')
# Handle value
except TypeError:
# reg is not a dict, do something about it...
P.S.: There may be times when our foo method needs to do some cleanup if there is an unexpected exit (e.g. it has allocated some resources which would leak if not closed).
In this case, foo should catch the exceptions, just so it can fix its state appropriately, but should then raise them back again to the caller.
I think the first part is cleaner and more elegant. Also more logical because as an implementer of the function, you want to handle all exceptions that it might throw rather than leave it to the client or caller. Even if you'll be the only one using the method, you still want to handle exceptions inside the function as in the future you may not remember what exception it is throwing.

Where can I find which exceptions are raised by Python built-ins?

I'm writing a decorator to validate some functions. I try to use built-ins as much as possible to do the heavy lifting, but I've been getting stuck on picking which exceptions I should catch when using them.
For example:
def Validated(fun):
def ValidatedFun(*args, **kwargs):
try:
_ = dict(kwargs.get('untrusted_data', ()))
except ? as e:
raise BetterError('Additional relevant info') from e
return fun(*args, **kwargs)
return ValidatedFun
I'd like to know:
What are the most-derived exceptions that dict (and other built-ins) explicitly raise?
Where can I find documentation that lists them? (they aren't on
https://docs.python.org/)
All Python standard types follow the conventions of a few default exceptions. The behaviour is documented for the exceptions, not the types.
For dict, the exceptions are TypeError and ValueError. There are other exception that can be raised at this point, but those are not dependent on the input (MemoryError and KeyboardInterrupt).
TypeError indicates that the type of object passed in is not supported; the dict documentation documents what types are accepted (mapping or iterable objects), everything else is cause to raise the exception. The accepted types must match certain expectations; if those are not met, a ValueError is raised (correct type, but the value is wrong).
there is no list of exception that a specific python function can throw. This is due to python's duck typing. Since you could provide objects of any type as your function parameters, and since these functions could do whatever they want in their implementation, any exception could in principle be raised. Usually, the docs are clear on what exception they rise under specific conditions (e.g. IOError when a file is not found) but this is different from "a list of all exceptions that a function can throw".
I would also advise against your strategy to redirect exceptions into a "BetterError" as you plan, since this hides the original reason and location where the error first occurred. If you really want to provide better error messages, do argument validation in beginning of your function and raise ValueErrors for situations that cannot be excluded but would raise any exception down the line:
if not is_valid_data(untrusted_data) :
raise ValueError("invalid input")
unused_dict = dict(untrusted_data)

The best way to determine exception type

I have an exception instance and need to execute code depending on it's type. Which way is more clearly - re raise exception or isinstance check?
re raise:
try:
raise exception
except OperationError as err:
result = do_something1(err)
except (InvalidValue, InvalidContext) as err:
result = do_something2(err)
except AnotherException as err:
result = do_something3(err)
except:
pass
isinstance check:
if isinstance(exception, OperationError):
result = do_something1(err)
elif isinstance(exception, (InvalidValue, InvalidContext)):
result = do_something2(err)
elif isinstance(exception, AnotherException):
result = do_something3(err)
PS. Code is used in django process_exception middleware, therefore when re-raising exception I should write except:pass for all unknown exceptions.
First get rid of the except: pass clause - one should never silently pass exceptions, specially in a bare except clause (one should never use a bare except clause anyway).
This being said, the "best" way really depends on concrete use case. In your above example you clearly have different handlers for different exceptions / exceptions sets, so the obvious solution is the first one. Sometimes you do have some code that's common to all or most of the handlers and some code that's specific to one exception or exceptions subset, then you may want to use isinstance for the specific part, ie:
try:
something_that_may_fail()
except (SomeException, SomeOtherException, YetAnotherOne) as e:
do_something_anyway(e)
if isinstance(e, YetAnotherOne):
do_something_specific_to(e)
Now as mkrieger commented, having three or more exceptions to handle may be a code or design smell - the part in the try block is possibly doing too many things - but then again sometimes you don't have much choice (call to a builtin or third-part function that can fail in many different ways...).
You could store the exceptions you want to handle as keys in a dictionary with different functions as their values. Then you can catch all errors in just one except and call the dictionary to make sure the relevant function is run.
error_handler = {
OperationError: do_something1,
InvalidValue: do_something2,
InvalidContext: do_something2,
AnotherException: do_something3,
}
try:
#raise your exception
except (OperationError, InvalidValue, InvalidContext, AnotherException) as err:
result = error_handler[type(err)]()
I suspect there might be a way to programmatically pass error_handler.keys() to except, but the means I've tried in Python2.7 have not worked so far.
Note that as martineau points out, because this uses type(err) as a dictionary key it won't handle derived exception classes the way that isinstance(err, ...) and except (err) would. You'd need to match exact exceptions.

Is it OK to raise a built-in exception, but with a different message, in Python?

Is it OK to raise a built-in exception with a custom text? or to raise a built-in warning also with custom text?
The documentation reads:
exception ValueError: Raised when a built-in operation or function receives an argument (…)
Is it implied that only built-in operations should raise a ValueError exception?
In practice, I understand that it is safe to create an exception class that inherits from ValueError or Exception. But is it OK not to do that, and directly raise a ValueError("custom text")?
Since ValueError is built-in, raising a ValueError (with a custom text) allows users to quickly see what kind of problem is involved, compared to a custom exception type (something like "ValueErrorSpecificModule", which is not standard).
There's nothing operationally wrong with doing something like:
raise ValueError("invalid input encoding")
In fact, I do that quite often when I'm writing the first pass of some code. The main problem with doing it that way is that clients of your code have a hard time being precise in their exception handling; in order to catch that specific exception, they would have to do string matching on the exception object they caught, which is obviously fragile and tedious. Thus, it would be better to introduce a ValueError subclass of your own; this could still be caught as ValueError, but also as the more specific exception class.
A general rule of thumb is that whenever you have code like:
raise ValueError('some problem: %s' % value)
You should probably replace it with something like:
class SomeProblem(ValueError):
"""
Raised to signal a problem with the specified value.
"""
# ...
raise SomeProblem(value)
You might say that the exception type specifies what went wrong, whereas the message / attributes specify how it went wrong.
It's perfectly ok.
However you may want to create your own subclass to help distinguish from the builtin exceptions
For example if you have something that works like a dict, you can raise a KeyError for the usual reasons, but what if the KeyError is really coming from an underlying dict you are using in the implementation.
Raising a subclass of KeyError makes it easier to see that there is a bug in the implementation, and not that the key just isn't in your object
It's OK and I do it all the time. I find it less surprising to see TypeError than MySpecialTypeError in many situations.
On the page you linked, I don't see the phrase "built-in":
exception TypeError: Raised when an operation or function is applied to an object of inappropriate type. The associated value is a string giving details about the type mismatch.
Perhaps someone saw your question and fixed the documentation already.
EDIT: It looks like you may have inserted the documentation for ValueError instead of TypeError

Categories