8000 Improve Python <-> .NET exception integration by lostmsu · Pull Request #1134 · pythonnet/pythonnet · GitHub
[go: up one dir, main page]

Skip to content

Improve Python <-> .NET exception integration #1134

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 42 commits into from
Jun 1, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
663df73
reworked PythonException to simplify memory management, and enable .N…
lostmsu May 5, 2020
d45c39a
allows codecs to encode and decode thrown exceptions
lostmsu Feb 25, 2020
0d941c5
turned on CLR exception marshaling
lostmsu May 5, 2020
02cd234
revert to C# 7.3
lostmsu May 5, 2020
defb200
fixed Restore when not all exception fields are set
lostmsu May 5, 2020
945dc85
cleaned PythonException code up a bit
lostmsu May 5, 2020
3d95e60
remove unused overloads of FromPyErr
lostmsu May 5, 2020
d667998
capture exception on Exceptions.SetError, restore in ThrowLastAsClrEx…
lostmsu May 5, 2020
bec8b7d
fixed failure in ExceptionEncoded test case caused by ExceptionDispat…
lostmsu May 5, 2020
0961c94
added tests for __cause__/InnerException #893 #1098
lostmsu May 6, 2020
2cd8627
introduced StolenReference type
lostmsu May 14, 2020
e4c1b9b
prevent undebuggable StackOverflowException during exception interop
lostmsu May 29, 2020
81cd197
Merge branch 'master' into PR/ExceptionsImprovement
lostmsu Jun 22, 2020
3c0b737
README: added summary of new exception handling features
lostmsu Jun 22, 2020
257a765
merge latest master
lostmsu Apr 9, 2021
c0fe430
reworked `PythonException`:
lostmsu Apr 9, 2021
e58411d
rum embedding tests before Python tests
lostmsu Apr 9, 2021
00653dc
PythonException.StackTrace is GIL-safe
lostmsu Apr 9, 2021
3433201
separate .Steal() and .StealNullable()
lostmsu Apr 11, 2021
95cc52f
can't test exception type when runtime is down
lostmsu Apr 11, 2021
63ad42c
PythonException: dispose intermediate values used in stack trace gene…
lostmsu Apr 11, 2021
faec7fc
Point users to Message property of PythonException
lostmsu Apr 11, 2021
dfc70f6
minor change in PythonEngine.With
lostmsu Apr 11, 2021
d976acf
simplified code of PythonException and added a lot more checks
lostmsu Apr 11, 2021
146ebf3
fixed type of reference in PyException_SetCause
lostmsu Apr 11, 2021
272687b
minor fixes to Converter.ToArray:
lostmsu Apr 11, 2021
2cd3f61
added a few debug checks to Exceptions.SetError
lostmsu Apr 11, 2021
e79f041
method binding failure now supports non-Python exception causes
lostmsu Apr 11, 2021
d068f36
XDecref now checks, that refcount is positive in debug builds
lostmsu Apr 11, 2021
4877fe7
fixed __cause__ on overload bind failure and array conversion
lostmsu Apr 11, 2021
ed594c1
cache PythonException message
lostmsu Apr 11, 2021
e5bce06
minor code cleanup
lostmsu Apr 11, 2021
6819e7b
improved handling of dict offset in object instances
lostmsu Apr 11, 2021
6679d1c
added a few debug checks
lostmsu Apr 11, 2021
67bd1c2
merge latest master
lostmsu May 15, 2021
2e57b04
+ class diagram for ManagedType and subclasses
lostmsu May 23, 2021
539ce81
added OverloadMapper to ManagedTypes class diagram
lostmsu May 23, 2021
25e3864
refactored tp_dealloc in ExtensionType and descendants
lostmsu May 23, 2021
5bca333
refactored tp_clear in ExtensionType and descendants into a virtual C…
lostmsu May 23, 2021
7271d88
all Dealloc overrides simply duplicate Clear, so just call both from …
lostmsu May 23, 2021
4f3f648
fixup! merge latest master
SDEII May 29, 2021
c500a39
fixup! reworked `PythonException`:
lostmsu May 29, 2021
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
Prev Previous commit
Next Next commit
merge latest master
  • Loading branch information
lostmsu committed May 15, 2021
commit 67bd1c2429d8aa84b18ea04d05aa9549f60d09ef
3 changes: 1 addition & 2 deletions src/embed_tests/TestRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Collections.Generic;
using NUnit.Framework;
using Python.Runtime;
using Python.Runtime.Platform;

namespace Python.EmbeddingTest
{
Expand Down Expand Up @@ -102,7 +101,7 @@ public static void PyCheck_Iter_PyObject_IsIterable_ThreadingLock_Test()
Exceptions.ErrorCheck(threadingDict);
var lockType = Runtime.Runtime.PyDict_GetItemString(threadingDict, "Lock");
if (lockType.IsNull)
throw new PythonException();
throw PythonException.ThrowLastAsClrException();

using var args = NewReference.DangerousFromPointer(Runtime.Runtime.PyTuple_New(0));
using var lockInstance = Runtime.Runtime.PyObject_CallObject(lockType, args);
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/clrobject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal CLRObject(object ob, IntPtr tp)

// Fix the BaseException args (and __cause__ in case of Python 3)
// slot if wrapping a CLR exception
Exceptions.SetArgsAndCause(ObjectReference);
if (ob is Exception e) Exceptions.SetArgsAndCause(ObjectReference, e);
}

internal CLRObject(object ob, BorrowedReference tp) : this(ob, tp.DangerousGetAddress()) { }
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/converter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ internal static int ToInt32(BorrowedReference value)
nint num = Runtime.PyLong_AsSignedSize_t(value);
if (num == -1 && Exceptions.ErrorOccurred())
{
throw new PythonException();
throw PythonException.ThrowLastAsClrException();
}
return checked((int)num);
}
Expand Down
10 changes: 6 additions & 4 deletions src/runtime/exceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,7 @@ internal static void Shutdown()
/// __getattr__ implementation, and thus dereferencing a NULL
/// pointer.
/// </summary>
/// <param name="ob">The python object wrapping </param>
internal static void SetArgsAndCause(BorrowedReference ob)
internal static void SetArgsAndCause(BorrowedReference ob, Exception e)
{
IntPtr args;
if (!string.IsNullOrEmpty(e.Message))
Expand All @@ -162,7 +161,10 @@ internal static void SetArgsAndCause(BorrowedReference ob)
args = Runtime.PyTuple_New(0);
}

Marshal.WriteIntPtr(ob.DangerousGetAddress(), ExceptionOffset.args, args);
using var argsTuple = NewReference.DangerousFromPointer(args);

if (Runtime.PyObject_SetAttrString(ob, "args", argsTuple) != 0)
throw PythonException.ThrowLastAsClrException();

if (e.InnerException != null)
{
Expand Down Expand Up @@ -201,7 +203,7 @@ internal static IntPtr ErrorCheckIfNull(IntPtr pointer)
{
if (pointer == IntPtr.Zero && ErrorOccurred())
{
throw new PythonException();
throw PythonException.ThrowLastAsClrException();
}
return pointer;
}
Expand Down
273 changes: 0 additions & 273 deletions src/runtime/interop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,279 +68,6 @@ public ModulePropertyAttribute()
}
}

internal static partial class TypeOffset
{
public static int magic() => ManagedDataOffsets.Magic;
}

internal static class ManagedDataOffsets
{
public static int Magic { get; internal set; }
public static readonly Dictionary<string, int> NameMapping = new Dictionary<string, int>();

static class DataOffsets
{
public static readonly int ob_data = 0;
public static readonly int ob_dict = 0;

static DataOffsets()
{
FieldInfo[] fields = typeof(DataOffsets).GetFields(BindingFlags.Static | BindingFlags.Public);
for (int i = 0; i < fields.Length; i++)
{
fields[i].SetValue(null, -(i * IntPtr.Size) - IntPtr.Size);
}
}
}

static ManagedDataOffsets()
{
NameMapping = TypeOffset.GetOffsets();

FieldInfo[] fields = typeof(DataOffsets).GetFields(BindingFlags.Static | BindingFlags.Public);
size = fields.Length * IntPtr.Size;
}

public static int GetSlotOffset(string name)
{
return NameMapping[name];
}

private static int BaseOffset(IntPtr type)
{
Debug.Assert(type != IntPtr.Zero);
int typeSize = Marshal.ReadInt32(type, TypeOffset.tp_basicsize);
Debug.Assert(typeSize > 0);
return typeSize;
}

public static int DataOffset(IntPtr type)
{
return BaseOffset(type) + DataOffsets.ob_data;
}

public static int DictOffset(IntPtr type)
{
return BaseOffset(type) + DataOffsets.ob_dict;
}

public static int ob_data => DataOffsets.ob_data;
public static int ob_dict => DataOffsets.ob_dict;
public static int Size { get { return size; } }

private static readonly int size;
}

internal static class OriginalObjectOffsets
{
static OriginalObjectOffsets()
{
int size = IntPtr.Size;
var n = 0; // Py_TRACE_REFS add two pointers to PyObject_HEAD
#if PYTHON_WITH_PYDEBUG
_ob_next = 0;
_ob_prev = 1 * size;
n = 2;
#endif
ob_refcnt = (n + 0) * size;
ob_type = (n + 1) * size;
}

public static int Size { get { return size; } }

private static readonly int size =
#if PYTHON_WITH_PYDEBUG
4 * IntPtr.Size;
#else
2 * IntPtr.Size;
#endif

#if PYTHON_WITH_PYDEBUG
public static int _ob_next;
public static int _ob_prev;
#endif
public static int ob_refcnt;
public static int ob_type;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal class ObjectOffset
{
static ObjectOffset()
{
#if PYTHON_WITH_PYDEBUG
_ob_next = OriginalObjectOffsets._ob_next;
_ob_prev = OriginalObjectOffsets._ob_prev;
#endif
ob_refcnt = OriginalObjectOffsets.ob_refcnt;
ob_type = OriginalObjectOffsets.ob_type;

size = OriginalObjectOffsets.Size + ManagedDataOffsets.Size;
}

public static int magic(IntPtr type)
{
return ManagedDataOffsets.DataOffset(type);
}

public static int TypeDictOffset(IntPtr type)
{
Debug.Assert(TypeOffset.tp_dictoffset > 0);
int dictOffset = Marshal.ReadInt32(type, TypeOffset.tp_dictoffset);
Debug.Assert(dictOffset > 0);
return dictOffset;
}

public static int Size(IntPtr pyType)
{
if (IsException(pyType))
{
return ExceptionOffset.Size();
}

return size;
}

#if PYTHON_WITH_PYDEBUG
public static int _ob_next;
public static int _ob_prev;
#endif
public static int ob_refcnt;
public static int ob_type;
private static readonly int size;

private static bool IsException(IntPtr pyObjectPtr)
{
var pyObject = new BorrowedReference(pyObjectPtr);
var type = Runtime.PyObject_TYPE(pyObject);
return Runtime.PyType_IsSameAsOrSubtype(type, ofType: Exceptions.BaseException)
|| Runtime.PyType_IsSameAsOrSubtype(type, ofType: Runtime.PyTypeType)
&& Runtime.PyType_IsSubtype(pyObject, Exceptions.BaseException);
}
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal class ExceptionOffset
{
static ExceptionOffset()
{
Type type = typeof(ExceptionOffset);
FieldInfo[] fi = type.GetFields(BindingFlags.Static | BindingFlags.Public);
for (int i = 0; i < fi.Length; i++)
{
fi[i].SetValue(null, (i * IntPtr.Size) + OriginalObjectOffsets.Size);
}

size = fi.Length * IntPtr.Size + OriginalObjectOffsets.Size + ManagedDataOffsets.Size;
}

public static int Size() { return size; }

// PyException_HEAD
// (start after PyObject_HEAD)
public static int dict = 0;
public static int args = 0;
public static int traceback = 0;
public static int context = 0;
public static int cause = 0;
public static int suppress_context = 0;

private static readonly int size;
}


[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal class BytesOffset
{
static BytesOffset()
{
Type type = typeof(BytesOffset);
FieldInfo[] fi = type.GetFields();
int size = IntPtr.Size;
for (int i = 0; i < fi.Length; i++)
{
fi[i].SetValue(null, i * size);
}
}

/* The *real* layout of a type object when allocated on the heap */
//typedef struct _heaptypeobject {
#if PYTHON_WITH_PYDEBUG
/* _PyObject_HEAD_EXTRA defines pointers to support a doubly-linked list of all live heap objects. */
public static int _ob_next = 0;
public static int _ob_prev = 0;
#endif
// PyObject_VAR_HEAD {
// PyObject_HEAD {
public static int ob_refcnt = 0;
public static int ob_type = 0;
// }
public static int ob_size = 0; /* Number of items in _VAR_iable part */
// }
public static int ob_shash = 0;
public static int ob_sval = 0; /* start of data */

/* Invariants:
* ob_sval contains space for 'ob_size+1' elements.
* ob_sval[ob_size] == 0.
* ob_shash is the hash of the string or -1 if not computed yet.
*/
//} PyBytesObject;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal class ModuleDefOffset
{
static ModuleDefOffset()
{
Type type = typeof(ModuleDefOffset);
FieldInfo[] fi = type.GetFields();
int size = IntPtr.Size;
for (int i = 0; i < fi.Length; i++)
{
fi[i].SetValue(null, (i * size) + TypeOffset.ob_size);
}
}

public static IntPtr AllocModuleDef(string modulename)
{
byte[] ascii = Encoding.ASCII.GetBytes(modulename);
int size = name + ascii.Length + 1;
IntPtr ptr = Marshal.AllocHGlobal(size);
for (int i = 0; i <= m_free; i += IntPtr.Size)
Marshal.WriteIntPtr(ptr, i, IntPtr.Zero);
Marshal.Copy(ascii, 0, (IntPtr)(ptr + name), ascii.Length);
Marshal.WriteIntPtr(ptr, m_name, (IntPtr)(ptr + name));
Marshal.WriteByte(ptr, name + ascii.Length, 0);
return ptr;
}

public static void FreeModuleDef(IntPtr ptr)
{
Marshal.FreeHGlobal(ptr);
}

// typedef struct PyModuleDef{
// typedef struct PyModuleDef_Base {
// starts after PyObject_HEAD (TypeOffset.ob_type + 1)
public static int m_init = 0;
public static int m_index = 0;
public static int m_copy = 0;
// } PyModuleDef_Base
public static int m_name = 0;
public static int m_doc = 0;
public static int m_size = 0;
public static int m_methods = 0;
public static int m_reload = 0;
public static int m_traverse = 0;
public static int m_clear = 0;
public static int m_free = 0;
// } PyModuleDef

public static int name = 0;
}


/// <summary>
/// TypeFlags(): The actual bit values for the Type Flags stored
/// in a class.
Expand Down
Loading
You are viewing a condensed version of this merge commit. You can view the full changes here.
0