aboutsummaryrefslogtreecommitdiffstats
diff options
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2025-04-17 14:49:57 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2025-05-07 21:10:45 +0200
commitd8ca812879ad70cd2aa914ea76cd529786deadc6 (patch)
tree97140467bca8c9bcda6f492ecb8598c9db136179
parentcffe2bc71d8e5d88efb879e6fe68c730e547fc4d (diff)
libshiboken: Add utility class for stashing Python errors
It encapsulates fetching/restoring errors and uses the old or new exception API depending on version. Task-number: PYSIDE-3067 Change-Id: I6e39d92c7e79fed864b364a90c5bd5b474a41ed6 Reviewed-by: Christian Tismer <tismer@stackless.com>
-rw-r--r--sources/pyside6/PySide6/QtCore/typesystem_core_common.xml1
-rw-r--r--sources/pyside6/PySide6/glue/qtcore.cpp6
-rw-r--r--sources/pyside6/libpyside/pyside.cpp13
-rw-r--r--sources/pyside6/libpyside/pysidesignal.cpp7
-rw-r--r--sources/pyside6/libpyside/signalmanager.cpp11
-rw-r--r--sources/pyside6/libpysideqml/pysideqmlmetacallerror.cpp15
-rw-r--r--sources/pyside6/libpysideremoteobjects/pysiderephandler.cpp13
-rw-r--r--sources/shiboken6/libshiboken/basewrapper.cpp8
-rw-r--r--sources/shiboken6/libshiboken/sbkerrors.cpp42
-rw-r--r--sources/shiboken6/libshiboken/sbkerrors.h28
-rw-r--r--sources/shiboken6/libshiboken/sbkfeature_base.cpp20
11 files changed, 106 insertions, 58 deletions
diff --git a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
index e0d711313..207844c56 100644
--- a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
+++ b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
@@ -17,6 +17,7 @@
<include file-name="pysidemetatype.h" location="global"/>
<include file-name="pysideutils.h" location="global"/> <!-- QString conversion -->
<include file-name="signalmanager.h" location="global"/>
+ <include file-name="sbkerrors.h" location="global"/>
<!-- QtCoreHelper::QGenericReturnArgumentHolder -->
<include file-name="qtcorehelper.h" location="local"/>
</extra-includes>
diff --git a/sources/pyside6/PySide6/glue/qtcore.cpp b/sources/pyside6/PySide6/glue/qtcore.cpp
index 689946652..78a25f0a1 100644
--- a/sources/pyside6/PySide6/glue/qtcore.cpp
+++ b/sources/pyside6/PySide6/glue/qtcore.cpp
@@ -433,10 +433,7 @@ static PyObject *qtmsghandler = nullptr;
static void msgHandlerCallback(QtMsgType type, const QMessageLogContext &ctx, const QString &msg)
{
Shiboken::GilState state;
- PyObject *excType{};
- PyObject *excValue{};
- PyObject *excTraceback{};
- PyErr_Fetch(&excType, &excValue, &excTraceback);
+ Shiboken::Errors::Stash errorStash;
Shiboken::AutoDecRef arglist(PyTuple_New(3));
PyTuple_SetItem(arglist, 0, %CONVERTTOPYTHON[QtMsgType](type));
PyTuple_SetItem(arglist, 1, %CONVERTTOPYTHON[QMessageLogContext &](ctx));
@@ -444,7 +441,6 @@ static void msgHandlerCallback(QtMsgType type, const QMessageLogContext &ctx, co
const char *data = array.constData();
PyTuple_SetItem(arglist, 2, %CONVERTTOPYTHON[const char *](data));
Shiboken::AutoDecRef ret(PyObject_CallObject(qtmsghandler, arglist));
- PyErr_Restore(excType, excValue, excTraceback);
}
// @snippet qt-messagehandler
diff --git a/sources/pyside6/libpyside/pyside.cpp b/sources/pyside6/libpyside/pyside.cpp
index 195c000dc..261b2fe77 100644
--- a/sources/pyside6/libpyside/pyside.cpp
+++ b/sources/pyside6/libpyside/pyside.cpp
@@ -30,6 +30,7 @@
#include <gilstate.h>
#include <helper.h>
#include <sbkconverter.h>
+#include <sbkerrors.h>
#include <sbkstring.h>
#include <sbkstaticstrings.h>
#include <sbkfeature_base.h>
@@ -595,10 +596,7 @@ PyObject *getHiddenDataFromQObject(QObject *cppSelf, PyObject *self, PyObject *n
// Search on metaobject (avoid internal attributes started with '__')
if (!attr) {
- PyObject *type{};
- PyObject *value{};
- PyObject *traceback{};
- PyErr_Fetch(&type, &value, &traceback); // This was omitted for a loong time.
+ Shiboken::Errors::Stash errorStash;
int flags = currentSelectId(Py_TYPE(self));
int snake_flag = flags & 0x01;
@@ -623,8 +621,10 @@ PyObject *getHiddenDataFromQObject(QObject *cppSelf, PyObject *self, PyObject *n
if (res) {
AutoDecRef elemName(PyObject_GetAttr(res, PySideMagicName::name()));
// Note: This comparison works because of interned strings.
- if (elemName == name)
+ if (elemName == name) {
+ errorStash.release();
return res;
+ }
Py_DECREF(res);
}
PyErr_Clear();
@@ -655,6 +655,7 @@ PyObject *getHiddenDataFromQObject(QObject *cppSelf, PyObject *self, PyObject *n
} else if (auto *func = MetaFunction::newObject(cppSelf, i)) {
auto *result = reinterpret_cast<PyObject *>(func);
PyObject_SetAttr(self, name, result);
+ errorStash.release();
return result;
}
}
@@ -663,10 +664,10 @@ PyObject *getHiddenDataFromQObject(QObject *cppSelf, PyObject *self, PyObject *n
auto *pySignal = reinterpret_cast<PyObject *>(
Signal::newObjectFromMethod(cppSelf, self, signalList));
PyObject_SetAttr(self, name, pySignal);
+ errorStash.release();
return pySignal;
}
}
- PyErr_Restore(type, value, traceback);
}
return attr;
}
diff --git a/sources/pyside6/libpyside/pysidesignal.cpp b/sources/pyside6/libpyside/pysidesignal.cpp
index 5058e3517..a4d1b66b5 100644
--- a/sources/pyside6/libpyside/pysidesignal.cpp
+++ b/sources/pyside6/libpyside/pysidesignal.cpp
@@ -16,6 +16,7 @@
#include <pep384ext.h>
#include <sbkconverter.h>
#include <sbkenum.h>
+#include <sbkerrors.h>
#include <sbkstaticstrings.h>
#include <sbkstring.h>
#include <sbktypefactory.h>
@@ -667,13 +668,9 @@ static PyObject *signalInstanceGetItem(PyObject *self, PyObject *key)
static inline void warnDisconnectFailed(PyObject *aSlot, const QByteArray &signature)
{
if (PyErr_Occurred() != nullptr) { // avoid "%S" invoking str() when an error is set.
- PyObject *exc{};
- PyObject *inst{};
- PyObject *tb{};
- PyErr_Fetch(&exc, &inst, &tb);
+ Shiboken::Errors::Stash errorStash;
PyErr_WarnFormat(PyExc_RuntimeWarning, 0, "Failed to disconnect (%s) from signal \"%s\".",
Py_TYPE(aSlot)->tp_name, signature.constData());
- PyErr_Restore(exc, inst, tb);
} else {
PyErr_WarnFormat(PyExc_RuntimeWarning, 0, "Failed to disconnect (%S) from signal \"%s\".",
aSlot, signature.constData());
diff --git a/sources/pyside6/libpyside/signalmanager.cpp b/sources/pyside6/libpyside/signalmanager.cpp
index 342737c1b..933edd318 100644
--- a/sources/pyside6/libpyside/signalmanager.cpp
+++ b/sources/pyside6/libpyside/signalmanager.cpp
@@ -347,20 +347,15 @@ int SignalManagerPrivate::qtPropertyMetacall(QObject *object,
if (PyErr_Occurred()) {
// PYSIDE-2160: An unknown type was reported. Indicated by StopIteration.
if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
- PyObject *excType{};
- PyObject *excValue{};
- PyObject *excTraceback{};
- PyErr_Fetch(&excType, &excValue, &excTraceback);
+ Shiboken::Errors::Stash errorStash;
bool ign = call == QMetaObject::WriteProperty;
PyErr_WarnFormat(PyExc_RuntimeWarning, 0,
ign ? "Unknown property type '%s' of QObject '%s' used in fset"
: "Unknown property type '%s' of QObject '%s' used in fget with %R",
- pp->d->typeName.constData(), metaObject->className(), excValue);
+ pp->d->typeName.constData(), metaObject->className(), errorStash.getException());
if (PyErr_Occurred())
Shiboken::Errors::storeErrorOrPrint();
- Py_DECREF(excType);
- Py_DECREF(excValue);
- Py_XDECREF(excTraceback);
+ errorStash.release();
return result;
}
diff --git a/sources/pyside6/libpysideqml/pysideqmlmetacallerror.cpp b/sources/pyside6/libpysideqml/pysideqmlmetacallerror.cpp
index a3d2664c4..4e0afa3b2 100644
--- a/sources/pyside6/libpysideqml/pysideqmlmetacallerror.cpp
+++ b/sources/pyside6/libpysideqml/pysideqmlmetacallerror.cpp
@@ -5,6 +5,7 @@
#include <sbkpython.h>
#include <sbkstring.h>
+#include <sbkerrors.h>
#include <autodecref.h>
// Remove deprecated MACRO of copysign for MSVC #86286
@@ -40,17 +41,17 @@ std::optional<int> qmlMetaCallErrorHandler(QObject *object)
if (engine->currentStackFrame == nullptr)
return {};
- PyObject *errType{};
- PyObject *errValue{};
- PyObject *errTraceback{};
- PyErr_Fetch(&errType, &errValue, &errTraceback);
+ Shiboken::Errors::Stash errorStash;
+ PyObject *errValue = errorStash.getException();
// PYSIDE-464: The error is only valid before PyErr_Restore,
// PYSIDE-464: therefore we take local copies.
Shiboken::AutoDecRef objStr(PyObject_Str(errValue));
const QString errString = QString::fromUtf8(Shiboken::String::toCString(objStr));
- const bool isSyntaxError = errType == PyExc_SyntaxError;
- const bool isTypeError = errType == PyExc_TypeError;
- PyErr_Restore(errType, errValue, errTraceback);
+ const bool isSyntaxError = errValue != nullptr
+ && PyErr_GivenExceptionMatches(errValue, PyExc_SyntaxError);
+ const bool isTypeError = errValue != nullptr
+ && PyErr_GivenExceptionMatches(errValue, PyExc_TypeError);
+ errorStash.restore();
PyErr_Print(); // Note: PyErr_Print clears the error.
diff --git a/sources/pyside6/libpysideremoteobjects/pysiderephandler.cpp b/sources/pyside6/libpysideremoteobjects/pysiderephandler.cpp
index bfe085456..aa59c329f 100644
--- a/sources/pyside6/libpysideremoteobjects/pysiderephandler.cpp
+++ b/sources/pyside6/libpysideremoteobjects/pysiderephandler.cpp
@@ -7,6 +7,7 @@
#include "pysidedynamiccommon_p.h"
#include <pep384ext.h>
+#include <sbkerrors.h>
#include <sbkstring.h>
#include <sbktypefactory.h>
#include <signature.h>
@@ -372,17 +373,11 @@ bool instantiateFromDefaultValue(QVariant &variant, const QString &defaultValue)
PyObject *pyResult = PyRun_String(code.c_str(), Py_eval_input, pyLocals, pyLocals);
if (!pyResult) {
- PyObject *ptype = nullptr;
- PyObject *pvalue = nullptr;
- PyObject *ptraceback = nullptr;
- PyErr_Fetch(&ptype, &pvalue, &ptraceback);
- PyErr_NormalizeException(&ptype, &pvalue, &ptraceback);
+ Shiboken::Errors::Stash errorStash;
PyErr_Format(PyExc_TypeError,
"Failed to generate default value. Error: %s. Problematic code: %s",
- Shiboken::String::toCString(PyObject_Str(pvalue)), code.c_str());
- Py_XDECREF(ptype);
- Py_XDECREF(pvalue);
- Py_XDECREF(ptraceback);
+ Shiboken::String::toCString(PyObject_Str(errorStash.getException())), code.c_str());
+ errorStash.release();
Py_DECREF(pyLocals);
return false;
}
diff --git a/sources/shiboken6/libshiboken/basewrapper.cpp b/sources/shiboken6/libshiboken/basewrapper.cpp
index 25f6ea7c8..a65359a1e 100644
--- a/sources/shiboken6/libshiboken/basewrapper.cpp
+++ b/sources/shiboken6/libshiboken/basewrapper.cpp
@@ -415,12 +415,8 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete)
}
}
- PyObject *error_type{};
- PyObject *error_value{};
- PyObject *error_traceback{};
-
/* Save the current exception, if any. */
- PyErr_Fetch(&error_type, &error_value, &error_traceback);
+ Shiboken::Errors::Stash errorStash;
if (canDelete) {
if (sotp->is_multicpp) {
@@ -441,7 +437,7 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete)
}
/* Restore the saved exception. */
- PyErr_Restore(error_type, error_value, error_traceback);
+ errorStash.restore();
if (needTypeDecref)
Py_DECREF(pyType);
diff --git a/sources/shiboken6/libshiboken/sbkerrors.cpp b/sources/shiboken6/libshiboken/sbkerrors.cpp
index 247fbefc4..6b0600082 100644
--- a/sources/shiboken6/libshiboken/sbkerrors.cpp
+++ b/sources/shiboken6/libshiboken/sbkerrors.cpp
@@ -164,6 +164,17 @@ static void restoreError(ErrorStore &s)
#endif
}
+static void releaseError(ErrorStore &s)
+{
+ Py_XDECREF(s.exc);
+ s.exc = nullptr;
+#ifdef PEP_OLD_ERR_API
+ Py_XDECREF(s.type);
+ Py_XDECREF(s.traceback);
+ s.type = s.traceback = nullptr;
+#endif
+}
+
static thread_local ErrorStore savedError;
static bool hasPythonContext()
@@ -209,6 +220,37 @@ PyObject *occurred()
return PyErr_Occurred();
}
+Stash::Stash() : m_store(std::make_unique<ErrorStore>())
+{
+ fetchError(*m_store);
+}
+
+Stash::~Stash()
+{
+ restore();
+}
+
+PyObject *Stash::getException() const
+{
+ return m_store ? m_store->exc : nullptr;
+}
+
+void Stash::restore()
+{
+ if (m_store) {
+ restoreError(*m_store);
+ m_store.reset();
+ }
+}
+
+void Stash::release()
+{
+ if (m_store) {
+ releaseError(*m_store);
+ m_store.reset();
+ }
+}
+
} // namespace Errors
namespace Warnings
diff --git a/sources/shiboken6/libshiboken/sbkerrors.h b/sources/shiboken6/libshiboken/sbkerrors.h
index d7247ded4..58576dc7b 100644
--- a/sources/shiboken6/libshiboken/sbkerrors.h
+++ b/sources/shiboken6/libshiboken/sbkerrors.h
@@ -7,6 +7,8 @@
#include "sbkpython.h"
#include "shibokenmacros.h"
+#include <memory>
+
/// Craving for C++20 and std::source_location::current()
#if defined(_MSC_VER)
# define SBK_FUNC_INFO __FUNCSIG__
@@ -35,6 +37,32 @@ public:
namespace Errors
{
+struct ErrorStore;
+
+/// Temporarily stash an error set in Python
+class Stash
+{
+public:
+ Stash(const Stash &) = delete;
+ Stash &operator=(const Stash &) = delete;
+ Stash(Stash &&) = delete;
+ Stash &operator=(Stash &&) = delete;
+
+ LIBSHIBOKEN_API Stash();
+ LIBSHIBOKEN_API ~Stash();
+
+ LIBSHIBOKEN_API operator bool() const { return getException() != nullptr; }
+ [[nodiscard]] LIBSHIBOKEN_API PyObject *getException() const;
+
+ /// Restore the stored error
+ LIBSHIBOKEN_API void restore();
+ /// Discard the stored error
+ LIBSHIBOKEN_API void release();
+
+private:
+ std::unique_ptr<ErrorStore> m_store;
+};
+
LIBSHIBOKEN_API void setIndexOutOfBounds(Py_ssize_t value, Py_ssize_t minValue,
Py_ssize_t maxValue);
LIBSHIBOKEN_API void setInstantiateAbstractClass(const char *name);
diff --git a/sources/shiboken6/libshiboken/sbkfeature_base.cpp b/sources/shiboken6/libshiboken/sbkfeature_base.cpp
index fe32d8ca2..9044539c6 100644
--- a/sources/shiboken6/libshiboken/sbkfeature_base.cpp
+++ b/sources/shiboken6/libshiboken/sbkfeature_base.cpp
@@ -6,6 +6,7 @@
#include "autodecref.h"
#include "pep384ext.h"
#include "sbkenum.h"
+#include "sbkerrors.h"
#include "sbkstring.h"
#include "sbkstaticstrings.h"
#include "sbkstaticstrings_p.h"
@@ -60,8 +61,8 @@ SelectableFeatureHook initSelectableFeature(SelectableFeatureHook func)
void disassembleFrame(const char *marker)
{
Shiboken::GilState gil;
- PyObject *error_type, *error_value, *error_traceback;
- PyErr_Fetch(&error_type, &error_value, &error_traceback);
+
+ Shiboken::Errors::Stash errorStash;
static PyObject *dismodule = PyImport_ImportModule("dis");
static PyObject *disco = PyObject_GetAttrString(dismodule, "disco");
static PyObject *const _f_lasti = Shiboken::String::createStaticString("f_lasti");
@@ -84,12 +85,11 @@ void disassembleFrame(const char *marker)
fprintf(stdout, "%s END line=%ld %s\n\n", marker, line, fname);
}
#if PY_VERSION_HEX >= 0x030C0000 && !Py_LIMITED_API
- if (error_type)
- PyErr_DisplayException(error_value);
+ if (auto *exc = errorStash.getException())
+ PyErr_DisplayException(exc);
#endif
static PyObject *stdout_file = PySys_GetObject("stdout");
ignore.reset(PyObject_CallMethod(stdout_file, "flush", nullptr));
- PyErr_Restore(error_type, error_value, error_traceback);
}
// Python 3.13
@@ -361,15 +361,11 @@ PyObject *mangled_type_getattro(PyTypeObject *type, PyObject *name)
}
if (!ret && name != ignAttr1 && name != ignAttr2) {
- PyObject *error_type{}, *error_value{}, *error_traceback{};
- PyErr_Fetch(&error_type, &error_value, &error_traceback);
+ Shiboken::Errors::Stash errorsStash;
ret = lookupUnqualifiedOrOldEnum(type, name);
if (ret) {
- Py_DECREF(error_type);
- Py_XDECREF(error_value);
- Py_XDECREF(error_traceback);
- } else {
- PyErr_Restore(error_type, error_value, error_traceback);
+ errorsStash.release();
+ return ret;
}
}
return ret;