5

In systems programming it is common to invoke some library function which may fail, and if it does fail, to check errno for the exact cause. This is true even in Python, and I think it's more cumbersome than it needs to be. Let's take for example some code which tries to remove a file, and continues silently if the file did not exist:

try:
    sftp.unlink(path)
except IOError as ex:
    if ex.errno != errno.ENOENT:
        raise

I'd like to know if it has ever been allowed or proposed in Python to do something more like this:

try:
    sftp.unlink(path)
except IOError as ex if ex.errno == errno.ENOENT:
    pass

I think this has a few things to recommend it:

  1. More concise.
  2. Familiar: as we can already catch certain types of exceptions, we catch only certain actual instances. An uncaught exception is propagated in the usual way.
  3. No new keywords required, nor any major new syntactic constructs.

I'd be surprised if this hasn't been considered before, so I'd accept as an answer any links to proposals past or pending. I'd also accept an answer explaining why the above would introduce any sort of problem with the existing language (Python 3.x, as I think 2.x is mostly frozen).

John Zwinck
  • 207,363
  • 31
  • 261
  • 371
  • 4
    I think this is a great idea, but it does not fit the format of this site at all, and it also invited discussion, as this can not be answered, and is opinionated. – Inbar Rose Nov 06 '13 at 08:37
  • 1
    And the best place to check if it's not already been proposed would be checking the [PEPs](http://www.python.org/dev/peps/) (if it's been formally proposed / rejected) and the [Python Ideas List](https://mail.python.org/mailman/listinfo/python-ideas) for any discussion... It's also worth referring to: the [Python List](https://mail.python.org/mailman/listinfo/python-list) and the [Python Developer's List](https://mail.python.org/mailman/listinfo/python-dev) – Jon Clements Nov 06 '13 at 08:49
  • There's a official workaround since Python3.4+: `contextlib.suppress(*exceptions)` http://docs.python.org/dev/library/contextlib.html#contextlib.suppress And I think it is better than mix if statements with except – Leonardo.Z Nov 06 '13 at 08:52
  • @InbarRose: I explicitly stated in the question how it might be answered: with a link to a PEP (I could find none) or other proposal document, or with some technical explanation of how it might break the language. Barring both, the best answer would be for it to eventually end up with a new PEP, or perhaps some code showing a better way to do it. If you have another StackExchange site I should post this on, please do let me know. – John Zwinck Nov 06 '13 at 08:57
  • @JonClements: thanks, I checked Python Ideas and found this: https://mail.python.org/pipermail/python-ideas/2012-September/016117.html - it's sort of subtopic in passing within a different idea, but it does give us some hints. – John Zwinck Nov 06 '13 at 08:58
  • 1
    It is solved by introducing [`FileNotFoundError`](http://docs.python.org/3/library/exceptions.html?highlight=filenotfounderror#FileNotFoundError), see [pep 3151 Reworking the OS and IO exception hierarchy](http://www.python.org/dev/peps/pep-3151/) – jfs Nov 06 '13 at 09:14
  • @J.F.Sebastian: unfortunately that solves specific cases, but not all of them. For example, it is unlikely to solve my specific issue, using Paramiko rather than a local filesystem, unless/until someone updates Paramiko to use the new exception classes. That said, I agree with you that PEP 3151 does seem to be how the Python folks have decided to address this--just not a complete solution for me. If you post it as an answer, I'd accept it (despite that this question is "on hold" now). – John Zwinck Nov 06 '13 at 10:55
  • @JohnZwinck: my understanding is that it should just work i.e., if old Paramiko code raises `IOError(errno.ENOENT, msg)` (either in pure Python code or in C extension) then `except FileNotFoundError` should catch it. – jfs Nov 06 '13 at 18:51
  • btw, "on hold" means that new answers can't be added and the question will be deleted after some time. – jfs Nov 06 '13 at 18:54

1 Answers1

0

I doubt you will get far with this proposal, but you can get what you want want with a context manager. Try this:

import os

class SuppressOSError(object):
    def __init__(self, *valid_errno):
        self.valid_errno = valid_errno

    def __enter__(self):
        return self

    def __exit__(self, ex_type, ex_value, traceback):
        if issubclass(ex_type, OSError) and ex_value.errno in self.valid_errno:
            return True # suppress exception

with SuppressOSError(os.errno.ENOENT):
    os.remove('/tmp/no_such_file')
    print("This line will not be reached.")
print("An exception was not raised.")

The above prints "An exception was not raised."

You can pass in any number of errno values; in my example I just passed in one. Tested in Python 2.7.4, should work in any Python 2.5 or newer.

steveha
  • 67,444
  • 18
  • 86
  • 112
  • Thanks for showing us this trick, but I have to say, I'd probably never use it, as it's far more complex (harder for new maintainers to follow, more code) than the naive solution. – John Zwinck Nov 06 '13 at 10:57
  • It is harder to implement, but easy to use. And you don't have to implement it because I just gave it to you. This is, IMHO, the best way to solve your problem; it is using the features that the language supports right now (and has since 2.5). Python in general is going in this direction; take a look at the documentation for `contextlib` and look up `contextlib.suppress()` here: http://docs.python.org/dev/library/contextlib.html – steveha Nov 06 '13 at 18:19
  • You will not succeed in getting your proposed change to the language. Something like it has already been discussed, and was not popular. The Python development community is very careful about adding new stuff to the language, and they won't go for this without a good reason. The newly refactored exceptions solve most of the problem anyway in 3.3 and newer, and my solution works in 2.5 and newer, so I don't think there is enough of a problem left to support a change to the language. – steveha Nov 06 '13 at 18:22