Skip to content

Commit 653a932

Browse files
authored
adds extension object.GetRawPythonProxy() (#1078)
GetRawPythonProxy creates a PyObject pointing to the specified object without performing any coversions, which lets .NET code to pass CLR objects as-is, if it needs Python to have direct access to them. This enables codecs to create arbitrary proxy objects, bypassing default conversions or other registered codecs.
1 parent 9fd877e commit 653a932

File tree

6 files changed

+57
-1
lines changed

6 files changed

+57
-1
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1313
- Added function that sets Py_NoSiteFlag to 1.
1414
- Added support for Jetson Nano.
1515
- Added support for __len__ for .NET classes that implement ICollection
16+
- Added `object.GetRawPythonProxy() -> PyObject` extension method, that bypasses any conversions
1617

1718
### Changed
1819

@@ -21,6 +22,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
2122
- Removes PyLong_GetMax and PyClass_New when targetting Python3
2223
- Added support for converting python iterators to C# arrays
2324
- Changed usage of obselete function GetDelegateForFunctionPointer(IntPtr, Type) to GetDelegateForFunctionPointer<TDelegate>(IntPtr)
25+
- 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])
2426
- Added support for kwarg parameters when calling .NET methods from Python
2527

2628
### Fixed
@@ -58,7 +60,6 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
5860
- Reattach python exception traceback information (#545)
5961
- 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])
6062
- Refactored MethodBinder.Bind in preparation to make it extensible (#829)
61-
- 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])
6263
- Look for installed Windows 10 sdk's during installation instead of relying on specific versions.
6364

6465
### Fixed

pythonnet.15.sln

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ EndProject
1717
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Repo", "Repo", "{441A0123-F4C6-4EE4-9AEE-315FD79BE2D5}"
1818
ProjectSection(SolutionItems) = preProject
1919
.editorconfig = .editorconfig
20+
.gitignore = .gitignore
21+
CHANGELOG.md = CHANGELOG.md
22+
README.rst = README.rst
2023
EndProjectSection
2124
EndProject
2225
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CI", "CI", "{D301657F-5EAF-4534-B280-B858D651B2E5}"

src/embed_tests/TestConverter.cs

+23
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System;
2+
using System.Collections.Generic;
13
using NUnit.Framework;
24
using Python.Runtime;
35

@@ -44,5 +46,26 @@ public void TestConvertDoubleToManaged(
4446
Assert.IsTrue(converted);
4547
Assert.IsTrue(((double) convertedValue).Equals(testValue));
4648
}
49+
50+
[Test]
51+
public void RawListProxy()
52+
{
53+
var list = new List<string> {"hello", "world"};
54+
var listProxy = list.GetRawPythonProxy();
55+
var clrObject = (CLRObject)ManagedType.GetManagedObject(listProxy.Handle);
56+
Assert.AreSame(list, clrObject.inst);
57+
}
58+
59+
[Test]
60+
public void RawPyObjectProxy()
61+
{
62+
var pyObject = "hello world!".ToPython();
63+
var pyObjectProxy = pyObject.GetRawPythonProxy();
64+
var clrObject = (CLRObject)ManagedType.GetManagedObject(pyObjectProxy.Handle);
65+
Assert.AreSame(pyObject, clrObject.inst);
66+
67+
var proxiedHandle = pyObjectProxy.GetAttr("Handle").As<IntPtr>();
68+
Assert.AreEqual(pyObject.Handle, proxiedHandle);
69+
}
4770
}
4871
}

src/runtime/NewReference.cs

+6
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ public void Dispose()
3333
this.pointer = IntPtr.Zero;
3434
}
3535

36+
/// <summary>
37+
/// Creates <see cref="NewReference"/> from a raw pointer
38+
/// </summary>
39+
public static NewReference DangerousFromPointer(IntPtr pointer)
40+
=> new NewReference {pointer = pointer};
41+
3642
[Pure]
3743
internal static IntPtr DangerousGetAddress(in NewReference reference)
3844
=> IsNull(reference) ? throw new NullReferenceException() : reference.pointer;

src/runtime/clrobject.cs

+12
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,17 @@ internal static IntPtr GetInstHandle(object ob)
6868
CLRObject co = GetInstance(ob);
6969
return co.pyHandle;
7070
}
71+
72+
/// <summary>
73+
/// Creates <see cref="CLRObject"/> proxy for the given object,
74+
/// and returns a <see cref="NewReference"/> to it.
75+
/// </summary>
76+
internal static NewReference MakeNewReference(object obj)
77+
{
78+
if (obj is null) throw new ArgumentNullException(nameof(obj));
79+
80+
// TODO: CLRObject currently does not have Dispose or finalizer which might change in the future
81+
return NewReference.DangerousFromPointer(GetInstHandle(obj));
82+
}
7183
}
7284
}

src/runtime/converter.cs

+11
Original file line numberDiff line numberDiff line change
@@ -967,5 +967,16 @@ public static PyObject ToPython(this object o)
967967
{
968968
return new PyObject(Converter.ToPython(o, o?.GetType()));
969969
}
970+
971+
/// <summary>
972+
/// Gets raw Python proxy for this object (bypasses all conversions,
973+
/// except <c>null</c> &lt;==&gt; <c>None</c>)
974+
/// </summary>
975+
public static PyObject GetRawPythonProxy(this object o)
976+
{
977+
if (o is null) return new PyObject(new BorrowedReference(Runtime.PyNone));
978+
979+
return CLRObject.MakeNewReference(o).MoveToPyObject();
980+
}
970981
}
971982
}

0 commit comments

Comments
 (0)