define and use errno_from_exception abstraction

If an OSError or IOError are instantiated without an errno value, e.g.
e = OSError(). The existing code would give an IndexError: tuple index
out of range. However there have been cases that the errno attribute
wasn't populated so instead of switching to it this introduces a helper
function to ensure we get the errno value through whatever means
possible.