@@ -20,25 +20,33 @@ pub use _io::{OpenArgs, io_open as open};
2020impl ToPyException for std:: io:: Error {
2121 fn to_pyexception ( & self , vm : & VirtualMachine ) -> PyBaseExceptionRef {
2222 let errno = self . posix_errno ( ) ;
23- let msg = self . to_string ( ) ;
24- #[ allow( clippy:: let_and_return) ]
25- let exc = vm. new_errno_error ( errno, msg) ;
2623
2724 #[ cfg( windows) ]
28- {
29- use crate :: object:: AsObject ;
30- let winerror = if let Some ( winerror) = self . raw_os_error ( ) {
31- vm. new_pyobj ( winerror)
32- } else {
33- vm. ctx . none ( )
34- } ;
35-
36- // FIXME: manual setup winerror due to lack of OSError.__init__ support
37- exc. as_object ( )
38- . set_attr ( "winerror" , vm. new_pyobj ( winerror) , vm)
39- . unwrap ( ) ;
40- }
41- exc
25+ let msg = {
26+ // On Windows, use C runtime's strerror for POSIX errno values
27+ // For Windows-specific error codes, fall back to FormatMessage
28+
29+ // UCRT's strerror returns "Unknown error" for invalid errno values
30+ // Use strerror for POSIX errno range, fall back to FormatMessage otherwise
31+ // Windows UCRT defines errno values 1-42 plus some more up to ~127
32+ const MAX_POSIX_ERRNO : i32 = 127 ;
33+ if errno > 0 && errno <= MAX_POSIX_ERRNO {
34+ // Safety: strerror returns a valid C string
35+ let ptr = unsafe { libc:: strerror ( errno) } ;
36+ if !ptr. is_null ( ) {
37+ let msg = unsafe { std:: ffi:: CStr :: from_ptr ( ptr) } . to_string_lossy ( ) ;
38+ // Check if strerror returned a valid message (not "Unknown error")
39+ if !msg. starts_with ( "Unknown error" ) {
40+ return vm. new_errno_error ( errno, msg. into_owned ( ) ) ;
41+ }
42+ }
43+ }
44+ // For Windows-specific errors or unknown errno, use FormatMessage
45+ self . to_string ( )
46+ } ;
47+ #[ cfg( not( windows) ) ]
48+ let msg = self . to_string ( ) ;
49+ vm. new_errno_error ( errno, msg)
4250 }
4351}
4452
0 commit comments