8000 Merge branch 'master' into list-codec · pythonnet/pythonnet@42697ba · GitHub
[go: up one dir, main page]

Skip to content

Commit 42697ba

Browse files
authored
Merge branch 'master' into list-codec
2 parents cde2eca + 50d947f commit 42697ba

33 files changed

+460
-188
lines changed

AUTHORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
- Christoph Gohlke ([@cgohlke](https://github.com/cgohlke))
2929
- Christopher Bremner ([@chrisjbremner](https://github.com/chrisjbremner))
3030
- Christopher Pow ([@christopherpow](https://github.com/christopherpow))
31+
- Daniel Abrahamsson ([@danabr](https://github.com/danabr))
3132
- Daniel Fernandez ([@fdanny](https://github.com/fdanny))
3233
- Daniel Santana ([@dgsantana](https://github.com/dgsantana))
3334
- Dave Hirschfeld ([@dhirschfeld](https://github.com/dhirschfeld))

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,20 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1414
- `clr.AddReference` may now throw errors besides `FileNotFoundException`, that provide more
1515
details about the cause of the failure
1616
- `clr.AddReference` no longer adds ".dll" implicitly
17+
- `PyIter(PyObject)` constructor replaced with static `PyIter.GetIter(PyObject)` method
18+
- Return values from .NET methods that return an interface are now automatically
19+
wrapped in that interface. This is a breaking change for users that rely on being
20+
able to access members that are part of the implementation class, but not the
21+
interface. Use the new __implementation__ or __raw_implementation__ properties to
22+
if you need to "downcast" to the implementation class.
1723

1824
### Fixed
1925

2026
- Fix incorrect dereference of wrapper object in `tp_repr`, which may result in a program crash
2127
- Fix incorrect dereference in params array handling
2228
- Fix `object[]` parameters taking precedence when should not in overload resolution
29+
- Fixed a bug where all .NET class instances were considered Iterable
30+
- Fix incorrect choice of method to invoke when using keyword arguments.
2331

2432
## [2.5.0][] - 2020-06-14
2533

src/embed_tests/TestFinalizer.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,18 @@ public void CollectBasicObject()
8787
Assert.GreaterOrEqual(objectCount, 1);
8888
}
8989

90+
[Test]
91+
public void CollectOnShutdown()
92+
{
93+
MakeAGarbage(out var shortWeak, out var longWeak);
94+
FullGCCollect();
95+
var garbage = Finalizer.Instance.GetCollectedObjects();
96+
Assert.IsNotEmpty(garbage);
97+
PythonEngine.Shutdown();
98+
garbage = Finalizer.Instance.GetCollectedObjects();
99+
Assert.IsEmpty(garbage);
100+
}
101+
90102
private static void MakeAGarbage(out WeakReference shortWeak, out WeakReference longWeak)
91103
{
92104
PyLong obj = new PyLong(1024);

src/perf_tests/Python.PerformanceTests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
</ItemGroup>
2929

3030
<Target Name="GetRuntimeLibBuildOutput" BeforeTargets="Build">
31-
<MSBuild Projects="..\runtime\Python.Runtime.15.csproj" Properties="PYTHONNET_PY3_VERSION=PYTHON35;Configuration=$(Configuration);TargetFramework=net40;Python3Version=PYTHON35;OutputPath=bin\for_perf\">
31+
<MSBuild Projects="..\runtime\Python.Runtime.15.csproj" Properties="PYTHONNET_PY3_VERSION=PYTHON38;Configuration=$(Configuration);TargetFramework=net40;Python3Version=PYTHON38;OutputPath=bin\for_perf\">
3232
<Output TaskParameter="TargetOutputs" ItemName="NewPythonRuntime" />
3333
</MSBuild>
3434
</Target>

src/runtime/arrayobject.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
4343
public static IntPtr mp_subscript(IntPtr ob, IntPtr idx)
4444
{
4545
var obj = (CLRObject)GetManagedObject(ob);
46+
var arrObj = (ArrayObject)GetManagedObjectType(ob);
4647
var items = obj.inst as Array;
47-
Type itemType = obj.inst.GetType().GetElementType();
48+
Type itemType = arrObj.type.GetElementType();
4849
int rank = items.Rank;
4950
int index;
5051
object value;

src/runtime/constructorbinder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ internal object InvokeRaw(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info)
8989
// any extra args are intended for the subclass' __init__.
9090

9191
IntPtr eargs = Runtime.PyTuple_New(0);
92-
binding = Bind(inst, eargs, kw);
92+
binding = Bind(inst, eargs, IntPtr.Zero);
9393
Runtime.XDecref(eargs);
9494

9595
if (binding == null)

src/runtime/converter.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,21 @@ internal static IntPtr ToPython(object value, Type type)
173173
}
174174
}
175175

176+
if (type.IsInterface)
177+
{
178+
var ifaceObj = (InterfaceObject)ClassManager.GetClass(type);
179+
return ifaceObj.WrapObject(value);
180+
}
181+
182+
// We need to special case interface array handling to ensure we
183+
// produce the correct type. Value may be an array of some concrete
184+
// type (FooImpl[]), but we want access to go via the interface type
185+
// (IFoo[]).
186+
if (type.IsArray && type.GetElementType().IsInterface)
187+
{
188+
return CLRObject.GetInstHandle(value, type);
189+
}
190+
176191
// it the type is a python subclass of a managed type then return the
177192
// underlying python object rather than construct a new wrapper object.
178193
var pyderived = value as IPythonDerivedType;

src/runtime/interfaceobject.cs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,43 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
7171
return IntPtr.Zero;
7272
}
7373

74-
return CLRObject.GetInstHandle(obj, self.pyHandle);
74+
return self.WrapObject(obj);
75+
}
76+
77+
/// <summary>
78+
/// Wrap the given object in an interface object, so that only methods
79+
/// of the interface are available.
80+
/// </summary>
81+
public IntPtr WrapObject(object impl)
82+
{
83+
var objPtr = CLRObject.GetInstHandle(impl, pyHandle);
84+
return objPtr;
85+
}
86+
87+
/// <summary>
88+
/// Expose the wrapped implementation through attributes in both
89+
/// converted/encoded (__implementation__) and raw (__raw_implementation__) form.
90+
/// </summary>
91+
public static IntPtr tp_getattro(IntPtr ob, IntPtr key)
92+
{
93+
var clrObj = (CLRObject)GetManagedObject(ob);
94+
95+
if (!Runtime.PyString_Check(key))
96+
{
97+
return Exceptions.RaiseTypeError("string expected");
98+
}
99+
100+
string name = Runtime.GetManagedString(key);
101+
if (name == "__implementation__")
102+
{
103+
return Converter.ToPython(clrObj.inst);
104+
}
105+
else if (name == "__raw_implementation__")
106+
{
107+
return CLRObject.GetInstHandle(clrObj.inst);
108+
}
109+
110+
return Runtime.PyObject_GenericGetAttr(ob, key);
75111
}
76112
}
77113
}

src/runtime/managedtype.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,25 @@ internal static ManagedType GetManagedObject(IntPtr ob)
4545
return null;
4646
}
4747

48+
/// <summary>
49+
/// Given a Python object, return the associated managed object type or null.
50+
/// </summary>
51+
internal static ManagedType GetManagedObjectType(IntPtr ob)
52+
{
53+
if (ob != IntPtr.Zero)
54+
{
55+
IntPtr tp = Runtime.PyObject_TYPE(ob);
56+
var flags = Util.ReadCLong(tp, TypeOffset.tp_flags);
57+
10000 if ((flags & TypeFlags.Managed) != 0)
58+
{
59+
tp = Marshal.ReadIntPtr(tp, TypeOffset.magic());
60+
var gc = (GCHandle)tp;
61+
return (ManagedType)gc.Target;
62+
}
63+
}
64+
return null;
65+
}
66+
4867

4968
internal static ManagedType GetManagedObjectErr(IntPtr ob)
5069
{

src/runtime/methodbinder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] pa
580580
var match = false;
581581
paramsArray = parameters.Length > 0 ? Attribute.IsDefined(parameters[parameters.Length - 1], typeof(ParamArrayAttribute)) : false;
582582

583-
if (positionalArgumentCount == parameters.Length)
583+
if (positionalArgumentCount == parameters.Length && kwargDict.Count == 0)
584584
{
585585
match = true;
586586
}
@@ -744,7 +744,7 @@ internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, MethodBase i
744744
Type pt = pi[i].ParameterType;
745745
if (pi[i].IsOut || pt.IsByRef)
746746
{
747-
v = Converter.ToPython(binding.args[i], pt);
747+
v = Converter.ToPython(binding.args[i], pt.GetElementType());
748748
Runtime.PyTuple_SetItem(t, n, v);
749749
n++;
750750
}

src/runtime/pyansistring.cs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ public PyAnsiString(IntPtr ptr) : base(ptr)
1717
}
1818

1919

20+
private static IntPtr FromObject(PyObject o)
21+
{
22+
if (o == null || !IsStringType(o))
23+
{
24+
throw new ArgumentException("object is not a string");
25+
}
26+
Runtime.XIncref(o.obj);
27+
return o.obj;
28+
}
29+
2030
/// <summary>
2131
/// PyString Constructor
2232
/// </summary>
@@ -25,14 +35,14 @@ public PyAnsiString(IntPtr ptr) : base(ptr)
2535
/// An ArgumentException will be thrown if the given object is not
2636
/// a Python string object.
2737
/// </remarks>
28-
public PyAnsiString(PyObject o)
38+
public PyAnsiString(PyObject o) : base(FromObject(o))
2939
{
30-
if (!IsStringType(o))
31-
{
32-
throw new ArgumentException("object is not a string");
33-
}
34-
Runtime.XIncref(o.obj);
35-
obj = o.obj;
40+
}
41+
private static IntPtr FromString(string s)
42+
{
43+
IntPtr val = Runtime.PyString_FromString(s);
44+
PythonException.ThrowIfIsNull(val);
45+
return val;
3646
}
3747

3848

@@ -42,10 +52,8 @@ public PyAnsiString(PyObject o)
4252
/// <remarks>
4353
/// Creates a Python string from a managed string.
4454
/// </remarks>
45-
public PyAnsiString(string s)
55+
public PyAnsiString(string s) : base(FromString(s))
4656
{
47-
obj = Runtime.PyString_FromString(s);
48-
PythonException.ThrowIfIsNull(obj);
4957
}
5058

5159

src/runtime/pydict.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@ public PyDict(IntPtr ptr) : base(ptr)
2929
/// <remarks>
3030
/// Creates a new Python dictionary object.
3131
/// </remarks>
32-
public PyDict()
32+
public PyDict() : base(Runtime.PyDict_New())
3333
{
34-
obj = Runtime.PyDict_New();
3534
if (obj == IntPtr.Zero)
3635
{
3736
throw new PythonException();
@@ -47,14 +46,13 @@ public PyDict()
4746
/// ArgumentException will be thrown if the given object is not a
4847
/// Python dictionary object.
4948
/// </remarks>
50-
public PyDict(PyObject o)
49+
public PyDict(PyObject o) : base(o.obj)
5150
{
51+
Runtime.XIncref(o.obj);
5252
if (!IsDictType(o))
5353
{
5454
throw new ArgumentException("object is not a dict");
5555
}
56-
Runtime.XIncref(o.obj);
57-
obj = o.obj;
5856
}
5957

6058

src/runtime/pyfloat.cs

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ namespace Python.Runtime
44
{
55
/// <summary>
66
/// Represents a Python float object. See the documentation at
7-
/// PY2: https://docs.python.org/2/c-api/float.html
87
/// PY3: https://docs.python.org/3/c-api/float.html
98
/// for details.
109
/// </summary>
@@ -31,14 +30,8 @@ public PyFloat(IntPtr ptr) : base(ptr)
3130
/// ArgumentException will be thrown if the given object is not a
3231
/// Python float object.
3332
/// </remarks>
34-
public PyFloat(PyObject o)
33+
public PyFloat(PyObject o) : base(FromObject(o))
3534
{
36-
if (!IsFloatType(o))
37-
{
38-
throw new ArgumentException("object is not a float");
39-
}
40-
Runtime.XIncref(o.obj);
41-
obj = o.obj;
4235
}
4336

4437

@@ -48,26 +41,45 @@ public PyFloat(PyObject o)
4841
/// <remarks>
4942
/// Creates a new Python float from a double value.
5043
/// </remarks>
51-
public PyFloat(double value)
44+
public PyFloat(double value) : base(FromDouble(value))
45+
{
46+
}
47+
48+
private static IntPtr FromObject(PyObject o)
49+
{
50+
if (o == null || !IsFloatType(o))
51+
{
52+
throw new ArgumentException("object is not a float");
53+
}
54+
Runtime.XIncref(o.obj);
55+
return o.obj;
56+
}
57+
58+
private static IntPtr FromDouble(double value)
5259
{
53-
obj = Runtime.PyFloat_FromDouble(value);
54-
PythonException.ThrowIfIsNull(obj);
60+
IntPtr val = Runtime.PyFloat_FromDouble(value);
61+
PythonException.ThrowIfIsNull(val);
62+
return val;
5563
}
5664

65+
private static IntPtr FromString(string value)
66+
{
67+
using (var s = new PyString(value))
68+
{
69+
IntPtr val = Runtime.PyFloat_FromString(s.obj, IntPtr.Zero);
70+
PythonException.ThrowIfIsNull(val);
71+
return val;
72+
}
73+
}
5774

5875
/// <summary>
5976
/// PyFloat Constructor
6077
/// </summary>
6178
/// <remarks>
6279
/// Creates a new Python float from a string value.
6380
/// </remarks>
64-
public PyFloat(string value)
81+
public PyFloat(string value) : base(FromString(value))
6582
{
66-
using (var s = new PyString(value))
67-
{
68-
obj = Runtime.PyFloat_FromString(s.obj, IntPtr.Zero);
69-
PythonException.ThrowIfIsNull(obj);
70-
}
7183
}
7284

7385

0 commit comments

Comments
 (0)
0