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

Commit3180f29

Browse files
committed
enabled decoding instanceless exceptions
Added new class clr.interop.PyErr with optional type, value, and traceback attributes. User can register decoders for it, that would let them decode instanceless (and even typeless) Python exceptions.These decoders will be invoked before the regular exception instance decoders.
1 parentee0ab7f commit3180f29

File tree

7 files changed

+147
-2
lines changed

7 files changed

+147
-2
lines changed

‎src/embed_tests/Codecs.cs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,17 @@ from datetime import datetime
371371
scope.Exec("Codecs.AcceptsDateTime(datetime(2021, 1, 22))");
372372
}
373373

374+
[Test]
375+
publicvoidExceptionDecodedNoInstance()
376+
{
377+
PyObjectConversions.RegisterDecoder(newInstancelessExceptionDecoder());
378+
usingvar_=Py.GIL();
379+
usingvarscope=Py.CreateScope();
380+
varerror=Assert.Throws<ValueErrorWrapper>(()=>PythonEngine.Exec(
381+
$"[].__iter__().__next__()"));
382+
Assert.AreEqual(TestExceptionMessage,error.Message);
383+
}
384+
374385
publicstaticvoidAcceptsDateTime(DateTimev){}
375386

376387
classValueErrorWrapper:Exception
@@ -381,7 +392,8 @@ public ValueErrorWrapper(string message) : base(message) { }
381392
classValueErrorCodec:IPyObjectEncoder,IPyObjectDecoder
382393
{
383394
publicboolCanDecode(PyObjectobjectType,TypetargetType)
384-
=>this.CanEncode(targetType)&&objectType.Equals(PythonEngine.Eval("ValueError"));
395+
=>this.CanEncode(targetType)
396+
&&PythonReferenceComparer.Instance.Equals(objectType,PythonEngine.Eval("ValueError"));
385397

386398
publicboolCanEncode(Typetype)=>type==typeof(ValueErrorWrapper)
387399
||typeof(ValueErrorWrapper).IsSubclassOf(type);
@@ -399,6 +411,26 @@ public PyObject TryEncode(object value)
399411
returnPythonEngine.Eval("ValueError").Invoke(error.Message.ToPython());
400412
}
401413
}
414+
415+
classInstancelessExceptionDecoder:IPyObjectDecoder
416+
{
417+
readonlyPyObjectPyErr=Py.Import("clr.interop").GetAttr("PyErr");
418+
419+
publicboolCanDecode(PyObjectobjectType,TypetargetType)
420+
=>PythonReferenceComparer.Instance.Equals(PyErr,objectType);
421+
422+
publicboolTryDecode<T>(PyObjectpyObj,outTvalue)
423+
{
424+
if(pyObj.HasAttr("value"))
425+
{
426+
value=default;
427+
returnfalse;
428+
}
429+
430+
value=(T)(object)newValueErrorWrapper(TestExceptionMessage);
431+
returntrue;
432+
}
433+
}
402434
}
403435

404436
/// <summary>

‎src/runtime/Python.Runtime.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@
4343
<EmbeddedResourceInclude="resources\clr.py">
4444
<LogicalName>clr.py</LogicalName>
4545
</EmbeddedResource>
46+
<EmbeddedResourceInclude="resources\interop.py">
47+
<LogicalName>interop.py</LogicalName>
48+
</EmbeddedResource>
4649
</ItemGroup>
4750

4851
<ItemGroup>

‎src/runtime/Util.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,17 @@ internal static void WriteCLong(IntPtr type, int offset, Int64 flags)
4141
/// </summary>
4242
internalstaticIntPtrCoalesce(thisIntPtrprimary,IntPtrfallback)
4343
=>primary==IntPtr.Zero?fallback:primary;
44+
45+
/// <summary>
46+
/// Gets substring after last occurrence of <paramref name="symbol"/>
47+
/// </summary>
48+
internalstaticstringAfterLast(thisstringstr,charsymbol)
49+
{
50+
if(strisnull)
51+
thrownewArgumentNullException(nameof(str));
52+
53+
intlast=str.LastIndexOf(symbol);
54+
returnlast>=0?str.Substring(last+1):null;
55+
}
4456
}
4557
}

‎src/runtime/pythonengine.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
usingSystem;
22
usingSystem.Collections.Generic;
3+
usingSystem.Diagnostics;
34
usingSystem.IO;
45
usingSystem.Linq;
56
usingSystem.Reflection;
@@ -237,6 +238,8 @@ public static void Initialize(IEnumerable<string> args, bool setSysArgv = true,
237238
Exec(clr_py,module_globals,locals.Reference);
238239
}
239240

241+
LoadSubmodule(module_globals,"clr.interop","interop.py");
242+
240243
// add the imported module to the clr module, and copy the API functions
241244
// and decorators into the main clr module.
242245
Runtime.PyDict_SetItemString(clr_dict,"_extras",module);
@@ -258,6 +261,32 @@ public static void Initialize(IEnumerable<string> args, bool setSysArgv = true,
258261
}
259262
}
260263

264+
staticBorrowedReferenceDefineModule(stringname)
265+
{
266+
varmodule=Runtime.PyImport_AddModule(name);
267+
varmodule_globals=Runtime.PyModule_GetDict(module);
268+
varbuiltins=Runtime.PyEval_GetBuiltins();
269+
Runtime.PyDict_SetItemString(module_globals,"__builtins__",builtins);
270+
returnmodule;
271+
}
272+
273+
staticvoidLoadSubmodule(BorrowedReferencetargetModuleDict,stringfullName,stringresourceName)
274+
{
275+
stringmemberName=fullName.AfterLast('.');
276+
Debug.Assert(memberName!=null);
277+
Assemblyassembly=Assembly.GetExecutingAssembly();
278+
varmodule=DefineModule(fullName);
279+
varmodule_globals=Runtime.PyModule_GetDict(module);
280+
using(varstream=assembly.GetManifestResourceStream(resourceName))
281+
using(varreader=newStreamReader(stream))
282+
{
283+
stringpyCode=reader.ReadToEnd();
284+
Exec(pyCode,module_globals.DangerousGetAddress(),module_globals.DangerousGetAddress());
285+
}
286+
287+
Runtime.PyDict_SetItemString(targetModuleDict,memberName,module);
288+
}
289+
261290
staticvoidOnDomainUnload(object_,EventArgs__)
262291
{
263292
Shutdown();

‎src/runtime/pythonexception.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ public PythonException(PyType type, PyObject? value, PyObject? traceback)
3737
/// </summary>
3838
internalstaticExceptionThrowLastAsClrException()
3939
{
40+
// prevent potential interop errors in this method
41+
// from crashing process with undebuggable StackOverflowException
42+
RuntimeHelpers.EnsureSufficientExecutionStack();
43+
4044
varexception=FetchCurrentOrNull(outExceptionDispatchInfo?dispatchInfo)
4145
??thrownewInvalidOperationException("No exception is set");
4246
dispatchInfo?.Throw();
@@ -83,6 +87,24 @@ internal static PythonException FetchCurrentRaw()
8387
returnnull;
8488
}
8589

90+
try
91+
{
92+
if(TryDecodePyErr(type,value,traceback)is{}pyErr)
93+
{
94+
type.Dispose();
95+
value.Dispose();
96+
traceback.Dispose();
97+
returnpyErr;
98+
}
99+
}
100+
catch
101+
{
102+
type.Dispose();
103+
value.Dispose();
104+
traceback.Dispose();
105+
throw;
106+
}
107+
86108
Runtime.PyErr_NormalizeException(type:reftype,val:refvalue,tb:reftraceback);
87109

88110
try
@@ -148,6 +170,11 @@ private static Exception FromPyErr(BorrowedReference typeRef, BorrowedReference
148170
returnexceptionDispatchInfo.SourceException;
149171
}
150172

173+
if(TryDecodePyErr(typeRef,valRef,tbRef)is{}pyErr)
174+
{
175+
returnpyErr;
176+
}
177+
151178
if(ManagedType.GetManagedObject(valRef)isCLRObject{inst:Exceptione})
152179
{
153180
returne;
@@ -164,6 +191,28 @@ private static Exception FromPyErr(BorrowedReference typeRef, BorrowedReference
164191
returnnewPythonException(type,value,traceback,inner);
165192
}
166193

194+
privatestaticException?TryDecodePyErr(BorrowedReferencetypeRef,BorrowedReferencevalRef,BorrowedReferencetbRef)
195+
{
196+
usingvartype=PyType.FromReference(typeRef);
197+
usingvarvalue=PyObject.FromNullableReference(valRef);
198+
usingvartraceback=PyObject.FromNullableReference(tbRef);
199+
200+
usingvarerrorDict=newPyDict();
201+
if(typeRef!=null)errorDict["type"]=type;
202+
if(valRef!=null)errorDict["value"]=value;
203+
if(tbRef!=null)errorDict["traceback"]=traceback;
204+
205+
usingvarpyErrType=Runtime.InteropModule.GetAttr("PyErr");
206+
usingvarpyErrInfo=pyErrType.Invoke(newPyTuple(),errorDict);
207+
if(PyObjectConversions.TryDecode(pyErrInfo.Reference,pyErrType.Reference,
208+
typeof(Exception),outobjectdecoded)&&decodedisExceptiondecodedPyErrInfo)
209+
{
210+
returndecodedPyErrInfo;
211+
}
212+
213+
returnnull;
214+
}
215+
167216
privatestaticException?FromCause(BorrowedReferencecause)
168217
{
169218
if(cause==null||cause.IsNone())returnnull;

‎src/runtime/resources/interop.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
_UNSET=object()
2+
3+
classPyErr:
4+
def__init__(self,type=_UNSET,value=_UNSET,traceback=_UNSET):
5+
ifnot(typeis_UNSET):
6+
self.type=type
7+
ifnot(valueis_UNSET):
8+
self.value=value
9+
ifnot(tracebackis_UNSET):
10+
self.traceback=traceback

‎src/runtime/runtime.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
178178
}
179179
XDecref(item);
180180
AssemblyManager.UpdatePath();
181+
182+
clrInterop=GetModuleLazy("clr.interop");
181183
}
182184

183185
privatestaticvoidInitPyMembers()
@@ -406,6 +408,11 @@ internal static void Shutdown()
406408
Shutdown(mode);
407409
}
408410

411+
privatestaticLazy<PyObject>GetModuleLazy(stringmoduleName)
412+
=>moduleNameisnull
413+
?thrownewArgumentNullException(nameof(moduleName))
414+
:newLazy<PyObject>(()=>PyModule.Import(moduleName),isThreadSafe:false);
415+
409416
internalstaticShutdownModeGetDefaultShutdownMode()
410417
{
411418
stringmodeEvn=Environment.GetEnvironmentVariable("PYTHONNET_SHUTDOWN_MODE");
@@ -574,6 +581,9 @@ private static void MoveClrInstancesOnwershipToPython()
574581
internalstaticIntPtrPyNone;
575582
internalstaticIntPtrError;
576583

584+
privatestaticLazy<PyObject>clrInterop;
585+
internalstaticPyObjectInteropModule=>clrInterop.Value;
586+
577587
internalstaticBorrowedReferenceCLRMetaType=>newBorrowedReference(PyCLRMetaType);
578588

579589
publicstaticPyObjectNone
@@ -2038,7 +2048,7 @@ internal static bool PyType_IsSameAsOrSubtype(BorrowedReference type, BorrowedRe
20382048
internalstaticNewReferencePyType_FromSpecWithBases(inNativeTypeSpecspec,BorrowedReferencebases)=>Delegates.PyType_FromSpecWithBases(inspec,bases);
20392049

20402050
/// <summary>
2041-
/// Finalize a type object. This should be called on all type objects to finish their initialization. This function is responsible for adding inherited slots from a types base class. Return 0 on success, or return -1 and sets an exception on error.
2051+
/// Finalize a type object. This should be called on all type objects to finish their initialization. This function is responsible for adding inherited slots from a types base class. Return 0 on success, or return -1 and sets an exception on error.
20422052
/// </summary>
20432053

20442054
internalstaticintPyType_Ready(IntPtrtype)=>Delegates.PyType_Ready(type);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp