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(); - Assert.IsTrue(PythonReferenceComparer.Instance.Equals(i, ni)); + Assert.That(PythonReferenceComparer.Instance.Equals(i, ni), Is.True); } [Test] @@ -158,7 +146,7 @@ public void ToPyList() list.Append("hello".ToPython()); list.Append("world".ToPython()); var back = list.ToPython().As(); - Assert.AreEqual(list.Length(), back.Length()); + Assert.That(back.Length(), Is.EqualTo(list.Length())); } [Test] @@ -182,7 +170,7 @@ public void RawPyObjectProxy() const string handlePropertyName = nameof(PyObject.Handle); #pragma warning restore CS0612 // Type or member is obsolete var proxiedHandle = pyObjectProxy.GetAttr(handlePropertyName).As(); - Assert.AreEqual(pyObject.DangerousGetAddressOrNull(), proxiedHandle); + Assert.That(proxiedHandle, Is.EqualTo(pyObject.DangerousGetAddressOrNull())); } [Test] @@ -191,7 +179,7 @@ public void GenericToPython() int i = 42; var pyObject = i.ToPythonAs(); var type = pyObject.GetPythonType(); - Assert.AreEqual(nameof(IConvertible), type.Name); + Assert.That(type.Name, Is.EqualTo(nameof(IConvertible))); } // regression for https://github.com/pythonnet/pythonnet/issues/451 @@ -207,7 +195,7 @@ class PyGetListImpl(test.GetListImpl): var pyImpl = scope.Get("PyGetListImpl"); dynamic inst = pyImpl.Invoke(); List result = inst.GetList(); - CollectionAssert.AreEqual(new[] { "testing" }, result); + Assert.That(result, Is.EqualTo(new[] { "testing" }).AsCollection); } } diff --git a/src/embed_tests/TestCustomMarshal.cs b/src/embed_tests/TestCustomMarshal.cs index 312863d0c..3bcb6b2e6 100644 --- a/src/embed_tests/TestCustomMarshal.cs +++ b/src/embed_tests/TestCustomMarshal.cs @@ -6,18 +6,6 @@ namespace Python.EmbeddingTest { public class TestCustomMarshal { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - [Test] public static void GetManagedStringTwice() { @@ -27,9 +15,9 @@ public static void GetManagedStringTwice() string s1 = Runtime.Runtime.GetManagedString(op.BorrowOrThrow()); string s2 = Runtime.Runtime.GetManagedString(op.Borrow()); - Assert.AreEqual(1, Runtime.Runtime.Refcount32(op.Borrow())); - Assert.AreEqual(expected, s1); - Assert.AreEqual(expected, s2); + Assert.That(Runtime.Runtime.Refcount32(op.Borrow()), Is.EqualTo(1)); + Assert.That(s1, Is.EqualTo(expected)); + Assert.That(s2, Is.EqualTo(expected)); } } } diff --git a/src/embed_tests/TestFinalizer.cs b/src/embed_tests/TestFinalizer.cs index b748a2244..89dcf137e 100644 --- a/src/embed_tests/TestFinalizer.cs +++ b/src/embed_tests/TestFinalizer.cs @@ -17,7 +17,6 @@ public class TestFinalizer public void SetUp() { _oldThreshold = Finalizer.Instance.Threshold; - PythonEngine.Initialize(); Exceptions.Clear(); } @@ -25,7 +24,6 @@ public void SetUp() public void TearDown() { Finalizer.Instance.Threshold = _oldThreshold; - PythonEngine.Shutdown(); } private static void FullGCCollect() @@ -38,7 +36,7 @@ private static void FullGCCollect() [Obsolete("GC tests are not guaranteed")] public void CollectBasicObject() { - Assert.IsTrue(Finalizer.Instance.Enable); + Assert.That(Finalizer.Instance.Enable, Is.True); Finalizer.Instance.Threshold = 1; bool called = false; @@ -49,7 +47,7 @@ public void CollectBasicObject() called = true; }; - Assert.IsFalse(called, "The event handler was called before it was installed"); + Assert.That(called, Is.False, "The event handler was called before it was installed"); Finalizer.Instance.BeforeCollect += handler; IntPtr pyObj = MakeAGarbage(out var shortWeak, out var longWeak); @@ -60,10 +58,10 @@ public void CollectBasicObject() "The referenced object is alive although it should have been collected", shortWeak ); - Assert.IsTrue( + Assert.That( longWeak.IsAlive, - "The reference object is not alive although it should still be", - longWeak + Is.True, + $"The reference object is not alive although it should still be" ); { @@ -83,20 +81,22 @@ public void CollectBasicObject() { Finalizer.Instance.BeforeCollect -= handler; } - Assert.IsTrue(called, "The event handler was not called during finalization"); + Assert.That(called, Is.True, "The event handler was not called during finalization"); Assert.GreaterOrEqual(objectCount, 1); } [Test] + [Ignore("Requires explicit shutdown")] [Obsolete("GC tests are not guaranteed")] public void CollectOnShutdown() { IntPtr op = MakeAGarbage(out var shortWeak, out var longWeak); FullGCCollect(); - Assert.IsFalse(shortWeak.IsAlive); + Assert.That(shortWeak.IsAlive, Is.False); List garbage = Finalizer.Instance.GetCollectedObjects(); Assert.IsNotEmpty(garbage, "The garbage object should be collected"); - Assert.IsTrue(garbage.Contains(op), + Assert.That(garbage.Contains(op), + Is.True, "Garbage should contains the collected object"); PythonEngine.Shutdown(); @@ -133,7 +133,7 @@ private static IntPtr MakeAGarbage(out WeakReference shortWeak, out WeakReferenc handle = obj.Handle; }); garbageGen.Start(); - Assert.IsTrue(garbageGen.Join(TimeSpan.FromSeconds(5)), "Garbage creation timed out"); + Assert.That(garbageGen.Join(TimeSpan.FromSeconds(5)), Is.True, "Garbage creation timed out"); shortWeak = @short; longWeak = @long; return handle; @@ -209,8 +209,8 @@ public void ValidateRefCount() Finalizer.IncorrectRefCntHandler handler = (s, e) => { called = true; - Assert.AreEqual(ptr, e.Handle); - Assert.AreEqual(2, e.ImpactedObjects.Count); + Assert.That(e.Handle, Is.EqualTo(ptr)); + Assert.That(e.ImpactedObjects.Count, Is.EqualTo(2)); // Fix for this test, don't do this on general environment #pragma warning disable CS0618 // Type or member is obsolete Runtime.Runtime.XIncref(e.Reference); @@ -223,7 +223,7 @@ public void ValidateRefCount() ptr = CreateStringGarbage(); FullGCCollect(); Assert.Throws(() => Finalizer.Instance.Collect()); - Assert.IsTrue(called); + Assert.That(called, Is.True); } finally { diff --git a/src/embed_tests/TestGILState.cs b/src/embed_tests/TestGILState.cs index bf6f02dc6..ba2ab500f 100644 --- a/src/embed_tests/TestGILState.cs +++ b/src/embed_tests/TestGILState.cs @@ -17,17 +17,5 @@ public void CanDisposeMultipleTimes() gilState.Dispose(); } } - - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } } } diff --git a/src/embed_tests/TestInstanceWrapping.cs b/src/embed_tests/TestInstanceWrapping.cs index 0a441c823..c6996fd87 100644 --- a/src/embed_tests/TestInstanceWrapping.cs +++ b/src/embed_tests/TestInstanceWrapping.cs @@ -7,18 +7,6 @@ namespace Python.EmbeddingTest { public class TestInstanceWrapping { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - // regression test for https://github.com/pythonnet/pythonnet/issues/811 [Test] public void OverloadResolution_UnknownToObject() @@ -30,7 +18,7 @@ public void OverloadResolution_UnknownToObject() dynamic callWithSelf = PythonEngine.Eval("lambda o: o.ObjOrClass(object())"); callWithSelf(o); - Assert.AreEqual(Overloaded.Object, overloaded.Value); + Assert.That(overloaded.Value, Is.EqualTo(Overloaded.Object)); } } @@ -41,7 +29,7 @@ public void WeakRefIsNone_AfterObjectIsGone() var ub = new UriBuilder().ToPython(); using var weakref = makeref.Invoke(ub); ub.Dispose(); - Assert.IsTrue(weakref.Invoke().IsNone()); + Assert.That(weakref.Invoke().IsNone(), Is.True); } class Base {} diff --git a/src/embed_tests/TestInterrupt.cs b/src/embed_tests/TestInterrupt.cs index e6546adb2..d48f7c73b 100644 --- a/src/embed_tests/TestInterrupt.cs +++ b/src/embed_tests/TestInterrupt.cs @@ -15,7 +15,6 @@ public class TestInterrupt [OneTimeSetUp] public void SetUp() { - PythonEngine.Initialize(); // workaround for assert tlock.locked() warning threading = Py.Import("threading"); } @@ -24,7 +23,6 @@ public void SetUp() public void Dispose() { threading.Dispose(); - PythonEngine.Shutdown(); } [Test] @@ -50,9 +48,9 @@ public void PythonThreadIDStable() } PythonEngine.EndAllowThreads(threadState); - Assert.IsTrue(asyncCall.Wait(TimeSpan.FromSeconds(5)), "Async thread has not finished in time"); + Assert.That(asyncCall.Wait(TimeSpan.FromSeconds(5)), Is.True, "Async thread has not finished in time"); - Assert.AreEqual(pythonThreadID, pythonThreadID2); + Assert.That(pythonThreadID2, Is.EqualTo(pythonThreadID)); Assert.NotZero(pythonThreadID); } @@ -86,13 +84,13 @@ import time PythonEngine.EndAllowThreads(threadState); int interruptReturnValue = PythonEngine.Interrupt((ulong)Interlocked.Read(ref pythonThreadID)); - Assert.AreEqual(1, interruptReturnValue); + Assert.That(interruptReturnValue, Is.EqualTo(1)); threadState = PythonEngine.BeginAllowThreads(); - Assert.IsTrue(asyncCall.Wait(TimeSpan.FromSeconds(5)), "Async thread was not interrupted in time"); + Assert.That(asyncCall.Wait(TimeSpan.FromSeconds(5)), Is.True, "Async thread was not interrupted in time"); PythonEngine.EndAllowThreads(threadState); - Assert.AreEqual(0, asyncCall.Result); + Assert.That(asyncCall.Result, Is.EqualTo(0)); } } } diff --git a/src/embed_tests/TestNamedArguments.cs b/src/embed_tests/TestNamedArguments.cs index c86302038..16d7d5b8f 100644 --- a/src/embed_tests/TestNamedArguments.cs +++ b/src/embed_tests/TestNamedArguments.cs @@ -6,18 +6,6 @@ namespace Python.EmbeddingTest { public class TestNamedArguments { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - /// /// Test named arguments support through Py.kw method /// @@ -27,7 +15,7 @@ public void TestKeywordArgs() dynamic a = CreateTestClass(); var result = (int)a.Test3(2, Py.kw("a4", 8)); - Assert.AreEqual(12, result); + Assert.That(result, Is.EqualTo(12)); } @@ -40,7 +28,7 @@ public void TestNamedArgs() dynamic a = CreateTestClass(); var result = (int)a.Test3(2, a4: 8); - Assert.AreEqual(12, result); + Assert.That(result, Is.EqualTo(12)); } diff --git a/src/embed_tests/TestNativeTypeOffset.cs b/src/embed_tests/TestNativeTypeOffset.cs index d692c24e6..61b6903c5 100644 --- a/src/embed_tests/TestNativeTypeOffset.cs +++ b/src/embed_tests/TestNativeTypeOffset.cs @@ -13,18 +13,6 @@ namespace Python.EmbeddingTest { public class TestNativeTypeOffset { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - /// /// Tests that installation has generated code for NativeTypeOffset and that it can be loaded. /// diff --git a/src/embed_tests/TestOperator.cs b/src/embed_tests/TestOperator.cs index 6bfb81bdb..ab71ed9b3 100644 --- a/src/embed_tests/TestOperator.cs +++ b/src/embed_tests/TestOperator.cs @@ -14,14 +14,13 @@ public class TestOperator [OneTimeSetUp] public void SetUp() { - PythonEngine.Initialize(); OwnIntCodec.Setup(); } [OneTimeTearDown] public void Dispose() { - PythonEngine.Shutdown(); + PyObjectConversions.Reset(); } // Mock Integer class to test math ops on non-native dotnet types diff --git a/src/embed_tests/TestPyBuffer.cs b/src/embed_tests/TestPyBuffer.cs index 1b4e28d12..89ddf9370 100644 --- a/src/embed_tests/TestPyBuffer.cs +++ b/src/embed_tests/TestPyBuffer.cs @@ -11,14 +11,13 @@ class TestPyBuffer [OneTimeSetUp] public void SetUp() { - PythonEngine.Initialize(); TupleCodec.Register(); } [OneTimeTearDown] public void Dispose() { - PythonEngine.Shutdown(); + PyObjectConversions.Reset(); } [Test] @@ -38,7 +37,7 @@ public void TestBufferWrite() } string result = pythonArray.InvokeMethod("decode", "utf-8".ToPython()).As(); - Assert.IsTrue(result == bufferTestString2); + Assert.That(result == bufferTestString2, Is.True); } [Test] @@ -58,7 +57,7 @@ public void TestBufferRead() } string result = new UTF8Encoding().GetString(managedArray); - Assert.IsTrue(result == " " + bufferTestString.Substring(1)); + Assert.That(result == " " + bufferTestString.Substring(1), Is.True); } [Test] @@ -67,8 +66,8 @@ public void ArrayHasBuffer() var array = new[,] {{1, 2}, {3,4}}; var memoryView = PythonEngine.Eval("memoryview"); var mem = memoryView.Invoke(array.ToPython()); - Assert.AreEqual(1, mem[(0, 0).ToPython()].As()); - Assert.AreEqual(array[1,0], mem[(1, 0).ToPython()].As()); + Assert.That(mem[(0, 0).ToPython()].As(), Is.EqualTo(1)); + Assert.That(mem[(1, 0).ToPython()].As(), Is.EqualTo(array[1, 0])); } [Test] @@ -77,14 +76,14 @@ public void RefCount() using var _ = Py.GIL(); using var arr = ByteArrayFromAsciiString("hello world! !$%&/()=?"); - Assert.AreEqual(1, arr.Refcount); + Assert.That(arr.Refcount, Is.EqualTo(1)); using (PyBuffer buf = arr.GetBuffer()) { - Assert.AreEqual(2, arr.Refcount); + Assert.That(arr.Refcount, Is.EqualTo(2)); } - Assert.AreEqual(1, arr.Refcount); + Assert.That(arr.Refcount, Is.EqualTo(1)); } [Test] @@ -99,7 +98,7 @@ public void Finalization() using var _ = Py.GIL(); using var arr = ByteArrayFromAsciiString("hello world! !$%&/()=?"); - Assert.AreEqual(1, arr.Refcount); + Assert.That(arr.Refcount, Is.EqualTo(1)); MakeBufAndLeak(arr); @@ -107,7 +106,7 @@ public void Finalization() GC.WaitForPendingFinalizers(); Finalizer.Instance.Collect(); - Assert.AreEqual(1, arr.Refcount); + Assert.That(arr.Refcount, Is.EqualTo(1)); } [Test] diff --git a/src/embed_tests/TestPyFloat.cs b/src/embed_tests/TestPyFloat.cs index 89e29e5fd..c6111f180 100644 --- a/src/embed_tests/TestPyFloat.cs +++ b/src/embed_tests/TestPyFloat.cs @@ -9,18 +9,6 @@ namespace Python.EmbeddingTest /// public class TestPyFloat { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - [Test] public void FloatCtor() { @@ -132,14 +120,14 @@ public void CompareTo() { var v = new PyFloat(42); - Assert.AreEqual(0, v.CompareTo(42f)); - Assert.AreEqual(0, v.CompareTo(42d)); + Assert.That(v.CompareTo(42f), Is.EqualTo(0)); + Assert.That(v.CompareTo(42d), Is.EqualTo(0)); - Assert.AreEqual(1, v.CompareTo(41f)); - Assert.AreEqual(1, v.CompareTo(41d)); + Assert.That(v.CompareTo(41f), Is.EqualTo(1)); + Assert.That(v.CompareTo(41d), Is.EqualTo(1)); - Assert.AreEqual(-1, v.CompareTo(43f)); - Assert.AreEqual(-1, v.CompareTo(43d)); + Assert.That(v.CompareTo(43f), Is.EqualTo(-1)); + Assert.That(v.CompareTo(43d), Is.EqualTo(-1)); } [Test] @@ -147,11 +135,11 @@ public void Equals() { var v = new PyFloat(42); - Assert.IsTrue(v.Equals(42f)); - Assert.IsTrue(v.Equals(42d)); + Assert.That(v.Equals(42f), Is.True); + Assert.That(v.Equals(42d), Is.True); - Assert.IsFalse(v.Equals(41f)); - Assert.IsFalse(v.Equals(41d)); + Assert.That(v.Equals(41f), Is.False); + Assert.That(v.Equals(41d), Is.False); } } } diff --git a/src/embed_tests/TestPyInt.cs b/src/embed_tests/TestPyInt.cs index d2767e664..36319cf1a 100644 --- a/src/embed_tests/TestPyInt.cs +++ b/src/embed_tests/TestPyInt.cs @@ -10,18 +10,6 @@ namespace Python.EmbeddingTest { public class TestPyInt { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - [Test] public void TestCtorInt() { diff --git a/src/embed_tests/TestPyIter.cs b/src/embed_tests/TestPyIter.cs index 7428da979..5da660242 100644 --- a/src/embed_tests/TestPyIter.cs +++ b/src/embed_tests/TestPyIter.cs @@ -9,18 +9,6 @@ namespace Python.EmbeddingTest { class TestPyIter { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - [Test] public void KeepOldObjects() { diff --git a/src/embed_tests/TestPyList.cs b/src/embed_tests/TestPyList.cs index eee129f2d..a380f0b2d 100644 --- a/src/embed_tests/TestPyList.cs +++ b/src/embed_tests/TestPyList.cs @@ -7,18 +7,6 @@ namespace Python.EmbeddingTest { public class TestPyList { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - [Test] public void TestStringIsListType() { diff --git a/src/embed_tests/TestPyNumber.cs b/src/embed_tests/TestPyNumber.cs index 0261c15c1..d8e275521 100644 --- a/src/embed_tests/TestPyNumber.cs +++ b/src/embed_tests/TestPyNumber.cs @@ -6,18 +6,6 @@ namespace Python.EmbeddingTest { public class TestPyNumber { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - [Test] public void IsNumberTypeTrue() { diff --git a/src/embed_tests/TestPyObject.cs b/src/embed_tests/TestPyObject.cs index 2f27eba1b..f762b94e9 100644 --- a/src/embed_tests/TestPyObject.cs +++ b/src/embed_tests/TestPyObject.cs @@ -8,18 +8,6 @@ namespace Python.EmbeddingTest { public class TestPyObject { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - [Test] public void TestGetDynamicMemberNames() { diff --git a/src/embed_tests/TestPySequence.cs b/src/embed_tests/TestPySequence.cs index dc35a2633..339ea1e83 100644 --- a/src/embed_tests/TestPySequence.cs +++ b/src/embed_tests/TestPySequence.cs @@ -6,18 +6,6 @@ namespace Python.EmbeddingTest { public class TestPySequence { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - [Test] public void TestIsSequenceTrue() { diff --git a/src/embed_tests/TestPyString.cs b/src/embed_tests/TestPyString.cs index 35c6339ee..a1fdd6079 100644 --- a/src/embed_tests/TestPyString.cs +++ b/src/embed_tests/TestPyString.cs @@ -6,18 +6,6 @@ namespace Python.EmbeddingTest { public class TestPyString { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - [Test] public void TestStringCtor() { diff --git a/src/embed_tests/TestPyTuple.cs b/src/embed_tests/TestPyTuple.cs index 5d76116aa..3a3fbf2a0 100644 --- a/src/embed_tests/TestPyTuple.cs +++ b/src/embed_tests/TestPyTuple.cs @@ -6,18 +6,6 @@ namespace Python.EmbeddingTest { public class TestPyTuple { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - /// /// Test IsTupleType without having to Initialize a tuple. /// PyTuple constructor use IsTupleType. This decouples the tests. diff --git a/src/embed_tests/TestPyType.cs b/src/embed_tests/TestPyType.cs index d98dfda2e..c29032a8a 100644 --- a/src/embed_tests/TestPyType.cs +++ b/src/embed_tests/TestPyType.cs @@ -10,18 +10,6 @@ namespace Python.EmbeddingTest { public class TestPyType { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - [Test] public void CanCreateHeapType() { diff --git a/src/embed_tests/TestPyWith.cs b/src/embed_tests/TestPyWith.cs index d1c9aac28..fbce811da 100644 --- a/src/embed_tests/TestPyWith.cs +++ b/src/embed_tests/TestPyWith.cs @@ -6,18 +6,6 @@ namespace Python.EmbeddingTest { public class TestPyWith { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - /// /// Test that exception is raised in context manager that ignores it. /// diff --git a/src/embed_tests/TestPythonEngineProperties.cs b/src/embed_tests/TestPythonEngineProperties.cs index be91d7f45..485931cfb 100644 --- a/src/embed_tests/TestPythonEngineProperties.cs +++ b/src/embed_tests/TestPythonEngineProperties.cs @@ -9,7 +9,6 @@ public class TestPythonEngineProperties [Test] public static void GetBuildinfoDoesntCrash() { - PythonEngine.Initialize(); using (Py.GIL()) { string s = PythonEngine.BuildInfo; @@ -22,7 +21,6 @@ public static void GetBuildinfoDoesntCrash() [Test] public static void GetCompilerDoesntCrash() { - PythonEngine.Initialize(); using (Py.GIL()) { string s = PythonEngine.Compiler; @@ -36,7 +34,6 @@ public static void GetCompilerDoesntCrash() [Test] public static void GetCopyrightDoesntCrash() { - PythonEngine.Initialize(); using (Py.GIL()) { string s = PythonEngine.Copyright; @@ -49,7 +46,6 @@ public static void GetCopyrightDoesntCrash() [Test] public static void GetPlatformDoesntCrash() { - PythonEngine.Initialize(); using (Py.GIL()) { string s = PythonEngine.Platform; @@ -62,7 +58,6 @@ public static void GetPlatformDoesntCrash() [Test] public static void GetVersionDoesntCrash() { - PythonEngine.Initialize(); using (Py.GIL()) { string s = PythonEngine.Version; @@ -75,21 +70,17 @@ public static void GetVersionDoesntCrash() [Test] public static void GetPythonPathDefault() { - PythonEngine.Initialize(); string s = PythonEngine.PythonPath; StringAssert.Contains("python", s.ToLower()); - PythonEngine.Shutdown(); } [Test] public static void GetProgramNameDefault() { - PythonEngine.Initialize(); string s = PythonEngine.ProgramName; Assert.NotNull(s); - PythonEngine.Shutdown(); } /// @@ -101,134 +92,9 @@ public static void GetPythonHomeDefault() { string envPythonHome = Environment.GetEnvironmentVariable("PYTHONHOME") ?? ""; - PythonEngine.Initialize(); string enginePythonHome = PythonEngine.PythonHome; Assert.AreEqual(envPythonHome, enginePythonHome); - PythonEngine.Shutdown(); - } - - [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/TestPythonException.cs b/src/embed_tests/TestPythonException.cs index a248b6a1f..91a412749 100644 --- a/src/embed_tests/TestPythonException.cs +++ b/src/embed_tests/TestPythonException.cs @@ -6,18 +6,6 @@ namespace Python.EmbeddingTest { public class TestPythonException { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - [Test] public void TestMessage() { diff --git a/src/embed_tests/pyimport.cs b/src/embed_tests/pyimport.cs index b828d5315..c774af345 100644 --- a/src/embed_tests/pyimport.cs +++ b/src/embed_tests/pyimport.cs @@ -21,28 +21,29 @@ namespace Python.EmbeddingTest /// public class PyImportTest { + string TestPath; + [OneTimeSetUp] public void SetUp() { - PythonEngine.Initialize(); - /* Append the tests directory to sys.path * using reflection to circumvent the private * modifiers placed on most Runtime methods. */ - string testPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "fixtures"); - TestContext.Out.WriteLine(testPath); + TestPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "fixtures"); + TestContext.Out.WriteLine(TestPath); - using var str = Runtime.Runtime.PyString_FromString(testPath); - Assert.IsFalse(str.IsNull()); + using var str = Runtime.Runtime.PyString_FromString(TestPath); + Assert.That(str.IsNull(), Is.False); BorrowedReference path = Runtime.Runtime.PySys_GetObject("path"); - Assert.IsFalse(path.IsNull); + Assert.That(path.IsNull, Is.False); Runtime.Runtime.PyList_Append(path, str.Borrow()); } [OneTimeTearDown] public void Dispose() { - PythonEngine.Shutdown(); + using var _ = Py.GIL(); + Py.Import("sys").GetAttr("path").InvokeMethod("remove", new PyString(TestPath)); } /// @@ -89,7 +90,7 @@ public void BadAssembly() path = @"C:\Windows\System32\kernel32.dll"; } - Assert.IsTrue(File.Exists(path), $"Test DLL {path} does not exist!"); + Assert.That(File.Exists(path), Is.True, $"Test DLL {path} does not exist!"); string code = $@" import clr diff --git a/src/embed_tests/pyrunstring.cs b/src/embed_tests/pyrunstring.cs index 57c133c00..fb1302800 100644 --- a/src/embed_tests/pyrunstring.cs +++ b/src/embed_tests/pyrunstring.cs @@ -6,26 +6,14 @@ namespace Python.EmbeddingTest { public class RunStringTest { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - [Test] public void TestRunSimpleString() { int aa = PythonEngine.RunSimpleString("import sys"); - Assert.AreEqual(0, aa); + Assert.That(aa, Is.EqualTo(0)); int bb = PythonEngine.RunSimpleString("import 1234"); - Assert.AreEqual(-1, bb); + Assert.That(bb, Is.EqualTo(-1)); } [Test] @@ -39,7 +27,7 @@ public void TestEval() object b = PythonEngine.Eval("sys.attr1 + a + 1", null, locals) .AsManagedObject(typeof(int)); - Assert.AreEqual(111, b); + Assert.That(b, Is.EqualTo(111)); } [Test] @@ -53,7 +41,7 @@ public void TestExec() PythonEngine.Exec("c = sys.attr1 + a + 1", null, locals); object c = locals.GetItem("c").AsManagedObject(typeof(int)); - Assert.AreEqual(111, c); + Assert.That(c, Is.EqualTo(111)); } [Test] diff --git a/src/python_tests_runner/Python.PythonTestsRunner.csproj b/src/python_tests_runner/Python.PythonTestsRunner.csproj index e55c1af37..5fc55d158 100644 --- a/src/python_tests_runner/Python.PythonTestsRunner.csproj +++ b/src/python_tests_runner/Python.PythonTestsRunner.csproj @@ -23,4 +23,12 @@ + + + + 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"); }