2020using IronPython . Runtime . Operations ;
2121using IronPython . Runtime . Types ;
2222
23+
2324#if ! FEATURE_REMOTING
2425using MarshalByRefObject = System . Object ;
2526#endif
@@ -36,7 +37,7 @@ namespace IronPython.Runtime.Exceptions {
3637 /// Because the oddity of the built-in exception types all sharing the same physical layout
3738 /// (see also PythonExceptions.BaseException) some classes are defined as classes w/ their
3839 /// proper name and some classes are defined as PythonType fields. When a class is defined
39- /// for convenience their 's also an _TypeName version which is the PythonType.
40+ /// for convenience there 's also an _TypeName version which is the PythonType.
4041 /// </summary>
4142 public static partial class PythonExceptions {
4243 private static readonly object _pythonExceptionKey = typeof ( BaseException ) ;
@@ -124,9 +125,9 @@ public partial class _OSError {
124125 if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ) {
125126 if ( args . Length >= 4 && args [ 3 ] is int winerror ) {
126127 errno = WinErrorToErrno ( winerror ) ;
127- }
128+ }
128129 }
129- cls = ErrnoToPythonType ( ErrnoToErrorEnum ( errno ) ) ;
130+ cls = ErrorEnumToPythonType ( ErrnoToErrorEnum ( errno ) ) ;
130131 }
131132 return Activator . CreateInstance ( cls . UnderlyingSystemType , cls ) ;
132133 }
@@ -179,6 +180,13 @@ public override void __init__(params object[] args) {
179180 base . __init__ ( args ) ;
180181 }
181182
183+ // This enum is used solely for the purpose of mapping errno values to Python exception types.
184+ // The values are based on errno codes but do not exactly match them;
185+ // they are selected such that it is possible to algorithmically map them from true platform-dependent errno values.
186+ // The subset of codes is chosen that is sufficient for mapping all relevant Python exceptions.
187+ // Because it is an enum, it can be used in switch statements and expressions, simplifying the code
188+ // over using actual errno values (which are not always compile-time constants) while keeping it readable.
189+ // In a way it is subset-equivalent to Mono.Unix.Native.Errno, but it is not dependent on Mono.Posix assembly.
182190 private enum Error {
183191 UNSPECIFIED = - 1 ,
184192 EPERM = 1 ,
@@ -211,15 +219,17 @@ private enum Error {
211219 WSAECONNREFUSED = 10061 ,
212220 }
213221
222+ // Not all input errno values are mapped to existing constants of Error.
223+ // This is suffcient since all values that are not listed as Error constants are mapped to OSError.
214224 private static Error ErrnoToErrorEnum ( int errno ) {
215225 if ( RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) ) {
216226 if ( errno == 11 ) return Error . UNSPECIFIED ; // EAGAIN on Linux/Windows but EDEADLK on OSX, which is not being remapped
217- if ( errno >= 35 ) errno += 10000 ; // add WSABASEERR to map to Windows error range
227+ if ( errno >= 35 ) errno += WSABASEERR ; // add WSABASEERR to map to Windows error range
218228 }
219229 return ( Error ) errno ;
220230 }
221231
222- private static PythonType ErrnoToPythonType ( Error errno ) {
232+ private static PythonType ErrorEnumToPythonType ( Error errno ) {
223233 var res = errno switch {
224234 Error . EPERM => PermissionError ,
225235 Error . ENOENT => FileNotFoundError ,
@@ -263,6 +273,42 @@ private static PythonType ErrnoToPythonType(Error errno) {
263273 return res ?? OSError ;
264274 }
265275
276+ /// <summary>
277+ /// Provides a subset of platform-independent errno codes to be used in this assembly.
278+ /// </summary>
279+ /// <remarks>
280+ /// Values of the Errno codes defined here are identical with values defined in PythonErrno in IronPython.Modules.dll.
281+ /// </remarks>
282+ internal static class Errno {
283+
284+ #region Generated Common Errno Codes
285+
286+ // *** BEGIN GENERATED CODE ***
287+ // generated by function: generate_common_errno_codes from: generate_os_codes.py
288+
289+ internal const int ENOENT = 2 ;
290+ internal const int E2BIG = 7 ;
291+ internal const int ENOEXEC = 8 ;
292+ internal const int EBADF = 9 ;
293+ internal const int ECHILD = 10 ;
294+ internal static int EAGAIN => RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ? 11 : RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) ? 35 : 11 ;
295+ internal const int ENOMEM = 12 ;
296+ internal const int EACCES = 13 ;
297+ internal const int EEXIST = 17 ;
298+ internal const int EXDEV = 18 ;
299+ internal const int ENOTDIR = 20 ;
300+ internal const int EMFILE = 24 ;
301+ internal const int ENOSPC = 28 ;
302+ internal const int EPIPE = 32 ;
303+ internal static int ENOTEMPTY => RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ? 41 : RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) ? 66 : 39 ;
304+ internal static int EILSEQ => RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ? 42 : RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) ? 92 : 84 ;
305+ internal const int EINVAL = 22 ;
306+
307+ // *** END GENERATED CODE ***
308+
309+ #endregion
310+ }
311+
266312 /*
267313 * errors were generated using this script run against CPython:
268314f = open(r'C:\Program Files\Microsoft SDKs\Windows\v6.0A\Include\WinError.h', 'r')
@@ -373,6 +419,7 @@ private static PythonType ErrnoToPythonType(Error errno) {
373419 internal const int ERROR_NESTING_NOT_ALLOWED = 215 ;
374420 internal const int ERROR_NO_DATA = 232 ;
375421 internal const int ERROR_DIRECTORY = 267 ;
422+ internal const int ERROR_NO_UNICODE_TRANSLATION = 1113 ;
376423 internal const int ERROR_NOT_ENOUGH_QUOTA = 1816 ;
377424
378425 // These map to POSIX errno 22 and are added by hand as needed.
@@ -381,14 +428,25 @@ private static PythonType ErrnoToPythonType(Error errno) {
381428 internal const int ERROR_FILE_INVALID = 1006 ;
382429 internal const int ERROR_MAPPED_ALIGNMENT = 1132 ;
383430
431+ // Some Winsock error codes are errno values.
432+ internal const int WSABASEERR = 10000 ;
433+ internal const int WSAEINTR = WSABASEERR + 4 ;
434+ internal const int WSAEBADF = WSABASEERR + 9 ;
435+ internal const int WSAEACCES = WSABASEERR + 13 ;
436+ internal const int WSAEFAULT = WSABASEERR + 14 ;
437+ internal const int WSAEINVAL = WSABASEERR + 22 ;
438+ internal const int WSAEMFILE = WSABASEERR + 24 ;
439+
440+ // See also errmap.h in CPython
384441 internal static int WinErrorToErrno ( int winerror ) {
442+ // Unwrap FACILITY_WIN32 HRESULT errors
443+ if ( ( winerror & 0xFFFF0000 ) == 0x80070000 ) {
444+ winerror &= 0x0000FFFF ;
445+ }
446+
385447 int errno = winerror ;
386- if ( winerror < 10000 ) {
448+ if ( winerror < WSABASEERR ) {
387449 switch ( winerror ) {
388- case ERROR_BROKEN_PIPE :
389- case ERROR_NO_DATA :
390- errno = 32 ;
391- break ;
392450 case ERROR_FILE_NOT_FOUND :
393451 case ERROR_PATH_NOT_FOUND :
394452 case ERROR_INVALID_DRIVE :
@@ -397,10 +455,10 @@ internal static int WinErrorToErrno(int winerror) {
397455 case ERROR_BAD_NET_NAME :
398456 case ERROR_BAD_PATHNAME :
399457 case ERROR_FILENAME_EXCED_RANGE :
400- errno = 2 ;
458+ errno = Errno . ENOENT ;
401459 break ;
402460 case ERROR_BAD_ENVIRONMENT :
403- errno = 7 ;
461+ errno = Errno . E2BIG ;
404462 break ;
405463 case ERROR_BAD_FORMAT :
406464 case ERROR_INVALID_STARTING_CODESEG :
@@ -418,27 +476,27 @@ internal static int WinErrorToErrno(int winerror) {
418476 case ERROR_RING2SEG_MUST_BE_MOVABLE :
419477 case ERROR_RELOC_CHAIN_XEEDS_SEGLIM :
420478 case ERROR_INFLOOP_IN_RELOC_CHAIN :
421- errno = 8 ;
479+ errno = Errno . ENOEXEC ;
422480 break ;
423481 case ERROR_INVALID_HANDLE :
424482 case ERROR_INVALID_TARGET_HANDLE :
425483 case ERROR_DIRECT_ACCESS_HANDLE :
426- errno = 9 ;
484+ errno = Errno . EBADF ;
427485 break ;
428486 case ERROR_WAIT_NO_CHILDREN :
429487 case ERROR_CHILD_NOT_COMPLETE :
430- errno = 10 ;
488+ errno = Errno . ECHILD ;
431489 break ;
432490 case ERROR_NO_PROC_SLOTS :
433491 case ERROR_MAX_THRDS_REACHED :
434492 case ERROR_NESTING_NOT_ALLOWED :
435- errno = 11 ;
493+ errno = Errno . EAGAIN ;
436494 break ;
437495 case ERROR_ARENA_TRASHED :
438496 case ERROR_NOT_ENOUGH_MEMORY :
439497 case ERROR_INVALID_BLOCK :
440498 case ERROR_NOT_ENOUGH_QUOTA :
441- errno = 12 ;
499+ errno = Errno . ENOMEM ;
442500 break ;
443501 case ERROR_ACCESS_DENIED :
444502 case ERROR_CURRENT_DIRECTORY :
@@ -466,29 +524,51 @@ internal static int WinErrorToErrno(int winerror) {
466524 case ERROR_SEEK_ON_DEVICE :
467525 case ERROR_NOT_LOCKED :
468526 case ERROR_LOCK_FAILED :
469- errno = 13 ;
527+ case 35 : // undefined
528+ errno = Errno . EACCES ;
470529 break ;
471530 case ERROR_FILE_EXISTS :
472531 case ERROR_ALREADY_EXISTS :
473- errno = 17 ;
532+ errno = Errno . EEXIST ;
474533 break ;
475534 case ERROR_NOT_SAME_DEVICE :
476- errno = 18 ;
535+ errno = Errno . EXDEV ;
477536 break ;
478537 case ERROR_DIRECTORY :
479- errno = 20 ;
480- break ;
481- case ERROR_DIR_NOT_EMPTY :
482- errno = 41 ;
538+ errno = Errno . ENOTDIR ;
483539 break ;
484540 case ERROR_TOO_MANY_OPEN_FILES :
485- errno = 24 ;
541+ errno = Errno . EMFILE ;
486542 break ;
487543 case ERROR_DISK_FULL :
488- errno = 28 ;
544+ errno = Errno . ENOSPC ;
545+ break ;
546+ case ERROR_BROKEN_PIPE :
547+ case ERROR_NO_DATA :
548+ errno = Errno . EPIPE ;
549+ break ;
550+ case ERROR_DIR_NOT_EMPTY : // ENOTEMPTY
551+ errno = Errno . ENOTEMPTY ;
552+ break ;
553+ case ERROR_NO_UNICODE_TRANSLATION : // EILSEQ
554+ errno = Errno . EILSEQ ;
555+ break ;
556+ default :
557+ errno = Errno . EINVAL ;
558+ break ;
559+ }
560+ } else if ( winerror < 12000 ) { // Winsock error codes are 10000-11999
561+ switch ( winerror ) {
562+ case WSAEINTR :
563+ case WSAEBADF :
564+ case WSAEACCES :
565+ case WSAEFAULT :
566+ case WSAEINVAL :
567+ case WSAEMFILE :
568+ errno = winerror - WSABASEERR ;
489569 break ;
490570 default :
491- errno = 22 ;
571+ errno = winerror ;
492572 break ;
493573 }
494574 }
0 commit comments