Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #8066

Re: those darn exceptions

From Ben Finney <ben+python@benfinney.id.au>
Newsgroups comp.lang.python
Subject Re: those darn exceptions
References <itot0b022i@news4.newsguy.com>
Date 2011-06-21 14:04 +1000
Message-ID <87y60vq18v.fsf@benfinney.id.au> (permalink)
Organization Unlimited download news at news.astraweb.com

Show all headers | View raw


Chris Torek <nospam@torek.net> writes:

> It can be pretty obvious. For instance, the os.* modules raise OSError
> on errors.

Not *only* OSError, of course.

> The examples here are slightly silly until I reach the "real" code at
> the bottom, but perhaps one will get the point:
>
>     >>> import os
>     >>> os.kill(getpid(), 0) # am I alive?
>     >>> # yep, I am alive.
>     ...
>
> [I'm not sure why the interpreter wants more after my comment here.]

Because it's waiting for the end of the statement. (A comment is ignored
by the compiler, so doesn't become part of the statement.)

For a no-op statement, use ‘pass’::

    >>> pass  # yep, I am alive.
    >>>

> So now I am ready to write my "is process <pid> running" function:
>
>     import os, errno
>
>     def is_running(pid):
>         "Return True if the given pid is running, False if not."
>         try:
>             os.kill(pid, 0)
>         except OSError, err:

You should avoid ambiguity (is this two types of exception, or two
arguments?) by using the new syntax for an ‘except’ clause::

            except OSError as err:

>             # We get an EPERM error if the pid is running
>             # but we are not allowed to signal it (even with
>             # signal 0).  If we get any other error we'll assume
>             # it's not running.

Why assume it's not running? You get a specific error for that case,
‘errno.ESRCH’. That's what you should be comparing

>             if err.errno != errno.EPERM:
>                 return False

You've caught the OSError, but you're throwing it away here. You've done
nothing with the exception and it's swallowed silently by your code.

Instead, if you realise you don't want to handle the exception, you need
to re-raise it so it propagates, using ‘raise’ with no arguments.

Here's my suggestion for improving that code::

        import os
        import signal
        import errno

        def is_running(pid):
            """ Return True iff the specified PID is running. """

            try:
                os.kill(pid, signal.SIG_DFL)
            except OSError as err:
                if err.errno == errno.EPERM:
                    # We get an EPERM error if the pid is running
                    # but we are not allowed to signal it (even with
                    # signal 0).
                    result = True
                if err.errno == errno.ESRCH:
                    # The process ID wasn't found by ‘os.kill’.
                    result = False
                else:
                    # If we get any other OSError we aren't handling it;
                    # re-raise it up the call stack.
                    raise
            else:
                # No exception was raised.
                result = True

            return result

> This function works great, and never raises an exception itself.

“Never raises an exception” is a poor design goal. The point of
exceptions is that they allow the programmer to handle them where it
makes most sense. Swallowing them silently is a code smell, as in this
case.

> Oops!  It turns out that os.kill() can raise OverflowError (at
> least in this version of Python, not sure what Python 3.x does).
>
> Now, I could add, to is_running, the clause:
>
>         except OverflowError:
>             return False

That would be a bad idea. An OverflowError is exactly what the caller
needs to know: the number specified was too big. There's little point in
catching it yourself, only to lose the information so the caller can't
know what the problem was.

> But how can I know a priori that os.kill() could raise OverflowError
> in the first place?

By designing your unit test cases well.

> It would be better just to note somewhere that OverflowError is
> one of the errors that os.kill() "normally" produces (and then,
> presumably, document just when this happens, so although having
> noted that it can, one could make an educated guess).

Agreed. I'm sure the Python documentation maintainers would appreciate a
patch to the documentation, submitted in the proper place: the Python
bug tracker <URL:http://bugs.python.org/>.

> Functions have a number of special "__" attributes.

That convention is for attributes treated as special by Python itself.
It's an indicator “this attribute will change the way Python uses this
object, without necessarily seeing this attribute used by this name in
any of my code”.

> I think it might be reasonable to have all of the built-in functions,
> at least, have one more, perhaps spelled __exceptions__, that gives
> you a tuple of all the exceptions that the function might raise.

There's no such set smaller than the entire set of all exceptions. Any
code might call any other, which can then raise any exception.

>     >>> os.kill.__exceptions__
>     (<type 'exceptions.OSError'>, <type 'exceptions.TypeError'>, <type 'exceptions.OverflowError'>, <type 'exceptions.DeprecationWarning'>)

This is false. Any exception could be raised by any call. Learn to live
with that.

-- 
 \     “Computers are useless. They can only give you answers.” —Pablo |
  `\                                                           Picasso |
_o__)                                                                  |
Ben Finney

Back to comp.lang.python | Previous | NextPrevious in thread | Next in thread | Find similar | Unroll thread


Thread

those darn exceptions Chris Torek <nospam@torek.net> - 2011-06-21 01:43 +0000
  Re: those darn exceptions Chris Angelico <rosuav@gmail.com> - 2011-06-21 13:19 +1000
    Re: those darn exceptions Chris Torek <nospam@torek.net> - 2011-06-21 04:40 +0000
  Re: those darn exceptions Ben Finney <ben+python@benfinney.id.au> - 2011-06-21 14:04 +1000
  Re: those darn exceptions Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-06-21 09:49 +0000
    Re: those darn exceptions Chris Torek <nospam@torek.net> - 2011-06-21 21:51 +0000
      Re: those darn exceptions John Nagle <nagle@animats.com> - 2011-06-27 11:52 -0700
  Re: those darn exceptions Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2011-06-23 20:16 +1200
    Re: those darn exceptions Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-06-23 18:40 +1000
    Re: those darn exceptions Chris Torek <nospam@torek.net> - 2011-06-23 18:33 +0000
      Re: those darn exceptions Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2011-06-24 19:50 +1200
        Re: those darn exceptions Chris Torek <nospam@torek.net> - 2011-06-24 18:21 +0000
          Re: those darn exceptions Ben Finney <ben+python@benfinney.id.au> - 2011-06-25 10:25 +1000
            Re: those darn exceptions Chris Angelico <rosuav@gmail.com> - 2011-06-25 13:55 +1000
              Re: those darn exceptions steve+comp.lang.python@pearwood.info - 2011-06-26 00:28 +1000
                Re: those darn exceptions Chris Angelico <rosuav@gmail.com> - 2011-06-26 01:52 +1000

csiph-web