Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit92932fd

Browse files
authored
Better error messages for method argument mismatch and others (#1361)
Better error messages for method argument mismatch, Python to managed value conversion, and other problems.- Converter.ToManaged obeys setError consistently- PyObject_Hash and tp_hash return nint- MakeGenericType and MakeGenericMethod have try/catch- RaiseTypeError sets __cause__ instead of changing the message string- PythonException.Normalize throws if an error is set- AggregateExceptions print better from Python- For an overloaded method, MethodBinder.Bind collects all binding errors with PythonExceptions as the inner.inner exceptions.- ExceptionClassObject.tp_str calls Exception.ToString() instead of Exception.Message(for justification seehttps://stackoverflow.com/questions/2176707/exception-message-vs-exception-tostring )- Binding failures always have AggregateException as the cause, - Disabled test_method_parameters_change
1 parent32fdc9c commit92932fd

22 files changed

+429
-110
lines changed

‎CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ details about the cause of the failure
3131
-`PyObject` now implements`IEnumerable<PyObject>` in addition to`IEnumerable`
3232
- floating point values passed from Python are no longer silently truncated
3333
when .NET expects an integer[#1342][i1342]
34+
- More specific error messages for method argument mismatch
3435

3536
###Fixed
3637

@@ -49,6 +50,7 @@ when .NET expects an integer [#1342][i1342]
4950
- Fixed objects returned by enumerating`PyObject` being disposed too soon
5051
- Incorrectly using a non-generic type with type parameters now produces a helpful Python error instead of throwing NullReferenceException
5152
-`import` may now raise errors with more detail than "No module named X"
53+
- Providing an invalid type parameter to a generic type or method produces a helpful Python error
5254

5355
###Removed
5456

‎src/clrmodule/ClrModule.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ public static IntPtr PyInit_clr()
6262
pythonRuntime=Assembly.Load(pythonRuntimeName);
6363
DebugPrint("Success loading 'Python.Runtime' using standard binding rules.");
6464
}
65-
catch(IOException)
65+
catch(IOExceptionex)
6666
{
67-
DebugPrint("'Python.Runtime' not found using standard binding rules.");
67+
DebugPrint($"'Python.Runtime' not found using standard binding rules:{ex}");
6868
try
6969
{
7070
// If the above fails for any reason, we fallback to attempting to load "Python.Runtime.dll"

‎src/domain_tests/test_domain_reload.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ def test_property_visibility_change():
5757
deftest_class_visibility_change():
5858
_run_test("class_visibility_change")
5959

60+
@pytest.mark.skip(reason='FIXME: Domain reload fails when Python points to a .NET object which points back to Python objects')
6061
@pytest.mark.skipif(platform.system()=='Darwin',reason='FIXME: macos can\'t find the python library')
6162
deftest_method_parameters_change():
6263
_run_test("method_parameters_change")

‎src/embed_tests/TestPythonException.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ public void TestPythonExceptionFormatNormalized()
9797
try
9898
{
9999
PythonEngine.Exec("a=b\n");
100+
Assert.Fail("Exception should have been raised");
100101
}
101102
catch(PythonExceptionex)
102103
{
@@ -135,5 +136,14 @@ def __init__(self, val):
135136
}
136137
}
137138
}
139+
140+
[Test]
141+
publicvoidTestPythonException_Normalize_ThrowsWhenErrorSet()
142+
{
143+
Exceptions.SetError(Exceptions.TypeError,"Error!");
144+
varpythonException=newPythonException();
145+
Exceptions.SetError(Exceptions.TypeError,"Another error");
146+
Assert.Throws<InvalidOperationException>(()=>pythonException.Normalize());
147+
}
138148
}
139149
}

‎src/runtime/classbase.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,16 @@ public virtual IntPtr type_subscript(IntPtr idx)
6666

6767
if(target!=null)
6868
{
69-
Typet=target.MakeGenericType(types);
69+
Typet;
70+
try
71+
{
72+
// MakeGenericType can throw ArgumentException
73+
t=target.MakeGenericType(types);
74+
}
75+
catch(ArgumentExceptione)
76+
{
77+
returnExceptions.RaiseTypeError(e.Message);
78+
}
7079
ManagedTypec=ClassManager.GetClass(t);
7180
Runtime.XIncref(c.pyHandle);
7281
returnc.pyHandle;
@@ -263,14 +272,14 @@ public static IntPtr tp_iter(IntPtr ob)
263272
/// <summary>
264273
/// Standard __hash__ implementation for instances of reflected types.
265274
/// </summary>
266-
publicstaticIntPtrtp_hash(IntPtrob)
275+
publicstaticninttp_hash(IntPtrob)
267276
{
268277
varco=GetManagedObject(ob)asCLRObject;
269278
if(co==null)
270279
{
271280
returnExceptions.RaiseTypeError("unhashable type");
272281
}
273-
returnnewIntPtr(co.inst.GetHashCode());
282+
returnco.inst.GetHashCode();
274283
}
275284

276285

‎src/runtime/converter.cs

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,11 @@ internal static IntPtr ToPythonImplicit(object value)
303303
/// Return a managed object for the given Python object, taking funny
304304
/// byref types into account.
305305
/// </summary>
306+
/// <param name="value">A Python object</param>
307+
/// <param name="type">The desired managed type</param>
308+
/// <param name="result">Receives the managed object</param>
309+
/// <param name="setError">If true, call <c>Exceptions.SetError</c> with the reason for failure.</param>
310+
/// <returns>True on success</returns>
306311
internalstaticboolToManaged(IntPtrvalue,Typetype,
307312
outobjectresult,boolsetError)
308313
{
@@ -341,7 +346,10 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
341346
result=tmp;
342347
returntrue;
343348
}
344-
Exceptions.SetError(Exceptions.TypeError,$"value cannot be converted to{obType}");
349+
if(setError)
350+
{
351+
Exceptions.SetError(Exceptions.TypeError,$"value cannot be converted to{obType}");
352+
}
345353
returnfalse;
346354
}
347355
if(mtisClassBase)
@@ -376,6 +384,15 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
376384
obType=obType.GetGenericArguments()[0];
377385
}
378386

387+
if(obType.ContainsGenericParameters)
388+
{
389+
if(setError)
390+
{
391+
Exceptions.SetError(Exceptions.TypeError,$"Cannot create an instance of the open generic type{obType}");
392+
}
393+
returnfalse;
394+
}
395+
379396
if(obType.IsArray)
380397
{
381398
returnToArray(value,obType,outresult,setError);
@@ -777,7 +794,7 @@ private static void SetConversionError(IntPtr value, Type target)
777794
IntPtrob=Runtime.PyObject_Repr(value);
778795
stringsrc=Runtime.GetManagedString(ob);
779796
Runtime.XDecref(ob);
780-
Exceptions.SetError(Exceptions.TypeError,$"Cannot convert{src} to{target}");
797+
Exceptions.RaiseTypeError($"Cannot convert{src} to{target}");
781798
}
782799

783800

@@ -791,32 +808,58 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s
791808
TypeelementType=obType.GetElementType();
792809
result=null;
793810

794-
boolIsSeqObj=Runtime.PySequence_Check(value);
795-
varlen=IsSeqObj?Runtime.PySequence_Size(value):-1;
796-
797811
IntPtrIterObject=Runtime.PyObject_GetIter(value);
798-
799-
if(IterObject==IntPtr.Zero){
812+
if(IterObject==IntPtr.Zero)
813+
{
800814
if(setError)
801815
{
802816
SetConversionError(value,obType);
803817
}
818+
else
819+
{
820+
// PyObject_GetIter will have set an error
821+
Exceptions.Clear();
822+
}
804823
returnfalse;
805824
}
806825

807-
Arrayitems;
826+
IListlist;
827+
try
828+
{
829+
// MakeGenericType can throw because elementType may not be a valid generic argument even though elementType[] is a valid array type.
830+
// For example, if elementType is a pointer type.
831+
// See https://docs.microsoft.com/en-us/dotnet/api/system.type.makegenerictype#System_Type_MakeGenericType_System_Type
832+
varconstructedListType=typeof(List<>).MakeGenericType(elementType);
833+
boolIsSeqObj=Runtime.PySequence_Check(value);
834+
if(IsSeqObj)
835+
{
836+
varlen=Runtime.PySequence_Size(value);
837+
list=(IList)Activator.CreateInstance(constructedListType,newObject[]{(int)len});
838+
}
839+
else
840+
{
841+
// CreateInstance can throw even if MakeGenericType succeeded.
842+
// See https://docs.microsoft.com/en-us/dotnet/api/system.activator.createinstance#System_Activator_CreateInstance_System_Type_
843+
list=(IList)Activator.CreateInstance(constructedListType);
844+
}
845+
}
846+
catch(Exceptione)
847+
{
848+
if(setError)
849+
{
850+
Exceptions.SetError(e);
851+
SetConversionError(value,obType);
852+
}
853+
returnfalse;
854+
}
808855

809-
varlistType=typeof(List<>);
810-
varconstructedListType=listType.MakeGenericType(elementType);
811-
IListlist=IsSeqObj?(IList)Activator.CreateInstance(constructedListType,newObject[]{(int)len}):
812-
(IList)Activator.CreateInstance(constructedListType);
813856
IntPtritem;
814857

815858
while((item=Runtime.PyIter_Next(IterObject))!=IntPtr.Zero)
816859
{
817-
objectobj=null;
860+
objectobj;
818861

819-
if(!Converter.ToManaged(item,elementType,outobj,true))
862+
if(!Converter.ToManaged(item,elementType,outobj,setError))
820863
{
821864
Runtime.XDecref(item);
822865
returnfalse;
@@ -827,7 +870,7 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s
827870
}
828871
Runtime.XDecref(IterObject);
829872

830-
items=Array.CreateInstance(elementType,list.Count);
873+
Arrayitems=Array.CreateInstance(elementType,list.Count);
831874
list.CopyTo(items,0);
832875

833876
result=items;

‎src/runtime/eventbinding.cs

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -68,35 +68,27 @@ public static IntPtr nb_inplace_subtract(IntPtr ob, IntPtr arg)
6868
/// <summary>
6969
/// EventBinding __hash__ implementation.
7070
/// </summary>
71-
publicstaticIntPtrtp_hash(IntPtrob)
71+
publicstaticninttp_hash(IntPtrob)
7272
{
7373
varself=(EventBinding)GetManagedObject(ob);
74-
longx=0;
75-
longy=0;
74+
nintx=0;
7675

7776
if(self.target!=IntPtr.Zero)
7877
{
79-
x=Runtime.PyObject_Hash(self.target).ToInt64();
78+
x=Runtime.PyObject_Hash(self.target);
8079
if(x==-1)
8180
{
82-
returnnewIntPtr(-1);
81+
returnx;
8382
}
8483
}
8584

86-
y=Runtime.PyObject_Hash(self.e.pyHandle).ToInt64();
85+
ninty=Runtime.PyObject_Hash(self.e.pyHandle);
8786
if(y==-1)
8887
{
89-
returnnewIntPtr(-1);
88+
returny;
9089
}
9190

92-
x^=y;
93-
94-
if(x==-1)
95-
{
96-
x=-1;
97-
}
98-
99-
returnnewIntPtr(x);
91+
returnx^y;
10092
}
10193

10294

‎src/runtime/eventobject.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,17 +72,22 @@ internal bool AddEventHandler(IntPtr target, IntPtr handler)
7272
/// </summary>
7373
internalboolRemoveEventHandler(IntPtrtarget,IntPtrhandler)
7474
{
75+
if(reg==null)
76+
{
77+
Exceptions.SetError(Exceptions.ValueError,"unknown event handler");
78+
returnfalse;
79+
}
80+
7581
objectobj=null;
7682
if(target!=IntPtr.Zero)
7783
{
7884
varco=(CLRObject)GetManagedObject(target);
7985
obj=co.inst;
8086
}
8187

82-
IntPtrhash=Runtime.PyObject_Hash(handler);
83-
if(Exceptions.ErrorOccurred()||reg==null)
88+
ninthash=Runtime.PyObject_Hash(handler);
89+
if(hash==-1&&Exceptions.ErrorOccurred())
8490
{
85-
Exceptions.SetError(Exceptions.ValueError,"unknown event handler");
8691
returnfalse;
8792
}
8893

‎src/runtime/exceptions.cs

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
usingSystem;
22
usingSystem.Reflection;
33
usingSystem.Runtime.InteropServices;
4+
usingSystem.Text;
45

56
namespacePython.Runtime
67
{
@@ -71,14 +72,16 @@ internal static Exception ToException(IntPtr ob)
7172
returnExceptions.RaiseTypeError("invalid object");
7273
}
7374

74-
stringmessage=string.Empty;
75-
if(e.Message!=string.Empty)
75+
stringmessage=e.ToString();
76+
stringfullTypeName=e.GetType().FullName;
77+
stringprefix=fullTypeName+": ";
78+
if(message.StartsWith(prefix))
7679
{
77-
message=e.Message;
80+
message=message.Substring(prefix.Length);
7881
}
79-
if(!string.IsNullOrEmpty(e.StackTrace))
82+
elseif(message.StartsWith(fullTypeName))
8083
{
81-
message=message+"\n"+e.StackTrace;
84+
message=message.Substring(fullTypeName.Length);
8285
}
8386
returnRuntime.PyUnicode_FromString(message);
8487
}
@@ -181,6 +184,7 @@ internal static void SetArgsAndCause(IntPtr ob)
181184

182185
if(e.InnerException!=null)
183186
{
187+
// Note: For an AggregateException, InnerException is only the first of the InnerExceptions.
184188
IntPtrcause=CLRObject.GetInstHandle(e.InnerException);
185189
Marshal.WriteIntPtr(ob,ExceptionOffset.cause,cause);
186190
}
@@ -290,6 +294,20 @@ public static void SetError(Exception e)
290294
Runtime.XDecref(op);
291295
}
292296

297+
/// <summary>
298+
/// When called after SetError, sets the cause of the error.
299+
/// </summary>
300+
/// <param name="cause">The cause of the current error</param>
301+
publicstaticvoidSetCause(PythonExceptioncause)
302+
{
303+
varcurrentException=newPythonException();
304+
currentException.Normalize();
305+
cause.Normalize();
306+
Runtime.XIncref(cause.PyValue);
307+
Runtime.PyException_SetCause(currentException.PyValue,cause.PyValue);
308+
currentException.Restore();
309+
}
310+
293311
/// <summary>
294312
/// ErrorOccurred Method
295313
/// </summary>
@@ -368,17 +386,31 @@ public static void deprecation(string message)
368386
// Internal helper methods for common error handling scenarios.
369387
//====================================================================
370388

389+
/// <summary>
390+
/// Raises a TypeError exception and attaches any existing exception as its cause.
391+
/// </summary>
392+
/// <param name="message">The exception message</param>
393+
/// <returns><c>IntPtr.Zero</c></returns>
371394
internalstaticIntPtrRaiseTypeError(stringmessage)
372395
{
396+
PythonExceptionpreviousException=null;
397+
if(ErrorOccurred())
398+
{
399+
previousException=newPythonException();
400+
}
373401
Exceptions.SetError(Exceptions.TypeError,message);
402+
if(previousException!=null)
403+
{
404+
SetCause(previousException);
405+
}
374406
returnIntPtr.Zero;
375407
}
376408

377409
// 2010-11-16: Arranged in python (2.6 & 2.7) source header file order
378410
/* Predefined exceptions are
379-
puplic static variables on the Exceptions class filled in from
411+
public static variables on the Exceptions class filled in from
380412
the python class using reflection in Initialize() looked up by
381-
name, notposistion. */
413+
name, notposition. */
382414
publicstaticIntPtrBaseException;
383415
publicstaticIntPtrException;
384416
publicstaticIntPtrStopIteration;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp