Skip to content

Enable user classes to override how Python.NET processes parameters of their functions #835

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
Closed
Next Next commit
introduced IPyArgumentConverter interface, that controls marshaling o…
…f Python objects when calling .NET methods
  • Loading branch information
lostmsu committed Jan 31, 2020
commit c355bd6995487eb5d0c993821f97a02207290a4c
11 changes: 7 additions & 4 deletions src/runtime/methodbinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal class MethodBinder
public MethodBase[] methods;
public bool init = false;
public bool allow_threads = true;
readonly IPyArgumentConverter pyArgumentConverter = DefaultPyArgumentConverter.Instance;

internal MethodBinder()
{
Expand Down Expand Up @@ -326,7 +327,7 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
continue;
}
var outs = 0;
var margs = TryConvertArguments(pi, paramsArray, args, pynargs, kwargDict, defaultArgList,
var margs = this.TryConvertArguments(pi, paramsArray, args, pynargs, kwargDict, defaultArgList,
needsResolution: _methods.Length > 1,
outs: out outs);

Expand Down Expand Up @@ -382,7 +383,7 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
/// <param name="needsResolution"><c>true</c>, if overloading resolution is required</param>
/// <param name="outs">Returns number of output parameters</param>
/// <returns>An array of .NET arguments, that can be passed to a method.</returns>
static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
IntPtr args, int pyArgCount,
Dictionary<string, IntPtr> kwargDict,
ArrayList defaultArgList,
Expand Down Expand Up @@ -423,7 +424,9 @@ static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
}

bool isOut;
if (!TryConvertArgument(op, parameter.ParameterType, needsResolution, out margs[paramIndex], out isOut))
if (!this.pyArgumentConverter.TryConvertArgument(
op, parameter.ParameterType, needsResolution,
out margs[paramIndex], out isOut))
{
return null;
}
Expand All @@ -445,7 +448,7 @@ static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
return margs;
}

static bool TryConvertArgument(IntPtr op, Type parameterType, bool needsResolution,
internal static bool TryConvertArgument(IntPtr op, Type parameterType, bool needsResolution,
out object arg, out bool isOut)
{
arg = null;
Expand Down
47 changes: 47 additions & 0 deletions src/runtime/pyargconverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace Python.Runtime {
using System;

/// <summary>
/// Specifies how to convert Python objects, passed to .NET functions to the expected CLR types.
/// </summary>
public interface IPyArgumentConverter
{
/// <summary>
/// Attempts to convert an argument passed by Python to the specified parameter type.
/// </summary>
/// <param name="pyarg">Unmanaged pointer to the Python argument value</param>
/// <param name="parameterType">The expected type of the parameter</param>
/// <param name="needsResolution"><c>true</c> if the method is overloaded</param>
/// <param name="arg">This parameter will receive the converted value, matching the specified type</param>
/// <param name="isOut">This parameter will be set to <c>true</c>,
/// if the final type needs to be marshaled as an out argument.</param>
/// <returns><c>true</c>, if the object matches requested type,
/// and conversion was successful, otherwise <c>false</c></returns>
bool TryConvertArgument(IntPtr pyarg, Type parameterType,
bool needsResolution, out object arg, out bool isOut);
}

public class DefaultPyArgumentConverter: IPyArgumentConverter {
public static DefaultPyArgumentConverter Instance { get; }= new DefaultPyArgumentConverter();

/// <summary>
/// Attempts to convert an argument passed by Python to the specified parameter type.
/// </summary>
/// <param name="pyarg">Unmanaged pointer to the Python argument value</param>
/// <param name="parameterType">The expected type of the parameter</param>
/// <param name="needsResolution"><c>true</c> if the method is overloaded</param>
/// <param name="arg">This parameter will receive the converted value, matching the specified type</param>
/// <param name="isOut">This parameter will be set to <c>true</c>,
/// if the final type needs to be marshaled as an out argument.</param>
/// <returns><c>true</c>, if the object matches requested type,
/// and conversion was successful, otherwise <c>false</c></returns>
public virtual bool TryConvertArgument(
IntPtr pyarg, Type parameterType, bool needsResolution,
out object arg, out bool isOut)
{
return MethodBinder.TryConvertArgument(
pyarg, parameterType, needsResolution,
out arg, out isOut);
}
}
}