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
21 changes: 18 additions & 3 deletions Src/IronPython.Modules/_ctypes/_ctypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ public static int CopyComPointer(object src, object dest) {
throw new NotImplementedException("CopyComPointer");
}

#nullable enable

public static string FormatError() {
return FormatError(get_last_error());
}
Expand All @@ -172,6 +174,15 @@ public static string FormatError(int errorCode) {
return new Win32Exception(errorCode).Message;
}

private static string FormatError(int errorCode, string? fileName) {
string msg = FormatError(errorCode);
// error codes: https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes
if (errorCode is not (< 0 or >= 8200 or 34 or 106 or 317 or 718)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How'd you pick these error codes for formatting?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The link in the comment shows all defined error codes and their messages, grouped by ranges. I searched each range for % to see what variable fields are being used. Luckily, % placeholders are not often used. If a filename is used, it is always on %1 with the four exceptions listed. Above code 8200, filename is not used at all and %1 means something else.

msg = msg.Replace("%1", $"'{fileName}'");
}
return msg;
}

[SupportedOSPlatform("windows"), PythonHidden(PlatformsAttribute.PlatformFamily.Unix)]
public static void FreeLibrary(int handle) {
FreeLibrary(new IntPtr(handle));
Expand All @@ -187,13 +198,17 @@ public static void FreeLibrary(IntPtr handle) {
NativeFunctions.FreeLibrary(handle);
}

#nullable enable

private static object LoadDLL(string? library, int mode) {
if (library is not null && library.IndexOf((char)0) != -1) throw PythonOps.ValueError("embedded null byte");
IntPtr res = NativeFunctions.LoadDLL(library, mode);
if (res == IntPtr.Zero) {
throw PythonOps.OSError($"cannot load library {library}");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
int code = NativeFunctions.GetLastError();
string msg = FormatError(code, library);
throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, 0, msg, null, code);
}

throw PythonOps.OSError("cannot load library '{0}'", library);
}

return res.ToPython();
Expand Down
14 changes: 13 additions & 1 deletion Tests/modules/type_related/test_ctypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
from array import array
import sys
import gc
import unittest

from iptest import IronPythonTestCase, is_cli, big, myint, run_test
from iptest import IronPythonTestCase, is_posix, is_cli, big, myint, run_test

class CTypesTest(IronPythonTestCase):
export_error_msg = "Existing exports of data: object cannot be re-sized" if is_cli else "cannot resize an array that is exporting buffers"
Expand Down Expand Up @@ -219,4 +220,15 @@ class TestU(Structure):
self.assertEqual(TestU(-(1 << 64)).x, 0)
self.assertEqual(TestU(-(1 << 64) - 1).x, 0x7fffffffffffffff)

@unittest.skipIf(is_posix, 'Windows specific test')
def test_loadlibrary_error(self):
with self.assertRaises(OSError) as cm:
windll.LoadLibrary(__file__)

self.assertEqual(cm.exception.errno, 8)
self.assertEqual(cm.exception.winerror, 193)
self.assertIn(" is not a valid Win32 application", cm.exception.strerror)
self.assertIsNone(cm.exception.filename)
self.assertIsNone(cm.exception.filename2)

run_test(__name__)
2 changes: 1 addition & 1 deletion WhatsNewInPython30.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ Changes To Exceptions
- [x] [PEP 3110][]: Catching exceptions. You must now use `except SomeException as variable` instead of `except SomeException, variable`. Moreover, the variable is explicitly deleted when the `except` block is left.
- [x] [PEP 3134][]: Exception chaining. There are two cases: implicit chaining and explicit chaining. Implicit chaining happens when an exception is raised in an `except` or `finally` handler block. This usually happens due to a bug in the handler block; we call this a secondary exception. In this case, the original exception (that was being handled) is saved as the `__context__` attribute of the secondary exception. Explicit chaining is invoked with this syntax: `raise SecondaryException() from primary_exception` (where `primary_exception` is any expression that produces an exception object, probably an exception that was previously caught). In this case, the primary exception is stored on the `__cause__` attribute of the secondary exception. The traceback printed when an unhandled exception occurs walks the chain of `__cause__` and `__context__` attributes and prints a separate traceback for each component of the chain, with the primary exception at the top. (Java users may recognize this behavior.)
- [x] [PEP 3134][]: Exception objects now store their traceback as the `__traceback__` attribute. This means that an exception object now contains all the information pertaining to an exception, and there are fewer reasons to use `sys.exc_info()` (though the latter is not removed).
- [ ] A few exception messages are improved when Windows fails to load an extension module. For example, error code 193 is now `%1 is not a valid Win32 application`. Strings now deal with non-English locales.
- [x] A few exception messages are improved when Windows fails to load an extension module. For example, error code 193 is now `%1 is not a valid Win32 application`. Strings now deal with non-English locales.

Operators And Special Methods
===============
Expand Down