Skip to content
Next Next commit
Only init/shutdown Python once
  • Loading branch information
filmor committed Dec 8, 2025
commit b871d3532bbc1a17f949949aa61cfc0d84316244
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<AssemblyCopyright>Copyright (c) 2006-2025 The Contributors of the Python.NET Project</AssemblyCopyright>
<AssemblyCompany>pythonnet</AssemblyCompany>
<AssemblyProduct>Python.NET</AssemblyProduct>
<LangVersion>10.0</LangVersion>
<LangVersion>12.0</LangVersion>
<IsPackable>false</IsPackable>
<FullVersion>$([System.IO.File]::ReadAllText("$(MSBuildThisFileDirectory)version.txt").Trim())</FullVersion>
<VersionPrefix>$(FullVersion.Split('-', 2)[0])</VersionPrefix>
Expand Down
25 changes: 13 additions & 12 deletions src/embed_tests/CallableObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,37 @@ namespace Python.EmbeddingTest
{
public class CallableObject
{
IPythonBaseTypeProvider BaseTypeProvider;

[OneTimeSetUp]
public void SetUp()
{
PythonEngine.Initialize();
using var locals = new PyDict();
PythonEngine.Exec(CallViaInheritance.BaseClassSource, locals: locals);
CustomBaseTypeProvider.BaseClass = new PyType(locals[CallViaInheritance.BaseClassName]);
PythonEngine.InteropConfiguration.PythonBaseTypeProviders.Add(new CustomBaseTypeProvider());
BaseTypeProvider = new CustomBaseTypeProvider(new PyType(locals[CallViaInheritance.BaseClassName]));
PythonEngine.InteropConfiguration.PythonBaseTypeProviders.Add(BaseTypeProvider);
}

[OneTimeTearDown]
public void Dispose()
{
PythonEngine.Shutdown();
PythonEngine.InteropConfiguration.PythonBaseTypeProviders.Remove(BaseTypeProvider);
}

[Test]
public void CallMethodMakesObjectCallable()
{
var doubler = new DerivedDoubler();
dynamic applyObjectTo21 = PythonEngine.Eval("lambda o: o(21)");
Assert.AreEqual(doubler.__call__(21), (int)applyObjectTo21(doubler.ToPython()));
Assert.That((int)applyObjectTo21(doubler.ToPython()), Is.EqualTo(doubler.__call__(21)));
}

[Test]
public void CallMethodCanBeInheritedFromPython()
{
var callViaInheritance = new CallViaInheritance();
dynamic applyObjectTo14 = PythonEngine.Eval("lambda o: o(14)");
Assert.AreEqual(callViaInheritance.Call(14), (int)applyObjectTo14(callViaInheritance.ToPython()));
Assert.That((int)applyObjectTo14(callViaInheritance.ToPython()), Is.EqualTo(callViaInheritance.Call(14)));
}

[Test]
Expand All @@ -48,7 +51,7 @@ public void CanOverwriteCall()
scope.Exec("orig_call = o.Call");
scope.Exec("o.Call = lambda a: orig_call(a*7)");
int result = scope.Eval<int>("o.Call(5)");
Assert.AreEqual(105, result);
Assert.That(result, Is.EqualTo(105));
}

class Doubler
Expand All @@ -71,16 +74,14 @@ class {BaseClassName}(MyCallableBase): pass
public int Call(int arg) => 3 * arg;
}

class CustomBaseTypeProvider : IPythonBaseTypeProvider
class CustomBaseTypeProvider(PyType BaseClass) : IPythonBaseTypeProvider
{
internal static PyType BaseClass;

public IEnumerable<PyType> GetBaseTypes(Type type, IList<PyType> existingBases)
{
Assert.Greater(BaseClass.Refcount, 0);
Assert.That(BaseClass.Refcount, Is.GreaterThan(0));
return type != typeof(CallViaInheritance)
? existingBases
: new[] { BaseClass };
: [BaseClass];
}
}
}
Expand Down
12 changes: 0 additions & 12 deletions src/embed_tests/ClassManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,6 @@ namespace Python.EmbeddingTest
{
public class ClassManagerTests
{
[OneTimeSetUp]
public void SetUp()
{
PythonEngine.Initialize();
}

[OneTimeTearDown]
public void Dispose()
{
PythonEngine.Shutdown();
}

[Test]
public void NestedClassDerivingFromParent()
{
Expand Down
62 changes: 30 additions & 32 deletions src/embed_tests/CodecGroups.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public void GetEncodersByType()
};

var got = group.GetEncoders(typeof(Uri)).ToArray();
CollectionAssert.AreEqual(new[]{encoder1, encoder2}, got);
Assert.That(got, Is.EqualTo(new[] { encoder1, encoder2 }).AsCollection);
}

[Test]
Expand All @@ -31,9 +31,13 @@ public void CanEncode()
new ObjectToEncoderInstanceEncoder<Uri>(),
};

Assert.IsTrue(group.CanEncode(typeof(Tuple<int>)));
Assert.IsTrue(group.CanEncode(typeof(Uri)));
Assert.IsFalse(group.CanEncode(typeof(string)));
Assert.Multiple(() =>
{
Assert.That(group.CanEncode(typeof(Tuple<int>)), Is.True);
Assert.That(group.CanEncode(typeof(Uri)), Is.True);
Assert.That(group.CanEncode(typeof(string)), Is.False);
});

}

[Test]
Expand All @@ -50,12 +54,12 @@ public void Encodes()

var uri = group.TryEncode(new Uri("data:"));
var clrObject = (CLRObject)ManagedType.GetManagedObject(uri);
Assert.AreSame(encoder1, clrObject.inst);
Assert.AreNotSame(encoder2, clrObject.inst);
Assert.That(clrObject.inst, Is.SameAs(encoder1));
Assert.That(clrObject.inst, Is.Not.SameAs(encoder2));

var tuple = group.TryEncode(Tuple.Create(1));
clrObject = (CLRObject)ManagedType.GetManagedObject(tuple);
Assert.AreSame(encoder0, clrObject.inst);
Assert.That(clrObject.inst, Is.SameAs(encoder0));
}

[Test]
Expand All @@ -72,11 +76,11 @@ public void GetDecodersByTypes()
};

var decoder = group.GetDecoder(pyfloat, typeof(string));
Assert.AreSame(decoder2, decoder);
Assert.That(decoder, Is.SameAs(decoder2));
decoder = group.GetDecoder(pystr, typeof(string));
Assert.IsNull(decoder);
Assert.That(decoder, Is.Null);
decoder = group.GetDecoder(pyint, typeof(long));
Assert.AreSame(decoder1, decoder);
Assert.That(decoder, Is.SameAs(decoder1));
}
[Test]
public void CanDecode()
Expand All @@ -91,10 +95,14 @@ public void CanDecode()
decoder2,
};

Assert.IsTrue(group.CanDecode(pyint, typeof(long)));
Assert.IsFalse(group.CanDecode(pyint, typeof(int)));
Assert.IsTrue(group.CanDecode(pyfloat, typeof(string)));
Assert.IsFalse(group.CanDecode(pystr, typeof(string)));
Assert.Multiple(() =>
{
Assert.That(group.CanDecode(pyint, typeof(long)));
Assert.That(group.CanDecode(pyint, typeof(int)), Is.False);
Assert.That(group.CanDecode(pyfloat, typeof(string)));
Assert.That(group.CanDecode(pystr, typeof(string)), Is.False);
});

}

[Test]
Expand All @@ -109,24 +117,14 @@ public void Decodes()
decoder2,
};

Assert.IsTrue(group.TryDecode(new PyInt(10), out long longResult));
Assert.AreEqual(42, longResult);
Assert.IsTrue(group.TryDecode(new PyFloat(10), out string strResult));
Assert.AreSame("atad:", strResult);

Assert.IsFalse(group.TryDecode(new PyInt(10), out int _));
}

[SetUp]
public void SetUp()
{
PythonEngine.Initialize();
}

[TearDown]
public void Dispose()
{
PythonEngine.Shutdown();
Assert.Multiple(() =>
{
Assert.That(group.TryDecode(new PyInt(10), out long longResult));
Assert.That(longResult, Is.EqualTo(42));
Assert.That(group.TryDecode(new PyFloat(10), out string strResult));
Assert.That(strResult, Is.SameAs("atad:"));
Assert.That(group.TryDecode(new PyInt(10), out int _), Is.False);
});
}
}
}
12 changes: 0 additions & 12 deletions src/embed_tests/Codecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,6 @@ namespace Python.EmbeddingTest {

public class Codecs
{
[SetUp]
public void SetUp()
{
PythonEngine.Initialize();
}

[TearDown]
public void Dispose()
{
PythonEngine.Shutdown();
}

[Test]
public void TupleConversionsGeneric()
{
Expand Down
12 changes: 0 additions & 12 deletions src/embed_tests/dynamic.cs → src/embed_tests/Dynamic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,6 @@ namespace Python.EmbeddingTest
{
public class DynamicTest
{
[OneTimeSetUp]
public void SetUp()
{
PythonEngine.Initialize();
}

[OneTimeTearDown]
public void Dispose()
{
PythonEngine.Shutdown();
}

/// <summary>
/// Set the attribute of a PyObject with a .NET object.
/// </summary>
Expand Down
14 changes: 1 addition & 13 deletions src/embed_tests/ExtensionTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,13 @@ namespace Python.EmbeddingTest;

public class ExtensionTypes
{
[OneTimeSetUp]
public void SetUp()
{
PythonEngine.Initialize();
}

[OneTimeTearDown]
public void Dispose()
{
PythonEngine.Shutdown();
}

[Test]
public void WeakrefIsNone_AfterBoundMethodIsGone()
{
using var makeref = Py.Import("weakref").GetAttr("ref");
var boundMethod = new UriBuilder().ToPython().GetAttr(nameof(UriBuilder.GetHashCode));
var weakref = makeref.Invoke(boundMethod);
boundMethod.Dispose();
Assert.IsTrue(weakref.Invoke().IsNone());
Assert.That(weakref.Invoke().IsNone(), Is.True);
}
}
1 change: 1 addition & 0 deletions src/embed_tests/GlobalTestsSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public partial class GlobalTestsSetup
public void GlobalSetup()
{
Finalizer.Instance.ErrorHandler += FinalizerErrorHandler;
PythonEngine.Initialize();
}

private void FinalizerErrorHandler(object sender, Finalizer.ErrorArgs e)
Expand Down
Loading