mirror of
https://github.com/python/cpython.git
synced 2025-11-13 07:26:31 +00:00
Merged revisions 84719 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84719 | r.david.murray | 2010-09-11 14:12:25 -0400 (Sat, 11 Sep 2010) | 2 lines #9608, #8518 : clarify and improve discussion of exceptions in howto. ........
This commit is contained in:
parent
42bc69154f
commit
bec04a9032
1 changed files with 53 additions and 34 deletions
|
|
@ -111,30 +111,40 @@ except:
|
||||||
-------
|
-------
|
||||||
|
|
||||||
Python has the ``except:`` clause, which catches all exceptions. Since *every*
|
Python has the ``except:`` clause, which catches all exceptions. Since *every*
|
||||||
error in Python raises an exception, this makes many programming errors look
|
error in Python raises an exception, using ``except:`` can make many
|
||||||
like runtime problems, and hinders the debugging process.
|
programming errors look like runtime problems, which hinders the debugging
|
||||||
|
process.
|
||||||
|
|
||||||
The following code shows a great example::
|
The following code shows a great example of why this is bad::
|
||||||
|
|
||||||
try:
|
try:
|
||||||
foo = opne("file") # misspelled "open"
|
foo = opne("file") # misspelled "open"
|
||||||
except:
|
except:
|
||||||
sys.exit("could not open file!")
|
sys.exit("could not open file!")
|
||||||
|
|
||||||
The second line triggers a :exc:`NameError` which is caught by the except
|
The second line triggers a :exc:`NameError`, which is caught by the except
|
||||||
clause. The program will exit, and you will have no idea that this has nothing
|
clause. The program will exit, and the error message the program prints will
|
||||||
to do with the readability of ``"file"``.
|
make you think the problem is the readability of ``"file"`` when in fact
|
||||||
|
the real error has nothing to do with ``"file"``.
|
||||||
|
|
||||||
The example above is better written ::
|
A better way to write the above is ::
|
||||||
|
|
||||||
try:
|
try:
|
||||||
foo = opne("file") # will be changed to "open" as soon as we run it
|
foo = opne("file")
|
||||||
except IOError:
|
except IOError:
|
||||||
sys.exit("could not open file")
|
sys.exit("could not open file")
|
||||||
|
|
||||||
There are some situations in which the ``except:`` clause is useful: for
|
When this is run, Python will produce a traceback showing the :exc:`NameError`,
|
||||||
example, in a framework when running callbacks, it is good not to let any
|
and it will be immediately apparent what needs to be fixed.
|
||||||
callback disturb the framework.
|
|
||||||
|
.. index:: bare except, except; bare
|
||||||
|
|
||||||
|
Because ``except:`` catches *all* exceptions, including :exc:`SystemExit`,
|
||||||
|
:exc:`KeyboardInterrupt`, and :exc:`GeneratorExit` (which is not an error and
|
||||||
|
should not normally be caught by user code), using a bare ``except:`` is almost
|
||||||
|
never a good idea. In situations where you need to catch all "normal" errors,
|
||||||
|
such as in a framework that runs callbacks, you can catch the base class for
|
||||||
|
all normal exceptions, :exc:`Exception`.
|
||||||
|
|
||||||
|
|
||||||
Exceptions
|
Exceptions
|
||||||
|
|
@ -152,40 +162,43 @@ The following is a very popular anti-idiom ::
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
return open(file).readline()
|
return open(file).readline()
|
||||||
|
|
||||||
Consider the case the file gets deleted between the time the call to
|
Consider the case where the file gets deleted between the time the call to
|
||||||
:func:`os.path.exists` is made and the time :func:`open` is called. That means
|
:func:`os.path.exists` is made and the time :func:`open` is called. In that
|
||||||
the last line will throw an :exc:`IOError`. The same would happen if *file*
|
case the last line will raise an :exc:`IOError`. The same thing would happen
|
||||||
exists but has no read permission. Since testing this on a normal machine on
|
if *file* exists but has no read permission. Since testing this on a normal
|
||||||
existing and non-existing files make it seem bugless, that means in testing the
|
machine on existent and non-existent files makes it seem bugless, the test
|
||||||
results will seem fine, and the code will get shipped. Then an unhandled
|
results will seem fine, and the code will get shipped. Later an unhandled
|
||||||
:exc:`IOError` escapes to the user, who has to watch the ugly traceback.
|
:exc:`IOError` (or perhaps some other :exc:`EnvironmentError`) escapes to the
|
||||||
|
user, who gets to watch the ugly traceback.
|
||||||
|
|
||||||
Here is a better way to do it. ::
|
Here is a somewhat better way to do it. ::
|
||||||
|
|
||||||
def get_status(file):
|
def get_status(file):
|
||||||
try:
|
try:
|
||||||
return open(file).readline()
|
return open(file).readline()
|
||||||
except (IOError, OSError):
|
except EnvironmentError as err:
|
||||||
print("file not found")
|
print("Unable to open file: {}".format(err))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
In this version, \*either\* the file gets opened and the line is read (so it
|
In this version, *either* the file gets opened and the line is read (so it
|
||||||
works even on flaky NFS or SMB connections), or the message is printed and the
|
works even on flaky NFS or SMB connections), or an error message is printed
|
||||||
application aborted.
|
that provides all the available information on why the open failed, and the
|
||||||
|
application is aborted.
|
||||||
|
|
||||||
Still, :func:`get_status` makes too many assumptions --- that it will only be
|
However, even this version of :func:`get_status` makes too many assumptions ---
|
||||||
used in a short running script, and not, say, in a long running server. Sure,
|
that it will only be used in a short running script, and not, say, in a long
|
||||||
the caller could do something like ::
|
running server. Sure, the caller could do something like ::
|
||||||
|
|
||||||
try:
|
try:
|
||||||
status = get_status(log)
|
status = get_status(log)
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
status = None
|
status = None
|
||||||
|
|
||||||
So, try to make as few ``except`` clauses in your code --- those will usually be
|
But there is a better way. You should try to use as few ``except`` clauses in
|
||||||
a catch-all in the :func:`main`, or inside calls which should always succeed.
|
your code as you can --- the ones you do use will usually be inside calls which
|
||||||
|
should always succeed, or a catch-all in a main function.
|
||||||
|
|
||||||
So, the best version is probably ::
|
So, an even better version of :func:`get_status()` is probably ::
|
||||||
|
|
||||||
def get_status(file):
|
def get_status(file):
|
||||||
return open(file).readline()
|
return open(file).readline()
|
||||||
|
|
@ -194,9 +207,15 @@ The caller can deal with the exception if it wants (for example, if it tries
|
||||||
several files in a loop), or just let the exception filter upwards to *its*
|
several files in a loop), or just let the exception filter upwards to *its*
|
||||||
caller.
|
caller.
|
||||||
|
|
||||||
The last version is not very good either --- due to implementation details, the
|
But the last version still has a serious problem --- due to implementation
|
||||||
file would not be closed when an exception is raised until the handler finishes,
|
details in CPython, the file would not be closed when an exception is raised
|
||||||
and perhaps not at all in non-C implementations (e.g., Jython). ::
|
until the exception handler finishes; and, worse, in other implementations
|
||||||
|
(e.g., Jython) it might not be closed at all regardless of whether or not
|
||||||
|
an exception is raised.
|
||||||
|
|
||||||
|
The best version of this function uses the ``open()`` call as a context
|
||||||
|
manager, which will ensure that the file gets closed as soon as the
|
||||||
|
function returns::
|
||||||
|
|
||||||
def get_status(file):
|
def get_status(file):
|
||||||
with open(file) as fp:
|
with open(file) as fp:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue