Granularity of OSError

0 views
Skip to first unread message

kj

unread,
Sep 18, 2009, 2:54:56 PM9/18/09
to


I've often come across the idea that good Python style deals with
potential errors using an EAFP ("easier to ask forgiveness than
permission") strategy rather than a LBYL ("look before you leap")
strategy.

For example, LBYL would look like this:

if os.path.isfile(some_file):
os.unlink(some_file)

In contrast, EAFP would look like this:

try:
os.unlink(some_file)
except OSError:
pass

But, as written, the EAFP code above is not satisfactory, because
there can be OSError's triggered for reasons other than the
non-existence of the regular file some_file.

What one needs is a finer granularity of exception, mapping to
exactly the error that one is guarding against.

Is there a standard approach to refining the granularity of exceptions
such as OSError? The only approach I can think of is to somehow
parse the error string (assuming is available) to determine whether
the exception is indeed of the specific kind we're trying to catch.
But this strikes me as grossly inelegant. Is there a better way?

(BTW, the problem is generic, because client code has no control
over the specificity of the exceptions raised by a library module.
If these exceptions turn out to be too broad, client code has to
somehow deal with this reality, at least in the short term.)

TIA!

kynn

Sean DiZazzo

unread,
Sep 18, 2009, 3:05:13 PM9/18/09
to

You can access the exception object which gives you greater detail.

try:
os.unlink(some_file)
except OSError, e:
print e.errno
print e.strerror

if e.errno == 2:
pass
else:
raise


If it's the error you are looking for, handle it, or else raise.

~Sean

Jeff McNeil

unread,
Sep 18, 2009, 3:42:38 PM9/18/09
to

I do this myself in a lot of places, almost exactly like this. It's
slightly clearer to use 'if e.errno == errno.ENOENT' in my opinion,
but, whatever.

The only place I've run into issues with this is when dealing with
socket programming across operating systems. Python on Windows exports
all of the WSA errors when dealing with sockets, but 'WSAEHOSTUNREACH'
is not the same as 'ENETUNREACH.' In cases like that, you've got to be
careful to check for all possible variations of the same error if you
care about supporting Windows.

Thanks,

Jeff
mcjeff.blogspot.com

kj

unread,
Sep 18, 2009, 3:47:59 PM9/18/09
to
In <254eac4d-ce19-4af9...@u16g2000pru.googlegroups.com> Sean DiZazzo <half.i...@gmail.com> writes:

>On Sep 18, 11:54=A0am, kj <no.em...@please.post> wrote:
>> I've often come across the idea that good Python style deals with
>> potential errors using an EAFP ("easier to ask forgiveness than
>> permission") strategy rather than a LBYL ("look before you leap")
>> strategy.
>>
>> For example, LBYL would look like this:
>>
>> if os.path.isfile(some_file):

>> =A0 =A0 os.unlink(some_file)


>>
>> In contrast, EAFP would look like this:
>>
>> try:

>> =A0 =A0 os.unlink(some_file)
>> except OSError:
>> =A0 =A0 pass


>>
>> But, as written, the EAFP code above is not satisfactory, because
>> there can be OSError's triggered for reasons other than the
>> non-existence of the regular file some_file.
>>
>> What one needs is a finer granularity of exception, mapping to
>> exactly the error that one is guarding against.
>>
>> Is there a standard approach to refining the granularity of exceptions

>> such as OSError? =A0The only approach I can think of is to somehow


>> parse the error string (assuming is available) to determine whether
>> the exception is indeed of the specific kind we're trying to catch.

>> But this strikes me as grossly inelegant. =A0Is there a better way?


>>
>> (BTW, the problem is generic, because client code has no control
>> over the specificity of the exceptions raised by a library module.
>> If these exceptions turn out to be too broad, client code has to
>> somehow deal with this reality, at least in the short term.)
>>
>> TIA!
>>
>> kynn

>You can access the exception object which gives you greater detail.

>try:
> os.unlink(some_file)
>except OSError, e:
> print e.errno
> print e.strerror

> if e.errno =3D=3D 2:
> pass
> else:
> raise


>If it's the error you are looking for, handle it, or else raise.


Thanks, that's very handy.

kynn

Ryan Kelly

unread,
Sep 18, 2009, 7:17:21 PM9/18/09
to pytho...@python.org
> > You can access the exception object which gives you greater detail.
> >
> > try:
> > os.unlink(some_file)
> > except OSError, e:
> > print e.errno
> > print e.strerror
> >
> > if e.errno == 2:
> > pass
> > else:
> > raise
>
> I do this myself in a lot of places, almost exactly like this. It's
> slightly clearer to use 'if e.errno == errno.ENOENT' in my opinion,
> but, whatever.

In some cases it's also more correct. While ENOENT is always 2, some
error codes differ between windows and posix. In general it's better to
use the constants from the errno module.

Ryan


--
Ryan Kelly
http://www.rfk.id.au | This message is digitally signed. Please visit
ry...@rfk.id.au | http://www.rfk.id.au/ramblings/gpg/ for details

signature.asc

Christian Heimes

unread,
Sep 18, 2009, 8:23:27 PM9/18/09
to pytho...@python.org
kj wrote:
> For example, LBYL would look like this:
>
> if os.path.isfile(some_file):
> os.unlink(some_file)
>
> In contrast, EAFP would look like this:
>
> try:
> os.unlink(some_file)
> except OSError:
> pass


The two version aren't equal. The first one suffers from a race
condition which may lead to a severe security issue. The file may be
gone or replaced by a different file in the time span between the check
and the call to unlink().

Christian

Sean DiZazzo

unread,
Sep 18, 2009, 9:48:33 PM9/18/09
to

Thanks for pointing that out. I never thought of it before.

~Sean

Grant Edwards

unread,
Sep 18, 2009, 10:30:22 PM9/18/09
to

IOW, just be cause you look before you leap, it doesn't mean
you're not going to land on anybody and have to ask for
forgiveness afterwards.

Since you always have to handle the error case, there's not
much point in checking first unless the error case has bad
side-effects that you're trying to avoid. In this case,
attempting to unlink a non-existent file has no bad
side-effects, so there's no point in checking before the
unlink.

--
Grant

MRAB

unread,
Sep 19, 2009, 10:10:54 AM9/19/09
to pytho...@python.org
It doesn't mean that LBYL is always a bad idea. If, for example, you're
going to copy a file, it's a good idea to check beforehand that there's
enough space available for the copy. There might be other process
changing the amount of freespace available, but it's still a reasonable
check to do.

kj

unread,
Sep 19, 2009, 7:09:31 PM9/19/09
to

>If, for example, you're
>going to copy a file, it's a good idea to check beforehand that there's
>enough space available for the copy.

How do you do that?

TIA,

kynn

MRAB

unread,
Sep 19, 2009, 9:22:21 PM9/19/09
to pytho...@python.org
There's os.statvfs(...), although that's Unix only.

The point is that it's sometimes a good idea to do a cheap check first
before attempting an operation that's 'expensive' even when it fails.

Grant Edwards

unread,
Sep 19, 2009, 9:35:21 PM9/19/09
to

It's also important to note than under some circumstances, the
side-effects of that particular failure (filling up a disk
partition) can be unpleasant -- particularly if you do it as
root. Incoming Mail gets bounced/dropped, cron jobs fail, etc.

--
Grant

ryles

unread,
Sep 20, 2009, 4:39:05 PM9/20/09
to
On Sep 19, 9:22 pm, MRAB <pyt...@mrabarnett.plus.com> wrote:
> The point is that it's sometimes a good idea to do a cheap check first
> before attempting an operation that's 'expensive' even when it fails.

Strongly agree. Furthermore, with LBYL it's often easier to give a
user clearer error messages for common usage errors, rather than
waiting for an exception in a much lower-level place where it's less
clear to them what the cause is.

kj

unread,
Sep 21, 2009, 10:50:28 AM9/21/09
to

>kj wrote:
>> In <mailman.107.1253369...@python.org> MRAB <pyt...@mrabarnett.plus.com> writes:
>>
>>> If, for example, you're
>>> going to copy a file, it's a good idea to check beforehand that there's
>>> enough space available for the copy.
>>
>> How do you do that?
>>
>There's os.statvfs(...), although that's Unix only.

Thanks!

kynn

Reply all
Reply to author
Forward
0 new messages