Handling external code exceptions in python - python

I have:
a function call_vendor_code() that calls external vendor code;
an exception class MyDomainException(Exceprion).
I know that function call_vendor_code() could throw MyDomainException (because that logic in function is my). But how do I know which vendor code exceptions could be thrown in call_vendor_code() in subcalls?
Example:
def call_vendor_code():
if not vendor.package.module.make_more_love():
raise MyDomainException('Not enough love.')
Vendor code could throw any other exceptions that I don't handle. It is possible to intercept all excepions from vendor code, but i thing that it is bad solution.
def call_vendor_code():
try:
if not vendor.package.module.make_more_love():
raise MyDomainException('Not enough love.')
except Exception, e:
raise MyDomainException(e)
How to interact/not interact with other-level exceptions?

Your logic is sound.
How you handle these vendor specific exceptions, more elegantly, depends
on the type of exceptions raised by the vendor code.
Are they plain python exceptions(e.g. ValueError, TypeError, etc)? Then
you don't have much of a choice. You should wrap them under a generic
except Exception statement. In these unfortunate cases, many people
try to differentiate the type of error by parsing the exception message.
A major anti-pattern, but what can you do?
Do they raise their own vendor specific exceptions(best case scenario)?
Then you can easily differentiate and handle them more elegantly in your
code. They should have some kind of documentation about it :)
Or, if the code is available, read through the code and see for yourself.
If we're talking about vendor code that talks to some external system(some
kind of DB for example), they might have a generic VendorException with
some internal error code(1133, 623, whatever) that is stored in the exception
instance as an attribute. Again, they most probably will have a listing of
those error codes, along with their description in some kind of documentation,
that you can use to map them into your own custom exceptions.

Related

How do I catch exceptions after a "raise from" in python?

I have code that raises exceptions from other exceptions so that we can see details about eveything that went wrong. In the example below we include information about what we are processing and what specific thing in the processing went wrong.
def process(thing):
try:
process_widgets(thing)
except Exception as e:
raise CouldNotProcess(thing) from e
def process_widgets(thing):
for widget in get_widgets(thing):
raise CouldNotProcessWidget(widget)
def do_processing():
for thing in things_to_process():
process(thing)
I am trying to change this so that process_widgets can raise a specific type of exception and do_processing can change its behaviour based on this exception. However the raise from in process is masking this exception which makes this impossible. Is there a good to let do_processing know about what went wrong in process_widgets while also doing raise from.
Ideas:
Python 3.11 has exception groups. So perhaps there is a way of adding exceptions to group and catching them with the likely confusing except* syntax.
There is a dirty trick where I do raise e from CouldNoPorcess(thing) to get both the helpful logging.
Apparently internally exception chaining works by adding __cause__ property (forming a linked list) so I could manually look through causes in the top most exception to manually implement behaviour like except* with exception groups.
def raised_from(e, type):
while e is not None:
if isinstance(e, Specific):
return True
e = e.__cause__
return False
...
try:
do_processing()
except CouldNotProcess as e:
if raised_from(e, CouldNotProcessWidget):
do_stuff()
You see this pattern quite a lot with status_codes from http.
I could use logging rather than adding information to exceptions. This hides information from the exception handling code, but works for logging. I think this is the work around that I'll use at the moment.
It's noticeable that the PEP says that exception chaining isn't quite designed for adding information to exceptions.
Update
python 3.11 has an add_note method and notes property which can be used to add information - which works for some use cases.
For this use case exception groups might be the way to go, though I am concerned that this might be a little confusing.

Should Docstring contain a 'Raises' statement if the error is handled in the code

Suppose I have a simple function. For example:
def if_a_float(string):
try:
float(string)
except ValueError:
return False
else:
return True
Should I include the Raises: ValueError statement into my docstring or should I avoid it as the error was already handled in the code? Is it done for any error (caught/uncaught)? I do understand that it probably depends on the style, so let's say I am using the Google Docstring style(though I guess it doesn't matter that much)
You should document the exception raised explicitly, as well as those that may be relevant to the interface, as per the Google Style Guidelines (the same document you mention yourself).
This code does not raise an exception explicitly (there is no raise), and you do not need to mention that you are catching one.
Actually, this code cannot even accidentally raise one (you are catching the only line that could) and therefore it would be misleading if you were to document that the if_a_float() was raising a ValueError.
You should only document the exceptions that callers need to be aware of and may want to catch. If the function catches an exception itself and doesn't raise it to the caller, it's an internal implementation detail that callers don't need to be aware of, so it doesn't need to be documented.

Python: Custom Exceptions with a embedded error handling routine?

Is it a bad idea to implement my own exceptions with special error handling/logging system embedded? I wish to concentrate some of my error handling stuff in just one place.
For example:
errors.py
class ReallyBadException(Exception):
def __init__(self, msg, error_handling):
super(Exception, self).__init__(msg)
...
error_handling.logger.error(msg)
error_handling.db.do_stuff(msg)
call_the_police()
...
Obviously I know I still will need to handle the exception locally in the main function etc
main.py
from errors import ReallyBadException, NotSoBadException, ...
...
try:
do_something()
except RellyBadException:
do_another_thing()
Simply put, no this is not a bad idea at all.
In fact, the way I see it, what you're trying to do is ultimately reason why programming languages have exception handling in the first place.

Can I cause an exception on purpose in python?

So this is a little bit of a strange question, but it could be fun!
I need to somehow reliably cause an exception in python. I would prefer it to be human triggered, but I am also willing to embed something in my code that will always cause an exception. (I have set up some exception handling and would like to test it)
I've been looking around and some ideas appear to be division by zero or something along those lines will always cause an exception--Is there a better way? The most ideal would be to simulate a loss of internet connection while the program is running....any ideas would be great!
Have fun!
Yes, there is: You can explicitly raise your own exceptions.
raise Exception("A custom message as to why you raised this.")
You would want to raise an appropriate exception/error for loss of network connectivity.
You can define your own Exceptions in Python, so you can create custom errors to suit your needs. You can test that certain conditions exist, and use the truthiness of that test to decide whether or not to raise your shiny, custom Exception:
class MyFancyException(Exception): pass
def do_something():
if sometestFunction() is True:
raise MyFancyException
carry_on_theres_nothing_to_see()
try:
do_something()
except MyFancyException:
# This is entirely up to you!
# What needs to happen if the exception is caught?
The documentation has some useful examples.
Yup, you can just plop
1 / 0
anywhere in your code for a run time error to occur, specifically in this case a ZeroDivisionError: integer division or modulo by zero.
This is the simplest way to get an exception by embedding something in your code (as you mentioned in your post). You can of course raise your own Exceptions too .. depends on your specific needs.

How often should custom exceptions be defined in python?

In trying to eliminate potential race condition in a python module I wrote to monitor some specialized workflows, I learned about python's "easier to ask forgiveness than permission" (EAFP) coding style, and I'm now raising lots of custom exceptions with try/except blocks where I used to use if/thens.
I'm new to python and This EAFP style makes sense logically and seems make my code more robust, but something about this feels way overboard. Is is bad practice to define one or more exceptions per method?
These custom exceptions tend to be useful only to a single method and, while it feels like a functionally correct solution, it seems like a lot of code to maintain.
Here a sample method for example:
class UploadTimeoutFileMissing(Exception):
def __init__(self, value):
self.parameter = value
def __str__(self):
return repr(self.parameter)
class UploadTimeoutTooSlow(Exception):
def __init__(self, value):
self.parameter = value
def __str__(self):
return repr(self.parameter)
def check_upload(file, timeout_seconds, max_age_seconds, min_age_seconds):
timeout = time.time() + timeout_seconds
## Check until file found or timeout
while (time.time() < timeout):
time.sleep(5)
try:
filetime = os.path.getmtime(file)
filesize = os.path.getsize(file)
except OSError:
print "File not found %s" % file
continue
fileage = time.time() - filetime
## Make sure file isn't pre-existing
if fileage > max_age_seconds:
print "File too old %s" % file
continue
## Make sure file isn't still uploading
elif fileage <= min_age_seconds:
print "File too new %s" % file
continue
return(filetime, filesize)
## Timeout
try:
filetime
filesize
raise UploadTimeoutTooSlow("File still uploading")
except NameError:
raise UploadTimeoutFileMissing("File not sent")
define one or more exceptions per method
If you mean that the exception is actually defined per method as in "within the method body", then yes. That is bad practice. This is true also if you define two exceptions that would relate to the same error but you create two because two different methods raise them.
If you ask whether it is bad practice to raise more than one exception per method, then no, that is good practice. And if the errors are not of the same category, it's perfectly ok to define several exceptions per module.
In general, for larger modules you will define more than one exception. If you would work on some arithmetic library and you would define a ZeroDivisionError and an OverflowError (if they weren't already defined in python, because you can of course re-use those) that would be perfectly fine.
Is is bad practice to define one or more exceptions per method?
Yes.
One per module is more typical. It depends, of course, on the detailed semantics. The question boils down to this: "What will you really try to catch?"
If you're never going to use except ThisVeryDetailedException: in your code, then your very detailed exception isn't very helpful.
If you can do this: except Error as e: if e.some_special_case for the very few times it matters, then you can easily simplify to one exception per module and handle your special cases as attributes of the exception rather than different types of exceptions.
The common suggestions (one per module, named Error) means that your code will often look like this.
try:
something
except some_module.Error as e:
carry on
This gives you a nice naming convention: module.Error. This covers numerous sins.
On an unrelated note, if you think you've got "potential race condition" you should probably redesign things correctly or stop trying to use threads or switch to multiprocessing. If you use multiprocessing, you'll find that it's very easy to avoid race conditions.
I'm going to weigh in on this because custom exceptions are dear to my heart. I'll explain my circumstances and the reader can weigh them against their own.
I'm the pipeline architect for a visual effects company - most of what I do involves developing what I call the "Facility API" - it's a system of a great many modules which handle everything from locating things on the filesystem, managing module/tool/project configuration, to handling datatypes from various CG applications to enable collaboration.
I go to great lengths to try to ensure that Python's built-in exceptions never bubble up. Since our developers will be relying on an ecosystem of existing modules to build their own tools on top of, having the API let a generic IOError escape is counterproductive - especially since the calling routine might not even be aware that it's reading the filesystem (abstraction is a beautiful thing). If the underlying module is unable to express something meaningful about that error, more work needs to be done.
My approach to solving this is to create a facility exception class from which all other facility exceptions are derived. There are subclasses of that for specific types of task or specific host applications - which allows me to customize error handling (for instance, exceptions raised in Maya will launch a UI to aid in troubleshooting since the usual exception would be raised in an inconspicuous console and would often be missed).
All sorts of reporting is built into the facility exception class - exceptions don't appear to a user without also being reported internally. For a range of exceptions, I get an IM any time one is raised. Others simply report quietly into a database that I can query for recent (daily or weekly) reports. Each links to EXTENSIVE data captured from the user session - typically including a screenshot, stack trace, system configuration, and a whole lot more. This means I can effectively troubleshoot problems before they're reported - and have more information at my fingertips than most users are likely able to provide.
Very fine gradations in purpose are discouraged - the exceptions accept passed values (sometimes even a dictionary instead of a string, if we want to provide plenty of data for troubleshooting) to provide with their formatted output.
So no - I don't think defining an exception or two per module is unreasonable - but they need to be meaningful and add something to the project. If you're just wrapping an IOError to raise MyIOError("I got an IO error!"), then you may want to rethink that.
I don't think it's necessary to have an extremely specific exception for every possible scenario. A single UploadTimeoutError would probably be fine, and you can just customize the exception string - that's what the strings are for, after all. Note how python doesn't have a separate exception for every possible type of syntax error, just a general SyntaxError.
Also - is it actually necessary to define the __init__ and __str__ methods for each of your custom exceptions? As far as I can tell, if you're not implementing any unusual behavior, you don't need to add any code:
>>> class MyException(Exception): pass
...
>>> raise MyException("oops!")
Traceback (most recent call last):
File "<ipython console>", line 1, in <module>
MyException: oops!
>>> str(MyException("oops!"))
'oops!'

Categories

Resources