Title: Map errno==ETIME to TimeoutError
Type: enhancement Stage: patch review
Components: Versions: Python 3.10
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: YoSTEALTH, ZackerySpytz, eric.smith, giampaolo.rodola, sjt
Priority: normal Keywords: patch

Created on 2020-02-18 09:51 by YoSTEALTH, last changed 2020-05-26 12:20 by eric.smith.

Pull Requests
URL Status Linked Edit
PR 20253 open ZackerySpytz, 2020-05-20 05:18
Messages (13)
msg362187 - (view) Author: (YoSTEALTH) * Date: 2020-02-18 09:51
import os

    no = -62
    raise OSError(-no, os.strerror(-no))
except TimeoutError:
except OSError as e:
    print('Failed:', e)
    # Failed: [Errno 62] Timer expired

Shouldn't `TimeoutError` catch this error?
msg362188 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2020-02-18 09:56
I don't see why it would. You're raising OSError, which is not a subclass of TimeoutError, so the TimeoutError code is not executing. You don't say, but I assume this is what you think should happen.

The exception handling machinery does not look inside a raised exception to try to assign meaning to the values, so it doesn't know what errno=62 means. Nor should it: it's only based on the class of the exception, not its attributes.
msg362190 - (view) Author: (YoSTEALTH) * Date: 2020-02-18 10:04
Since I provide `OSError` with appropriate `errono`, it raises that error for example:

import os

    no = -11
    raise OSError(-no, os.strerror(-no))
except BlockingIOError as e:
    print('Success:', e)
    # Success: [Errno 11] Resource temporarily unavailable
except OSError as e:
    print('Failed:', e)

should work the same to raise `TimeoutError` as well
msg362191 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2020-02-18 10:23
In both examples, what's being printed? It's not clear from your messages.
msg362192 - (view) Author: (YoSTEALTH) * Date: 2020-02-18 10:28
First example prints
# Failed: [Errno 62] Timer expired

Second example prints
# Success: [Errno 11] Resource temporarily unavailable
msg362200 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2020-02-18 12:23
Ah, I see.

What platform are you on, and what's the value of errno.ETIMEDOUT?

On cygwin I get:
>>> errno.ETIMEDOUT

On a native Windows build I get:
>>> errno.ETIMEDOUT

and on Fedora I get:
>>> errno.ETIMEDOUT

If you use errno.ETIMEDOUT instead of 62, do you get different behavior?
msg362201 - (view) Author: (YoSTEALTH) * Date: 2020-02-18 12:37
I am on Linux 5.5.2-1-MANJARO

>>> sorted(errno.errorcode.items())
[(1, 'EPERM'), (2, 'ENOENT'), (3, 'ESRCH'), (4, 'EINTR'), (5, 'EIO'), (6, 'ENXIO'), (7, 'E2BIG'), (8, 'ENOEXEC'), (9, 'EBADF'), (10, 'ECHILD'), (11, 'EAGAIN'), (12, 'ENOMEM'), (13, 'EACCES'), (14, 'EFAULT'), (15, 'ENOTBLK'), (16, 'EBUSY'), (17, 'EEXIST'), (18, 'EXDEV'), (19, 'ENODEV'), (20, 'ENOTDIR'), (21, 'EISDIR'), (22, 'EINVAL'), (23, 'ENFILE'), (24, 'EMFILE'), (25, 'ENOTTY'), (26, 'ETXTBSY'), (27, 'EFBIG'), (28, 'ENOSPC'), (29, 'ESPIPE'), (30, 'EROFS'), (31, 'EMLINK'), (32, 'EPIPE'), (33, 'EDOM'), (34, 'ERANGE'), (35, 'EDEADLOCK'), (36, 'ENAMETOOLONG'), (37, 'ENOLCK'), (38, 'ENOSYS'), (39, 'ENOTEMPTY'), (40, 'ELOOP'), (42, 'ENOMSG'), (43, 'EIDRM'), (44, 'ECHRNG'), (45, 'EL2NSYNC'), (46, 'EL3HLT'), (47, 'EL3RST'), (48, 'ELNRNG'), (49, 'EUNATCH'), (50, 'ENOCSI'), (51, 'EL2HLT'), (52, 'EBADE'), (53, 'EBADR'), (54, 'EXFULL'), (55, 'ENOANO'), (56, 'EBADRQC'), (57, 'EBADSLT'), (59, 'EBFONT'), (60, 'ENOSTR'), (61, 'ENODATA'), (62, 'ETIME'), (63, 'ENOSR'), (64, 'ENONET'), (65, 'ENOPKG'), (66, 'EREMOTE'), (67, 'ENOLINK'), (68, 'EADV'), (69, 'ESRMNT'), (70, 'ECOMM'), (71, 'EPROTO'), (72, 'EMULTIHOP'), (73, 'EDOTDOT'), (74, 'EBADMSG'), (75, 'EOVERFLOW'), (76, 'ENOTUNIQ'), (77, 'EBADFD'), (78, 'EREMCHG'), (79, 'ELIBACC'), (80, 'ELIBBAD'), (81, 'ELIBSCN'), (82, 'ELIBMAX'), (83, 'ELIBEXEC'), (84, 'EILSEQ'), (85, 'ERESTART'), (86, 'ESTRPIPE'), (87, 'EUSERS'), (88, 'ENOTSOCK'), (89, 'EDESTADDRREQ'), (90, 'EMSGSIZE'), (91, 'EPROTOTYPE'), (92, 'ENOPROTOOPT'), (93, 'EPROTONOSUPPORT'), (94, 'ESOCKTNOSUPPORT'), (95, 'ENOTSUP'), (96, 'EPFNOSUPPORT'), (97, 'EAFNOSUPPORT'), (98, 'EADDRINUSE'), (99, 'EADDRNOTAVAIL'), (100, 'ENETDOWN'), (101, 'ENETUNREACH'), (102, 'ENETRESET'), (103, 'ECONNABORTED'), (104, 'ECONNRESET'), (105, 'ENOBUFS'), (106, 'EISCONN'), (107, 'ENOTCONN'), (108, 'ESHUTDOWN'), (109, 'ETOOMANYREFS'), (110, 'ETIMEDOUT'), (111, 'ECONNREFUSED'), (112, 'EHOSTDOWN'), (113, 'EHOSTUNREACH'), (114, 'EALREADY'), (115, 'EINPROGRESS'), (116, 'ESTALE'), (117, 'EUCLEAN'), (118, 'ENOTNAM'), (119, 'ENAVAIL'), (120, 'EISNAM'), (121, 'EREMOTEIO'), (122, 'EDQUOT'), (123, 'ENOMEDIUM'), (124, 'EMEDIUMTYPE'), (125, 'ECANCELED'), (126, 'ENOKEY'), (127, 'EKEYEXPIRED'), (128, 'EKEYREVOKED'), (129, 'EKEYREJECTED'), (130, 'EOWNERDEAD'), (131, 'ENOTRECOVERABLE'), (132, 'ERFKILL')]

Its an automated process the value is return by the C/kernel. I suppose its calling `ETIME`

ETIME - Timer expired (POSIX.1 (XSI STREAMS option)).
                      (POSIX.1 says "STREAM ioctl(2) timeout".)
ETIMEDOUT - Connection timed out (POSIX.1-2001).

These are both timeout errors but only `ETIMEDOUT` is accounted for?
msg362204 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2020-02-18 13:03
> These are both timeout errors but only `ETIMEDOUT` is accounted for?

Yes, only ETIMEDOUT is accounted for in Objects/exceptions.c. There's precedent for mapping multiple errnos to the same exception:

    ADD_ERRNO(BlockingIOError, EAGAIN);
    ADD_ERRNO(BlockingIOError, EALREADY);

You should probably raise this on python-ideas. I don't know if ETIME has some other accepted meaning. It's not mapped to any other exception, so it could be easily added. The only thing it would affect are people who are catching OSError and TimeoutError and are expecting ETIME to give an OSError, which seems a pretty niche case. Or I guess people who only catch TimeoutError and want to not catch the case where errno==ETIME.
msg362205 - (view) Author: (YoSTEALTH) * Date: 2020-02-18 13:11
If nothing else, it could be a feature of next Python release as its appropriate that `TimeoutError` catches both `ETIME` and `ETIMEDOUT`.
msg369924 - (view) Author: Giampaolo Rodola' (giampaolo.rodola) * (Python committer) Date: 2020-05-25 20:38
I'm -1 about TimeoutError because the concept of "timeout" is generic enough to be often implemented as a custom exception, which poses questions re. backward/forward compatibilty. E.g. in psutil I have "TimeoutExpired", also providing a "seconds" attribute. Also I've probably never seen ETIME / ETIMEDOUT happening, whereas AFAIU the point of PEP 3151 was to create mappings for the most common errnos.
msg369932 - (view) Author: Stephen J. Turnbull (sjt) * (Python triager) Date: 2020-05-26 00:27
First, let me say I like Giampaolo's TimeoutExpired *much* better as the name for this kind of exception!  But that ship has sailed.

I don't understand Giampaolo's comment.  If I understand the claim correctly, the problem is that people who should be catching some application-specific exception may be misled into catching TimeoutError instead, or into trying to get application-specific attributes from TimeoutError.  But that ship sailed with the creation of TimeoutError.  (We have a whole fleet sailing with this exception.)  Unless Giampaolo is proposing to deprecate TimeoutError?  I'm sympathetic ;-), but deprecation is a PITA and takes forever.

If we're not going to deprecate, it seems to me that it's much more developer-friendly to catch ETIME with TimeoutError, as that seems very likely to be the expected behavior.  It's true that even if Giampaolo changes TimeoutExpired to subclass TimeoutError, generic TimeoutError won't have .seconds.  But if you catch a TimeoutExpired with TimeoutError, that instance *will* have .seconds, and if you try to get .seconds on generic TimeoutError, you'll get a different uncaught exception (AttributeError vs. TimeoutError), but that TimeoutError wouldn't have been handled by catching TimeoutExpired.

I agree with Eric that people who were distinguishing OSError with .errno=ETIME from TimeoutError might be at risk, but I wouldn't do that: if I were going to be distinguishing particular OSErrors on the basis of errno (other than in "Unexpected OSError (errno = %d)" reporting style), I'd just catch OSError and do that.  On the other hand, I might expect TimeoutError to catch ETIME.  And Giampaolo says he's never seen either.  I suppose the author of psutil would be as likely as anyone to have seen it!

On net (unless we go the deprecation route) it seems that the convenience and "intuition" of adding ETIME to TimeoutError outweighs that risk.

I wish there were somebody who was there at the creation of ETIME!
msg369960 - (view) Author: Giampaolo Rodola' (giampaolo.rodola) * (Python committer) Date: 2020-05-26 09:06
Sigh! I misread the OP's post and thought the proposal was to add TimeoutError which I forgot existed. Sorry for the noise and please disregard my previous comment.
msg369973 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2020-05-26 12:20
Thanks for clarifying, Giampaolo. I'll accept this PR once it's cleaned up.
Date User Action Args
2020-05-26 12:20:54eric.smithsetmessages: + msg369973
2020-05-26 09:06:20giampaolo.rodolasetmessages: + msg369960
2020-05-26 00:27:05sjtsetnosy: + sjt
messages: + msg369932
2020-05-25 20:38:04giampaolo.rodolasetnosy: + giampaolo.rodola
messages: + msg369924
2020-05-20 05:20:42ZackerySpytzsetversions: + Python 3.10, - Python 3.9
2020-05-20 05:18:25ZackerySpytzsetkeywords: + patch
nosy: + ZackerySpytz

pull_requests: + pull_request19538
stage: needs patch -> patch review
2020-02-18 13:36:07eric.smithsetstage: needs patch
2020-02-18 13:35:53eric.smithsettype: behavior -> enhancement
title: TimeoutError -> Map errno==ETIME to TimeoutError
2020-02-18 13:11:47YoSTEALTHsetmessages: + msg362205
versions: + Python 3.9, - Python 3.8
2020-02-18 13:03:06eric.smithsetmessages: + msg362204
2020-02-18 12:37:25YoSTEALTHsetmessages: + msg362201
2020-02-18 12:23:04eric.smithsetmessages: + msg362200
2020-02-18 10:28:33YoSTEALTHsetmessages: + msg362192
2020-02-18 10:23:40eric.smithsetmessages: + msg362191
2020-02-18 10:04:36YoSTEALTHsetmessages: + msg362190
2020-02-18 09:56:10eric.smithsetnosy: + eric.smith
messages: + msg362188
2020-02-18 09:51:59YoSTEALTHcreate