Skip to content

Commit c96d215

Browse files
authored
python 修复小于 3.12 版本下的异常处理 调整异常格式化逻辑 并精简异常信息 (Tencent#2233)
* python 修复小于 3.12 版本下的异常处理 调整异常格式化逻辑 并精简异常信息 * python 调整 ThrowInFunction 用例
1 parent 5020e28 commit c96d215

File tree

4 files changed

+112
-38
lines changed

4 files changed

+112
-38
lines changed

unity/native/papi-python/include/PapiData.h

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,25 @@ struct pesapi_value_ref__ : pesapi_env_ref__
5858

5959
struct caught_exception_info
6060
{
61-
PyObject* ex;
61+
#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 12
62+
PyObject* value;
63+
#else
64+
PyObject* type;
65+
PyObject* value;
66+
PyObject* traceback;
67+
#endif
68+
~caught_exception_info()
69+
{
70+
#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 12
71+
// From PyErr_GetRaisedException
72+
Py_XDECREF(value);
73+
#else
74+
// From PyErr_Fetch
75+
Py_XDECREF(type);
76+
Py_XDECREF(value);
77+
Py_XDECREF(traceback);
78+
#endif
79+
}
6280
};
6381

6482
struct pesapi_scope__;
@@ -110,20 +128,34 @@ struct pesapi_scope__
110128
return ret;
111129
}
112130

131+
#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 12
132+
// From PyErr_GetRaisedException
113133
void setCaughtException(PyObject* ex)
114134
{
115135
if (caught == nullptr)
116136
{
117137
caught = (caught_exception_info*) PyMem_Malloc(sizeof(caught_exception_info));
118138
}
119-
caught->ex = ex;
139+
caught->value = ex;
140+
}
141+
#else
142+
// From PyErr_Fetch
143+
void setCaughtException(PyObject * type, PyObject * value, PyObject * traceback)
144+
{
145+
if (caught == nullptr)
146+
{
147+
caught = (caught_exception_info*) PyMem_Malloc(sizeof(caught_exception_info));
148+
}
149+
caught->type = type;
150+
caught->value = value;
151+
caught->traceback = traceback;
120152
}
153+
#endif
121154

122155
~pesapi_scope__()
123156
{
124157
if (caught)
125158
{
126-
Py_XDECREF(caught->ex);
127159
caught->~caught_exception_info();
128160
PyMem_Free(caught);
129161
}

unity/native/papi-python/source/PapiPythonImpl.cpp

Lines changed: 75 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -602,25 +602,79 @@ const char* pesapi_get_exception_as_string(pesapi_scope pscope, int with_stack)
602602
{
603603
return nullptr;
604604
}
605-
auto ex = scope->caught->ex;
606-
auto globals = PyModule_GetDict(PyImport_AddModule("__main__"));
607-
PyDict_SetItem(globals, PyUnicode_FromString("__pesapi_last_exception"), ex);
608-
const char* ret;
605+
606+
const char* ret = nullptr;
609607
if (with_stack)
610608
{
611-
PyRun_SimpleString(
612-
"import traceback\n"
613-
"try:\n"
614-
" raise __pesapi_last_exception\n"
615-
"except Exception as e:\n"
616-
" __pesapi_last_exception_str = ''.join(traceback.format_exception(type(e), e, e.__traceback__))\n");
617-
ret = PyUnicode_AsUTF8(PyDict_GetItem(globals, PyUnicode_FromString("__pesapi_last_exception_str")));
609+
#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 12
610+
auto tracebackModule = PyImport_ImportModule("traceback");
611+
auto value = scope->caught->value;
612+
auto type = Py_TYPE(value);
613+
auto traceback = PyObject_GetAttrString(value, "__traceback__");
614+
if (traceback && traceback != Py_None)
615+
{
616+
if (PyObject* formatExceptionFunc = PyObject_GetAttrString(tracebackModule, "format_exception");
617+
formatExceptionFunc && PyCallable_Check(formatExceptionFunc))
618+
{
619+
PyObject* formattedList = PyObject_CallFunctionObjArgs(formatExceptionFunc, type, value, traceback, nullptr);
620+
auto formattedStr = PyUnicode_Join(PyUnicode_FromString(""), formattedList);
621+
ret = strdup(PyUnicode_AsUTF8(formattedStr));
622+
Py_DECREF(formattedStr);
623+
Py_DECREF(formattedList);
624+
Py_DECREF(formatExceptionFunc);
625+
}
626+
}
627+
else
628+
{
629+
if (PyObject* formatExceptionOnlyFunc = PyObject_GetAttrString(tracebackModule, "format_exception_only");
630+
formatExceptionOnlyFunc && PyCallable_Check(formatExceptionOnlyFunc))
631+
{
632+
PyObject* formattedList = PyObject_CallFunctionObjArgs(formatExceptionOnlyFunc, type, value, nullptr);
633+
auto formattedStr = PyUnicode_Join(PyUnicode_FromString(""), formattedList);
634+
ret = strdup(PyUnicode_AsUTF8(formattedStr));
635+
Py_DECREF(formattedStr);
636+
Py_DECREF(formattedList);
637+
Py_DECREF(formatExceptionOnlyFunc);
638+
}
639+
}
640+
#else
641+
auto tracebackModule = PyImport_ImportModule("traceback");
642+
PyErr_NormalizeException(&scope->caught->type, &scope->caught->value, &scope->caught->traceback);
643+
auto traceback = scope->caught->traceback;
644+
auto type = scope->caught->type;
645+
auto value = scope->caught->value;
646+
if (traceback)
647+
{
648+
if (PyObject* formatExceptionFunc = PyObject_GetAttrString(tracebackModule, "format_exception");
649+
formatExceptionFunc && PyCallable_Check(formatExceptionFunc))
650+
{
651+
PyObject* formattedList = PyObject_CallFunctionObjArgs(formatExceptionFunc, type, value, traceback, nullptr);
652+
auto formattedStr = PyUnicode_Join(PyUnicode_FromString(""), formattedList);
653+
ret = strdup(PyUnicode_AsUTF8(formattedStr));
654+
Py_DECREF(formattedStr);
655+
Py_DECREF(formattedList);
656+
Py_DECREF(formatExceptionFunc);
657+
}
658+
}
659+
else
660+
{
661+
if (PyObject* formatExceptionOnlyFunc = PyObject_GetAttrString(tracebackModule, "format_exception_only");
662+
formatExceptionOnlyFunc && PyCallable_Check(formatExceptionOnlyFunc))
663+
{
664+
PyObject* formattedList = PyObject_CallFunctionObjArgs(formatExceptionOnlyFunc, type, value, nullptr);
665+
auto formattedStr = PyUnicode_Join(PyUnicode_FromString(""), formattedList);
666+
ret = strdup(PyUnicode_AsUTF8(formattedStr));
667+
Py_DECREF(formattedStr);
668+
Py_DECREF(formattedList);
669+
Py_DECREF(formatExceptionOnlyFunc);
670+
}
671+
}
672+
#endif
618673
}
619674
else
620675
{
621-
ret = PyUnicode_AsUTF8(PyObject_Str(ex));
676+
ret = PyUnicode_AsUTF8(PyObject_Str(scope->caught->value));
622677
}
623-
PyDict_DelItemString(globals, "__pesapi_last_exception");
624678
return ret;
625679
}
626680

@@ -780,16 +834,13 @@ pesapi_value pesapi_call_function(
780834
else
781835
{
782836
auto scope = (pesapi_scope__*)mapper->getCurrentScope();
783-
#if PY_VERSION_HEX >= 0x030B0000
837+
#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 12
784838
scope->setCaughtException(PyErr_GetRaisedException());
785839
#else
786840
{
787-
PyObject *type = nullptr, *value = nullptr, *tb = nullptr;
788-
PyErr_Fetch(&type, &value, &tb);
789-
PyObject *exc = value ? value : type;
790-
if (exc) Py_XINCREF(exc);
791-
PyErr_Restore(type, value, tb);
792-
scope->setCaughtException(exc);
841+
PyObject *type = nullptr, *value = nullptr, *traceback = nullptr;
842+
PyErr_Fetch(&type, &value, &traceback);
843+
scope->setCaughtException(type, value, traceback);
793844
}
794845
#endif
795846
return nullptr;
@@ -816,16 +867,13 @@ pesapi_value pesapi_eval(pesapi_env env, const uint8_t* code, size_t code_size,
816867
}
817868
}
818869
auto scope = (pesapi_scope__*)mapper->getCurrentScope();
819-
#if PY_VERSION_HEX >= 0x030B0000
870+
#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 12
820871
scope->setCaughtException(PyErr_GetRaisedException());
821872
#else
822873
{
823-
PyObject *type = nullptr, *value = nullptr, *tb = nullptr;
824-
PyErr_Fetch(&type, &value, &tb);
825-
PyObject *exc = value ? value : type;
826-
if (exc) Py_XINCREF(exc);
827-
PyErr_Restore(type, value, tb);
828-
scope->setCaughtException(exc);
874+
PyObject *type = nullptr, *value = nullptr, *traceback = nullptr;
875+
PyErr_Fetch(&type, &value, &traceback);
876+
scope->setCaughtException(type, value, traceback);
829877
}
830878
#endif
831879
return nullptr;

unity/native/papi-python/test/papi_py_base_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ TEST_F(PApiBaseTest, EvalJavaScriptEx)
427427

428428
EXPECT_STREQ("abc", apis->get_exception_as_string(scope, false));
429429
EXPECT_STREQ(
430-
"Traceback (most recent call last):\n File \"<string>\", line 3, in <module>\n File \"test.py\", line 1, in <module>\n "
430+
"Traceback (most recent call last):\n File \"test.py\", line 1, in <module>\n "
431431
"File \"test.py\", line 1, in <lambda>\n File \"test.py\", line 1, in <genexpr>\nException: abc\n",
432432
apis->get_exception_as_string(scope, true));
433433
}

unity/test/Src/Cases/Python/API/EvalTest.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,7 @@ public void ThrowInFunction()
139139
var pythonEnv = new ScriptEnv(new BackendPython());
140140
Assert.Catch(() =>
141141
{
142-
var foo = pythonEnv.Eval<Action>(@"
143-
exec('''
144-
def foo():
145-
raise Exception('test error in function')
146-
''')
147-
foo
148-
");
142+
var foo = pythonEnv.Eval<Action>(@"lambda: (lambda: raise Exception('test error in function'))");
149143
foo();
150144
});
151145
pythonEnv.Dispose();

0 commit comments

Comments
 (0)