Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Src/IronPython/Lib/iptest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# The .NET Foundation licenses this file to you under the Apache 2.0 License.
# See the LICENSE file in the project root for more information.

from .ipunittest import IronPythonTestCase, stdout_trapper, stderr_trapper, path_modifier, retryOnFailure, run_test, skipUnlessIronPython, source_root
from .ipunittest import IronPythonTestCase, stdout_trapper, stderr_trapper, path_modifier, retryOnFailure, run_test, skipUnlessIronPython, source_root, expectedFailureIf
from .test_env import *
from .type_util import *
from .misc_util import ip_supported_encodings
9 changes: 9 additions & 0 deletions Src/IronPython/Lib/iptest/ipunittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ def skipUnlessIronPython():
"""Skips the test unless currently running on IronPython"""
return unittest.skipUnless(is_cli, 'IronPython specific test')

def expectedFailureIf(condition):
"""The test is marked as an expectedFailure if the condition is satisfied."""
def wrapper(func):
if condition:
return unittest.expectedFailure(func)
else:
return func
return wrapper

MAX_FAILURE_RETRY = 3
def retryOnFailure(f, times=MAX_FAILURE_RETRY, *args, **kwargs):
'''
Expand Down
34 changes: 21 additions & 13 deletions Src/IronPython/Runtime/Exceptions/PythonExceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public partial class _OSError {
errno = WinErrorToErrno(winerror);
}
}
cls = ErrnoToPythonType((Error)errno);
cls = ErrnoToPythonType(ErrnoToErrorEnum(errno));
}
return Activator.CreateInstance(cls.UnderlyingSystemType, cls);
}
Expand Down Expand Up @@ -183,25 +183,26 @@ public override void __init__(params object[] args) {
}

private enum Error {
UNSPECIFIED = -1,
EPERM = 1,
ENOENT = 2,
ESRCH = 3,
EINTR = 4,
ECHILD = 10,
EAGAIN = 11,
EAGAIN = 11, // 35 on OSX
EACCES = 13,
EEXIST = 17,
ENOTDIR = 20,
EISDIR = 21,
EPIPE = 32,
// Linux
ECONNABORTED = 103,
ECONNRESET = 104,
ESHUTDOWN = 108,
ETIMEDOUT = 110,
ECONNREFUSED = 111,
EALREADY = 114,
EINPROGRESS = 115,
ECONNABORTED = 103, // 53 on OSX
ECONNRESET = 104, // 54 on OSX
ESHUTDOWN = 108, // 58 on OSX
ETIMEDOUT = 110, // 60 on OSX
ECONNREFUSED = 111, // 61 on OSX
EALREADY = 114, // 37 on OSX
EINPROGRESS = 115, // 36 on OSX
// Windows
WSAEWOULDBLOCK = 10035,
WSAEINPROGRESS = 10036,
Expand All @@ -213,6 +214,14 @@ private enum Error {
WSAECONNREFUSED = 10061,
}

private static Error ErrnoToErrorEnum(int errno) {
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
if (errno == 11) return Error.UNSPECIFIED; // EAGAIN on Linux/Windows but EDEADLK on OSX, which is not being remapped
if (errno >= 35) errno += 10000; // add WSABASEERR to map to Windows error range
}
return (Error)errno;
}

private static PythonType ErrnoToPythonType(Error errno) {
var res = errno switch
{
Expand All @@ -229,10 +238,10 @@ private static PythonType ErrnoToPythonType(Error errno) {
Error.EPIPE => BrokenPipeError,
_ => null
};
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
res ??= errno switch
{
// Windows
// Windows or remapped OSX
Error.WSAEWOULDBLOCK => BlockingIOError,
Error.WSAEINPROGRESS => BlockingIOError,
Error.WSAEALREADY => BlockingIOError,
Expand All @@ -243,8 +252,7 @@ private static PythonType ErrnoToPythonType(Error errno) {
Error.WSAECONNREFUSED => ConnectionRefusedError,
_ => null
};
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
// TODO: verify that these are the same on OSX
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
res ??= errno switch
{
// Linux
Expand Down
37 changes: 35 additions & 2 deletions Tests/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import sys
import unittest

from iptest import IronPythonTestCase, is_cli, is_netcoreapp, run_test, skipUnlessIronPython
from iptest import IronPythonTestCase, is_cli, is_netcoreapp, run_test, skipUnlessIronPython, expectedFailureIf

class CP35300_Derived(EnvironmentError):
def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -828,7 +828,7 @@ def __new__(cls, *args): return 42
with self.assertRaises(TypeError): # TypeError: exceptions must derive from BaseException
raise MyException('abc')

@unittest.expectedFailure # https://github.com/IronLanguages/ironpython3/issues/876
@expectedFailureIf(is_cli) # https://github.com/IronLanguages/ironpython3/issues/876
def test_oserror_init(self):
x = OSError()
self.assertEqual(x.errno, None)
Expand Down Expand Up @@ -1025,6 +1025,39 @@ def test_windows_error(self):
# winerror code is mapped to Python error code
self.assertEqual(OSError('foo', 'bar', 'baz', 10).errno, 7)

def test_os_error_mapping(self):
import errno
import itertools

error_map = {
errno.EPERM : PermissionError,
errno.ENOENT : FileNotFoundError,
errno.ESRCH : ProcessLookupError,
errno.EINTR : InterruptedError,
errno.ECHILD : ChildProcessError,
errno.EAGAIN : BlockingIOError,
errno.EACCES : PermissionError,
errno.EEXIST : FileExistsError,
errno.ENOTDIR : NotADirectoryError,
errno.EISDIR : IsADirectoryError,
errno.EPIPE : BrokenPipeError,
errno.EWOULDBLOCK : BlockingIOError,
errno.ECONNABORTED : ConnectionAbortedError,
errno.ECONNRESET : ConnectionResetError,
errno.ESHUTDOWN : BrokenPipeError,
errno.ETIMEDOUT : TimeoutError,
errno.ECONNREFUSED : ConnectionRefusedError,
errno.EALREADY : BlockingIOError,
errno.EINPROGRESS : BlockingIOError,
}

for errno, exc in error_map.items():
self.assertIsInstance(OSError(errno, ''), exc)

for errno in itertools.chain(range(200), range(10000, 10200)):
if errno not in error_map:
self.assertIsInstance(OSError(errno, ''), OSError)

def test_derived_keyword_args(self):
class ED(Exception):
def __init__(self, args=''):
Expand Down