Skip to content

Commit 2dd8d84

Browse files
authored
Merge branch 'master' into bugs/1067
2 parents d8e6dcb + 9fd877e commit 2dd8d84

File tree

11 files changed

+106
-30
lines changed

11 files changed

+106
-30
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
5858
- Reattach python exception traceback information (#545)
5959
- 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])
6060
- 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])
6162
- Look for installed Windows 10 sdk's during installation instead of relying on specific versions.
6263

6364
### Fixed

src/embed_tests/Python.EmbeddingTest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
<Compile Include="TestDomainReload.cs" />
9595
<Compile Include="TestExample.cs" />
9696
<Compile Include="TestFinalizer.cs" />
97+
<Compile Include="TestInstanceWrapping.cs" />
9798
<Compile Include="TestPyAnsiString.cs" />
9899
<Compile Include="TestPyFloat.cs" />
99100
<Compile Include="TestPyInt.cs" />
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System;
2+
using System.Globalization;
3+
using NUnit.Framework;
4+
using Python.Runtime;
5+
6+
namespace Python.EmbeddingTest
7+
{
8+
public class TestInstanceWrapping
9+
{
10+
[OneTimeSetUp]
11+
public void SetUp()
12+
{
13+
PythonEngine.Initialize();
14+
}
15+
16+
[OneTimeTearDown]
17+
public void Dispose()
18+
{
19+
PythonEngine.Shutdown();
20+
}
21+
22+
// regression test for https://github.com/pythonnet/pythonnet/issues/811
23+
[Test]
24+
public void OverloadResolution_UnknownToObject()
25+
{
26+
var overloaded = new Overloaded();
27+
using (Py.GIL())
28+
{
29+
var o = overloaded.ToPython();
30+
31+
dynamic callWithSelf = PythonEngine.Eval("lambda o: o.ObjOrClass(object())");
32+
callWithSelf(o);
33+
Assert.AreEqual(Overloaded.Object, overloaded.Value);
34+
}
35+
}
36+
37+
class Base {}
38+
class Derived: Base { }
39+
40+
class Overloaded: Derived
41+
{
42+
public int Value { get; set; }
43+
public void IntOrStr(int arg) => this.Value = arg;
44+
public void IntOrStr(string arg) =>
45+
this.Value = int.Parse(arg, NumberStyles.Integer, CultureInfo.InvariantCulture);
46+
47+
public const int Object = 1;
48+
public const int ConcreteClass = 2;
49+
public void ObjOrClass(object _) => this.Value = Object;
50+
public void ObjOrClass(Overloaded _) => this.Value = ConcreteClass;
51+
52+
public const int Base = ConcreteClass + 1;
53+
public const int Derived = Base + 1;
54+
public void BaseOrDerived(Base _) => this.Value = Base;
55+
public void BaseOrDerived(Derived _) => this.Value = Derived;
56+
}
57+
}
58+
}

src/embed_tests/pyimport.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public void SetUp()
3939

4040
IntPtr str = Runtime.Runtime.PyString_FromString(testPath);
4141
IntPtr path = Runtime.Runtime.PySys_GetObject("path");
42-
Runtime.Runtime.PyList_Append(path, str);
42+
Runtime.Runtime.PyList_Append(new BorrowedReference(path), str);
4343
}
4444

4545
[TearDown]

src/runtime/BorrowedReference.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ readonly ref struct BorrowedReference
1414
public IntPtr DangerousGetAddress()
1515
=> this.IsNull ? throw new NullReferenceException() : this.pointer;
1616

17-
BorrowedReference(IntPtr pointer)
17+
/// <summary>
18+
/// Creates new instance of <see cref="BorrowedReference"/> from raw pointer. Unsafe.
19+
/// </summary>
20+
public BorrowedReference(IntPtr pointer)
1821
{
1922
this.pointer = pointer;
2023
}

src/runtime/converter.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -398,12 +398,9 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
398398
return ToArray(value, typeof(object[]), out result, setError);
399399
}
400400

401-
if (setError)
402-
{
403-
Exceptions.SetError(Exceptions.TypeError, "value cannot be converted to Object");
404-
}
405-
406-
return false;
401+
Runtime.XIncref(value); // PyObject() assumes ownership
402+
result = new PyObject(value);
403+
return true;
407404
}
408405

409406
// Conversion to 'Type' is done using the same mappings as above for objects.

src/runtime/pylist.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public static PyList AsList(PyObject value)
120120
/// </remarks>
121121
public void Append(PyObject item)
122122
{
123-
int r = Runtime.PyList_Append(obj, item.obj);
123+
int r = Runtime.PyList_Append(this.Reference, item.obj);
124124
if (r < 0)
125125
{
126126
throw new PythonException();
@@ -135,7 +135,7 @@ public void Append(PyObject item)
135135
/// </remarks>
136136
public void Insert(int index, PyObject item)
137137
{
138-
int r = Runtime.PyList_Insert(obj, index, item.obj);
138+
int r = Runtime.PyList_Insert(this.Reference, index, item.obj);
139139
if (r < 0)
140140
{
141141
throw new PythonException();
@@ -151,7 +151,7 @@ public void Insert(int index, PyObject item)
151151
/// </remarks>
152152
public void Reverse()
153153
{
154-
int r = Runtime.PyList_Reverse(obj);
154+
int r = Runtime.PyList_Reverse(this.Reference);
155155
if (r < 0)
156156
{
157157
throw new PythonException();
@@ -167,7 +167,7 @@ public void Reverse()
167167
/// </remarks>
168168
public void Sort()
169169
{
170-
int r = Runtime.PyList_Sort(obj);
170+
int r = Runtime.PyList_Sort(this.Reference);
171171
if (r < 0)
172172
{
173173
throw new PythonException();

src/runtime/pyobject.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public class PyObject : DynamicObject, IEnumerable, IPyDisposable
3333
private bool disposed = false;
3434
private bool _finalized = false;
3535

36+
internal BorrowedReference Reference => new BorrowedReference(obj);
37+
3638
/// <summary>
3739
/// PyObject Constructor
3840
/// </summary>
@@ -52,9 +54,24 @@ public PyObject(IntPtr ptr)
5254
#endif
5355
}
5456

57+
/// <summary>
58+
/// Creates new <see cref="PyObject"/> pointing to the same object as
59+
/// the <paramref name="reference"/>. Increments refcount, allowing <see cref="PyObject"/>
60+
/// to have ownership over its own reference.
61+
/// </summary>
62+
internal PyObject(BorrowedReference reference)
63+
{
64+
if (reference.IsNull) throw new ArgumentNullException(nameof(reference));
65+
66+
obj = Runtime.SelfIncRef(reference.DangerousGetAddress());
67+
#if TRACE_ALLOC
68+
Traceback = new StackTrace(1);
69+
#endif
70+
}
71+
5572
// Protected default constructor to allow subclasses to manage
5673
// initialization in different ways as appropriate.
57-
[Obsolete("Please, always use PyObject(IntPtr)")]
74+
[Obsolete("Please, always use PyObject(*Reference)")]
5875
protected PyObject()
5976
{
6077
#if TRACE_ALLOC

src/runtime/runtime.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ internal static void Initialize(bool initSigs = false)
341341
string rtdir = RuntimeEnvironment.GetRuntimeDirectory();
342342
IntPtr path = PySys_GetObject("path");
343343
IntPtr item = PyString_FromString(rtdir);
344-
PyList_Append(path, item);
344+
PyList_Append(new BorrowedReference(path), item);
345345
XDecref(item);
346346
AssemblyManager.UpdatePath();
347347
}
@@ -1658,22 +1658,22 @@ internal static int PyList_SetItem(IntPtr pointer, long index, IntPtr value)
16581658
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
16591659
private static extern int PyList_SetItem(IntPtr pointer, IntPtr index, IntPtr value);
16601660

1661-
internal static int PyList_Insert(IntPtr pointer, long index, IntPtr value)
1661+
internal static int PyList_Insert(BorrowedReference pointer, long index, IntPtr value)
16621662
{
16631663
return PyList_Insert(pointer, new IntPtr(index), value);
16641664
}
16651665

16661666
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
1667-
private static extern int PyList_Insert(IntPtr pointer, IntPtr index, IntPtr value);
1667+
private static extern int PyList_Insert(BorrowedReference pointer, IntPtr index, IntPtr value);
16681668

16691669
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
1670-
internal static extern int PyList_Append(IntPtr pointer, IntPtr value);
1670+
internal static extern int PyList_Append(BorrowedReference pointer, IntPtr value);
16711671

16721672
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
1673-
internal static extern int PyList_Reverse(IntPtr pointer);
1673+
internal static extern int PyList_Reverse(BorrowedReference pointer);
16741674

16751675
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
1676-
internal static extern int PyList_Sort(IntPtr pointer);
1676+
internal static extern int PyList_Sort(BorrowedReference pointer);
16771677

16781678
internal static IntPtr PyList_GetSlice(IntPtr pointer, long start, long end)
16791679
{

src/tests/test_conversion.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -595,11 +595,10 @@ def test_object_conversion():
595595

596596
# need to test subclass here
597597

598-
with pytest.raises(TypeError):
599-
class Foo(object):
600-
pass
601-
ob = ConversionTest()
602-
ob.ObjectField = Foo
598+
class Foo(object):
599+
pass
600+
ob.ObjectField = Foo
601+
assert ob.ObjectField == Foo
603602

604603

605604
def test_enum_conversion():

src/tests/test_indexer.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -438,13 +438,13 @@ def test_object_indexer():
438438
ob[long(1)] = "long"
439439
assert ob[long(1)] == "long"
440440

441-
with pytest.raises(TypeError):
442-
class Eggs(object):
443-
pass
441+
class Eggs(object):
442+
pass
444443

445-
key = Eggs()
446-
ob = Test.ObjectIndexerTest()
447-
ob[key] = "wrong"
444+
key = Eggs()
445+
ob = Test.ObjectIndexerTest()
446+
ob[key] = "eggs_key"
447+
assert ob[key] == "eggs_key"
448448

449449

450450
def test_interface_indexer():

0 commit comments

Comments
 (0)