Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9c81661

Browse files
committed
moved default arg converter to its own file; cached converter lookup
1 parent 11a3ee6 commit 9c81661

File tree

3 files changed

+131
-131
lines changed

3 files changed

+131
-131
lines changed

src/runtime/defaultpyargconverter.cs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
namespace Python.Runtime {
2+
using System;
3+
4+
/// <summary>
5+
/// The implementation of <see cref="T:Python.Runtime.IPyArgumentConverter" /> used by default
6+
/// </summary>
7+
public class DefaultPyArgumentConverter: IPyArgumentConverter
8+
{
9+
/// <summary>
10+
/// Gets the singleton instance.
11+
/// </summary>
12+
public static DefaultPyArgumentConverter Instance { get; } = new DefaultPyArgumentConverter();
13+
14+
/// <inheritdoc />
15+
/// <summary>
16+
/// Attempts to convert an argument passed by Python to the specified parameter type.
17+
/// </summary>
18+
/// <param name="pyarg">Unmanaged pointer to the Python argument value</param>
19+
/// <param name="parameterType">The expected type of the parameter</param>
20+
/// <param name="needsResolution"><c>true</c> if the method is overloaded</param>
21+
/// <param name="arg">This parameter will receive the converted value, matching the specified type</param>
22+
/// <param name="isOut">This parameter will be set to <c>true</c>,
23+
/// if the final type needs to be marshaled as an out argument.</param>
24+
/// <returns><c>true</c>, if the object matches requested type,
25+
/// and conversion was successful, otherwise <c>false</c></returns>
26+
public virtual bool TryConvertArgument(
27+
IntPtr pyarg, Type parameterType, bool needsResolution,
28+
out object arg, out bool isOut)
29+
{
30+
arg = null;
31+
isOut = false;
32+
Type clrType = TryComputeClrArgumentType(parameterType, pyarg, needsResolution: needsResolution);
33+
if (clrType == null)
34+
{
35+
return false;
36+
}
37+
38+
if (!Converter.ToManaged(pyarg, clrType, out arg, false))
39+
{
40+
Exceptions.Clear();
41+
return false;
42+
}
43+
44+
isOut = clrType.IsByRef;
45+
return true;
46+
}
47+
48+
static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool needsResolution)
49+
{
50+
// this logic below handles cases when multiple overloading methods
51+
// are ambiguous, hence comparison between Python and CLR types
52+
// is necessary
53+
Type clrType = null;
54+
IntPtr pyArgType;
55+
if (needsResolution)
56+
{
57+
// HACK: each overload should be weighted in some way instead
58+
pyArgType = Runtime.PyObject_Type(argument);
59+
Exceptions.Clear();
60+
if (pyArgType != IntPtr.Zero)
61+
{
62+
clrType = Converter.GetTypeByAlias(pyArgType);
63+
}
64+
Runtime.XDecref(pyArgType);
65+
}
66+
67+
if (clrType != null)
68+
{
69+
if ((parameterType != typeof(object)) && (parameterType != clrType))
70+
{
71+
IntPtr pyParamType = Converter.GetPythonTypeByAlias(parameterType);
72+
pyArgType = Runtime.PyObject_Type(argument);
73+
Exceptions.Clear();
74+
75+
bool typeMatch = false;
76+
if (pyArgType != IntPtr.Zero && pyParamType == pyArgType)
77+
{
78+
typeMatch = true;
79+
clrType = parameterType;
80+
}
81+
if (!typeMatch)
82+
{
83+
// this takes care of enum values
84+
TypeCode argTypeCode = Type.GetTypeCode(parameterType);
85+
TypeCode paramTypeCode = Type.GetTypeCode(clrType);
86+
if (argTypeCode == paramTypeCode)
87+
{
88+
typeMatch = true;
89+
clrType = parameterType;
90+
}
91+
}
92+
Runtime.XDecref(pyArgType);
93+
if (!typeMatch)
94+
{
95+
return null;
96+
}
97+
}
98+
else
99+
{
100+
clrType = parameterType;
101+
}
102+
}
103+
else
104+
{
105+
clrType = parameterType;
106+
}
107+
108+
return clrType;
109+
}
110+
}
111+
}

src/runtime/methodbinder.cs

Lines changed: 20 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
using System;
22
using System.Collections;
3+
using System.Collections.Concurrent;
34
using System.Diagnostics;
5+
using System.Linq;
46
using System.Reflection;
57
using System.Text;
68
using System.Collections.Generic;
79
using System.Linq;
810

911
namespace Python.Runtime
1012
{
11-
using System.Linq;
12-
1313
/// <summary>
1414
/// A MethodBinder encapsulates information about a (possibly overloaded)
1515
/// managed method, and is responsible for selecting the right method given
@@ -180,14 +180,7 @@ IPyArgumentConverter GetArgumentConverter() {
180180
Type converterType = null;
181181
foreach (MethodBase method in this.methods)
182182
{
183-
var attribute = method.DeclaringType?
184-
.GetCustomAttributes(typeof(PyArgConverterAttribute), inherit: false)
185-
.OfType<PyArgConverterAttribute>()
186-
.SingleOrDefault()
187-
?? method.DeclaringType?.Assembly
188-
.GetCustomAttributes(typeof(PyArgConverterAttribute), inherit: false)
189-
.OfType<PyArgConverterAttribute>()
190-
.SingleOrDefault();
183+
PyArgConverterAttribute attribute = TryGetArgConverter(method.DeclaringType);
191184
if (converterType == null)
192185
{
193186
if (attribute == null) continue;
@@ -203,6 +196,23 @@ IPyArgumentConverter GetArgumentConverter() {
203196
return converter ?? DefaultPyArgumentConverter.Instance;
204197
}
205198

199+
static readonly ConcurrentDictionary<Type, PyArgConverterAttribute> ArgConverterCache =
200+
new ConcurrentDictionary<Type, PyArgConverterAttribute>();
201+
static PyArgConverterAttribute TryGetArgConverter(Type type) {
202+
if (type == null) return null;
203+
204+
return ArgConverterCache.GetOrAdd(type, declaringType =>
205+
declaringType
206+
.GetCustomAttributes(typeof(PyArgConverterAttribute), inherit: false)
207+
.OfType<PyArgConverterAttribute>()
208+
.SingleOrDefault()
209+
?? declaringType.Assembly
210+
.GetCustomAttributes(typeof(PyArgConverterAttribute), inherit: false)
211+
.OfType<PyArgConverterAttribute>()
212+
.SingleOrDefault()
213+
);
214+
}
215+
206216
/// <summary>
207217
/// Precedence algorithm largely lifted from Jython - the concerns are
208218
/// generally the same so we'll start with this and tweak as necessary.
@@ -481,97 +491,6 @@ object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
481491
return margs;
482492
}
483493

484-
internal static bool TryConvertArgument(IntPtr op, Type parameterType, bool needsResolution,
485-
out object arg, out bool isOut)
486-
{
487-
arg = null;
488-
isOut = false;
489-
var clrtype = TryComputeClrArgumentType(parameterType, op, needsResolution: needsResolution);
490-
if (clrtype == null)
491-
{
492-
return false;
493-
}
494-
495-
if (!Converter.ToManaged(op, clrtype, out arg, false))
496-
{
497-
Exceptions.Clear();
498-
return false;
499-
}
500-
501-
isOut = clrtype.IsByRef;
502-
return true;
503-
}
504-
505-
static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool needsResolution)
506-
{
507-
// this logic below handles cases when multiple overloading methods
508-
// are ambiguous, hence comparison between Python and CLR types
509-
// is necessary
510-
Type clrtype = null;
511-
IntPtr pyoptype;
512-
if (needsResolution)
513-
{
514-
// HACK: each overload should be weighted in some way instead
515-
pyoptype = Runtime.PyObject_Type(argument);
516-
Exceptions.Clear();
517-
if (pyoptype != IntPtr.Zero)
518-
{
519-
clrtype = Converter.GetTypeByAlias(pyoptype);
520-
}
521-
Runtime.XDecref(pyoptype);
522-
}
523-
524-
if (clrtype != null)
525-
{
526-
var typematch = false;
527-
if ((parameterType != typeof(object)) && (parameterType != clrtype))
528-
{
529-
IntPtr pytype = Converter.GetPythonTypeByAlias(parameterType);
530-
pyoptype = Runtime.PyObject_Type(argument);
531-
Exceptions.Clear();
532-
if (pyoptype != IntPtr.Zero)
533-
{
534-
if (pytype != pyoptype)
535-
{
536-
typematch = false;
537-
}
538-
else
539-
{
540-
typematch = true;
541-
clrtype = parameterType;
542-
}
543-
}
544-
if (!typematch)
545-
{
546-
// this takes care of enum values
547-
TypeCode argtypecode = Type.GetTypeCode(parameterType);
548-
TypeCode paramtypecode = Type.GetTypeCode(clrtype);
549-
if (argtypecode == paramtypecode)
550-
{
551-
typematch = true;
552-
clrtype = parameterType;
553-
}
554-
}
555-
Runtime.XDecref(pyoptype);
556-
if (!typematch)
557-
{
558-
return null;
559-
}
560-
}
561-
else
562-
{
563-
typematch = true;
564-
clrtype = parameterType;
565-
}
566-
}
567-
else
568-
{
569-
clrtype = parameterType;
570-
}
571-
572-
return clrtype;
573-
}
574-
575494
static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] parameters,
576495
Dictionary<string, IntPtr> kwargDict,
577496
out bool paramsArray,

src/runtime/pyargconverter.cs

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,36 +21,6 @@ bool TryConvertArgument(IntPtr pyarg, Type parameterType,
2121
bool needsResolution, out object arg, out bool isOut);
2222
}
2323

24-
/// <summary>
25-
/// The implementation of <see cref="IPyArgumentConverter"/> used by default
26-
/// </summary>
27-
public class DefaultPyArgumentConverter: IPyArgumentConverter {
28-
/// <summary>
29-
/// Gets the singleton instance.
30-
/// </summary>
31-
public static DefaultPyArgumentConverter Instance { get; } = new DefaultPyArgumentConverter();
32-
33-
/// <summary>
34-
/// Attempts to convert an argument passed by Python to the specified parameter type.
35-
/// </summary>
36-
/// <param name="pyarg">Unmanaged pointer to the Python argument value</param>
37-
/// <param name="parameterType">The expected type of the parameter</param>
38-
/// <param name="needsResolution"><c>true</c> if the method is overloaded</param>
39-
/// <param name="arg">This parameter will receive the converted value, matching the specified type</param>
40-
/// <param name="isOut">This parameter will be set to <c>true</c>,
41-
/// if the final type needs to be marshaled as an out argument.</param>
42-
/// <returns><c>true</c>, if the object matches requested type,
43-
/// and conversion was successful, otherwise <c>false</c></returns>
44-
public virtual bool TryConvertArgument(
45-
IntPtr pyarg, Type parameterType, bool needsResolution,
46-
out object arg, out bool isOut)
47-
{
48-
return MethodBinder.TryConvertArgument(
49-
pyarg, parameterType, needsResolution,
50-
out arg, out isOut);
51-
}
52-
}
53-
5424
/// <summary>
5525
/// Specifies an argument converter to be used, when methods in this class/assembly are called from Python.
5626
/// </summary>

0 commit comments

Comments
 (0)