Skip to content

Commit 6a0c1aa

Browse files
committed
implemented __signature__ on methodbinding
1 parent 81998d7 commit 6a0c1aa

File tree

3 files changed

+51
-1
lines changed

3 files changed

+51
-1
lines changed

src/embed_tests/Inspect.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ public void BoundMethodsAreInspectable() {
2727
}
2828

2929
class Class {
30-
public void Method() { }
30+
public void Method(int a, int b = 10) { }
31+
public void Method(int a, object b) { }
3132
}
3233
}
3334
}

src/runtime/methodbinding.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Linq;
34
using System.Reflection;
45

56
namespace Python.Runtime
@@ -59,6 +60,47 @@ public static IntPtr mp_subscript(IntPtr tp, IntPtr idx)
5960
return mb.pyHandle;
6061
}
6162

63+
PyObject Singature {
64+
get {
65+
var infos = this.info == null ? this.m.info : new[] {this.info};
66+
// this is a primitive version
67+
// the overload with the maximum number of parameters should be used
68+
var primary = infos.First();
69+
var primaryParameters = primary.GetParameters();
70+
PyObject signatureClass = Runtime.InspectModule.GetAttr("Signature");
71+
var primaryReturn = primary.ReturnParameter;
72+
if (infos.Any(i => i.GetParameters().Length != primaryParameters.Length
73+
|| i.ReturnParameter?.ParameterType != primaryReturn?.ParameterType)) {
74+
return signatureClass.Invoke();
75+
}
76+
77+
var parameters = new PyList();
78+
var parameterClass = primaryParameters.Length > 0 ? Runtime.InspectModule.GetAttr("Parameter") : null;
79+
var positionalOrKeyword = primaryParameters.Length > 0 ? parameterClass.GetAttr("POSITIONAL_OR_KEYWORD") : null;
80+
for (int i = 0; i < primaryParameters.Length; i++) {
81+
var parameter = primaryParameters[i];
82+
var alternatives = infos.Select(info => info.GetParameters()[i]);
83+
var defaultValue = alternatives
84+
.Select(alternative => alternative.DefaultValue != DBNull.Value ? alternative.DefaultValue.ToPython() : null)
85+
.FirstOrDefault(v => v != null) ?? parameterClass.GetAttr("empty");
86+
87+
if (alternatives.Any(alternative => alternative.Name != parameter.Name)) {
88+
return signatureClass.Invoke();
89+
}
90+
91+
var args = new PyTuple(new []{ parameter.Name.ToPython(), positionalOrKeyword});
92+
var kw = new PyDict();
93+
if (defaultValue != null) {
94+
kw["default"] = defaultValue;
95+
}
96+
var parameterInfo = parameterClass.Invoke(args: args, kw: kw);
97+
parameters.Append(parameterInfo);
98+
}
99+
100+
// TODO: add return annotation
101+
return signatureClass.Invoke(parameters);
102+
}
103+
}
62104

63105
/// <summary>
64106
/// MethodBinding __getattribute__ implementation.
@@ -85,6 +127,9 @@ public static IntPtr tp_getattro(IntPtr ob, IntPtr key)
85127
case "Overloads":
86128
var om = new OverloadMapper(self.m, self.target);
87129
return om.pyHandle;
130+
case "__signature__":
131+
var sig = self.Singature;
132+
return sig.Handle;
88133
default:
89134
return Runtime.PyObject_GenericGetAttr(ob, key);
90135
}

src/runtime/runtime.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,10 @@ public static PyObject None
509509
}
510510
}
511511

512+
private static readonly Lazy<PyObject> inspect =
513+
new Lazy<PyObject>(() => PythonEngine.ImportModule("inspect"), isThreadSafe: false);
514+
public static PyObject InspectModule => inspect.Value;
515+
512516
/// <summary>
513517
/// Check if any Python Exceptions occurred.
514518
/// If any exist throw new PythonException.

0 commit comments

Comments
 (0)