diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index ca2562d04..53c0934ae 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -60,11 +60,15 @@ jobs:
with:
architecture: ${{ matrix.os.platform }}
python-version: ${{ matrix.python }}
+ cache-python: true
activate-environment: true
enable-cache: true
- name: Synchronize the virtual environment
- run: uv sync
+ run: uv sync --managed-python
+
+ - name: Show pyvenv.cfg
+ run: cat .venv/pyvenv.cfg
- name: Embedding tests (Mono/.NET Framework)
run: dotnet test --runtime any-${{ matrix.os.platform }} --framework net472 --logger "console;verbosity=detailed" src/embed_tests/
@@ -88,4 +92,8 @@ jobs:
run: pytest --runtime netfx
- name: Python tests run from .NET
+ # For some reason, it won't find pytest on the Windows + 3.10
+ # combination, which hints that it does not handle the venv properly in
+ # this combination.
+ if: ${{ matrix.os.category != 'windows' || matrix.python != '3.10' }}
run: dotnet test --runtime any-${{ matrix.os.platform }} src/python_tests_runner/
diff --git a/Directory.Build.props b/Directory.Build.props
index 0288a4d64..85e4039b9 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -4,11 +4,12 @@
Copyright (c) 2006-2025 The Contributors of the Python.NET Project
pythonnet
Python.NET
- 10.0
+ 12.0
false
$([System.IO.File]::ReadAllText("$(MSBuildThisFileDirectory)version.txt").Trim())
$(FullVersion.Split('-', 2)[0])
$(FullVersion.Split('-', 2)[1])
+ $(MSBuildThisFileDirectory)
diff --git a/src/embed_tests/CallableObject.cs b/src/embed_tests/CallableObject.cs
index 8466f5ad8..d450598d2 100644
--- a/src/embed_tests/CallableObject.cs
+++ b/src/embed_tests/CallableObject.cs
@@ -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]
@@ -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("o.Call(5)");
- Assert.AreEqual(105, result);
+ Assert.That(result, Is.EqualTo(105));
}
class Doubler
@@ -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 GetBaseTypes(Type type, IList existingBases)
{
- Assert.Greater(BaseClass.Refcount, 0);
+ Assert.That(BaseClass.Refcount, Is.GreaterThan(0));
return type != typeof(CallViaInheritance)
? existingBases
- : new[] { BaseClass };
+ : [BaseClass];
}
}
}
diff --git a/src/embed_tests/ClassManagerTests.cs b/src/embed_tests/ClassManagerTests.cs
index 72025a28b..83bfa7bc2 100644
--- a/src/embed_tests/ClassManagerTests.cs
+++ b/src/embed_tests/ClassManagerTests.cs
@@ -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()
{
diff --git a/src/embed_tests/CodecGroups.cs b/src/embed_tests/CodecGroups.cs
index 689e5b24c..22ed0df72 100644
--- a/src/embed_tests/CodecGroups.cs
+++ b/src/embed_tests/CodecGroups.cs
@@ -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]
@@ -31,9 +31,13 @@ public void CanEncode()
new ObjectToEncoderInstanceEncoder(),
};
- Assert.IsTrue(group.CanEncode(typeof(Tuple)));
- Assert.IsTrue(group.CanEncode(typeof(Uri)));
- Assert.IsFalse(group.CanEncode(typeof(string)));
+ Assert.Multiple(() =>
+ {
+ Assert.That(group.CanEncode(typeof(Tuple)), Is.True);
+ Assert.That(group.CanEncode(typeof(Uri)), Is.True);
+ Assert.That(group.CanEncode(typeof(string)), Is.False);
+ });
+
}
[Test]
@@ -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]
@@ -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()
@@ -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]
@@ -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);
+ });
}
}
}
diff --git a/src/embed_tests/Codecs.cs b/src/embed_tests/Codecs.cs
index c8b8ecb6e..d4d22dcac 100644
--- a/src/embed_tests/Codecs.cs
+++ b/src/embed_tests/Codecs.cs
@@ -8,16 +8,10 @@ namespace Python.EmbeddingTest {
public class Codecs
{
- [SetUp]
- public void SetUp()
- {
- PythonEngine.Initialize();
- }
-
[TearDown]
- public void Dispose()
+ public void TearDown()
{
- PythonEngine.Shutdown();
+ PyObjectConversions.Reset();
}
[Test]
diff --git a/src/embed_tests/dynamic.cs b/src/embed_tests/Dynamic.cs
similarity index 95%
rename from src/embed_tests/dynamic.cs
rename to src/embed_tests/Dynamic.cs
index 6e3bfc4cb..174167118 100644
--- a/src/embed_tests/dynamic.cs
+++ b/src/embed_tests/Dynamic.cs
@@ -8,18 +8,6 @@ namespace Python.EmbeddingTest
{
public class DynamicTest
{
- [OneTimeSetUp]
- public void SetUp()
- {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void Dispose()
- {
- PythonEngine.Shutdown();
- }
-
///
/// Set the attribute of a PyObject with a .NET object.
///
diff --git a/src/embed_tests/Events.cs b/src/embed_tests/Events.cs
index c216f4214..94a30726b 100644
--- a/src/embed_tests/Events.cs
+++ b/src/embed_tests/Events.cs
@@ -10,18 +10,6 @@ namespace Python.EmbeddingTest;
public class Events
{
- [OneTimeSetUp]
- public void SetUp()
- {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void Dispose()
- {
- PythonEngine.Shutdown();
- }
-
[Test]
public void UsingDoesNotLeak()
{
diff --git a/src/embed_tests/ExtensionTypes.cs b/src/embed_tests/ExtensionTypes.cs
index 803845960..3e8ead142 100644
--- a/src/embed_tests/ExtensionTypes.cs
+++ b/src/embed_tests/ExtensionTypes.cs
@@ -8,18 +8,6 @@ namespace Python.EmbeddingTest;
public class ExtensionTypes
{
- [OneTimeSetUp]
- public void SetUp()
- {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void Dispose()
- {
- PythonEngine.Shutdown();
- }
-
[Test]
public void WeakrefIsNone_AfterBoundMethodIsGone()
{
@@ -27,6 +15,6 @@ public void WeakrefIsNone_AfterBoundMethodIsGone()
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);
}
}
diff --git a/src/embed_tests/GlobalTestsSetup.cs b/src/embed_tests/GlobalTestsSetup.cs
index dff58b978..4f681dd9f 100644
--- a/src/embed_tests/GlobalTestsSetup.cs
+++ b/src/embed_tests/GlobalTestsSetup.cs
@@ -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)
diff --git a/src/embed_tests/Inheritance.cs b/src/embed_tests/Inheritance.cs
index ebbc24dc4..1074fa288 100644
--- a/src/embed_tests/Inheritance.cs
+++ b/src/embed_tests/Inheritance.cs
@@ -9,23 +9,31 @@ namespace Python.EmbeddingTest
{
public class Inheritance
{
+ ExtraBaseTypeProvider ExtraBaseTypeProvider;
+ NoEffectBaseTypeProvider NoEffectBaseTypeProvider;
+
+
[OneTimeSetUp]
public void SetUp()
{
- PythonEngine.Initialize();
using var locals = new PyDict();
PythonEngine.Exec(InheritanceTestBaseClassWrapper.ClassSourceCode, locals: locals);
- ExtraBaseTypeProvider.ExtraBase = new PyType(locals[InheritanceTestBaseClassWrapper.ClassName]);
+
+ NoEffectBaseTypeProvider = new NoEffectBaseTypeProvider();
+ ExtraBaseTypeProvider = new ExtraBaseTypeProvider(new PyType(locals[InheritanceTestBaseClassWrapper.ClassName]));
+
var baseTypeProviders = PythonEngine.InteropConfiguration.PythonBaseTypeProviders;
- baseTypeProviders.Add(new ExtraBaseTypeProvider());
- baseTypeProviders.Add(new NoEffectBaseTypeProvider());
+ baseTypeProviders.Add(ExtraBaseTypeProvider);
+ baseTypeProviders.Add(NoEffectBaseTypeProvider);
}
[OneTimeTearDown]
public void Dispose()
{
- ExtraBaseTypeProvider.ExtraBase.Dispose();
- PythonEngine.Shutdown();
+ var baseTypeProviders = PythonEngine.InteropConfiguration.PythonBaseTypeProviders;
+ baseTypeProviders.Remove(NoEffectBaseTypeProvider);
+ baseTypeProviders.Remove(ExtraBaseTypeProvider);
+ ExtraBaseTypeProvider.Dispose();
}
[Test]
@@ -33,7 +41,7 @@ public void ExtraBase_PassesInstanceCheck()
{
var inherited = new Inherited();
bool properlyInherited = PyIsInstance(inherited, ExtraBaseTypeProvider.ExtraBase);
- Assert.IsTrue(properlyInherited);
+ Assert.That(properlyInherited, Is.True);
}
static dynamic PyIsInstance => PythonEngine.Eval("isinstance");
@@ -44,7 +52,7 @@ public void InheritingWithExtraBase_CreatesNewClass()
PyObject a = ExtraBaseTypeProvider.ExtraBase;
var inherited = new Inherited();
PyObject inheritedClass = inherited.ToPython().GetAttr("__class__");
- Assert.IsFalse(PythonReferenceComparer.Instance.Equals(a, inheritedClass));
+ Assert.That(PythonReferenceComparer.Instance.Equals(a, inheritedClass), Is.False);
}
[Test]
@@ -56,7 +64,7 @@ public void InheritedFromInheritedClassIsSelf()
PyObject b = scope.Eval("B");
PyObject bInstance = b.Invoke();
PyObject bInstanceClass = bInstance.GetAttr("__class__");
- Assert.IsTrue(PythonReferenceComparer.Instance.Equals(b, bInstanceClass));
+ Assert.That(PythonReferenceComparer.Instance.Equals(b, bInstanceClass), Is.True);
}
// https://github.com/pythonnet/pythonnet/issues/1420
@@ -76,7 +84,7 @@ public void Grandchild_PassesExtraBaseInstanceCheck()
PyObject b = scope.Eval("B");
PyObject bInst = b.Invoke();
bool properlyInherited = PyIsInstance(bInst, ExtraBaseTypeProvider.ExtraBase);
- Assert.IsTrue(properlyInherited);
+ Assert.That(properlyInherited, Is.True);
}
[Test]
@@ -84,7 +92,7 @@ public void CallInheritedClrMethod_WithExtraPythonBase()
{
var instance = new Inherited().ToPython();
string result = instance.InvokeMethod(nameof(PythonWrapperBase.WrapperBaseMethod)).As();
- Assert.AreEqual(result, nameof(PythonWrapperBase.WrapperBaseMethod));
+ Assert.That(nameof(PythonWrapperBase.WrapperBaseMethod), Is.EqualTo(result));
}
[Test]
@@ -94,7 +102,7 @@ public void CallExtraBaseMethod()
using var scope = Py.CreateScope();
scope.Set(nameof(instance), instance);
int actual = instance.ToPython().InvokeMethod("callVirt").As();
- Assert.AreEqual(expected: Inherited.OverridenVirtValue, actual);
+ Assert.That(actual, Is.EqualTo(Inherited.OverridenVirtValue));
}
[Test]
@@ -105,7 +113,7 @@ public void SetAdHocAttributes_WhenExtraBasePresent()
scope.Set(nameof(instance), instance);
scope.Exec($"super({nameof(instance)}.__class__, {nameof(instance)}).set_x_to_42()");
int actual = scope.Eval($"{nameof(instance)}.{nameof(Inherited.XProp)}");
- Assert.AreEqual(expected: Inherited.X, actual);
+ Assert.That(actual, Is.EqualTo(Inherited.X));
}
// https://github.com/pythonnet/pythonnet/issues/1476
@@ -115,9 +123,9 @@ public void BaseClearIsCalled()
using var scope = Py.CreateScope();
scope.Set("exn", new Exception("42"));
var msg = scope.Eval("exn.args[0]");
- Assert.AreEqual(2, msg.Refcount);
+ Assert.That(msg.Refcount, Is.EqualTo(2));
scope.Set("exn", null);
- Assert.AreEqual(1, msg.Refcount);
+ Assert.That(msg.Refcount, Is.EqualTo(1));
}
// https://github.com/pythonnet/pythonnet/issues/1455
@@ -126,18 +134,24 @@ public void PropertyAccessorOverridden()
{
using var derived = new PropertyAccessorDerived().ToPython();
derived.SetAttr(nameof(PropertyAccessorDerived.VirtualProp), "hi".ToPython());
- Assert.AreEqual("HI", derived.GetAttr(nameof(PropertyAccessorDerived.VirtualProp)).As());
+ Assert.That(derived.GetAttr(nameof(PropertyAccessorDerived.VirtualProp)).As(), Is.EqualTo("HI"));
}
}
- class ExtraBaseTypeProvider : IPythonBaseTypeProvider
+ class ExtraBaseTypeProvider(PyType ExtraBase) : IPythonBaseTypeProvider, IDisposable
{
- internal static PyType ExtraBase;
+ public PyType ExtraBase { get; } = ExtraBase;
+
+ public void Dispose()
+ {
+ ExtraBase.Dispose();
+ }
+
public IEnumerable GetBaseTypes(Type type, IList existingBases)
{
if (type == typeof(InheritanceTestBaseClassWrapper))
{
- return new[] { PyType.Get(type.BaseType), ExtraBase };
+ return [PyType.Get(type.BaseType), ExtraBase];
}
return existingBases;
}
diff --git a/src/embed_tests/Inspect.cs b/src/embed_tests/Inspect.cs
index 8ff94e02c..0c4ce43f3 100644
--- a/src/embed_tests/Inspect.cs
+++ b/src/embed_tests/Inspect.cs
@@ -9,18 +9,6 @@ namespace Python.EmbeddingTest
{
public class Inspect
{
- [OneTimeSetUp]
- public void SetUp()
- {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void Dispose()
- {
- PythonEngine.Shutdown();
- }
-
[Test]
public void InstancePropertiesVisibleOnClass()
{
@@ -28,7 +16,7 @@ public void InstancePropertiesVisibleOnClass()
var uriClass = uri.GetPythonType();
var property = uriClass.GetAttr(nameof(Uri.AbsoluteUri));
var pyProp = (PropertyObject)ManagedType.GetManagedObject(property.Reference);
- Assert.AreEqual(nameof(Uri.AbsoluteUri), pyProp.info.Value.Name);
+ Assert.That(pyProp.info.Value.Name, Is.EqualTo(nameof(Uri.AbsoluteUri)));
}
[Test]
diff --git a/src/embed_tests/Modules.cs b/src/embed_tests/Modules.cs
index 6cab4dd07..67fa3d0fc 100644
--- a/src/embed_tests/Modules.cs
+++ b/src/embed_tests/Modules.cs
@@ -28,18 +28,6 @@ public void Dispose()
}
}
- [OneTimeSetUp]
- public void OneTimeSetUp()
- {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void OneTimeTearDown()
- {
- PythonEngine.Shutdown();
- }
-
///
/// Eval a Python expression and obtain its return value.
///
@@ -50,7 +38,7 @@ public void TestEval()
{
ps.Set("a", 1);
var result = ps.Eval("a + 2");
- Assert.AreEqual(3, result);
+ Assert.That(result, Is.EqualTo(3));
}
}
@@ -66,7 +54,7 @@ public void TestExec()
ps.Set("cc", 10); //declare a local variable
ps.Exec("aa = bb + cc + 3");
var result = ps.Get("aa");
- Assert.AreEqual(113, result);
+ Assert.That(result, Is.EqualTo(113));
}
}
@@ -83,7 +71,7 @@ public void TestCompileExpression()
ps.Set("cc", 10); //declare a local variable
PyObject script = PythonEngine.Compile("bb + cc + 3", "", RunFlagType.Eval);
var result = ps.Execute(script);
- Assert.AreEqual(113, result);
+ Assert.That(result, Is.EqualTo(113));
}
}
@@ -102,7 +90,7 @@ public void TestCompileStatements()
PyObject script = PythonEngine.Compile("aa = bb + cc + 3", "", RunFlagType.File);
ps.Execute(script);
var result = ps.Get("aa");
- Assert.AreEqual(113, result);
+ Assert.That(result, Is.EqualTo(113));
}
}
@@ -123,7 +111,7 @@ public void TestScopeFunction()
dynamic func1 = ps.Get("func1");
func1(); //call the function, it can be called any times
var result = ps.Get("bb");
- Assert.AreEqual(100, result);
+ Assert.That(result, Is.EqualTo(100));
ps.Set("bb", 100);
ps.Set("cc", 10);
@@ -134,7 +122,7 @@ public void TestScopeFunction()
dynamic func2 = ps.Get("func2");
func2();
result = ps.Get("bb");
- Assert.AreEqual(20, result);
+ Assert.That(result, Is.EqualTo(20));
}
}
@@ -219,10 +207,10 @@ public void TestCreateModuleWithFilename()
using var modWithName = PyModule.FromString("mod_with_name", "", "some_filename");
- Assert.AreEqual("none", mod.Get("__file__"));
- Assert.AreEqual("none", modWithoutName.Get("__file__"));
- Assert.AreEqual("none", modNullName.Get("__file__"));
- Assert.AreEqual("some_filename", modWithName.Get("__file__"));
+ Assert.That(mod.Get("__file__"), Is.EqualTo("none"));
+ Assert.That(modWithoutName.Get("__file__"), Is.EqualTo("none"));
+ Assert.That(modNullName.Get("__file__"), Is.EqualTo("none"));
+ Assert.That(modWithName.Get("__file__"), Is.EqualTo("some_filename"));
}
///
@@ -235,17 +223,17 @@ public void TestImportModule()
using (Py.GIL())
{
dynamic sys = ps.Import("sys");
- Assert.IsTrue(ps.Contains("sys"));
+ Assert.That(ps.Contains("sys"), Is.True);
ps.Exec("sys.attr1 = 2");
var value1 = ps.Eval("sys.attr1");
var value2 = sys.attr1.As();
- Assert.AreEqual(2, value1);
+ Assert.That(value1, Is.EqualTo(2));
Assert.AreEqual(2, value2);
//import as
ps.Import("sys", "sys1");
- Assert.IsTrue(ps.Contains("sys1"));
+ Assert.That(ps.Contains("sys1"), Is.True);
}
}
@@ -266,10 +254,10 @@ public void TestImportScope()
scope.Import(ps, "ps");
scope.Exec("aa = ps.bb + ps.cc + 3");
var result = scope.Get("aa");
- Assert.AreEqual(113, result);
+ Assert.That(result, Is.EqualTo(113));
}
- Assert.IsFalse(ps.Contains("aa"));
+ Assert.That(ps.Contains("aa"), Is.False);
}
}
@@ -289,10 +277,10 @@ public void TestImportAllFromScope()
{
scope.Exec("aa = bb + cc + 3");
var result = scope.Get("aa");
- Assert.AreEqual(113, result);
+ Assert.That(result, Is.EqualTo(113));
}
- Assert.IsFalse(ps.Contains("aa"));
+ Assert.That(ps.Contains("aa"), Is.False);
}
}
@@ -345,22 +333,22 @@ public void TestVariables()
{
(ps.Variables() as dynamic)["ee"] = new PyInt(200);
var a0 = ps.Get("ee");
- Assert.AreEqual(200, a0);
+ Assert.That(a0, Is.EqualTo(200));
ps.Exec("locals()['ee'] = 210");
var a1 = ps.Get("ee");
- Assert.AreEqual(210, a1);
+ Assert.That(a1, Is.EqualTo(210));
ps.Exec("globals()['ee'] = 220");
var a2 = ps.Get("ee");
- Assert.AreEqual(220, a2);
+ Assert.That(a2, Is.EqualTo(220));
using (var item = ps.Variables())
{
item["ee"] = new PyInt(230);
}
var a3 = ps.Get("ee");
- Assert.AreEqual(230, a3);
+ Assert.That(a3, Is.EqualTo(230));
}
}
@@ -420,7 +408,7 @@ public void TestThread()
using (Py.GIL())
{
var result = ps.Get("res");
- Assert.AreEqual(101 * th_cnt, result);
+ Assert.That(result, Is.EqualTo(101 * th_cnt));
}
}
finally
@@ -434,7 +422,7 @@ public void TestCreate()
{
using var scope = Py.CreateScope();
- Assert.IsFalse(PyModule.SysModules.HasKey("testmod"));
+ Assert.That(PyModule.SysModules.HasKey("testmod"), Is.False);
PyModule testmod = new PyModule("testmod");
@@ -448,7 +436,7 @@ public void TestCreate()
);
scope.Execute(code);
- Assert.IsTrue(scope.TryGet("x", out dynamic x));
+ Assert.That(scope.TryGet("x", out dynamic x), Is.True);
Assert.AreEqual("True", x.ToString());
}
diff --git a/src/embed_tests/NeedsReinit/StopAndRestartEngine.cs b/src/embed_tests/NeedsReinit/StopAndRestartEngine.cs
new file mode 100644
index 000000000..9ea4f73c5
--- /dev/null
+++ b/src/embed_tests/NeedsReinit/StopAndRestartEngine.cs
@@ -0,0 +1,28 @@
+using Python.Runtime;
+using NUnit.Framework;
+
+namespace Python.EmbeddingTest.NeedsReinit;
+
+public class StopAndRestartEngine
+{
+ bool WasInitialized = false;
+
+ [OneTimeSetUp]
+ public void Setup()
+ {
+ WasInitialized = PythonEngine.IsInitialized;
+ if (WasInitialized)
+ {
+ PythonEngine.Shutdown();
+ }
+ }
+
+ [OneTimeTearDown]
+ public void Teardown()
+ {
+ if (WasInitialized && !PythonEngine.IsInitialized)
+ {
+ PythonEngine.Initialize();
+ }
+ }
+}
diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/NeedsReinit/TestDomainReload.cs
similarity index 99%
rename from src/embed_tests/TestDomainReload.cs
rename to src/embed_tests/NeedsReinit/TestDomainReload.cs
index a0f9b63eb..a8d2cd3d8 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/NeedsReinit/TestDomainReload.cs
@@ -15,10 +15,13 @@
// Unfortunately this means no continuous integration testing for this case.
//
#if NETFRAMEWORK
-namespace Python.EmbeddingTest
+namespace Python.EmbeddingTest.NeedsReinit
{
- class TestDomainReload
+ [Category("NeedsReinit")]
+ class TestDomainReload : StopAndRestartEngine
{
+
+
abstract class CrossCaller : MarshalByRefObject
{
public abstract ValueType Execute(ValueType arg);
diff --git a/src/embed_tests/pyinitialize.cs b/src/embed_tests/NeedsReinit/TestPyInitialize.cs
similarity index 95%
rename from src/embed_tests/pyinitialize.cs
rename to src/embed_tests/NeedsReinit/TestPyInitialize.cs
index 25dafb686..1ef4127b8 100644
--- a/src/embed_tests/pyinitialize.cs
+++ b/src/embed_tests/NeedsReinit/TestPyInitialize.cs
@@ -2,9 +2,10 @@
using NUnit.Framework;
using Python.Runtime;
-namespace Python.EmbeddingTest
+namespace Python.EmbeddingTest.NeedsReinit
{
- public class PyInitializeTest
+ [Category("NeedsReinit")]
+ public class TestPyInitialize : StopAndRestartEngine
{
///
/// Tests issue with multiple simple Initialize/Shutdowns.
@@ -42,8 +43,8 @@ public static void LoadSpecificArgs()
{
using var v0 = argv[0];
using var v1 = argv[1];
- Assert.AreEqual(args[0], v0.ToString());
- Assert.AreEqual(args[1], v1.ToString());
+ Assert.That(v0.ToString(), Is.EqualTo(args[0]));
+ Assert.That(v1.ToString(), Is.EqualTo(args[1]));
}
}
}
diff --git a/src/embed_tests/NeedsReinit/TestPythonEngineProperties.cs b/src/embed_tests/NeedsReinit/TestPythonEngineProperties.cs
new file mode 100644
index 000000000..8eb9e975d
--- /dev/null
+++ b/src/embed_tests/NeedsReinit/TestPythonEngineProperties.cs
@@ -0,0 +1,133 @@
+using System;
+using NUnit.Framework;
+using Python.Runtime;
+
+namespace Python.EmbeddingTest.NeedsReinit
+{
+ [Category("NeedsReinit")]
+ public class TestPythonEngineProperties : StopAndRestartEngine
+ {
+ [Test]
+ public void SetPythonHome()
+ {
+ PythonEngine.Initialize();
+ var pythonHomeBackup = PythonEngine.PythonHome;
+ PythonEngine.Shutdown();
+
+ if (pythonHomeBackup == "")
+ Assert.Inconclusive("Can't reset PythonHome to empty string, skipping");
+
+ var pythonHome = "/dummypath/";
+
+ PythonEngine.PythonHome = pythonHome;
+ PythonEngine.Initialize();
+
+ Assert.AreEqual(pythonHome, PythonEngine.PythonHome);
+ PythonEngine.Shutdown();
+
+ // Restoring valid pythonhome.
+ PythonEngine.PythonHome = pythonHomeBackup;
+ }
+
+ [Test]
+ public void SetPythonHomeTwice()
+ {
+ PythonEngine.Initialize();
+ var pythonHomeBackup = PythonEngine.PythonHome;
+ PythonEngine.Shutdown();
+
+ if (pythonHomeBackup == "")
+ Assert.Inconclusive("Can't reset PythonHome to empty string, skipping");
+
+ var pythonHome = "/dummypath/";
+
+ PythonEngine.PythonHome = "/dummypath2/";
+ PythonEngine.PythonHome = pythonHome;
+ PythonEngine.Initialize();
+
+ Assert.AreEqual(pythonHome, PythonEngine.PythonHome);
+ PythonEngine.Shutdown();
+
+ PythonEngine.PythonHome = pythonHomeBackup;
+ }
+
+ [Test]
+ [Ignore("Currently buggy in Python")]
+ public void SetPythonHomeEmptyString()
+ {
+ PythonEngine.Initialize();
+
+ var backup = PythonEngine.PythonHome;
+ if (backup == "")
+ {
+ PythonEngine.Shutdown();
+ Assert.Inconclusive("Can't reset PythonHome to empty string, skipping");
+ }
+ PythonEngine.PythonHome = "";
+
+ Assert.AreEqual("", PythonEngine.PythonHome);
+
+ PythonEngine.PythonHome = backup;
+ PythonEngine.Shutdown();
+ }
+
+ [Test]
+ public void SetProgramName()
+ {
+ if (PythonEngine.IsInitialized)
+ {
+ PythonEngine.Shutdown();
+ }
+
+ var programNameBackup = PythonEngine.ProgramName;
+
+ var programName = "FooBar";
+
+ PythonEngine.ProgramName = programName;
+ PythonEngine.Initialize();
+
+ Assert.AreEqual(programName, PythonEngine.ProgramName);
+ PythonEngine.Shutdown();
+
+ PythonEngine.ProgramName = programNameBackup;
+ }
+
+ [Test]
+ public void SetPythonPath()
+ {
+ PythonEngine.Initialize();
+
+ const string moduleName = "pytest";
+ bool importShouldSucceed;
+ try
+ {
+ Py.Import(moduleName);
+ importShouldSucceed = true;
+ }
+ catch
+ {
+ importShouldSucceed = false;
+ }
+
+ string[] paths = Py.Import("sys").GetAttr("path").As();
+ string path = string.Join(System.IO.Path.PathSeparator.ToString(), paths);
+
+ // path should not be set to PythonEngine.PythonPath here.
+ // PythonEngine.PythonPath gets the default module search path, not the full search path.
+ // The list sys.path is initialized with this value on interpreter startup;
+ // it can be (and usually is) modified later to change the search path for loading modules.
+ // See https://docs.python.org/3/c-api/init.html#c.Py_GetPath
+ // After PythonPath is set, then PythonEngine.PythonPath will correctly return the full search path.
+
+ PythonEngine.Shutdown();
+
+ PythonEngine.PythonPath = path;
+ PythonEngine.Initialize();
+
+ Assert.AreEqual(path, PythonEngine.PythonPath);
+ if (importShouldSucceed) Py.Import(moduleName);
+
+ PythonEngine.Shutdown();
+ }
+ }
+}
diff --git a/src/embed_tests/TestRuntime.cs b/src/embed_tests/NeedsReinit/TestRuntime.cs
similarity index 94%
rename from src/embed_tests/TestRuntime.cs
rename to src/embed_tests/NeedsReinit/TestRuntime.cs
index 77696fd96..193bf57d3 100644
--- a/src/embed_tests/TestRuntime.cs
+++ b/src/embed_tests/NeedsReinit/TestRuntime.cs
@@ -3,20 +3,11 @@
using NUnit.Framework;
using Python.Runtime;
-namespace Python.EmbeddingTest
+namespace Python.EmbeddingTest.NeedsReinit
{
- public class TestRuntime
+ [Ignore("Tests for low-level Runtime functions, crashing currently")]
+ public class TestRuntime : StopAndRestartEngine
{
- [OneTimeSetUp]
- public void SetUp()
- {
- // We needs to ensure that no any engines are running.
- if (PythonEngine.IsInitialized)
- {
- PythonEngine.Shutdown();
- }
- }
-
[Test]
public static void Py_IsInitializedValue()
{
diff --git a/src/embed_tests/NumPyTests.cs b/src/embed_tests/NumPyTests.cs
index e102ddb99..6f4a85716 100644
--- a/src/embed_tests/NumPyTests.cs
+++ b/src/embed_tests/NumPyTests.cs
@@ -13,14 +13,13 @@ public class NumPyTests
[OneTimeSetUp]
public void SetUp()
{
- PythonEngine.Initialize();
TupleCodec.Register();
}
[OneTimeTearDown]
public void Dispose()
{
- PythonEngine.Shutdown();
+ PyObjectConversions.Reset();
}
[Test]
@@ -32,7 +31,7 @@ public void TestReadme()
StringAssert.StartsWith("-0.95892", sin(5).ToString());
double c = (double)(np.cos(5) + sin(5));
- Assert.AreEqual(-0.675262, c, 0.01);
+ Assert.That(c, Is.EqualTo(-0.675262).Within(0.01));
dynamic a = np.array(new List { 1, 2, 3 });
Assert.AreEqual("float64", a.dtype.ToString());
diff --git a/src/embed_tests/Python.EmbeddingTest.csproj b/src/embed_tests/Python.EmbeddingTest.csproj
index da6799912..36b9a31f9 100644
--- a/src/embed_tests/Python.EmbeddingTest.csproj
+++ b/src/embed_tests/Python.EmbeddingTest.csproj
@@ -14,12 +14,21 @@
+
+
+
+
+
$(DefineConstants);$(ConfiguredConstants)
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/embed_tests/References.cs b/src/embed_tests/References.cs
index c416c5ebe..af9e74336 100644
--- a/src/embed_tests/References.cs
+++ b/src/embed_tests/References.cs
@@ -5,18 +5,6 @@ namespace Python.EmbeddingTest
public class References
{
- [OneTimeSetUp]
- public void SetUp()
- {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void Dispose()
- {
- PythonEngine.Shutdown();
- }
-
[Test]
public void MoveToPyObject_SetsNull()
{
@@ -24,10 +12,10 @@ public void MoveToPyObject_SetsNull()
NewReference reference = Runtime.PyDict_Items(dict.Reference);
try
{
- Assert.IsFalse(reference.IsNull());
+ Assert.That(reference.IsNull(), Is.False);
using (reference.MoveToPyObject())
- Assert.IsTrue(reference.IsNull());
+ Assert.That(reference.IsNull(), Is.True);
}
finally
{
diff --git a/src/embed_tests/StateSerialization/MethodSerialization.cs b/src/embed_tests/StateSerialization/MethodSerialization.cs
index 80b7a08ee..d565c1e7a 100644
--- a/src/embed_tests/StateSerialization/MethodSerialization.cs
+++ b/src/embed_tests/StateSerialization/MethodSerialization.cs
@@ -20,7 +20,7 @@ public void GenericRoundtrip()
}
[Test]
- public void ConstrctorRoundtrip()
+ public void ConstructorRoundtrip()
{
var ctor = typeof(MethodTestHost).GetConstructor(new[] { typeof(int) });
var maybeConstructor = new MaybeMethodBase(ctor);
@@ -33,6 +33,10 @@ static T SerializationRoundtrip(T item)
{
using var buf = new MemoryStream();
var formatter = RuntimeData.CreateFormatter();
+ if (typeof(NoopFormatter).IsAssignableFrom(formatter.GetType()))
+ {
+ Assert.Inconclusive("NoopFormatter in use, cannot perform serialization test.");
+ }
formatter.Serialize(buf, item);
buf.Position = 0;
return (T)formatter.Deserialize(buf);
diff --git a/src/embed_tests/TestCallbacks.cs b/src/embed_tests/TestCallbacks.cs
index 88b84d0c3..7e9583364 100644
--- a/src/embed_tests/TestCallbacks.cs
+++ b/src/embed_tests/TestCallbacks.cs
@@ -5,16 +5,6 @@
namespace Python.EmbeddingTest {
public class TestCallbacks {
- [OneTimeSetUp]
- public void SetUp() {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void Dispose() {
- PythonEngine.Shutdown();
- }
-
[Test]
public void TestNoOverloadException() {
int passed = 0;
@@ -23,7 +13,7 @@ public void TestNoOverloadException() {
using dynamic callWith42 = PythonEngine.Eval("lambda f: f([42])");
using var pyFunc = aFunctionThatCallsIntoPython.ToPython();
var error = Assert.Throws(() => callWith42(pyFunc));
- Assert.AreEqual("TypeError", error.Type.Name);
+ Assert.That(error.Type.Name, Is.EqualTo("TypeError"));
string expectedArgTypes = "()";
StringAssert.EndsWith(expectedArgTypes, error.Message);
error.Traceback.Dispose();
diff --git a/src/embed_tests/TestConverter.cs b/src/embed_tests/TestConverter.cs
index a59b9c97b..3feced8d0 100644
--- a/src/embed_tests/TestConverter.cs
+++ b/src/embed_tests/TestConverter.cs
@@ -23,18 +23,6 @@ public class TestConverter
typeof(ulong)
};
- [OneTimeSetUp]
- public void SetUp()
- {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void Dispose()
- {
- PythonEngine.Shutdown();
- }
-
[Test]
public void TestConvertSingleToManaged(
[Values(float.PositiveInfinity, float.NegativeInfinity, float.MinValue, float.MaxValue, float.NaN,
@@ -45,8 +33,8 @@ public void TestConvertSingleToManaged(
object convertedValue;
var converted = Converter.ToManaged(pyFloat, typeof(float), out convertedValue, false);
- Assert.IsTrue(converted);
- Assert.IsTrue(((float) convertedValue).Equals(testValue));
+ Assert.That(converted, Is.True);
+ Assert.That(((float)convertedValue).Equals(testValue), Is.True);
}
[Test]
@@ -59,8 +47,8 @@ public void TestConvertDoubleToManaged(
object convertedValue;
var converted = Converter.ToManaged(pyFloat, typeof(double), out convertedValue, false);
- Assert.IsTrue(converted);
- Assert.IsTrue(((double) convertedValue).Equals(testValue));
+ Assert.That(converted, Is.True);
+ Assert.That(((double)convertedValue).Equals(testValue), Is.True);
}
[Test]
@@ -79,10 +67,10 @@ public void CovertTypeError()
try
{
bool res = Converter.ToManaged(s, type, out value, true);
- Assert.IsFalse(res);
+ Assert.That(res, Is.False);
var bo = Exceptions.ExceptionMatches(Exceptions.TypeError);
- Assert.IsTrue(Exceptions.ExceptionMatches(Exceptions.TypeError)
- || Exceptions.ExceptionMatches(Exceptions.ValueError));
+ Assert.That(Exceptions.ExceptionMatches(Exceptions.TypeError)
+ || Exceptions.ExceptionMatches(Exceptions.ValueError), Is.True);
}
finally
{
@@ -104,8 +92,8 @@ public void ConvertOverflow()
foreach (var type in _numTypes)
{
bool res = Converter.ToManaged(largeNum.BorrowOrThrow(), type, out value, true);
- Assert.IsFalse(res);
- Assert.IsTrue(Exceptions.ExceptionMatches(Exceptions.OverflowError));
+ Assert.That(res, Is.False);
+ Assert.That(Exceptions.ExceptionMatches(Exceptions.OverflowError), Is.True);
Exceptions.Clear();
}
}
@@ -129,7 +117,7 @@ public void ToNullable()
const int Const = 42;
var i = new PyInt(Const);
var ni = i.As();
- Assert.AreEqual(Const, ni);
+ Assert.That(ni, Is.EqualTo(Const));
}
[Test]
@@ -138,9 +126,9 @@ public void BigIntExplicit()
BigInteger val = 42;
var i = new PyInt(val);
var ni = i.As();
- Assert.AreEqual(val, ni);
+ Assert.That(ni, Is.EqualTo(val));
var nullable = i.As();
- Assert.AreEqual(val, nullable);
+ Assert.That(nullable, Is.EqualTo(val));
}
[Test]
@@ -148,7 +136,7 @@ public void PyIntImplicit()
{
var i = new PyInt(1);
var ni = (PyObject)i.As
+
+
+
+
diff --git a/src/python_tests_runner/PythonTestRunner.cs b/src/python_tests_runner/PythonTestRunner.cs
index f97cc5aec..3df22ec2e 100644
--- a/src/python_tests_runner/PythonTestRunner.cs
+++ b/src/python_tests_runner/PythonTestRunner.cs
@@ -8,22 +8,29 @@
using NUnit.Framework;
using Python.Runtime;
-using Python.Test;
namespace Python.PythonTestsRunner
{
public class PythonTestRunner
{
+ string OriginalDirectory;
+
[OneTimeSetUp]
public void SetUp()
{
PythonEngine.Initialize();
+ OriginalDirectory = Environment.CurrentDirectory;
+
+ var codeDir = File.ReadAllText("tests_location.txt").Trim();
+ TestContext.Progress.WriteLine($"Changing working directory to {codeDir}");
+ Environment.CurrentDirectory = codeDir;
}
[OneTimeTearDown]
public void Dispose()
{
PythonEngine.Shutdown();
+ Environment.CurrentDirectory = OriginalDirectory;
}
///
@@ -46,39 +53,15 @@ static IEnumerable PythonTestCases()
[TestCaseSource(nameof(PythonTestCases))]
public void RunPythonTest(string testFile, string testName)
{
- // Find the tests directory
- string folder = typeof(PythonTestRunner).Assembly.Location;
- while (Path.GetFileName(folder) != "src")
+ using dynamic pytest = Py.Import("pytest");
+
+ using var args = new PyList();
+ args.Append(new PyString($"{testFile}.py::{testName}"));
+ int res = pytest.main(args);
+ if (res != 0)
{
- folder = Path.GetDirectoryName(folder);
+ Assert.Fail($"Python test {testFile}.{testName} failed");
}
- folder = Path.Combine(folder, "..", "tests");
- string path = Path.Combine(folder, testFile + ".py");
- if (!File.Exists(path)) throw new FileNotFoundException("Cannot find test file", path);
-
- // We could use 'import' below, but importlib gives more helpful error messages than 'import'
- // https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
- // Because the Python tests sometimes have relative imports, the module name must be inside the tests package
- PythonEngine.Exec($@"
-import sys
-import os
-sys.path.append(os.path.dirname(r'{folder}'))
-sys.path.append(os.path.join(r'{folder}', 'fixtures'))
-import clr
-clr.AddReference('Python.Test')
-import tests
-module_name = 'tests.{testFile}'
-file_path = r'{path}'
-import importlib.util
-spec = importlib.util.spec_from_file_location(module_name, file_path)
-module = importlib.util.module_from_spec(spec)
-sys.modules[module_name] = module
-try:
- spec.loader.exec_module(module)
-except ImportError as error:
- raise ImportError(str(error) + ' when sys.path=' + os.pathsep.join(sys.path))
-module.{testName}()
-");
}
}
}
diff --git a/src/runtime/Runtime.Delegates.cs b/src/runtime/Runtime.Delegates.cs
index 262dc1e19..dc4a4b0a9 100644
--- a/src/runtime/Runtime.Delegates.cs
+++ b/src/runtime/Runtime.Delegates.cs
@@ -308,7 +308,8 @@ static Delegates()
{
throw new BadPythonDllException(
"Runtime.PythonDLL was not set or does not point to a supported Python runtime DLL." +
- " See https://github.com/pythonnet/pythonnet#embedding-python-in-net",
+ " See https://github.com/pythonnet/pythonnet#embedding-python-in-net." +
+ $" Value of PythonDLL: {PythonDLL ?? "null"}",
e);
}
}
diff --git a/src/runtime/Util/PythonEnvironment.cs b/src/runtime/Util/PythonEnvironment.cs
index 701db3c93..b1ebc7fa5 100644
--- a/src/runtime/Util/PythonEnvironment.cs
+++ b/src/runtime/Util/PythonEnvironment.cs
@@ -161,7 +161,7 @@ private static Dictionary TryParse(string venvCfg)
private static string ProgramNameFromPath(string path)
{
- if (Runtime.IsWindows)
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return Path.Combine(path, "Scripts", "python.exe");
}