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

Commitbb54fc6

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 commitbb54fc6

File tree

7 files changed

+165
-31
lines changed

7 files changed

+165
-31
lines changed

‎src/embed_tests/Codecs.cs

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ static void TupleConversionsGeneric<T, TTuple>()
3131
TupleCodec<TTuple>.Register();
3232
vartuple=Activator.CreateInstance(typeof(T),42,"42",newobject());
3333
Trestored=default;
34-
using(Py.GIL())
3534
using(varscope=Py.CreateScope())
3635
{
3736
voidAccept(Tvalue)=>restored=value;
@@ -53,7 +52,6 @@ static void TupleConversionsObject<T, TTuple>()
5352
TupleCodec<TTuple>.Register();
5453
vartuple=Activator.CreateInstance(typeof(T),42,"42",newobject());
5554
Trestored=default;
56-
using(Py.GIL())
5755
using(varscope=Py.CreateScope())
5856
{
5957
voidAccept(objectvalue)=>restored=(T)value;
@@ -73,12 +71,9 @@ public void TupleRoundtripObject()
7371
staticvoidTupleRoundtripObject<T,TTuple>()
7472
{
7573
vartuple=Activator.CreateInstance(typeof(T),42,"42",newobject());
76-
using(Py.GIL())
77-
{
78-
varpyTuple=TupleCodec<TTuple>.Instance.TryEncode(tuple);
79-
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple,outobjectrestored));
80-
Assert.AreEqual(expected:tuple,actual:restored);
81-
}
74+
varpyTuple=TupleCodec<TTuple>.Instance.TryEncode(tuple);
75+
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple,outobjectrestored));
76+
Assert.AreEqual(expected:tuple,actual:restored);
8277
}
8378

8479
[Test]
@@ -90,21 +85,12 @@ public void TupleRoundtripGeneric()
9085
staticvoidTupleRoundtripGeneric<T,TTuple>()
9186
{
9287
vartuple=Activator.CreateInstance(typeof(T),42,"42",newobject());
93-
using(Py.GIL())
94-
{
95-
varpyTuple=TupleCodec<TTuple>.Instance.TryEncode(tuple);
96-
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple,outTrestored));
97-
Assert.AreEqual(expected:tuple,actual:restored);
98-
}
88+
varpyTuple=TupleCodec<TTuple>.Instance.TryEncode(tuple);
89+
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple,outTrestored));
90+
Assert.AreEqual(expected:tuple,actual:restored);
9991
}
10092

101-
staticPyObjectGetPythonIterable()
102-
{
103-
using(Py.GIL())
104-
{
105-
returnPythonEngine.Eval("map(lambda x: x, [1,2,3])");
106-
}
107-
}
93+
staticPyObjectGetPythonIterable()=>PythonEngine.Eval("map(lambda x: x, [1,2,3])");
10894

10995
[Test]
11096
publicvoidListDecoderTest()
@@ -330,7 +316,6 @@ public void ExceptionEncoded()
330316
PyObjectConversions.RegisterEncoder(newValueErrorCodec());
331317
voidCallMe()=>thrownewValueErrorWrapper(TestExceptionMessage);
332318
varcallMeAction=newAction(CallMe);
333-
usingvar_=Py.GIL();
334319
usingvarscope=Py.CreateScope();
335320
scope.Exec(@"
336321
def call(func):
@@ -348,7 +333,6 @@ def call(func):
348333
publicvoidExceptionDecoded()
349334
{
350335
PyObjectConversions.RegisterDecoder(newValueErrorCodec());
351-
usingvar_=Py.GIL();
352336
usingvarscope=Py.CreateScope();
353337
varerror=Assert.Throws<ValueErrorWrapper>(()
354338
=>PythonEngine.Exec($"raise ValueError('{TestExceptionMessage}')"));
@@ -371,6 +355,16 @@ from datetime import datetime
371355
scope.Exec("Codecs.AcceptsDateTime(datetime(2021, 1, 22))");
372356
}
373357

358+
[Test]
359+
publicvoidExceptionDecodedNoInstance()
360+
{
361+
PyObjectConversions.RegisterDecoder(newInstancelessExceptionDecoder());
362+
usingvarscope=Py.CreateScope();
363+
varerror=Assert.Throws<ValueErrorWrapper>(()=>PythonEngine.Exec(
364+
$"[].__iter__().__next__()"));
365+
Assert.AreEqual(TestExceptionMessage,error.Message);
366+
}
367+
374368
publicstaticvoidAcceptsDateTime(DateTimev){}
375369

376370
classValueErrorWrapper:Exception
@@ -381,7 +375,8 @@ public ValueErrorWrapper(string message) : base(message) { }
381375
classValueErrorCodec:IPyObjectEncoder,IPyObjectDecoder
382376
{
383377
publicboolCanDecode(PyObjectobjectType,TypetargetType)
384-
=>this.CanEncode(targetType)&&objectType.Equals(PythonEngine.Eval("ValueError"));
378+
=>this.CanEncode(targetType)
379+
&&PythonReferenceComparer.Instance.Equals(objectType,PythonEngine.Eval("ValueError"));
385380

386381
publicboolCanEncode(Typetype)=>type==typeof(ValueErrorWrapper)
387382
||typeof(ValueErrorWrapper).IsSubclassOf(type);
@@ -399,6 +394,26 @@ public PyObject TryEncode(object value)
399394
returnPythonEngine.Eval("ValueError").Invoke(error.Message.ToPython());
400395
}
401396
}
397+
398+
classInstancelessExceptionDecoder:IPyObjectDecoder
399+
{
400+
readonlyPyObjectPyErr=Py.Import("clr.interop").GetAttr("PyErr");
401+
402+
publicboolCanDecode(PyObjectobjectType,TypetargetType)
403+
=>PythonReferenceComparer.Instance.Equals(PyErr,objectType);
404+
405+
publicboolTryDecode<T>(PyObjectpyObj,outTvalue)
406+
{
407+
if(pyObj.HasAttr("value"))
408+
{
409+
value=default;
410+
returnfalse;
411+
}
412+
413+
value=(T)(object)newValueErrorWrapper(TestExceptionMessage);
414+
returntrue;
415+
}
416+
}
402417
}
403418

404419
/// <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: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
#nullable enable
12
usingSystem;
3+
usingSystem.IO;
24
usingSystem.Runtime.InteropServices;
35

46
namespacePython.Runtime
@@ -41,5 +43,27 @@ internal static void WriteCLong(IntPtr type, int offset, Int64 flags)
4143
/// </summary>
4244
internalstaticIntPtrCoalesce(thisIntPtrprimary,IntPtrfallback)
4345
=>primary==IntPtr.Zero?fallback:primary;
46+
47+
/// <summary>
48+
/// Gets substring after last occurrence of <paramref name="symbol"/>
49+
/// </summary>
50+
internalstaticstring?AfterLast(thisstringstr,charsymbol)
51+
{
52+
if(strisnull)
53+
thrownewArgumentNullException(nameof(str));
54+
55+
intlast=str.LastIndexOf(symbol);
56+
returnlast>=0?str.Substring(last+1):null;
57+
}
58+
59+
internalstaticstringReadStringResource(thisSystem.Reflection.Assemblyassembly,stringresourceName)
60+
{
61+
if(assemblyisnull)thrownewArgumentNullException(nameof(assembly));
62+
if(string.IsNullOrEmpty(resourceName))thrownewArgumentNullException(nameof(resourceName));
63+
64+
usingvarstream=assembly.GetManifestResourceStream(resourceName);
65+
usingvarreader=newStreamReader(stream);
66+
returnreader.ReadToEnd();
67+
}
4468
}
4569
}

‎src/runtime/pythonengine.cs

Lines changed: 30 additions & 7 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;
@@ -229,13 +230,11 @@ public static void Initialize(IEnumerable<string> args, bool setSysArgv = true,
229230
Runtime.PyDict_SetItemString(module_globals,"__builtins__",builtins);
230231

231232
Assemblyassembly=Assembly.GetExecutingAssembly();
232-
using(Streamstream=assembly.GetManifestResourceStream("clr.py"))
233-
using(varreader=newStreamReader(stream))
234-
{
235-
// add the contents of clr.py to the module
236-
stringclr_py=reader.ReadToEnd();
237-
Exec(clr_py,module_globals,locals.Reference);
238-
}
233+
// add the contents of clr.py to the module
234+
stringclr_py=assembly.ReadStringResource("clr.py");
235+
Exec(clr_py,module_globals,locals.Reference);
236+
237+
LoadSubmodule(module_globals,"clr.interop","interop.py");
239238

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

260+
staticBorrowedReferenceDefineModule(stringname)
261+
{
262+
varmodule=Runtime.PyImport_AddModule(name);
263+
varmodule_globals=Runtime.PyModule_GetDict(module);
264+
varbuiltins=Runtime.PyEval_GetBuiltins();
265+
Runtime.PyDict_SetItemString(module_globals,"__builtins__",builtins);
266+
returnmodule;
267+
}
268+
269+
staticvoidLoadSubmodule(BorrowedReferencetargetModuleDict,stringfullName,stringresourceName)
270+
{
271+
stringmemberName=fullName.AfterLast('.');
272+
Debug.Assert(memberName!=null);
273+
274+
varmodule=DefineModule(fullName);
275+
varmodule_globals=Runtime.PyModule_GetDict(module);
276+
277+
Assemblyassembly=Assembly.GetExecutingAssembly();
278+
stringpyCode=assembly.ReadStringResource(resourceName);
279+
Exec(pyCode,module_globals.DangerousGetAddress(),module_globals.DangerousGetAddress());
280+
281+
Runtime.PyDict_SetItemString(targetModuleDict,memberName,module);
282+
}
283+
261284
staticvoidOnDomainUnload(object_,EventArgs__)
262285
{
263286
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
@@ -153,6 +175,11 @@ private static Exception FromPyErr(BorrowedReference typeRef, BorrowedReference
153175
returne;
154176
}
155177

178+
if(TryDecodePyErr(typeRef,valRef,tbRef)is{}pyErr)
179+
{
180+
returnpyErr;
181+
}
182+
156183
if(PyObjectConversions.TryDecode(valRef,typeRef,typeof(Exception),outobjectdecoded)
157184
&&decodedisExceptiondecodedException)
158185
{
@@ -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: 10 additions & 0 deletions
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

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp