Skip to content

Commit d877337

Browse files
committed
merge changes from master
2 parents 197689e + 832126c commit d877337

File tree

13 files changed

+164
-95
lines changed

13 files changed

+164
-95
lines changed

src/embed_tests/Codecs.cs

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ static void TupleConversionsGeneric<T, TTuple>()
3131
TupleCodec<TTuple>.Register();
3232
var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
3333
T restored = default;
34-
using (Py.GIL())
3534
using (var scope = Py.CreateScope())
3635
{
3736
void Accept(T value) => restored = value;
@@ -53,7 +52,6 @@ static void TupleConversionsObject<T, TTuple>()
5352
TupleCodec<TTuple>.Register();
5453
var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
5554
T restored = default;
56-
using (Py.GIL())
5755
using (var scope = Py.CreateScope())
5856
{
5957
void Accept(object value) => restored = (T)value;
@@ -73,12 +71,9 @@ public void TupleRoundtripObject()
7371
static void TupleRoundtripObject<T, TTuple>()
7472
{
7573
var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
76-
using (Py.GIL())
77-
{
78-
var pyTuple = TupleCodec<TTuple>.Instance.TryEncode(tuple);
79-
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple, out object restored));
80-
Assert.AreEqual(expected: tuple, actual: restored);
81-
}
74+
var pyTuple = TupleCodec<TTuple>.Instance.TryEncode(tuple);
75+
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple, out object restored));
76+
Assert.AreEqual(expected: tuple, actual: restored);
8277
}
8378

8479
[Test]
@@ -90,21 +85,12 @@ public void TupleRoundtripGeneric()
9085
static void TupleRoundtripGeneric<T, TTuple>()
9186
{
9287
var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
93-
using (Py.GIL())
94-
{
95-
var pyTuple = TupleCodec<TTuple>.Instance.TryEncode(tuple);
96-
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple, out T restored));
97-
Assert.AreEqual(expected: tuple, actual: restored);
98-
}
88+
var pyTuple = TupleCodec<TTuple>.Instance.TryEncode(tuple);
89+
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple, out T restored));
90+
Assert.AreEqual(expected: tuple, actual: restored);
9991
}
10092

101-
static PyObject GetPythonIterable()
102-
{
103-
using (Py.GIL())
104-
{
105-
return PythonEngine.Eval("map(lambda x: x, [1,2,3])");
106-
}
107-
}
93+
static PyObject GetPythonIterable() => PythonEngine.Eval("map(lambda x: x, [1,2,3])");
10894

10995
[Test]
11096
public void ListDecoderTest()
@@ -330,7 +316,6 @@ public void ExceptionEncoded()
330316
PyObjectConversions.RegisterEncoder(new ValueErrorCodec());
331317
void CallMe() => throw new ValueErrorWrapper(TestExceptionMessage);
332318
var callMeAction = new Action(CallMe);
333-
using var _ = Py.GIL();
334319
using var scope = Py.CreateScope();
335320
scope.Exec(@"
336321
def call(func):
@@ -348,7 +333,6 @@ def call(func):
348333
public void ExceptionDecoded()
349334
{
350335
PyObjectConversions.RegisterDecoder(new ValueErrorCodec());
351-
using var _ = Py.GIL();
352336
using var scope = Py.CreateScope();
353337
var error = Assert.Throws<ValueErrorWrapper>(()
354338
=> PythonEngine.Exec($"raise ValueError('{TestExceptionMessage}')"));
@@ -371,6 +355,16 @@ from datetime import datetime
371355
scope.Exec("Codecs.AcceptsDateTime(datetime(2021, 1, 22))");
372356
}
373357

358+
[Test]
359+
public void ExceptionDecodedNoInstance()
360+
{
361+
PyObjectConversions.RegisterDecoder(new InstancelessExceptionDecoder());
362+
using var scope = Py.CreateScope();
363+
var error = Assert.Throws<ValueErrorWrapper>(() => PythonEngine.Exec(
364+
$"[].__iter__().__next__()"));
365+
Assert.AreEqual(TestExceptionMessage, error.Message);
366+
}
367+
374368
public static void AcceptsDateTime(DateTime v) {}
375369

376370
[Test]
@@ -406,7 +400,8 @@ public ValueErrorWrapper(string message) : base(message) { }
406400
class ValueErrorCodec : IPyObjectEncoder, IPyObjectDecoder
407401
{
408402
public bool CanDecode(PyObject objectType, Type targetType)
409-
=> this.CanEncode(targetType) && objectType.Equals(PythonEngine.Eval("ValueError"));
403+
=> this.CanEncode(targetType)
404+
&& PythonReferenceComparer.Instance.Equals(objectType, PythonEngine.Eval("ValueError"));
410405

411406
public bool CanEncode(Type type) => type == typeof(ValueErrorWrapper)
412407
|| typeof(ValueErrorWrapper).IsSubclassOf(type);
@@ -424,6 +419,26 @@ public PyObject TryEncode(object value)
424419
return PythonEngine.Eval("ValueError").Invoke(error.Message.ToPython());
425420
}
426421
}
422+
423+
class InstancelessExceptionDecoder : IPyObjectDecoder
424+
{
425+
readonly PyObject PyErr = Py.Import("clr.interop").GetAttr("PyErr");
426+
427+
public bool CanDecode(PyObject objectType, Type targetType)
428+
=> PythonReferenceComparer.Instance.Equals(PyErr, objectType);
429+
430+
public bool TryDecode<T>(PyObject pyObj, out T value)
431+
{
432+
if (pyObj.HasAttr("value"))
433+
{
434+
value = default;
435+
return false;
436+
}
437+
438+
value = (T)(object)new ValueErrorWrapper(TestExceptionMessage);
439+
return true;
440+
}
441+
}
427442
}
428443

429444
/// <summary>

src/embed_tests/References.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,16 @@ namespace Python.EmbeddingTest
55

66
public class References
77
{
8-
private Py.GILState _gs;
9-
10-
[SetUp]
8+
[OneTimeSetUp]
119
public void SetUp()
1210
{
13-
_gs = Py.GIL();
11+
PythonEngine.Initialize();
1412
}
1513

16-
[TearDown]
14+
[OneTimeTearDown]
1715
public void Dispose()
1816
{
19-
_gs.Dispose();
17+
PythonEngine.Shutdown();
2018
}
2119

2220
[Test]

src/embed_tests/TestNativeTypeOffset.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,16 @@ namespace Python.EmbeddingTest
1313
{
1414
public class TestNativeTypeOffset
1515
{
16-
private Py.GILState _gs;
17-
18-
[SetUp]
16+
[OneTimeSetUp]
1917
public void SetUp()
2018
{
21-
_gs = Py.GIL();
19+
PythonEngine.Initialize();
2220
}
2321

24-
[TearDown]
22+
[OneTimeTearDown]
2523
public void Dispose()
2624
{
27-
_gs.Dispose();
25+
PythonEngine.Shutdown();
2826
}
2927

3028
/// <summary>

src/embed_tests/TestPythonException.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,15 @@ namespace Python.EmbeddingTest
66
{
77
public class TestPythonException
88
{
9-
private IntPtr _gs;
10-
11-
[SetUp]
9+
[OneTimeSetUp]
1210
public void SetUp()
1311
{
1412
PythonEngine.Initialize();
15-
_gs = PythonEngine.AcquireLock();
1613
}
1714

18-
[TearDown]
15+
[OneTimeTearDown]
1916
public void Dispose()
2017
{
21-
PythonEngine.ReleaseLock(_gs);
2218
PythonEngine.Shutdown();
2319
}
2420

src/embed_tests/dynamic.cs

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,16 @@ namespace Python.EmbeddingTest
77
{
88
public class DynamicTest
99
{
10-
private Py.GILState _gs;
11-
12-
[SetUp]
10+
[OneTimeSetUp]
1311
public void SetUp()
1412
{
15-
try {
16-
_gs = Py.GIL();
17-
} catch (Exception e) {
18-
Console.WriteLine($"exception in SetUp: {e}");
19-
throw;
20-
}
13+
PythonEngine.Initialize();
2114
}
2215

23-
[TearDown]
16+
[OneTimeTearDown]
2417
public void Dispose()
2518
{
26-
try {
27-
_gs.Dispose();
28-
} catch(Exception e) {
29-
Console.WriteLine($"exception in TearDown: {e}");
30-
throw;
31-
}
19+
PythonEngine.Shutdown();
3220
}
3321

3422
/// <summary>

src/embed_tests/pyimport.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,10 @@ namespace Python.EmbeddingTest
1919
/// </remarks>
2020
public class PyImportTest
2121
{
22-
private IntPtr _gs;
23-
24-
[SetUp]
22+
[OneTimeSetUp]
2523
public void SetUp()
2624
{
2725
PythonEngine.Initialize();
28-
_gs = PythonEngine.AcquireLock();
2926

3027
/* Append the tests directory to sys.path
3128
* using reflection to circumvent the private
@@ -41,10 +38,9 @@ public void SetUp()
4138
Runtime.Runtime.XDecref(str);
4239
}
4340

44-
[TearDown]
41+
[OneTimeTearDown]
4542
public void Dispose()
4643
{
47-
PythonEngine.ReleaseLock(_gs);
4844
PythonEngine.Shutdown();
4945
}
5046

src/embed_tests/pyrunstring.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,16 @@ namespace Python.EmbeddingTest
66
{
77
public class RunStringTest
88
{
9-
private Py.GILState _gs;
10-
11-
[SetUp]
9+
[OneTimeSetUp]
1210
public void SetUp()
1311
{
14-
_gs = Py.GIL();
12+
PythonEngine.Initialize();
1513
}
1614

17-
[TearDown]
15+
[OneTimeTearDown]
1816
public void Dispose()
1917
{
20-
_gs.Dispose();
18+
PythonEngine.Shutdown();
2119
}
2220

2321
[Test]

src/runtime/Python.Runtime.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
<EmbeddedResource Include="resources\clr.py">
4343
<LogicalName>clr.py</LogicalName>
4444
</EmbeddedResource>
45+
<EmbeddedResource Include="resources\interop.py">
46+
<LogicalName>interop.py</LogicalName>
47+
</EmbeddedResource>
4548
<EmbeddedResource Include="Mixins\*.py" />
4649
</ItemGroup>
4750

src/runtime/Util.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
#nullable enable
12
using System;
3+
using System.IO;
24
using System.Runtime.InteropServices;
35

46
namespace Python.Runtime
@@ -48,13 +50,23 @@ internal static IntPtr Coalesce(this IntPtr primary, IntPtr fallback)
4850
/// <summary>
4951
/// Gets substring after last occurrence of <paramref name="symbol"/>
5052
/// </summary>
51-
internal static string AfterLast(this string str, char symbol)
53+
internal static string? AfterLast(this string str, char symbol)
5254
{
5355
if (str is null)
5456
throw new ArgumentNullException(nameof(str));
5557

5658
int last = str.LastIndexOf(symbol);
5759
return last >= 0 ? str.Substring(last + 1) : null;
5860
}
61+
62+
internal static string ReadStringResource(this System.Reflection.Assembly assembly, string resourceName)
63+
{
64+
if (assembly is null) throw new ArgumentNullException(nameof(assembly));
65+
if (string.IsNullOrEmpty(resourceName)) throw new ArgumentNullException(nameof(resourceName));
66+
67+
using var stream = assembly.GetManifestResourceStream(resourceName);
68+
using var reader = new StreamReader(stream);
69+
return reader.ReadToEnd();
70+
}
5971
}
6072
}

src/runtime/pythonengine.cs

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -228,13 +228,11 @@ public static void Initialize(IEnumerable<string> args, bool setSysArgv = true,
228228
BorrowedReference module_globals = Runtime.PyModule_GetDict(module);
229229

230230
Assembly assembly = Assembly.GetExecutingAssembly();
231-
using (Stream stream = assembly.GetManifestResourceStream("clr.py"))
232-
using (var reader = new StreamReader(stream))
233-
{
234-
// add the contents of clr.py to the module
235-
string clr_py = reader.ReadToEnd();
236-
Exec(clr_py, module_globals, locals.Reference);
237-
}
231+
// add the contents of clr.py to the module
232+
string clr_py = assembly.ReadStringResource("clr.py");
233+
Exec(clr_py, module_globals, locals.Reference);
234+
235+
LoadSubmodule(module_globals, "clr.interop", "interop.py");
238236

239237
LoadMixins(module_globals);
240238

@@ -268,33 +266,31 @@ static BorrowedReference DefineModule(string name)
268266
return module;
269267
}
270268

271-
static void LoadMixins(BorrowedReference targetModuleDict)
272-
{
273-
foreach (string nested in new[] {"collections"})
274-
{
275-
LoadSubmodule(targetModuleDict,
276-
fullName: "clr._extras." + nested,
277-
resourceName: typeof(PythonEngine).Namespace + ".Mixins." + nested + ".py");
278-
}
279-
}
280-
281269
static void LoadSubmodule(BorrowedReference targetModuleDict, string fullName, string resourceName)
282270
{
283271
string memberName = fullName.AfterLast('.');
284272
Debug.Assert(memberName != null);
285-
Assembly assembly = Assembly.GetExecutingAssembly();
273+
286274
var module = DefineModule(fullName);
287275
var module_globals = Runtime.PyModule_GetDict(module);
288-
using (var stream = assembly.GetManifestResourceStream(resourceName))
289-
using (var reader = new StreamReader(stream))
290-
{
291-
string pyCode = reader.ReadToEnd();
292-
Exec(pyCode, module_globals.DangerousGetAddress(), module_globals.DangerousGetAddress());
293-
}
276+
277+
Assembly assembly = Assembly.GetExecutingAssembly();
278+
string pyCode = assembly.ReadStringResource(resourceName);
279+
Exec(pyCode, module_globals.DangerousGetAddress(), module_globals.DangerousGetAddress());
294280

295281
Runtime.PyDict_SetItemString(targetModuleDict, memberName, module);
296282
}
297283

284+
static void LoadMixins(BorrowedReference targetModuleDict)
285+
{
286+
foreach (string nested in new[] { "collections" })
287+
{
288+
LoadSubmodule(targetModuleDict,
289+
fullName: "clr._extras." + nested,
290+
resourceName: typeof(PythonEngine).Namespace + ".Mixins." + nested + ".py");
291+
}
292+
}
293+
298294
static void OnDomainUnload(object _, EventArgs __)
299295
{
300296
Shutdown();

0 commit comments

Comments
 (0)