From 7d078ed0414da6a1e6ad7d2d0731a755faa487c3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 29 Jul 2022 23:58:19 +0200 Subject: [PATCH] Express argument details in Python call errors --- src/pya/pya/pya.cc | 33 +++++++++++++++++ src/pya/pya/pya.h | 25 ++++++++----- src/pya/pya/pyaConvert.cc | 20 +++++----- src/pya/pya/pyaModule.cc | 77 +++++++++++++++++++-------------------- 4 files changed, 97 insertions(+), 58 deletions(-) diff --git a/src/pya/pya/pya.cc b/src/pya/pya/pya.cc index 9a39c7704..3092eb8e4 100644 --- a/src/pya/pya/pya.cc +++ b/src/pya/pya/pya.cc @@ -38,6 +38,7 @@ #include "tlTimer.h" #include "tlFileUtils.h" #include "tlString.h" +#include "tlInternational.h" #if defined(HAVE_QT) # include @@ -51,6 +52,38 @@ namespace pya { +// -------------------------------------------------------------------------- +// PythonError implementation + +PythonError::PythonError (const char *msg, const char *cls, const std::vector &backtrace) + : tl::ScriptError (msg, cls, backtrace) +{ } + +PythonError::PythonError (const char *msg, const char *sourcefile, int line, const char *cls, const std::vector &backtrace) + : tl::ScriptError (msg, sourcefile, line, cls, backtrace) +{ } + +PythonError::PythonError (const PythonError &d) + : tl::ScriptError (d) +{ } + +// -------------------------------------------------------------------------- +// PythonArgumentError implementation + +PythonArgumentError::PythonArgumentError (const tl::Exception &ex, int index, const std::string &name) + : tl::Exception (ex.msg ()), m_index (index), m_name (name) +{ } + +std::string +PythonArgumentError::msg () const +{ + if (! m_name.empty ()) { + return tl::sprintf (tl::to_string (tr ("%s for argument #%d ('%s')")), tl::Exception::msg (), m_index + 1, m_name); + } else { + return tl::sprintf (tl::to_string (tr ("%s for argument #%d")), tl::Exception::msg (), m_index + 1); + } +} + // -------------------------------------------------------------------------- /** diff --git a/src/pya/pya/pya.h b/src/pya/pya/pya.h index 6ca3c4ca7..11cdcd9f0 100644 --- a/src/pya/pya/pya.h +++ b/src/pya/pya/pya.h @@ -77,17 +77,24 @@ class PYA_PUBLIC PythonError : public tl::ScriptError { public: - PythonError (const char *msg, const char *cls, const std::vector &backtrace) - : tl::ScriptError (msg, cls, backtrace) - { } + PythonError (const char *msg, const char *cls, const std::vector &backtrace); + PythonError (const char *msg, const char *sourcefile, int line, const char *cls, const std::vector &backtrace); + PythonError (const PythonError &d); +}; - PythonError (const char *msg, const char *sourcefile, int line, const char *cls, const std::vector &backtrace) - : tl::ScriptError (msg, sourcefile, line, cls, backtrace) - { } +/** + * @brief A class encapsulating an argument error + */ +class PYA_PUBLIC PythonArgumentError + : public tl::Exception +{ +public: + PythonArgumentError (const tl::Exception &ex, int index, const std::string &name); + virtual std::string msg () const; - PythonError (const PythonError &d) - : tl::ScriptError (d) - { } +private: + int m_index; + std::string m_name; }; class PythonModule; diff --git a/src/pya/pya/pyaConvert.cc b/src/pya/pya/pyaConvert.cc index 09fccb5e7..fb24e5732 100644 --- a/src/pya/pya/pyaConvert.cc +++ b/src/pya/pya/pyaConvert.cc @@ -52,7 +52,7 @@ long python2c_func::operator() (PyObject *rval) } else if (PyFloat_Check (rval)) { return (long) (PyFloat_AsDouble (rval)); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to an integer"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to an integer"))); } } @@ -75,7 +75,7 @@ char python2c_func::operator() (PyObject *rval) } else if (PyFloat_Check (rval)) { return char (PyFloat_AsDouble (rval)); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to a character"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to a character"))); } } @@ -92,7 +92,7 @@ unsigned long python2c_func::operator() (PyObject *rval) } else if (PyFloat_Check (rval)) { return (unsigned long) (PyFloat_AsDouble (rval)); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to an integer"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to an integer"))); } } @@ -109,7 +109,7 @@ long long python2c_func::operator() (PyObject *rval) } else if (PyFloat_Check (rval)) { return (long long) (PyFloat_AsDouble (rval)); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to an integer"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to an integer"))); } } @@ -126,7 +126,7 @@ unsigned long long python2c_func::operator() (PyObject *rval } else if (PyFloat_Check (rval)) { return (unsigned long long) (PyFloat_AsDouble (rval)); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to an integer"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to an integer"))); } } @@ -145,7 +145,7 @@ __int128 python2c_func<__int128>::operator() (PyObject *rval) } else if (PyFloat_Check (rval)) { return PyFloat_AsDouble (rval); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to an integer"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to an integer"))); } } #endif @@ -163,7 +163,7 @@ double python2c_func::operator() (PyObject *rval) } else if (PyFloat_Check (rval)) { return PyFloat_AsDouble (rval); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to a floating-point value"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to a floating-point value"))); } } @@ -188,7 +188,7 @@ std::string python2c_func::operator() (PyObject *rval) } else if (PyByteArray_Check (rval)) { return std::string (PyByteArray_AsString (rval), PyByteArray_Size (rval)); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to a string"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to a string"))); } } @@ -224,7 +224,7 @@ std::vector python2c_func >::operator() (PyObject *rval) Py_ssize_t sz = PyByteArray_Size (rval); return std::vector (cp, cp + sz); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to a byte array"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to a byte array"))); } } @@ -250,7 +250,7 @@ QByteArray python2c_func::operator() (PyObject *rval) } else if (PyByteArray_Check (rval)) { return QByteArray (PyByteArray_AsString (rval), PyByteArray_Size (rval)); } else { - throw tl::Exception (tl::to_string (tr ("Argument cannot be converted to a byte array"))); + throw tl::Exception (tl::to_string (tr ("Value cannot be converted to a byte array"))); } } diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index 5eff6c85d..37d7dfab0 100644 --- a/src/pya/pya/pyaModule.cc +++ b/src/pya/pya/pyaModule.cc @@ -1175,6 +1175,42 @@ special_method_impl (gsi::MethodBase::special_method_type smt, PyObject *self, P } } +static void +push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args, tl::Heap &heap) +{ + int i = 0; + int argc = args == NULL ? 0 : int (PyTuple_Size (args)); + + try { + + for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); i < argc && a != meth->end_arguments (); ++a, ++i) { + push_arg (*a, arglist, PyTuple_GetItem (args, i), heap); + } + + } catch (tl::Exception &ex) { + + // In case of an error upon write, pop the arguments to clean them up. + // Without this, there is a risk to keep dead objects on the stack. + for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) { + pop_arg (*a, arglist, 0, heap); + } + + const gsi::ArgSpecBase *arg_spec = meth->begin_arguments () [i].spec (); + throw PythonArgumentError (ex, i, arg_spec ? arg_spec->name () : std::string ()); + + } catch (...) { + + // In case of an error upon write, pop the arguments to clean them up. + // Without this, there is a risk to keep dead objects on the stack. + for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) { + pop_arg (*a, arglist, 0, heap); + } + + throw; + + } +} + static PyObject * method_adaptor (int mid, PyObject *self, PyObject *args) { @@ -1203,8 +1239,6 @@ method_adaptor (int mid, PyObject *self, PyObject *args) throw tl::Exception (tl::to_string (tr ("Cannot call non-const method on a const reference"))); } - int argc = args == NULL ? 0 : int (PyTuple_Size (args)); - void *obj = 0; if (p) { // Hint: this potentially instantiates the object @@ -1214,24 +1248,7 @@ method_adaptor (int mid, PyObject *self, PyObject *args) gsi::SerialArgs retlist (meth->retsize ()); gsi::SerialArgs arglist (meth->argsize ()); - try { - - int i = 0; - for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); i < argc && a != meth->end_arguments (); ++a, ++i) { - push_arg (*a, arglist, PyTuple_GetItem (args, i), heap); - } - - } catch (...) { - - // In case of an error upon write, pop the arguments to clean them up. - // Without this, there is a risk to keep dead objects on the stack. - for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) { - pop_arg (*a, arglist, 0, heap); - } - - throw; - - } + push_args (arglist, meth, args, heap); meth->call (obj, arglist, retlist); @@ -1764,25 +1781,7 @@ method_init_adaptor (int mid, PyObject *self, PyObject *args) gsi::SerialArgs retlist (meth->retsize ()); gsi::SerialArgs arglist (meth->argsize ()); - try { - - int i = 0; - int argc = args == NULL ? 0 : int (PyTuple_Size (args)); - for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); i < argc && a != meth->end_arguments (); ++a, ++i) { - push_arg (*a, arglist, PyTuple_GetItem (args, i), heap); - } - - } catch (...) { - - // In case of an error upon write, pop the arguments to clean them up. - // Without this, there is a risk to keep dead objects on the stack. - for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) { - pop_arg (*a, arglist, 0, heap); - } - - throw; - - } + push_args (arglist, meth, args, heap); meth->call (0, arglist, retlist);