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

Allow decoding instanceless exceptions#1544

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
lostmsu merged 1 commit intopythonnet:masterfromlosttech:decode-instanceless-exn
Sep 22, 2021
Merged
Show file tree
Hide file tree
Changes fromall 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
63 changes: 39 additions & 24 deletionssrc/embed_tests/Codecs.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,6 @@ static void TupleConversionsGeneric<T, TTuple>()
TupleCodec<TTuple>.Register();
var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
T restored = default;
using (Py.GIL())
using (var scope = Py.CreateScope())
{
void Accept(T value) => restored = value;
Expand All@@ -53,7 +52,6 @@ static void TupleConversionsObject<T, TTuple>()
TupleCodec<TTuple>.Register();
var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
T restored = default;
using (Py.GIL())
using (var scope = Py.CreateScope())
{
void Accept(object value) => restored = (T)value;
Expand All@@ -73,12 +71,9 @@ public void TupleRoundtripObject()
static void TupleRoundtripObject<T, TTuple>()
{
var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
using (Py.GIL())
{
var pyTuple = TupleCodec<TTuple>.Instance.TryEncode(tuple);
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple, out object restored));
Assert.AreEqual(expected: tuple, actual: restored);
}
var pyTuple = TupleCodec<TTuple>.Instance.TryEncode(tuple);
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple, out object restored));
Assert.AreEqual(expected: tuple, actual: restored);
}

[Test]
Expand All@@ -90,21 +85,12 @@ public void TupleRoundtripGeneric()
static void TupleRoundtripGeneric<T, TTuple>()
{
var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
using (Py.GIL())
{
var pyTuple = TupleCodec<TTuple>.Instance.TryEncode(tuple);
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple, out T restored));
Assert.AreEqual(expected: tuple, actual: restored);
}
var pyTuple = TupleCodec<TTuple>.Instance.TryEncode(tuple);
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple, out T restored));
Assert.AreEqual(expected: tuple, actual: restored);
}

static PyObject GetPythonIterable()
{
using (Py.GIL())
{
return PythonEngine.Eval("map(lambda x: x, [1,2,3])");
}
}
static PyObject GetPythonIterable() => PythonEngine.Eval("map(lambda x: x, [1,2,3])");

[Test]
public void ListDecoderTest()
Expand DownExpand Up@@ -330,7 +316,6 @@ public void ExceptionEncoded()
PyObjectConversions.RegisterEncoder(new ValueErrorCodec());
void CallMe() => throw new ValueErrorWrapper(TestExceptionMessage);
var callMeAction = new Action(CallMe);
using var _ = Py.GIL();
using var scope = Py.CreateScope();
scope.Exec(@"
def call(func):
Expand All@@ -348,7 +333,6 @@ def call(func):
public void ExceptionDecoded()
{
PyObjectConversions.RegisterDecoder(new ValueErrorCodec());
using var _ = Py.GIL();
using var scope = Py.CreateScope();
var error = Assert.Throws<ValueErrorWrapper>(()
=> PythonEngine.Exec($"raise ValueError('{TestExceptionMessage}')"));
Expand All@@ -371,6 +355,16 @@ from datetime import datetime
scope.Exec("Codecs.AcceptsDateTime(datetime(2021, 1, 22))");
}

[Test]
public void ExceptionDecodedNoInstance()
{
PyObjectConversions.RegisterDecoder(new InstancelessExceptionDecoder());
using var scope = Py.CreateScope();
var error = Assert.Throws<ValueErrorWrapper>(() => PythonEngine.Exec(
$"[].__iter__().__next__()"));
Assert.AreEqual(TestExceptionMessage, error.Message);
}

public static void AcceptsDateTime(DateTime v) {}

[Test]
Expand DownExpand Up@@ -406,7 +400,8 @@ public ValueErrorWrapper(string message) : base(message) { }
class ValueErrorCodec : IPyObjectEncoder, IPyObjectDecoder
{
public bool CanDecode(PyObject objectType, Type targetType)
=> this.CanEncode(targetType) && objectType.Equals(PythonEngine.Eval("ValueError"));
=> this.CanEncode(targetType)
&& PythonReferenceComparer.Instance.Equals(objectType, PythonEngine.Eval("ValueError"));

public bool CanEncode(Type type) => type == typeof(ValueErrorWrapper)
|| typeof(ValueErrorWrapper).IsSubclassOf(type);
Expand All@@ -424,6 +419,26 @@ public PyObject TryEncode(object value)
return PythonEngine.Eval("ValueError").Invoke(error.Message.ToPython());
}
}

class InstancelessExceptionDecoder : IPyObjectDecoder
{
readonly PyObject PyErr = Py.Import("clr.interop").GetAttr("PyErr");

public bool CanDecode(PyObject objectType, Type targetType)
=> PythonReferenceComparer.Instance.Equals(PyErr, objectType);

public bool TryDecode<T>(PyObject pyObj, out T value)
{
if (pyObj.HasAttr("value"))
{
value = default;
return false;
}

value = (T)(object)new ValueErrorWrapper(TestExceptionMessage);
return true;
}
}
}

/// <summary>
Expand Down
3 changes: 3 additions & 0 deletionssrc/runtime/Python.Runtime.csproj
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -43,6 +43,9 @@
<EmbeddedResource Include="resources\clr.py">
<LogicalName>clr.py</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="resources\interop.py">
<LogicalName>interop.py</LogicalName>
</EmbeddedResource>
</ItemGroup>

<ItemGroup>
Expand Down
24 changes: 24 additions & 0 deletionssrc/runtime/Util.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
#nullable enable
using System;
using System.IO;
using System.Runtime.InteropServices;

namespace Python.Runtime
Expand DownExpand Up@@ -41,5 +43,27 @@ internal static void WriteCLong(IntPtr type, int offset, Int64 flags)
/// </summary>
internal static IntPtr Coalesce(this IntPtr primary, IntPtr fallback)
=> primary == IntPtr.Zero ? fallback : primary;

/// <summary>
/// Gets substring after last occurrence of <paramref name="symbol"/>
/// </summary>
internal static string? AfterLast(this string str, char symbol)
{
if (str is null)
throw new ArgumentNullException(nameof(str));

int last = str.LastIndexOf(symbol);
return last >= 0 ? str.Substring(last + 1) : null;
}

internal static string ReadStringResource(this System.Reflection.Assembly assembly, string resourceName)
{
if (assembly is null) throw new ArgumentNullException(nameof(assembly));
if (string.IsNullOrEmpty(resourceName)) throw new ArgumentNullException(nameof(resourceName));

using var stream = assembly.GetManifestResourceStream(resourceName);
using var reader = new StreamReader(stream);
return reader.ReadToEnd();
}
}
}
37 changes: 30 additions & 7 deletionssrc/runtime/pythonengine.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
Expand DownExpand Up@@ -229,13 +230,11 @@ public static void Initialize(IEnumerable<string> args, bool setSysArgv = true,
Runtime.PyDict_SetItemString(module_globals, "__builtins__", builtins);

Assembly assembly = Assembly.GetExecutingAssembly();
using (Stream stream = assembly.GetManifestResourceStream("clr.py"))
using (var reader = new StreamReader(stream))
{
// add the contents of clr.py to the module
string clr_py = reader.ReadToEnd();
Exec(clr_py, module_globals, locals.Reference);
}
// add the contents of clr.py to the module
string clr_py = assembly.ReadStringResource("clr.py");
Exec(clr_py, module_globals, locals.Reference);

LoadSubmodule(module_globals, "clr.interop", "interop.py");

// add the imported module to the clr module, and copy the API functions
// and decorators into the main clr module.
Expand All@@ -258,6 +257,30 @@ public static void Initialize(IEnumerable<string> args, bool setSysArgv = true,
}
}

static BorrowedReference DefineModule(string name)
{
var module = Runtime.PyImport_AddModule(name);
var module_globals = Runtime.PyModule_GetDict(module);
var builtins = Runtime.PyEval_GetBuiltins();
Runtime.PyDict_SetItemString(module_globals, "__builtins__", builtins);
return module;
}

static void LoadSubmodule(BorrowedReference targetModuleDict, string fullName, string resourceName)
{
string memberName = fullName.AfterLast('.');
Debug.Assert(memberName != null);

var module = DefineModule(fullName);
var module_globals = Runtime.PyModule_GetDict(module);

Assembly assembly = Assembly.GetExecutingAssembly();
string pyCode = assembly.ReadStringResource(resourceName);
Exec(pyCode, module_globals.DangerousGetAddress(), module_globals.DangerousGetAddress());

Runtime.PyDict_SetItemString(targetModuleDict, memberName, module);
}

static void OnDomainUnload(object _, EventArgs __)
{
Shutdown();
Expand Down
49 changes: 49 additions & 0 deletionssrc/runtime/pythonexception.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,6 +37,10 @@ public PythonException(PyType type, PyObject? value, PyObject? traceback)
/// </summary>
internal static Exception ThrowLastAsClrException()
{
// prevent potential interop errors in this method
// from crashing process with undebuggable StackOverflowException
RuntimeHelpers.EnsureSufficientExecutionStack();

var exception = FetchCurrentOrNull(out ExceptionDispatchInfo? dispatchInfo)
?? throw new InvalidOperationException("No exception is set");
dispatchInfo?.Throw();
Expand DownExpand Up@@ -83,6 +87,24 @@ internal static PythonException FetchCurrentRaw()
return null;
}

try
{
if (TryDecodePyErr(type, value, traceback) is { } pyErr)
{
type.Dispose();
value.Dispose();
traceback.Dispose();
return pyErr;
}
}
catch
{
type.Dispose();
value.Dispose();
traceback.Dispose();
throw;
}

Runtime.PyErr_NormalizeException(type: ref type, val: ref value, tb: ref traceback);

try
Expand DownExpand Up@@ -153,6 +175,11 @@ private static Exception FromPyErr(BorrowedReference typeRef, BorrowedReference
return e;
}

if (TryDecodePyErr(typeRef, valRef, tbRef) is { } pyErr)
{
return pyErr;
}

if (PyObjectConversions.TryDecode(valRef, typeRef, typeof(Exception), out object decoded)
&& decoded is Exception decodedException)
{
Expand All@@ -164,6 +191,28 @@ private static Exception FromPyErr(BorrowedReference typeRef, BorrowedReference
return new PythonException(type, value, traceback, inner);
}

private static Exception? TryDecodePyErr(BorrowedReference typeRef, BorrowedReference valRef, BorrowedReference tbRef)
{
using var type = PyType.FromReference(typeRef);
using var value = PyObject.FromNullableReference(valRef);
using var traceback = PyObject.FromNullableReference(tbRef);

using var errorDict = new PyDict();
if (typeRef != null) errorDict["type"] = type;
if (valRef != null) errorDict["value"] = value;
if (tbRef != null) errorDict["traceback"] = traceback;

using var pyErrType = Runtime.InteropModule.GetAttr("PyErr");
using var pyErrInfo = pyErrType.Invoke(new PyTuple(), errorDict);
if (PyObjectConversions.TryDecode(pyErrInfo.Reference, pyErrType.Reference,
typeof(Exception), out object decoded) && decoded is Exception decodedPyErrInfo)
{
return decodedPyErrInfo;
}

return null;
}

private static Exception? FromCause(BorrowedReference cause)
{
if (cause == null || cause.IsNone()) return null;
Expand Down
10 changes: 10 additions & 0 deletionssrc/runtime/resources/interop.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
_UNSET = object()

class PyErr:
def __init__(self, type=_UNSET, value=_UNSET, traceback=_UNSET):
if not(type is _UNSET):
self.type = type
if not(value is _UNSET):
self.value = value
if not(traceback is _UNSET):
self.traceback = traceback
10 changes: 10 additions & 0 deletionssrc/runtime/runtime.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -178,6 +178,8 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
}
XDecref(item);
AssemblyManager.UpdatePath();

clrInterop = GetModuleLazy("clr.interop");
}

private static void InitPyMembers()
Expand DownExpand Up@@ -406,6 +408,11 @@ internal static void Shutdown()
Shutdown(mode);
}

private static Lazy<PyObject> GetModuleLazy(string moduleName)
=> moduleName is null
? throw new ArgumentNullException(nameof(moduleName))
: new Lazy<PyObject>(() => PyModule.Import(moduleName), isThreadSafe: false);

internal static ShutdownMode GetDefaultShutdownMode()
{
string modeEvn = Environment.GetEnvironmentVariable("PYTHONNET_SHUTDOWN_MODE");
Expand DownExpand Up@@ -574,6 +581,9 @@ private static void MoveClrInstancesOnwershipToPython()
internal static IntPtr PyNone;
internal static IntPtr Error;

private static Lazy<PyObject> clrInterop;
internal static PyObject InteropModule => clrInterop.Value;

internal static BorrowedReference CLRMetaType => new BorrowedReference(PyCLRMetaType);

public static PyObject None
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp