Skip to content

Commit 9811437

Browse files
authored
Merge branch 'master' into fix-exception-string
2 parents 8cb47c7 + 32fdc9c commit 9811437

File tree

9 files changed

+159
-16
lines changed

9 files changed

+159
-16
lines changed

AUTHORS.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@
6666
- Ville M. Vainio ([@vivainio](https://github.com/vivainio))
6767
- Virgil Dupras ([@hsoft](https://github.com/hsoft))
6868
- Wenguang Yang ([@yagweb](https://github.com/yagweb))
69-
- William Sardar ([@williamsardar])(https://github.com/williamsardar)
69+
- William Sardar ([@williamsardar](https://github.com/williamsardar))
7070
- Xavier Dupré ([@sdpython](https://github.com/sdpython))
7171
- Zane Purvis ([@zanedp](https://github.com/zanedp))
72-
- ([@amos402]https://github.com/amos402)
72+
- ([@amos402](https://github.com/amos402))
7373
- ([@bltribble](https://github.com/bltribble))
7474
- ([@civilx64](https://github.com/civilx64))
7575
- ([@GSPP](https://github.com/GSPP))
@@ -82,3 +82,4 @@
8282
- ([@testrunner123](https://github.com/testrunner123))
8383
- ([@DanBarzilian](https://github.com/DanBarzilian))
8484
- ([@alxnull](https://github.com/alxnull))
85+
- ([@gpetrou](https://github.com/gpetrou))

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1111

1212
- Ability to instantiate new .NET arrays using `Array[T](dim1, dim2, ...)` syntax
1313
- Python operator method will call C# operator method for supported binary and unary operators ([#1324][p1324]).
14+
- Add GetPythonThreadID and Interrupt methods in PythonEngine
1415

1516
### Changed
1617
- Drop support for Python 2, 3.4, and 3.5

src/embed_tests/TestInterrupt.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
2+
using System;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
6+
using NUnit.Framework;
7+
8+
using Python.Runtime;
9+
10+
namespace Python.EmbeddingTest
11+
{
12+
public class TestInterrupt
13+
{
14+
private IntPtr _threadState;
15+
16+
[OneTimeSetUp]
17+
public void SetUp()
18+
{
19+
PythonEngine.Initialize();
20+
_threadState = PythonEngine.BeginAllowThreads();
21+
}
22+
23+
[OneTimeTearDown]
24+
public void Dispose()
25+
{
26+
PythonEngine.EndAllowThreads(_threadState);
27+
PythonEngine.Shutdown();
28+
}
29+
30+
[Test]
31+
public void InterruptTest()
32+
{
33+
int runSimpleStringReturnValue = int.MinValue;
34+
ulong pythonThreadID = ulong.MinValue;
35+
Task.Factory.StartNew(() =>
36+
{
37+
using (Py.GIL())
38+
{
39+
pythonThreadID = PythonEngine.GetPythonThreadID();
40+
runSimpleStringReturnValue = PythonEngine.RunSimpleString(@"
41+
import time
42+
43+
while True:
44+
time.sleep(0.2)");
45+
}
46+
});
47+
48+
Thread.Sleep(200);
49+
50+
Assert.AreNotEqual(ulong.MinValue, pythonThreadID);
51+
52+
using (Py.GIL())
53+
{
54+
int interruptReturnValue = PythonEngine.Interrupt(pythonThreadID);
55+
Assert.AreEqual(1, interruptReturnValue);
56+
}
57+
58+
Thread.Sleep(300);
59+
60+
Assert.AreEqual(-1, runSimpleStringReturnValue);
61+
}
62+
}
63+
}

src/runtime/arrayobject.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ static NewReference NewInstance(Type elementType, BorrowedReference arrayPyType,
159159

160160
if (rank == 1)
161161
{
162+
if (!Runtime.PyInt_Check(idx))
163+
{
164+
return RaiseIndexMustBeIntegerError(idx);
165+
}
162166
index = Runtime.PyInt_AsLong(idx);
163167

164168
if (Exceptions.ErrorOccurred())
@@ -199,6 +203,10 @@ static NewReference NewInstance(Type elementType, BorrowedReference arrayPyType,
199203
for (var i = 0; i < count; i++)
200204
{
201205
IntPtr op = Runtime.PyTuple_GetItem(idx, i);
206+
if (!Runtime.PyInt_Check(op))
207+
{
208+
return RaiseIndexMustBeIntegerError(op);
209+
}
202210
index = Runtime.PyInt_AsLong(op);
203211

204212
if (Exceptions.ErrorOccurred())
@@ -253,6 +261,11 @@ static NewReference NewInstance(Type elementType, BorrowedReference arrayPyType,
253261

254262
if (rank == 1)
255263
{
264+
if (!Runtime.PyInt_Check(idx))
265+
{
266+
RaiseIndexMustBeIntegerError(idx);
267+
return -1;
268+
}
256269
index = Runtime.PyInt_AsLong(idx);
257270

258271
if (Exceptions.ErrorOccurred())
@@ -291,6 +304,11 @@ static NewReference NewInstance(Type elementType, BorrowedReference arrayPyType,
291304
for (var i = 0; i < count; i++)
292305
{
293306
IntPtr op = Runtime.PyTuple_GetItem(idx, i);
307+
if (!Runtime.PyInt_Check(op))
308+
{
309+
RaiseIndexMustBeIntegerError(op);
310+
return -1;
311+
}
294312
index = Runtime.PyInt_AsLong(op);
295313

296314
if (Exceptions.ErrorOccurred())
@@ -320,6 +338,11 @@ static NewReference NewInstance(Type elementType, BorrowedReference arrayPyType,
320338
return 0;
321339
}
322340

341+
private static IntPtr RaiseIndexMustBeIntegerError(IntPtr idx)
342+
{
343+
string tpName = Runtime.PyObject_GetTypeName(idx);
344+
return Exceptions.RaiseTypeError($"array index has type {tpName}, expected an integer");
345+
}
323346

324347
/// <summary>
325348
/// Implements __contains__ for array types.

src/runtime/converter.cs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -698,19 +698,10 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo
698698

699699
case TypeCode.UInt64:
700700
{
701-
op = value;
702-
if (Runtime.PyObject_TYPE(value) != Runtime.PyLongType)
703-
{
704-
op = Runtime.PyNumber_Long(value);
705-
if (op == IntPtr.Zero)
706-
{
707-
goto convert_error;
708-
}
709-
}
710-
ulong num = Runtime.PyLong_AsUnsignedLongLong(op);
701+
ulong num = Runtime.PyLong_AsUnsignedLongLong(value);
711702
if (num == ulong.MaxValue && Exceptions.ErrorOccurred())
712703
{
713-
goto overflow;
704+
goto convert_error;
714705
}
715706
result = num;
716707
return true;

src/runtime/pythonengine.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,30 @@ public static void Exec(string code, IntPtr? globals = null, IntPtr? locals = nu
567567
}
568568
}
569569

570+
/// <summary>
571+
/// Gets the Python thread ID.
572+
/// </summary>
573+
/// <returns>The Python thread ID.</returns>
574+
public static ulong GetPythonThreadID()
575+
{
576+
dynamic threading = Py.Import("threading");
577+
return threading.InvokeMethod("get_ident");
578+
}
579+
580+
/// <summary>
581+
/// Interrupts the execution of a thread.
582+
/// </summary>
583+
/// <param name="pythonThreadID">The Python thread ID.</param>
584+
/// <returns>The number of thread states modified; this is normally one, but will be zero if the thread id isn’t found.</returns>
585+
public static int Interrupt(ulong pythonThreadID)
586+
{
587+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
588+
{
589+
return Runtime.PyThreadState_SetAsyncExcLLP64((uint)pythonThreadID, Exceptions.KeyboardInterrupt);
590+
}
591+
592+
return Runtime.PyThreadState_SetAsyncExcLP64(pythonThreadID, Exceptions.KeyboardInterrupt);
593+
}
570594

571595
/// <summary>
572596
/// RunString Method. Function has been deprecated and will be removed.

src/runtime/runtime.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2143,6 +2143,12 @@ internal static void Py_CLEAR(ref IntPtr ob)
21432143
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
21442144
internal static extern int Py_AddPendingCall(IntPtr func, IntPtr arg);
21452145

2146+
[DllImport(_PythonDll, EntryPoint = "PyThreadState_SetAsyncExc", CallingConvention = CallingConvention.Cdecl)]
2147+
internal static extern int PyThreadState_SetAsyncExcLLP64(uint id, IntPtr exc);
2148+
2149+
[DllImport(_PythonDll, EntryPoint = "PyThreadState_SetAsyncExc", CallingConvention = CallingConvention.Cdecl)]
2150+
internal static extern int PyThreadState_SetAsyncExcLP64(ulong id, IntPtr exc);
2151+
21462152
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
21472153
internal static extern int Py_MakePendingCalls();
21482154

src/tests/test_array.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -836,8 +836,15 @@ def test_typed_array():
836836

837837
with pytest.raises(TypeError):
838838
ob = Test.TypedArrayTest()
839-
ob.items["wrong"] = "wrong"
839+
ob.items["wrong"] = Spam("0")
840+
841+
with pytest.raises(TypeError):
842+
ob = Test.TypedArrayTest()
843+
_ = ob.items[0.5]
840844

845+
with pytest.raises(TypeError):
846+
ob = Test.TypedArrayTest()
847+
ob.items[0.5] = Spam("0")
841848

842849
def test_multi_dimensional_array():
843850
"""Test multi-dimensional arrays."""
@@ -901,8 +908,23 @@ def test_multi_dimensional_array():
901908

902909
with pytest.raises(TypeError):
903910
ob = Test.MultiDimensionalArrayTest()
904-
ob[0, 0] = "wrong"
911+
ob.items[0, 0] = "wrong"
905912

913+
with pytest.raises(TypeError):
914+
ob = Test.MultiDimensionalArrayTest()
915+
ob["0", 0] = 0
916+
917+
with pytest.raises(TypeError):
918+
ob = Test.MultiDimensionalArrayTest()
919+
ob.items["0", 0] = 0
920+
921+
with pytest.raises(TypeError):
922+
ob = Test.MultiDimensionalArrayTest()
923+
_ = ob.items[0.5, 0]
924+
925+
with pytest.raises(TypeError):
926+
ob = Test.MultiDimensionalArrayTest()
927+
ob.items[0.5, 0] = 0
906928

907929
def test_array_iteration():
908930
"""Test array iteration."""
@@ -1189,6 +1211,15 @@ def test_create_array_from_shape():
11891211
with pytest.raises(ValueError):
11901212
Array[int](-1)
11911213

1214+
with pytest.raises(TypeError):
1215+
Array[int]('1')
1216+
1217+
with pytest.raises(ValueError):
1218+
Array[int](-1, -1)
1219+
1220+
with pytest.raises(TypeError):
1221+
Array[int]('1', '1')
1222+
11921223
def test_special_array_creation():
11931224
"""Test using the Array[<type>] syntax for creating arrays."""
11941225
from Python.Test import ISayHello1, InterfaceTest, ShortEnum

src/tests/test_conversion.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,10 @@ def test_uint64_conversion():
382382
ob.UInt64Field = System.UInt64(0)
383383
assert ob.UInt64Field == 0
384384

385-
with pytest.raises(ValueError):
385+
with pytest.raises(TypeError):
386+
ConversionTest().UInt64Field = 0.5
387+
388+
with pytest.raises(TypeError):
386389
ConversionTest().UInt64Field = "spam"
387390

388391
with pytest.raises(TypeError):

0 commit comments

Comments
 (0)