Python custom exception and sub-exceptions - python

What is a good way of raising sub-exceptions (is that's the term)?
Scenario:
I want to raise a custom ConnectivityException when http or ftp exception occurs. Is there any way to raise ConnectivityException such that exceptions are categorized properly (i.e. I should be able to tell if ConnectivityException is raised because of http ot ftp)?

A standard technique would be to subclass ConnectivityException to create exception classes specific to each kind of error condition:
class ConnectivityException(Exception): pass
class HTTPConnectivityException(ConnectivityException): pass
class FTPConnectivityException(ConnectivityException): pass
Then instead of raise ConnectivityException you can use raise HTTPConnectivityException or raise FTPConnectivityException, depending on which specific type of error you want to indicate.
Multiple exception blocks can be used to dispatch error handling according to the exception type:
try:
some_network_operation()
except HTTPConnectivityException as ex:
# This will execute if the error is an HTTPConnectivityException.
except FTPConnectivityException as ex:
# Likewise for FTPConnectivityException.
except ConnectivityException as ex:
# The generic case; this block will execute if the ConnectivityException isn't
# an instance of one of the earlier specified subclasses.
Note that the exception-handling blocks are tried in lexical order; the first block specifying a class to which the exception object belongs will be used. In this case, that means that you need to put the ConnectivityException block last, or else it will catch HTTPConnectivityException and FTPConnectivityException as well.

you can add an attribute named 'source' to ConnectivityException, and set it to 'http' or 'ftp' according to specific situation, when catch ConnectivityException, check the source attribute and decide what to do
here i recommend another way which uses inherit class
class ConnectivityException(Exception):
pass # you can define some attributes and methods, here I just escape
class HTTPConnectivityException(ConnectivityException):
pass
class FTPConnectivityException(ConnectivityException):
pass
def func():
if some_condition:
raise HTTPConnectivityException()
if some_other_condition:
raise FTPConnectivityException()
def another_func():
try:
func()
except HTTPConnectivityException as e:
pass # you can do something here
except FTPConnectivityException as e:
pass

Related

How to avoid duplicates when python multiple except executes the same method?

I have the code block like below:
try:
method()
except ErrorType1:
todo()
return
except ErrorType2 as e:
todo()
raise e
Basically for the two error types, I need to execute todo() first, then either return or raise e. Is it possible to just write todo() once? I was thinking using finally but don't think that actually works.
You could catch both exceptions in one except clause, execute todo and then decide to do based on the exception type:
try:
method()
except (ErrorType1, ErrorType2) as e:
todo()
if isinstance(e, ErrorType1):
return
raise
Note - as pointed out by #ShadowRanger in the comments to the question - you should just use raise to re-raise the existing exception, using raise e will raise a second copy of it, resulting in the traceback including the line with raise e on it as well as the line where the original error occurred.
If you have an common set of instructions (either encapsulated as a function or series of functions) that must be executed as part of an exception handling, consider using a context manager to encapsulate the common bits. The following two results in identical outcome, albeit with different construction (one using try..finally, the other using try..except).
from contextlib import contextmanager
#contextmanager
def context1(method):
print("starting context1")
completed = False
try:
yield method()
completed = True
finally:
if completed:
commit()
else:
rollback()
print("finished")
#contextmanager
def context2(method):
print("starting context2")
try:
yield method()
except Exception:
rollback()
raise
else:
commit()
print("finished")
The latter one will not be able to deal with KeyboardInterrupt or other exceptions that subclass off BaseException, so for certain use case this is not exactly ideal, though it is included to follow suite of the question. The first one is more of a response to the fact that you never appeared to have tried using finally, but rather simply thinking it does not actually works, and thus provided to show it can be used to achieve your goal (where only todo() in the question is executed if failure, through the use of a boolean variable).
In both cases, note how the common control flow is fully encapsulated inside the context manager, and usage is fairly straightforward like so such that all the unique extra cases can be done with another try..except block around the with context block.
try:
with context1(f) as result:
pass # or use the result to do something
except Exception: ...
# all the special unique cases be handled here.
To complete demo, more code is below; the commit and rollback functions I defined the following:
def commit():
print("commit")
def rollback():
print("rollback")
Now to test it, I defined the following helpers:
from functools import partial
class ErrorType1(Exception):
pass
class ErrorType2(Exception):
pass
def raise_e(e):
raise e
subject = [
object,
partial(raise_e, ErrorType1),
partial(raise_e, ErrorType2),
]
With the tests defined as such (replace context1 with context2 for the other demonstration):
for f in subject:
try:
with context1(f) as result:
print('done - got result %r' % result)
except ErrorType2:
print("ErrorType2 will be raised")
# raise # uncomment to actually re-raise the exception
except Exception as e:
print("Exception trapped: %r raised by %r" % (e, f))
Note the output of both the above should look about like so (aside from context1 vs context2):
starting context1
done - got result <object object at 0x7f20ccd3e180>
commit
finished
starting context1
rollback
Exception trapped: ErrorType1() raised by functools.partial(<function raise_e at 0x7f20ccb30af0>, <class '__main__.ErrorType1'>)
starting context1
rollback
ErrorType2 will be raised

How to handle multiple exceptions

If I have a code that raises more exceptions simultaniously like this
class A(Exception): pass
class B(Exception): pass
try:
try:
raise A('first')
finally:
raise B('second')
except X as c:
print(c)
Is there a way for handle all the exceptions toghether?
You can handle them all in this way:
except (A, B) as c:
Also you can define your own base class for your exceptions:
class BaseCustomException(Exception): pass
class A(BaseCustomException): pass
class B(BaseCustomException): pass
After it you can catch base exception, it will cover all derived exceptions:
except BaseCustomException as c:
When an new exception is thrown in a catch block or finally block that will propagate out of that block, then the current exception will be aborted (and forgotten) as the new exception is propagated outward. The new exception starts unwinding up the stack just like any other exception, aborting out of the current block (the catch or finally block) and subject to any applicable catch or finally blocks along the way.
check:Exception thrown in catch and finally clause

Python - Implementing Custom exceptions

I have a project that I need to run and have no idea how to implement custom exceptions. It mostly does complicated scientific functions, to be vague.
Mostly it will be raising exceptions if something is not set. I've been given this as a starting example from runnables.
# Define a class inherit from an exception type
class CustomError(Exception):
def __init__(self, arg):
# Set some exception infomation
self.msg = arg
try:
# Raise an exception with argument
raise CustomError('This is a CustomError')
except CustomError, arg:
# Catch the custom exception
print 'Error: ', arg.msg
I have no idea how this is meant to work or how I am meant to implement my code. It's not very explicit.
To give an idea of a basic exception that needs created.
In a function:
if self.humidities is None:
print "ERROR: Humidities have not been set..."
return
Apparently this needs to raise/throw an exception instead.
A ValueError looks suitable for your humidities example.
if self.humidities is None:
raise ValueError('Humidities value required')
If you want to be specific:
class HumiditiesError(Exception):
pass
def set_humidities(humidities):
if humidities is None:
raise HumiditiesError('Value required')
try:
set_humidities(None)
except HumiditiesError as e:
print 'Humidities error:', e.message
This defines a subclass of Exception named HumiditiesError. The default behavior seems sufficient for your example, so the body of the class is empty (pass) as no additional nor modified functionality is required.
N.B. Python 2 assumed. In Python 3 you would access elements of the e.args tuple.

re-raise exception from method as argument

I have a method that needs some wrapping called joule, so I wrap that joule method inside a wrapper called respond (which you will see shortly):
someclass.respond(somemodule.joule(someArgument, someStrategy), 202)
I have a wrapper called respond:
#classmethod
def respond(cls, method, successStatus):
try:
method, successStatus
except Exception as e:
return {
'status': 500,
'message': str(e)
}
The actual method that gets called and raises an Exception:
def joule(params, strategy):
try:
return strategy(params)
finally:
session.rollback()
conn.execute('UNLOCK TABLES')
For some reason, the re-raised exception does not seem to get caught in the respond wrapper! Can you folks help me understand what am I doing incorrectly here?
If this helps, the exception being thrown by sqlalchemy is (please note that this is a scenario being forcibly created to handle the exception correctly):
ProgrammingError: (ProgrammingError) (1146, u"Table 'matrix.vmop_queue' doesn't exist") 'LOCK TABLES vmop_queue WRITE' ()
You are misunderstanding how exception handling works. Exception handling operates on the stack frame of called functions.
In the example you give, someclass.respond does not actually invoke somemodule.joule, instead wherever the line that you have written in your example, which is some outer context is the place that receives the uncaught exception. Thus someclass.respond can't possibly handle the exception thrown by somemodule.joule.
There are other ways to achieve what you are trying to accomplish, but I would need more context in order to give you a better suggestion.
To make this a bit more concrete, let's say that foo contains the example line you gave:
def foo():
someclass.respond(somemodule.joule(someArgument, someStrategy), 202)
You could add the try block to foo to handle the exception thrown by somemodule.joule. This would look like this:
def foo():
try:
someclass.respond(somemodule.joule(someArgument, someStrategy), 202)
except Exception as e:
pass # do something with the exception here
Alternatively, if the whole purpose for someclass.respond is to handle this exception, then you should move the invocation of somemodule.joule inside of someclass.respond. You could even do this more than one way. You could generically take a function and its arguments, and apply that function to the arguments inside of someclass.respond or you could just directly do the invocation inside of someclass.respond.
Let's take the first approach, since you've said that you don't want to repeat the exception handling. I'll call this new method exception_catcher:
def exception_catcher(func, *args):
try:
return func(*args)
except Exception as e:
pass # do whatever you want to the exception
Now the foo context will look like this:
def foo():
exception_catcher(somemodule.joule, someArgument, someStrategy)
Note that exception_catcher is taking somemodule.joule as an argument, and the remaining arguments will be passed to somemodule.joule from within exception_catcher.

python calling custom exceptions from if-statement and try-except

So, I've created a custom exception that I want to call in 2 different ways (a if/else statement, and a try/except statement). Here is the custom exception:
class CustomException(Exception):
def __init__(self, value=None, *args, **kwargs):
self.parameter = value
for key, value in kwargs.items():
setattr(self, key, value)
for key, value in self.__dict__.items():
print "%s => %s" % ( key, value )
def __str__(self):
return repr(self.parameter)
Here is how I am wanting to implement the custom exception:
try:
if something:
#make an error
;lsdfj
else:
raise CustomException('this is my custom message', file='somefile.txt', var2='something')
except Exception, e:
raise CustomException(e)
My issues, I believe, are two fold:
1: When the standard NameError that is thrown in the try/except block (due to ;lsdfj), I want to pass CustomExceptions some extra parameters like 'file', just like the if/else implementation; how would I do that?
2: When the custom exception is raised (from the if/else statement being false), the CustomExceptions class ends up being called twice, because I raise it in the if/else block then it gets raised again within the except: section. I don't know how to get around this.
So, in the above case, I want to call CustomException when the if-statement is not true, and I want to call it when there is a standard exception thrown inside the code block... but currently, if something: evaluates to false then the CustomException will be raised twice...
So I want the custom exception to be used unilaterally throughout my code for if/else conditions, and standard python exceptions...
I know this explanation was convoluted but I'm not sure how else to explain what I'm after... Any help would be much appreciated! Thanks in advance!
In order not to raise the exception twice, you should wrap the try/except block around the if statemnt only, like so:
if something:
try:
#make an error
;fdsfas
except Exception, e:
raise CustomException(e.message, file='somefile.txt', var2='something')
else:
raise CustomException('this is my custom message', file='somefile.txt', var2='something')
And in order to pass the custom exception some parameters you must provide that parameters to the constructor of the class just like you did in the if/else statement.
You could in the except block use:
if not isinstance(e, CustomException): raise CustomException(e)
Edit:
A sys.exc_info() before the raise inside the except will successfully remove the traceback to the source of the exception i.e. NameError.

Categories