diff --git a/src/embed_tests/BaseFixture.cs b/src/embed_tests/BaseFixture.cs new file mode 100644 index 000000000..e09172bce --- /dev/null +++ b/src/embed_tests/BaseFixture.cs @@ -0,0 +1,20 @@ +using NUnit.Framework; +using Python.Runtime; + +namespace Python.EmbeddingTest; + +public class BaseFixture +{ + [OneTimeSetUp] + public void BaseSetup() + { + PythonEngine.Initialize(); + } + + [OneTimeTearDown] + public void BaseTearDown() + { + PythonEngine.Shutdown(allowReload: true); + PyObjectConversions.Reset(); + } +} diff --git a/src/embed_tests/CallableObject.cs b/src/embed_tests/CallableObject.cs index 8466f5ad8..716a75259 100644 --- a/src/embed_tests/CallableObject.cs +++ b/src/embed_tests/CallableObject.cs @@ -5,83 +5,85 @@ using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class CallableObject : BaseFixture { - public class CallableObject + IPythonBaseTypeProvider _provider; + + [OneTimeSetUp] + public void SetUp() { - [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()); - } + using var locals = new PyDict(); + PythonEngine.Exec(CallViaInheritance.BaseClassSource, locals: locals); + CustomBaseTypeProvider.BaseClass = new PyType(locals[CallViaInheritance.BaseClassName]); + _provider = new CustomBaseTypeProvider(); + PythonEngine.InteropConfiguration.PythonBaseTypeProviders.Add(_provider); + } - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - [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())); - } - [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())); - } + [OneTimeTearDown] + public void TearDown() + { + PythonEngine.InteropConfiguration.PythonBaseTypeProviders.Remove(_provider); + } - [Test] - public void CanOverwriteCall() - { - var callViaInheritance = new CallViaInheritance(); - using var scope = Py.CreateScope(); - scope.Set("o", callViaInheritance); - 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); - } + [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())); + } + [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())); + } - class Doubler - { - public int __call__(int arg) => 2 * arg; - } + [Test] + public void CanOverwriteCall() + { + var callViaInheritance = new CallViaInheritance(); + using var scope = Py.CreateScope(); + scope.Set("o", callViaInheritance); + 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); + } + + class Doubler + { + public int __call__(int arg) => 2 * arg; + } - class DerivedDoubler : Doubler { } + class DerivedDoubler : Doubler { } - class CallViaInheritance - { - public const string BaseClassName = "Forwarder"; - public static readonly string BaseClassSource = $@" + class CallViaInheritance + { + public const string BaseClassName = "Forwarder"; + public static readonly string BaseClassSource = $@" class MyCallableBase: def __call__(self, val): return self.Call(val) class {BaseClassName}(MyCallableBase): pass "; - public int Call(int arg) => 3 * arg; - } + public int Call(int arg) => 3 * arg; + } - class CustomBaseTypeProvider : IPythonBaseTypeProvider - { - internal static PyType BaseClass; + class CustomBaseTypeProvider : IPythonBaseTypeProvider + { + internal static PyType BaseClass; - public IEnumerable GetBaseTypes(Type type, IList existingBases) - { - Assert.Greater(BaseClass.Refcount, 0); - return type != typeof(CallViaInheritance) - ? existingBases - : new[] { BaseClass }; - } + public IEnumerable GetBaseTypes(Type type, IList existingBases) + { + Assert.Greater(BaseClass.Refcount, 0); + return type != typeof(CallViaInheritance) + ? existingBases + : new[] { BaseClass }; } } } diff --git a/src/embed_tests/ClassManagerTests.cs b/src/embed_tests/ClassManagerTests.cs index 72025a28b..a2d3cb2bb 100644 --- a/src/embed_tests/ClassManagerTests.cs +++ b/src/embed_tests/ClassManagerTests.cs @@ -2,39 +2,26 @@ using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class ClassManagerTests : BaseFixture { - public class ClassManagerTests + [Test] + public void NestedClassDerivingFromParent() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - - [Test] - public void NestedClassDerivingFromParent() - { - var f = new NestedTestContainer().ToPython(); - f.GetAttr(nameof(NestedTestContainer.Bar)); - } + var f = new NestedTestContainer().ToPython(); + f.GetAttr(nameof(NestedTestContainer.Bar)); } +} - public class NestedTestParent +public class NestedTestParent +{ + public class Nested : NestedTestParent { - public class Nested : NestedTestParent - { - } } +} - public class NestedTestContainer - { - public NestedTestParent Bar = new NestedTestParent.Nested(); - } +public class NestedTestContainer +{ + public NestedTestParent Bar = new NestedTestParent.Nested(); } diff --git a/src/embed_tests/CodecGroups.cs b/src/embed_tests/CodecGroups.cs index 689e5b24c..492516594 100644 --- a/src/embed_tests/CodecGroups.cs +++ b/src/embed_tests/CodecGroups.cs @@ -1,132 +1,119 @@ -namespace Python.EmbeddingTest -{ - using System; - using System.Linq; - using NUnit.Framework; - using Python.Runtime; - using Python.Runtime.Codecs; - - public class CodecGroups - { - [Test] - public void GetEncodersByType() - { - var encoder1 = new ObjectToEncoderInstanceEncoder(); - var encoder2 = new ObjectToEncoderInstanceEncoder(); - var group = new EncoderGroup { - new ObjectToEncoderInstanceEncoder>(), - encoder1, - encoder2, - }; +using System; +using System.Linq; +using NUnit.Framework; +using Python.Runtime; +using Python.Runtime.Codecs; - var got = group.GetEncoders(typeof(Uri)).ToArray(); - CollectionAssert.AreEqual(new[]{encoder1, encoder2}, got); - } +namespace Python.EmbeddingTest; - [Test] - public void CanEncode() - { - var group = new EncoderGroup { - new ObjectToEncoderInstanceEncoder>(), - new ObjectToEncoderInstanceEncoder(), - }; +public class CodecGroups : BaseFixture +{ + [Test] + public void GetEncodersByType() + { + var encoder1 = new ObjectToEncoderInstanceEncoder(); + var encoder2 = new ObjectToEncoderInstanceEncoder(); + var group = new EncoderGroup { + new ObjectToEncoderInstanceEncoder>(), + encoder1, + encoder2, + }; - Assert.IsTrue(group.CanEncode(typeof(Tuple))); - Assert.IsTrue(group.CanEncode(typeof(Uri))); - Assert.IsFalse(group.CanEncode(typeof(string))); - } + var got = group.GetEncoders(typeof(Uri)).ToArray(); + CollectionAssert.AreEqual(new[]{encoder1, encoder2}, got); + } - [Test] - public void Encodes() - { - var encoder0 = new ObjectToEncoderInstanceEncoder>(); - var encoder1 = new ObjectToEncoderInstanceEncoder(); - var encoder2 = new ObjectToEncoderInstanceEncoder(); - var group = new EncoderGroup { - encoder0, - encoder1, - encoder2, - }; + [Test] + public void CanEncode() + { + var group = new EncoderGroup { + new ObjectToEncoderInstanceEncoder>(), + new ObjectToEncoderInstanceEncoder(), + }; - var uri = group.TryEncode(new Uri("data:")); - var clrObject = (CLRObject)ManagedType.GetManagedObject(uri); - Assert.AreSame(encoder1, clrObject.inst); - Assert.AreNotSame(encoder2, clrObject.inst); + Assert.IsTrue(group.CanEncode(typeof(Tuple))); + Assert.IsTrue(group.CanEncode(typeof(Uri))); + Assert.IsFalse(group.CanEncode(typeof(string))); + } - var tuple = group.TryEncode(Tuple.Create(1)); - clrObject = (CLRObject)ManagedType.GetManagedObject(tuple); - Assert.AreSame(encoder0, clrObject.inst); - } + [Test] + public void Encodes() + { + var encoder0 = new ObjectToEncoderInstanceEncoder>(); + var encoder1 = new ObjectToEncoderInstanceEncoder(); + var encoder2 = new ObjectToEncoderInstanceEncoder(); + var group = new EncoderGroup { + encoder0, + encoder1, + encoder2, + }; - [Test] - public void GetDecodersByTypes() - { - var pyint = new PyInt(10).GetPythonType(); - var pyfloat = new PyFloat(10).GetPythonType(); - var pystr = new PyString("world").GetPythonType(); - var decoder1 = new DecoderReturningPredefinedValue(pyint, decodeResult: 42); - var decoder2 = new DecoderReturningPredefinedValue(pyfloat, decodeResult: "atad:"); - var group = new DecoderGroup { - decoder1, - decoder2, - }; + var uri = group.TryEncode(new Uri("data:")); + var clrObject = (CLRObject)ManagedType.GetManagedObject(uri); + Assert.AreSame(encoder1, clrObject.inst); + Assert.AreNotSame(encoder2, clrObject.inst); - var decoder = group.GetDecoder(pyfloat, typeof(string)); - Assert.AreSame(decoder2, decoder); - decoder = group.GetDecoder(pystr, typeof(string)); - Assert.IsNull(decoder); - decoder = group.GetDecoder(pyint, typeof(long)); - Assert.AreSame(decoder1, decoder); - } - [Test] - public void CanDecode() - { - var pyint = new PyInt(10).GetPythonType(); - var pyfloat = new PyFloat(10).GetPythonType(); - var pystr = new PyString("world").GetPythonType(); - var decoder1 = new DecoderReturningPredefinedValue(pyint, decodeResult: 42); - var decoder2 = new DecoderReturningPredefinedValue(pyfloat, decodeResult: "atad:"); - var group = new DecoderGroup { - decoder1, - decoder2, - }; + var tuple = group.TryEncode(Tuple.Create(1)); + clrObject = (CLRObject)ManagedType.GetManagedObject(tuple); + Assert.AreSame(encoder0, clrObject.inst); + } - 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))); - } + [Test] + public void GetDecodersByTypes() + { + var pyint = new PyInt(10).GetPythonType(); + var pyfloat = new PyFloat(10).GetPythonType(); + var pystr = new PyString("world").GetPythonType(); + var decoder1 = new DecoderReturningPredefinedValue(pyint, decodeResult: 42); + var decoder2 = new DecoderReturningPredefinedValue(pyfloat, decodeResult: "atad:"); + var group = new DecoderGroup { + decoder1, + decoder2, + }; - [Test] - public void Decodes() - { - var pyint = new PyInt(10).GetPythonType(); - var pyfloat = new PyFloat(10).GetPythonType(); - var decoder1 = new DecoderReturningPredefinedValue(pyint, decodeResult: 42); - var decoder2 = new DecoderReturningPredefinedValue(pyfloat, decodeResult: "atad:"); - var group = new DecoderGroup { - decoder1, - decoder2, - }; + var decoder = group.GetDecoder(pyfloat, typeof(string)); + Assert.AreSame(decoder2, decoder); + decoder = group.GetDecoder(pystr, typeof(string)); + Assert.IsNull(decoder); + decoder = group.GetDecoder(pyint, typeof(long)); + Assert.AreSame(decoder1, decoder); + } + [Test] + public void CanDecode() + { + var pyint = new PyInt(10).GetPythonType(); + var pyfloat = new PyFloat(10).GetPythonType(); + var pystr = new PyString("world").GetPythonType(); + var decoder1 = new DecoderReturningPredefinedValue(pyint, decodeResult: 42); + var decoder2 = new DecoderReturningPredefinedValue(pyfloat, decodeResult: "atad:"); + var group = new DecoderGroup { + decoder1, + 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.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.IsFalse(group.TryDecode(new PyInt(10), out int _)); - } + [Test] + public void Decodes() + { + var pyint = new PyInt(10).GetPythonType(); + var pyfloat = new PyFloat(10).GetPythonType(); + var decoder1 = new DecoderReturningPredefinedValue(pyint, decodeResult: 42); + var decoder2 = new DecoderReturningPredefinedValue(pyfloat, decodeResult: "atad:"); + var group = new DecoderGroup { + decoder1, + decoder2, + }; - [SetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } + 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); - [TearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } + Assert.IsFalse(group.TryDecode(new PyInt(10), out int _)); } } diff --git a/src/embed_tests/Codecs.cs b/src/embed_tests/Codecs.cs index 9b764d43f..a2149d0b2 100644 --- a/src/embed_tests/Codecs.cs +++ b/src/embed_tests/Codecs.cs @@ -1,294 +1,289 @@ -namespace Python.EmbeddingTest { - using System; - using System.Collections.Generic; - using System.Linq; - using NUnit.Framework; - using Python.Runtime; - using Python.Runtime.Codecs; - - public class Codecs +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Python.Runtime; +using Python.Runtime.Codecs; + +namespace Python.EmbeddingTest; + +public class Codecs : BaseFixture +{ + [SetUp] + public void SetUp() { - [SetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } + PyObjectConversions.Reset(); + } - [TearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } + [Test] + public void TupleConversionsGeneric() + { + TupleConversionsGeneric, ValueTuple>(); + } - [Test] - public void TupleConversionsGeneric() + static void TupleConversionsGeneric() + { + TupleCodec.Register(); + var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object()); + T restored = default; + using (var scope = Py.CreateScope()) { - TupleConversionsGeneric, ValueTuple>(); + void Accept(T value) => restored = value; + using var accept = new Action(Accept).ToPython(); + scope.Set(nameof(tuple), tuple); + scope.Set(nameof(accept), accept); + scope.Exec($"{nameof(accept)}({nameof(tuple)})"); + Assert.AreEqual(expected: tuple, actual: restored); } + } - static void TupleConversionsGeneric() + [Test] + public void TupleConversionsObject() + { + TupleConversionsObject, ValueTuple>(); + } + static void TupleConversionsObject() + { + TupleCodec.Register(); + var tuple = Activator.CreateInstance(typeof(T), 42.0, "42", new object()); + T restored = default; + using (var scope = Py.CreateScope()) { - TupleCodec.Register(); - var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object()); - T restored = default; - using (var scope = Py.CreateScope()) - { - void Accept(T value) => restored = value; - using var accept = new Action(Accept).ToPython(); - scope.Set(nameof(tuple), tuple); - scope.Set(nameof(accept), accept); - scope.Exec($"{nameof(accept)}({nameof(tuple)})"); - Assert.AreEqual(expected: tuple, actual: restored); - } + void Accept(object value) => restored = (T)value; + using var accept = new Action(Accept).ToPython(); + scope.Set(nameof(tuple), tuple); + scope.Set(nameof(accept), accept); + scope.Exec($"{nameof(accept)}({nameof(tuple)})"); + Assert.AreEqual(expected: tuple, actual: restored); } + } - [Test] - public void TupleConversionsObject() - { - TupleConversionsObject, ValueTuple>(); - } - static void TupleConversionsObject() - { - TupleCodec.Register(); - var tuple = Activator.CreateInstance(typeof(T), 42.0, "42", new object()); - T restored = default; - using (var scope = Py.CreateScope()) - { - void Accept(object value) => restored = (T)value; - using var accept = new Action(Accept).ToPython(); - scope.Set(nameof(tuple), tuple); - scope.Set(nameof(accept), accept); - scope.Exec($"{nameof(accept)}({nameof(tuple)})"); - Assert.AreEqual(expected: tuple, actual: restored); - } - } + [Test] + public void TupleRoundtripObject() + { + TupleRoundtripObject, ValueTuple>(); + } + static void TupleRoundtripObject() + { + var tuple = Activator.CreateInstance(typeof(T), 42.0, "42", new object()); + using var pyTuple = TupleCodec.Instance.TryEncode(tuple); + Assert.IsTrue(TupleCodec.Instance.TryDecode(pyTuple, out object restored)); + Assert.AreEqual(expected: tuple, actual: restored); + } - [Test] - public void TupleRoundtripObject() - { - TupleRoundtripObject, ValueTuple>(); - } - static void TupleRoundtripObject() - { - var tuple = Activator.CreateInstance(typeof(T), 42.0, "42", new object()); - using var pyTuple = TupleCodec.Instance.TryEncode(tuple); - Assert.IsTrue(TupleCodec.Instance.TryDecode(pyTuple, out object restored)); - Assert.AreEqual(expected: tuple, actual: restored); - } + [Test] + public void TupleRoundtripGeneric() + { + TupleRoundtripGeneric, ValueTuple>(); + } - [Test] - public void TupleRoundtripGeneric() - { - TupleRoundtripGeneric, ValueTuple>(); - } + static void TupleRoundtripGeneric() + { + var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object()); + using var pyTuple = TupleCodec.Instance.TryEncode(tuple); + Assert.IsTrue(TupleCodec.Instance.TryDecode(pyTuple, out T restored)); + Assert.AreEqual(expected: tuple, actual: restored); + } - static void TupleRoundtripGeneric() - { - var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object()); - using var pyTuple = TupleCodec.Instance.TryEncode(tuple); - Assert.IsTrue(TupleCodec.Instance.TryDecode(pyTuple, out T restored)); - Assert.AreEqual(expected: tuple, actual: restored); - } + static PyObject GetPythonIterable() => PythonEngine.Eval("map(lambda x: x, [1,2,3])"); - static PyObject GetPythonIterable() => PythonEngine.Eval("map(lambda x: x, [1,2,3])"); + [Test] + public void ListDecoderTest() + { + var codec = ListDecoder.Instance; + var items = new List() { new PyInt(1), new PyInt(2), new PyInt(3) }; + + using var pyList = new PyList(items.ToArray()); + + using var pyListType = pyList.GetPythonType(); + Assert.IsTrue(codec.CanDecode(pyListType, typeof(IList))); + Assert.IsTrue(codec.CanDecode(pyListType, typeof(IList))); + Assert.IsFalse(codec.CanDecode(pyListType, typeof(System.Collections.IEnumerable))); + Assert.IsFalse(codec.CanDecode(pyListType, typeof(IEnumerable))); + Assert.IsFalse(codec.CanDecode(pyListType, typeof(ICollection))); + Assert.IsFalse(codec.CanDecode(pyListType, typeof(bool))); + + //we'd have to copy into a list instance to do this, it would not be lossless. + //lossy converters can be implemented outside of the python.net core library + Assert.IsFalse(codec.CanDecode(pyListType, typeof(List))); + + //convert to list of int + IList intList = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out intList); }); + CollectionAssert.AreEqual(intList, new List { 1, 2, 3 }); + + //convert to list of string. This will not work. + //The ListWrapper class will throw a python exception when it tries to access any element. + //TryDecode is a lossless conversion so there will be no exception at that point + //interestingly, since the size of the python list can be queried without any conversion, + //the IList will report a Count of 3. + IList stringList = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out stringList); }); + Assert.AreEqual(stringList.Count, 3); + Assert.Throws(typeof(InvalidCastException), () => { var x = stringList[0]; }); + + //can't convert python iterable to list (this will require a copy which isn't lossless) + using var foo = GetPythonIterable(); + using var fooType = foo.GetPythonType(); + Assert.IsFalse(codec.CanDecode(fooType, typeof(IList))); + } - [Test] - public void ListDecoderTest() - { - var codec = ListDecoder.Instance; - var items = new List() { new PyInt(1), new PyInt(2), new PyInt(3) }; - - using var pyList = new PyList(items.ToArray()); - - using var pyListType = pyList.GetPythonType(); - Assert.IsTrue(codec.CanDecode(pyListType, typeof(IList))); - Assert.IsTrue(codec.CanDecode(pyListType, typeof(IList))); - Assert.IsFalse(codec.CanDecode(pyListType, typeof(System.Collections.IEnumerable))); - Assert.IsFalse(codec.CanDecode(pyListType, typeof(IEnumerable))); - Assert.IsFalse(codec.CanDecode(pyListType, typeof(ICollection))); - Assert.IsFalse(codec.CanDecode(pyListType, typeof(bool))); - - //we'd have to copy into a list instance to do this, it would not be lossless. - //lossy converters can be implemented outside of the python.net core library - Assert.IsFalse(codec.CanDecode(pyListType, typeof(List))); - - //convert to list of int - IList intList = null; - Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out intList); }); - CollectionAssert.AreEqual(intList, new List { 1, 2, 3 }); - - //convert to list of string. This will not work. - //The ListWrapper class will throw a python exception when it tries to access any element. - //TryDecode is a lossless conversion so there will be no exception at that point - //interestingly, since the size of the python list can be queried without any conversion, - //the IList will report a Count of 3. - IList stringList = null; - Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out stringList); }); - Assert.AreEqual(stringList.Count, 3); - Assert.Throws(typeof(InvalidCastException), () => { var x = stringList[0]; }); - - //can't convert python iterable to list (this will require a copy which isn't lossless) - using var foo = GetPythonIterable(); - using var fooType = foo.GetPythonType(); - Assert.IsFalse(codec.CanDecode(fooType, typeof(IList))); - } + [Test] + public void SequenceDecoderTest() + { + var codec = SequenceDecoder.Instance; + var items = new List() { new PyInt(1), new PyInt(2), new PyInt(3) }; + + //SequenceConverter can only convert to any ICollection + using var pyList = new PyList(items.ToArray()); + using var listType = pyList.GetPythonType(); + //it can convert a PyList, since PyList satisfies the python sequence protocol + + Assert.IsFalse(codec.CanDecode(listType, typeof(bool))); + Assert.IsFalse(codec.CanDecode(listType, typeof(IList))); + Assert.IsFalse(codec.CanDecode(listType, typeof(System.Collections.IEnumerable))); + Assert.IsFalse(codec.CanDecode(listType, typeof(IEnumerable))); + + Assert.IsTrue(codec.CanDecode(listType, typeof(ICollection))); + Assert.IsTrue(codec.CanDecode(listType, typeof(ICollection))); + Assert.IsTrue(codec.CanDecode(listType, typeof(ICollection))); + + //convert to collection of int + ICollection intCollection = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out intCollection); }); + CollectionAssert.AreEqual(intCollection, new List { 1, 2, 3 }); + + //no python exception should have occurred during the above conversion and check + Runtime.Runtime.CheckExceptionOccurred(); + + //convert to collection of string. This will not work. + //The SequenceWrapper class will throw a python exception when it tries to access any element. + //TryDecode is a lossless conversion so there will be no exception at that point + //interestingly, since the size of the python sequence can be queried without any conversion, + //the IList will report a Count of 3. + ICollection stringCollection = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out stringCollection); }); + Assert.AreEqual(3, stringCollection.Count()); + Assert.Throws(typeof(InvalidCastException), () => { + string[] array = new string[3]; + stringCollection.CopyTo(array, 0); + }); + + Runtime.Runtime.CheckExceptionOccurred(); + + //can't convert python iterable to collection (this will require a copy which isn't lossless) + //python iterables do not satisfy the python sequence protocol + var foo = GetPythonIterable(); + var fooType = foo.GetPythonType(); + Assert.IsFalse(codec.CanDecode(fooType, typeof(ICollection))); + + //python tuples do satisfy the python sequence protocol + var pyTuple = new PyTuple(items.ToArray()); + var pyTupleType = pyTuple.GetPythonType(); + + Assert.IsTrue(codec.CanDecode(pyTupleType, typeof(ICollection))); + Assert.IsTrue(codec.CanDecode(pyTupleType, typeof(ICollection))); + Assert.IsTrue(codec.CanDecode(pyTupleType, typeof(ICollection))); + + //convert to collection of int + ICollection intCollection2 = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyTuple, out intCollection2); }); + CollectionAssert.AreEqual(intCollection2, new List { 1, 2, 3 }); + + //no python exception should have occurred during the above conversion and check + Runtime.Runtime.CheckExceptionOccurred(); + + //convert to collection of string. This will not work. + //The SequenceWrapper class will throw a python exception when it tries to access any element. + //TryDecode is a lossless conversion so there will be no exception at that point + //interestingly, since the size of the python sequence can be queried without any conversion, + //the IList will report a Count of 3. + ICollection stringCollection2 = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyTuple, out stringCollection2); }); + Assert.AreEqual(3, stringCollection2.Count()); + Assert.Throws(typeof(InvalidCastException), () => { + string[] array = new string[3]; + stringCollection2.CopyTo(array, 0); + }); + + Runtime.Runtime.CheckExceptionOccurred(); - [Test] - public void SequenceDecoderTest() - { - var codec = SequenceDecoder.Instance; - var items = new List() { new PyInt(1), new PyInt(2), new PyInt(3) }; - - //SequenceConverter can only convert to any ICollection - using var pyList = new PyList(items.ToArray()); - using var listType = pyList.GetPythonType(); - //it can convert a PyList, since PyList satisfies the python sequence protocol - - Assert.IsFalse(codec.CanDecode(listType, typeof(bool))); - Assert.IsFalse(codec.CanDecode(listType, typeof(IList))); - Assert.IsFalse(codec.CanDecode(listType, typeof(System.Collections.IEnumerable))); - Assert.IsFalse(codec.CanDecode(listType, typeof(IEnumerable))); - - Assert.IsTrue(codec.CanDecode(listType, typeof(ICollection))); - Assert.IsTrue(codec.CanDecode(listType, typeof(ICollection))); - Assert.IsTrue(codec.CanDecode(listType, typeof(ICollection))); - - //convert to collection of int - ICollection intCollection = null; - Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out intCollection); }); - CollectionAssert.AreEqual(intCollection, new List { 1, 2, 3 }); - - //no python exception should have occurred during the above conversion and check - Runtime.CheckExceptionOccurred(); - - //convert to collection of string. This will not work. - //The SequenceWrapper class will throw a python exception when it tries to access any element. - //TryDecode is a lossless conversion so there will be no exception at that point - //interestingly, since the size of the python sequence can be queried without any conversion, - //the IList will report a Count of 3. - ICollection stringCollection = null; - Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out stringCollection); }); - Assert.AreEqual(3, stringCollection.Count()); - Assert.Throws(typeof(InvalidCastException), () => { - string[] array = new string[3]; - stringCollection.CopyTo(array, 0); - }); - - Runtime.CheckExceptionOccurred(); - - //can't convert python iterable to collection (this will require a copy which isn't lossless) - //python iterables do not satisfy the python sequence protocol - var foo = GetPythonIterable(); - var fooType = foo.GetPythonType(); - Assert.IsFalse(codec.CanDecode(fooType, typeof(ICollection))); - - //python tuples do satisfy the python sequence protocol - var pyTuple = new PyTuple(items.ToArray()); - var pyTupleType = pyTuple.GetPythonType(); - - Assert.IsTrue(codec.CanDecode(pyTupleType, typeof(ICollection))); - Assert.IsTrue(codec.CanDecode(pyTupleType, typeof(ICollection))); - Assert.IsTrue(codec.CanDecode(pyTupleType, typeof(ICollection))); - - //convert to collection of int - ICollection intCollection2 = null; - Assert.DoesNotThrow(() => { codec.TryDecode(pyTuple, out intCollection2); }); - CollectionAssert.AreEqual(intCollection2, new List { 1, 2, 3 }); - - //no python exception should have occurred during the above conversion and check - Runtime.CheckExceptionOccurred(); - - //convert to collection of string. This will not work. - //The SequenceWrapper class will throw a python exception when it tries to access any element. - //TryDecode is a lossless conversion so there will be no exception at that point - //interestingly, since the size of the python sequence can be queried without any conversion, - //the IList will report a Count of 3. - ICollection stringCollection2 = null; - Assert.DoesNotThrow(() => { codec.TryDecode(pyTuple, out stringCollection2); }); - Assert.AreEqual(3, stringCollection2.Count()); - Assert.Throws(typeof(InvalidCastException), () => { - string[] array = new string[3]; - stringCollection2.CopyTo(array, 0); - }); - - Runtime.CheckExceptionOccurred(); + } - } + [Test] + public void IterableDecoderTest() + { + var codec = IterableDecoder.Instance; + var items = new List() { new PyInt(1), new PyInt(2), new PyInt(3) }; - [Test] - public void IterableDecoderTest() - { - var codec = IterableDecoder.Instance; - var items = new List() { new PyInt(1), new PyInt(2), new PyInt(3) }; - - var pyList = new PyList(items.ToArray()); - var pyListType = pyList.GetPythonType(); - Assert.IsFalse(codec.CanDecode(pyListType, typeof(IList))); - Assert.IsTrue(codec.CanDecode(pyListType, typeof(System.Collections.IEnumerable))); - Assert.IsTrue(codec.CanDecode(pyListType, typeof(IEnumerable))); - Assert.IsFalse(codec.CanDecode(pyListType, typeof(ICollection))); - Assert.IsFalse(codec.CanDecode(pyListType, typeof(bool))); - - //ensure a PyList can be converted to a plain IEnumerable - System.Collections.IEnumerable plainEnumerable1 = null; - Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out plainEnumerable1); }); - CollectionAssert.AreEqual(plainEnumerable1.Cast().Select(i => i.ToInt32()), new List { 1, 2, 3 }); - - //can convert to any generic ienumerable. If the type is not assignable from the python element - //it will lead to an empty iterable when decoding. TODO - should it throw? - Assert.IsTrue(codec.CanDecode(pyListType, typeof(IEnumerable))); - Assert.IsTrue(codec.CanDecode(pyListType, typeof(IEnumerable))); - Assert.IsTrue(codec.CanDecode(pyListType, typeof(IEnumerable))); - - IEnumerable intEnumerable = null; - Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out intEnumerable); }); - CollectionAssert.AreEqual(intEnumerable, new List { 1, 2, 3 }); - - Runtime.CheckExceptionOccurred(); - - IEnumerable doubleEnumerable = null; - Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out doubleEnumerable); }); - CollectionAssert.AreEqual(doubleEnumerable, new List { 1, 2, 3 }); - - Runtime.CheckExceptionOccurred(); - - IEnumerable stringEnumerable = null; - Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out stringEnumerable); }); - - Assert.Throws(typeof(InvalidCastException), () => { - foreach (string item in stringEnumerable) - { - var x = item; - } - }); - Assert.Throws(typeof(InvalidCastException), () => { - stringEnumerable.Count(); - }); - - Runtime.CheckExceptionOccurred(); - - //ensure a python class which implements the iterator protocol can be converter to a plain IEnumerable - var foo = GetPythonIterable(); - var fooType = foo.GetPythonType(); - System.Collections.IEnumerable plainEnumerable2 = null; - Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out plainEnumerable2); }); - CollectionAssert.AreEqual(plainEnumerable2.Cast().Select(i => i.ToInt32()), new List { 1, 2, 3 }); - - //can convert to any generic ienumerable. If the type is not assignable from the python element - //it will be an exception during TryDecode - Assert.IsTrue(codec.CanDecode(fooType, typeof(IEnumerable))); - Assert.IsTrue(codec.CanDecode(fooType, typeof(IEnumerable))); - Assert.IsTrue(codec.CanDecode(fooType, typeof(IEnumerable))); - - Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out intEnumerable); }); - CollectionAssert.AreEqual(intEnumerable, new List { 1, 2, 3 }); - } + var pyList = new PyList(items.ToArray()); + var pyListType = pyList.GetPythonType(); + Assert.IsFalse(codec.CanDecode(pyListType, typeof(IList))); + Assert.IsTrue(codec.CanDecode(pyListType, typeof(System.Collections.IEnumerable))); + Assert.IsTrue(codec.CanDecode(pyListType, typeof(IEnumerable))); + Assert.IsFalse(codec.CanDecode(pyListType, typeof(ICollection))); + Assert.IsFalse(codec.CanDecode(pyListType, typeof(bool))); - // regression for https://github.com/pythonnet/pythonnet/issues/1427 - [Test] - public void PythonRegisteredDecoder_NoStackOverflowOnSystemType() - { - const string PyCode = @" + //ensure a PyList can be converted to a plain IEnumerable + System.Collections.IEnumerable plainEnumerable1 = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out plainEnumerable1); }); + CollectionAssert.AreEqual(plainEnumerable1.Cast().Select(i => i.ToInt32()), new List { 1, 2, 3 }); + + //can convert to any generic ienumerable. If the type is not assignable from the python element + //it will lead to an empty iterable when decoding. TODO - should it throw? + Assert.IsTrue(codec.CanDecode(pyListType, typeof(IEnumerable))); + Assert.IsTrue(codec.CanDecode(pyListType, typeof(IEnumerable))); + Assert.IsTrue(codec.CanDecode(pyListType, typeof(IEnumerable))); + + IEnumerable intEnumerable = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out intEnumerable); }); + CollectionAssert.AreEqual(intEnumerable, new List { 1, 2, 3 }); + + Runtime.Runtime.CheckExceptionOccurred(); + + IEnumerable doubleEnumerable = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out doubleEnumerable); }); + CollectionAssert.AreEqual(doubleEnumerable, new List { 1, 2, 3 }); + + Runtime.Runtime.CheckExceptionOccurred(); + + IEnumerable stringEnumerable = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out stringEnumerable); }); + + Assert.Throws(typeof(InvalidCastException), () => { + foreach (string item in stringEnumerable) + { + var x = item; + } + }); + Assert.Throws(typeof(InvalidCastException), () => { + stringEnumerable.Count(); + }); + + Runtime.Runtime.CheckExceptionOccurred(); + + //ensure a python class which implements the iterator protocol can be converter to a plain IEnumerable + var foo = GetPythonIterable(); + var fooType = foo.GetPythonType(); + System.Collections.IEnumerable plainEnumerable2 = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out plainEnumerable2); }); + CollectionAssert.AreEqual(plainEnumerable2.Cast().Select(i => i.ToInt32()), new List { 1, 2, 3 }); + + //can convert to any generic ienumerable. If the type is not assignable from the python element + //it will be an exception during TryDecode + Assert.IsTrue(codec.CanDecode(fooType, typeof(IEnumerable))); + Assert.IsTrue(codec.CanDecode(fooType, typeof(IEnumerable))); + Assert.IsTrue(codec.CanDecode(fooType, typeof(IEnumerable))); + + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out intEnumerable); }); + CollectionAssert.AreEqual(intEnumerable, new List { 1, 2, 3 }); + } + + // regression for https://github.com/pythonnet/pythonnet/issues/1427 + [Test] + public void PythonRegisteredDecoder_NoStackOverflowOnSystemType() + { + const string PyCode = @" import clr import System from Python.Runtime import PyObjectConversions @@ -306,44 +301,44 @@ def CanEncode(self, clr_type): system_type = list_encoder.GetType()"; - PythonEngine.Exec(PyCode); - } + PythonEngine.Exec(PyCode); + } - const string TestExceptionMessage = "Hello World!"; - [Test] - public void ExceptionEncoded() - { - PyObjectConversions.RegisterEncoder(new ValueErrorCodec()); - void CallMe() => throw new ValueErrorWrapper(TestExceptionMessage); - var callMeAction = new Action(CallMe); - using var scope = Py.CreateScope(); - scope.Exec(@" + const string TestExceptionMessage = "Hello World!"; + [Test] + public void ExceptionEncoded() + { + PyObjectConversions.RegisterEncoder(new ValueErrorCodec()); + void CallMe() => throw new ValueErrorWrapper(TestExceptionMessage); + var callMeAction = new Action(CallMe); + using var scope = Py.CreateScope(); + scope.Exec(@" def call(func): try: func() except ValueError as e: return str(e) "); - var callFunc = scope.Get("call"); - string message = callFunc.Invoke(callMeAction.ToPython()).As(); - Assert.AreEqual(TestExceptionMessage, message); - } + var callFunc = scope.Get("call"); + string message = callFunc.Invoke(callMeAction.ToPython()).As(); + Assert.AreEqual(TestExceptionMessage, message); + } - [Test] - public void ExceptionDecoded() - { - PyObjectConversions.RegisterDecoder(new ValueErrorCodec()); - using var scope = Py.CreateScope(); - var error = Assert.Throws(() - => PythonEngine.Exec($"raise ValueError('{TestExceptionMessage}')")); - Assert.AreEqual(TestExceptionMessage, error.Message); - } + [Test] + public void ExceptionDecoded() + { + PyObjectConversions.RegisterDecoder(new ValueErrorCodec()); + using var scope = Py.CreateScope(); + var error = Assert.Throws(() + => PythonEngine.Exec($"raise ValueError('{TestExceptionMessage}')")); + Assert.AreEqual(TestExceptionMessage, error.Message); + } - [Test] - public void DateTimeDecoded() - { - using var scope = Py.CreateScope(); - scope.Exec(@" + [Test] + public void DateTimeDecoded() + { + using var scope = Py.CreateScope(); + scope.Exec(@" import clr from datetime import datetime @@ -352,173 +347,172 @@ from datetime import datetime DateTimeDecoder.Setup() "); - scope.Exec("Codecs.AcceptsDateTime(datetime(2021, 1, 22))"); - } + scope.Exec("Codecs.AcceptsDateTime(datetime(2021, 1, 22))"); + } - [Test] - public void FloatDerivedDecoded() - { - using var scope = Py.CreateScope(); - scope.Exec(@"class FloatDerived(float): pass"); - using var floatDerived = scope.Eval("FloatDerived"); - var decoder = new DecoderReturningPredefinedValue(floatDerived, 42); - PyObjectConversions.RegisterDecoder(decoder); - using var result = scope.Eval("FloatDerived()"); - object decoded = result.As(); - Assert.AreEqual(42, decoded); - } + [Test] + public void FloatDerivedDecoded() + { + using var scope = Py.CreateScope(); + scope.Exec(@"class FloatDerived(float): pass"); + using var floatDerived = scope.Eval("FloatDerived"); + var decoder = new DecoderReturningPredefinedValue(floatDerived, 42); + PyObjectConversions.RegisterDecoder(decoder); + using var result = scope.Eval("FloatDerived()"); + object decoded = result.As(); + Assert.AreEqual(42, decoded); + } - [Test] - public void ExceptionDecodedNoInstance() - { - PyObjectConversions.RegisterDecoder(new InstancelessExceptionDecoder()); - using var scope = Py.CreateScope(); - var error = Assert.Throws(() => PythonEngine.Exec( - $"[].__iter__().__next__()")); - Assert.AreEqual(TestExceptionMessage, error.Message); - } + [Test] + public void ExceptionDecodedNoInstance() + { + PyObjectConversions.RegisterDecoder(new InstancelessExceptionDecoder()); + using var scope = Py.CreateScope(); + var error = Assert.Throws(() => PythonEngine.Exec( + $"[].__iter__().__next__()")); + Assert.AreEqual(TestExceptionMessage, error.Message); + } - public static void AcceptsDateTime(DateTime v) {} + public static void AcceptsDateTime(DateTime v) {} - [Test] - public void As_Object_AffectedByDecoders() - { - var everythingElseToSelf = new EverythingElseToSelfDecoder(); - PyObjectConversions.RegisterDecoder(everythingElseToSelf); + [Test] + public void As_Object_AffectedByDecoders() + { + var everythingElseToSelf = new EverythingElseToSelfDecoder(); + PyObjectConversions.RegisterDecoder(everythingElseToSelf); - var pyObj = PythonEngine.Eval("iter"); - var decoded = pyObj.As(); - Assert.AreSame(everythingElseToSelf, decoded); - } + var pyObj = PythonEngine.Eval("iter"); + var decoded = pyObj.As(); + Assert.AreSame(everythingElseToSelf, decoded); + } - public class EverythingElseToSelfDecoder : IPyObjectDecoder + public class EverythingElseToSelfDecoder : IPyObjectDecoder + { + public bool CanDecode(PyType objectType, Type targetType) { - public bool CanDecode(PyType objectType, Type targetType) - { - return targetType.IsAssignableFrom(typeof(EverythingElseToSelfDecoder)); - } - - public bool TryDecode(PyObject pyObj, out T value) - { - value = (T)(object)this; - return true; - } + return targetType.IsAssignableFrom(typeof(EverythingElseToSelfDecoder)); } - class ValueErrorWrapper : Exception + public bool TryDecode(PyObject pyObj, out T value) { - public ValueErrorWrapper(string message) : base(message) { } + value = (T)(object)this; + return true; } + } - class ValueErrorCodec : IPyObjectEncoder, IPyObjectDecoder - { - public bool CanDecode(PyType objectType, Type targetType) - => this.CanEncode(targetType) - && PythonReferenceComparer.Instance.Equals(objectType, PythonEngine.Eval("ValueError")); + class ValueErrorWrapper : Exception + { + public ValueErrorWrapper(string message) : base(message) { } + } - public bool CanEncode(Type type) => type == typeof(ValueErrorWrapper) - || typeof(ValueErrorWrapper).IsSubclassOf(type); + class ValueErrorCodec : IPyObjectEncoder, IPyObjectDecoder + { + public bool CanDecode(PyType objectType, Type targetType) + => this.CanEncode(targetType) + && PythonReferenceComparer.Instance.Equals(objectType, PythonEngine.Eval("ValueError")); - public bool TryDecode(PyObject pyObj, out T value) - { - var message = pyObj.GetAttr("args")[0].As(); - value = (T)(object)new ValueErrorWrapper(message); - return true; - } + public bool CanEncode(Type type) => type == typeof(ValueErrorWrapper) + || typeof(ValueErrorWrapper).IsSubclassOf(type); - public PyObject TryEncode(object value) - { - var error = (ValueErrorWrapper)value; - return PythonEngine.Eval("ValueError").Invoke(error.Message.ToPython()); - } + public bool TryDecode(PyObject pyObj, out T value) + { + var message = pyObj.GetAttr("args")[0].As(); + value = (T)(object)new ValueErrorWrapper(message); + return true; } - class InstancelessExceptionDecoder : IPyObjectDecoder, IDisposable + public PyObject TryEncode(object value) { - readonly PyObject PyErr = Py.Import("clr.interop").GetAttr("PyErr"); - - public bool CanDecode(PyType objectType, Type targetType) - => PythonReferenceComparer.Instance.Equals(PyErr, objectType); - - public void Dispose() - { - PyErr.Dispose(); - } - - public bool TryDecode(PyObject pyObj, out T value) - { - if (pyObj.HasAttr("value")) - { - value = default; - return false; - } - - value = (T)(object)new ValueErrorWrapper(TestExceptionMessage); - return true; - } + var error = (ValueErrorWrapper)value; + return PythonEngine.Eval("ValueError").Invoke(error.Message.ToPython()); } } - /// - /// "Decodes" only objects of exact type . - /// Result is just the raw proxy to the encoder instance itself. - /// - class ObjectToEncoderInstanceEncoder : IPyObjectEncoder + class InstancelessExceptionDecoder : IPyObjectDecoder, IDisposable { - public bool CanEncode(Type type) => type == typeof(T); - public PyObject TryEncode(object value) => PyObject.FromManagedObject(this); - } + readonly PyObject PyErr = Py.Import("clr.interop").GetAttr("PyErr"); - /// - /// Decodes object of specified Python type to the predefined value - /// - /// Type of the - class DecoderReturningPredefinedValue : IPyObjectDecoder - { - public PyObject TheOnlySupportedSourceType { get; } - public TTarget DecodeResult { get; } + public bool CanDecode(PyType objectType, Type targetType) + => PythonReferenceComparer.Instance.Equals(PyErr, objectType); - public DecoderReturningPredefinedValue(PyObject objectType, TTarget decodeResult) + public void Dispose() { - this.TheOnlySupportedSourceType = objectType; - this.DecodeResult = decodeResult; + PyErr.Dispose(); } - public bool CanDecode(PyType objectType, Type targetType) - => PythonReferenceComparer.Instance.Equals(objectType, TheOnlySupportedSourceType) - && targetType == typeof(TTarget); public bool TryDecode(PyObject pyObj, out T value) { - if (typeof(T) != typeof(TTarget)) - throw new ArgumentException(nameof(T)); - value = (T)(object)DecodeResult; + if (pyObj.HasAttr("value")) + { + value = default; + return false; + } + + value = (T)(object)new ValueErrorWrapper(TestExceptionMessage); return true; } } +} - public class DateTimeDecoder : IPyObjectDecoder +/// +/// "Decodes" only objects of exact type . +/// Result is just the raw proxy to the encoder instance itself. +/// +class ObjectToEncoderInstanceEncoder : IPyObjectEncoder +{ + public bool CanEncode(Type type) => type == typeof(T); + public PyObject TryEncode(object value) => PyObject.FromManagedObject(this); +} + +/// +/// Decodes object of specified Python type to the predefined value +/// +/// Type of the +class DecoderReturningPredefinedValue : IPyObjectDecoder +{ + public PyObject TheOnlySupportedSourceType { get; } + public TTarget DecodeResult { get; } + + public DecoderReturningPredefinedValue(PyObject objectType, TTarget decodeResult) { - public static void Setup() - { - PyObjectConversions.RegisterDecoder(new DateTimeDecoder()); - } + this.TheOnlySupportedSourceType = objectType; + this.DecodeResult = decodeResult; + } - public bool CanDecode(PyType objectType, Type targetType) - { - return targetType == typeof(DateTime); - } + public bool CanDecode(PyType objectType, Type targetType) + => PythonReferenceComparer.Instance.Equals(objectType, TheOnlySupportedSourceType) + && targetType == typeof(TTarget); + public bool TryDecode(PyObject pyObj, out T value) + { + if (typeof(T) != typeof(TTarget)) + throw new ArgumentException(nameof(T)); + value = (T)(object)DecodeResult; + return true; + } +} - public bool TryDecode(PyObject pyObj, out T value) - { - var dt = new DateTime( - pyObj.GetAttr("year").As(), - pyObj.GetAttr("month").As(), - pyObj.GetAttr("day").As(), - pyObj.GetAttr("hour").As(), - pyObj.GetAttr("minute").As(), - pyObj.GetAttr("second").As()); - value = (T)(object)dt; - return true; - } +public class DateTimeDecoder : IPyObjectDecoder +{ + public static void Setup() + { + PyObjectConversions.RegisterDecoder(new DateTimeDecoder()); + } + + public bool CanDecode(PyType objectType, Type targetType) + { + return targetType == typeof(DateTime); + } + + public bool TryDecode(PyObject pyObj, out T value) + { + var dt = new DateTime( + pyObj.GetAttr("year").As(), + pyObj.GetAttr("month").As(), + pyObj.GetAttr("day").As(), + pyObj.GetAttr("hour").As(), + pyObj.GetAttr("minute").As(), + pyObj.GetAttr("second").As()); + value = (T)(object)dt; + return true; } } diff --git a/src/embed_tests/Events.cs b/src/embed_tests/Events.cs index c216f4214..74259ed42 100644 --- a/src/embed_tests/Events.cs +++ b/src/embed_tests/Events.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.Threading; using NUnit.Framework; @@ -8,20 +7,8 @@ namespace Python.EmbeddingTest; -public class Events +public class Events : BaseFixture { - [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..3e744dc75 100644 --- a/src/embed_tests/ExtensionTypes.cs +++ b/src/embed_tests/ExtensionTypes.cs @@ -1,25 +1,11 @@ using System; - using NUnit.Framework; - using Python.Runtime; namespace Python.EmbeddingTest; -public class ExtensionTypes +public class ExtensionTypes : BaseFixture { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - [Test] public void WeakrefIsNone_AfterBoundMethodIsGone() { diff --git a/src/embed_tests/GlobalTestsSetup.cs b/src/embed_tests/GlobalTestsSetup.cs index dff58b978..e405241cd 100644 --- a/src/embed_tests/GlobalTestsSetup.cs +++ b/src/embed_tests/GlobalTestsSetup.cs @@ -1,37 +1,37 @@ using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest -{ +namespace Python.EmbeddingTest; - // As the SetUpFixture, the OneTimeTearDown of this class is executed after - // all tests have run. - [SetUpFixture] - public partial class GlobalTestsSetup +// As the SetUpFixture, the OneTimeTearDown of this class is executed after +// all tests have run. +[SetUpFixture] +public partial class GlobalTestsSetup +{ + [OneTimeSetUp] + public void GlobalSetup() { - [OneTimeSetUp] - public void GlobalSetup() - { - Finalizer.Instance.ErrorHandler += FinalizerErrorHandler; - } + PythonEngine.Initialize(); + Finalizer.Instance.ErrorHandler += FinalizerErrorHandler; + } - private void FinalizerErrorHandler(object sender, Finalizer.ErrorArgs e) + private void FinalizerErrorHandler(object sender, Finalizer.ErrorArgs e) + { + if (e.Error is RuntimeShutdownException) { - if (e.Error is RuntimeShutdownException) - { - // allow objects to leak after the python runtime run - // they were created in is gone - e.Handled = true; - } + // allow objects to leak after the python runtime run + // they were created in is gone + e.Handled = true; } + } - [OneTimeTearDown] - public void FinalCleanup() + [OneTimeTearDown] + public void FinalCleanup() + { + PyObjectConversions.Reset(); + if (PythonEngine.IsInitialized) { - if (PythonEngine.IsInitialized) - { - PythonEngine.Shutdown(); - } + PythonEngine.Shutdown(); } } } diff --git a/src/embed_tests/Inheritance.cs b/src/embed_tests/Inheritance.cs index ebbc24dc4..eb9dc6b0d 100644 --- a/src/embed_tests/Inheritance.cs +++ b/src/embed_tests/Inheritance.cs @@ -5,159 +5,157 @@ using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class Inheritance : BaseFixture { - public class Inheritance + [OneTimeSetUp] + public void SetUp() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - using var locals = new PyDict(); - PythonEngine.Exec(InheritanceTestBaseClassWrapper.ClassSourceCode, locals: locals); - ExtraBaseTypeProvider.ExtraBase = new PyType(locals[InheritanceTestBaseClassWrapper.ClassName]); - var baseTypeProviders = PythonEngine.InteropConfiguration.PythonBaseTypeProviders; - baseTypeProviders.Add(new ExtraBaseTypeProvider()); - baseTypeProviders.Add(new NoEffectBaseTypeProvider()); - } - - [OneTimeTearDown] - public void Dispose() - { - ExtraBaseTypeProvider.ExtraBase.Dispose(); - PythonEngine.Shutdown(); - } - - [Test] - public void ExtraBase_PassesInstanceCheck() - { - var inherited = new Inherited(); - bool properlyInherited = PyIsInstance(inherited, ExtraBaseTypeProvider.ExtraBase); - Assert.IsTrue(properlyInherited); - } - - static dynamic PyIsInstance => PythonEngine.Eval("isinstance"); + using var locals = new PyDict(); + PythonEngine.Exec(InheritanceTestBaseClassWrapper.ClassSourceCode, locals: locals); + ExtraBaseTypeProvider.ExtraBase = new PyType(locals[InheritanceTestBaseClassWrapper.ClassName]); + var baseTypeProviders = PythonEngine.InteropConfiguration.PythonBaseTypeProviders; + baseTypeProviders.Add(new ExtraBaseTypeProvider()); + baseTypeProviders.Add(new NoEffectBaseTypeProvider()); + } - [Test] - 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)); - } + [OneTimeTearDown] + public void Dispose() + { + ExtraBaseTypeProvider.ExtraBase.Dispose(); + } - [Test] - public void InheritedFromInheritedClassIsSelf() - { - using var scope = Py.CreateScope(); - scope.Exec($"from {typeof(Inherited).Namespace} import {nameof(Inherited)}"); - scope.Exec($"class B({nameof(Inherited)}): pass"); - PyObject b = scope.Eval("B"); - PyObject bInstance = b.Invoke(); - PyObject bInstanceClass = bInstance.GetAttr("__class__"); - Assert.IsTrue(PythonReferenceComparer.Instance.Equals(b, bInstanceClass)); - } + [Test] + public void ExtraBase_PassesInstanceCheck() + { + var inherited = new Inherited(); + bool properlyInherited = PyIsInstance(inherited, ExtraBaseTypeProvider.ExtraBase); + Assert.IsTrue(properlyInherited); + } - // https://github.com/pythonnet/pythonnet/issues/1420 - [Test] - public void CallBaseMethodFromContainerInNestedClass() - { - using var nested = new ContainerClass.InnerClass().ToPython(); - nested.InvokeMethod(nameof(ContainerClass.BaseMethod)); - } + static dynamic PyIsInstance => PythonEngine.Eval("isinstance"); - [Test] - public void Grandchild_PassesExtraBaseInstanceCheck() - { - using var scope = Py.CreateScope(); - scope.Exec($"from {typeof(Inherited).Namespace} import {nameof(Inherited)}"); - scope.Exec($"class B({nameof(Inherited)}): pass"); - PyObject b = scope.Eval("B"); - PyObject bInst = b.Invoke(); - bool properlyInherited = PyIsInstance(bInst, ExtraBaseTypeProvider.ExtraBase); - Assert.IsTrue(properlyInherited); - } + [Test] + 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)); + } - [Test] - public void CallInheritedClrMethod_WithExtraPythonBase() - { - var instance = new Inherited().ToPython(); - string result = instance.InvokeMethod(nameof(PythonWrapperBase.WrapperBaseMethod)).As(); - Assert.AreEqual(result, nameof(PythonWrapperBase.WrapperBaseMethod)); - } + [Test] + public void InheritedFromInheritedClassIsSelf() + { + using var scope = Py.CreateScope(); + scope.Exec($"from {typeof(Inherited).Namespace} import {nameof(Inherited)}"); + scope.Exec($"class B({nameof(Inherited)}): pass"); + PyObject b = scope.Eval("B"); + PyObject bInstance = b.Invoke(); + PyObject bInstanceClass = bInstance.GetAttr("__class__"); + Assert.IsTrue(PythonReferenceComparer.Instance.Equals(b, bInstanceClass)); + } - [Test] - public void CallExtraBaseMethod() - { - var instance = new Inherited(); - using var scope = Py.CreateScope(); - scope.Set(nameof(instance), instance); - int actual = instance.ToPython().InvokeMethod("callVirt").As(); - Assert.AreEqual(expected: Inherited.OverridenVirtValue, actual); - } + // https://github.com/pythonnet/pythonnet/issues/1420 + [Test] + public void CallBaseMethodFromContainerInNestedClass() + { + using var nested = new ContainerClass.InnerClass().ToPython(); + nested.InvokeMethod(nameof(ContainerClass.BaseMethod)); + } - [Test] - public void SetAdHocAttributes_WhenExtraBasePresent() - { - var instance = new Inherited(); - using var scope = Py.CreateScope(); - 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); - } + [Test] + public void Grandchild_PassesExtraBaseInstanceCheck() + { + using var scope = Py.CreateScope(); + scope.Exec($"from {typeof(Inherited).Namespace} import {nameof(Inherited)}"); + scope.Exec($"class B({nameof(Inherited)}): pass"); + PyObject b = scope.Eval("B"); + PyObject bInst = b.Invoke(); + bool properlyInherited = PyIsInstance(bInst, ExtraBaseTypeProvider.ExtraBase); + Assert.IsTrue(properlyInherited); + } - // https://github.com/pythonnet/pythonnet/issues/1476 - [Test] - 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); - scope.Set("exn", null); - Assert.AreEqual(1, msg.Refcount); - } + [Test] + public void CallInheritedClrMethod_WithExtraPythonBase() + { + var instance = new Inherited().ToPython(); + string result = instance.InvokeMethod(nameof(PythonWrapperBase.WrapperBaseMethod)).As(); + Assert.AreEqual(result, nameof(PythonWrapperBase.WrapperBaseMethod)); + } - // https://github.com/pythonnet/pythonnet/issues/1455 - [Test] - 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()); - } + [Test] + public void CallExtraBaseMethod() + { + var instance = new Inherited(); + using var scope = Py.CreateScope(); + scope.Set(nameof(instance), instance); + int actual = instance.ToPython().InvokeMethod("callVirt").As(); + Assert.AreEqual(expected: Inherited.OverridenVirtValue, actual); } - class ExtraBaseTypeProvider : IPythonBaseTypeProvider + [Test] + public void SetAdHocAttributes_WhenExtraBasePresent() { - internal static PyType ExtraBase; - public IEnumerable GetBaseTypes(Type type, IList existingBases) - { - if (type == typeof(InheritanceTestBaseClassWrapper)) - { - return new[] { PyType.Get(type.BaseType), ExtraBase }; - } - return existingBases; - } + var instance = new Inherited(); + using var scope = Py.CreateScope(); + 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); } - class NoEffectBaseTypeProvider : IPythonBaseTypeProvider + // https://github.com/pythonnet/pythonnet/issues/1476 + [Test] + public void BaseClearIsCalled() { - public IEnumerable GetBaseTypes(Type type, IList existingBases) - => existingBases; + using var scope = Py.CreateScope(); + scope.Set("exn", new Exception("42")); + var msg = scope.Eval("exn.args[0]"); + Assert.AreEqual(2, msg.Refcount); + scope.Set("exn", null); + Assert.AreEqual(1, msg.Refcount); } - public class PythonWrapperBase + // https://github.com/pythonnet/pythonnet/issues/1455 + [Test] + public void PropertyAccessorOverridden() { - public string WrapperBaseMethod() => nameof(WrapperBaseMethod); + using var derived = new PropertyAccessorDerived().ToPython(); + derived.SetAttr(nameof(PropertyAccessorDerived.VirtualProp), "hi".ToPython()); + Assert.AreEqual("HI", derived.GetAttr(nameof(PropertyAccessorDerived.VirtualProp)).As()); } +} - public class InheritanceTestBaseClassWrapper : PythonWrapperBase +class ExtraBaseTypeProvider : IPythonBaseTypeProvider +{ + internal static PyType ExtraBase; + public IEnumerable GetBaseTypes(Type type, IList existingBases) { - public const string ClassName = "InheritanceTestBaseClass"; - public const string ClassSourceCode = "class " + ClassName + + if (type == typeof(InheritanceTestBaseClassWrapper)) + { + return new[] { PyType.Get(type.BaseType), ExtraBase }; + } + return existingBases; + } +} + +class NoEffectBaseTypeProvider : IPythonBaseTypeProvider +{ + public IEnumerable GetBaseTypes(Type type, IList existingBases) + => existingBases; +} + +public class PythonWrapperBase +{ + public string WrapperBaseMethod() => nameof(WrapperBaseMethod); +} + +public class InheritanceTestBaseClassWrapper : PythonWrapperBase +{ + public const string ClassName = "InheritanceTestBaseClass"; + public const string ClassSourceCode = "class " + ClassName + @": def virt(self): return 42 @@ -170,56 +168,55 @@ def __getattr__(self, name): def __setattr__(self, name, value): value[name] = name " + ClassName + " = " + ClassName + "\n"; - } +} - public class Inherited : InheritanceTestBaseClassWrapper +public class Inherited : InheritanceTestBaseClassWrapper +{ + public const int OverridenVirtValue = -42; + public const int X = 42; + readonly Dictionary extras = new Dictionary(); + public int virt() => OverridenVirtValue; + public int XProp { - public const int OverridenVirtValue = -42; - public const int X = 42; - readonly Dictionary extras = new Dictionary(); - public int virt() => OverridenVirtValue; - public int XProp + get { - get + using (var scope = Py.CreateScope()) { - using (var scope = Py.CreateScope()) + scope.Set("this", this); + try + { + return scope.Eval($"super(this.__class__, this).{nameof(XProp)}"); + } + catch (PythonException ex) when (PythonReferenceComparer.Instance.Equals(ex.Type, Exceptions.AttributeError)) { - scope.Set("this", this); - try - { - return scope.Eval($"super(this.__class__, this).{nameof(XProp)}"); - } - catch (PythonException ex) when (PythonReferenceComparer.Instance.Equals(ex.Type, Exceptions.AttributeError)) - { - if (this.extras.TryGetValue(nameof(this.XProp), out object value)) - return (int)value; - throw; - } + if (this.extras.TryGetValue(nameof(this.XProp), out object value)) + return (int)value; + throw; } } - set => this.extras[nameof(this.XProp)] = value; } + set => this.extras[nameof(this.XProp)] = value; } +} - public class PropertyAccessorBase - { - public virtual string VirtualProp { get; set; } - } +public class PropertyAccessorBase +{ + public virtual string VirtualProp { get; set; } +} - public class PropertyAccessorIntermediate: PropertyAccessorBase { } +public class PropertyAccessorIntermediate: PropertyAccessorBase { } - public class PropertyAccessorDerived: PropertyAccessorIntermediate - { - public override string VirtualProp { set => base.VirtualProp = value.ToUpperInvariant(); } - } +public class PropertyAccessorDerived: PropertyAccessorIntermediate +{ + public override string VirtualProp { set => base.VirtualProp = value.ToUpperInvariant(); } +} - public class ContainerClass - { - public void BaseMethod() { } +public class ContainerClass +{ + public void BaseMethod() { } - public class InnerClass: ContainerClass - { + public class InnerClass: ContainerClass + { - } } } diff --git a/src/embed_tests/Inspect.cs b/src/embed_tests/Inspect.cs index 8ff94e02c..36131e393 100644 --- a/src/embed_tests/Inspect.cs +++ b/src/embed_tests/Inspect.cs @@ -1,59 +1,43 @@ using System; -using System.Collections.Generic; - using NUnit.Framework; - using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class Inspect : BaseFixture { - public class Inspect + [Test] + public void InstancePropertiesVisibleOnClass() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } + var uri = new Uri("http://example.org").ToPython(); + 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); + } - [OneTimeTearDown] - public void Dispose() + [Test] + public void BoundMethodsAreInspectable() + { + using var scope = Py.CreateScope(); + try { - PythonEngine.Shutdown(); + scope.Import("inspect"); } - - [Test] - public void InstancePropertiesVisibleOnClass() + catch (PythonException) { - var uri = new Uri("http://example.org").ToPython(); - 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.Inconclusive("Python build does not include inspect module"); + return; } - [Test] - public void BoundMethodsAreInspectable() - { - using var scope = Py.CreateScope(); - try - { - scope.Import("inspect"); - } - catch (PythonException) - { - Assert.Inconclusive("Python build does not include inspect module"); - return; - } - - var obj = new Class(); - scope.Set(nameof(obj), obj); - using var spec = scope.Eval($"inspect.getfullargspec({nameof(obj)}.{nameof(Class.Method)})"); - } + var obj = new Class(); + scope.Set(nameof(obj), obj); + using var spec = scope.Eval($"inspect.getfullargspec({nameof(obj)}.{nameof(Class.Method)})"); + } - class Class - { - public void Method(int a, int b = 10) { } - public void Method(int a, object b) { } - } + class Class + { + public void Method(int a, int b = 10) { } + public void Method(int a, object b) { } } } diff --git a/src/embed_tests/Modules.cs b/src/embed_tests/Modules.cs index a88ab8552..99efa95f5 100644 --- a/src/embed_tests/Modules.cs +++ b/src/embed_tests/Modules.cs @@ -1,405 +1,391 @@ -using System; using System.Threading; using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class Modules : BaseFixture { - public class Modules - { - private PyModule ps; + private PyModule ps; - [SetUp] - public void SetUp() + [SetUp] + public void SetUp() + { + using (Py.GIL()) { - using (Py.GIL()) - { - ps = Py.CreateScope("test"); - } + ps = Py.CreateScope("test"); } + } - [TearDown] - public void Dispose() + [TearDown] + public void Dispose() + { + using (Py.GIL()) { - using (Py.GIL()) - { - ps.Dispose(); - ps = null; - } + ps.Dispose(); + ps = null; } + } - [OneTimeSetUp] - public void OneTimeSetUp() + /// + /// Eval a Python expression and obtain its return value. + /// + [Test] + public void TestEval() + { + using (Py.GIL()) { - PythonEngine.Initialize(); + ps.Set("a", 1); + var result = ps.Eval("a + 2"); + Assert.AreEqual(3, result); } + } - [OneTimeTearDown] - public void OneTimeTearDown() + /// + /// Exec Python statements and obtain the variables created. + /// + [Test] + public void TestExec() + { + using (Py.GIL()) { - PythonEngine.Shutdown(); + ps.Set("bb", 100); //declare a global variable + ps.Set("cc", 10); //declare a local variable + ps.Exec("aa = bb + cc + 3"); + var result = ps.Get("aa"); + Assert.AreEqual(113, result); } + } - /// - /// Eval a Python expression and obtain its return value. - /// - [Test] - public void TestEval() + /// + /// Compile an expression into an ast object; + /// Execute the ast and obtain its return value. + /// + [Test] + public void TestCompileExpression() + { + using (Py.GIL()) { - using (Py.GIL()) - { - ps.Set("a", 1); - var result = ps.Eval("a + 2"); - Assert.AreEqual(3, result); - } + ps.Set("bb", 100); //declare a global variable + 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); } + } - /// - /// Exec Python statements and obtain the variables created. - /// - [Test] - public void TestExec() + /// + /// Compile Python statements into an ast object; + /// Execute the ast; + /// Obtain the local variables created. + /// + [Test] + public void TestCompileStatements() + { + using (Py.GIL()) { - using (Py.GIL()) - { - ps.Set("bb", 100); //declare a global variable - ps.Set("cc", 10); //declare a local variable - ps.Exec("aa = bb + cc + 3"); - var result = ps.Get("aa"); - Assert.AreEqual(113, result); - } + ps.Set("bb", 100); //declare a global variable + ps.Set("cc", 10); //declare a local variable + PyObject script = PythonEngine.Compile("aa = bb + cc + 3", "", RunFlagType.File); + ps.Execute(script); + var result = ps.Get("aa"); + Assert.AreEqual(113, result); } + } - /// - /// Compile an expression into an ast object; - /// Execute the ast and obtain its return value. - /// - [Test] - public void TestCompileExpression() + /// + /// Create a function in the scope, then the function can read variables in the scope. + /// It cannot write the variables unless it uses the 'global' keyword. + /// + [Test] + public void TestScopeFunction() + { + using (Py.GIL()) { - using (Py.GIL()) - { - ps.Set("bb", 100); //declare a global variable - 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); - } + ps.Set("bb", 100); + ps.Set("cc", 10); + ps.Exec( + "def func1():\n" + + " bb = cc + 10\n"); + dynamic func1 = ps.Get("func1"); + func1(); //call the function, it can be called any times + var result = ps.Get("bb"); + Assert.AreEqual(100, result); + + ps.Set("bb", 100); + ps.Set("cc", 10); + ps.Exec( + "def func2():\n" + + " global bb\n" + + " bb = cc + 10\n"); + dynamic func2 = ps.Get("func2"); + func2(); + result = ps.Get("bb"); + Assert.AreEqual(20, result); } + } - /// - /// Compile Python statements into an ast object; - /// Execute the ast; - /// Obtain the local variables created. - /// - [Test] - public void TestCompileStatements() + /// + /// Create a class in the scope, the class can read variables in the scope. + /// Its methods can write the variables with the help of 'global' keyword. + /// + [Test] + public void TestScopeClass() + { + using (Py.GIL()) { - using (Py.GIL()) - { - ps.Set("bb", 100); //declare a global variable - ps.Set("cc", 10); //declare a local variable - PyObject script = PythonEngine.Compile("aa = bb + cc + 3", "", RunFlagType.File); - ps.Execute(script); - var result = ps.Get("aa"); - Assert.AreEqual(113, result); - } + dynamic _ps = ps; + _ps.bb = 100; + ps.Exec( + "class Class1():\n" + + " def __init__(self, value):\n" + + " self.value = value\n" + + " def call(self, arg):\n" + + " return self.value + bb + arg\n" + //use scope variables + " def update(self, arg):\n" + + " global bb\n" + + " bb = self.value + arg\n" //update scope variable + ); + dynamic obj1 = _ps.Class1(20); + var result = obj1.call(10).As(); + Assert.AreEqual(130, result); + + obj1.update(10); + result = ps.Get("bb"); + Assert.AreEqual(30, result); } + } - /// - /// Create a function in the scope, then the function can read variables in the scope. - /// It cannot write the variables unless it uses the 'global' keyword. - /// - [Test] - public void TestScopeFunction() + /// + /// Import a python module into the session. + /// Equivalent to the Python "import" statement. + /// + [Test] + public void TestImportModule() + { + using (Py.GIL()) { - using (Py.GIL()) - { - ps.Set("bb", 100); - ps.Set("cc", 10); - ps.Exec( - "def func1():\n" + - " bb = cc + 10\n"); - dynamic func1 = ps.Get("func1"); - func1(); //call the function, it can be called any times - var result = ps.Get("bb"); - Assert.AreEqual(100, result); - - ps.Set("bb", 100); - ps.Set("cc", 10); - ps.Exec( - "def func2():\n" + - " global bb\n" + - " bb = cc + 10\n"); - dynamic func2 = ps.Get("func2"); - func2(); - result = ps.Get("bb"); - Assert.AreEqual(20, result); - } + dynamic sys = ps.Import("sys"); + Assert.IsTrue(ps.Contains("sys")); + + ps.Exec("sys.attr1 = 2"); + var value1 = ps.Eval("sys.attr1"); + var value2 = sys.attr1.As(); + Assert.AreEqual(2, value1); + Assert.AreEqual(2, value2); + + //import as + ps.Import("sys", "sys1"); + Assert.IsTrue(ps.Contains("sys1")); } + } - /// - /// Create a class in the scope, the class can read variables in the scope. - /// Its methods can write the variables with the help of 'global' keyword. - /// - [Test] - public void TestScopeClass() + /// + /// Create a scope and import variables from a scope, + /// exec Python statements in the scope then discard it. + /// + [Test] + public void TestImportScope() + { + using (Py.GIL()) { - using (Py.GIL()) - { - dynamic _ps = ps; - _ps.bb = 100; - ps.Exec( - "class Class1():\n" + - " def __init__(self, value):\n" + - " self.value = value\n" + - " def call(self, arg):\n" + - " return self.value + bb + arg\n" + //use scope variables - " def update(self, arg):\n" + - " global bb\n" + - " bb = self.value + arg\n" //update scope variable - ); - dynamic obj1 = _ps.Class1(20); - var result = obj1.call(10).As(); - Assert.AreEqual(130, result); + ps.Set("bb", 100); + ps.Set("cc", 10); - obj1.update(10); - result = ps.Get("bb"); - Assert.AreEqual(30, result); + using (var scope = Py.CreateScope()) + { + scope.Import(ps, "ps"); + scope.Exec("aa = ps.bb + ps.cc + 3"); + var result = scope.Get("aa"); + Assert.AreEqual(113, result); } + + Assert.IsFalse(ps.Contains("aa")); } + } - /// - /// Import a python module into the session. - /// Equivalent to the Python "import" statement. - /// - [Test] - public void TestImportModule() + /// + /// Create a scope and import variables from a scope, + /// exec Python statements in the scope then discard it. + /// + [Test] + public void TestImportAllFromScope() + { + using (Py.GIL()) { - using (Py.GIL()) + ps.Set("bb", 100); + ps.Set("cc", 10); + + using (var scope = ps.NewScope()) { - dynamic sys = ps.Import("sys"); - Assert.IsTrue(ps.Contains("sys")); - - ps.Exec("sys.attr1 = 2"); - var value1 = ps.Eval("sys.attr1"); - var value2 = sys.attr1.As(); - Assert.AreEqual(2, value1); - Assert.AreEqual(2, value2); - - //import as - ps.Import("sys", "sys1"); - Assert.IsTrue(ps.Contains("sys1")); + scope.Exec("aa = bb + cc + 3"); + var result = scope.Get("aa"); + Assert.AreEqual(113, result); } + + Assert.IsFalse(ps.Contains("aa")); } + } - /// - /// Create a scope and import variables from a scope, - /// exec Python statements in the scope then discard it. - /// - [Test] - public void TestImportScope() + /// + /// Create a scope and import variables from a scope, + /// call the function imported. + /// + [Test] + public void TestImportScopeFunction() + { + using (Py.GIL()) { - using (Py.GIL()) + ps.Set("bb", 100); + ps.Set("cc", 10); + ps.Exec( + "def func1():\n" + + " return cc + bb\n"); + + using (var scope = ps.NewScope()) { - ps.Set("bb", 100); - ps.Set("cc", 10); + //'func1' is imported from the origion scope + scope.Exec( + "def func2():\n" + + " return func1() - cc - bb\n"); + dynamic func2 = scope.Get("func2"); - using (var scope = Py.CreateScope()) - { - scope.Import(ps, "ps"); - scope.Exec("aa = ps.bb + ps.cc + 3"); - var result = scope.Get("aa"); - Assert.AreEqual(113, result); - } + var result1 = func2().As(); + Assert.AreEqual(0, result1); + + scope.Set("cc", 20);//it has no effect on the globals of 'func1' + var result2 = func2().As(); + Assert.AreEqual(-10, result2); + scope.Set("cc", 10); //rollback - Assert.IsFalse(ps.Contains("aa")); + ps.Set("cc", 20); + var result3 = func2().As(); + Assert.AreEqual(10, result3); + ps.Set("cc", 10); //rollback } } + } - /// - /// Create a scope and import variables from a scope, - /// exec Python statements in the scope then discard it. - /// - [Test] - public void TestImportAllFromScope() + /// + /// Use the locals() and globals() method just like in python module + /// + [Test] + public void TestVariables() + { + using (Py.GIL()) { - using (Py.GIL()) - { - ps.Set("bb", 100); - ps.Set("cc", 10); + (ps.Variables() as dynamic)["ee"] = new PyInt(200); + var a0 = ps.Get("ee"); + Assert.AreEqual(200, a0); - using (var scope = ps.NewScope()) - { - scope.Exec("aa = bb + cc + 3"); - var result = scope.Get("aa"); - Assert.AreEqual(113, result); - } + ps.Exec("locals()['ee'] = 210"); + var a1 = ps.Get("ee"); + Assert.AreEqual(210, a1); - Assert.IsFalse(ps.Contains("aa")); - } - } + ps.Exec("globals()['ee'] = 220"); + var a2 = ps.Get("ee"); + Assert.AreEqual(220, a2); - /// - /// Create a scope and import variables from a scope, - /// call the function imported. - /// - [Test] - public void TestImportScopeFunction() - { - using (Py.GIL()) + using (var item = ps.Variables()) { - ps.Set("bb", 100); - ps.Set("cc", 10); - ps.Exec( - "def func1():\n" + - " return cc + bb\n"); - - using (var scope = ps.NewScope()) - { - //'func1' is imported from the origion scope - scope.Exec( - "def func2():\n" + - " return func1() - cc - bb\n"); - dynamic func2 = scope.Get("func2"); - - var result1 = func2().As(); - Assert.AreEqual(0, result1); - - scope.Set("cc", 20);//it has no effect on the globals of 'func1' - var result2 = func2().As(); - Assert.AreEqual(-10, result2); - scope.Set("cc", 10); //rollback - - ps.Set("cc", 20); - var result3 = func2().As(); - Assert.AreEqual(10, result3); - ps.Set("cc", 10); //rollback - } + item["ee"] = new PyInt(230); } + var a3 = ps.Get("ee"); + Assert.AreEqual(230, a3); } + } - /// - /// Use the locals() and globals() method just like in python module - /// - [Test] - public void TestVariables() + /// + /// Share a pyscope by multiple threads. + /// + [Test] + public void TestThread() + { + //After the proposal here https://github.com/pythonnet/pythonnet/pull/419 complished, + //the BeginAllowThreads statement blow and the last EndAllowThreads statement + //should be removed. + dynamic _ps = ps; + var ts = PythonEngine.BeginAllowThreads(); + try { using (Py.GIL()) - { - (ps.Variables() as dynamic)["ee"] = new PyInt(200); - var a0 = ps.Get("ee"); - Assert.AreEqual(200, a0); - - ps.Exec("locals()['ee'] = 210"); - var a1 = ps.Get("ee"); - Assert.AreEqual(210, a1); - - ps.Exec("globals()['ee'] = 220"); - var a2 = ps.Get("ee"); - Assert.AreEqual(220, a2); - - using (var item = ps.Variables()) - { - item["ee"] = new PyInt(230); - } - var a3 = ps.Get("ee"); - Assert.AreEqual(230, a3); + { + _ps.res = 0; + _ps.bb = 100; + _ps.th_cnt = 0; + //add function to the scope + //can be call many times, more efficient than ast + ps.Exec( + "import threading\n"+ + "lock = threading.Lock()\n"+ + "def update():\n" + + " global res, th_cnt\n" + + " with lock:\n" + + " res += bb + 1\n" + + " th_cnt += 1\n" + ); } - } - - /// - /// Share a pyscope by multiple threads. - /// - [Test] - public void TestThread() - { - //After the proposal here https://github.com/pythonnet/pythonnet/pull/419 complished, - //the BeginAllowThreads statement blow and the last EndAllowThreads statement - //should be removed. - dynamic _ps = ps; - var ts = PythonEngine.BeginAllowThreads(); - try + int th_cnt = 100; + for (int i = 0; i < th_cnt; i++) { - using (Py.GIL()) - { - _ps.res = 0; - _ps.bb = 100; - _ps.th_cnt = 0; - //add function to the scope - //can be call many times, more efficient than ast - ps.Exec( - "import threading\n"+ - "lock = threading.Lock()\n"+ - "def update():\n" + - " global res, th_cnt\n" + - " with lock:\n" + - " res += bb + 1\n" + - " th_cnt += 1\n" - ); - } - int th_cnt = 100; - for (int i = 0; i < th_cnt; i++) - { - System.Threading.Thread th = new System.Threading.Thread(() => - { - using (Py.GIL()) - { - //ps.GetVariable("update")(); //call the scope function dynamicly - _ps.update(); - } - }); - th.Start(); - } - //equivalent to Thread.Join, make the main thread join the GIL competition - int cnt = 0; - while (cnt != th_cnt) + System.Threading.Thread th = new System.Threading.Thread(() => { using (Py.GIL()) { - cnt = ps.Get("th_cnt"); + //ps.GetVariable("update")(); //call the scope function dynamicly + _ps.update(); } - Thread.Yield(); - } + }); + th.Start(); + } + //equivalent to Thread.Join, make the main thread join the GIL competition + int cnt = 0; + while (cnt != th_cnt) + { using (Py.GIL()) { - var result = ps.Get("res"); - Assert.AreEqual(101 * th_cnt, result); + cnt = ps.Get("th_cnt"); } + Thread.Yield(); } - finally + using (Py.GIL()) { - PythonEngine.EndAllowThreads(ts); + var result = ps.Get("res"); + Assert.AreEqual(101 * th_cnt, result); } } - - [Test] - public void TestCreate() + finally { - using var scope = Py.CreateScope(); + PythonEngine.EndAllowThreads(ts); + } + } - Assert.IsFalse(PyModule.SysModules.HasKey("testmod")); + [Test] + public void TestCreate() + { + using var scope = Py.CreateScope(); - PyModule testmod = new PyModule("testmod"); + Assert.IsFalse(PyModule.SysModules.HasKey("testmod")); - testmod.SetAttr("testattr1", "True".ToPython()); + PyModule testmod = new PyModule("testmod"); - PyModule.SysModules.SetItem("testmod", testmod); + testmod.SetAttr("testattr1", "True".ToPython()); - using PyObject code = PythonEngine.Compile( - "import testmod\n" + - "x = testmod.testattr1" - ); - scope.Execute(code); + PyModule.SysModules.SetItem("testmod", testmod); - Assert.IsTrue(scope.TryGet("x", out dynamic x)); - Assert.AreEqual("True", x.ToString()); - } + using PyObject code = PythonEngine.Compile( + "import testmod\n" + + "x = testmod.testattr1" + ); + scope.Execute(code); - [Test] - public void ImportClrNamespace() - { - Py.Import(GetType().Namespace); - } + Assert.IsTrue(scope.TryGet("x", out dynamic x)); + Assert.AreEqual("True", x.ToString()); + } + + [Test] + public void ImportClrNamespace() + { + Py.Import(GetType().Namespace); } } diff --git a/src/embed_tests/NumPyTests.cs b/src/embed_tests/NumPyTests.cs index e102ddb99..05b9539a2 100644 --- a/src/embed_tests/NumPyTests.cs +++ b/src/embed_tests/NumPyTests.cs @@ -6,92 +6,84 @@ using Python.Runtime; using Python.Runtime.Codecs; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class NumPyTests : BaseFixture { - public class NumPyTests + [OneTimeSetUp] + public void SetUp() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - TupleCodec.Register(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } + TupleCodec.Register(); + } - [Test] - public void TestReadme() - { - Assert.AreEqual("1.0", np.cos(np.pi * 2).ToString()); + [Test] + public void TestReadme() + { + Assert.AreEqual("1.0", np.cos(np.pi * 2).ToString()); - dynamic sin = np.sin; - StringAssert.StartsWith("-0.95892", sin(5).ToString()); + dynamic sin = np.sin; + StringAssert.StartsWith("-0.95892", sin(5).ToString()); - double c = (double)(np.cos(5) + sin(5)); - Assert.AreEqual(-0.675262, c, 0.01); + double c = (double)(np.cos(5) + sin(5)); + Assert.AreEqual(-0.675262, c, 0.01); - dynamic a = np.array(new List { 1, 2, 3 }); - Assert.AreEqual("float64", a.dtype.ToString()); + dynamic a = np.array(new List { 1, 2, 3 }); + Assert.AreEqual("float64", a.dtype.ToString()); - dynamic b = np.array(new List { 6, 5, 4 }, Py.kw("dtype", np.int32)); - Assert.AreEqual("int32", b.dtype.ToString()); + dynamic b = np.array(new List { 6, 5, 4 }, Py.kw("dtype", np.int32)); + Assert.AreEqual("int32", b.dtype.ToString()); - Assert.AreEqual("[ 6. 10. 12.]", (a * b).ToString().Replace(" ", " ")); - } + Assert.AreEqual("[ 6. 10. 12.]", (a * b).ToString().Replace(" ", " ")); + } - [Test] - public void MultidimensionalNumPyArray() - { - var array = new[,] { { 1, 2 }, { 3, 4 } }; - var ndarray = np.InvokeMethod("asarray", array.ToPython()); - Assert.AreEqual((2, 2), ndarray.GetAttr("shape").As<(int, int)>()); - Assert.AreEqual(1, ndarray[(0, 0).ToPython()].InvokeMethod("__int__").As()); - Assert.AreEqual(array[1, 0], ndarray[(1, 0).ToPython()].InvokeMethod("__int__").As()); - } + [Test] + public void MultidimensionalNumPyArray() + { + var array = new[,] { { 1, 2 }, { 3, 4 } }; + var ndarray = np.InvokeMethod("asarray", array.ToPython()); + Assert.AreEqual((2, 2), ndarray.GetAttr("shape").As<(int, int)>()); + Assert.AreEqual(1, ndarray[(0, 0).ToPython()].InvokeMethod("__int__").As()); + Assert.AreEqual(array[1, 0], ndarray[(1, 0).ToPython()].InvokeMethod("__int__").As()); + } - [Test] - public void Int64Array() - { - var array = new long[,] { { 1, 2 }, { 3, 4 } }; - var ndarray = np.InvokeMethod("asarray", array.ToPython()); - Assert.AreEqual((2, 2), ndarray.GetAttr("shape").As<(int, int)>()); - Assert.AreEqual(1, ndarray[(0, 0).ToPython()].InvokeMethod("__int__").As()); - Assert.AreEqual(array[1, 0], ndarray[(1, 0).ToPython()].InvokeMethod("__int__").As()); - } + [Test] + public void Int64Array() + { + var array = new long[,] { { 1, 2 }, { 3, 4 } }; + var ndarray = np.InvokeMethod("asarray", array.ToPython()); + Assert.AreEqual((2, 2), ndarray.GetAttr("shape").As<(int, int)>()); + Assert.AreEqual(1, ndarray[(0, 0).ToPython()].InvokeMethod("__int__").As()); + Assert.AreEqual(array[1, 0], ndarray[(1, 0).ToPython()].InvokeMethod("__int__").As()); + } - [Test] - public void VarArg() - { - dynamic zX = np.array(new[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 8, 9, 0 } }); - dynamic grad = np.gradient(zX, 4.0, 5.0); - dynamic grad2 = np.InvokeMethod("gradient", new PyObject[] {zX, new PyFloat(4.0), new PyFloat(5.0)}); - - Assert.AreEqual(4.125, grad[0].sum().__float__().As(), 0.001); - Assert.AreEqual(-1.2, grad[1].sum().__float__().As(), 0.001); - Assert.AreEqual(4.125, grad2[0].sum().__float__().As(), 0.001); - Assert.AreEqual(-1.2, grad2[1].sum().__float__().As(), 0.001); - } + [Test] + public void VarArg() + { + dynamic zX = np.array(new[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 8, 9, 0 } }); + dynamic grad = np.gradient(zX, 4.0, 5.0); + dynamic grad2 = np.InvokeMethod("gradient", new PyObject[] {zX, new PyFloat(4.0), new PyFloat(5.0)}); + + Assert.AreEqual(4.125, grad[0].sum().__float__().As(), 0.001); + Assert.AreEqual(-1.2, grad[1].sum().__float__().As(), 0.001); + Assert.AreEqual(4.125, grad2[0].sum().__float__().As(), 0.001); + Assert.AreEqual(-1.2, grad2[1].sum().__float__().As(), 0.001); + } #pragma warning disable IDE1006 - dynamic np + dynamic np + { + get { - get + try { - try - { - return Py.Import("numpy"); - } - catch (PythonException) - { - Assert.Inconclusive("Numpy or dependency not installed"); - return null; - } + return Py.Import("numpy"); + } + catch (PythonException) + { + Assert.Inconclusive("Numpy or dependency not installed"); + return null; } } - } + } diff --git a/src/embed_tests/References.cs b/src/embed_tests/References.cs index c416c5ebe..eeca08ae6 100644 --- a/src/embed_tests/References.cs +++ b/src/embed_tests/References.cs @@ -1,47 +1,34 @@ -namespace Python.EmbeddingTest -{ - using NUnit.Framework; - using Python.Runtime; +using NUnit.Framework; +using Python.Runtime; + +namespace Python.EmbeddingTest; - public class References +public class References : BaseFixture +{ + [Test] + public void MoveToPyObject_SetsNull() { - [OneTimeSetUp] - public void SetUp() + using var dict = new PyDict(); + NewReference reference = Runtime.Runtime.PyDict_Items(dict.Reference); + try { - PythonEngine.Initialize(); - } + Assert.IsFalse(reference.IsNull()); - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); + using (reference.MoveToPyObject()) + Assert.IsTrue(reference.IsNull()); } - - [Test] - public void MoveToPyObject_SetsNull() + finally { - var dict = new PyDict(); - NewReference reference = Runtime.PyDict_Items(dict.Reference); - try - { - Assert.IsFalse(reference.IsNull()); - - using (reference.MoveToPyObject()) - Assert.IsTrue(reference.IsNull()); - } - finally - { - reference.Dispose(); - } + reference.Dispose(); } + } - [Test] - public void CanBorrowFromNewReference() - { - var dict = new PyDict(); - using NewReference reference = Runtime.PyDict_Items(dict.Reference); - BorrowedReference borrowed = reference.BorrowOrThrow(); - PythonException.ThrowIfIsNotZero(Runtime.PyList_Reverse(borrowed)); - } + [Test] + public void CanBorrowFromNewReference() + { + using var dict = new PyDict(); + using NewReference reference = Runtime.Runtime.PyDict_Items(dict.Reference); + BorrowedReference borrowed = reference.BorrowOrThrow(); + PythonException.ThrowIfIsNotZero(Runtime.Runtime.PyList_Reverse(borrowed)); } } diff --git a/src/embed_tests/TestCallbacks.cs b/src/embed_tests/TestCallbacks.cs index 88b84d0c3..a9b057142 100644 --- a/src/embed_tests/TestCallbacks.cs +++ b/src/embed_tests/TestCallbacks.cs @@ -3,31 +3,23 @@ using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest { - public class TestCallbacks { - [OneTimeSetUp] - public void SetUp() { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() { - PythonEngine.Shutdown(); - } +namespace Python.EmbeddingTest; - [Test] - public void TestNoOverloadException() { - int passed = 0; - var aFunctionThatCallsIntoPython = new Action(value => passed = value); - using (Py.GIL()) { - 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); - string expectedArgTypes = "()"; - StringAssert.EndsWith(expectedArgTypes, error.Message); - error.Traceback.Dispose(); - } +public class TestCallbacks : BaseFixture +{ + [Test] + public void TestNoOverloadException() + { + int passed = 0; + var aFunctionThatCallsIntoPython = new Action(value => passed = value); + using (Py.GIL()) { + 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); + 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 0686d528b..7570d9498 100644 --- a/src/embed_tests/TestConverter.cs +++ b/src/embed_tests/TestConverter.cs @@ -9,206 +9,193 @@ using PyRuntime = Python.Runtime.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestConverter : BaseFixture { - public class TestConverter + static readonly Type[] _numTypes = new Type[] { - static readonly Type[] _numTypes = new Type[] - { - typeof(short), - typeof(ushort), - typeof(int), - typeof(uint), - typeof(long), - typeof(ulong) - }; + typeof(short), + typeof(ushort), + typeof(int), + typeof(uint), + typeof(long), + typeof(ulong) + }; + + [Test] + public void TestConvertSingleToManaged( + [Values(float.PositiveInfinity, float.NegativeInfinity, float.MinValue, float.MaxValue, float.NaN, + float.Epsilon)] float testValue) + { + var pyFloat = new PyFloat(testValue); - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } + object convertedValue; + var converted = Converter.ToManaged(pyFloat, typeof(float), out convertedValue, false); - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } + Assert.IsTrue(converted); + Assert.IsTrue(((float) convertedValue).Equals(testValue)); + } - [Test] - public void TestConvertSingleToManaged( - [Values(float.PositiveInfinity, float.NegativeInfinity, float.MinValue, float.MaxValue, float.NaN, - float.Epsilon)] float testValue) - { - var pyFloat = new PyFloat(testValue); + [Test] + public void TestConvertDoubleToManaged( + [Values(double.PositiveInfinity, double.NegativeInfinity, double.MinValue, double.MaxValue, double.NaN, + double.Epsilon)] double testValue) + { + var pyFloat = new PyFloat(testValue); - object convertedValue; - var converted = Converter.ToManaged(pyFloat, typeof(float), out convertedValue, false); + object convertedValue; + var converted = Converter.ToManaged(pyFloat, typeof(double), out convertedValue, false); - Assert.IsTrue(converted); - Assert.IsTrue(((float) convertedValue).Equals(testValue)); - } + Assert.IsTrue(converted); + Assert.IsTrue(((double) convertedValue).Equals(testValue)); + } - [Test] - public void TestConvertDoubleToManaged( - [Values(double.PositiveInfinity, double.NegativeInfinity, double.MinValue, double.MaxValue, double.NaN, - double.Epsilon)] double testValue) + [Test] + public void CovertTypeError() + { + Type[] floatTypes = new Type[] { - var pyFloat = new PyFloat(testValue); - - object convertedValue; - var converted = Converter.ToManaged(pyFloat, typeof(double), out convertedValue, false); - - Assert.IsTrue(converted); - Assert.IsTrue(((double) convertedValue).Equals(testValue)); - } - - [Test] - public void CovertTypeError() + typeof(float), + typeof(double) + }; + using (var s = new PyString("abc")) { - Type[] floatTypes = new Type[] + foreach (var type in _numTypes.Union(floatTypes)) { - typeof(float), - typeof(double) - }; - using (var s = new PyString("abc")) - { - foreach (var type in _numTypes.Union(floatTypes)) + object value; + try { - object value; - try - { - bool res = Converter.ToManaged(s, type, out value, true); - Assert.IsFalse(res); - var bo = Exceptions.ExceptionMatches(Exceptions.TypeError); - Assert.IsTrue(Exceptions.ExceptionMatches(Exceptions.TypeError) - || Exceptions.ExceptionMatches(Exceptions.ValueError)); - } - finally - { - Exceptions.Clear(); - } + bool res = Converter.ToManaged(s, type, out value, true); + Assert.IsFalse(res); + var bo = Exceptions.ExceptionMatches(Exceptions.TypeError); + Assert.IsTrue(Exceptions.ExceptionMatches(Exceptions.TypeError) + || Exceptions.ExceptionMatches(Exceptions.ValueError)); + } + finally + { + Exceptions.Clear(); } } } + } - [Test] - public void ConvertOverflow() + [Test] + public void ConvertOverflow() + { + using (var num = new PyInt(ulong.MaxValue)) { - using (var num = new PyInt(ulong.MaxValue)) + using var largeNum = PyRuntime.PyNumber_Add(num, num); + try { - using var largeNum = PyRuntime.PyNumber_Add(num, num); - try - { - object value; - foreach (var type in _numTypes) - { - bool res = Converter.ToManaged(largeNum.BorrowOrThrow(), type, out value, true); - Assert.IsFalse(res); - Assert.IsTrue(Exceptions.ExceptionMatches(Exceptions.OverflowError)); - Exceptions.Clear(); - } - } - finally + object value; + foreach (var type in _numTypes) { + bool res = Converter.ToManaged(largeNum.BorrowOrThrow(), type, out value, true); + Assert.IsFalse(res); + Assert.IsTrue(Exceptions.ExceptionMatches(Exceptions.OverflowError)); Exceptions.Clear(); } } + finally + { + Exceptions.Clear(); + } } + } - [Test] - public void NoImplicitConversionToBool() - { - var pyObj = new PyList(items: new[] { 1.ToPython(), 2.ToPython() }).ToPython(); - Assert.Throws(() => pyObj.As()); - } + [Test] + public void NoImplicitConversionToBool() + { + var pyObj = new PyList(items: new[] { 1.ToPython(), 2.ToPython() }).ToPython(); + Assert.Throws(() => pyObj.As()); + } - [Test] - public void ToNullable() - { - const int Const = 42; - var i = new PyInt(Const); - var ni = i.As(); - Assert.AreEqual(Const, ni); - } + [Test] + public void ToNullable() + { + const int Const = 42; + var i = new PyInt(Const); + var ni = i.As(); + Assert.AreEqual(Const, ni); + } - [Test] - public void BigIntExplicit() - { - BigInteger val = 42; - var i = new PyInt(val); - var ni = i.As(); - Assert.AreEqual(val, ni); - var nullable = i.As(); - Assert.AreEqual(val, nullable); - } + [Test] + public void BigIntExplicit() + { + BigInteger val = 42; + var i = new PyInt(val); + var ni = i.As(); + Assert.AreEqual(val, ni); + var nullable = i.As(); + Assert.AreEqual(val, nullable); + } - [Test] - public void PyIntImplicit() - { - var i = new PyInt(1); - var ni = (PyObject)i.As(); - Assert.IsTrue(PythonReferenceComparer.Instance.Equals(i, ni)); - } + [Test] + public void PyIntImplicit() + { + var i = new PyInt(1); + var ni = (PyObject)i.As(); + Assert.IsTrue(PythonReferenceComparer.Instance.Equals(i, ni)); + } - [Test] - public void ToPyList() - { - var list = new PyList(); - list.Append("hello".ToPython()); - list.Append("world".ToPython()); - var back = list.ToPython().As(); - Assert.AreEqual(list.Length(), back.Length()); - } + [Test] + public void ToPyList() + { + var list = new PyList(); + list.Append("hello".ToPython()); + list.Append("world".ToPython()); + var back = list.ToPython().As(); + Assert.AreEqual(list.Length(), back.Length()); + } - [Test] - public void RawListProxy() - { - var list = new List {"hello", "world"}; - var listProxy = PyObject.FromManagedObject(list); - var clrObject = (CLRObject)ManagedType.GetManagedObject(listProxy); - Assert.AreSame(list, clrObject.inst); - } + [Test] + public void RawListProxy() + { + var list = new List {"hello", "world"}; + var listProxy = PyObject.FromManagedObject(list); + var clrObject = (CLRObject)ManagedType.GetManagedObject(listProxy); + Assert.AreSame(list, clrObject.inst); + } - [Test] - public void RawPyObjectProxy() - { - var pyObject = "hello world!".ToPython(); - var pyObjectProxy = PyObject.FromManagedObject(pyObject); - var clrObject = (CLRObject)ManagedType.GetManagedObject(pyObjectProxy); - Assert.AreSame(pyObject, clrObject.inst); + [Test] + public void RawPyObjectProxy() + { + var pyObject = "hello world!".ToPython(); + var pyObjectProxy = PyObject.FromManagedObject(pyObject); + var clrObject = (CLRObject)ManagedType.GetManagedObject(pyObjectProxy); + Assert.AreSame(pyObject, clrObject.inst); #pragma warning disable CS0612 // Type or member is obsolete - const string handlePropertyName = nameof(PyObject.Handle); + 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); - } + var proxiedHandle = pyObjectProxy.GetAttr(handlePropertyName).As(); + Assert.AreEqual(pyObject.DangerousGetAddressOrNull(), proxiedHandle); + } - // regression for https://github.com/pythonnet/pythonnet/issues/451 - [Test] - public void CanGetListFromDerivedClass() - { - using var scope = Py.CreateScope(); - scope.Import(typeof(GetListImpl).Namespace, asname: "test"); - scope.Exec(@" + // regression for https://github.com/pythonnet/pythonnet/issues/451 + [Test] + public void CanGetListFromDerivedClass() + { + using var scope = Py.CreateScope(); + scope.Import(typeof(GetListImpl).Namespace, asname: "test"); + scope.Exec(@" class PyGetListImpl(test.GetListImpl): pass "); - var pyImpl = scope.Get("PyGetListImpl"); - dynamic inst = pyImpl.Invoke(); - List result = inst.GetList(); - CollectionAssert.AreEqual(new[] { "testing" }, result); - } + var pyImpl = scope.Get("PyGetListImpl"); + dynamic inst = pyImpl.Invoke(); + List result = inst.GetList(); + CollectionAssert.AreEqual(new[] { "testing" }, result); } +} - public interface IGetList - { - List GetList(); - } +public interface IGetList +{ + List GetList(); +} - public class GetListImpl : IGetList - { - public List GetList() => new() { "testing" }; - } +public class GetListImpl : IGetList +{ + public List GetList() => new() { "testing" }; } diff --git a/src/embed_tests/TestCustomMarshal.cs b/src/embed_tests/TestCustomMarshal.cs index 312863d0c..0b00147b3 100644 --- a/src/embed_tests/TestCustomMarshal.cs +++ b/src/embed_tests/TestCustomMarshal.cs @@ -1,35 +1,21 @@ -using System; using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestCustomMarshal : BaseFixture { - public class TestCustomMarshal + [Test] + public static void GetManagedStringTwice() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - - [Test] - public static void GetManagedStringTwice() - { - const string expected = "FooBar"; + const string expected = "FooBar"; - using var op = Runtime.Runtime.PyString_FromString(expected); - string s1 = Runtime.Runtime.GetManagedString(op.BorrowOrThrow()); - string s2 = Runtime.Runtime.GetManagedString(op.Borrow()); + using var op = Runtime.Runtime.PyString_FromString(expected); + 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.AreEqual(1, Runtime.Runtime.Refcount32(op.Borrow())); + Assert.AreEqual(expected, s1); + Assert.AreEqual(expected, s2); } } diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs index a0f9b63eb..b2b5fa85a 100644 --- a/src/embed_tests/TestDomainReload.cs +++ b/src/embed_tests/TestDomainReload.cs @@ -303,7 +303,7 @@ static void RunDomainReloadSteps() where T1 : CrossCaller where T2 : Cro var caller = CreateInstanceInstanceAndUnwrap(domain); caller.Execute(arg); - theProxy.Call("ShutdownPythonCompletely"); + theProxy.Call("ShutdownPython"); } finally { @@ -367,14 +367,7 @@ public static void InitPython(string dllName) public static void ShutdownPython() { PythonEngine.EndAllowThreads(_state); - PythonEngine.Shutdown(); - } - - public static void ShutdownPythonCompletely() - { - PythonEngine.EndAllowThreads(_state); - - PythonEngine.Shutdown(); + PythonEngine.Shutdown(allowReload: true); } static void OnDomainUnload(object sender, EventArgs e) diff --git a/src/embed_tests/TestFinalizer.cs b/src/embed_tests/TestFinalizer.cs index b748a2244..cf01c60f0 100644 --- a/src/embed_tests/TestFinalizer.cs +++ b/src/embed_tests/TestFinalizer.cs @@ -1,244 +1,244 @@ using NUnit.Framework; + using Python.Runtime; + using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestFinalizer { - public class TestFinalizer + private int _oldThreshold; + + [SetUp] + public void SetUp() { - private int _oldThreshold; + _oldThreshold = Finalizer.Instance.Threshold; + PythonEngine.Initialize(); + Exceptions.Clear(); + } + + [TearDown] + public void TearDown() + { + Finalizer.Instance.Threshold = _oldThreshold; + PythonEngine.Shutdown(allowReload: true); + } + + private static void FullGCCollect() + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } - [SetUp] - public void SetUp() + [Test] + [Obsolete("GC tests are not guaranteed")] + public void CollectBasicObject() + { + Assert.IsTrue(Finalizer.Instance.Enable); + + Finalizer.Instance.Threshold = 1; + bool called = false; + var objectCount = 0; + EventHandler handler = (s, e) => { - _oldThreshold = Finalizer.Instance.Threshold; - PythonEngine.Initialize(); - Exceptions.Clear(); - } + objectCount = e.ObjectCount; + called = true; + }; + + Assert.IsFalse(called, "The event handler was called before it was installed"); + Finalizer.Instance.BeforeCollect += handler; + + IntPtr pyObj = MakeAGarbage(out var shortWeak, out var longWeak); + FullGCCollect(); + // The object has been resurrected + Warn.If( + shortWeak.IsAlive, + "The referenced object is alive although it should have been collected", + shortWeak + ); + Assert.IsTrue( + longWeak.IsAlive, + "The reference object is not alive although it should still be", + longWeak + ); - [TearDown] - public void TearDown() { - Finalizer.Instance.Threshold = _oldThreshold; - PythonEngine.Shutdown(); + var garbage = Finalizer.Instance.GetCollectedObjects(); + Assert.NotZero(garbage.Count, "There should still be garbage around"); + Warn.Unless( + garbage.Contains(pyObj), + $"The {nameof(longWeak)} reference doesn't show up in the garbage list", + garbage + ); } - - private static void FullGCCollect() + try { - GC.Collect(); - GC.WaitForPendingFinalizers(); + Finalizer.Instance.Collect(); } - - [Test] - [Obsolete("GC tests are not guaranteed")] - public void CollectBasicObject() + finally { - Assert.IsTrue(Finalizer.Instance.Enable); - - Finalizer.Instance.Threshold = 1; - bool called = false; - var objectCount = 0; - EventHandler handler = (s, e) => - { - objectCount = e.ObjectCount; - called = true; - }; - - Assert.IsFalse(called, "The event handler was called before it was installed"); - Finalizer.Instance.BeforeCollect += handler; - - IntPtr pyObj = MakeAGarbage(out var shortWeak, out var longWeak); - FullGCCollect(); - // The object has been resurrected - Warn.If( - shortWeak.IsAlive, - "The referenced object is alive although it should have been collected", - shortWeak - ); - Assert.IsTrue( - longWeak.IsAlive, - "The reference object is not alive although it should still be", - longWeak - ); - - { - var garbage = Finalizer.Instance.GetCollectedObjects(); - Assert.NotZero(garbage.Count, "There should still be garbage around"); - Warn.Unless( - garbage.Contains(pyObj), - $"The {nameof(longWeak)} reference doesn't show up in the garbage list", - garbage - ); - } - try - { - Finalizer.Instance.Collect(); - } - finally - { - Finalizer.Instance.BeforeCollect -= handler; - } - Assert.IsTrue(called, "The event handler was not called during finalization"); - Assert.GreaterOrEqual(objectCount, 1); + Finalizer.Instance.BeforeCollect -= handler; } + Assert.IsTrue(called, "The event handler was not called during finalization"); + Assert.GreaterOrEqual(objectCount, 1); + } - [Test] - [Obsolete("GC tests are not guaranteed")] - public void CollectOnShutdown() + [Test] + // [Obsolete("GC tests are not guaranteed")] + public void CollectOnShutdown() + { + IntPtr op = MakeAGarbage(out var shortWeak, out var longWeak); + FullGCCollect(); + Assert.IsFalse(shortWeak.IsAlive); + List garbage = Finalizer.Instance.GetCollectedObjects(); + Assert.IsNotEmpty(garbage, "The garbage object should be collected"); + Assert.IsTrue(garbage.Contains(op), + "Garbage should contains the collected object"); + + PythonEngine.Shutdown(allowReload: true); + garbage = Finalizer.Instance.GetCollectedObjects(); + + if (garbage.Count > 0) { - IntPtr op = MakeAGarbage(out var shortWeak, out var longWeak); - FullGCCollect(); - Assert.IsFalse(shortWeak.IsAlive); - List garbage = Finalizer.Instance.GetCollectedObjects(); - Assert.IsNotEmpty(garbage, "The garbage object should be collected"); - Assert.IsTrue(garbage.Contains(op), - "Garbage should contains the collected object"); - - PythonEngine.Shutdown(); - garbage = Finalizer.Instance.GetCollectedObjects(); - - if (garbage.Count > 0) + PythonEngine.Initialize(); + string objects = string.Join("\n", garbage.Select(ob => { - PythonEngine.Initialize(); - string objects = string.Join("\n", garbage.Select(ob => - { - var obj = new PyObject(new BorrowedReference(ob)); - return $"{obj} [{obj.GetPythonType()}@{obj.Handle}]"; - })); - Assert.Fail("Garbage is not empty:\n" + objects); - } + var obj = new PyObject(new BorrowedReference(ob)); + return $"{obj} [{obj.GetPythonType()}@{obj.Handle}]"; + })); + Assert.Fail("Garbage is not empty:\n" + objects); } + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] // ensure lack of references to obj + [Obsolete("GC tests are not guaranteed")] + private static IntPtr MakeAGarbage(out WeakReference shortWeak, out WeakReference longWeak) + { + IntPtr handle = IntPtr.Zero; + WeakReference @short = null, @long = null; + // must create Python object in the thread where we have GIL + IntPtr val = Runtime.Runtime.PyLong_FromLongLong(1024).DangerousMoveToPointerOrNull(); + // must create temp object in a different thread to ensure it is not present + // when conservatively scanning stack for GC roots. + // see https://xamarin.github.io/bugzilla-archives/17/17593/bug.html + var garbageGen = new Thread(() => + { + var obj = new PyObject(val, skipCollect: true); + @short = new WeakReference(obj); + @long = new WeakReference(obj, true); + handle = obj.Handle; + }); + garbageGen.Start(); + Assert.IsTrue(garbageGen.Join(TimeSpan.FromSeconds(5)), "Garbage creation timed out"); + shortWeak = @short; + longWeak = @long; + return handle; + } - [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] // ensure lack of references to obj - [Obsolete("GC tests are not guaranteed")] - private static IntPtr MakeAGarbage(out WeakReference shortWeak, out WeakReference longWeak) + private static long CompareWithFinalizerOn(PyObject pyCollect, bool enbale) + { + // Must larger than 512 bytes make sure Python use + string str = new string('1', 1024); + Finalizer.Instance.Enable = true; + FullGCCollect(); + FullGCCollect(); + pyCollect.Invoke(); + Finalizer.Instance.Collect(); + Finalizer.Instance.Enable = enbale; + + // Estimate unmanaged memory size + long before = Environment.WorkingSet - GC.GetTotalMemory(true); + for (int i = 0; i < 10000; i++) { - IntPtr handle = IntPtr.Zero; - WeakReference @short = null, @long = null; - // must create Python object in the thread where we have GIL - IntPtr val = Runtime.Runtime.PyLong_FromLongLong(1024).DangerousMoveToPointerOrNull(); - // must create temp object in a different thread to ensure it is not present - // when conservatively scanning stack for GC roots. - // see https://xamarin.github.io/bugzilla-archives/17/17593/bug.html - var garbageGen = new Thread(() => - { - var obj = new PyObject(val, skipCollect: true); - @short = new WeakReference(obj); - @long = new WeakReference(obj, true); - handle = obj.Handle; - }); - garbageGen.Start(); - Assert.IsTrue(garbageGen.Join(TimeSpan.FromSeconds(5)), "Garbage creation timed out"); - shortWeak = @short; - longWeak = @long; - return handle; + // Memory will leak when disable Finalizer + new PyString(str); } - - private static long CompareWithFinalizerOn(PyObject pyCollect, bool enbale) + FullGCCollect(); + FullGCCollect(); + pyCollect.Invoke(); + if (enbale) { - // Must larger than 512 bytes make sure Python use - string str = new string('1', 1024); - Finalizer.Instance.Enable = true; - FullGCCollect(); - FullGCCollect(); - pyCollect.Invoke(); Finalizer.Instance.Collect(); - Finalizer.Instance.Enable = enbale; - - // Estimate unmanaged memory size - long before = Environment.WorkingSet - GC.GetTotalMemory(true); - for (int i = 0; i < 10000; i++) - { - // Memory will leak when disable Finalizer - new PyString(str); - } - FullGCCollect(); - FullGCCollect(); - pyCollect.Invoke(); - if (enbale) - { - Finalizer.Instance.Collect(); - } + } - FullGCCollect(); - FullGCCollect(); - long after = Environment.WorkingSet - GC.GetTotalMemory(true); - return after - before; + FullGCCollect(); + FullGCCollect(); + long after = Environment.WorkingSet - GC.GetTotalMemory(true); + return after - before; - } + } - /// - /// Because of two vms both have their memory manager, - /// this test only prove the finalizer has take effect. - /// - [Test] - [Ignore("Too many uncertainties, only manual on when debugging")] - public void SimpleTestMemory() + /// + /// Because of two vms both have their memory manager, + /// this test only prove the finalizer has take effect. + /// + [Test] + [Ignore("Too many uncertainties, only manual on when debugging")] + public void SimpleTestMemory() + { + bool oldState = Finalizer.Instance.Enable; + try { - bool oldState = Finalizer.Instance.Enable; - try + using (PyObject gcModule = PyModule.Import("gc")) + using (PyObject pyCollect = gcModule.GetAttr("collect")) { - using (PyObject gcModule = PyModule.Import("gc")) - using (PyObject pyCollect = gcModule.GetAttr("collect")) - { - long span1 = CompareWithFinalizerOn(pyCollect, false); - long span2 = CompareWithFinalizerOn(pyCollect, true); - Assert.Less(span2, span1); - } - } - finally - { - Finalizer.Instance.Enable = oldState; + long span1 = CompareWithFinalizerOn(pyCollect, false); + long span2 = CompareWithFinalizerOn(pyCollect, true); + Assert.Less(span2, span1); } } + finally + { + Finalizer.Instance.Enable = oldState; + } + } - [Test] - public void ValidateRefCount() + [Test] + public void ValidateRefCount() + { + if (!Finalizer.Instance.RefCountValidationEnabled) { - if (!Finalizer.Instance.RefCountValidationEnabled) - { - Assert.Ignore("Only run with FINALIZER_CHECK"); - } - IntPtr ptr = IntPtr.Zero; - bool called = false; - Finalizer.IncorrectRefCntHandler handler = (s, e) => - { - called = true; - Assert.AreEqual(ptr, e.Handle); - Assert.AreEqual(2, e.ImpactedObjects.Count); - // Fix for this test, don't do this on general environment + Assert.Ignore("Only run with FINALIZER_CHECK"); + } + IntPtr ptr = IntPtr.Zero; + bool called = false; + Finalizer.IncorrectRefCntHandler handler = (s, e) => + { + called = true; + Assert.AreEqual(ptr, e.Handle); + Assert.AreEqual(2, e.ImpactedObjects.Count); + // 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); + Runtime.Runtime.XIncref(e.Reference); #pragma warning restore CS0618 // Type or member is obsolete - return false; - }; - Finalizer.Instance.IncorrectRefCntResolver += handler; - try - { - ptr = CreateStringGarbage(); - FullGCCollect(); - Assert.Throws(() => Finalizer.Instance.Collect()); - Assert.IsTrue(called); - } - finally - { - Finalizer.Instance.IncorrectRefCntResolver -= handler; - } + return false; + }; + Finalizer.Instance.IncorrectRefCntResolver += handler; + try + { + ptr = CreateStringGarbage(); + FullGCCollect(); + Assert.Throws(() => Finalizer.Instance.Collect()); + Assert.IsTrue(called); } - - [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] // ensure lack of references to s1 and s2 - private static IntPtr CreateStringGarbage() + finally { - PyString s1 = new PyString("test_string"); - // s2 steal a reference from s1 - IntPtr address = s1.Reference.DangerousGetAddress(); - PyString s2 = new (StolenReference.DangerousFromPointer(address)); - return address; + Finalizer.Instance.IncorrectRefCntResolver -= handler; } } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] // ensure lack of references to s1 and s2 + private static IntPtr CreateStringGarbage() + { + PyString s1 = new PyString("test_string"); + // s2 steal a reference from s1 + IntPtr address = s1.Reference.DangerousGetAddress(); + PyString s2 = new (StolenReference.DangerousFromPointer(address)); + return address; + } } diff --git a/src/embed_tests/TestGILState.cs b/src/embed_tests/TestGILState.cs index bf6f02dc6..1b121cdcf 100644 --- a/src/embed_tests/TestGILState.cs +++ b/src/embed_tests/TestGILState.cs @@ -1,33 +1,20 @@ -namespace Python.EmbeddingTest -{ - using NUnit.Framework; - using Python.Runtime; +using NUnit.Framework; +using Python.Runtime; - public class TestGILState - { - /// - /// Ensure, that calling multiple times is safe - /// - [Test] - public void CanDisposeMultipleTimes() - { - using (var gilState = Py.GIL()) - { - for(int i = 0; i < 50; i++) - gilState.Dispose(); - } - } +namespace Python.EmbeddingTest; - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() +public class TestGILState : BaseFixture +{ + /// + /// Ensure, that calling multiple times is safe + /// + [Test] + public void CanDisposeMultipleTimes() + { + using (var gilState = Py.GIL()) { - PythonEngine.Shutdown(); + for(int i = 0; i < 50; i++) + gilState.Dispose(); } } } diff --git a/src/embed_tests/TestInstanceWrapping.cs b/src/embed_tests/TestInstanceWrapping.cs index 0a441c823..7ec9f43ff 100644 --- a/src/embed_tests/TestInstanceWrapping.cs +++ b/src/embed_tests/TestInstanceWrapping.cs @@ -3,66 +3,53 @@ using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestInstanceWrapping : BaseFixture { - public class TestInstanceWrapping + // regression test for https://github.com/pythonnet/pythonnet/issues/811 + [Test] + public void OverloadResolution_UnknownToObject() { - [OneTimeSetUp] - public void SetUp() + var overloaded = new Overloaded(); + using (Py.GIL()) { - PythonEngine.Initialize(); - } + var o = overloaded.ToPython(); - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); + dynamic callWithSelf = PythonEngine.Eval("lambda o: o.ObjOrClass(object())"); + callWithSelf(o); + Assert.AreEqual(Overloaded.Object, overloaded.Value); } + } - // regression test for https://github.com/pythonnet/pythonnet/issues/811 - [Test] - public void OverloadResolution_UnknownToObject() - { - var overloaded = new Overloaded(); - using (Py.GIL()) - { - var o = overloaded.ToPython(); - - dynamic callWithSelf = PythonEngine.Eval("lambda o: o.ObjOrClass(object())"); - callWithSelf(o); - Assert.AreEqual(Overloaded.Object, overloaded.Value); - } - } - - [Test] - public void WeakRefIsNone_AfterObjectIsGone() - { - using var makeref = Py.Import("weakref").GetAttr("ref"); - var ub = new UriBuilder().ToPython(); - using var weakref = makeref.Invoke(ub); - ub.Dispose(); - Assert.IsTrue(weakref.Invoke().IsNone()); - } - - class Base {} - class Derived: Base { } - - class Overloaded: Derived - { - public int Value { get; set; } - public void IntOrStr(int arg) => this.Value = arg; - public void IntOrStr(string arg) => - this.Value = int.Parse(arg, NumberStyles.Integer, CultureInfo.InvariantCulture); + [Test] + public void WeakRefIsNone_AfterObjectIsGone() + { + using var makeref = Py.Import("weakref").GetAttr("ref"); + var ub = new UriBuilder().ToPython(); + using var weakref = makeref.Invoke(ub); + ub.Dispose(); + Assert.IsTrue(weakref.Invoke().IsNone()); + } - public const int Object = 1; - public const int ConcreteClass = 2; - public void ObjOrClass(object _) => this.Value = Object; - public void ObjOrClass(Overloaded _) => this.Value = ConcreteClass; + class Base {} + class Derived: Base { } - public const int Base = ConcreteClass + 1; - public const int Derived = Base + 1; - public void BaseOrDerived(Base _) => this.Value = Base; - public void BaseOrDerived(Derived _) => this.Value = Derived; - } + class Overloaded: Derived + { + public int Value { get; set; } + public void IntOrStr(int arg) => this.Value = arg; + public void IntOrStr(string arg) => + this.Value = int.Parse(arg, NumberStyles.Integer, CultureInfo.InvariantCulture); + + public const int Object = 1; + public const int ConcreteClass = 2; + public void ObjOrClass(object _) => this.Value = Object; + public void ObjOrClass(Overloaded _) => this.Value = ConcreteClass; + + public const int Base = ConcreteClass + 1; + public const int Derived = Base + 1; + public void BaseOrDerived(Base _) => this.Value = Base; + public void BaseOrDerived(Derived _) => this.Value = Derived; } } diff --git a/src/embed_tests/TestInterrupt.cs b/src/embed_tests/TestInterrupt.cs index e6546adb2..7abf177cd 100644 --- a/src/embed_tests/TestInterrupt.cs +++ b/src/embed_tests/TestInterrupt.cs @@ -7,65 +7,64 @@ using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestInterrupt : BaseFixture { - public class TestInterrupt + PyObject threading; + + [OneTimeSetUp] + public void SetUp() { - PyObject threading; - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - // workaround for assert tlock.locked() warning - threading = Py.Import("threading"); - } + // workaround for assert tlock.locked() warning + threading = Py.Import("threading"); + } - [OneTimeTearDown] - public void Dispose() - { - threading.Dispose(); - PythonEngine.Shutdown(); - } + [OneTimeTearDown] + public void Dispose() + { + threading.Dispose(); + } - [Test] - public void PythonThreadIDStable() + [Test] + public void PythonThreadIDStable() + { + long pythonThreadID = 0; + long pythonThreadID2 = 0; + var asyncCall = Task.Factory.StartNew(() => { - long pythonThreadID = 0; - long pythonThreadID2 = 0; - var asyncCall = Task.Factory.StartNew(() => - { - using (Py.GIL()) - { - Interlocked.Exchange(ref pythonThreadID, (long)PythonEngine.GetPythonThreadID()); - Interlocked.Exchange(ref pythonThreadID2, (long)PythonEngine.GetPythonThreadID()); - } - }); - - var timeout = Stopwatch.StartNew(); - - IntPtr threadState = PythonEngine.BeginAllowThreads(); - while (Interlocked.Read(ref pythonThreadID) == 0 || Interlocked.Read(ref pythonThreadID2) == 0) + using (Py.GIL()) { - Assert.Less(timeout.Elapsed, TimeSpan.FromSeconds(5), "thread IDs were not assigned in time"); + Interlocked.Exchange(ref pythonThreadID, (long)PythonEngine.GetPythonThreadID()); + Interlocked.Exchange(ref pythonThreadID2, (long)PythonEngine.GetPythonThreadID()); } - PythonEngine.EndAllowThreads(threadState); + }); - Assert.IsTrue(asyncCall.Wait(TimeSpan.FromSeconds(5)), "Async thread has not finished in time"); + var timeout = Stopwatch.StartNew(); - Assert.AreEqual(pythonThreadID, pythonThreadID2); - Assert.NotZero(pythonThreadID); + IntPtr threadState = PythonEngine.BeginAllowThreads(); + while (Interlocked.Read(ref pythonThreadID) == 0 || Interlocked.Read(ref pythonThreadID2) == 0) + { + Assert.Less(timeout.Elapsed, TimeSpan.FromSeconds(5), "thread IDs were not assigned in time"); } + PythonEngine.EndAllowThreads(threadState); + + Assert.IsTrue(asyncCall.Wait(TimeSpan.FromSeconds(5)), "Async thread has not finished in time"); - [Test] - public void InterruptTest() + Assert.AreEqual(pythonThreadID, pythonThreadID2); + Assert.NotZero(pythonThreadID); + } + + [Test] + public void InterruptTest() + { + long pythonThreadID = 0; + var asyncCall = Task.Factory.StartNew(() => { - long pythonThreadID = 0; - var asyncCall = Task.Factory.StartNew(() => + using (Py.GIL()) { - using (Py.GIL()) - { - Interlocked.Exchange(ref pythonThreadID, (long)PythonEngine.GetPythonThreadID()); - return PythonEngine.RunSimpleString(@" + Interlocked.Exchange(ref pythonThreadID, (long)PythonEngine.GetPythonThreadID()); + return PythonEngine.RunSimpleString(@" try: import time @@ -73,26 +72,25 @@ import time time.sleep(0.2) except KeyboardInterrupt: pass"); - } - }); + } + }); - var timeout = Stopwatch.StartNew(); + var timeout = Stopwatch.StartNew(); - IntPtr threadState = PythonEngine.BeginAllowThreads(); - while (Interlocked.Read(ref pythonThreadID) == 0) - { - Assert.Less(timeout.Elapsed, TimeSpan.FromSeconds(5), "thread ID was not assigned in time"); - } - PythonEngine.EndAllowThreads(threadState); + IntPtr threadState = PythonEngine.BeginAllowThreads(); + while (Interlocked.Read(ref pythonThreadID) == 0) + { + Assert.Less(timeout.Elapsed, TimeSpan.FromSeconds(5), "thread ID was not assigned in time"); + } + PythonEngine.EndAllowThreads(threadState); - int interruptReturnValue = PythonEngine.Interrupt((ulong)Interlocked.Read(ref pythonThreadID)); - Assert.AreEqual(1, interruptReturnValue); + int interruptReturnValue = PythonEngine.Interrupt((ulong)Interlocked.Read(ref pythonThreadID)); + Assert.AreEqual(1, interruptReturnValue); - threadState = PythonEngine.BeginAllowThreads(); - Assert.IsTrue(asyncCall.Wait(TimeSpan.FromSeconds(5)), "Async thread was not interrupted in time"); - PythonEngine.EndAllowThreads(threadState); + threadState = PythonEngine.BeginAllowThreads(); + Assert.IsTrue(asyncCall.Wait(TimeSpan.FromSeconds(5)), "Async thread was not interrupted in time"); + PythonEngine.EndAllowThreads(threadState); - Assert.AreEqual(0, asyncCall.Result); - } + Assert.AreEqual(0, asyncCall.Result); } } diff --git a/src/embed_tests/TestNamedArguments.cs b/src/embed_tests/TestNamedArguments.cs index c86302038..ecd0ae7cd 100644 --- a/src/embed_tests/TestNamedArguments.cs +++ b/src/embed_tests/TestNamedArguments.cs @@ -2,54 +2,42 @@ using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestNamedArguments : BaseFixture { - public class TestNamedArguments + /// + /// Test named arguments support through Py.kw method + /// + [Test] + public void TestKeywordArgs() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - - /// - /// Test named arguments support through Py.kw method - /// - [Test] - public void TestKeywordArgs() - { - dynamic a = CreateTestClass(); - var result = (int)a.Test3(2, Py.kw("a4", 8)); + dynamic a = CreateTestClass(); + var result = (int)a.Test3(2, Py.kw("a4", 8)); - Assert.AreEqual(12, result); - } + Assert.AreEqual(12, result); + } - /// - /// Test keyword arguments with .net named arguments - /// - [Test] - public void TestNamedArgs() - { - dynamic a = CreateTestClass(); - var result = (int)a.Test3(2, a4: 8); + /// + /// Test keyword arguments with .net named arguments + /// + [Test] + public void TestNamedArgs() + { + dynamic a = CreateTestClass(); + var result = (int)a.Test3(2, a4: 8); - Assert.AreEqual(12, result); - } + Assert.AreEqual(12, result); + } - private static PyObject CreateTestClass() - { - var locals = new PyDict(); + private static PyObject CreateTestClass() + { + var locals = new PyDict(); - PythonEngine.Exec(@" + PythonEngine.Exec(@" class cmTest3: def Test3(self, a1 = 1, a2 = 1, a3 = 1, a4 = 1): return a1 + a2 + a3 + a4 @@ -57,8 +45,7 @@ def Test3(self, a1 = 1, a2 = 1, a3 = 1, a4 = 1): a = cmTest3() ", null, locals); - return locals.GetItem("a"); - } - + return locals.GetItem("a"); } + } diff --git a/src/embed_tests/TestNativeTypeOffset.cs b/src/embed_tests/TestNativeTypeOffset.cs index d692c24e6..ff3a72baa 100644 --- a/src/embed_tests/TestNativeTypeOffset.cs +++ b/src/embed_tests/TestNativeTypeOffset.cs @@ -1,45 +1,25 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - using NUnit.Framework; - using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestNativeTypeOffset : BaseFixture { - public class TestNativeTypeOffset + /// + /// Tests that installation has generated code for NativeTypeOffset and that it can be loaded. + /// + [Test] + public void LoadNativeTypeOffsetClass() { - [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. - /// - [Test] - public void LoadNativeTypeOffsetClass() + PyObject sys = Py.Import("sys"); + // We can safely ignore the "m" abi flag + var abiflags = sys.HasAttr("abiflags") ? sys.GetAttr("abiflags").ToString() : ""; + abiflags = abiflags.Replace("m", ""); + if (!string.IsNullOrEmpty(abiflags)) { - PyObject sys = Py.Import("sys"); - // We can safely ignore the "m" abi flag - var abiflags = sys.HasAttr("abiflags") ? sys.GetAttr("abiflags").ToString() : ""; - abiflags = abiflags.Replace("m", ""); - if (!string.IsNullOrEmpty(abiflags)) - { - string typeName = "Python.Runtime.NativeTypeOffset, Python.Runtime"; - Assert.NotNull(Type.GetType(typeName), $"{typeName} does not exist and sys.abiflags={abiflags}"); - } + string typeName = "Python.Runtime.NativeTypeOffset, Python.Runtime"; + Assert.NotNull(Type.GetType(typeName), $"{typeName} does not exist and sys.abiflags={abiflags}"); } } } diff --git a/src/embed_tests/TestOperator.cs b/src/embed_tests/TestOperator.cs index a5713274a..34df538ba 100644 --- a/src/embed_tests/TestOperator.cs +++ b/src/embed_tests/TestOperator.cs @@ -4,279 +4,266 @@ using Python.Runtime.Codecs; using System; -using System.Linq; using System.Reflection; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestOperator : BaseFixture { - public class TestOperator + public class OperableObject { - [OneTimeSetUp] - public void SetUp() + public int Num { get; set; } + + public override int GetHashCode() { - PythonEngine.Initialize(); + return unchecked(159832395 + Num.GetHashCode()); } - [OneTimeTearDown] - public void Dispose() + public override bool Equals(object obj) { - PythonEngine.Shutdown(); + return obj is OperableObject @object && + Num == @object.Num; } - public class OperableObject + public OperableObject(int num) { - public int Num { get; set; } - - public override int GetHashCode() - { - return unchecked(159832395 + Num.GetHashCode()); - } - - public override bool Equals(object obj) - { - return obj is OperableObject @object && - Num == @object.Num; - } - - public OperableObject(int num) - { - Num = num; - } - - public static OperableObject operator ~(OperableObject a) - { - return new OperableObject(~a.Num); - } + Num = num; + } - public static OperableObject operator +(OperableObject a) - { - return new OperableObject(+a.Num); - } + public static OperableObject operator ~(OperableObject a) + { + return new OperableObject(~a.Num); + } - public static OperableObject operator -(OperableObject a) - { - return new OperableObject(-a.Num); - } + public static OperableObject operator +(OperableObject a) + { + return new OperableObject(+a.Num); + } - public static OperableObject operator +(int a, OperableObject b) - { - return new OperableObject(a + b.Num); - } - public static OperableObject operator +(OperableObject a, OperableObject b) - { - return new OperableObject(a.Num + b.Num); - } - public static OperableObject operator +(OperableObject a, int b) - { - return new OperableObject(a.Num + b); - } + public static OperableObject operator -(OperableObject a) + { + return new OperableObject(-a.Num); + } - public static OperableObject operator -(int a, OperableObject b) - { - return new OperableObject(a - b.Num); - } - public static OperableObject operator -(OperableObject a, OperableObject b) - { - return new OperableObject(a.Num - b.Num); - } - public static OperableObject operator -(OperableObject a, int b) - { - return new OperableObject(a.Num - b); - } + public static OperableObject operator +(int a, OperableObject b) + { + return new OperableObject(a + b.Num); + } + public static OperableObject operator +(OperableObject a, OperableObject b) + { + return new OperableObject(a.Num + b.Num); + } + public static OperableObject operator +(OperableObject a, int b) + { + return new OperableObject(a.Num + b); + } - public static OperableObject operator *(int a, OperableObject b) - { - return new OperableObject(a * b.Num); - } - public static OperableObject operator *(OperableObject a, OperableObject b) - { - return new OperableObject(a.Num * b.Num); - } - public static OperableObject operator *(OperableObject a, int b) - { - return new OperableObject(a.Num * b); - } + public static OperableObject operator -(int a, OperableObject b) + { + return new OperableObject(a - b.Num); + } + public static OperableObject operator -(OperableObject a, OperableObject b) + { + return new OperableObject(a.Num - b.Num); + } + public static OperableObject operator -(OperableObject a, int b) + { + return new OperableObject(a.Num - b); + } - public static OperableObject operator /(int a, OperableObject b) - { - return new OperableObject(a / b.Num); - } - public static OperableObject operator /(OperableObject a, OperableObject b) - { - return new OperableObject(a.Num / b.Num); - } - public static OperableObject operator /(OperableObject a, int b) - { - return new OperableObject(a.Num / b); - } + public static OperableObject operator *(int a, OperableObject b) + { + return new OperableObject(a * b.Num); + } + public static OperableObject operator *(OperableObject a, OperableObject b) + { + return new OperableObject(a.Num * b.Num); + } + public static OperableObject operator *(OperableObject a, int b) + { + return new OperableObject(a.Num * b); + } - public static OperableObject operator %(int a, OperableObject b) - { - return new OperableObject(a % b.Num); - } - public static OperableObject operator %(OperableObject a, OperableObject b) - { - return new OperableObject(a.Num % b.Num); - } - public static OperableObject operator %(OperableObject a, int b) - { - return new OperableObject(a.Num % b); - } + public static OperableObject operator /(int a, OperableObject b) + { + return new OperableObject(a / b.Num); + } + public static OperableObject operator /(OperableObject a, OperableObject b) + { + return new OperableObject(a.Num / b.Num); + } + public static OperableObject operator /(OperableObject a, int b) + { + return new OperableObject(a.Num / b); + } - public static OperableObject operator &(int a, OperableObject b) - { - return new OperableObject(a & b.Num); - } - public static OperableObject operator &(OperableObject a, OperableObject b) - { - return new OperableObject(a.Num & b.Num); - } - public static OperableObject operator &(OperableObject a, int b) - { - return new OperableObject(a.Num & b); - } + public static OperableObject operator %(int a, OperableObject b) + { + return new OperableObject(a % b.Num); + } + public static OperableObject operator %(OperableObject a, OperableObject b) + { + return new OperableObject(a.Num % b.Num); + } + public static OperableObject operator %(OperableObject a, int b) + { + return new OperableObject(a.Num % b); + } - public static OperableObject operator |(int a, OperableObject b) - { - return new OperableObject(a | b.Num); - } - public static OperableObject operator |(OperableObject a, OperableObject b) - { - return new OperableObject(a.Num | b.Num); - } - public static OperableObject operator |(OperableObject a, int b) - { - return new OperableObject(a.Num | b); - } + public static OperableObject operator &(int a, OperableObject b) + { + return new OperableObject(a & b.Num); + } + public static OperableObject operator &(OperableObject a, OperableObject b) + { + return new OperableObject(a.Num & b.Num); + } + public static OperableObject operator &(OperableObject a, int b) + { + return new OperableObject(a.Num & b); + } - public static OperableObject operator ^(int a, OperableObject b) - { - return new OperableObject(a ^ b.Num); - } - public static OperableObject operator ^(OperableObject a, OperableObject b) - { - return new OperableObject(a.Num ^ b.Num); - } - public static OperableObject operator ^(OperableObject a, int b) - { - return new OperableObject(a.Num ^ b); - } + public static OperableObject operator |(int a, OperableObject b) + { + return new OperableObject(a | b.Num); + } + public static OperableObject operator |(OperableObject a, OperableObject b) + { + return new OperableObject(a.Num | b.Num); + } + public static OperableObject operator |(OperableObject a, int b) + { + return new OperableObject(a.Num | b); + } - public static bool operator ==(int a, OperableObject b) - { - return (a == b.Num); - } - public static bool operator ==(OperableObject a, OperableObject b) - { - return (a.Num == b.Num); - } - public static bool operator ==(OperableObject a, int b) - { - return (a.Num == b); - } + public static OperableObject operator ^(int a, OperableObject b) + { + return new OperableObject(a ^ b.Num); + } + public static OperableObject operator ^(OperableObject a, OperableObject b) + { + return new OperableObject(a.Num ^ b.Num); + } + public static OperableObject operator ^(OperableObject a, int b) + { + return new OperableObject(a.Num ^ b); + } - public static bool operator !=(int a, OperableObject b) - { - return (a != b.Num); - } - public static bool operator !=(OperableObject a, OperableObject b) - { - return (a.Num != b.Num); - } - public static bool operator !=(OperableObject a, int b) - { - return (a.Num != b); - } + public static bool operator ==(int a, OperableObject b) + { + return (a == b.Num); + } + public static bool operator ==(OperableObject a, OperableObject b) + { + return (a.Num == b.Num); + } + public static bool operator ==(OperableObject a, int b) + { + return (a.Num == b); + } - public static bool operator <=(int a, OperableObject b) - { - return (a <= b.Num); - } - public static bool operator <=(OperableObject a, OperableObject b) - { - return (a.Num <= b.Num); - } - public static bool operator <=(OperableObject a, int b) - { - return (a.Num <= b); - } + public static bool operator !=(int a, OperableObject b) + { + return (a != b.Num); + } + public static bool operator !=(OperableObject a, OperableObject b) + { + return (a.Num != b.Num); + } + public static bool operator !=(OperableObject a, int b) + { + return (a.Num != b); + } - public static bool operator >=(int a, OperableObject b) - { - return (a >= b.Num); - } - public static bool operator >=(OperableObject a, OperableObject b) - { - return (a.Num >= b.Num); - } - public static bool operator >=(OperableObject a, int b) - { - return (a.Num >= b); - } + public static bool operator <=(int a, OperableObject b) + { + return (a <= b.Num); + } + public static bool operator <=(OperableObject a, OperableObject b) + { + return (a.Num <= b.Num); + } + public static bool operator <=(OperableObject a, int b) + { + return (a.Num <= b); + } - public static bool operator >=(OperableObject a, (int, int) b) - { - using (Py.GIL()) - { - int bNum = b.Item1; - return a.Num >= bNum; - } - } - public static bool operator <=(OperableObject a, (int, int) b) - { - using (Py.GIL()) - { - int bNum = b.Item1; - return a.Num <= bNum; - } - } + public static bool operator >=(int a, OperableObject b) + { + return (a >= b.Num); + } + public static bool operator >=(OperableObject a, OperableObject b) + { + return (a.Num >= b.Num); + } + public static bool operator >=(OperableObject a, int b) + { + return (a.Num >= b); + } - public static bool operator <(int a, OperableObject b) - { - return (a < b.Num); - } - public static bool operator <(OperableObject a, OperableObject b) + public static bool operator >=(OperableObject a, (int, int) b) + { + using (Py.GIL()) { - return (a.Num < b.Num); + int bNum = b.Item1; + return a.Num >= bNum; } - public static bool operator <(OperableObject a, int b) + } + public static bool operator <=(OperableObject a, (int, int) b) + { + using (Py.GIL()) { - return (a.Num < b); + int bNum = b.Item1; + return a.Num <= bNum; } + } - public static bool operator >(int a, OperableObject b) - { - return (a > b.Num); - } - public static bool operator >(OperableObject a, OperableObject b) - { - return (a.Num > b.Num); - } - public static bool operator >(OperableObject a, int b) - { - return (a.Num > b); - } + public static bool operator <(int a, OperableObject b) + { + return (a < b.Num); + } + public static bool operator <(OperableObject a, OperableObject b) + { + return (a.Num < b.Num); + } + public static bool operator <(OperableObject a, int b) + { + return (a.Num < b); + } - public static OperableObject operator <<(OperableObject a, int offset) - { - return new OperableObject(a.Num << offset); - } + public static bool operator >(int a, OperableObject b) + { + return (a > b.Num); + } + public static bool operator >(OperableObject a, OperableObject b) + { + return (a.Num > b.Num); + } + public static bool operator >(OperableObject a, int b) + { + return (a.Num > b); + } - public static OperableObject operator >>(OperableObject a, int offset) - { - return new OperableObject(a.Num >> offset); - } + public static OperableObject operator <<(OperableObject a, int offset) + { + return new OperableObject(a.Num << offset); } - [Test] - public void SymmetricalOperatorOverloads() + public static OperableObject operator >>(OperableObject a, int offset) { - string name = string.Format("{0}.{1}", - typeof(OperableObject).DeclaringType.Name, - typeof(OperableObject).Name); - string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; + return new OperableObject(a.Num >> offset); + } + } - PythonEngine.Exec($@" + [Test] + public void SymmetricalOperatorOverloads() + { + string name = string.Format("{0}.{1}", + typeof(OperableObject).DeclaringType.Name, + typeof(OperableObject).Name); + string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; + + PythonEngine.Exec($@" from {module} import * cls = {name} a = cls(-2) @@ -333,43 +320,43 @@ public void SymmetricalOperatorOverloads() c = a > b assert c == (a.Num > b.Num) "); - } + } - [Test] - public void EnumOperator() - { - PythonEngine.Exec($@" + [Test] + public void EnumOperator() + { + PythonEngine.Exec($@" from System.IO import FileAccess c = FileAccess.Read | FileAccess.Write"); - } + } - [Test] - public void OperatorOverloadMissingArgument() - { - string name = string.Format("{0}.{1}", - typeof(OperableObject).DeclaringType.Name, - typeof(OperableObject).Name); - string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; + [Test] + public void OperatorOverloadMissingArgument() + { + string name = string.Format("{0}.{1}", + typeof(OperableObject).DeclaringType.Name, + typeof(OperableObject).Name); + string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; - Assert.Throws(() => - PythonEngine.Exec($@" + Assert.Throws(() => + PythonEngine.Exec($@" from {module} import * cls = {name} a = cls(2) b = cls(10) a.op_Addition() ")); - } + } - [Test] - public void ForwardOperatorOverloads() - { - string name = string.Format("{0}.{1}", - typeof(OperableObject).DeclaringType.Name, - typeof(OperableObject).Name); - string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; + [Test] + public void ForwardOperatorOverloads() + { + string name = string.Format("{0}.{1}", + typeof(OperableObject).DeclaringType.Name, + typeof(OperableObject).Name); + string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; - PythonEngine.Exec($@" + PythonEngine.Exec($@" from {module} import * cls = {name} a = cls(2) @@ -416,17 +403,17 @@ public void ForwardOperatorOverloads() c = a > b assert c == (a.Num > b) "); - } + } - [Test] - public void TupleComparisonOperatorOverloads() - { - TupleCodec.Register(); - string name = string.Format("{0}.{1}", - typeof(OperableObject).DeclaringType.Name, - typeof(OperableObject).Name); - string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; - PythonEngine.Exec($@" + [Test] + public void TupleComparisonOperatorOverloads() + { + TupleCodec.Register(); + string name = string.Format("{0}.{1}", + typeof(OperableObject).DeclaringType.Name, + typeof(OperableObject).Name); + string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; + PythonEngine.Exec($@" from {module} import * cls = {name} a = cls(2) @@ -444,18 +431,18 @@ public void TupleComparisonOperatorOverloads() c = b <= a assert c == (b[0] <= a.Num) "); - } + } - [Test] - public void ReverseOperatorOverloads() - { - string name = string.Format("{0}.{1}", - typeof(OperableObject).DeclaringType.Name, - typeof(OperableObject).Name); - string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; + [Test] + public void ReverseOperatorOverloads() + { + string name = string.Format("{0}.{1}", + typeof(OperableObject).DeclaringType.Name, + typeof(OperableObject).Name); + string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; - PythonEngine.Exec($@" + PythonEngine.Exec($@" from {module} import * cls = {name} a = 2 @@ -504,16 +491,16 @@ public void ReverseOperatorOverloads() assert c == (a > b.Num) "); - } - [Test] - public void ShiftOperatorOverloads() - { - string name = string.Format("{0}.{1}", - typeof(OperableObject).DeclaringType.Name, - typeof(OperableObject).Name); - string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; + } + [Test] + public void ShiftOperatorOverloads() + { + string name = string.Format("{0}.{1}", + typeof(OperableObject).DeclaringType.Name, + typeof(OperableObject).Name); + string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; - PythonEngine.Exec($@" + PythonEngine.Exec($@" from {module} import * cls = {name} a = cls(2) @@ -525,6 +512,5 @@ public void ShiftOperatorOverloads() c = a >> b.Num assert c.Num == a.Num >> b.Num "); - } } } diff --git a/src/embed_tests/TestPyBuffer.cs b/src/embed_tests/TestPyBuffer.cs index 1b4e28d12..06bd529b0 100644 --- a/src/embed_tests/TestPyBuffer.cs +++ b/src/embed_tests/TestPyBuffer.cs @@ -5,156 +5,149 @@ using Python.Runtime; using Python.Runtime.Codecs; -namespace Python.EmbeddingTest { - class TestPyBuffer - { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - TupleCodec.Register(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - - [Test] - public void TestBufferWrite() - { - string bufferTestString = "hello world! !$%&/()=?"; - string bufferTestString2 = "h llo world! !$%&/()=?"; +namespace Python.EmbeddingTest; - using var _ = Py.GIL(); +public class TestPyBuffer : BaseFixture +{ + [OneTimeSetUp] + public void SetUp() + { + TupleCodec.Register(); + } - using var pythonArray = ByteArrayFromAsciiString(bufferTestString); + [Test] + public void TestBufferWrite() + { + string bufferTestString = "hello world! !$%&/()=?"; + string bufferTestString2 = "h llo world! !$%&/()=?"; - using (PyBuffer buf = pythonArray.GetBuffer(PyBUF.WRITABLE)) - { - byte[] managedArray = { (byte)' ' }; - buf.Write(managedArray, 0, managedArray.Length, 1); - } + using var _ = Py.GIL(); - string result = pythonArray.InvokeMethod("decode", "utf-8".ToPython()).As(); - Assert.IsTrue(result == bufferTestString2); - } + using var pythonArray = ByteArrayFromAsciiString(bufferTestString); - [Test] - public void TestBufferRead() + using (PyBuffer buf = pythonArray.GetBuffer(PyBUF.WRITABLE)) { - string bufferTestString = "hello world! !$%&/()=?"; + byte[] managedArray = { (byte)' ' }; + buf.Write(managedArray, 0, managedArray.Length, 1); + } - using var _ = Py.GIL(); + string result = pythonArray.InvokeMethod("decode", "utf-8".ToPython()).As(); + Assert.IsTrue(result == bufferTestString2); + } - using var pythonArray = ByteArrayFromAsciiString(bufferTestString); - byte[] managedArray = new byte[bufferTestString.Length]; + [Test] + public void TestBufferRead() + { + string bufferTestString = "hello world! !$%&/()=?"; - using (PyBuffer buf = pythonArray.GetBuffer()) - { - managedArray[0] = (byte)' '; - buf.Read(managedArray, 1, managedArray.Length - 1, 1); - } + using var _ = Py.GIL(); - string result = new UTF8Encoding().GetString(managedArray); - Assert.IsTrue(result == " " + bufferTestString.Substring(1)); - } + using var pythonArray = ByteArrayFromAsciiString(bufferTestString); + byte[] managedArray = new byte[bufferTestString.Length]; - [Test] - public void ArrayHasBuffer() + using (PyBuffer buf = pythonArray.GetBuffer()) { - 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()); + managedArray[0] = (byte)' '; + buf.Read(managedArray, 1, managedArray.Length - 1, 1); } - [Test] - public void RefCount() - { - using var _ = Py.GIL(); - using var arr = ByteArrayFromAsciiString("hello world! !$%&/()=?"); + string result = new UTF8Encoding().GetString(managedArray); + Assert.IsTrue(result == " " + bufferTestString.Substring(1)); + } - Assert.AreEqual(1, arr.Refcount); + [Test] + 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()); + } - using (PyBuffer buf = arr.GetBuffer()) - { - Assert.AreEqual(2, arr.Refcount); - } + [Test] + public void RefCount() + { + using var _ = Py.GIL(); + using var arr = ByteArrayFromAsciiString("hello world! !$%&/()=?"); - Assert.AreEqual(1, arr.Refcount); - } + Assert.AreEqual(1, arr.Refcount); - [Test] - public void Finalization() + using (PyBuffer buf = arr.GetBuffer()) { - if (Type.GetType("Mono.Runtime") is not null) - { - Assert.Inconclusive("test unreliable in Mono"); - return; - } + Assert.AreEqual(2, arr.Refcount); + } - using var _ = Py.GIL(); - using var arr = ByteArrayFromAsciiString("hello world! !$%&/()=?"); + Assert.AreEqual(1, arr.Refcount); + } - Assert.AreEqual(1, arr.Refcount); + [Test] + public void Finalization() + { + if (Type.GetType("Mono.Runtime") is not null) + { + Assert.Inconclusive("test unreliable in Mono"); + return; + } - MakeBufAndLeak(arr); + using var _ = Py.GIL(); + using var arr = ByteArrayFromAsciiString("hello world! !$%&/()=?"); - GC.Collect(); - GC.WaitForPendingFinalizers(); - Finalizer.Instance.Collect(); + Assert.AreEqual(1, arr.Refcount); - Assert.AreEqual(1, arr.Refcount); - } + MakeBufAndLeak(arr); - [Test] - public void MultidimensionalNumPyArray() - { - var ndarray = np.arange(24).reshape(1,2,3,4).T; - PyObject ndim = ndarray.ndim; - PyObject shape = ndarray.shape; - PyObject strides = ndarray.strides; - PyObject contiguous = ndarray.flags["C_CONTIGUOUS"]; + GC.Collect(); + GC.WaitForPendingFinalizers(); + Finalizer.Instance.Collect(); - using PyBuffer buf = ndarray.GetBuffer(PyBUF.STRIDED); + Assert.AreEqual(1, arr.Refcount); + } - Assert.Multiple(() => - { - Assert.That(buf.Dimensions, Is.EqualTo(ndim.As())); - Assert.That(buf.Shape, Is.EqualTo(shape.As())); - Assert.That(buf.Strides, Is.EqualTo(strides.As())); - Assert.That(buf.IsContiguous(BufferOrderStyle.C), Is.EqualTo(contiguous.As())); - }); - } + [Test] + public void MultidimensionalNumPyArray() + { + var ndarray = np.arange(24).reshape(1,2,3,4).T; + PyObject ndim = ndarray.ndim; + PyObject shape = ndarray.shape; + PyObject strides = ndarray.strides; + PyObject contiguous = ndarray.flags["C_CONTIGUOUS"]; - [MethodImpl(MethodImplOptions.NoInlining)] - static void MakeBufAndLeak(PyObject bufProvider) - { - PyBuffer buf = bufProvider.GetBuffer(); - } + using PyBuffer buf = ndarray.GetBuffer(PyBUF.STRIDED); - static PyObject ByteArrayFromAsciiString(string str) + Assert.Multiple(() => { - using var scope = Py.CreateScope(); - return Runtime.Runtime.PyByteArray_FromStringAndSize(str).MoveToPyObject(); - } + Assert.That(buf.Dimensions, Is.EqualTo(ndim.As())); + Assert.That(buf.Shape, Is.EqualTo(shape.As())); + Assert.That(buf.Strides, Is.EqualTo(strides.As())); + Assert.That(buf.IsContiguous(BufferOrderStyle.C), Is.EqualTo(contiguous.As())); + }); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void MakeBufAndLeak(PyObject bufProvider) + { + PyBuffer buf = bufProvider.GetBuffer(); + } - dynamic np + static PyObject ByteArrayFromAsciiString(string str) + { + using var scope = Py.CreateScope(); + return Runtime.Runtime.PyByteArray_FromStringAndSize(str).MoveToPyObject(); + } + + dynamic np + { + get { - get + try + { + return Py.Import("numpy"); + } + catch (PythonException) { - try - { - return Py.Import("numpy"); - } - catch (PythonException) - { - Assert.Inconclusive("Numpy or dependency not installed"); - return null; - } + Assert.Inconclusive("Numpy or dependency not installed"); + return null; } } } diff --git a/src/embed_tests/TestPyFloat.cs b/src/embed_tests/TestPyFloat.cs index 36531cb6a..1cc971dbd 100644 --- a/src/embed_tests/TestPyFloat.cs +++ b/src/embed_tests/TestPyFloat.cs @@ -2,129 +2,116 @@ using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +/// +/// PyFloat implementation isn't complete, thus tests aren't complete. +/// +public class TestPyFloat : BaseFixture { - /// - /// PyFloat implementation isn't complete, thus tests aren't complete. - /// - public class TestPyFloat + [Test] + public void FloatCtor() + { + const float a = 4.5F; + var i = new PyFloat(a); + Assert.True(PyFloat.IsFloatType(i)); + // Assert.Assert.AreEqual(i, a.ToInt32()); + } + + [Test] + public void PyObjectCtorGood() + { + var i = new PyFloat(5); + var a = new PyFloat(i); + Assert.True(PyFloat.IsFloatType(a)); + // Assert.Assert.AreEqual(i, a.ToInt32()); + } + + [Test] + public void PyObjectCtorBad() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - - [Test] - public void FloatCtor() - { - const float a = 4.5F; - var i = new PyFloat(a); - Assert.True(PyFloat.IsFloatType(i)); - // Assert.Assert.AreEqual(i, a.ToInt32()); - } - - [Test] - public void PyObjectCtorGood() - { - var i = new PyFloat(5); - var a = new PyFloat(i); - Assert.True(PyFloat.IsFloatType(a)); - // Assert.Assert.AreEqual(i, a.ToInt32()); - } - - [Test] - public void PyObjectCtorBad() - { - var i = new PyString("Foo"); - PyFloat a = null; - - var ex = Assert.Throws(() => a = new PyFloat(i)); - - StringAssert.StartsWith("object is not a float", ex.Message); - Assert.IsNull(a); - } - - [Test] - public void DoubleCtor() - { - const double a = 4.5; - var i = new PyFloat(a); - Assert.True(PyFloat.IsFloatType(i)); - // Assert.Assert.AreEqual(i, a.ToInt32()); - } - - [Test] - public void StringIntCtor() - { - const string a = "5"; - var i = new PyFloat(a); - Assert.True(PyFloat.IsFloatType(i)); - // Assert.Assert.AreEqual(i, a.ToInt32()); - } - - [Test] - public void StringDoubleCtor() - { - const string a = "4.5"; - var i = new PyFloat(a); - Assert.True(PyFloat.IsFloatType(i)); - // Assert.Assert.AreEqual(i, a.ToInt32()); - } - - [Test] - public void StringBadCtor() - { - const string i = "Foo"; - PyFloat a = null; - - var ex = Assert.Throws(() => a = new PyFloat(i)); - - StringAssert.StartsWith("could not convert string to float", ex.Message); - Assert.IsNull(a); - } - - [Test] - public void IsFloatTrue() - { - const double a = 4.5; - var i = new PyFloat(a); - Assert.True(PyFloat.IsFloatType(i)); - } - - [Test] - public void IsFloatFalse() - { - var i = new PyString("Foo"); - Assert.False(PyFloat.IsFloatType(i)); - } - - [Test] - public void AsFloatGood() - { - const double a = 4.5; - var i = new PyFloat(a); - PyFloat s = PyFloat.AsFloat(i); - - Assert.True(PyFloat.IsFloatType(s)); - // Assert.Assert.AreEqual(i, a.ToInt32()); - } - - [Test] - public void AsFloatBad() - { - var s = new PyString("Foo"); - PyFloat a = null; - - var ex = Assert.Throws(() => a = PyFloat.AsFloat(s)); - StringAssert.StartsWith("could not convert string to float", ex.Message); - Assert.IsNull(a); - } + var i = new PyString("Foo"); + PyFloat a = null; + + var ex = Assert.Throws(() => a = new PyFloat(i)); + + StringAssert.StartsWith("object is not a float", ex.Message); + Assert.IsNull(a); + } + + [Test] + public void DoubleCtor() + { + const double a = 4.5; + var i = new PyFloat(a); + Assert.True(PyFloat.IsFloatType(i)); + // Assert.Assert.AreEqual(i, a.ToInt32()); + } + + [Test] + public void StringIntCtor() + { + const string a = "5"; + var i = new PyFloat(a); + Assert.True(PyFloat.IsFloatType(i)); + // Assert.Assert.AreEqual(i, a.ToInt32()); + } + + [Test] + public void StringDoubleCtor() + { + const string a = "4.5"; + var i = new PyFloat(a); + Assert.True(PyFloat.IsFloatType(i)); + // Assert.Assert.AreEqual(i, a.ToInt32()); + } + + [Test] + public void StringBadCtor() + { + const string i = "Foo"; + PyFloat a = null; + + var ex = Assert.Throws(() => a = new PyFloat(i)); + + StringAssert.StartsWith("could not convert string to float", ex.Message); + Assert.IsNull(a); + } + + [Test] + public void IsFloatTrue() + { + const double a = 4.5; + var i = new PyFloat(a); + Assert.True(PyFloat.IsFloatType(i)); + } + + [Test] + public void IsFloatFalse() + { + var i = new PyString("Foo"); + Assert.False(PyFloat.IsFloatType(i)); + } + + [Test] + public void AsFloatGood() + { + const double a = 4.5; + var i = new PyFloat(a); + PyFloat s = PyFloat.AsFloat(i); + + Assert.True(PyFloat.IsFloatType(s)); + // Assert.Assert.AreEqual(i, a.ToInt32()); + } + + [Test] + public void AsFloatBad() + { + var s = new PyString("Foo"); + PyFloat a = null; + + var ex = Assert.Throws(() => a = PyFloat.AsFloat(s)); + StringAssert.StartsWith("could not convert string to float", ex.Message); + Assert.IsNull(a); } } diff --git a/src/embed_tests/TestPyInt.cs b/src/embed_tests/TestPyInt.cs index c147e074b..837287b81 100644 --- a/src/embed_tests/TestPyInt.cs +++ b/src/embed_tests/TestPyInt.cs @@ -6,219 +6,206 @@ using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestPyInt : BaseFixture { - public class TestPyInt + [Test] + public void TestCtorInt() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } + const int i = 5; + var a = new PyInt(i); + Assert.AreEqual(i, a.ToInt32()); + } - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } + [Test] + public void TestCtorUInt() + { + const uint i = 5; + var a = new PyInt(i); + Assert.AreEqual(i, a.ToInt32()); + } - [Test] - public void TestCtorInt() - { - const int i = 5; - var a = new PyInt(i); - Assert.AreEqual(i, a.ToInt32()); - } + [Test] + public void TestCtorLong() + { + const long i = 5; + var a = new PyInt(i); + Assert.AreEqual(i, a.ToInt32()); + } - [Test] - public void TestCtorUInt() - { - const uint i = 5; - var a = new PyInt(i); - Assert.AreEqual(i, a.ToInt32()); - } + [Test] + public void TestCtorULong() + { + const ulong i = 5; + var a = new PyInt(i); + Assert.AreEqual(i, a.ToInt32()); + } - [Test] - public void TestCtorLong() - { - const long i = 5; - var a = new PyInt(i); - Assert.AreEqual(i, a.ToInt32()); - } + [Test] + public void TestCtorShort() + { + const short i = 5; + var a = new PyInt(i); + Assert.AreEqual(i, a.ToInt32()); + } - [Test] - public void TestCtorULong() - { - const ulong i = 5; - var a = new PyInt(i); - Assert.AreEqual(i, a.ToInt32()); - } + [Test] + public void TestCtorUShort() + { + const ushort i = 5; + var a = new PyInt(i); + Assert.AreEqual(i, a.ToInt32()); + } - [Test] - public void TestCtorShort() - { - const short i = 5; - var a = new PyInt(i); - Assert.AreEqual(i, a.ToInt32()); - } + [Test] + public void TestCtorByte() + { + const byte i = 5; + var a = new PyInt(i); + Assert.AreEqual(i, a.ToInt32()); + } - [Test] - public void TestCtorUShort() - { - const ushort i = 5; - var a = new PyInt(i); - Assert.AreEqual(i, a.ToInt32()); - } + [Test] + public void TestCtorSByte() + { + const sbyte i = 5; + var a = new PyInt(i); + Assert.AreEqual(i, a.ToInt32()); + } - [Test] - public void TestCtorByte() - { - const byte i = 5; - var a = new PyInt(i); - Assert.AreEqual(i, a.ToInt32()); - } + [Test] + public void TestCtorPyObject() + { + var i = new PyInt(5); + var a = new PyInt(i); + Assert.AreEqual(5, a.ToInt32()); + } - [Test] - public void TestCtorSByte() - { - const sbyte i = 5; - var a = new PyInt(i); - Assert.AreEqual(i, a.ToInt32()); - } + [Test] + public void TestCtorBadPyObject() + { + var i = new PyString("Foo"); + PyInt a = null; - [Test] - public void TestCtorPyObject() - { - var i = new PyInt(5); - var a = new PyInt(i); - Assert.AreEqual(5, a.ToInt32()); - } + var ex = Assert.Throws(() => a = new PyInt(i)); - [Test] - public void TestCtorBadPyObject() - { - var i = new PyString("Foo"); - PyInt a = null; + StringAssert.StartsWith("object is not an int", ex.Message); + Assert.IsNull(a); + } - var ex = Assert.Throws(() => a = new PyInt(i)); + [Test] + public void TestCtorString() + { + const string i = "5"; + var a = new PyInt(i); + Assert.AreEqual(5, a.ToInt32()); + } - StringAssert.StartsWith("object is not an int", ex.Message); - Assert.IsNull(a); - } + [Test] + public void TestCtorBadString() + { + const string i = "Foo"; + PyInt a = null; - [Test] - public void TestCtorString() - { - const string i = "5"; - var a = new PyInt(i); - Assert.AreEqual(5, a.ToInt32()); - } + var ex = Assert.Throws(() => a = new PyInt(i)); - [Test] - public void TestCtorBadString() - { - const string i = "Foo"; - PyInt a = null; + StringAssert.StartsWith("invalid literal for int", ex.Message); + Assert.IsNull(a); + } - var ex = Assert.Throws(() => a = new PyInt(i)); + [Test] + public void TestIsIntTypeTrue() + { + var i = new PyInt(5); + Assert.True(PyInt.IsIntType(i)); + } - StringAssert.StartsWith("invalid literal for int", ex.Message); - Assert.IsNull(a); - } + [Test] + public void TestIsIntTypeFalse() + { + var s = new PyString("Foo"); + Assert.False(PyInt.IsIntType(s)); + } - [Test] - public void TestIsIntTypeTrue() - { - var i = new PyInt(5); - Assert.True(PyInt.IsIntType(i)); - } + [Test] + public void TestAsIntGood() + { + var i = new PyInt(5); + var a = PyInt.AsInt(i); + Assert.AreEqual(5, a.ToInt32()); + } - [Test] - public void TestIsIntTypeFalse() - { - var s = new PyString("Foo"); - Assert.False(PyInt.IsIntType(s)); - } + [Test] + public void TestAsIntBad() + { + var s = new PyString("Foo"); + PyInt a = null; - [Test] - public void TestAsIntGood() - { - var i = new PyInt(5); - var a = PyInt.AsInt(i); - Assert.AreEqual(5, a.ToInt32()); - } + var ex = Assert.Throws(() => a = PyInt.AsInt(s)); + StringAssert.StartsWith("invalid literal for int", ex.Message); + Assert.IsNull(a); + } - [Test] - public void TestAsIntBad() - { - var s = new PyString("Foo"); - PyInt a = null; + [Test] + public void TestConvertToInt32() + { + var a = new PyInt(5); + Assert.IsInstanceOf(typeof(int), a.ToInt32()); + Assert.AreEqual(5, a.ToInt32()); + } - var ex = Assert.Throws(() => a = PyInt.AsInt(s)); - StringAssert.StartsWith("invalid literal for int", ex.Message); - Assert.IsNull(a); - } + [Test] + public void TestConvertToInt16() + { + var a = new PyInt(5); + Assert.IsInstanceOf(typeof(short), a.ToInt16()); + Assert.AreEqual(5, a.ToInt16()); + } - [Test] - public void TestConvertToInt32() - { - var a = new PyInt(5); - Assert.IsInstanceOf(typeof(int), a.ToInt32()); - Assert.AreEqual(5, a.ToInt32()); - } + [Test] + public void TestConvertToInt64() + { + long val = 5 + (long)int.MaxValue; + var a = new PyInt(val); + Assert.IsInstanceOf(typeof(long), a.ToInt64()); + Assert.AreEqual(val, a.ToInt64()); + } - [Test] - public void TestConvertToInt16() - { - var a = new PyInt(5); - Assert.IsInstanceOf(typeof(short), a.ToInt16()); - Assert.AreEqual(5, a.ToInt16()); - } + [Test] + public void ToBigInteger() + { + int[] simpleValues = + { + 0, 1, 2, + 0x10, + 0x79, + 0x80, + 0x81, + 0xFF, + 0x123, + 0x8000, + 0x1234, + 0x8001, + 0x4000, + 0xFF, + }; + simpleValues = simpleValues.Concat(simpleValues.Select(v => -v)).ToArray(); + + var expected = simpleValues.Select(v => new BigInteger(v)).ToArray(); + var actual = simpleValues.Select(v => new PyInt(v).ToBigInteger()).ToArray(); + + CollectionAssert.AreEqual(expected, actual); + } - [Test] - public void TestConvertToInt64() - { - long val = 5 + (long)int.MaxValue; - var a = new PyInt(val); - Assert.IsInstanceOf(typeof(long), a.ToInt64()); - Assert.AreEqual(val, a.ToInt64()); - } - - [Test] - public void ToBigInteger() - { - int[] simpleValues = - { - 0, 1, 2, - 0x10, - 0x79, - 0x80, - 0x81, - 0xFF, - 0x123, - 0x8000, - 0x1234, - 0x8001, - 0x4000, - 0xFF, - }; - simpleValues = simpleValues.Concat(simpleValues.Select(v => -v)).ToArray(); - - var expected = simpleValues.Select(v => new BigInteger(v)).ToArray(); - var actual = simpleValues.Select(v => new PyInt(v).ToBigInteger()).ToArray(); - - CollectionAssert.AreEqual(expected, actual); - } - - [Test] - public void ToBigIntegerLarge() - { - BigInteger val = BigInteger.Pow(2, 1024) + 3; - var pyInt = new PyInt(val); - Assert.AreEqual(val, pyInt.ToBigInteger()); - val = -val; - pyInt = new PyInt(val); - Assert.AreEqual(val, pyInt.ToBigInteger()); - } + [Test] + public void ToBigIntegerLarge() + { + BigInteger val = BigInteger.Pow(2, 1024) + 3; + var pyInt = new PyInt(val); + Assert.AreEqual(val, pyInt.ToBigInteger()); + val = -val; + pyInt = new PyInt(val); + Assert.AreEqual(val, pyInt.ToBigInteger()); } } diff --git a/src/embed_tests/TestPyIter.cs b/src/embed_tests/TestPyIter.cs index 7428da979..8805e96f4 100644 --- a/src/embed_tests/TestPyIter.cs +++ b/src/embed_tests/TestPyIter.cs @@ -1,37 +1,21 @@ using System.Linq; -using System.Text; - using NUnit.Framework; - using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestPyIter : BaseFixture { - class TestPyIter + [Test] + public void KeepOldObjects() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - - [Test] - public void KeepOldObjects() + using (Py.GIL()) + using (var testString = new PyString("hello world! !$%&/()=?")) { - using (Py.GIL()) - using (var testString = new PyString("hello world! !$%&/()=?")) - { - PyObject[] chars = testString.ToArray(); - Assert.IsTrue(chars.Length > 1); - string reconstructed = string.Concat(chars.Select(c => c.As())); - Assert.AreEqual(testString.As(), reconstructed); - } + PyObject[] chars = testString.ToArray(); + Assert.IsTrue(chars.Length > 1); + string reconstructed = string.Concat(chars.Select(c => c.As())); + Assert.AreEqual(testString.As(), reconstructed); } } } diff --git a/src/embed_tests/TestPyList.cs b/src/embed_tests/TestPyList.cs index eee129f2d..5ea00b4e0 100644 --- a/src/embed_tests/TestPyList.cs +++ b/src/embed_tests/TestPyList.cs @@ -3,170 +3,157 @@ using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestPyList : BaseFixture { - public class TestPyList + [Test] + public void TestStringIsListType() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } + var s = new PyString("foo"); + Assert.False(PyList.IsListType(s)); + } - [Test] - public void TestStringIsListType() - { - var s = new PyString("foo"); - Assert.False(PyList.IsListType(s)); - } + [Test] + public void TestListIsListType() + { + var s = new PyList(); + Assert.True(PyList.IsListType(s)); + } - [Test] - public void TestListIsListType() - { - var s = new PyList(); - Assert.True(PyList.IsListType(s)); - } + [Test] + public void TestStringAsListType() + { + var i = new PyInt(5); + PyList t = null; - [Test] - public void TestStringAsListType() - { - var i = new PyInt(5); - PyList t = null; + var ex = Assert.Throws(() => t = PyList.AsList(i)); - var ex = Assert.Throws(() => t = PyList.AsList(i)); + Assert.AreEqual("'int' object is not iterable", ex.Message); + Assert.IsNull(t); + } - Assert.AreEqual("'int' object is not iterable", ex.Message); - Assert.IsNull(t); - } + [Test] + public void TestListAsListType() + { + var l = new PyList(); + PyList t = PyList.AsList(l); - [Test] - public void TestListAsListType() - { - var l = new PyList(); - PyList t = PyList.AsList(l); + Assert.IsNotNull(t); + Assert.IsInstanceOf(typeof(PyList), t); + } - Assert.IsNotNull(t); - Assert.IsInstanceOf(typeof(PyList), t); - } + [Test] + public void TestEmptyCtor() + { + var s = new PyList(); - [Test] - public void TestEmptyCtor() - { - var s = new PyList(); + Assert.IsInstanceOf(typeof(PyList), s); + Assert.AreEqual(0, s.Length()); + } - Assert.IsInstanceOf(typeof(PyList), s); - Assert.AreEqual(0, s.Length()); - } + [Test] + public void TestPyObjectArrayCtor() + { + var ai = new PyObject[] {new PyInt(3), new PyInt(2), new PyInt(1) }; + var s = new PyList(ai); + + Assert.IsInstanceOf(typeof(PyList), s); + Assert.AreEqual(3, s.Length()); + Assert.AreEqual("3", s[0].ToString()); + Assert.AreEqual("2", s[1].ToString()); + Assert.AreEqual("1", s[2].ToString()); + } - [Test] - public void TestPyObjectArrayCtor() - { - var ai = new PyObject[] {new PyInt(3), new PyInt(2), new PyInt(1) }; - var s = new PyList(ai); - - Assert.IsInstanceOf(typeof(PyList), s); - Assert.AreEqual(3, s.Length()); - Assert.AreEqual("3", s[0].ToString()); - Assert.AreEqual("2", s[1].ToString()); - Assert.AreEqual("1", s[2].ToString()); - } + [Test] + public void TestPyObjectCtor() + { + var a = new PyList(); + var s = new PyList(a); - [Test] - public void TestPyObjectCtor() - { - var a = new PyList(); - var s = new PyList(a); + Assert.IsInstanceOf(typeof(PyList), s); + Assert.AreEqual(0, s.Length()); + } - Assert.IsInstanceOf(typeof(PyList), s); - Assert.AreEqual(0, s.Length()); - } + [Test] + public void TestBadPyObjectCtor() + { + var i = new PyInt(5); + PyList t = null; - [Test] - public void TestBadPyObjectCtor() - { - var i = new PyInt(5); - PyList t = null; + var ex = Assert.Throws(() => t = new PyList(i)); - var ex = Assert.Throws(() => t = new PyList(i)); + Assert.AreEqual("object is not a list", ex.Message); + Assert.IsNull(t); + } - Assert.AreEqual("object is not a list", ex.Message); - Assert.IsNull(t); - } + [Test] + public void TestAppend() + { + var ai = new PyObject[] { new PyInt(3), new PyInt(2), new PyInt(1) }; + var s = new PyList(ai); + s.Append(new PyInt(4)); - [Test] - public void TestAppend() - { - var ai = new PyObject[] { new PyInt(3), new PyInt(2), new PyInt(1) }; - var s = new PyList(ai); - s.Append(new PyInt(4)); + Assert.AreEqual(4, s.Length()); + Assert.AreEqual("4", s[3].ToString()); + } - Assert.AreEqual(4, s.Length()); - Assert.AreEqual("4", s[3].ToString()); - } + [Test] + public void TestInsert() + { + var ai = new PyObject[] { new PyInt(3), new PyInt(2), new PyInt(1) }; + var s = new PyList(ai); + s.Insert(0, new PyInt(4)); - [Test] - public void TestInsert() - { - var ai = new PyObject[] { new PyInt(3), new PyInt(2), new PyInt(1) }; - var s = new PyList(ai); - s.Insert(0, new PyInt(4)); + Assert.AreEqual(4, s.Length()); + Assert.AreEqual("4", s[0].ToString()); + } - Assert.AreEqual(4, s.Length()); - Assert.AreEqual("4", s[0].ToString()); - } + [Test] + public void TestReverse() + { + var ai = new PyObject[] { new PyInt(3), new PyInt(1), new PyInt(2) }; + var s = new PyList(ai); - [Test] - public void TestReverse() - { - var ai = new PyObject[] { new PyInt(3), new PyInt(1), new PyInt(2) }; - var s = new PyList(ai); + s.Reverse(); - s.Reverse(); + Assert.AreEqual(3, s.Length()); + Assert.AreEqual("2", s[0].ToString()); + Assert.AreEqual("1", s[1].ToString()); + Assert.AreEqual("3", s[2].ToString()); + } - Assert.AreEqual(3, s.Length()); - Assert.AreEqual("2", s[0].ToString()); - Assert.AreEqual("1", s[1].ToString()); - Assert.AreEqual("3", s[2].ToString()); - } + [Test] + public void TestSort() + { + var ai = new PyObject[] { new PyInt(3), new PyInt(1), new PyInt(2) }; + var s = new PyList(ai); - [Test] - public void TestSort() - { - var ai = new PyObject[] { new PyInt(3), new PyInt(1), new PyInt(2) }; - var s = new PyList(ai); + s.Sort(); - s.Sort(); + Assert.AreEqual(3, s.Length()); + Assert.AreEqual("1", s[0].ToString()); + Assert.AreEqual("2", s[1].ToString()); + Assert.AreEqual("3", s[2].ToString()); + } - Assert.AreEqual(3, s.Length()); - Assert.AreEqual("1", s[0].ToString()); - Assert.AreEqual("2", s[1].ToString()); - Assert.AreEqual("3", s[2].ToString()); - } + [Test] + public void TestOnPyList() + { + var list = new PyList(); - [Test] - public void TestOnPyList() + list.Append(new PyString("foo")); + list.Append(new PyString("bar")); + list.Append(new PyString("baz")); + var result = new List(); + foreach (PyObject item in list) { - var list = new PyList(); - - list.Append(new PyString("foo")); - list.Append(new PyString("bar")); - list.Append(new PyString("baz")); - var result = new List(); - foreach (PyObject item in list) - { - result.Add(item.ToString()); - } - - Assert.AreEqual(3, result.Count); - Assert.AreEqual("foo", result[0]); - Assert.AreEqual("bar", result[1]); - Assert.AreEqual("baz", result[2]); + result.Add(item.ToString()); } + + Assert.AreEqual(3, result.Count); + Assert.AreEqual("foo", result[0]); + Assert.AreEqual("bar", result[1]); + Assert.AreEqual("baz", result[2]); } } diff --git a/src/embed_tests/TestPyNumber.cs b/src/embed_tests/TestPyNumber.cs index 0261c15c1..5ea7543d4 100644 --- a/src/embed_tests/TestPyNumber.cs +++ b/src/embed_tests/TestPyNumber.cs @@ -2,34 +2,21 @@ using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestPyNumber : BaseFixture { - public class TestPyNumber + [Test] + public void IsNumberTypeTrue() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - - [Test] - public void IsNumberTypeTrue() - { - var i = new PyInt(1); - Assert.True(PyNumber.IsNumberType(i)); - } + var i = new PyInt(1); + Assert.True(PyNumber.IsNumberType(i)); + } - [Test] - public void IsNumberTypeFalse() - { - var s = new PyString("Foo"); - Assert.False(PyNumber.IsNumberType(s)); - } + [Test] + public void IsNumberTypeFalse() + { + var s = new PyString("Foo"); + Assert.False(PyNumber.IsNumberType(s)); } } diff --git a/src/embed_tests/TestPyObject.cs b/src/embed_tests/TestPyObject.cs index 2f27eba1b..0d25b7d0e 100644 --- a/src/embed_tests/TestPyObject.cs +++ b/src/embed_tests/TestPyObject.cs @@ -4,36 +4,24 @@ using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestPyObject : BaseFixture { - public class TestPyObject + [Test] + public void TestGetDynamicMemberNames() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - - [Test] - public void TestGetDynamicMemberNames() + List expectedMemberNames = new List { - List expectedMemberNames = new List - { - "add", - "getNumber", - "member1", - "member2" - }; + "add", + "getNumber", + "member1", + "member2" + }; - PyDict locals = new PyDict(); + PyDict locals = new PyDict(); - PythonEngine.Exec(@" + PythonEngine.Exec(@" class MemberNamesTest(object): def __init__(self): self.member1 = 123 @@ -48,64 +36,63 @@ def add(self, x, y): a = MemberNamesTest() ", null, locals); - PyObject a = locals.GetItem("a"); + PyObject a = locals.GetItem("a"); - IEnumerable memberNames = a.GetDynamicMemberNames(); + IEnumerable memberNames = a.GetDynamicMemberNames(); - foreach (string expectedName in expectedMemberNames) - { - Assert.IsTrue(memberNames.Contains(expectedName), "Could not find member '{0}'.", expectedName); - } - } - - [Test] - public void InvokeNull() + foreach (string expectedName in expectedMemberNames) { - var list = PythonEngine.Eval("list"); - Assert.Throws(() => list.Invoke(new PyObject[] {null})); + Assert.IsTrue(memberNames.Contains(expectedName), "Could not find member '{0}'.", expectedName); } + } - [Test] - public void AsManagedObjectInvalidCast() - { - var list = PythonEngine.Eval("list"); - Assert.Throws(() => list.AsManagedObject(typeof(int))); - } + [Test] + public void InvokeNull() + { + var list = PythonEngine.Eval("list"); + Assert.Throws(() => list.Invoke(new PyObject[] {null})); + } - [Test] - public void UnaryMinus_ThrowsOnBadType() - { - dynamic list = new PyList(); - var error = Assert.Throws(() => list = -list); - Assert.AreEqual("TypeError", error.Type.Name); - } + [Test] + public void AsManagedObjectInvalidCast() + { + var list = PythonEngine.Eval("list"); + Assert.Throws(() => list.AsManagedObject(typeof(int))); + } - [Test] - [Obsolete] - public void GetAttrDefault_IgnoresAttributeErrorOnly() - { - var ob = new PyObjectTestMethods().ToPython(); - using var fallback = new PyList(); - var attrErrResult = ob.GetAttr(nameof(PyObjectTestMethods.RaisesAttributeError), fallback); - Assert.IsTrue(PythonReferenceComparer.Instance.Equals(fallback, attrErrResult)); - - var typeErrResult = Assert.Throws( - () => ob.GetAttr(nameof(PyObjectTestMethods.RaisesTypeError), fallback) - ); - Assert.AreEqual(Exceptions.TypeError, typeErrResult.Type); - } + [Test] + public void UnaryMinus_ThrowsOnBadType() + { + dynamic list = new PyList(); + var error = Assert.Throws(() => list = -list); + Assert.AreEqual("TypeError", error.Type.Name); + } - // regression test from https://github.com/pythonnet/pythonnet/issues/1642 - [Test] - public void InheritedMethodsAutoacquireGIL() - { - PythonEngine.Exec("from System import String\nString.Format('{0},{1}', 1, 2)"); - } + [Test] + [Obsolete] + public void GetAttrDefault_IgnoresAttributeErrorOnly() + { + var ob = new PyObjectTestMethods().ToPython(); + using var fallback = new PyList(); + var attrErrResult = ob.GetAttr(nameof(PyObjectTestMethods.RaisesAttributeError), fallback); + Assert.IsTrue(PythonReferenceComparer.Instance.Equals(fallback, attrErrResult)); + + var typeErrResult = Assert.Throws( + () => ob.GetAttr(nameof(PyObjectTestMethods.RaisesTypeError), fallback) + ); + Assert.AreEqual(Exceptions.TypeError, typeErrResult.Type); } - public class PyObjectTestMethods + // regression test from https://github.com/pythonnet/pythonnet/issues/1642 + [Test] + public void InheritedMethodsAutoacquireGIL() { - public string RaisesAttributeError => throw new PythonException(new PyType(Exceptions.AttributeError), value: null, traceback: null); - public string RaisesTypeError => throw new PythonException(new PyType(Exceptions.TypeError), value: null, traceback: null); + PythonEngine.Exec("from System import String\nString.Format('{0},{1}', 1, 2)"); } } + +public class PyObjectTestMethods +{ + public string RaisesAttributeError => throw new PythonException(new PyType(Exceptions.AttributeError), value: null, traceback: null); + public string RaisesTypeError => throw new PythonException(new PyType(Exceptions.TypeError), value: null, traceback: null); +} diff --git a/src/embed_tests/TestPySequence.cs b/src/embed_tests/TestPySequence.cs index dc35a2633..43c5480f0 100644 --- a/src/embed_tests/TestPySequence.cs +++ b/src/embed_tests/TestPySequence.cs @@ -2,94 +2,81 @@ using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestPySequence : BaseFixture { - public class TestPySequence + [Test] + public void TestIsSequenceTrue() + { + var t = new PyString("FooBar"); + Assert.True(PySequence.IsSequenceType(t)); + } + + [Test] + public void TestIsSequenceFalse() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - - [Test] - public void TestIsSequenceTrue() - { - var t = new PyString("FooBar"); - Assert.True(PySequence.IsSequenceType(t)); - } - - [Test] - public void TestIsSequenceFalse() - { - var t = new PyInt(5); - Assert.False(PySequence.IsSequenceType(t)); - } - - [Test] - public void TestGetSlice() - { - var t = new PyString("FooBar"); - - PyObject s = t.GetSlice(0, 3); - Assert.AreEqual("Foo", s.ToString()); - - PyObject s2 = t.GetSlice(3, 6); - Assert.AreEqual("Bar", s2.ToString()); - - PyObject s3 = t.GetSlice(0, 6); - Assert.AreEqual("FooBar", s3.ToString()); - - PyObject s4 = t.GetSlice(0, 12); - Assert.AreEqual("FooBar", s4.ToString()); - } - - [Test] - public void TestConcat() - { - var t1 = new PyString("Foo"); - var t2 = new PyString("Bar"); - - PyObject actual = t1.Concat(t2); - - Assert.AreEqual("FooBar", actual.ToString()); - } - - [Test] - public void TestRepeat() - { - var t1 = new PyString("Foo"); - - PyObject actual = t1.Repeat(3); - Assert.AreEqual("FooFooFoo", actual.ToString()); - - actual = t1.Repeat(-3); - Assert.AreEqual("", actual.ToString()); - } - - [Test] - public void TestContains() - { - var t1 = new PyString("FooBar"); - - Assert.True(t1.Contains(new PyString("a"))); - Assert.False(t1.Contains(new PyString("z"))); - } - - [Test] - public void TestIndex() - { - var t1 = new PyString("FooBar"); - - Assert.AreEqual(4, t1.Index32(new PyString("a"))); - Assert.AreEqual(5L, t1.Index64(new PyString("r"))); - Assert.AreEqual(-(nint)1, t1.Index(new PyString("z"))); - } + var t = new PyInt(5); + Assert.False(PySequence.IsSequenceType(t)); + } + + [Test] + public void TestGetSlice() + { + var t = new PyString("FooBar"); + + PyObject s = t.GetSlice(0, 3); + Assert.AreEqual("Foo", s.ToString()); + + PyObject s2 = t.GetSlice(3, 6); + Assert.AreEqual("Bar", s2.ToString()); + + PyObject s3 = t.GetSlice(0, 6); + Assert.AreEqual("FooBar", s3.ToString()); + + PyObject s4 = t.GetSlice(0, 12); + Assert.AreEqual("FooBar", s4.ToString()); + } + + [Test] + public void TestConcat() + { + var t1 = new PyString("Foo"); + var t2 = new PyString("Bar"); + + PyObject actual = t1.Concat(t2); + + Assert.AreEqual("FooBar", actual.ToString()); + } + + [Test] + public void TestRepeat() + { + var t1 = new PyString("Foo"); + + PyObject actual = t1.Repeat(3); + Assert.AreEqual("FooFooFoo", actual.ToString()); + + actual = t1.Repeat(-3); + Assert.AreEqual("", actual.ToString()); + } + + [Test] + public void TestContains() + { + var t1 = new PyString("FooBar"); + + Assert.True(t1.Contains(new PyString("a"))); + Assert.False(t1.Contains(new PyString("z"))); + } + + [Test] + public void TestIndex() + { + var t1 = new PyString("FooBar"); + + Assert.AreEqual(4, t1.Index32(new PyString("a"))); + Assert.AreEqual(5L, t1.Index64(new PyString("r"))); + Assert.AreEqual(-(nint)1, t1.Index(new PyString("z"))); } } diff --git a/src/embed_tests/TestPyString.cs b/src/embed_tests/TestPyString.cs index b12e08c23..9b0116711 100644 --- a/src/embed_tests/TestPyString.cs +++ b/src/embed_tests/TestPyString.cs @@ -2,115 +2,102 @@ using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestPyString : BaseFixture { - public class TestPyString + [Test] + public void TestStringCtor() + { + const string expected = "foo"; + var actual = new PyString(expected); + Assert.AreEqual(expected, actual.ToString()); + } + + [Test] + public void TestEmptyStringCtor() + { + const string expected = ""; + var actual = new PyString(expected); + Assert.AreEqual(expected, actual.ToString()); + } + + [Test] + [Ignore("Ambiguous behavior between PY2/PY3. Needs remapping")] + public void TestPyObjectCtor() + { + const string expected = "Foo"; + + var t = new PyString(expected); + var actual = new PyString(t); + + Assert.AreEqual(expected, actual.ToString()); + } + + [Test] + public void TestBadPyObjectCtor() + { + var t = new PyInt(5); + PyString actual = null; + + var ex = Assert.Throws(() => actual = new PyString(t)); + + StringAssert.StartsWith("object is not a string", ex.Message); + Assert.IsNull(actual); + } + + [Test] + public void TestCtorBorrowed() + { + const string expected = "foo"; + + var t = new PyString(expected); + var actual = new PyString(t.Reference); + + Assert.AreEqual(expected, actual.ToString()); + } + + [Test] + [Ignore("Ambiguous behavior between PY2/PY3. Needs remapping")] + public void IsStringTrue() + { + var t = new PyString("foo"); + + Assert.True(PyString.IsStringType(t)); + } + + [Test] + public void IsStringFalse() + { + var t = new PyInt(5); + + Assert.False(PyString.IsStringType(t)); + } + + [Test] + public void TestUnicode() + { + const string expected = "foo\u00e9"; + PyObject actual = new PyString(expected); + Assert.AreEqual(expected, actual.ToString()); + } + + [Test] + public void TestUnicodeSurrogateToString() + { + var expected = "foo\ud83d\udc3c"; + var actual = PythonEngine.Eval("'foo\ud83d\udc3c'"); + Assert.AreEqual(4, actual.Length()); + Assert.AreEqual(expected, actual.ToString()); + } + + [Test] + public void TestUnicodeSurrogate() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - - [Test] - public void TestStringCtor() - { - const string expected = "foo"; - var actual = new PyString(expected); - Assert.AreEqual(expected, actual.ToString()); - } - - [Test] - public void TestEmptyStringCtor() - { - const string expected = ""; - var actual = new PyString(expected); - Assert.AreEqual(expected, actual.ToString()); - } - - [Test] - [Ignore("Ambiguous behavior between PY2/PY3. Needs remapping")] - public void TestPyObjectCtor() - { - const string expected = "Foo"; - - var t = new PyString(expected); - var actual = new PyString(t); - - Assert.AreEqual(expected, actual.ToString()); - } - - [Test] - public void TestBadPyObjectCtor() - { - var t = new PyInt(5); - PyString actual = null; - - var ex = Assert.Throws(() => actual = new PyString(t)); - - StringAssert.StartsWith("object is not a string", ex.Message); - Assert.IsNull(actual); - } - - [Test] - public void TestCtorBorrowed() - { - const string expected = "foo"; - - var t = new PyString(expected); - var actual = new PyString(t.Reference); - - Assert.AreEqual(expected, actual.ToString()); - } - - [Test] - [Ignore("Ambiguous behavior between PY2/PY3. Needs remapping")] - public void IsStringTrue() - { - var t = new PyString("foo"); - - Assert.True(PyString.IsStringType(t)); - } - - [Test] - public void IsStringFalse() - { - var t = new PyInt(5); - - Assert.False(PyString.IsStringType(t)); - } - - [Test] - public void TestUnicode() - { - const string expected = "foo\u00e9"; - PyObject actual = new PyString(expected); - Assert.AreEqual(expected, actual.ToString()); - } - - [Test] - public void TestUnicodeSurrogateToString() - { - var expected = "foo\ud83d\udc3c"; - var actual = PythonEngine.Eval("'foo\ud83d\udc3c'"); - Assert.AreEqual(4, actual.Length()); - Assert.AreEqual(expected, actual.ToString()); - } - - [Test] - public void TestUnicodeSurrogate() - { - const string expected = "foo\ud83d\udc3c"; // "foo🐼" - PyObject actual = new PyString(expected); - // python treats "foo🐼" as 4 characters, dotnet as 5 - Assert.AreEqual(4, actual.Length()); - Assert.AreEqual(expected, actual.ToString()); - } + const string expected = "foo\ud83d\udc3c"; // "foo🐼" + PyObject actual = new PyString(expected); + // python treats "foo🐼" as 4 characters, dotnet as 5 + Assert.AreEqual(4, actual.Length()); + Assert.AreEqual(expected, actual.ToString()); } } diff --git a/src/embed_tests/TestPyTuple.cs b/src/embed_tests/TestPyTuple.cs index 5d76116aa..42e9379ea 100644 --- a/src/embed_tests/TestPyTuple.cs +++ b/src/embed_tests/TestPyTuple.cs @@ -2,170 +2,157 @@ using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestPyTuple : BaseFixture { - public class TestPyTuple + /// + /// Test IsTupleType without having to Initialize a tuple. + /// PyTuple constructor use IsTupleType. This decouples the tests. + /// + [Test] + public void TestStringIsTupleType() + { + var s = new PyString("foo"); + Assert.False(PyTuple.IsTupleType(s)); + } + + /// + /// Test IsTupleType with Tuple. + /// + [Test] + public void TestPyTupleIsTupleType() + { + var t = new PyTuple(); + Assert.True(PyTuple.IsTupleType(t)); + } + + [Test] + public void TestPyTupleEmpty() { - [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. - /// - [Test] - public void TestStringIsTupleType() - { - var s = new PyString("foo"); - Assert.False(PyTuple.IsTupleType(s)); - } - - /// - /// Test IsTupleType with Tuple. - /// - [Test] - public void TestPyTupleIsTupleType() - { - var t = new PyTuple(); - Assert.True(PyTuple.IsTupleType(t)); - } - - [Test] - public void TestPyTupleEmpty() - { - var t = new PyTuple(); - Assert.AreEqual(0, t.Length()); - } - - [Test] - public void TestPyTupleBadCtor() - { - var i = new PyInt(5); - PyTuple t = null; - - var ex = Assert.Throws(() => t = new PyTuple(i)); - - Assert.AreEqual("object is not a tuple", ex.Message); - Assert.IsNull(t); - } - - [Test] - public void TestPyTupleCtorEmptyArray() - { - var a = new PyObject[] { }; - var t = new PyTuple(a); - - Assert.AreEqual(0, t.Length()); - } - - [Test] - public void TestPyTupleCtorArrayPyIntEmpty() - { - var a = new PyInt[] { }; - var t = new PyTuple(a); - - Assert.AreEqual(0, t.Length()); - } - - [Test] - public void TestPyTupleCtorArray() - { - var a = new PyObject[] { new PyInt(1), new PyString("Foo") }; - var t = new PyTuple(a); - - Assert.AreEqual(2, t.Length()); - } - - /// - /// Test PyTuple.Concat(...) doesn't let invalid appends happen - /// and throws and exception. - /// - /// - /// Test has second purpose. Currently it generated an Exception - /// that the GC failed to remove often and caused AppDomain unload - /// errors at the end of tests. See GH#397 for more info. - /// - /// Curious, on PY27 it gets a Unicode on the ex.Message. On PY3+ its string. - /// - [Test] - public void TestPyTupleInvalidAppend() - { - PyObject s = new PyString("foo"); - var t = new PyTuple(); - - var ex = Assert.Throws(() => t.Concat(s)); - - StringAssert.StartsWith("can only concatenate tuple", ex.Message); - Assert.AreEqual(0, t.Length()); - Assert.IsEmpty(t); - } - - [Test] - public void TestPyTupleValidAppend() - { - var t0 = new PyTuple(); - var t = new PyTuple(); - t.Concat(t0); - - Assert.IsNotNull(t); - Assert.IsInstanceOf(typeof(PyTuple), t); - } - - [Test] - public void TestPyTupleStringConvert() - { - PyObject s = new PyString("foo"); - PyTuple t = PyTuple.AsTuple(s); - - Assert.IsNotNull(t); - Assert.IsInstanceOf(typeof(PyTuple), t); - Assert.AreEqual("f", t[0].ToString()); - Assert.AreEqual("o", t[1].ToString()); - Assert.AreEqual("o", t[2].ToString()); - } - - [Test] - public void TestPyTupleValidConvert() - { - var l = new PyList(); - PyTuple t = PyTuple.AsTuple(l); - - Assert.IsNotNull(t); - Assert.IsInstanceOf(typeof(PyTuple), t); - } - - [Test] - public void TestNewPyTupleFromPyTuple() - { - var t0 = new PyTuple(); - var t = new PyTuple(t0); - - Assert.IsNotNull(t); - Assert.IsInstanceOf(typeof(PyTuple), t); - } - - /// - /// TODO: Should this throw ArgumentError instead? - /// - [Test] - public void TestInvalidAsTuple() - { - var i = new PyInt(5); - PyTuple t = null; - - var ex = Assert.Throws(() => t = PyTuple.AsTuple(i)); - - Assert.AreEqual("'int' object is not iterable", ex.Message); - Assert.IsNull(t); - } + var t = new PyTuple(); + Assert.AreEqual(0, t.Length()); + } + + [Test] + public void TestPyTupleBadCtor() + { + var i = new PyInt(5); + PyTuple t = null; + + var ex = Assert.Throws(() => t = new PyTuple(i)); + + Assert.AreEqual("object is not a tuple", ex.Message); + Assert.IsNull(t); + } + + [Test] + public void TestPyTupleCtorEmptyArray() + { + var a = new PyObject[] { }; + var t = new PyTuple(a); + + Assert.AreEqual(0, t.Length()); + } + + [Test] + public void TestPyTupleCtorArrayPyIntEmpty() + { + var a = new PyInt[] { }; + var t = new PyTuple(a); + + Assert.AreEqual(0, t.Length()); + } + + [Test] + public void TestPyTupleCtorArray() + { + var a = new PyObject[] { new PyInt(1), new PyString("Foo") }; + var t = new PyTuple(a); + + Assert.AreEqual(2, t.Length()); + } + + /// + /// Test PyTuple.Concat(...) doesn't let invalid appends happen + /// and throws and exception. + /// + /// + /// Test has second purpose. Currently it generated an Exception + /// that the GC failed to remove often and caused AppDomain unload + /// errors at the end of tests. See GH#397 for more info. + /// + /// Curious, on PY27 it gets a Unicode on the ex.Message. On PY3+ its string. + /// + [Test] + public void TestPyTupleInvalidAppend() + { + PyObject s = new PyString("foo"); + var t = new PyTuple(); + + var ex = Assert.Throws(() => t.Concat(s)); + + StringAssert.StartsWith("can only concatenate tuple", ex.Message); + Assert.AreEqual(0, t.Length()); + Assert.IsEmpty(t); + } + + [Test] + public void TestPyTupleValidAppend() + { + var t0 = new PyTuple(); + var t = new PyTuple(); + t.Concat(t0); + + Assert.IsNotNull(t); + Assert.IsInstanceOf(typeof(PyTuple), t); + } + + [Test] + public void TestPyTupleStringConvert() + { + PyObject s = new PyString("foo"); + PyTuple t = PyTuple.AsTuple(s); + + Assert.IsNotNull(t); + Assert.IsInstanceOf(typeof(PyTuple), t); + Assert.AreEqual("f", t[0].ToString()); + Assert.AreEqual("o", t[1].ToString()); + Assert.AreEqual("o", t[2].ToString()); + } + + [Test] + public void TestPyTupleValidConvert() + { + var l = new PyList(); + PyTuple t = PyTuple.AsTuple(l); + + Assert.IsNotNull(t); + Assert.IsInstanceOf(typeof(PyTuple), t); + } + + [Test] + public void TestNewPyTupleFromPyTuple() + { + var t0 = new PyTuple(); + var t = new PyTuple(t0); + + Assert.IsNotNull(t); + Assert.IsInstanceOf(typeof(PyTuple), t); + } + + /// + /// TODO: Should this throw ArgumentError instead? + /// + [Test] + public void TestInvalidAsTuple() + { + var i = new PyInt(5); + PyTuple t = null; + + var ex = Assert.Throws(() => t = PyTuple.AsTuple(i)); + + Assert.AreEqual("'int' object is not iterable", ex.Message); + Assert.IsNull(t); } } diff --git a/src/embed_tests/TestPyType.cs b/src/embed_tests/TestPyType.cs index 34645747d..c92462a62 100644 --- a/src/embed_tests/TestPyType.cs +++ b/src/embed_tests/TestPyType.cs @@ -1,4 +1,3 @@ -using System.Runtime.InteropServices; using System.Text; using NUnit.Framework; @@ -6,42 +5,29 @@ using Python.Runtime; using Python.Runtime.Native; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestPyType : BaseFixture { - public class TestPyType + [Test] + public void CanCreateHeapType() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - - [Test] - public void CanCreateHeapType() - { - const string name = "nÁmæ"; - const string docStr = "dÁcæ"; + const string name = "nÁmæ"; + const string docStr = "dÁcæ"; - using var doc = new StrPtr(docStr, Encoding.UTF8); - var spec = new TypeSpec( - name: name, - basicSize: Util.ReadInt32(Runtime.Runtime.PyBaseObjectType, TypeOffset.tp_basicsize), - slots: new TypeSpec.Slot[] { - new (TypeSlotID.tp_doc, doc.RawPointer), - }, - TypeFlags.Default | TypeFlags.HeapType - ); + using var doc = new StrPtr(docStr, Encoding.UTF8); + var spec = new TypeSpec( + name: name, + basicSize: Util.ReadInt32(Runtime.Runtime.PyBaseObjectType, TypeOffset.tp_basicsize), + slots: new TypeSpec.Slot[] { + new (TypeSlotID.tp_doc, doc.RawPointer), + }, + TypeFlags.Default | TypeFlags.HeapType + ); - using var type = new PyType(spec); - Assert.AreEqual(name, type.GetAttr("__name__").As()); - Assert.AreEqual(name, type.Name); - Assert.AreEqual(docStr, type.GetAttr("__doc__").As()); - } + using var type = new PyType(spec); + Assert.AreEqual(name, type.GetAttr("__name__").As()); + Assert.AreEqual(name, type.Name); + Assert.AreEqual(docStr, type.GetAttr("__doc__").As()); } } diff --git a/src/embed_tests/TestPyWith.cs b/src/embed_tests/TestPyWith.cs index d1c9aac28..d695fdb9c 100644 --- a/src/embed_tests/TestPyWith.cs +++ b/src/embed_tests/TestPyWith.cs @@ -1,32 +1,19 @@ -using System; using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestPyWith : BaseFixture { - public class TestPyWith + /// + /// Test that exception is raised in context manager that ignores it. + /// + [Test] + public void TestWithPositive() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } + var locals = new PyDict(); - /// - /// Test that exception is raised in context manager that ignores it. - /// - [Test] - public void TestWithPositive() - { - var locals = new PyDict(); - - PythonEngine.Exec(@" + PythonEngine.Exec(@" class CmTest: def __enter__(self): return self @@ -39,32 +26,32 @@ def fail(self): a = CmTest() ", null, locals); - var a = locals.GetItem("a"); + var a = locals.GetItem("a"); - try - { - Py.With(a, cmTest => - { - cmTest.fail(); - }); - } - catch (PythonException e) + try + { + Py.With(a, cmTest => { - TestContext.Out.WriteLine(e.Message); - Assert.IsTrue(e.Type.Name == "ZeroDivisionError"); - } + cmTest.fail(); + }); + } + catch (PythonException e) + { + TestContext.Out.WriteLine(e.Message); + Assert.IsTrue(e.Type.Name == "ZeroDivisionError"); } + } - /// - /// Test that exception is not raised in context manager that handles it - /// - [Test] - public void TestWithNegative() - { - var locals = new PyDict(); + /// + /// Test that exception is not raised in context manager that handles it + /// + [Test] + public void TestWithNegative() + { + var locals = new PyDict(); - PythonEngine.Exec(@" + PythonEngine.Exec(@" class CmTest: def __enter__(self): print('Enter') @@ -78,11 +65,10 @@ def fail(self): a = CmTest() ", null, locals); - var a = locals.GetItem("a"); - Py.With(a, cmTest => - { - cmTest.fail(); - }); - } + var a = locals.GetItem("a"); + Py.With(a, cmTest => + { + cmTest.fail(); + }); } } diff --git a/src/embed_tests/TestPythonEngineProperties.cs b/src/embed_tests/TestPythonEngineProperties.cs index be91d7f45..63fa09e62 100644 --- a/src/embed_tests/TestPythonEngineProperties.cs +++ b/src/embed_tests/TestPythonEngineProperties.cs @@ -2,233 +2,232 @@ using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestPythonEngineProperties : BaseFixture { - public class TestPythonEngineProperties + [Test] + public static void GetBuildinfoDoesntCrash() { - [Test] - public static void GetBuildinfoDoesntCrash() + PythonEngine.Initialize(); + using (Py.GIL()) { - PythonEngine.Initialize(); - using (Py.GIL()) - { - string s = PythonEngine.BuildInfo; - - Assert.True(s.Length > 5); - Assert.True(s.Contains(",")); - } - } + string s = PythonEngine.BuildInfo; - [Test] - public static void GetCompilerDoesntCrash() - { - PythonEngine.Initialize(); - using (Py.GIL()) - { - string s = PythonEngine.Compiler; - - Assert.True(s.Length > 0); - Assert.True(s.Contains("[")); - Assert.True(s.Contains("]")); - } + Assert.True(s.Length > 5); + Assert.True(s.Contains(",")); } + } - [Test] - public static void GetCopyrightDoesntCrash() + [Test] + public static void GetCompilerDoesntCrash() + { + PythonEngine.Initialize(); + using (Py.GIL()) { - PythonEngine.Initialize(); - using (Py.GIL()) - { - string s = PythonEngine.Copyright; - - Assert.True(s.Length > 0); - Assert.True(s.Contains("Python Software Foundation")); - } - } + string s = PythonEngine.Compiler; - [Test] - public static void GetPlatformDoesntCrash() - { - PythonEngine.Initialize(); - using (Py.GIL()) - { - string s = PythonEngine.Platform; - - Assert.True(s.Length > 0); - Assert.True(s.Contains("x") || s.Contains("win")); - } + Assert.True(s.Length > 0); + Assert.True(s.Contains("[")); + Assert.True(s.Contains("]")); } + } - [Test] - public static void GetVersionDoesntCrash() + [Test] + public static void GetCopyrightDoesntCrash() + { + PythonEngine.Initialize(); + using (Py.GIL()) { - PythonEngine.Initialize(); - using (Py.GIL()) - { - string s = PythonEngine.Version; - - Assert.True(s.Length > 0); - Assert.True(s.Contains(",")); - } + string s = PythonEngine.Copyright; + + Assert.True(s.Length > 0); + Assert.True(s.Contains("Python Software Foundation")); } + } - [Test] - public static void GetPythonPathDefault() + [Test] + public static void GetPlatformDoesntCrash() + { + PythonEngine.Initialize(); + using (Py.GIL()) { - PythonEngine.Initialize(); - string s = PythonEngine.PythonPath; + string s = PythonEngine.Platform; - StringAssert.Contains("python", s.ToLower()); - PythonEngine.Shutdown(); + Assert.True(s.Length > 0); + Assert.True(s.Contains("x") || s.Contains("win")); } + } - [Test] - public static void GetProgramNameDefault() + [Test] + public static void GetVersionDoesntCrash() + { + PythonEngine.Initialize(); + using (Py.GIL()) { - PythonEngine.Initialize(); - string s = PythonEngine.ProgramName; + string s = PythonEngine.Version; - Assert.NotNull(s); - PythonEngine.Shutdown(); + Assert.True(s.Length > 0); + Assert.True(s.Contains(",")); } + } - /// - /// Test default behavior of PYTHONHOME. If ENVVAR is set it will - /// return the same value. If not, returns EmptyString. - /// - [Test] - public static void GetPythonHomeDefault() - { - string envPythonHome = Environment.GetEnvironmentVariable("PYTHONHOME") ?? ""; + [Test] + public static void GetPythonPathDefault() + { + PythonEngine.Initialize(); + string s = PythonEngine.PythonPath; - PythonEngine.Initialize(); - string enginePythonHome = PythonEngine.PythonHome; + StringAssert.Contains("python", s.ToLower()); + PythonEngine.Shutdown(allowReload: true); + } - Assert.AreEqual(envPythonHome, enginePythonHome); - PythonEngine.Shutdown(); - } + [Test] + public static void GetProgramNameDefault() + { + PythonEngine.Initialize(); + string s = PythonEngine.ProgramName; - [Test] - public void SetPythonHome() - { - PythonEngine.Initialize(); - var pythonHomeBackup = PythonEngine.PythonHome; - PythonEngine.Shutdown(); + Assert.NotNull(s); + PythonEngine.Shutdown(allowReload: true); + } - if (pythonHomeBackup == "") - Assert.Inconclusive("Can't reset PythonHome to empty string, skipping"); + /// + /// Test default behavior of PYTHONHOME. If ENVVAR is set it will + /// return the same value. If not, returns EmptyString. + /// + [Test] + public static void GetPythonHomeDefault() + { + string envPythonHome = Environment.GetEnvironmentVariable("PYTHONHOME") ?? ""; - var pythonHome = "/dummypath/"; + PythonEngine.Initialize(); + string enginePythonHome = PythonEngine.PythonHome; - PythonEngine.PythonHome = pythonHome; - PythonEngine.Initialize(); + Assert.AreEqual(envPythonHome, enginePythonHome); + PythonEngine.Shutdown(allowReload: true); + } - Assert.AreEqual(pythonHome, PythonEngine.PythonHome); - PythonEngine.Shutdown(); + [Test] + public void SetPythonHome() + { + PythonEngine.Initialize(); + var pythonHomeBackup = PythonEngine.PythonHome; + PythonEngine.Shutdown(allowReload: true); - // Restoring valid pythonhome. - PythonEngine.PythonHome = pythonHomeBackup; - } + if (pythonHomeBackup == "") + Assert.Inconclusive("Can't reset PythonHome to empty string, skipping"); - [Test] - public void SetPythonHomeTwice() - { - PythonEngine.Initialize(); - var pythonHomeBackup = PythonEngine.PythonHome; - PythonEngine.Shutdown(); + var pythonHome = "/dummypath/"; - if (pythonHomeBackup == "") - Assert.Inconclusive("Can't reset PythonHome to empty string, skipping"); + PythonEngine.PythonHome = pythonHome; + PythonEngine.Initialize(); - var pythonHome = "/dummypath/"; + Assert.AreEqual(pythonHome, PythonEngine.PythonHome); + PythonEngine.Shutdown(allowReload: true); - PythonEngine.PythonHome = "/dummypath2/"; - PythonEngine.PythonHome = pythonHome; - PythonEngine.Initialize(); + // Restoring valid pythonhome. + PythonEngine.PythonHome = pythonHomeBackup; + } - Assert.AreEqual(pythonHome, PythonEngine.PythonHome); - PythonEngine.Shutdown(); + [Test] + public void SetPythonHomeTwice() + { + PythonEngine.Initialize(); + var pythonHomeBackup = PythonEngine.PythonHome; + PythonEngine.Shutdown(allowReload: true); - PythonEngine.PythonHome = pythonHomeBackup; - } + if (pythonHomeBackup == "") + Assert.Inconclusive("Can't reset PythonHome to empty string, skipping"); - [Test] - [Ignore("Currently buggy in Python")] - public void SetPythonHomeEmptyString() - { - PythonEngine.Initialize(); + var pythonHome = "/dummypath/"; - var backup = PythonEngine.PythonHome; - if (backup == "") - { - PythonEngine.Shutdown(); - Assert.Inconclusive("Can't reset PythonHome to empty string, skipping"); - } - PythonEngine.PythonHome = ""; + PythonEngine.PythonHome = "/dummypath2/"; + PythonEngine.PythonHome = pythonHome; + PythonEngine.Initialize(); - Assert.AreEqual("", PythonEngine.PythonHome); + Assert.AreEqual(pythonHome, PythonEngine.PythonHome); + PythonEngine.Shutdown(allowReload: true); - PythonEngine.PythonHome = backup; - PythonEngine.Shutdown(); + PythonEngine.PythonHome = pythonHomeBackup; + } + + [Test] + [Ignore("Currently buggy in Python")] + public void SetPythonHomeEmptyString() + { + PythonEngine.Initialize(); + + var backup = PythonEngine.PythonHome; + if (backup == "") + { + PythonEngine.Shutdown(allowReload: true); + Assert.Inconclusive("Can't reset PythonHome to empty string, skipping"); } + PythonEngine.PythonHome = ""; + + Assert.AreEqual("", PythonEngine.PythonHome); - [Test] - public void SetProgramName() + PythonEngine.PythonHome = backup; + PythonEngine.Shutdown(allowReload: true); + } + + [Test] + public void SetProgramName() + { + if (PythonEngine.IsInitialized) { - if (PythonEngine.IsInitialized) - { - PythonEngine.Shutdown(); - } + PythonEngine.Shutdown(allowReload: true); + } - var programNameBackup = PythonEngine.ProgramName; + var programNameBackup = PythonEngine.ProgramName; - var programName = "FooBar"; + var programName = "FooBar"; - PythonEngine.ProgramName = programName; - PythonEngine.Initialize(); + PythonEngine.ProgramName = programName; + PythonEngine.Initialize(); - Assert.AreEqual(programName, PythonEngine.ProgramName); - PythonEngine.Shutdown(); + Assert.AreEqual(programName, PythonEngine.ProgramName); + PythonEngine.Shutdown(allowReload: true); - PythonEngine.ProgramName = programNameBackup; - } + PythonEngine.ProgramName = programNameBackup; + } - [Test] - public void SetPythonPath() + [Test] + public void SetPythonPath() + { + PythonEngine.Initialize(); + + const string moduleName = "pytest"; + bool importShouldSucceed; + try + { + Py.Import(moduleName); + importShouldSucceed = true; + } + catch { - 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(); + 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(allowReload: true); + + PythonEngine.PythonPath = path; + PythonEngine.Initialize(); + + Assert.AreEqual(path, PythonEngine.PythonPath); + if (importShouldSucceed) Py.Import(moduleName); + + PythonEngine.Shutdown(allowReload: true); } } diff --git a/src/embed_tests/TestPythonException.cs b/src/embed_tests/TestPythonException.cs index a248b6a1f..7d33389e7 100644 --- a/src/embed_tests/TestPythonException.cs +++ b/src/embed_tests/TestPythonException.cs @@ -2,179 +2,166 @@ using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestPythonException : BaseFixture { - public class TestPythonException + [Test] + public void TestMessage() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } + var list = new PyList(); + PyObject foo = null; - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } + var ex = Assert.Throws(() => foo = list[0]); - [Test] - public void TestMessage() - { - var list = new PyList(); - PyObject foo = null; - - var ex = Assert.Throws(() => foo = list[0]); - - Assert.AreEqual("list index out of range", ex.Message); - Assert.IsNull(foo); - } + Assert.AreEqual("list index out of range", ex.Message); + Assert.IsNull(foo); + } - [Test] - public void TestType() - { - var list = new PyList(); - PyObject foo = null; + [Test] + public void TestType() + { + var list = new PyList(); + PyObject foo = null; - var ex = Assert.Throws(() => foo = list[0]); + var ex = Assert.Throws(() => foo = list[0]); - Assert.AreEqual("IndexError", ex.Type.Name); - Assert.IsNull(foo); - } + Assert.AreEqual("IndexError", ex.Type.Name); + Assert.IsNull(foo); + } - [Test] - public void TestNoError() - { - // There is no PyErr to fetch - Assert.Throws(() => PythonException.FetchCurrentRaw()); - var currentError = PythonException.FetchCurrentOrNullRaw(); - Assert.IsNull(currentError); - } + [Test] + public void TestNoError() + { + // There is no PyErr to fetch + Assert.Throws(() => PythonException.FetchCurrentRaw()); + var currentError = PythonException.FetchCurrentOrNullRaw(); + Assert.IsNull(currentError); + } - [Test] - public void TestNestedExceptions() + [Test] + public void TestNestedExceptions() + { + try { - try - { - PythonEngine.Exec(@" + PythonEngine.Exec(@" try: raise Exception('inner') except Exception as ex: raise Exception('outer') from ex "); - } - catch (PythonException ex) - { - Assert.That(ex.InnerException, Is.InstanceOf()); - Assert.That(ex.InnerException.Message, Is.EqualTo("inner")); - } } - - [Test] - public void InnerIsEmptyWithNoCause() + catch (PythonException ex) { - var list = new PyList(); - PyObject foo = null; + Assert.That(ex.InnerException, Is.InstanceOf()); + Assert.That(ex.InnerException.Message, Is.EqualTo("inner")); + } + } - var ex = Assert.Throws(() => foo = list[0]); + [Test] + public void InnerIsEmptyWithNoCause() + { + var list = new PyList(); + PyObject foo = null; - Assert.IsNull(ex.InnerException); - } + var ex = Assert.Throws(() => foo = list[0]); + + Assert.IsNull(ex.InnerException); + } - [Test] - public void TestPythonExceptionFormat() + [Test] + public void TestPythonExceptionFormat() + { + try { - try - { - PythonEngine.Exec("raise ValueError('Error!')"); - Assert.Fail("Exception should have been raised"); - } - catch (PythonException ex) - { - // Console.WriteLine($"Format: {ex.Format()}"); - // Console.WriteLine($"Stacktrace: {ex.StackTrace}"); - Assert.That( - ex.Format(), - Does.Contain("Traceback") - .And.Contains("(most recent call last):") - .And.Contains("ValueError: Error!") - ); - - // Check that the stacktrace is properly formatted - Assert.That( - ex.StackTrace, - Does.Not.StartWith("[") - .And.Not.Contain("\\n") - ); - } + PythonEngine.Exec("raise ValueError('Error!')"); + Assert.Fail("Exception should have been raised"); } + catch (PythonException ex) + { + // Console.WriteLine($"Format: {ex.Format()}"); + // Console.WriteLine($"Stacktrace: {ex.StackTrace}"); + Assert.That( + ex.Format(), + Does.Contain("Traceback") + .And.Contains("(most recent call last):") + .And.Contains("ValueError: Error!") + ); + + // Check that the stacktrace is properly formatted + Assert.That( + ex.StackTrace, + Does.Not.StartWith("[") + .And.Not.Contain("\\n") + ); + } + } - [Test] - public void TestPythonExceptionFormatNoTraceback() + [Test] + public void TestPythonExceptionFormatNoTraceback() + { + try + { + var module = PyModule.Import("really____unknown___module"); + Assert.Fail("Unknown module should not be loaded"); + } + catch (PythonException ex) { - try - { - var module = PyModule.Import("really____unknown___module"); - Assert.Fail("Unknown module should not be loaded"); - } - catch (PythonException ex) - { - // ImportError/ModuleNotFoundError do not have a traceback when not running in a script - Assert.AreEqual(ex.StackTrace, ex.Format()); - } + // ImportError/ModuleNotFoundError do not have a traceback when not running in a script + Assert.AreEqual(ex.StackTrace, ex.Format()); } + } - [Test] - public void TestPythonExceptionFormatNormalized() + [Test] + public void TestPythonExceptionFormatNormalized() + { + try { - try - { - PythonEngine.Exec("a=b\n"); - Assert.Fail("Exception should have been raised"); - } - catch (PythonException ex) - { - Assert.AreEqual("Traceback (most recent call last):\n File \"\", line 1, in \nNameError: name 'b' is not defined\n", ex.Format()); - } + PythonEngine.Exec("a=b\n"); + Assert.Fail("Exception should have been raised"); } + catch (PythonException ex) + { + Assert.AreEqual("Traceback (most recent call last):\n File \"\", line 1, in \nNameError: name 'b' is not defined\n", ex.Format()); + } + } - [Test] - public void TestPythonException_PyErr_NormalizeException() + [Test] + public void TestPythonException_PyErr_NormalizeException() + { + using (var scope = Py.CreateScope()) { - using (var scope = Py.CreateScope()) - { - scope.Exec(@" + scope.Exec(@" class TestException(NameError): def __init__(self, val): super().__init__(val) x = int(val)"); - Assert.IsTrue(scope.TryGet("TestException", out PyObject type)); - - PyObject str = "dummy string".ToPython(); - var typePtr = new NewReference(type.Reference); - var strPtr = new NewReference(str.Reference); - var tbPtr = new NewReference(Runtime.Runtime.None.Reference); - Runtime.Runtime.PyErr_NormalizeException(ref typePtr, ref strPtr, ref tbPtr); - - using var typeObj = typePtr.MoveToPyObject(); - using var strObj = strPtr.MoveToPyObject(); - using var tbObj = tbPtr.MoveToPyObject(); - // the type returned from PyErr_NormalizeException should not be the same type since a new - // exception was raised by initializing the exception - Assert.IsFalse(PythonReferenceComparer.Instance.Equals(type, typeObj)); - // the message should now be the string from the throw exception during normalization - Assert.AreEqual("invalid literal for int() with base 10: 'dummy string'", strObj.ToString()); - } + Assert.IsTrue(scope.TryGet("TestException", out PyObject type)); + + PyObject str = "dummy string".ToPython(); + var typePtr = new NewReference(type.Reference); + var strPtr = new NewReference(str.Reference); + var tbPtr = new NewReference(Runtime.Runtime.None.Reference); + Runtime.Runtime.PyErr_NormalizeException(ref typePtr, ref strPtr, ref tbPtr); + + using var typeObj = typePtr.MoveToPyObject(); + using var strObj = strPtr.MoveToPyObject(); + using var tbObj = tbPtr.MoveToPyObject(); + // the type returned from PyErr_NormalizeException should not be the same type since a new + // exception was raised by initializing the exception + Assert.IsFalse(PythonReferenceComparer.Instance.Equals(type, typeObj)); + // the message should now be the string from the throw exception during normalization + Assert.AreEqual("invalid literal for int() with base 10: 'dummy string'", strObj.ToString()); } + } - [Test] - public void TestPythonException_Normalize_ThrowsWhenErrorSet() - { - Exceptions.SetError(Exceptions.TypeError, "Error!"); - var pythonException = PythonException.FetchCurrentRaw(); - Exceptions.SetError(Exceptions.TypeError, "Another error"); - Assert.Throws(() => pythonException.Normalize()); - Exceptions.Clear(); - } + [Test] + public void TestPythonException_Normalize_ThrowsWhenErrorSet() + { + Exceptions.SetError(Exceptions.TypeError, "Error!"); + var pythonException = PythonException.FetchCurrentRaw(); + Exceptions.SetError(Exceptions.TypeError, "Another error"); + Assert.Throws(() => pythonException.Normalize()); + Exceptions.Clear(); } } diff --git a/src/embed_tests/TestRuntime.cs b/src/embed_tests/TestRuntime.cs index 77696fd96..62958d0de 100644 --- a/src/embed_tests/TestRuntime.cs +++ b/src/embed_tests/TestRuntime.cs @@ -1,130 +1,117 @@ -using System; -using System.Collections.Generic; using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class TestRuntime { - public class TestRuntime + [Test] + public static void Py_IsInitializedValue() { - [OneTimeSetUp] - public void SetUp() + if (Runtime.Runtime.Py_IsInitialized() == 1) { - // We needs to ensure that no any engines are running. - if (PythonEngine.IsInitialized) - { - PythonEngine.Shutdown(); - } - } - - [Test] - public static void Py_IsInitializedValue() - { - if (Runtime.Runtime.Py_IsInitialized() == 1) - { - Runtime.Runtime.PyGILState_Ensure(); - } - Runtime.Runtime.Py_Finalize(); - Assert.AreEqual(0, Runtime.Runtime.Py_IsInitialized()); - Runtime.Runtime.Py_Initialize(); - Assert.AreEqual(1, Runtime.Runtime.Py_IsInitialized()); - Runtime.Runtime.Py_Finalize(); - Assert.AreEqual(0, Runtime.Runtime.Py_IsInitialized()); + Runtime.Runtime.PyGILState_Ensure(); } + Runtime.Runtime.Py_Finalize(); + Assert.AreEqual(0, Runtime.Runtime.Py_IsInitialized()); + Runtime.Runtime.Py_Initialize(); + Assert.AreEqual(1, Runtime.Runtime.Py_IsInitialized()); + Runtime.Runtime.Py_Finalize(); + Assert.AreEqual(0, Runtime.Runtime.Py_IsInitialized()); + } - [Test] - public static void RefCountTest() - { - Runtime.Runtime.Py_Initialize(); - using var op = Runtime.Runtime.PyString_FromString("FooBar"); + [Test] + public static void RefCountTest() + { + Runtime.Runtime.Py_Initialize(); + using var op = Runtime.Runtime.PyString_FromString("FooBar"); - // New object RefCount should be one - Assert.AreEqual(1, Runtime.Runtime.Refcount32(op.BorrowOrThrow())); + // New object RefCount should be one + Assert.AreEqual(1, Runtime.Runtime.Refcount32(op.BorrowOrThrow())); - // Checking refcount didn't change refcount - Assert.AreEqual(1, Runtime.Runtime.Refcount32(op.Borrow())); + // Checking refcount didn't change refcount + Assert.AreEqual(1, Runtime.Runtime.Refcount32(op.Borrow())); - // Borrowing a reference doesn't increase refcount - BorrowedReference p = op.Borrow(); - Assert.AreEqual(1, Runtime.Runtime.Refcount32(p)); + // Borrowing a reference doesn't increase refcount + BorrowedReference p = op.Borrow(); + Assert.AreEqual(1, Runtime.Runtime.Refcount32(p)); - // Py_IncRef/Py_DecRef increase and decrease RefCount - Runtime.Runtime.Py_IncRef(op.Borrow()); - Assert.AreEqual(2, Runtime.Runtime.Refcount32(p)); - Runtime.Runtime.Py_DecRef(StolenReference.DangerousFromPointer(op.DangerousGetAddress())); - Assert.AreEqual(1, Runtime.Runtime.Refcount32(p)); + // Py_IncRef/Py_DecRef increase and decrease RefCount + Runtime.Runtime.Py_IncRef(op.Borrow()); + Assert.AreEqual(2, Runtime.Runtime.Refcount32(p)); + Runtime.Runtime.Py_DecRef(StolenReference.DangerousFromPointer(op.DangerousGetAddress())); + Assert.AreEqual(1, Runtime.Runtime.Refcount32(p)); - // XIncref/XDecref increase and decrease RefCount + // XIncref/XDecref increase and decrease RefCount #pragma warning disable CS0618 // Type or member is obsolete. We are testing corresponding members - Runtime.Runtime.XIncref(p); - Assert.AreEqual(2, Runtime.Runtime.Refcount32(p)); - Runtime.Runtime.XDecref(op.Steal()); - Assert.AreEqual(1, Runtime.Runtime.Refcount32(p)); + Runtime.Runtime.XIncref(p); + Assert.AreEqual(2, Runtime.Runtime.Refcount32(p)); + Runtime.Runtime.XDecref(op.Steal()); + Assert.AreEqual(1, Runtime.Runtime.Refcount32(p)); #pragma warning restore CS0618 // Type or member is obsolete - op.Dispose(); + op.Dispose(); - Runtime.Runtime.Py_Finalize(); - } + Runtime.Runtime.Py_Finalize(); + } + + [Test] + public static void PyCheck_Iter_PyObject_IsIterable_Test() + { + Runtime.Runtime.Py_Initialize(); + + Runtime.Native.ABI.Initialize(Runtime.Runtime.PyVersion); - [Test] - public static void PyCheck_Iter_PyObject_IsIterable_Test() + // Tests that a python list is an iterable, but not an iterator + using (var pyListNew = Runtime.Runtime.PyList_New(0)) { - Runtime.Runtime.Py_Initialize(); - - Runtime.Native.ABI.Initialize(Runtime.Runtime.PyVersion); - - // Tests that a python list is an iterable, but not an iterator - using (var pyListNew = Runtime.Runtime.PyList_New(0)) - { - BorrowedReference pyList = pyListNew.BorrowOrThrow(); - Assert.IsFalse(Runtime.Runtime.PyIter_Check(pyList)); - Assert.IsTrue(Runtime.Runtime.PyObject_IsIterable(pyList)); - - // Tests that a python list iterator is both an iterable and an iterator - using var pyListIter = Runtime.Runtime.PyObject_GetIter(pyList); - Assert.IsTrue(Runtime.Runtime.PyObject_IsIterable(pyListIter.BorrowOrThrow())); - Assert.IsTrue(Runtime.Runtime.PyIter_Check(pyListIter.Borrow())); - } - - // Tests that a python float is neither an iterable nor an iterator - using (var pyFloat = Runtime.Runtime.PyFloat_FromDouble(2.73)) - { - Assert.IsFalse(Runtime.Runtime.PyObject_IsIterable(pyFloat.BorrowOrThrow())); - Assert.IsFalse(Runtime.Runtime.PyIter_Check(pyFloat.Borrow())); - } + BorrowedReference pyList = pyListNew.BorrowOrThrow(); + Assert.IsFalse(Runtime.Runtime.PyIter_Check(pyList)); + Assert.IsTrue(Runtime.Runtime.PyObject_IsIterable(pyList)); + + // Tests that a python list iterator is both an iterable and an iterator + using var pyListIter = Runtime.Runtime.PyObject_GetIter(pyList); + Assert.IsTrue(Runtime.Runtime.PyObject_IsIterable(pyListIter.BorrowOrThrow())); + Assert.IsTrue(Runtime.Runtime.PyIter_Check(pyListIter.Borrow())); + } - Runtime.Runtime.Py_Finalize(); + // Tests that a python float is neither an iterable nor an iterator + using (var pyFloat = Runtime.Runtime.PyFloat_FromDouble(2.73)) + { + Assert.IsFalse(Runtime.Runtime.PyObject_IsIterable(pyFloat.BorrowOrThrow())); + Assert.IsFalse(Runtime.Runtime.PyIter_Check(pyFloat.Borrow())); } - [Test] - public static void PyCheck_Iter_PyObject_IsIterable_ThreadingLock_Test() + Runtime.Runtime.Py_Finalize(); + } + + [Test] + public static void PyCheck_Iter_PyObject_IsIterable_ThreadingLock_Test() + { + Runtime.Runtime.Py_Initialize(); + + Runtime.Native.ABI.Initialize(Runtime.Runtime.PyVersion); + + try { - Runtime.Runtime.Py_Initialize(); - - Runtime.Native.ABI.Initialize(Runtime.Runtime.PyVersion); - - try - { - // Create an instance of threading.Lock, which is one of the very few types that does not have the - // TypeFlags.HaveIter set in Python 2. This tests a different code path in PyObject_IsIterable and PyIter_Check. - using var threading = Runtime.Runtime.PyImport_ImportModule("threading"); - BorrowedReference threadingDict = Runtime.Runtime.PyModule_GetDict(threading.BorrowOrThrow()); - Exceptions.ErrorCheck(threadingDict); - BorrowedReference lockType = Runtime.Runtime.PyDict_GetItemString(threadingDict, "Lock"); - if (lockType.IsNull) - throw PythonException.ThrowLastAsClrException(); - - using var args = Runtime.Runtime.PyTuple_New(0); - using var lockInstance = Runtime.Runtime.PyObject_CallObject(lockType, args.Borrow()); - - Assert.IsFalse(Runtime.Runtime.PyObject_IsIterable(lockInstance.BorrowOrThrow())); - Assert.IsFalse(Runtime.Runtime.PyIter_Check(lockInstance.Borrow())); - } - finally - { - Runtime.Runtime.Py_Finalize(); - } + // Create an instance of threading.Lock, which is one of the very few types that does not have the + // TypeFlags.HaveIter set in Python 2. This tests a different code path in PyObject_IsIterable and PyIter_Check. + using var threading = Runtime.Runtime.PyImport_ImportModule("threading"); + BorrowedReference threadingDict = Runtime.Runtime.PyModule_GetDict(threading.BorrowOrThrow()); + Exceptions.ErrorCheck(threadingDict); + BorrowedReference lockType = Runtime.Runtime.PyDict_GetItemString(threadingDict, "Lock"); + if (lockType.IsNull) + throw PythonException.ThrowLastAsClrException(); + + using var args = Runtime.Runtime.PyTuple_New(0); + using var lockInstance = Runtime.Runtime.PyObject_CallObject(lockType, args.Borrow()); + + Assert.IsFalse(Runtime.Runtime.PyObject_IsIterable(lockInstance.BorrowOrThrow())); + Assert.IsFalse(Runtime.Runtime.PyIter_Check(lockInstance.Borrow())); + } + finally + { + Runtime.Runtime.Py_Finalize(); } } } diff --git a/src/embed_tests/dynamic.cs b/src/embed_tests/dynamic.cs index 6e3bfc4cb..f23a96718 100644 --- a/src/embed_tests/dynamic.cs +++ b/src/embed_tests/dynamic.cs @@ -4,136 +4,124 @@ using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class DynamicTest : BaseFixture { - public class DynamicTest + /// + /// Set the attribute of a PyObject with a .NET object. + /// + [Test] + public void AssignObject() { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } + var stream = new StringBuilder(); + dynamic sys = Py.Import("sys"); + sys.testattr = stream; + // Check whether there are the same object. + dynamic _stream = sys.testattr.AsManagedObject(typeof(StringBuilder)); + Assert.AreEqual(_stream, stream); + + PythonEngine.RunSimpleString( + "import sys\n" + + "sys.testattr.Append('Hello!')\n"); + Assert.AreEqual(stream.ToString(), "Hello!"); + } - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } + /// + /// Set the attribute of a PyObject to null. + /// + [Test] + public void AssignNone() + { + dynamic sys = Py.Import("sys"); + sys.testattr = new StringBuilder(); + Assert.IsNotNull(sys.testattr); - /// - /// Set the attribute of a PyObject with a .NET object. - /// - [Test] - public void AssignObject() - { - var stream = new StringBuilder(); - dynamic sys = Py.Import("sys"); - sys.testattr = stream; - // Check whether there are the same object. - dynamic _stream = sys.testattr.AsManagedObject(typeof(StringBuilder)); - Assert.AreEqual(_stream, stream); - - PythonEngine.RunSimpleString( - "import sys\n" + - "sys.testattr.Append('Hello!')\n"); - Assert.AreEqual(stream.ToString(), "Hello!"); - } + sys.testattr = null; + Assert.IsNull(sys.testattr); + } - /// - /// Set the attribute of a PyObject to null. - /// - [Test] - public void AssignNone() + /// + /// Check whether we can get the attr of a python object when the + /// value of attr is a PyObject. + /// + /// + /// FIXME: Issue on Travis PY27: Error : Python.EmbeddingTest.dynamicTest.AssignPyObject + /// Python.Runtime.PythonException : ImportError : /home/travis/virtualenv/python2.7.9/lib/python2.7/lib-dynload/_io.so: undefined symbol: _PyLong_AsInt + /// + [Test] + public void AssignPyObject() + { + if (Environment.GetEnvironmentVariable("TRAVIS") == "true" && + Environment.GetEnvironmentVariable("TRAVIS_PYTHON_VERSION") == "2.7") { - dynamic sys = Py.Import("sys"); - sys.testattr = new StringBuilder(); - Assert.IsNotNull(sys.testattr); - - sys.testattr = null; - Assert.IsNull(sys.testattr); + Assert.Ignore("Fails on Travis/PY27: ImportError: ... undefined symbol: _PyLong_AsInt"); } - /// - /// Check whether we can get the attr of a python object when the - /// value of attr is a PyObject. - /// - /// - /// FIXME: Issue on Travis PY27: Error : Python.EmbeddingTest.dynamicTest.AssignPyObject - /// Python.Runtime.PythonException : ImportError : /home/travis/virtualenv/python2.7.9/lib/python2.7/lib-dynload/_io.so: undefined symbol: _PyLong_AsInt - /// - [Test] - public void AssignPyObject() - { - if (Environment.GetEnvironmentVariable("TRAVIS") == "true" && - Environment.GetEnvironmentVariable("TRAVIS_PYTHON_VERSION") == "2.7") - { - Assert.Ignore("Fails on Travis/PY27: ImportError: ... undefined symbol: _PyLong_AsInt"); - } - - dynamic sys = Py.Import("sys"); - dynamic io = Py.Import("io"); - sys.testattr = io.StringIO(); - dynamic bb = sys.testattr; // Get the PyObject - bb.write("Hello!"); - Assert.AreEqual(bb.getvalue().ToString(), "Hello!"); - } + dynamic sys = Py.Import("sys"); + dynamic io = Py.Import("io"); + sys.testattr = io.StringIO(); + dynamic bb = sys.testattr; // Get the PyObject + bb.write("Hello!"); + Assert.AreEqual(bb.getvalue().ToString(), "Hello!"); + } - /// - /// Pass the .NET object in Python side. - /// - [Test] - public void PassObjectInPython() - { - var stream = new StringBuilder(); - dynamic sys = Py.Import("sys"); - sys.testattr1 = stream; - - // Pass the .NET object in Python side - PythonEngine.RunSimpleString( - "import sys\n" + - "sys.testattr2 = sys.testattr1\n" - ); - - // Compare in Python - PythonEngine.RunSimpleString( - "import sys\n" + - "sys.testattr3 = sys.testattr1 is sys.testattr2\n" - ); - Assert.AreEqual(sys.testattr3.ToString(), "True"); - - // Compare in .NET - Assert.IsTrue(sys.testattr1.Equals(sys.testattr2)); - } + /// + /// Pass the .NET object in Python side. + /// + [Test] + public void PassObjectInPython() + { + var stream = new StringBuilder(); + dynamic sys = Py.Import("sys"); + sys.testattr1 = stream; + + // Pass the .NET object in Python side + PythonEngine.RunSimpleString( + "import sys\n" + + "sys.testattr2 = sys.testattr1\n" + ); + + // Compare in Python + PythonEngine.RunSimpleString( + "import sys\n" + + "sys.testattr3 = sys.testattr1 is sys.testattr2\n" + ); + Assert.AreEqual(sys.testattr3.ToString(), "True"); + + // Compare in .NET + Assert.IsTrue(sys.testattr1.Equals(sys.testattr2)); + } - /// - /// Pass the PyObject in .NET side - /// - [Test] - public void PassPyObjectInNet() - { - var stream = new StringBuilder(); - dynamic sys = Py.Import("sys"); - sys.testattr1 = stream; - sys.testattr2 = sys.testattr1; + /// + /// Pass the PyObject in .NET side + /// + [Test] + public void PassPyObjectInNet() + { + var stream = new StringBuilder(); + dynamic sys = Py.Import("sys"); + sys.testattr1 = stream; + sys.testattr2 = sys.testattr1; - // Compare in Python - PythonEngine.RunSimpleString( - "import sys\n" + - "sys.testattr3 = sys.testattr1 is sys.testattr2\n" - ); + // Compare in Python + PythonEngine.RunSimpleString( + "import sys\n" + + "sys.testattr3 = sys.testattr1 is sys.testattr2\n" + ); - Assert.AreEqual(sys.testattr3.ToString(), "True"); + Assert.AreEqual(sys.testattr3.ToString(), "True"); - // Compare in .NET - Assert.IsTrue(sys.testattr1.Equals(sys.testattr2)); - } + // Compare in .NET + Assert.IsTrue(sys.testattr1.Equals(sys.testattr2)); + } - // regression test for https://github.com/pythonnet/pythonnet/issues/1848 - [Test] - public void EnumEquality() - { - using var scope = Py.CreateScope(); - scope.Exec(@" + // regression test for https://github.com/pythonnet/pythonnet/issues/1848 + [Test] + public void EnumEquality() + { + using var scope = Py.CreateScope(); + scope.Exec(@" import enum class MyEnum(enum.IntEnum): @@ -141,23 +129,22 @@ class MyEnum(enum.IntEnum): ERROR = 2 def get_status(): - return MyEnum.OK + return MyEnum.OK " ); - dynamic MyEnum = scope.Get("MyEnum"); - dynamic status = scope.Get("get_status").Invoke(); - Assert.IsTrue(status == MyEnum.OK); - } + dynamic MyEnum = scope.Get("MyEnum"); + dynamic status = scope.Get("get_status").Invoke(); + Assert.IsTrue(status == MyEnum.OK); + } - // regression test for https://github.com/pythonnet/pythonnet/issues/1680 - [Test] - public void ForEach() - { - dynamic pyList = PythonEngine.Eval("[1,2,3]"); - var list = new List(); - foreach (int item in pyList) - list.Add(item); - } + // regression test for https://github.com/pythonnet/pythonnet/issues/1680 + [Test] + public void ForEach() + { + dynamic pyList = PythonEngine.Eval("[1,2,3]"); + var list = new List(); + foreach (int item in pyList) + list.Add(item); } } diff --git a/src/embed_tests/pyimport.cs b/src/embed_tests/pyimport.cs index b828d5315..f8caa944c 100644 --- a/src/embed_tests/pyimport.cs +++ b/src/embed_tests/pyimport.cs @@ -5,99 +5,96 @@ using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +/// +/// Test Import unittests and regressions +/// +/// +/// The required directory structure was added to .\pythonnet\src\embed_tests\fixtures\ directory: +/// + PyImportTest/ +/// | - __init__.py +/// | + test/ +/// | | - __init__.py +/// | | - one.py +/// +public class PyImportTest : BaseFixture { - /// - /// Test Import unittests and regressions - /// - /// - /// Keeping in old-style SetUp/TearDown due to required SetUp. - /// The required directory structure was added to .\pythonnet\src\embed_tests\fixtures\ directory: - /// + PyImportTest/ - /// | - __init__.py - /// | + test/ - /// | | - __init__.py - /// | | - one.py - /// - public class PyImportTest + [OneTimeSetUp] + public void SetUp() { - [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); + /* 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); - using var str = Runtime.Runtime.PyString_FromString(testPath); - Assert.IsFalse(str.IsNull()); - BorrowedReference path = Runtime.Runtime.PySys_GetObject("path"); - Assert.IsFalse(path.IsNull); - Runtime.Runtime.PyList_Append(path, str.Borrow()); - } + using var str = Runtime.Runtime.PyString_FromString(testPath); + Assert.IsFalse(str.IsNull()); + BorrowedReference path = Runtime.Runtime.PySys_GetObject("path"); + Assert.IsFalse(path.IsNull); + Runtime.Runtime.PyList_Append(path, str.Borrow()); + } - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } + [OneTimeTearDown] + public void Dispose() + { + using var path = new PyList(Runtime.Runtime.PySys_GetObject("path")); + path.InvokeMethod("pop"); + } - /// - /// Test subdirectory import - /// - [Test] - public void TestDottedName() - { - var module = PyModule.Import("PyImportTest.test.one"); - Assert.IsNotNull(module); - } + /// + /// Test subdirectory import + /// + [Test] + public void TestDottedName() + { + var module = PyModule.Import("PyImportTest.test.one"); + Assert.IsNotNull(module); + } - /// - /// Tests that sys.args is set. If it wasn't exception would be raised. - /// - [Test] - public void TestSysArgsImportException() - { - var module = PyModule.Import("PyImportTest.sysargv"); - Assert.IsNotNull(module); - } + /// + /// Tests that sys.args is set. If it wasn't exception would be raised. + /// + [Test] + public void TestSysArgsImportException() + { + var module = PyModule.Import("PyImportTest.sysargv"); + Assert.IsNotNull(module); + } - /// - /// Test Global Variable casting. GH#420 - /// - [Test] - public void TestCastGlobalVar() - { - dynamic foo = Py.Import("PyImportTest.cast_global_var"); - Assert.AreEqual("1", foo.FOO.ToString()); - Assert.AreEqual("1", foo.test_foo().ToString()); + /// + /// Test Global Variable casting. GH#420 + /// + [Test] + public void TestCastGlobalVar() + { + dynamic foo = Py.Import("PyImportTest.cast_global_var"); + Assert.AreEqual("1", foo.FOO.ToString()); + Assert.AreEqual("1", foo.test_foo().ToString()); - foo.FOO = 2; - Assert.AreEqual("2", foo.FOO.ToString()); - Assert.AreEqual("2", foo.test_foo().ToString()); - } + foo.FOO = 2; + Assert.AreEqual("2", foo.FOO.ToString()); + Assert.AreEqual("2", foo.test_foo().ToString()); + } - [Test] - public void BadAssembly() + [Test] + public void BadAssembly() + { + string path = Runtime.Runtime.PythonDLL; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - string path = Runtime.Runtime.PythonDLL; - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - path = @"C:\Windows\System32\kernel32.dll"; - } + path = @"C:\Windows\System32\kernel32.dll"; + } - Assert.IsTrue(File.Exists(path), $"Test DLL {path} does not exist!"); + Assert.IsTrue(File.Exists(path), $"Test DLL {path} does not exist!"); - string code = $@" + string code = $@" import clr clr.AddReference('{path}') "; - Assert.Throws(() => PythonEngine.Exec(code)); - } + Assert.Throws(() => PythonEngine.Exec(code)); } } @@ -105,9 +102,9 @@ import clr // initialize fails if a class derived from IEnumerable is in global namespace public class PublicEnumerator : System.Collections.IEnumerable { - public System.Collections.IEnumerator GetEnumerator() - { - return null; - } +public System.Collections.IEnumerator GetEnumerator() +{ + return null; +} } diff --git a/src/embed_tests/pyinitialize.cs b/src/embed_tests/pyinitialize.cs index 25dafb686..e90bc1fd1 100644 --- a/src/embed_tests/pyinitialize.cs +++ b/src/embed_tests/pyinitialize.cs @@ -1,157 +1,152 @@ -using System; using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class PyInitializeTest { - public class PyInitializeTest + /// + /// Tests issue with multiple simple Initialize/Shutdowns. + /// Fixed by #343 + /// + [Test] + public static void StartAndStopTwice() { - /// - /// Tests issue with multiple simple Initialize/Shutdowns. - /// Fixed by #343 - /// - [Test] - public static void StartAndStopTwice() - { - PythonEngine.Initialize(); - PythonEngine.Shutdown(); + PythonEngine.Initialize(); + PythonEngine.Shutdown(allowReload: true); - PythonEngine.Initialize(); - PythonEngine.Shutdown(); - } + PythonEngine.Initialize(); + PythonEngine.Shutdown(allowReload: true); + } - [Test] - public static void LoadDefaultArgs() + [Test] + public static void LoadDefaultArgs() + { + PythonEngine.Initialize(); + using (var argv = new PyList(Runtime.Runtime.PySys_GetObject("argv"))) { - using (new PythonEngine()) - { - using(var argv = new PyList(Runtime.Runtime.PySys_GetObject("argv"))) - { - Assert.AreNotEqual(0, argv.Length()); - } - } + Assert.AreNotEqual(0, argv.Length()); } + PythonEngine.Shutdown(allowReload: true); + } - [Test] - public static void LoadSpecificArgs() + [Test] + public static void LoadSpecificArgs() + { + var args = new[] { "test1", "test2" }; + PythonEngine.Initialize(args); + using (var argv = new PyList(Runtime.Runtime.PySys_GetObject("argv"))) { - var args = new[] { "test1", "test2" }; - using (new PythonEngine(args)) - { - using (var argv = new PyList(Runtime.Runtime.PySys_GetObject("argv"))) - { - using var v0 = argv[0]; - using var v1 = argv[1]; - Assert.AreEqual(args[0], v0.ToString()); - Assert.AreEqual(args[1], v1.ToString()); - } - } + using var v0 = argv[0]; + using var v1 = argv[1]; + Assert.AreEqual(args[0], v0.ToString()); + Assert.AreEqual(args[1], v1.ToString()); } + PythonEngine.Shutdown(allowReload: true); + } - // regression test for https://github.com/pythonnet/pythonnet/issues/1561 - [Test] - public void ImportClassShutdownRefcount() - { - PythonEngine.Initialize(); + // regression test for https://github.com/pythonnet/pythonnet/issues/1561 + [Test] + public void ImportClassShutdownRefcount() + { + PythonEngine.Initialize(); - PyObject ns = Py.Import(typeof(ImportClassShutdownRefcountClass).Namespace); - PyObject cls = ns.GetAttr(nameof(ImportClassShutdownRefcountClass)); - BorrowedReference clsRef = cls.Reference; + PyObject ns = Py.Import(typeof(ImportClassShutdownRefcountClass).Namespace); + PyObject cls = ns.GetAttr(nameof(ImportClassShutdownRefcountClass)); + BorrowedReference clsRef = cls.Reference; #pragma warning disable CS0618 // Type or member is obsolete - cls.Leak(); + cls.Leak(); #pragma warning restore CS0618 // Type or member is obsolete - ns.Dispose(); + ns.Dispose(); - Assert.Less(Runtime.Runtime.Refcount32(clsRef), 256); + Assert.Less(Runtime.Runtime.Refcount32(clsRef), 256); - PythonEngine.Shutdown(); - Assert.Greater(Runtime.Runtime.Refcount32(clsRef), 0); - } + PythonEngine.Shutdown(allowReload: true); + Assert.Greater(Runtime.Runtime.Refcount32(clsRef), 0); + } - /// - /// Failing test demonstrating current issue with OverflowException (#376) - /// and ArgumentException issue after that one is fixed. - /// More complex version of StartAndStopTwice test - /// - [Test] - [Ignore("GH#376: System.OverflowException : Arithmetic operation resulted in an overflow")] - //[Ignore("System.ArgumentException : Cannot pass a GCHandle across AppDomains")] - public void ReInitialize() + /// + /// Failing test demonstrating current issue with OverflowException (#376) + /// and ArgumentException issue after that one is fixed. + /// More complex version of StartAndStopTwice test + /// + [Test] + //[Ignore("System.ArgumentException : Cannot pass a GCHandle across AppDomains")] + public void ReInitialize() + { + var code = "from System import Int32\n"; + PythonEngine.Initialize(); + using (Py.GIL()) { - var code = "from System import Int32\n"; - PythonEngine.Initialize(); - using (Py.GIL()) - { - // Import any class or struct from .NET - PythonEngine.RunSimpleString(code); - } - PythonEngine.Shutdown(); - - PythonEngine.Initialize(); - using (Py.GIL()) - { - // Import a class/struct from .NET - // This class/struct must be imported during the first initialization. - PythonEngine.RunSimpleString(code); - // Create an instance of the class/struct - // System.OverflowException Exception will be raised here. - // If replacing int with Int64, OverflowException will be replaced with AppDomain exception. - PythonEngine.RunSimpleString("Int32(1)"); - } - PythonEngine.Shutdown(); + // Import any class or struct from .NET + PythonEngine.RunSimpleString(code); } + PythonEngine.Shutdown(allowReload: true); - /// - /// Helper for testing the shutdown handlers. - /// - int shutdown_count = 0; - void OnShutdownIncrement() + PythonEngine.Initialize(); + using (Py.GIL()) { - shutdown_count++; - } - void OnShutdownDouble() - { - shutdown_count *= 2; + // Import a class/struct from .NET + // This class/struct must be imported during the first initialization. + PythonEngine.RunSimpleString(code); + // Create an instance of the class/struct + // System.OverflowException Exception will be raised here. + // If replacing int with Int64, OverflowException will be replaced with AppDomain exception. + PythonEngine.RunSimpleString("Int32(1)"); } + PythonEngine.Shutdown(allowReload: true); + } - /// - /// Test the shutdown handlers. - /// - [Test] - public void ShutdownHandlers() - { - // Test we can run one shutdown handler. - shutdown_count = 0; - PythonEngine.Initialize(); - PythonEngine.AddShutdownHandler(OnShutdownIncrement); - PythonEngine.Shutdown(); - Assert.That(shutdown_count, Is.EqualTo(1)); - - // Test we can run multiple shutdown handlers in the right order. - shutdown_count = 4; - PythonEngine.Initialize(); - PythonEngine.AddShutdownHandler(OnShutdownIncrement); - PythonEngine.AddShutdownHandler(OnShutdownDouble); - PythonEngine.Shutdown(); - // Correct: 4 * 2 + 1 = 9 - // Wrong: (4 + 1) * 2 = 10 - Assert.That(shutdown_count, Is.EqualTo(9)); - - // Test we can remove shutdown handlers, handling duplicates. - shutdown_count = 4; - PythonEngine.Initialize(); - PythonEngine.AddShutdownHandler(OnShutdownIncrement); - PythonEngine.AddShutdownHandler(OnShutdownIncrement); - PythonEngine.AddShutdownHandler(OnShutdownDouble); - PythonEngine.AddShutdownHandler(OnShutdownIncrement); - PythonEngine.AddShutdownHandler(OnShutdownDouble); - PythonEngine.RemoveShutdownHandler(OnShutdownDouble); - PythonEngine.Shutdown(); - // Correct: (4 + 1) * 2 + 1 + 1 = 12 - // Wrong: (4 * 2) + 1 + 1 + 1 = 11 - Assert.That(shutdown_count, Is.EqualTo(12)); - } + /// + /// Helper for testing the shutdown handlers. + /// + int shutdown_count = 0; + void OnShutdownIncrement() + { + shutdown_count++; } + void OnShutdownDouble() + { + shutdown_count *= 2; + } + + /// + /// Test the shutdown handlers. + /// + [Test] + public void ShutdownHandlers() + { + // Test we can run one shutdown handler. + shutdown_count = 0; + PythonEngine.Initialize(); + PythonEngine.AddShutdownHandler(OnShutdownIncrement); + PythonEngine.Shutdown(allowReload: true); + Assert.That(shutdown_count, Is.EqualTo(1)); - public class ImportClassShutdownRefcountClass { } + // Test we can run multiple shutdown handlers in the right order. + shutdown_count = 4; + PythonEngine.Initialize(); + PythonEngine.AddShutdownHandler(OnShutdownIncrement); + PythonEngine.AddShutdownHandler(OnShutdownDouble); + PythonEngine.Shutdown(allowReload: true); + // Correct: 4 * 2 + 1 = 9 + // Wrong: (4 + 1) * 2 = 10 + Assert.That(shutdown_count, Is.EqualTo(9)); + + // Test we can remove shutdown handlers, handling duplicates. + shutdown_count = 4; + PythonEngine.Initialize(); + PythonEngine.AddShutdownHandler(OnShutdownIncrement); + PythonEngine.AddShutdownHandler(OnShutdownIncrement); + PythonEngine.AddShutdownHandler(OnShutdownDouble); + PythonEngine.AddShutdownHandler(OnShutdownIncrement); + PythonEngine.AddShutdownHandler(OnShutdownDouble); + PythonEngine.RemoveShutdownHandler(OnShutdownDouble); + PythonEngine.Shutdown(allowReload: true); + // Correct: (4 + 1) * 2 + 1 + 1 = 12 + // Wrong: (4 * 2) + 1 + 1 + 1 = 11 + Assert.That(shutdown_count, Is.EqualTo(12)); + } } + +public class ImportClassShutdownRefcountClass { } diff --git a/src/embed_tests/pyrunstring.cs b/src/embed_tests/pyrunstring.cs index 57c133c00..ab3cf0907 100644 --- a/src/embed_tests/pyrunstring.cs +++ b/src/embed_tests/pyrunstring.cs @@ -1,65 +1,52 @@ -using System; using NUnit.Framework; using Python.Runtime; -namespace Python.EmbeddingTest +namespace Python.EmbeddingTest; + +public class RunStringTest : BaseFixture { - public class RunStringTest + [Test] + public void TestRunSimpleString() { - [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); + int aa = PythonEngine.RunSimpleString("import sys"); + Assert.AreEqual(0, aa); - int bb = PythonEngine.RunSimpleString("import 1234"); - Assert.AreEqual(-1, bb); - } - - [Test] - public void TestEval() - { - dynamic sys = Py.Import("sys"); - sys.attr1 = 100; - var locals = new PyDict(); - locals.SetItem("sys", sys); - locals.SetItem("a", new PyInt(10)); - - object b = PythonEngine.Eval("sys.attr1 + a + 1", null, locals) - .AsManagedObject(typeof(int)); - Assert.AreEqual(111, b); - } + int bb = PythonEngine.RunSimpleString("import 1234"); + Assert.AreEqual(-1, bb); + } - [Test] - public void TestExec() - { - dynamic sys = Py.Import("sys"); - sys.attr1 = 100; - var locals = new PyDict(); - locals.SetItem("sys", sys); - locals.SetItem("a", new PyInt(10)); + [Test] + public void TestEval() + { + dynamic sys = Py.Import("sys"); + sys.attr1 = 100; + var locals = new PyDict(); + locals.SetItem("sys", sys); + locals.SetItem("a", new PyInt(10)); + + object b = PythonEngine.Eval("sys.attr1 + a + 1", null, locals) + .AsManagedObject(typeof(int)); + Assert.AreEqual(111, b); + } - PythonEngine.Exec("c = sys.attr1 + a + 1", null, locals); - object c = locals.GetItem("c").AsManagedObject(typeof(int)); - Assert.AreEqual(111, c); - } + [Test] + public void TestExec() + { + dynamic sys = Py.Import("sys"); + sys.attr1 = 100; + var locals = new PyDict(); + locals.SetItem("sys", sys); + locals.SetItem("a", new PyInt(10)); + + PythonEngine.Exec("c = sys.attr1 + a + 1", null, locals); + object c = locals.GetItem("c").AsManagedObject(typeof(int)); + Assert.AreEqual(111, c); + } - [Test] - public void TestExec2() - { - string code = @" + [Test] + public void TestExec2() + { + string code = @" class Test1(): pass @@ -68,7 +55,6 @@ def __init__(self): Test1() Test2()"; - PythonEngine.Exec(code); - } + PythonEngine.Exec(code); } } diff --git a/src/runtime/PythonEngine.cs b/src/runtime/PythonEngine.cs index 4ed45b9e9..af5c2fb4c 100644 --- a/src/runtime/PythonEngine.cs +++ b/src/runtime/PythonEngine.cs @@ -309,13 +309,13 @@ static void LoadMixins(BorrowedReference targetModuleDict) static void OnDomainUnload(object _, EventArgs __) { - Shutdown(); + Shutdown(allowReload: true); } static void OnProcessExit(object _, EventArgs __) { Runtime.ProcessIsTerminating = true; - Shutdown(); + Shutdown(allowReload: false); } /// @@ -366,6 +366,11 @@ private static void AllowLeaksDuringShutdown(object sender, Finalizer.ErrorArgs /// after calling the Shutdown method. /// public static void Shutdown() + { + Shutdown(allowReload: false); + } + + public static void Shutdown(bool allowReload) { if (!initialized) { @@ -389,7 +394,7 @@ public static void Shutdown() ExecuteShutdownHandlers(); // Remember to shut down the runtime. - Runtime.Shutdown(); + Runtime.Shutdown(allowReload); initialized = false; diff --git a/src/runtime/Runtime.cs b/src/runtime/Runtime.cs index beb577e45..6c1a480f5 100644 --- a/src/runtime/Runtime.cs +++ b/src/runtime/Runtime.cs @@ -66,6 +66,7 @@ private static string GetDefaultDllName(Version version) // .NET core: System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows) internal static bool IsWindows = Environment.OSVersion.Platform == PlatformID.Win32NT; + internal static bool IsNetCore = RuntimeInformation.FrameworkDescription == ".NET" || RuntimeInformation.FrameworkDescription == ".NET Core"; internal static Version InteropVersion { get; } = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; @@ -96,8 +97,9 @@ internal static int GetRun() return runNumber; } - internal static bool HostedInPython; - internal static bool ProcessIsTerminating; + internal static bool HostedInPython = false; + internal static bool ProcessIsTerminating = false; + internal static bool ShutdownWithoutReload = false; /// /// Initialize the runtime... @@ -144,6 +146,18 @@ internal static void Initialize(bool initSigs = false) NewRun(); } } + + if (ShutdownWithoutReload && RuntimeData.StashedDataFromDifferentDomain()) + { + if (!HostedInPython) + Py_Finalize(); + + // Shutdown again + throw new Exception( + "Runtime was shut down with allowReload: false, can not be reloaded in different domain" + ); + } + MainManagedThreadId = Thread.CurrentThread.ManagedThreadId; Finalizer.Initialize(); @@ -163,6 +177,7 @@ internal static void Initialize(bool initSigs = false) // Initialize modules that depend on the runtime class. AssemblyManager.Initialize(); OperatorMethod.Initialize(); + if (RuntimeData.HasStashData()) { RuntimeData.RestoreRuntimeData(); @@ -253,17 +268,18 @@ private static void InitPyMembers() return Util.ReadPtr(pyType.Borrow(), TypeOffset.tp_iternext); } - internal static void Shutdown() + internal static void Shutdown(bool allowReload) { if (Py_IsInitialized() == 0 || !_isInitialized) { return; } _isInitialized = false; + ShutdownWithoutReload = !allowReload; var state = PyGILState_Ensure(); - if (!HostedInPython && !ProcessIsTerminating) + if (!HostedInPython && !ProcessIsTerminating && !IsNetCore && allowReload) { // avoid saving dead objects TryCollectingGarbage(runs: 3); @@ -294,20 +310,26 @@ internal static void Shutdown() DisposeLazyObject(hexCallable); PyObjectConversions.Reset(); - PyGC_Collect(); - bool everythingSeemsCollected = TryCollectingGarbage(MaxCollectRetriesOnShutdown, - forceBreakLoops: true); - Debug.Assert(everythingSeemsCollected); + if (!ProcessIsTerminating) + { + PyGC_Collect(); + bool everythingSeemsCollected = TryCollectingGarbage(MaxCollectRetriesOnShutdown, + forceBreakLoops: true); + Debug.Assert(everythingSeemsCollected); + } Finalizer.Shutdown(); InternString.Shutdown(); - ResetPyMembers(); if (!HostedInPython) { - GC.Collect(); - GC.WaitForPendingFinalizers(); + if (!ProcessIsTerminating) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + PyGILState_Release(state); // Then release the GIL for good, if there is somehting to release // Use the unchecked version as the checked version calls `abort()` diff --git a/src/runtime/StateSerialization/PythonNetState.cs b/src/runtime/StateSerialization/PythonNetState.cs index 771f92ecc..cb34ea3db 100644 --- a/src/runtime/StateSerialization/PythonNetState.cs +++ b/src/runtime/StateSerialization/PythonNetState.cs @@ -9,6 +9,7 @@ namespace Python.Runtime.StateSerialization; [Serializable] internal class PythonNetState { + public int DomainId { get; init; } public MetatypeState Metatype { get; init; } public SharedObjectsState SharedObjects { get; init; } public TypeManagerState Types { get; init; } diff --git a/src/runtime/StateSerialization/RuntimeData.cs b/src/runtime/StateSerialization/RuntimeData.cs index 204e15b5b..ccae6cdef 100644 --- a/src/runtime/StateSerialization/RuntimeData.cs +++ b/src/runtime/StateSerialization/RuntimeData.cs @@ -17,6 +17,10 @@ namespace Python.Runtime { public static class RuntimeData { + static readonly string DataName = "_pythonnet_data"; + static readonly string DomainIdName = "_pythonnet_domain_id"; + + private static Type? _formatterType; public static Type? FormatterType { @@ -34,11 +38,11 @@ public static Type? FormatterType public static ICLRObjectStorer? WrappersStorer { get; set; } /// - /// Clears the old "clr_data" entry if a previous one is present. + /// Clears the old DataName entry if a previous one is present. /// static void ClearCLRData () { - BorrowedReference capsule = PySys_GetObject("clr_data"); + BorrowedReference capsule = PySys_GetObject(DataName); if (!capsule.IsNull) { IntPtr oldData = PyCapsule_GetPointer(capsule, IntPtr.Zero); @@ -51,6 +55,7 @@ internal static void Stash() { var runtimeStorage = new PythonNetState { + DomainId = AppDomain.CurrentDomain.Id, Metatype = MetaType.SaveRuntimeData(), ImportHookState = ImportHook.SaveRuntimeData(), Types = TypeManager.SaveRuntimeData(), @@ -72,7 +77,10 @@ internal static void Stash() ClearCLRData(); using NewReference capsule = PyCapsule_New(mem, IntPtr.Zero, IntPtr.Zero); - int res = PySys_SetObject("clr_data", capsule.BorrowOrThrow()); + int res = PySys_SetObject(DataName, capsule.BorrowOrThrow()); + PythonException.ThrowIfIsNotZero(res); + using NewReference id = PyInt_FromInt32(AppDomain.CurrentDomain.Id); + res = PySys_SetObject(DomainIdName, id.BorrowOrThrow()); PythonException.ThrowIfIsNotZero(res); } @@ -90,7 +98,7 @@ internal static void RestoreRuntimeData() private static void RestoreRuntimeDataImpl() { - BorrowedReference capsule = PySys_GetObject("clr_data"); + BorrowedReference capsule = PySys_GetObject(DataName); if (capsule.IsNull) { return; @@ -115,12 +123,24 @@ private static void RestoreRuntimeDataImpl() public static bool HasStashData() { - return !PySys_GetObject("clr_data").IsNull; + return !PySys_GetObject(DataName).IsNull; + } + + public static bool StashedDataFromDifferentDomain() + { + var val = PySys_GetObject(DomainIdName); + if (!val.IsNull) + { + var n = PyLong_AsLongLong(val); + if (n != null) + return (int)n != AppDomain.CurrentDomain.Id; + } + return false; } public static void ClearStash() { - PySys_SetObject("clr_data", default); + PySys_SetObject(DataName, default); } static bool CheckSerializable (object o) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy