If you want to add an extra check not provided by argparse, such as:
if variable a == b then c should be not None
...is it permissible to raise ArgumentError yourself?
Or, should you raise Exception instead?
Also what is common practice for this kind of situation? Say that you add a piece of code that's almost like a local extension of the library. Should you use the same exception type(s) as those provided by the library you are extending?
There's nothing inherently wrong with raising an ArgumentError. You can use it anytime the arguments you receive are not what you expected them to be, including checking range of numbers.
Also, yes, in general it's alright for you to use the same exceptions provided by a given library if you are writing an extension to that library.
Regarding raising Exceptions, I wouldn't do that. You should always raise a specific exception so you know how to handle it in the code. Catching Exception objects should be done at the highest level in your application, to catch and log all exceptions that you missed.
Related
I'm writing a Python function which takes data from an online source and copies it into a local data dump. If there's a file present already on the intended path for said data dump, I'd like my program to stop abruptly, giving a short message to the user - I'm just building a tiny CLI-type program - explaining why what he or she was about to attempt could destroy valuable data.
Would it be appropriate to raise a FileExists error in the above circumstances? If so, I imagine my code would look something like this:
def make_data_dump():
if os.path.exists("path/to/dump"):
raise FileExistsError("Must not overwrite dump at path/to/dump.")
data = get_data_from_url()
write_to_path(data, "path/to/dump")
Apologies if this is a silly question, but I couldn't find any guidance on when to raise a FileExistsError manually, only on what to do if one's program raises such an exception unexpectedly - hence my asking if raising said exception manually is ever good practice.
The Python documentation explicitly states that this is allowed:
User code can raise built-in exceptions. This can be used to test an exception handler or to report an error condition “just like” the situation in which the interpreter raises the same exception; but beware that there is nothing to prevent user code from raising an inappropriate error.
However, your code example is wrong for a different reason. The problem with this code is that it's using the LBYL (Look Before You Leap) pattern, which might read to race conditions. (In the time between checking if the file exists and writing the file, another process could have created the file, which now would be overwritten). A better pattern for these type of scenarios is the EAFP (Easier to Ask for Forgiveness than Permission) pattern.
See What is the EAFP principle in Python? for more information and examples.
Having said that, I think that for most Python code, manually raising a FileExistsError isn't that common, since you would use the standard Python libraries that already throw this error when necessary. However, one valid reason I can think of is when you would write a wrapper for a low-level function (implemented in another language like C) and would want to translate the error to Python.
A hypothetical code example to demonstrate this:
def make_data_dump():
data = get_data_from_url()
# Assuming write_to_path() is a function in a C library, which returns an error code.
error = write_to_path(data, "path/to/dump")
if error == EEXIST:
raise FileExistsError("Must not overwrite dump at path/to/dump.")
Note that some other built-in exceptions are much more common to raise manually, for example a StopIteration, when implementing an iterator, or a ValueError, for example when your method gets an argument with the wrong value (example).
Some exceptions you will rarely use yourself, like a SyntaxError for example.
As per NobbyNobbs's comment above: if the programmer raises standard exception in his code, it's difficult to work out, during error handling, if a given exception was raised on the application or the system level. Therefore, it's a practice best avoided.
This question already has answers here:
How can I know which exceptions might be thrown from a method call?
(8 answers)
Closed 6 years ago.
We are working on a medium-sized commercial Python project and have one reccuring problem when using functions from the standard library.
The documentation of the standard library often does not list all (or even any) exceptions that a function can throw, so we try all the error cases that we can come up with, have a look through the source of the library and then catch whatever is plausible. But quite often we miss that one random error that can still happen, but that we didn't come up with. For example, we missed, that json.loads() can raise a ValueError, if any of the built-in constants are spelled the wrong way (e.g. True instead of true).
In other cases, we tried to just catch Exception, because that part of the code is so critical, that it should never break on an Exception, but should rather try again. The problem here is, that it even caught the KeyboardInterrupt.
So, is there any way to find all exceptions that a function can raise, even if the documentation does not say anything about that?
Are there any tools that can determine what exceptions can be raised?
There is no real way to do this other than reading all of the possible code paths that can be taken in that function and looking to see what exceptions can be raised there. I suppose some sort of automated tool could be written to do this, but even that is pretty tricky because due to python's dynamic nature, just about any exception could be raised from anywhere (If I really wanted to, I can always patch a dependency function with a different function that raises something else entirely).
Monkey Patching aside, To actually get it right, you'd need a really good type inferencer (maybe astroid could help?) to infer various TypeError or AttributeError that could be raised from accessing non-existent members or calling functions with the wrong arguments, etc. ValueError is particularly tricky because it can still get raised when you pass something of the correct type.
In other cases, we tried to just catch Exception, because that part of the code is so critical, that it should never break on an Exception, but should rather try again. The problem here is, that it even caught the KeyboardInterrupt.
This feels like a bad idea to me. For one, retrying code should only be done for exceptions that might give you a different result if you retry it (weird connectivity issues, etc.). For your ValueError case, you'll just raise the ValueError again. The best case scenario here is that the ValueError is allowed to propagate out of the exception handler on the second call -- The worst case is that you end up in an infinite loop (or RecursionError) that you don't really get much information to help debug.
Catching Exception should be a last resort (and it shouldn't catch KeyboardInterrupt or SystemExit since those don't inherit from Exception) and should probably only format some sort of error message that somebody can use to track down the issue and fix it.
In the Java world, we know that the exceptions are classified into checked vs runtime and whenever something throws a checked exception, the caller of that something will be forced to handle that exception, one way or another. Thus the caller would be well aware of the fact that there is an exception and be prepared/coded to handle that.
But coming to Python, given there is no concept of checked exceptions (I hope that is correct), how does the caller of something know if that something would throw an exception or not? Given this "lack of knowledge that an exception could be thrown", how does the caller ever know that it could have handled an exception until it is too late?
There are no checked exceptions in Python.
Read the module docs.
Read the source.
Discover during testing.
Catch a wide range of exception types if necessary (see below).
For example, if you need to be safe:
try:
...
except Exception:
...
Avoid using a bare except clause, as it will even catch things like a KeyboardInterrupt.
As far as I know Python (6 years) there isn't anything similar to Java's throws keyword in Python.
how does the caller of something know if that something would throw an exception or not?
By reading the documentation for that something.
Design Principle of Python: it's easier to ask forgiveness than permission
EAFP
Easier to ask for forgiveness than permission. 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.
Basics of Unix Philosophy: Rule of Repair
Repair what you can — but when you must fail, fail noisily and as soon
as possible.
The essence of both is to use error handling that allows you to find your bugs quickly and wind up with a much more robust program over the long run.
The practical lesson is to learn what errors you should look for as you develop, and only attempt to catch those in your modules, and only use generic Exception handling as a wrapper.
I find myself handling exceptions without specifying an exception type when I call code that interacts with system libraries like shutil, http, etc, all of which can throw if the system is in an unexpected state (e.g. a file locked, network unavailable, etc)
try:
# call something
except:
print("OK, so it went wrong.")
This bothers me because it also catches SyntaxError and other exceptions based in programmer error, and I've seen recommendations to avoid such open-ended exception handlers.
Is there a convention that all runtime errors derive from some common exception base class that I can use here? Or anything that doesn't involve syntax errors, module import failures, etc.? Even KeyError I would consider a bug, because I tend to use dict.get() if I'm not 100% sure the key will be there.
I'd hate to have to list every single conceivable exception type, especially since I'm calling a lot of supporting code I have no control over.
UPDATE: OK, the answers made me realize I'm asking the wrong question -- what I'm really wondering is if there's a Python convention or explicit recommendation for library writers to use specific base classes for their exceptions, so as to separate them from the more mundane SyntaxError & friends.
Because if there's a convention for library writers, I, as a library consumer, can make general assumptions about what might be thrown, even if specific cases may vary. Not sure if that makes more sense?
UPDATE AGAIN: Sven's answer finally led me to understand that instead of giving up and catching everything at the top level, I can handle and refine exceptions at the lower levels, so the top level only needs to worry about the specific exception type from the level below.
Thanks!
Always make the try block as small as possible.
Only catch the exceptions you want to handle. Look in the documentation of the functions you are dealing with.
This ensures that you think about what exceptions may occur, and you think about what to do if they occur. If something happens you never thought about, chances are your exception handling code won't be able to correctly deal with that case anyway, so it would be better the exception gets propagated.
You said you'd "hate to have to list every single conceivable exception type", but usually it's not that bad. Opening a file? Catch IOError. Dealing with some library code? They often have their own exception hierarchies with a specific top-level exception -- just catch this one if you want to catch any of the library-specific exceptions. Be as specific as possible, otherwise errors will pass unnoticed sooner or later.
As for convention about user-defined exceptions in Python: They usually should be derived from Exception. This is also what most user-defined exceptions in the standard library derive from, so the least you should do is use
except Exception:
instead of a bare except clause, which also catches KeyboardInterrupt and SystemExit. As you have noted yourself, this would still catch a lot of exceptions you don't want to catch.
Check out this list of built-in exceptions in the Python docs. It sounds like what you mostly want is to catch StandardError although this also includes KeyError. There are also other base classes, such as ArithmeticError and EnvironmentError, that you may find useful sometimes.
I find third-party libraries do derive their custom exceptions from Exception as documented here. Programmers also tend to raise standard Python exceptions such as TypeError, ValueError, etc. in appropriate situations. Unfortunately, this makes it difficult to consistently catch library errors separately from other errors derived from Exception. I wish Python defined e.g. a UserException base class. Some libraries do declare a base class for their exceptions, but you'd have to know what these are, import the module, and catch them explicitly.
Of course, if you want to catch everything except KeyError and, say, IndexError, along with the script-stopper exceptions, you could do this:
try:
doitnow()
except (StopIteration, GeneratorExit, KeyboardInterrupt, SystemExit):
raise # these stop the script
except (KeyError, IndexError):
raise # we don't want to handle these ones
except Exception as e:
handleError(e)
Admittedly, this becomes a hassle to write each time.
How about RuntimeError: http://docs.python.org/library/exceptions.html#exceptions.RuntimeError
If that isn't what you want (and it may well not be), look at the list of exceptions on that page. If you're confused by how the hierarchy fits together, I suggest you spend ten minutes examining the __bases__ property of the exceptions you're interested in, to see what base classes they share. (Note that __bases__ isn't closed over the whole hierarchy - you may need to examine superclass bases also).
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
python: Should I use ValueError or create my own subclass to handle invalid strings?
Reading Built-in Exceptions I read:
All user-defined exceptions should also be derived from this class" with regards to Exception.
I also see a ValueError which says:
Raised when a built-in operation or function receives an argument that has the right type but an inappropriate value, and the situation is not described by a more precise exception such as IndexError.
If I want to raise an exception for invalid arguments (the equivalent of Ruby's ArgumentError), what should I do? Should I raise ValueError directly, or preferably subclass ValueError with my own intention revealing name?
In my case, I accept a key argument, but I want to restrict the set of characters in the key, such that only /\A[\w.]+\Z/ (Perl/Ruby regular expression) is accepted.
I think the general idea is this: ValueError should almost always denote some sort of client error (where 'client' means the programmer using your interface). There are two high-level types of exceptions in Python:
uncommon cases for otherwise normally functioning code; the client is not to blame
usage errors where some interface is used incorrectly, or the through a series of interface calls, the system has reached an inconsistent state; time to blame the client
In my opinion, for the first case, it makes sense to create exception class hierarchies to allow client code fine-grained control over what to do in uncommon cases.
In the second case, and ValueError is an example of this, you are telling the client that they did something wrong. Fine-grained exception hierarchies are not as important here, because client code probably should be fixed to do the right thing (e.g., pass the right parameter types in the first place).
TL;DR: Just use ValueError, but include a helpful message (e.g., raise ValueError("I'm afraid I can't let you do that, Dave. -HAL 9000"). Don't subclass unless you genuinely expect someone to want to catch SubClassError but not other ValueErrors.
With that said, as you've mentioned, the Python built-in library does subclass ValueError in some cases (e.g. UnicodeError).
ValueError seems to cover your situation quite well.