diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2025-04-17 14:49:57 +0200 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2025-05-07 21:10:45 +0200 |
commit | d8ca812879ad70cd2aa914ea76cd529786deadc6 (patch) | |
tree | 97140467bca8c9bcda6f492ecb8598c9db136179 | |
parent | cffe2bc71d8e5d88efb879e6fe68c730e547fc4d (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.xml | 1 | ||||
-rw-r--r-- | sources/pyside6/PySide6/glue/qtcore.cpp | 6 | ||||
-rw-r--r-- | sources/pyside6/libpyside/pyside.cpp | 13 | ||||
-rw-r--r-- | sources/pyside6/libpyside/pysidesignal.cpp | 7 | ||||
-rw-r--r-- | sources/pyside6/libpyside/signalmanager.cpp | 11 | ||||
-rw-r--r-- | sources/pyside6/libpysideqml/pysideqmlmetacallerror.cpp | 15 | ||||
-rw-r--r-- | sources/pyside6/libpysideremoteobjects/pysiderephandler.cpp | 13 | ||||
-rw-r--r-- | sources/shiboken6/libshiboken/basewrapper.cpp | 8 | ||||
-rw-r--r-- | sources/shiboken6/libshiboken/sbkerrors.cpp | 42 | ||||
-rw-r--r-- | sources/shiboken6/libshiboken/sbkerrors.h | 28 | ||||
-rw-r--r-- | sources/shiboken6/libshiboken/sbkfeature_base.cpp | 20 |
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; |