-
-
Notifications
You must be signed in to change notification settings - Fork 30.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
winerror_to_errno implementation #81886
Comments
OSError and _Py_fstat_noraise rely on winerror_to_errno() in PC/errmap.h in order to translate a Windows system error code into a POSIX errno value. PC/errmap.h is supposed to be generated by PC/generrmap.c, which is based on the old CRT's _dosmapperr() function. However, this function isn't implemented in the Universal CRT, and ucrt's corresponding function, __acrt_errno_from_os_error, isn't exported for public use. If generrmap.c is effectively dead, then it's no longer possible to add custom mappings there, as was done for bpo-12802 (ERROR_DIRECTORY -> ENOTDIR) and bpo-13063 (ERROR_NO_DATA -> EPIPE). Also, errmap.h hasn't been regenerated in 8 years, and since then the CRT added a new mapped value: ERROR_NO_UNICODE_TRANSLATION (1113) -> EILSEQ. Unless someone can suggest a way to continue automatically generating errmap.h via generrmap.c, then I think winerror_to_errno should be manually implemented in a more readable and maintainable way, and updated from the ucrt source (ucrt\misc\errno.cpp) with each major Python release. The implementation could use a switch statement like it currently does, but with named error codes, grouped by result. For example: int
winerror_to_errno(int winerror)
{
switch(winerror) {
case ERROR_FILE_NOT_FOUND: // 2
case ERROR_PATH_NOT_FOUND: // 3
case ERROR_INVALID_DRIVE: // 15
case ERROR_NO_MORE_FILES: // 18
case ERROR_BAD_NETPATH: // 53
case ERROR_BAD_NET_NAME: // 67
case ERROR_BAD_PATHNAME: // 161
case ERROR_FILENAME_EXCED_RANGE: // 206
return ENOENT;
case ERROR_INVALID_FUNCTION: // 1
case ERROR_INVALID_ACCESS: // 12
case ERROR_INVALID_DATA: // 13
case ERROR_INVALID_PARAMETER: // 87
case ERROR_NEGATIVE_SEEK: // 131
default:
return EINVAL;
}
} |
Special casing of Winsock error codes should be handled in winerror_to_errno rather than in oserror_parse_args, so it's consistently applied everywhere. And it should use the proper range for Winsock, 10000-11999 -- not 10000-2147483647. Codes above the Winsock range should map to EINVAL. It's not a non-issue since this range has the following blocks allocated:
Another thing winerror_to_errno should do is unwrap HRESULT codes for Win32 errors (0x80070000-0x8007FFFF). In other words, 0x80070005 is effectively the same as ERROR_ACCESS_DENIED (5). It should be mapped to EACCES and raise PermissionError. For example: int
winerror_to_errno(int winerror)
{
/* Unwrap FACILITY_WIN32 HRESULT errors. */
if ((winerror & 0xFFFF0000) == 0x80070000) {
winerror &= 0x0000FFFF;
}
/* Winsock error codes (10000-11999) are errno values. */
if (winerror >= 10000 && winerror < 12000) {
return winerror;
}
switch(winerror) {
/* ... */
}
} |
While I'm inclined to think it's okay to find the CRT sources (e.g. "C:\Program Files (x86)\Windows Kits\10\Source\10.0.18362.0\ucrt\misc\errno.cpp") and extract the table from there as part of build, the problem I have is that it is woefully incomplete. I'd much rather have a manually managed table. It'd also be nice to expose it through the errno module somehow, probably just as a dict. It also seems like at least some of the WSA* constants (e.g. WSAEBADF==10009) are the equivalent errno plus 10000 (e.g.EBADF==9). Should we be mapping those back to the errno value? |
Mapping WSAEINTR (10004) -> EINTR (4) and WSAEACCES (10013) -> EACCES (13) would allow special casing them as InterruptedError and PermissionError. This is different from the aliases such as EWOULDBLOCK = WSAEWOULDBLOCK, but it should be fine since the six affected values are singled out in WinSock2.h as C errno values. So the Winsock section becomes a switch: /* Winsock error codes (10000-11999) are errno values. */
if (winerror >= 10000 && winerror < 12000) {
switch(winerror) {
case WSAEINTR:
case WSAEBADF:
case WSAEACCES:
case WSAEFAULT:
case WSAEINVAL:
case WSAEMFILE:
/* Winsock definitions of errno values. See WinSock2.h */
return winerror - 10000;
default:
return winerror;
}
} |
I have created a pull request for this issue. Please take a look. |
I'm going to merge and backport this all the way to 3.7. Adding a publicly accessible dict for use in Python code should be 3.9 only. |
New changeset 38e0b9e by Oleg Iarygin in branch 'main': |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: