Custom exception codes and messages in Python 3.4 - python

I'd like to have something like a custom error code/message database and use it when raising exceptions (in Python 3.4). So I did the following:
class RecipeError(Exception):
# Custom error codes
ERRBADFLAVORMIX = 1
ERRNOINGREDIENTS = ERRBADFLAVORMIX + 1
# Custom messages
ERRMSG = {ERRBADFLAVORMIX: "Bad flavor mix",
ERRNOINGREDIENTS: "No ingredients to mix"}
raise RecipeError(RecipeError.ERRMSG[RecipeError.ERRBADFLAVORMIX])
This works as expected, but the raise statement is just monstrous. Sure, I could have stored the values in a more compact way, but what I really want to know is: Can I just do something like raise RecipeError(code) and leave the work of getting the message to RecipeError?

Sure. Exception classes are just normal classes, so you can define your own __init__ that calls super appropriately:
class RecipeError(BaseException):
# existing stuff
def __init__(self, code):
super().__init__(self, RecipeError.ERRMSG[code])
You might also want to save the code:
class RecipeError(BaseException):
# existing stuff
def __init__(self, code):
msg = RecipeError.ERRMSG[code]
super().__init__(self, msg)
self.code, self.msg = code, msg
Take a look at the information stored in the standard library's exceptions (which are pretty decent in 3.4, although there are still more changes to comeā€¦) to see what kinds of things might be useful to stash.
Some side notes:
First, it may be better to use subclasses instead of error codes. For example, if someone wants to write code that catches an ERRBADFLAVORMIX but not an ERRNOINGREDIENTS, they have to do this:
try:
follow_recipe()
except RecipeError as e:
if e != RecipeError.ERRBADFLAVORMIX:
raise
print('Bad flavor, bad!')
Or, if you'd used subclasses:
try:
follow_recipe():
except BadFlavorRecipeError as e:
print('Bad flavor, bad!')
That's exactly why Python no longer has a monolithic OSError with an errno value that you have to switch on, and instead has separate subclasses like FileNotFoundError.
If you do want to use error codes, you might want to consider using an Enum, or maybe one of the fancier enum types on PyPI that make it easier to attach a custom string to each one.
You almost never want to inherit from BaseException, unless you're specifically trying to make sure your exception doesn't get caught.

Related

What exception should I raise for "operation is currently invalid"?

Lets assume there is a method, and I want this method to be called only at certain conditions of a class variable, and otherwise I want to throw an exception:
def foo(self):
if not self.somecondition:
raise ????
else:
#do the thing it supposed to do
Now I am looking for a correct exception for that. I went through them all at python built-in Exceptions docs and found nothing suitable. The classic valueerror is not quite suitable because there is not necessarily a problem with the values but rather with the method being called in this
circumstances.
What exception should I use then? Do I need to create my own in this case or does python has a built in answer?

Eating specific exceptions in Python while using standard exception types

Background: I'm somewhat new to Python, and I'm writing a small roguelike with TDL while taking a course on Python. I'm attempting to make things "pythonic" (my background is mostly in C#).
According to my courseware, there are three things that are "pythonic":
Throw standard exception types unless you really need something different
Don't check pre-conditions extensively; use try/except and recover appropriately
Use duck-typing. Don't check if methods exist, just call them.
In the main loop of my game, I have a collection of entities. I iterate over them and, if they're walkable, call walk. My code looks something like this:
for e in self.area_map.entities:
try:
e.walk()
except AttributeError:
# not walkable
pass
except ValueError:
# all adjacent tiles are not walkable
pass
I recently introduced a bug where one of my entities referenced a variable that ended up being None instead of having a value. Instead of catching this error, I ended up with my entities simply not walking.
It turns out that my two exceptions that I catch can include a wide variety of different exceptions; however, I don't want to hide them -- I only want to hide specific cases (one where the method doesn't exist, and one where my code throws a ValueError).
What's the right way of doing this?
Currently, I'm checking if the exception message is something specific, which smells to me (what if a newer version of Python tweaks the exception message slightly?)
My code now looks like this:
for entity in self.area_map.entities:
try:
entity.walk()
except AttributeError as a:
# AttributeError => entity isn't walkable.
message = str(a)
if not "object has no attribute 'walk'" in message:
raise
except ValueError as v:
# ValueError => nothing adjacent to walk to.
message = str(v)
if message is not "There are no available adjacent locations":
raise
What's the right way to do this?

Python: Try/Except around whole module

I have a python module containing functions and a few classes. This module is basically used as a tool-set by several of my co-workers.
I want to set-up a sort of bug reporting system where anytime someone generates an exception that I don't handle, an email will be sent with information on the exception. This way I can continually improve the robustness of my code and the help-fullness of my own error messages. Is the best way to do this to just put a try/except block around the entire module?
There are several reasons I think your approach might not be the best.
Sometimes exceptions should be thrown. For example, if I pass some stupid argument to a function, it should complain by throwing an exception. You don't want to get an email every time someone passes a string instead of an integer, etc. do you?
Besides, wrapping the entire thing in a try...except won't work, as that will only be catching exceptions that would occur during the definition of the classes/functions (when your module is loaded/imported). For example,
# Your python library
try:
def foo():
raise Exception('foo exception')
return 42
except Exception as e:
print 'Handled: ', e
# A consumer of your library
foo()
The exception is still uncaught.
I guess you can make your own SelfMailingException and subclass it. Not that I would recommend this approach.
another option:
def raises(*exception_list):
def wrap(f):
def wrapped_f(*x, **y):
try:
f(*x, **y)
except Exception as e:
if not isinstance(e, tuple(exception_list)):
print('send mail')
# send mail
raise
return wrapped_f
return wrap
usage:
#raises(MyException)
def foo():
...

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!'

Is this an appropriate way to define and use a Python exception?

I just want to make sure I'm doing this in the proper "pythonic" way - I want to make sure I've defind - and am using - this exception class correctly. Especially the eval(repr()) logic - it's mostly for cleanliness, I understand why you end up with quotes around the string repr() returns, but I don't like to log them.
class IPCClientError(Exception):
""" General IPC Client Exception class """
def __init__(self, value = "Unspecified error"):
self.val = value + ", see IPC client log for details."
def __str__(self):
return eval(repr(self.val))
When I raise the exception, I use something like:
raise IPCClientError("Socket error")
And then the calling method will have something like this:
except IPCClientError, exc:
self.log.error(str(exc))
return ERROR
eval(repr(self.val))
Eek, what are you trying to accomplish here? isn't self.val already supposed to be a string?
The way to avoid the quotes that repr attaches is not to use it in the first place.
If you're worried that the value passed to the constructor won't be a string, well - that will fail in the constructor (unicode quibbles notwithstanding) anyway and you'll just get a TypeError raised before your custom exception can be.
As for how you handle the exception, exception handling is kind of an art, and really not something that can be covered in this space...
(apart from the eval stuff already mentioned by others)
In your except statement, you should write except IPCClientError as exc: (notice the "as"), which is the newer, python 3 compatible way to do it. (the other syntax won't work anymore in python 3, the new one works in python 2.6 and higher)

Categories

Resources