diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bc0c9981..db126bd1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Added function that sets Py_NoSiteFlag to 1. - Added support for Jetson Nano. - Added support for __len__ for .NET classes that implement ICollection +- Added `object.GetRawPythonProxy() -> PyObject` extension method, that bypasses any conversions ### Changed @@ -21,6 +22,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Removes PyLong_GetMax and PyClass_New when targetting Python3 - Added support for converting python iterators to C# arrays - Changed usage of obselete function GetDelegateForFunctionPointer(IntPtr, Type) to GetDelegateForFunctionPointer(IntPtr) +- When calling C# from Python, enable passing argument of any type to a parameter of C# type `object` by wrapping it into `PyObject` instance. ([#881][i881]) - Added support for kwarg parameters when calling .NET methods from Python ### Fixed @@ -58,7 +60,6 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Reattach python exception traceback information (#545) - PythonEngine.Intialize will now call `Py_InitializeEx` with a default value of 0, so signals will not be configured by default on embedding. This is different from the previous behaviour, where `Py_Initialize` was called instead, which sets initSigs to 1. ([#449][i449]) - Refactored MethodBinder.Bind in preparation to make it extensible (#829) -- When calling C# from Python, enable passing argument of any type to a parameter of C# type `object` by wrapping it into `PyObject` instance. ([#881][i881]) - Look for installed Windows 10 sdk's during installation instead of relying on specific versions. ### Fixed diff --git a/pythonnet.15.sln b/pythonnet.15.sln index 6d1f4fcd9..ce863817f 100644 --- a/pythonnet.15.sln +++ b/pythonnet.15.sln @@ -17,6 +17,9 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Repo", "Repo", "{441A0123-F4C6-4EE4-9AEE-315FD79BE2D5}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + .gitignore = .gitignore + CHANGELOG.md = CHANGELOG.md + README.rst = README.rst EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CI", "CI", "{D301657F-5EAF-4534-B280-B858D651B2E5}" diff --git a/src/embed_tests/TestConverter.cs b/src/embed_tests/TestConverter.cs index caaec311b..078f4c0f8 100644 --- a/src/embed_tests/TestConverter.cs +++ b/src/embed_tests/TestConverter.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using NUnit.Framework; using Python.Runtime; @@ -44,5 +46,26 @@ public void TestConvertDoubleToManaged( Assert.IsTrue(converted); Assert.IsTrue(((double) convertedValue).Equals(testValue)); } + + [Test] + public void RawListProxy() + { + var list = new List {"hello", "world"}; + var listProxy = list.GetRawPythonProxy(); + var clrObject = (CLRObject)ManagedType.GetManagedObject(listProxy.Handle); + Assert.AreSame(list, clrObject.inst); + } + + [Test] + public void RawPyObjectProxy() + { + var pyObject = "hello world!".ToPython(); + var pyObjectProxy = pyObject.GetRawPythonProxy(); + var clrObject = (CLRObject)ManagedType.GetManagedObject(pyObjectProxy.Handle); + Assert.AreSame(pyObject, clrObject.inst); + + var proxiedHandle = pyObjectProxy.GetAttr("Handle").As(); + Assert.AreEqual(pyObject.Handle, proxiedHandle); + } } } diff --git a/src/runtime/NewReference.cs b/src/runtime/NewReference.cs index bbeb86dc4..3ab4b6530 100644 --- a/src/runtime/NewReference.cs +++ b/src/runtime/NewReference.cs @@ -33,6 +33,12 @@ public void Dispose() this.pointer = IntPtr.Zero; } + /// + /// Creates from a raw pointer + /// + public static NewReference DangerousFromPointer(IntPtr pointer) + => new NewReference {pointer = pointer}; + [Pure] internal static IntPtr DangerousGetAddress(in NewReference reference) => IsNull(reference) ? throw new NullReferenceException() : reference.pointer; diff --git a/src/runtime/clrobject.cs b/src/runtime/clrobject.cs index 502677655..13c15f862 100644 --- a/src/runtime/clrobject.cs +++ b/src/runtime/clrobject.cs @@ -68,5 +68,17 @@ internal static IntPtr GetInstHandle(object ob) CLRObject co = GetInstance(ob); return co.pyHandle; } + + /// + /// Creates proxy for the given object, + /// and returns a to it. + /// + internal static NewReference MakeNewReference(object obj) + { + if (obj is null) throw new ArgumentNullException(nameof(obj)); + + // TODO: CLRObject currently does not have Dispose or finalizer which might change in the future + return NewReference.DangerousFromPointer(GetInstHandle(obj)); + } } } diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 3add8aba0..a7b7b5c48 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -967,5 +967,16 @@ public static PyObject ToPython(this object o) { return new PyObject(Converter.ToPython(o, o?.GetType())); } + + /// + /// Gets raw Python proxy for this object (bypasses all conversions, + /// except null <==> None) + /// + public static PyObject GetRawPythonProxy(this object o) + { + if (o is null) return new PyObject(new BorrowedReference(Runtime.PyNone)); + + return CLRObject.MakeNewReference(o).MoveToPyObject(); + } } }