FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
pythonnet/src/runtime/MethodSignatureFormatter.cs at master · QuantConnect/pythonnet · GitHub
Uh oh!
There was an error while loading.
Please reload this page
.
QuantConnect
/
pythonnet
Public
forked from
pythonnet/pythonnet
Notifications
You must be signed in to change notification settings
Fork
31
Star
29
Code
Issues
5
Pull requests
0
Actions
Projects
Wiki
Security and quality
0
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security and quality
Insights
Expand file tree
Breadcrumbs
pythonnet
/
src
/
runtime
/
MethodSignatureFormatter.cs
Copy path
More file actions
More file actions
Latest commit
History
History
History
297 lines (277 loc) · 11.5 KB
Breadcrumbs
pythonnet
/
src
/
runtime
/
MethodSignatureFormatter.cs
Copy path
File metadata and controls
297 lines (277 loc) · 11.5 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
using
System
;
using
System
.
Collections
.
Generic
;
using
System
.
Linq
;
using
System
.
Reflection
;
using
System
.
Text
;
namespace
Python
.
Runtime
{
/// <summary>
/// Formats method and constructor signatures the way Python callers see them
/// (snake_case names, Python type names). Used to hint the available overloads
/// in error messages when a call cannot be matched to any of them.
/// </summary>
public
static
class
MethodSignatureFormatter
{
/// <summary>
/// Formats the signatures of the candidate overloads as an error message hint,
/// so the caller can see what the method expects, e.g.
/// "The following overloads are available:" followed by one signature per line.
/// Overloads taking PyObject parameters are skipped: they accept any Python
/// object and carry no type information (they are typically the overloads that
/// just rejected the value). If every candidate takes a PyObject, they are shown
/// anyway rather than producing no hint at all.
/// Returns an empty string if there are no signatures to show.
/// </summary>
/// <param name="methods">The candidate overloads</param>
/// <param name="maxShown">The maximum number of signatures to include</param>
/// <param name="displayName">Optional name to display for the methods, e.g. the type
/// name for constructors instead of the special <c>.ctor</c> token</param>
public
static
string
FormatOverloads
(
IEnumerable
<
MethodBase
>
methods
,
int
maxShown
=
10
,
string
displayName
=
null
)
{
if
(
methods
==
null
)
{
return
string
.
Empty
;
}
// Building this only runs on error paths; never let it throw and mask
// the original failure.
try
{
var
candidates
=
methods
.
Where
(
method
=>
method
!=
null
)
.
ToList
(
)
;
var
withoutPyObject
=
candidates
.
Where
(
method
=>
!
TakesPyObject
(
method
)
)
.
ToList
(
)
;
if
(
withoutPyObject
.
Count
>
0
)
{
candidates
=
withoutPyObject
;
}
// Distinct signatures, preserving order. Snake-cased duplicates and
// repeated overloads collapse into a single entry.
var
signatures
=
new
List
<
string
>
(
)
;
var
seen
=
new
HashSet
<
string
>
(
)
;
foreach
(
var
method
in
candidates
)
{
var
signature
=
FormatSignature
(
method
,
displayName
)
;
if
(
seen
.
Add
(
signature
)
)
{
signatures
.
Add
(
signature
)
;
}
}
if
(
signatures
.
Count
==
0
)
{
return
string
.
Empty
;
}
var
to
=
new
StringBuilder
(
signatures
.
Count
==
1
?
"The expected signature is:"
:
"The following overloads are available:"
)
;
for
(
var
i
=
0
;
i
<
signatures
.
Count
&&
i
<
maxShown
;
i
++
)
{
to
.
Append
(
"
\n
"
)
.
Append
(
signatures
[
i
]
)
;
}
if
(
signatures
.
Count
>
maxShown
)
{
to
.
Append
(
$
"
\n
... and
{
signatures
.
Count
-
maxShown
}
more"
)
;
}
return
to
.
ToString
(
)
;
}
catch
{
// Best-effort hint only.
return
string
.
Empty
;
}
}
/// <summary>
/// Formats a method/constructor as a Python signature: snake_case name and
/// parameters annotated with the Python types a Python caller uses, e.g.
/// <c>range_consolidator(range: int, selector: Callable[[IBaseData], float] = None)</c>.
/// The constructor's special <c>.ctor</c> token is left as-is unless
/// <paramref name="displayName"/> is provided.
/// </summary>
public
static
string
FormatSignature
(
MethodBase
method
,
string
displayName
=
null
)
{
var
to
=
new
StringBuilder
(
)
;
to
.
Append
(
displayName
??
SnakeCaseName
(
method
)
)
.
Append
(
'('
)
;
var
parameters
=
method
.
GetParameters
(
)
;
for
(
var
i
=
0
;
i
<
parameters
.
Length
;
i
++
)
{
if
(
i
>
0
)
{
to
.
Append
(
", "
)
;
}
var
parameter
=
parameters
[
i
]
;
if
(
parameter
.
IsDefined
(
typeof
(
ParamArrayAttribute
)
,
false
)
)
{
// Python variadic form; annotate with the element type
var
elementType
=
parameter
.
ParameterType
.
IsArray
?
parameter
.
ParameterType
.
GetElementType
(
)
:
parameter
.
ParameterType
;
to
.
Append
(
'*'
)
.
Append
(
parameter
.
Name
.
ToSnakeCase
(
)
)
.
Append
(
": "
)
.
Append
(
FormatType
(
elementType
)
)
;
continue
;
}
to
.
Append
(
parameter
.
Name
.
ToSnakeCase
(
)
)
.
Append
(
": "
)
.
Append
(
FormatType
(
parameter
.
ParameterType
)
)
;
if
(
parameter
.
IsOptional
)
{
to
.
Append
(
" = "
)
.
Append
(
FormatDefaultValue
(
parameter
.
DefaultValue
)
)
;
}
}
to
.
Append
(
')'
)
;
return
to
.
ToString
(
)
;
}
/// <summary>
/// The snake_case name a Python caller uses for the given method. Constructors
/// keep their special <c>.ctor</c> token (a Python caller invokes the type).
/// </summary>
internal
static
string
SnakeCaseName
(
MethodBase
method
)
{
return
method
.
IsConstructor
?
method
.
Name
:
method
.
Name
.
ToSnakeCase
(
)
;
}
/// <summary>
/// Determines whether any of the method's parameters is a PyObject
/// </summary>
private
static
bool
TakesPyObject
(
MethodBase
method
)
{
return
method
.
GetParameters
(
)
.
Any
(
parameter
=>
{
var
type
=
parameter
.
ParameterType
;
if
(
type
.
IsByRef
)
{
type
=
type
.
GetElementType
(
)
;
}
return
typeof
(
PyObject
)
.
IsAssignableFrom
(
type
)
;
}
)
;
}
/// <summary>
/// Produces the Python-side name for a CLR type, following the conversions the
/// runtime performs on arguments: primitives map to their Python equivalents
/// (str, int, float, bool, datetime, timedelta), Nullable to Optional, delegates
/// to Callable, list/dictionary shapes to List/Dict and PyObject/object to Any.
/// CLR types without a Python equivalent keep their name, with generics rendered
/// as <c>Name[Arg1, Arg2]</c>.
/// </summary>
internal
static
string
FormatType
(
Type
type
)
{
if
(
type
.
IsByRef
)
{
type
=
type
.
GetElementType
(
)
;
}
var
underlying
=
Nullable
.
GetUnderlyingType
(
type
)
;
if
(
underlying
!=
null
)
{
return
$
"Optional[
{
FormatType
(
underlying
)
}
]"
;
}
if
(
type
==
typeof
(
void
)
)
{
return
"None"
;
}
if
(
type
==
typeof
(
TimeSpan
)
)
{
return
"timedelta"
;
}
if
(
type
==
typeof
(
object
)
)
{
return
"Any"
;
}
if
(
typeof
(
Type
)
.
IsAssignableFrom
(
type
)
)
{
return
"type"
;
}
// pythonnet wrapper parameters accept any Python object of the matching shape
if
(
type
==
typeof
(
PyList
)
)
{
return
"List[Any]"
;
}
if
(
type
==
typeof
(
PyDict
)
)
{
return
"Dict[Any, Any]"
;
}
if
(
typeof
(
PyObject
)
.
IsAssignableFrom
(
type
)
)
{
return
"Any"
;
}
if
(
type
.
IsArray
)
{
return
$
"List[
{
FormatType
(
type
.
GetElementType
(
)
)
}
]"
;
}
if
(
typeof
(
Delegate
)
.
IsAssignableFrom
(
type
)
&&
!
type
.
ContainsGenericParameters
)
{
var
invoke
=
type
.
GetMethod
(
"Invoke"
)
;
if
(
invoke
!=
null
)
{
var
args
=
string
.
Join
(
", "
,
invoke
.
GetParameters
(
)
.
Select
(
p
=>
FormatType
(
p
.
ParameterType
)
)
)
;
return
$
"Callable[[
{
args
}
],
{
FormatType
(
invoke
.
ReturnType
)
}
]"
;
}
}
// Enums have an integer type code but keep their Python-visible name
if
(
!
type
.
IsEnum
)
{
switch
(
Type
.
GetTypeCode
(
type
)
)
{
case
TypeCode
.
Boolean
:
return
"bool"
;
case
TypeCode
.
Char
:
case
TypeCode
.
String
:
return
"str"
;
case
TypeCode
.
SByte
:
case
TypeCode
.
Byte
:
case
TypeCode
.
Int16
:
case
TypeCode
.
UInt16
:
case
TypeCode
.
Int32
:
case
TypeCode
.
UInt32
:
case
TypeCode
.
Int64
:
case
TypeCode
.
UInt64
:
return
"int"
;
case
TypeCode
.
Single
:
case
TypeCode
.
Double
:
case
TypeCode
.
Decimal
:
return
"float"
;
case
TypeCode
.
DateTime
:
return
"datetime"
;
}
}
if
(
type
.
IsGenericType
)
{
var
definition
=
type
.
GetGenericTypeDefinition
(
)
;
var
genericArguments
=
type
.
GetGenericArguments
(
)
;
// list and dictionary shapes the runtime converts from Python lists/dicts
if
(
definition
==
typeof
(
List
<
>
)
||
definition
==
typeof
(
IList
<
>
)
||
definition
==
typeof
(
IEnumerable
<
>
)
||
definition
==
typeof
(
ICollection
<
>
)
||
definition
==
typeof
(
IReadOnlyList
<
>
)
||
definition
==
typeof
(
IReadOnlyCollection
<
>
)
)
{
return
$
"List[
{
FormatType
(
genericArguments
[
0
]
)
}
]"
;
}
if
(
definition
==
typeof
(
Dictionary
<
,
>
)
||
definition
==
typeof
(
IDictionary
<
,
>
)
||
definition
==
typeof
(
IReadOnlyDictionary
<
,
>
)
||
definition
==
typeof
(
KeyValuePair
<
,
>
)
)
{
return
$
"Dict[
{
FormatType
(
genericArguments
[
0
]
)
}
,
{
FormatType
(
genericArguments
[
1
]
)
}
]"
;
}
var
name
=
type
.
Name
;
var
tick
=
name
.
IndexOf
(
'`'
)
;
if
(
tick
>=
0
)
{
name
=
name
.
Substring
(
0
,
tick
)
;
}
var
args
=
genericArguments
.
Select
(
FormatType
)
;
return
$
"
{
name
}
[
{
string
.
Join
(
", "
,
args
)
}
]"
;
}
return
type
.
Name
;
}
private
static
string
FormatDefaultValue
(
object
value
)
{
if
(
value
==
null
||
value
is
DBNull
)
{
return
"None"
;
}
if
(
value
is
string
s
)
{
return
$
"
\"
{
s
}
\"
"
;
}
if
(
value
is
bool
b
)
{
return
b
?
"True"
:
"False"
;
}
if
(
value
is
Enum
e
)
{
// Render enum defaults the way Python callers access them, e.g. Resolution.DAILY
return
$
"
{
e
.
GetType
(
)
.
Name
}
.
{
e
.
ToString
(
)
.
ToSnakeCase
(
constant
:
true
)
}
"
;
}
return
value
.
ToString
(
)
;
}
}
}
Back
|
FazBrowse Home
|
New Git URL