14

I try to write some code to catch a Broken Pipe Error. The code should run in Python 2.x and Python 3.x.

In Python 2.x a broken pipe is represented by a socket.error

socket.error: [Errno 32] Broken pipe

This was changed in Python 3.x - a broken pipe now is a BrokenPipeError

BrokenPipeError: [Errno 32] Broken pipe

Also the syntax of exception handling has changed a bit (see https://stackoverflow.com/a/34463112/263589) so what I need to do would be something like:

try:
    do_something()
except BrokenPipeError as e: # implies Python 3.x
    resolve_for_python2()
except socket.error as e:
    if sys.version_info[0] == 2: # this is necessary, as in Python >=3.3
                                 # socket.error is an alias of OSError
                                 # https://docs.python.org/3/library/socket.html#socket.error
        resolve_for_python3()
    else:
        raise

There's (at least) one remaining problem: In Python 2.x there is no BrokenPipeError, so whenever there is an exception in do_something() Python 2.x would throw another exception and complain that it doesn't know BrokenPipeError. As socket.error is deprecated in Python 3.x a similar problem could arise in Python 3.x in the near future.

What can I do to make this code run in Python 2.x and Python 3.x?

Community
  • 1
  • 1
speendo
  • 13,045
  • 22
  • 71
  • 107
  • Have a look at http://python-future.org/compatible_idioms.html, they show exception handling. – MKesper Jan 11 '16 at 09:44
  • 1
    http://newbebweb.blogspot.in/2012/02/python-head-ioerror-errno-32-broken.html here is – Rajarshi Das Jan 11 '16 at 09:47
  • Thank you! But http://python-future.org/compatible_idioms.html#catching-exceptions doesn't explain how to catch an exception that doesn't exist in either Python 2 or Python 3 but is mandatory in the other version. – speendo Jan 11 '16 at 09:48
  • @RajarshiDas this is interesting! Are you trying to say that catching the broken pipe isn't necessary at all if Python is advised to just ignore the SIGPIPE error? – speendo Jan 11 '16 at 09:53

2 Answers2

13

If all you care about are broken pipe errors, then you might want to catch socket.error and simply check whether it's indeed a broken pipe error.

You can do so using the exception's errno attribute, which is present in both Python 2 and Python 3, which means, you don't need different Python 2 vs. 3 logic (I'd argue the intent is a little clearer this way):

import socket
import errno


try:
    do_something()
except socket.error as e:
    if e.errno != errno.EPIPE:
        # Not a broken pipe
        raise
    do_something_about_the_broken_pipe()

If you do care about more than broken pipes, thefourtheye's answer is appropriate and idiomatic.

Thomas Orozco
  • 53,284
  • 11
  • 113
  • 116
  • Thank you! It worries me a bit that `socket.error` is deprecated in Python 3. So in case I update my Python 3 interpreter, there might be another problem in the (near) future... – speendo Jan 11 '16 at 10:00
  • 2
    @speendo there are dozens of references to `socket.error` *in the Python standard library itself*. It's deprecated, sure, but it's not going away. Removing `socket.error` would be a pointless breaking change, which is something the Python core developers have publicly stated they'll avoid in the future. If it did end up removed (in a few decades?! ;) ), the error would be trivial to identify and fix, and at that point you probably wouldn't be supporting Python 2 anymore anyway. – Thomas Orozco Jan 11 '16 at 10:02
6

You can try using BrokenPipeError and if it throws a NameError, then fall back to socket.error, like this

import socket
try:
    expected_error = BrokenPipeError
except NameError:
    expected_error = socket.error

And then use it like this

try:
    1 == 2
except expected_error as ex:
    # Handle the actual exception here
thefourtheye
  • 233,700
  • 52
  • 457
  • 497