Skip to content

Commit 663df73

Browse files
committed
reworked PythonException to simplify memory management, and enable .NET interop
New method ThrowLastAsClrException should be used everywhere instead of obsolete PythonException constructor. It automatically restores .NET exceptions, and applies codecs for Python exceptions. Traceback, PyVal and PyType are now stored and returned as PyObjects. PythonException now has InnerException set from its cause (e.g. __cause__ in Python, if any). PythonException.Restore no longer clears the exception instance. All helper methods were removed from public API surface.
1 parent 72fae73 commit 663df73

9 files changed

Lines changed: 235 additions & 107 deletions

src/runtime/BorrowedReference.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ readonly ref struct BorrowedReference
1414
public IntPtr DangerousGetAddress()
1515
=> this.IsNull ? throw new NullReferenceException() : this.pointer;
1616

17+
public static BorrowedReference Null => new BorrowedReference();
18+
1719
/// <summary>
1820
/// Creates new instance of <see cref="BorrowedReference"/> from raw pointer. Unsafe.
1921
/// </summary>

src/runtime/NewReference.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ public PyObject MoveToPyObject()
2828
return result;
2929
}
3030
/// <summary>
31+
/// Returns <see cref="PyObject"/> wrapper around this reference, which now owns
32+
/// the pointer. Sets the original reference to <c>null</c>, as it no longer owns it.
33+
/// </summary>
34+
public PyObject MoveToPyObjectOrNull() => this.IsNull() ? null : this.MoveToPyObject();
35+
/// <summary>
3136
/// Removes this reference to a Python object, and sets it to <c>null</c>.
3237
/// </summary>
3338
public void Dispose()

src/runtime/converterextensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ static IPyObjectEncoder[] GetEncoders(Type type)
110110
#region Decoding
111111
static readonly ConcurrentDictionary<TypePair, Converter.TryConvertFromPythonDelegate>
112112
pythonToClr = new ConcurrentDictionary<TypePair, Converter.TryConvertFromPythonDelegate>();
113+
internal static bool TryDecode(BorrowedReference value, BorrowedReference type, Type targetType, out object result)
114+
=> TryDecode(value.DangerousGetAddress(), type.DangerousGetAddress(), targetType, out result);
113115
internal static bool TryDecode(IntPtr pyHandle, IntPtr pyType, Type targetType, out object result)
114116
{
115117
if (pyHandle == IntPtr.Zero) throw new ArgumentNullException(nameof(pyHandle));

src/runtime/exceptions.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -279,10 +279,7 @@ public static void SetError(Exception e)
279279
var pe = e as PythonException;
280280
if (pe != null)
281281
{
282-
Runtime.XIncref(pe.PyType);
283-
Runtime.XIncref(pe.PyValue);
284-
Runtime.XIncref(pe.PyTB);
285-
Runtime.PyErr_Restore(pe.PyType, pe.PyValue, pe.PyTB);
282+
pe.Restore();
286283
return;
287284
}
288285

src/runtime/managedtype.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ internal abstract class ManagedType
1515
internal IntPtr tpHandle; // PyType *
1616

1717

18+
/// <summary>
19+
/// Given a Python object, return the associated managed object or null.
20+
/// </summary>
21+
internal static ManagedType GetManagedObject(BorrowedReference ob)
22+
=> GetManagedObject(ob.DangerousGetAddress());
23+
1824
/// <summary>
1925
/// Given a Python object, return the associated managed object or null.
2026
/// </summary>

src/runtime/pyobject.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ public class PyObject : DynamicObject, IEnumerable, IPyDisposable
3131

3232
protected internal IntPtr obj = IntPtr.Zero;
3333

34-
internal BorrowedReference Reference => new BorrowedReference(obj);
34+
public static PyObject None => new PyObject(new BorrowedReference(Runtime.PyNone));
35+
internal BorrowedReference Reference => new BorrowedReference(this.obj);
36+
internal NewReference MakeNewReference()
37+
=> NewReference.DangerousFromPointer(Runtime.SelfIncRef(this.obj));
3538

3639
/// <summary>
3740
/// PyObject Constructor
@@ -125,6 +128,13 @@ public static PyObject FromManagedObject(object ob)
125128
return new PyObject(op);
126129
}
127130

131+
/// <summary>
132+
/// Creates new <see cref="PyObject"/> from a nullable reference.
133+
/// When <paramref name="reference"/> is <c>null</c>, <c>null</c> is returned.
134+
/// </summary>
135+
internal static PyObject FromNullableReference(BorrowedReference reference)
136+
=> reference.IsNull ? null : new PyObject(reference);
137+
128138

129139
/// <summary>
130140
/// AsManagedObject Method
@@ -226,6 +236,9 @@ public IntPtr[] GetTrackedHandles()
226236
return new IntPtr[] { obj };
227237
}
228238

239+
internal BorrowedReference GetPythonTypeReference()
240+
=> new BorrowedReference(Runtime.PyObject_TYPE(obj));
241+
229242
/// <summary>
230243
/// GetPythonType Method
231244
/// </summary>

src/runtime/pythonengine.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -755,9 +755,9 @@ public static void With(PyObject obj, Action<dynamic> Body)
755755
// Behavior described here:
756756
// https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers
757757

758-
IntPtr type = Runtime.PyNone;
759-
IntPtr val = Runtime.PyNone;
760-
IntPtr traceBack = Runtime.PyNone;
758+
PyObject type = PyObject.None;
759+
PyObject val = PyObject.None;
760+
PyObject traceBack = PyObject.None;
761761
PythonException ex = null;
762762

763763
try
@@ -769,15 +769,12 @@ public static void With(PyObject obj, Action<dynamic> Body)
769769
catch (PythonException e)
770770
{
771771
ex = e;
772-
type = ex.PyType.Coalesce(type);
773-
val = ex.PyValue.Coalesce(val);
774-
traceBack = ex.PyTB.Coalesce(traceBack);
772+
type = ex.PyType ?? type;
773+
val = ex.PyValue ?? val;
774+
traceBack = ex.PyTB ?? traceBack;
775775
}
776776

777-
Runtime.XIncref(type);
778-
Runtime.XIncref(val);
779-
Runtime.XIncref(traceBack);
780-
var exitResult = obj.InvokeMethod("__exit__", new PyObject(type), new PyObject(val), new PyObject(traceBack));
777+
var exitResult = obj.InvokeMethod("__exit__", type, val, traceBack);
781778

782779
if (ex != null && !exitResult.IsTrue()) throw ex;
783780
}

0 commit comments

Comments
 (0)