From 1ef1e7ba5be974c4cc500c5d134641ceb96f6b9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Neves?= Date: Wed, 17 Jul 2024 19:06:31 +0100 Subject: [PATCH 01/12] Encoders no support specifying the target encoding type --- src/runtime/Codecs/EncoderGroup.cs | 4 ++-- src/runtime/Codecs/EnumPyIntCodec.cs | 7 +++---- src/runtime/Codecs/IPyObjectEncoder.cs | 3 ++- src/runtime/Codecs/PyObjectConversions.cs | 2 +- src/runtime/Codecs/RawProxyEncoder.cs | 2 +- src/runtime/Codecs/TupleCodecs.cs | 13 ++++++------- src/runtime/Converter.cs | 6 ++++++ 7 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/runtime/Codecs/EncoderGroup.cs b/src/runtime/Codecs/EncoderGroup.cs index 63abf35a3..97f14a512 100644 --- a/src/runtime/Codecs/EncoderGroup.cs +++ b/src/runtime/Codecs/EncoderGroup.cs @@ -28,13 +28,13 @@ public void Add(IPyObjectEncoder item) /// public bool CanEncode(Type type) => this.encoders.Any(encoder => encoder.CanEncode(type)); /// - public PyObject? TryEncode(object value) + public PyObject? TryEncode(object value, Type type) { if (value is null) throw new ArgumentNullException(nameof(value)); foreach (var encoder in this.GetEncoders(value.GetType())) { - var result = encoder.TryEncode(value); + var result = encoder.TryEncode(value, type); if (result != null) { return result; diff --git a/src/runtime/Codecs/EnumPyIntCodec.cs b/src/runtime/Codecs/EnumPyIntCodec.cs index 42f5eb1b2..5023d4395 100644 --- a/src/runtime/Codecs/EnumPyIntCodec.cs +++ b/src/runtime/Codecs/EnumPyIntCodec.cs @@ -46,12 +46,11 @@ public bool TryDecode(PyObject pyObj, out T? value) return false; } - public PyObject? TryEncode(object value) + public PyObject? TryEncode(object value, Type type) { if (value is null) return null; - - var enumType = value.GetType(); - if (!enumType.IsEnum) return null; + + if (!type.IsEnum) return null; try { diff --git a/src/runtime/Codecs/IPyObjectEncoder.cs b/src/runtime/Codecs/IPyObjectEncoder.cs index 94d19da90..1eab1f48b 100644 --- a/src/runtime/Codecs/IPyObjectEncoder.cs +++ b/src/runtime/Codecs/IPyObjectEncoder.cs @@ -13,6 +13,7 @@ public interface IPyObjectEncoder bool CanEncode(Type type); /// /// Attempts to encode CLR object into Python object + /// using the specified /// - PyObject? TryEncode(object value); + PyObject? TryEncode(object value, Type type); } diff --git a/src/runtime/Codecs/PyObjectConversions.cs b/src/runtime/Codecs/PyObjectConversions.cs index cde97c8c9..705253c20 100644 --- a/src/runtime/Codecs/PyObjectConversions.cs +++ b/src/runtime/Codecs/PyObjectConversions.cs @@ -54,7 +54,7 @@ public static void RegisterDecoder(IPyObjectDecoder decoder) foreach (var encoder in clrToPython.GetOrAdd(type, GetEncoders)) { - var result = encoder.TryEncode(obj); + var result = encoder.TryEncode(obj, type); if (result != null) return result; } diff --git a/src/runtime/Codecs/RawProxyEncoder.cs b/src/runtime/Codecs/RawProxyEncoder.cs index 37ad0487b..c02cb0a36 100644 --- a/src/runtime/Codecs/RawProxyEncoder.cs +++ b/src/runtime/Codecs/RawProxyEncoder.cs @@ -8,7 +8,7 @@ namespace Python.Runtime.Codecs /// public class RawProxyEncoder: IPyObjectEncoder { - public PyObject TryEncode(object value) + public PyObject TryEncode(object value, Type type) { if (value is null) throw new ArgumentNullException(nameof(value)); diff --git a/src/runtime/Codecs/TupleCodecs.cs b/src/runtime/Codecs/TupleCodecs.cs index ec8975e3a..631f2c49e 100644 --- a/src/runtime/Codecs/TupleCodecs.cs +++ b/src/runtime/Codecs/TupleCodecs.cs @@ -18,20 +18,19 @@ public bool CanEncode(Type type) && type.Name.StartsWith(typeof(TTuple).Name + '`'); } - public PyObject? TryEncode(object value) + public PyObject? TryEncode(object value, Type type) { if (value == null) return null; - var tupleType = value.GetType(); - if (tupleType == typeof(object)) return null; - if (!this.CanEncode(tupleType)) return null; - if (tupleType == typeof(TTuple)) return new PyTuple(); + if (type == typeof(object)) return null; + if (!this.CanEncode(type)) return null; + if (type == typeof(TTuple)) return new PyTuple(); - nint fieldCount = tupleType.GetGenericArguments().Length; + nint fieldCount = type.GetGenericArguments().Length; using var tuple = Runtime.PyTuple_New(fieldCount); PythonException.ThrowIfIsNull(tuple); int fieldIndex = 0; - foreach (FieldInfo field in tupleType.GetFields()) + foreach (FieldInfo field in type.GetFields()) { var item = field.GetValue(value); using var pyItem = Converter.ToPython(item, field.FieldType); diff --git a/src/runtime/Converter.cs b/src/runtime/Converter.cs index 50b33e60e..a6d12b069 100644 --- a/src/runtime/Converter.cs +++ b/src/runtime/Converter.cs @@ -987,5 +987,11 @@ public static PyObject ToPythonAs(this T? o) if (o is null) return Runtime.None; return Converter.ToPython(o, typeof(T)).MoveToPyObject(); } + + public static PyObject ToPythonAs(this object o, Type type) + { + if (o is null) return Runtime.None; + return Converter.ToPython(o, type).MoveToPyObject(); + } } } From 255201afbc81f382b74ebcb74ec9506ee67a83ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Neves?= Date: Thu, 18 Jul 2024 10:03:42 +0100 Subject: [PATCH 02/12] Fix build --- src/embed_tests/CodecGroups.cs | 4 ++-- src/embed_tests/Codecs.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/embed_tests/CodecGroups.cs b/src/embed_tests/CodecGroups.cs index 689e5b24c..f834c1ee2 100644 --- a/src/embed_tests/CodecGroups.cs +++ b/src/embed_tests/CodecGroups.cs @@ -48,12 +48,12 @@ public void Encodes() encoder2, }; - var uri = group.TryEncode(new Uri("data:")); + var uri = group.TryEncode(new Uri("data:"), typeof(Uri)); var clrObject = (CLRObject)ManagedType.GetManagedObject(uri); Assert.AreSame(encoder1, clrObject.inst); Assert.AreNotSame(encoder2, clrObject.inst); - var tuple = group.TryEncode(Tuple.Create(1)); + var tuple = group.TryEncode(Tuple.Create(1), typeof(Tuple)); clrObject = (CLRObject)ManagedType.GetManagedObject(tuple); Assert.AreSame(encoder0, clrObject.inst); } diff --git a/src/embed_tests/Codecs.cs b/src/embed_tests/Codecs.cs index c8b8ecb6e..c7fdf8283 100644 --- a/src/embed_tests/Codecs.cs +++ b/src/embed_tests/Codecs.cs @@ -71,7 +71,7 @@ public void TupleRoundtripObject() static void TupleRoundtripObject() { var tuple = Activator.CreateInstance(typeof(T), 42.0, "42", new object()); - using var pyTuple = TupleCodec.Instance.TryEncode(tuple); + using var pyTuple = TupleCodec.Instance.TryEncode(tuple, typeof(T)); Assert.IsTrue(TupleCodec.Instance.TryDecode(pyTuple, out object restored)); Assert.AreEqual(expected: tuple, actual: restored); } @@ -85,7 +85,7 @@ public void TupleRoundtripGeneric() static void TupleRoundtripGeneric() { var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object()); - using var pyTuple = TupleCodec.Instance.TryEncode(tuple); + using var pyTuple = TupleCodec.Instance.TryEncode(tuple, typeof(T)); Assert.IsTrue(TupleCodec.Instance.TryDecode(pyTuple, out T restored)); Assert.AreEqual(expected: tuple, actual: restored); } @@ -438,7 +438,7 @@ public bool TryDecode(PyObject pyObj, out T value) return true; } - public PyObject TryEncode(object value) + public PyObject TryEncode(object value, Type type) { var error = (ValueErrorWrapper)value; return PythonEngine.Eval("ValueError").Invoke(error.Message.ToPython()); @@ -478,7 +478,7 @@ public bool TryDecode(PyObject pyObj, out T value) class ObjectToEncoderInstanceEncoder : IPyObjectEncoder { public bool CanEncode(Type type) => type == typeof(T); - public PyObject TryEncode(object value) => PyObject.FromManagedObject(this); + public PyObject TryEncode(object value, Type type) => PyObject.FromManagedObject(this); } /// From 37c4d049a3cf81cfa5c14a0bcf19b363a14e3519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Neves?= Date: Thu, 18 Jul 2024 17:47:20 +0100 Subject: [PATCH 03/12] Add unit test --- src/embed_tests/CodecGroups.cs | 4 +-- src/embed_tests/Codecs.cs | 40 ++++++++++++++++++--- src/embed_tests/Python.EmbeddingTest.csproj | 1 + src/runtime/Codecs/EncoderGroup.cs | 4 +-- src/runtime/Codecs/EnumPyIntCodec.cs | 6 ++-- src/runtime/Codecs/IPyObjectEncoder.cs | 3 +- src/runtime/Codecs/PyObjectConversions.cs | 2 +- src/runtime/Codecs/RawProxyEncoder.cs | 4 +-- src/runtime/Codecs/TupleCodecs.cs | 3 +- src/runtime/PythonTypes/PyObject.cs | 8 ++--- 10 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/embed_tests/CodecGroups.cs b/src/embed_tests/CodecGroups.cs index f834c1ee2..689e5b24c 100644 --- a/src/embed_tests/CodecGroups.cs +++ b/src/embed_tests/CodecGroups.cs @@ -48,12 +48,12 @@ public void Encodes() encoder2, }; - var uri = group.TryEncode(new Uri("data:"), typeof(Uri)); + var uri = group.TryEncode(new Uri("data:")); var clrObject = (CLRObject)ManagedType.GetManagedObject(uri); Assert.AreSame(encoder1, clrObject.inst); Assert.AreNotSame(encoder2, clrObject.inst); - var tuple = group.TryEncode(Tuple.Create(1), typeof(Tuple)); + var tuple = group.TryEncode(Tuple.Create(1)); clrObject = (CLRObject)ManagedType.GetManagedObject(tuple); Assert.AreSame(encoder0, clrObject.inst); } diff --git a/src/embed_tests/Codecs.cs b/src/embed_tests/Codecs.cs index c7fdf8283..1a815e2cd 100644 --- a/src/embed_tests/Codecs.cs +++ b/src/embed_tests/Codecs.cs @@ -71,7 +71,7 @@ public void TupleRoundtripObject() static void TupleRoundtripObject() { var tuple = Activator.CreateInstance(typeof(T), 42.0, "42", new object()); - using var pyTuple = TupleCodec.Instance.TryEncode(tuple, typeof(T)); + using var pyTuple = TupleCodec.Instance.TryEncode(tuple); Assert.IsTrue(TupleCodec.Instance.TryDecode(pyTuple, out object restored)); Assert.AreEqual(expected: tuple, actual: restored); } @@ -85,7 +85,7 @@ public void TupleRoundtripGeneric() static void TupleRoundtripGeneric() { var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object()); - using var pyTuple = TupleCodec.Instance.TryEncode(tuple, typeof(T)); + using var pyTuple = TupleCodec.Instance.TryEncode(tuple); Assert.IsTrue(TupleCodec.Instance.TryDecode(pyTuple, out T restored)); Assert.AreEqual(expected: tuple, actual: restored); } @@ -339,6 +339,26 @@ public void ExceptionDecoded() Assert.AreEqual(TestExceptionMessage, error.Message); } + [Test] + public void ExplicitObjectInterfaceEncoded() + { + var obj = new ExplicitInterfaceObject(); + + // explicitly pass an interface (but not a generic type argument) to simulate a scenario where the type is not know at build time + // var encoder = new InterfaceEncoder(typeof(IObjectInterface)); + // PyObjectConversions.RegisterEncoder(encoder); + + using var scope = Py.CreateScope(); + scope.Exec(@" +def call(obj): + return dir(obj) +"); + var callFunc = scope.Get("call"); + var members = callFunc.Invoke(obj.ToPythonAs(typeof(IObjectInterface))).As(); + CollectionAssert.Contains(members, nameof(IObjectInterface.MemberFromInterface)); + CollectionAssert.DoesNotContain(members, nameof(ExplicitInterfaceObject.MemberFromObject)); + } + [Test] public void DateTimeDecoded() { @@ -438,7 +458,7 @@ public bool TryDecode(PyObject pyObj, out T value) return true; } - public PyObject TryEncode(object value, Type type) + public PyObject TryEncode(object value) { var error = (ValueErrorWrapper)value; return PythonEngine.Eval("ValueError").Invoke(error.Message.ToPython()); @@ -478,7 +498,7 @@ public bool TryDecode(PyObject pyObj, out T value) class ObjectToEncoderInstanceEncoder : IPyObjectEncoder { public bool CanEncode(Type type) => type == typeof(T); - public PyObject TryEncode(object value, Type type) => PyObject.FromManagedObject(this); + public PyObject TryEncode(object value) => PyObject.FromManagedObject(this); } /// @@ -533,4 +553,16 @@ public bool TryDecode(PyObject pyObj, out T value) return true; } } + + interface IObjectInterface + { + int MemberFromInterface { get; } + } + + class ExplicitInterfaceObject : IObjectInterface + { + int IObjectInterface.MemberFromInterface { get; } + + public int MemberFromObject { get; } + } } diff --git a/src/embed_tests/Python.EmbeddingTest.csproj b/src/embed_tests/Python.EmbeddingTest.csproj index 4993994d3..be36ca787 100644 --- a/src/embed_tests/Python.EmbeddingTest.csproj +++ b/src/embed_tests/Python.EmbeddingTest.csproj @@ -2,6 +2,7 @@ net472;net6.0 + LatestMajor ..\pythonnet.snk true diff --git a/src/runtime/Codecs/EncoderGroup.cs b/src/runtime/Codecs/EncoderGroup.cs index 97f14a512..63abf35a3 100644 --- a/src/runtime/Codecs/EncoderGroup.cs +++ b/src/runtime/Codecs/EncoderGroup.cs @@ -28,13 +28,13 @@ public void Add(IPyObjectEncoder item) /// public bool CanEncode(Type type) => this.encoders.Any(encoder => encoder.CanEncode(type)); /// - public PyObject? TryEncode(object value, Type type) + public PyObject? TryEncode(object value) { if (value is null) throw new ArgumentNullException(nameof(value)); foreach (var encoder in this.GetEncoders(value.GetType())) { - var result = encoder.TryEncode(value, type); + var result = encoder.TryEncode(value); if (result != null) { return result; diff --git a/src/runtime/Codecs/EnumPyIntCodec.cs b/src/runtime/Codecs/EnumPyIntCodec.cs index 5023d4395..8c6bc234e 100644 --- a/src/runtime/Codecs/EnumPyIntCodec.cs +++ b/src/runtime/Codecs/EnumPyIntCodec.cs @@ -46,11 +46,11 @@ public bool TryDecode(PyObject pyObj, out T? value) return false; } - public PyObject? TryEncode(object value, Type type) + public PyObject? TryEncode(object value) { if (value is null) return null; - - if (!type.IsEnum) return null; + + if (!value.GetType().IsEnum) return null; try { diff --git a/src/runtime/Codecs/IPyObjectEncoder.cs b/src/runtime/Codecs/IPyObjectEncoder.cs index 1eab1f48b..94d19da90 100644 --- a/src/runtime/Codecs/IPyObjectEncoder.cs +++ b/src/runtime/Codecs/IPyObjectEncoder.cs @@ -13,7 +13,6 @@ public interface IPyObjectEncoder bool CanEncode(Type type); /// /// Attempts to encode CLR object into Python object - /// using the specified /// - PyObject? TryEncode(object value, Type type); + PyObject? TryEncode(object value); } diff --git a/src/runtime/Codecs/PyObjectConversions.cs b/src/runtime/Codecs/PyObjectConversions.cs index 705253c20..cde97c8c9 100644 --- a/src/runtime/Codecs/PyObjectConversions.cs +++ b/src/runtime/Codecs/PyObjectConversions.cs @@ -54,7 +54,7 @@ public static void RegisterDecoder(IPyObjectDecoder decoder) foreach (var encoder in clrToPython.GetOrAdd(type, GetEncoders)) { - var result = encoder.TryEncode(obj, type); + var result = encoder.TryEncode(obj); if (result != null) return result; } diff --git a/src/runtime/Codecs/RawProxyEncoder.cs b/src/runtime/Codecs/RawProxyEncoder.cs index c02cb0a36..46d9205cb 100644 --- a/src/runtime/Codecs/RawProxyEncoder.cs +++ b/src/runtime/Codecs/RawProxyEncoder.cs @@ -8,11 +8,11 @@ namespace Python.Runtime.Codecs /// public class RawProxyEncoder: IPyObjectEncoder { - public PyObject TryEncode(object value, Type type) + public PyObject TryEncode(object value) { if (value is null) throw new ArgumentNullException(nameof(value)); - return PyObject.FromManagedObject(value); + return PyObject.FromManagedObject(value, value.GetType()); } public virtual bool CanEncode(Type type) => false; diff --git a/src/runtime/Codecs/TupleCodecs.cs b/src/runtime/Codecs/TupleCodecs.cs index 631f2c49e..432b3eb7d 100644 --- a/src/runtime/Codecs/TupleCodecs.cs +++ b/src/runtime/Codecs/TupleCodecs.cs @@ -18,10 +18,11 @@ public bool CanEncode(Type type) && type.Name.StartsWith(typeof(TTuple).Name + '`'); } - public PyObject? TryEncode(object value, Type type) + public PyObject? TryEncode(object value) { if (value == null) return null; + var type = value.GetType(); if (type == typeof(object)) return null; if (!this.CanEncode(type)) return null; if (type == typeof(TTuple)) return new PyTuple(); diff --git a/src/runtime/PythonTypes/PyObject.cs b/src/runtime/PythonTypes/PyObject.cs index cf0c2a03f..554c30697 100644 --- a/src/runtime/PythonTypes/PyObject.cs +++ b/src/runtime/PythonTypes/PyObject.cs @@ -25,7 +25,7 @@ public partial class PyObject : DynamicObject, IDisposable, ISerializable /// Trace stack for PyObject's construction /// public StackTrace Traceback { get; } = new StackTrace(1); -#endif +#endif protected IntPtr rawPtr = IntPtr.Zero; internal readonly int run = Runtime.GetRun(); @@ -136,14 +136,14 @@ public IntPtr Handle /// Given an arbitrary managed object, return a Python instance that /// reflects the managed object. /// - public static PyObject FromManagedObject(object ob) + public static PyObject FromManagedObject(object ob, Type? type = null) { // Special case: if ob is null, we return None. if (ob == null) { return new PyObject(Runtime.PyNone); } - return CLRObject.GetReference(ob).MoveToPyObject(); + return CLRObject.GetReference(ob, type ?? ob.GetType()).MoveToPyObject(); } /// @@ -235,7 +235,7 @@ public void Dispose() { GC.SuppressFinalize(this); Dispose(true); - + } internal StolenReference Steal() From 4fbf04c34783e717ef096ffb7fe7268611ee324f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Neves?= Date: Thu, 18 Jul 2024 17:53:30 +0100 Subject: [PATCH 04/12] Revert unnecessary changes --- src/runtime/Codecs/EnumPyIntCodec.cs | 3 ++- src/runtime/Codecs/RawProxyEncoder.cs | 2 +- src/runtime/Codecs/TupleCodecs.cs | 12 ++++++------ src/runtime/PythonTypes/PyObject.cs | 1 - 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/runtime/Codecs/EnumPyIntCodec.cs b/src/runtime/Codecs/EnumPyIntCodec.cs index 8c6bc234e..42f5eb1b2 100644 --- a/src/runtime/Codecs/EnumPyIntCodec.cs +++ b/src/runtime/Codecs/EnumPyIntCodec.cs @@ -50,7 +50,8 @@ public bool TryDecode(PyObject pyObj, out T? value) { if (value is null) return null; - if (!value.GetType().IsEnum) return null; + var enumType = value.GetType(); + if (!enumType.IsEnum) return null; try { diff --git a/src/runtime/Codecs/RawProxyEncoder.cs b/src/runtime/Codecs/RawProxyEncoder.cs index 46d9205cb..37ad0487b 100644 --- a/src/runtime/Codecs/RawProxyEncoder.cs +++ b/src/runtime/Codecs/RawProxyEncoder.cs @@ -12,7 +12,7 @@ public PyObject TryEncode(object value) { if (value is null) throw new ArgumentNullException(nameof(value)); - return PyObject.FromManagedObject(value, value.GetType()); + return PyObject.FromManagedObject(value); } public virtual bool CanEncode(Type type) => false; diff --git a/src/runtime/Codecs/TupleCodecs.cs b/src/runtime/Codecs/TupleCodecs.cs index 432b3eb7d..ec8975e3a 100644 --- a/src/runtime/Codecs/TupleCodecs.cs +++ b/src/runtime/Codecs/TupleCodecs.cs @@ -22,16 +22,16 @@ public bool CanEncode(Type type) { if (value == null) return null; - var type = value.GetType(); - if (type == typeof(object)) return null; - if (!this.CanEncode(type)) return null; - if (type == typeof(TTuple)) return new PyTuple(); + var tupleType = value.GetType(); + if (tupleType == typeof(object)) return null; + if (!this.CanEncode(tupleType)) return null; + if (tupleType == typeof(TTuple)) return new PyTuple(); - nint fieldCount = type.GetGenericArguments().Length; + nint fieldCount = tupleType.GetGenericArguments().Length; using var tuple = Runtime.PyTuple_New(fieldCount); PythonException.ThrowIfIsNull(tuple); int fieldIndex = 0; - foreach (FieldInfo field in type.GetFields()) + foreach (FieldInfo field in tupleType.GetFields()) { var item = field.GetValue(value); using var pyItem = Converter.ToPython(item, field.FieldType); diff --git a/src/runtime/PythonTypes/PyObject.cs b/src/runtime/PythonTypes/PyObject.cs index 554c30697..a62f92ee7 100644 --- a/src/runtime/PythonTypes/PyObject.cs +++ b/src/runtime/PythonTypes/PyObject.cs @@ -235,7 +235,6 @@ public void Dispose() { GC.SuppressFinalize(this); Dispose(true); - } internal StolenReference Steal() From cf8ff1f1d9e8514660b3bcc8ca79543fa49ee971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Neves?= Date: Thu, 18 Jul 2024 17:56:30 +0100 Subject: [PATCH 05/12] Add test description --- src/embed_tests/Codecs.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/embed_tests/Codecs.cs b/src/embed_tests/Codecs.cs index 1a815e2cd..90eb26669 100644 --- a/src/embed_tests/Codecs.cs +++ b/src/embed_tests/Codecs.cs @@ -339,22 +339,19 @@ public void ExceptionDecoded() Assert.AreEqual(TestExceptionMessage, error.Message); } - [Test] + [Test(Description = "Tests encoding of an object that explicitly implements an interface.")] public void ExplicitObjectInterfaceEncoded() { var obj = new ExplicitInterfaceObject(); - - // explicitly pass an interface (but not a generic type argument) to simulate a scenario where the type is not know at build time - // var encoder = new InterfaceEncoder(typeof(IObjectInterface)); - // PyObjectConversions.RegisterEncoder(encoder); - using var scope = Py.CreateScope(); scope.Exec(@" def call(obj): return dir(obj) "); var callFunc = scope.Get("call"); - var members = callFunc.Invoke(obj.ToPythonAs(typeof(IObjectInterface))).As(); + // explicitly pass an interface (but not a generic type argument) to simulate a scenario where the type is not know at build time + var callArg = obj.ToPythonAs(typeof(IObjectInterface)); + var members = callFunc.Invoke(callArg).As(); CollectionAssert.Contains(members, nameof(IObjectInterface.MemberFromInterface)); CollectionAssert.DoesNotContain(members, nameof(ExplicitInterfaceObject.MemberFromObject)); } From bc111365482557e85f7474e68d2d76dc5ff8fff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Neves?= Date: Thu, 18 Jul 2024 18:01:07 +0100 Subject: [PATCH 06/12] Remove warning --- src/runtime/Converter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/Converter.cs b/src/runtime/Converter.cs index a6d12b069..a1b5703a5 100644 --- a/src/runtime/Converter.cs +++ b/src/runtime/Converter.cs @@ -988,7 +988,7 @@ public static PyObject ToPythonAs(this T? o) return Converter.ToPython(o, typeof(T)).MoveToPyObject(); } - public static PyObject ToPythonAs(this object o, Type type) + public static PyObject ToPythonAs(this object? o, Type type) { if (o is null) return Runtime.None; return Converter.ToPython(o, type).MoveToPyObject(); From 1073b03ef600925040721ea5bca0160eff6ecdb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Neves?= Date: Thu, 18 Jul 2024 18:10:42 +0100 Subject: [PATCH 07/12] Update authors and changelog @microsoft-github-policy-service agree --- AUTHORS.md | 1 + CHANGELOG.md | 87 ++++++++++++++++++++++++---------------------------- 2 files changed, 41 insertions(+), 47 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index 18435671c..f0d9f96ec 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -44,6 +44,7 @@ - Jan Krivanek ([@jakrivan](https://github.com/jakrivan)) - Jeff Reback ([@jreback](https://github.com/jreback)) - Jeff Robbins ([@jeff17robbins](https://github.com/jeff17robbins)) +- João Neves ([@joaompneves](https://github.com/joaompneves)) - Joe Frayne ([@jfrayne](https://github.com/jfrayne)) - Joe Lidbetter ([@jmlidbetter](https://github.com/jmlidbetter)) - Joe Savage ([@s4v4g3](https://github.com/s4v4g3)) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6cc52d72..13ed9b331 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,12 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. ### Added +- Added `ToPythonAs()` extension method to allow for explicit conversion using a specific type not known at build time. ([#2419][i2419]) + - Added `ToPythonAs()` extension method to allow for explicit conversion using a specific type. ([#2311][i2311]) - Added `IComparable` and `IEquatable` implementations to `PyInt`, `PyFloat`, and `PyString` - to compare with primitive .NET types like `long`. + to compare with primitive .NET types like `long`. ### Changed @@ -56,7 +58,6 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Fixed `PyInt` conversion to `BigInteger` and `System.String` produced incorrect result for values between 128 and 255. - Fixed implementing a generic interface with a Python class - ## [3.0.0](https://github.com/pythonnet/pythonnet/releases/tag/v3.0.0) - 2022-09-29 ### Added @@ -67,61 +68,63 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Ability to implement delegates with `ref` and `out` parameters in Python, by returning the modified parameter values in a tuple. ([#1355][i1355]) - Ability to override .NET methods that have `out` or `ref` in Python by returning the modified parameter values in a tuple. ([#1481][i1481]) - `PyType` - a wrapper for Python type objects, that also permits creating new heap types from `TypeSpec` -- Improved exception handling: - * exceptions can now be converted with codecs - * `InnerException` and `__cause__` are propagated properly +- Improved exception handling: + +* exceptions can now be converted with codecs +* `InnerException` and `__cause__` are propagated properly + - `__name__` and `__signature__` to reflected .NET methods - .NET collection types now implement standard Python collection interfaces from `collections.abc`. -See [Mixins/collections.py](src/runtime/Mixins/collections.py). + See [Mixins/collections.py](src/runtime/Mixins/collections.py). - you can cast objects to generic .NET interfaces without specifying generic arguments as long as there is no ambiguity. - .NET arrays implement Python buffer protocol - Python integer interoperability with `System.Numerics.BigInteger` - Python.NET will correctly resolve .NET methods, that accept `PyList`, `PyInt`, -and other `PyObject` derived types when called from Python. + and other `PyObject` derived types when called from Python. - .NET classes, that have `__call__` method are callable from Python - `PyIterable` type, that wraps any iterable object in Python - `PythonEngine` properties for supported Python versions: `MinSupportedVersion`, `MaxSupportedVersion`, and `IsSupportedVersion` - The runtime that is loaded on `import clr` can now be configured via environment variables - ### Changed + - Drop support for Python 2, 3.4, 3.5, and 3.6 - `wchar_t` size aka `Runtime.UCS` is now determined at runtime - `clr.AddReference` may now throw errors besides `FileNotFoundException`, that provide more -details about the cause of the failure + details about the cause of the failure - `clr.AddReference` no longer adds ".dll" implicitly - `PyIter(PyObject)` constructor replaced with static `PyIter.GetIter(PyObject)` method - Python runtime can no longer be shut down if the Python error indicator is set, as it would have unpredictable behavior - BREAKING: Return values from .NET methods that return an interface are now automatically - wrapped in that interface. This is a breaking change for users that rely on being - able to access members that are part of the implementation class, but not the - interface. Use the new `__implementation__` or `__raw_implementation__` properties to - if you need to "downcast" to the implementation class. + wrapped in that interface. This is a breaking change for users that rely on being + able to access members that are part of the implementation class, but not the + interface. Use the new `__implementation__` or `__raw_implementation__` properties to + if you need to "downcast" to the implementation class. - BREAKING: `==` and `!=` operators on `PyObject` instances now use Python comparison - (previously was equivalent to `object.ReferenceEquals(,)`) + (previously was equivalent to `object.ReferenceEquals(,)`) - BREAKING: Parameters marked with `ParameterAttributes.Out` are no longer returned in addition - to the regular method return value (unless they are passed with `ref` or `out` keyword). -- BREAKING: Drop support for the long-deprecated CLR.* prefix. + to the regular method return value (unless they are passed with `ref` or `out` keyword). +- BREAKING: Drop support for the long-deprecated CLR.\* prefix. - `PyObject` now implements `IEnumerable` in addition to `IEnumerable` - floating point values passed from Python are no longer silently truncated -when .NET expects an integer [#1342][i1342] + when .NET expects an integer [#1342][i1342] - More specific error messages for method argument mismatch - members of `PyObject` inherited from `System.Object and `DynamicObject` now autoacquire GIL - BREAKING: when inheriting from .NET types in Python if you override `__init__` you -must explicitly call base constructor using `super().__init__(.....)`. Not doing so will lead -to undefined behavior. + must explicitly call base constructor using `super().__init__(.....)`. Not doing so will lead + to undefined behavior. - BREAKING: most `PyScope` methods will never return `null`. Instead, `PyObject` `None` will be returned. - BREAKING: `PyScope` was renamed to `PyModule` - BREAKING: Methods with `ref` or `out` parameters and void return type return a tuple of only the `ref` and `out` parameters. - BREAKING: to call Python from .NET `Runtime.PythonDLL` property must be set to Python DLL name -or the DLL must be loaded in advance. This must be done before calling any other Python.NET functions. + or the DLL must be loaded in advance. This must be done before calling any other Python.NET functions. - BREAKING: `PyObject.Length()` now raises a `PythonException` when object does not support a concept of length. - BREAKING: disabled implicit conversion from C# enums to Python `int` and back. -One must now either use enum members (e.g. `MyEnum.Option`), or use enum constructor -(e.g. `MyEnum(42)` or `MyEnum(42, True)` when `MyEnum` does not have a member with value 42). + One must now either use enum members (e.g. `MyEnum.Option`), or use enum constructor + (e.g. `MyEnum(42)` or `MyEnum(42, True)` when `MyEnum` does not have a member with value 42). - BREAKING: disabled implicit conversion from Python objects implementing sequence protocol to -.NET arrays when the target .NET type is `System.Object`. The conversion is still attempted when the -target type is a `System.Array`. + .NET arrays when the target .NET type is `System.Object`. The conversion is still attempted when the + target type is a `System.Array`. - Sign Runtime DLL with a strong name - Implement loading through `clr_loader` instead of the included `ClrModule`, enables support for .NET Core @@ -131,23 +134,23 @@ target type is a `System.Array`. - Replaced the old `__import__` hook hack with a PEP302-style Meta Path Loader - BREAKING: Names of .NET types (e.g. `str(__class__)`) changed to better support generic types - BREAKING: overload resolution will no longer prefer basic types. Instead, first matching overload will -be chosen. + be chosen. - BREAKING: acquiring GIL using `Py.GIL` no longer forces `PythonEngine` to initialize - BREAKING: `Exec` and `Eval` from `PythonEngine` no longer accept raw pointers. - BREAKING: .NET collections and arrays are no longer automatically converted to -Python collections. Instead, they implement standard Python -collection interfaces from `collections.abc`. -See [Mixins/collections.py](src/runtime/Mixins/collections.py). + Python collections. Instead, they implement standard Python + collection interfaces from `collections.abc`. + See [Mixins/collections.py](src/runtime/Mixins/collections.py). - BREAKING: When trying to convert Python `int` to `System.Object`, result will -be of type `PyInt` instead of `System.Int32` due to possible loss of information. -Python `float` will continue to be converted to `System.Double`. + be of type `PyInt` instead of `System.Int32` due to possible loss of information. + Python `float` will continue to be converted to `System.Double`. - BREAKING: Python.NET will no longer implicitly convert types like `numpy.float64`, that implement `__float__` to -`System.Single` and `System.Double`. An explicit conversion is required on Python or .NET side. + `System.Single` and `System.Double`. An explicit conversion is required on Python or .NET side. - BREAKING: `PyObject.GetHashCode` can fail. - BREAKING: Python.NET will no longer implicitly convert any Python object to `System.Boolean`. - BREAKING: `PyObject.GetAttr(name, default)` now only ignores `AttributeError` (previously ignored all exceptions). - BREAKING: `PyObject` no longer implements `IEnumerable`. -Instead, `PyIterable` does that. + Instead, `PyIterable` does that. - BREAKING: `IPyObjectDecoder.CanDecode` `objectType` parameter type changed from `PyObject` to `PyType` ### Fixed @@ -179,18 +182,19 @@ Instead, `PyIterable` does that. ### Removed - `ShutdownMode` has been removed. The only shutdown mode supported now is an equivalent of `ShutdownMode.Reload`. -There is no need to specify it. + There is no need to specify it. - implicit assembly loading (you have to explicitly `clr.AddReference` before doing import) - messages in `PythonException` no longer start with exception type - `PyScopeManager`, `PyScopeException`, `PyScope` (use `PyModule` instead) - support for .NET Framework 4.0-4.6; Mono before 5.4. Python.NET now requires .NET Standard 2.0 -(see [the matrix](https://docs.microsoft.com/en-us/dotnet/standard/net-standard#net-implementation-support)) + (see [the matrix](https://docs.microsoft.com/en-us/dotnet/standard/net-standard#net-implementation-support)) ## [2.5.2](https://github.com/pythonnet/pythonnet/releases/tag/v2.5.2) - 2021-02-05 Bugfix release. ### Fixed + - Fix `object[]` parameters taking precedence when should not in overload resolution - Empty parameter names (as can be generated from F#) do not cause crashes @@ -200,8 +204,8 @@ Bugfix release. ### Fixed -- Fix incorrect dereference of wrapper object in `tp_repr`, which may result in a program crash -- Fix incorrect dereference in params array handling +- Fix incorrect dereference of wrapper object in `tp_repr`, which may result in a program crash +- Fix incorrect dereference in params array handling ## [2.5.0](https://github.com/pythonnet/pythonnet/releases/tag/v2.5.0) - 2020-06-14 @@ -308,7 +312,6 @@ This version improves performance on benchmarks significantly compared to 2.3. - Fix spurious assembly loading exceptions from private types ([#703][i703]) - Fix inheritance of non-abstract base methods ([#755][i755]) - ## [2.3.0][] - 2017-03-11 ### Added @@ -845,25 +848,15 @@ This version improves performance on benchmarks significantly compared to 2.3. - Initial (mostly) working experimental release. [keep a changelog]: http://keepachangelog.com/ - [semantic versioning]: http://semver.org/ - [unreleased]: ../../compare/v3.0.1...HEAD - [2.3.0]: ../../compare/v2.2.2...v2.3.0 - [2.2.2]: ../../compare/v2.2.1...v2.2.2 - [2.2.1]: ../../compare/v2.2.0-dev1...v2.2.1 - [2.2.0-dev1]: ../../compare/v2.1.0...v2.2.0-dev1 - [2.1.0]: ../../compare/v2.0.0...v2.1.0 - [2.0.0]: ../../compare/1.0...v2.0.0 - [1.0.0]: https://github.com/pythonnet/pythonnet/releases/tag/1.0 - [i714]: https://github.com/pythonnet/pythonnet/issues/714 [i608]: https://github.com/pythonnet/pythonnet/issues/608 [i443]: https://github.com/pythonnet/pythonnet/issues/443 From b5a18d1b125ec838cd215090aabd7ca83932cf94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Neves?= Date: Thu, 18 Jul 2024 18:13:09 +0100 Subject: [PATCH 08/12] Update CHANGELOG.md Revert unintentional changes --- CHANGELOG.md | 90 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13ed9b331..1930fe9d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,13 +14,18 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Added `ToPythonAs()` extension method to allow for explicit conversion using a specific type. ([#2311][i2311]) - Added `IComparable` and `IEquatable` implementations to `PyInt`, `PyFloat`, and `PyString` - to compare with primitive .NET types like `long`. + to compare with primitive .NET types like `long`. ### Changed +- Added a `FormatterFactory` member in RuntimeData to create formatters with parameters. For compatibility, the `FormatterType` member is still present and has precedence when defining both `FormatterFactory` and `FormatterType` +- Added a post-serialization and a pre-deserialization step callbacks to extend (de)serialization process +- Added an API to stash serialized data on Python capsules ### Fixed - Fixed RecursionError for reverse operators on C# operable types from python. See #2240 +- Fixed crash when .NET event has no `AddMethod` +- Fixed probing for assemblies in `sys.path` failing when a path in `sys.path` has invalid characters. See #2376 ## [3.0.3](https://github.com/pythonnet/pythonnet/releases/tag/v3.0.3) - 2023-10-11 @@ -58,6 +63,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Fixed `PyInt` conversion to `BigInteger` and `System.String` produced incorrect result for values between 128 and 255. - Fixed implementing a generic interface with a Python class + ## [3.0.0](https://github.com/pythonnet/pythonnet/releases/tag/v3.0.0) - 2022-09-29 ### Added @@ -68,63 +74,61 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Ability to implement delegates with `ref` and `out` parameters in Python, by returning the modified parameter values in a tuple. ([#1355][i1355]) - Ability to override .NET methods that have `out` or `ref` in Python by returning the modified parameter values in a tuple. ([#1481][i1481]) - `PyType` - a wrapper for Python type objects, that also permits creating new heap types from `TypeSpec` -- Improved exception handling: - -* exceptions can now be converted with codecs -* `InnerException` and `__cause__` are propagated properly - +- Improved exception handling: + * exceptions can now be converted with codecs + * `InnerException` and `__cause__` are propagated properly - `__name__` and `__signature__` to reflected .NET methods - .NET collection types now implement standard Python collection interfaces from `collections.abc`. - See [Mixins/collections.py](src/runtime/Mixins/collections.py). +See [Mixins/collections.py](src/runtime/Mixins/collections.py). - you can cast objects to generic .NET interfaces without specifying generic arguments as long as there is no ambiguity. - .NET arrays implement Python buffer protocol - Python integer interoperability with `System.Numerics.BigInteger` - Python.NET will correctly resolve .NET methods, that accept `PyList`, `PyInt`, - and other `PyObject` derived types when called from Python. +and other `PyObject` derived types when called from Python. - .NET classes, that have `__call__` method are callable from Python - `PyIterable` type, that wraps any iterable object in Python - `PythonEngine` properties for supported Python versions: `MinSupportedVersion`, `MaxSupportedVersion`, and `IsSupportedVersion` - The runtime that is loaded on `import clr` can now be configured via environment variables -### Changed +### Changed - Drop support for Python 2, 3.4, 3.5, and 3.6 - `wchar_t` size aka `Runtime.UCS` is now determined at runtime - `clr.AddReference` may now throw errors besides `FileNotFoundException`, that provide more - details about the cause of the failure +details about the cause of the failure - `clr.AddReference` no longer adds ".dll" implicitly - `PyIter(PyObject)` constructor replaced with static `PyIter.GetIter(PyObject)` method - Python runtime can no longer be shut down if the Python error indicator is set, as it would have unpredictable behavior - BREAKING: Return values from .NET methods that return an interface are now automatically - wrapped in that interface. This is a breaking change for users that rely on being - able to access members that are part of the implementation class, but not the - interface. Use the new `__implementation__` or `__raw_implementation__` properties to - if you need to "downcast" to the implementation class. + wrapped in that interface. This is a breaking change for users that rely on being + able to access members that are part of the implementation class, but not the + interface. Use the new `__implementation__` or `__raw_implementation__` properties to + if you need to "downcast" to the implementation class. - BREAKING: `==` and `!=` operators on `PyObject` instances now use Python comparison - (previously was equivalent to `object.ReferenceEquals(,)`) + (previously was equivalent to `object.ReferenceEquals(,)`) - BREAKING: Parameters marked with `ParameterAttributes.Out` are no longer returned in addition - to the regular method return value (unless they are passed with `ref` or `out` keyword). -- BREAKING: Drop support for the long-deprecated CLR.\* prefix. + to the regular method return value (unless they are passed with `ref` or `out` keyword). +- BREAKING: Drop support for the long-deprecated CLR.* prefix. - `PyObject` now implements `IEnumerable` in addition to `IEnumerable` - floating point values passed from Python are no longer silently truncated - when .NET expects an integer [#1342][i1342] +when .NET expects an integer [#1342][i1342] - More specific error messages for method argument mismatch - members of `PyObject` inherited from `System.Object and `DynamicObject` now autoacquire GIL - BREAKING: when inheriting from .NET types in Python if you override `__init__` you - must explicitly call base constructor using `super().__init__(.....)`. Not doing so will lead - to undefined behavior. +must explicitly call base constructor using `super().__init__(.....)`. Not doing so will lead +to undefined behavior. - BREAKING: most `PyScope` methods will never return `null`. Instead, `PyObject` `None` will be returned. - BREAKING: `PyScope` was renamed to `PyModule` - BREAKING: Methods with `ref` or `out` parameters and void return type return a tuple of only the `ref` and `out` parameters. - BREAKING: to call Python from .NET `Runtime.PythonDLL` property must be set to Python DLL name - or the DLL must be loaded in advance. This must be done before calling any other Python.NET functions. +or the DLL must be loaded in advance. This must be done before calling any other Python.NET functions. - BREAKING: `PyObject.Length()` now raises a `PythonException` when object does not support a concept of length. - BREAKING: disabled implicit conversion from C# enums to Python `int` and back. - One must now either use enum members (e.g. `MyEnum.Option`), or use enum constructor - (e.g. `MyEnum(42)` or `MyEnum(42, True)` when `MyEnum` does not have a member with value 42). +One must now either use enum members (e.g. `MyEnum.Option`), or use enum constructor +(e.g. `MyEnum(42)` or `MyEnum(42, True)` when `MyEnum` does not have a member with value 42). - BREAKING: disabled implicit conversion from Python objects implementing sequence protocol to - .NET arrays when the target .NET type is `System.Object`. The conversion is still attempted when the - target type is a `System.Array`. +.NET arrays when the target .NET type is `System.Object`. The conversion is still attempted when the +target type is a `System.Array`. - Sign Runtime DLL with a strong name - Implement loading through `clr_loader` instead of the included `ClrModule`, enables support for .NET Core @@ -134,23 +138,23 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Replaced the old `__import__` hook hack with a PEP302-style Meta Path Loader - BREAKING: Names of .NET types (e.g. `str(__class__)`) changed to better support generic types - BREAKING: overload resolution will no longer prefer basic types. Instead, first matching overload will - be chosen. +be chosen. - BREAKING: acquiring GIL using `Py.GIL` no longer forces `PythonEngine` to initialize - BREAKING: `Exec` and `Eval` from `PythonEngine` no longer accept raw pointers. - BREAKING: .NET collections and arrays are no longer automatically converted to - Python collections. Instead, they implement standard Python - collection interfaces from `collections.abc`. - See [Mixins/collections.py](src/runtime/Mixins/collections.py). +Python collections. Instead, they implement standard Python +collection interfaces from `collections.abc`. +See [Mixins/collections.py](src/runtime/Mixins/collections.py). - BREAKING: When trying to convert Python `int` to `System.Object`, result will - be of type `PyInt` instead of `System.Int32` due to possible loss of information. - Python `float` will continue to be converted to `System.Double`. +be of type `PyInt` instead of `System.Int32` due to possible loss of information. +Python `float` will continue to be converted to `System.Double`. - BREAKING: Python.NET will no longer implicitly convert types like `numpy.float64`, that implement `__float__` to - `System.Single` and `System.Double`. An explicit conversion is required on Python or .NET side. +`System.Single` and `System.Double`. An explicit conversion is required on Python or .NET side. - BREAKING: `PyObject.GetHashCode` can fail. - BREAKING: Python.NET will no longer implicitly convert any Python object to `System.Boolean`. - BREAKING: `PyObject.GetAttr(name, default)` now only ignores `AttributeError` (previously ignored all exceptions). - BREAKING: `PyObject` no longer implements `IEnumerable`. - Instead, `PyIterable` does that. +Instead, `PyIterable` does that. - BREAKING: `IPyObjectDecoder.CanDecode` `objectType` parameter type changed from `PyObject` to `PyType` ### Fixed @@ -182,19 +186,18 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. ### Removed - `ShutdownMode` has been removed. The only shutdown mode supported now is an equivalent of `ShutdownMode.Reload`. - There is no need to specify it. +There is no need to specify it. - implicit assembly loading (you have to explicitly `clr.AddReference` before doing import) - messages in `PythonException` no longer start with exception type - `PyScopeManager`, `PyScopeException`, `PyScope` (use `PyModule` instead) - support for .NET Framework 4.0-4.6; Mono before 5.4. Python.NET now requires .NET Standard 2.0 - (see [the matrix](https://docs.microsoft.com/en-us/dotnet/standard/net-standard#net-implementation-support)) +(see [the matrix](https://docs.microsoft.com/en-us/dotnet/standard/net-standard#net-implementation-support)) ## [2.5.2](https://github.com/pythonnet/pythonnet/releases/tag/v2.5.2) - 2021-02-05 Bugfix release. ### Fixed - - Fix `object[]` parameters taking precedence when should not in overload resolution - Empty parameter names (as can be generated from F#) do not cause crashes @@ -204,8 +207,8 @@ Bugfix release. ### Fixed -- Fix incorrect dereference of wrapper object in `tp_repr`, which may result in a program crash -- Fix incorrect dereference in params array handling +- Fix incorrect dereference of wrapper object in `tp_repr`, which may result in a program crash +- Fix incorrect dereference in params array handling ## [2.5.0](https://github.com/pythonnet/pythonnet/releases/tag/v2.5.0) - 2020-06-14 @@ -312,6 +315,7 @@ This version improves performance on benchmarks significantly compared to 2.3. - Fix spurious assembly loading exceptions from private types ([#703][i703]) - Fix inheritance of non-abstract base methods ([#755][i755]) + ## [2.3.0][] - 2017-03-11 ### Added @@ -848,15 +852,25 @@ This version improves performance on benchmarks significantly compared to 2.3. - Initial (mostly) working experimental release. [keep a changelog]: http://keepachangelog.com/ + [semantic versioning]: http://semver.org/ + [unreleased]: ../../compare/v3.0.1...HEAD + [2.3.0]: ../../compare/v2.2.2...v2.3.0 + [2.2.2]: ../../compare/v2.2.1...v2.2.2 + [2.2.1]: ../../compare/v2.2.0-dev1...v2.2.1 + [2.2.0-dev1]: ../../compare/v2.1.0...v2.2.0-dev1 + [2.1.0]: ../../compare/v2.0.0...v2.1.0 + [2.0.0]: ../../compare/1.0...v2.0.0 + [1.0.0]: https://github.com/pythonnet/pythonnet/releases/tag/1.0 + [i714]: https://github.com/pythonnet/pythonnet/issues/714 [i608]: https://github.com/pythonnet/pythonnet/issues/608 [i443]: https://github.com/pythonnet/pythonnet/issues/443 From 37ec583443f740fcbd73e2bc52d5e06c8a693b3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Neves?= Date: Thu, 18 Jul 2024 18:14:38 +0100 Subject: [PATCH 09/12] Update CHANGELOG.md --- CHANGELOG.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1930fe9d5..4ec8ee287 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,14 +17,9 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. to compare with primitive .NET types like `long`. ### Changed -- Added a `FormatterFactory` member in RuntimeData to create formatters with parameters. For compatibility, the `FormatterType` member is still present and has precedence when defining both `FormatterFactory` and `FormatterType` -- Added a post-serialization and a pre-deserialization step callbacks to extend (de)serialization process -- Added an API to stash serialized data on Python capsules ### Fixed -- Fixed RecursionError for reverse operators on C# operable types from python. See #2240 -- Fixed crash when .NET event has no `AddMethod` - Fixed probing for assemblies in `sys.path` failing when a path in `sys.path` has invalid characters. See #2376 ## [3.0.3](https://github.com/pythonnet/pythonnet/releases/tag/v3.0.3) - 2023-10-11 From ad7f9857f6389d5445c0283b2e7c24606d744159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Neves?= Date: Thu, 18 Jul 2024 18:15:19 +0100 Subject: [PATCH 10/12] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ec8ee287..9103d5a33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. ### Fixed -- Fixed probing for assemblies in `sys.path` failing when a path in `sys.path` has invalid characters. See #2376 +- Fixed RecursionError for reverse operators on C# operable types from python. See #2240 ## [3.0.3](https://github.com/pythonnet/pythonnet/releases/tag/v3.0.3) - 2023-10-11 From bac598fa1d64d4b9d44514689b62e7d5d5b321e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Neves?= Date: Tue, 23 Jul 2024 10:45:25 +0100 Subject: [PATCH 11/12] CR --- src/runtime/Converter.cs | 3 +-- src/runtime/PythonTypes/PyObject.cs | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/runtime/Converter.cs b/src/runtime/Converter.cs index a1b5703a5..d31614067 100644 --- a/src/runtime/Converter.cs +++ b/src/runtime/Converter.cs @@ -984,8 +984,7 @@ public static PyObject ToPython(this object? o) public static PyObject ToPythonAs(this T? o) { - if (o is null) return Runtime.None; - return Converter.ToPython(o, typeof(T)).MoveToPyObject(); + return ToPythonAs(o, typeof(T)); } public static PyObject ToPythonAs(this object? o, Type type) diff --git a/src/runtime/PythonTypes/PyObject.cs b/src/runtime/PythonTypes/PyObject.cs index a62f92ee7..f560be343 100644 --- a/src/runtime/PythonTypes/PyObject.cs +++ b/src/runtime/PythonTypes/PyObject.cs @@ -136,14 +136,14 @@ public IntPtr Handle /// Given an arbitrary managed object, return a Python instance that /// reflects the managed object. /// - public static PyObject FromManagedObject(object ob, Type? type = null) + public static PyObject FromManagedObject(object ob) { // Special case: if ob is null, we return None. if (ob == null) { return new PyObject(Runtime.PyNone); } - return CLRObject.GetReference(ob, type ?? ob.GetType()).MoveToPyObject(); + return CLRObject.GetReference(ob).MoveToPyObject(); } /// @@ -235,6 +235,7 @@ public void Dispose() { GC.SuppressFinalize(this); Dispose(true); + } internal StolenReference Steal() From 8bb75194671cb9e9a90eb4b4de00576da1a75131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Neves?= Date: Tue, 23 Jul 2024 10:49:00 +0100 Subject: [PATCH 12/12] Revert file --- src/runtime/PythonTypes/PyObject.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/PythonTypes/PyObject.cs b/src/runtime/PythonTypes/PyObject.cs index f560be343..cf0c2a03f 100644 --- a/src/runtime/PythonTypes/PyObject.cs +++ b/src/runtime/PythonTypes/PyObject.cs @@ -25,7 +25,7 @@ public partial class PyObject : DynamicObject, IDisposable, ISerializable /// Trace stack for PyObject's construction /// public StackTrace Traceback { get; } = new StackTrace(1); -#endif +#endif protected IntPtr rawPtr = IntPtr.Zero; internal readonly int run = Runtime.GetRun(); @@ -235,7 +235,7 @@ public void Dispose() { GC.SuppressFinalize(this); Dispose(true); - + } internal StolenReference Steal()