Better error messages for method argument mismatch and others by tminka · Pull Request #1361 · pythonnet/pythonnet · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
4 changes: 2 additions & 2 deletions src/clrmodule/ClrModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ public static IntPtr PyInit_clr()
pythonRuntime = Assembly.Load(pythonRuntimeName);
DebugPrint("Success loading 'Python.Runtime' using standard binding rules.");
}
catch (IOException)
catch (IOException ex)
{
DebugPrint("'Python.Runtime' not found using standard binding rules.");
DebugPrint($"'Python.Runtime' not found using standard binding rules: {ex}");
try
{
// If the above fails for any reason, we fallback to attempting to load "Python.Runtime.dll"
Expand Down
1 change: 1 addition & 0 deletions src/domain_tests/test_domain_reload.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def test_property_visibility_change():
def test_class_visibility_change():
_run_test("class_visibility_change")

@pytest.mark.skip(reason='FIXME: Domain reload fails when Python points to a .NET object which points back to Python objects')
@pytest.mark.skipif(platform.system() == 'Darwin', reason='FIXME: macos can\'t find the python library')
def test_method_parameters_change():
_run_test("method_parameters_change")
Expand Down
10 changes: 10 additions & 0 deletions src/embed_tests/TestPythonException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public void TestPythonExceptionFormatNormalized()
try
{
PythonEngine.Exec("a=b\n");
Assert.Fail("Exception should have been raised");
}
catch (PythonException ex)
{
Expand Down Expand Up @@ -135,5 +136,14 @@ def __init__(self, val):
}
}
}

[Test]
public void TestPythonException_Normalize_ThrowsWhenErrorSet()
{
Exceptions.SetError(Exceptions.TypeError, "Error!");
var pythonException = new PythonException();
Exceptions.SetError(Exceptions.TypeError, "Another error");
Assert.Throws<InvalidOperationException>(() => pythonException.Normalize());
}
}
}
15 changes: 12 additions & 3 deletions src/runtime/classbase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,16 @@ public virtual IntPtr type_subscript(IntPtr idx)

if (target != null)
{
Type t = target.MakeGenericType(types);
Type t;
try
{
// MakeGenericType can throw ArgumentException
t = target.MakeGenericType(types);
}
catch (ArgumentException e)
{
return Exceptions.RaiseTypeError(e.Message);
}
ManagedType c = ClassManager.GetClass(t);
Runtime.XIncref(c.pyHandle);
return c.pyHandle;
Expand Down Expand Up @@ -263,14 +272,14 @@ public static IntPtr tp_iter(IntPtr ob)
/// <summary>
/// Standard __hash__ implementation for instances of reflected types.
/// </summary>
public static IntPtr tp_hash(IntPtr ob)
public static nint tp_hash(IntPtr ob)
{
var co = GetManagedObject(ob) as CLRObject;
if (co == null)
{
return Exceptions.RaiseTypeError("unhashable type");
}
return new IntPtr(co.inst.GetHashCode());
return co.inst.GetHashCode();
}


Expand Down
73 changes: 58 additions & 15 deletions src/runtime/converter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,11 @@ internal static IntPtr ToPythonImplicit(object value)
/// Return a managed object for the given Python object, taking funny
/// byref types into account.
/// </summary>
/// <param name="value">A Python object</param>
/// <param name="type">The desired managed type</param>
/// <param name="result">Receives the managed object</param>
/// <param name="setError">If true, call <c>Exceptions.SetError</c> with the reason for failure.</param>
/// <returns>True on success</returns>
internal static bool ToManaged(IntPtr value, Type type,
out object result, bool setError)
{
Expand Down Expand Up @@ -341,7 +346,10 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
result = tmp;
return true;
}
Exceptions.SetError(Exceptions.TypeError, $"value cannot be converted to {obType}");
if (setError)
{
Exceptions.SetError(Exceptions.TypeError, $"value cannot be converted to {obType}");
}
return false;
}
if (mt is ClassBase)
Expand Down Expand Up @@ -376,6 +384,15 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
obType = obType.GetGenericArguments()[0];
}

if (obType.ContainsGenericParameters)
{
if (setError)
{
Exceptions.SetError(Exceptions.TypeError, $"Cannot create an instance of the open generic type {obType}");
}
return false;
}

if (obType.IsArray)
{
return ToArray(value, obType, out result, setError);
Expand Down Expand Up @@ -777,7 +794,7 @@ private static void SetConversionError(IntPtr value, Type target)
IntPtr ob = Runtime.PyObject_Repr(value);
string src = Runtime.GetManagedString(ob);
Runtime.XDecref(ob);
Exceptions.SetError(Exceptions.TypeError, $"Cannot convert {src} to {target}");
Exceptions.RaiseTypeError($"Cannot convert {src} to {target}");
}


Expand All @@ -791,32 +808,58 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s
Type elementType = obType.GetElementType();
result = null;

bool IsSeqObj = Runtime.PySequence_Check(value);
var len = IsSeqObj ? Runtime.PySequence_Size(value) : -1;

IntPtr IterObject = Runtime.PyObject_GetIter(value);

if(IterObject==IntPtr.Zero) {
if (IterObject == IntPtr.Zero)
{
if (setError)
{
SetConversionError(value, obType);
}
else
{
// PyObject_GetIter will have set an error
Exceptions.Clear();
}
return false;
}

Array items;
IList list;
try
{
// MakeGenericType can throw because elementType may not be a valid generic argument even though elementType[] is a valid array type.
// For example, if elementType is a pointer type.
// See https://docs.microsoft.com/en-us/dotnet/api/system.type.makegenerictype#System_Type_MakeGenericType_System_Type
var constructedListType = typeof(List<>).MakeGenericType(elementType);
bool IsSeqObj = Runtime.PySequence_Check(value);
if (IsSeqObj)
{
var len = Runtime.PySequence_Size(value);
list = (IList)Activator.CreateInstance(constructedListType, new Object[] { (int)len });
}
else
{
// CreateInstance can throw even if MakeGenericType succeeded.
// See https://docs.microsoft.com/en-us/dotnet/api/system.activator.createinstance#System_Activator_CreateInstance_System_Type_
list = (IList)Activator.CreateInstance(constructedListType);
}
}
catch (Exception e)
{
if (setError)
{
Exceptions.SetError(e);
SetConversionError(value, obType);
}
return false;
}

var listType = typeof(List<>);
var constructedListType = listType.MakeGenericType(elementType);
IList list = IsSeqObj ? (IList) Activator.CreateInstance(constructedListType, new Object[] {(int) len}) :
(IList) Activator.CreateInstance(constructedListType);
IntPtr item;

while ((item = Runtime.PyIter_Next(IterObject)) != IntPtr.Zero)
{
object obj = null;
object obj;

if (!Converter.ToManaged(item, elementType, out obj, true))
if (!Converter.ToManaged(item, elementType, out obj, setError))
{
Runtime.XDecref(item);
return false;
Expand All @@ -827,7 +870,7 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s
}
Runtime.XDecref(IterObject);

items = Array.CreateInstance(elementType, list.Count);
Array items = Array.CreateInstance(elementType, list.Count);
list.CopyTo(items, 0);

result = items;
Expand Down
22 changes: 7 additions & 15 deletions src/runtime/eventbinding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,35 +68,27 @@ public static IntPtr nb_inplace_subtract(IntPtr ob, IntPtr arg)
/// <summary>
/// EventBinding __hash__ implementation.
/// </summary>
public static IntPtr tp_hash(IntPtr ob)
public static nint tp_hash(IntPtr ob)
{
var self = (EventBinding)GetManagedObject(ob);
long x = 0;
long y = 0;
nint x = 0;

if (self.target != IntPtr.Zero)
{
x = Runtime.PyObject_Hash(self.target).ToInt64();
x = Runtime.PyObject_Hash(self.target);
if (x == -1)
{
return new IntPtr(-1);
return x;
}
}

y = Runtime.PyObject_Hash(self.e.pyHandle).ToInt64();
nint y = Runtime.PyObject_Hash(self.e.pyHandle);
if (y == -1)
{
return new IntPtr(-1);
return y;
}

x ^= y;

if (x == -1)
{
x = -1;
}

return new IntPtr(x);
return x ^ y;
}


Expand Down
11 changes: 8 additions & 3 deletions src/runtime/eventobject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,22 @@ internal bool AddEventHandler(IntPtr target, IntPtr handler)
/// </summary>
internal bool RemoveEventHandler(IntPtr target, IntPtr handler)
{
if (reg == null)
{
Exceptions.SetError(Exceptions.ValueError, "unknown event handler");
return false;
}

object obj = null;
if (target != IntPtr.Zero)
{
var co = (CLRObject)GetManagedObject(target);
obj = co.inst;
}

IntPtr hash = Runtime.PyObject_Hash(handler);
if (Exceptions.ErrorOccurred() || reg == null)
nint hash = Runtime.PyObject_Hash(handler);
if (hash == -1 && Exceptions.ErrorOccurred())
{
Exceptions.SetError(Exceptions.ValueError, "unknown event handler");
return false;
}

Expand Down
46 changes: 39 additions & 7 deletions src/runtime/exceptions.cs
Loading