Express argument details in Python call errors

This commit is contained in:
Matthias Koefferlein 2022-07-29 23:58:19 +02:00
parent 82fe920ac9
commit 7d078ed041
4 changed files with 97 additions and 58 deletions

View File

@ -38,6 +38,7 @@
#include "tlTimer.h" #include "tlTimer.h"
#include "tlFileUtils.h" #include "tlFileUtils.h"
#include "tlString.h" #include "tlString.h"
#include "tlInternational.h"
#if defined(HAVE_QT) #if defined(HAVE_QT)
# include <QCoreApplication> # include <QCoreApplication>
@ -51,6 +52,38 @@
namespace pya namespace pya
{ {
// --------------------------------------------------------------------------
// PythonError implementation
PythonError::PythonError (const char *msg, const char *cls, const std::vector <tl::BacktraceElement> &backtrace)
: tl::ScriptError (msg, cls, backtrace)
{ }
PythonError::PythonError (const char *msg, const char *sourcefile, int line, const char *cls, const std::vector <tl::BacktraceElement> &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);
}
}
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
/** /**

View File

@ -77,17 +77,24 @@ class PYA_PUBLIC PythonError
: public tl::ScriptError : public tl::ScriptError
{ {
public: public:
PythonError (const char *msg, const char *cls, const std::vector <tl::BacktraceElement> &backtrace) PythonError (const char *msg, const char *cls, const std::vector <tl::BacktraceElement> &backtrace);
: tl::ScriptError (msg, cls, backtrace) PythonError (const char *msg, const char *sourcefile, int line, const char *cls, const std::vector <tl::BacktraceElement> &backtrace);
{ } PythonError (const PythonError &d);
};
PythonError (const char *msg, const char *sourcefile, int line, const char *cls, const std::vector <tl::BacktraceElement> &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) private:
: tl::ScriptError (d) int m_index;
{ } std::string m_name;
}; };
class PythonModule; class PythonModule;

View File

@ -52,7 +52,7 @@ long python2c_func<long>::operator() (PyObject *rval)
} else if (PyFloat_Check (rval)) { } else if (PyFloat_Check (rval)) {
return (long) (PyFloat_AsDouble (rval)); return (long) (PyFloat_AsDouble (rval));
} else { } 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<char>::operator() (PyObject *rval)
} else if (PyFloat_Check (rval)) { } else if (PyFloat_Check (rval)) {
return char (PyFloat_AsDouble (rval)); return char (PyFloat_AsDouble (rval));
} else { } 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<unsigned long>::operator() (PyObject *rval)
} else if (PyFloat_Check (rval)) { } else if (PyFloat_Check (rval)) {
return (unsigned long) (PyFloat_AsDouble (rval)); return (unsigned long) (PyFloat_AsDouble (rval));
} else { } 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<long long>::operator() (PyObject *rval)
} else if (PyFloat_Check (rval)) { } else if (PyFloat_Check (rval)) {
return (long long) (PyFloat_AsDouble (rval)); return (long long) (PyFloat_AsDouble (rval));
} else { } 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<unsigned long long>::operator() (PyObject *rval
} else if (PyFloat_Check (rval)) { } else if (PyFloat_Check (rval)) {
return (unsigned long long) (PyFloat_AsDouble (rval)); return (unsigned long long) (PyFloat_AsDouble (rval));
} else { } 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)) { } else if (PyFloat_Check (rval)) {
return PyFloat_AsDouble (rval); return PyFloat_AsDouble (rval);
} else { } 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 #endif
@ -163,7 +163,7 @@ double python2c_func<double>::operator() (PyObject *rval)
} else if (PyFloat_Check (rval)) { } else if (PyFloat_Check (rval)) {
return PyFloat_AsDouble (rval); return PyFloat_AsDouble (rval);
} else { } 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<std::string>::operator() (PyObject *rval)
} else if (PyByteArray_Check (rval)) { } else if (PyByteArray_Check (rval)) {
return std::string (PyByteArray_AsString (rval), PyByteArray_Size (rval)); return std::string (PyByteArray_AsString (rval), PyByteArray_Size (rval));
} else { } 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<char> python2c_func<std::vector<char> >::operator() (PyObject *rval)
Py_ssize_t sz = PyByteArray_Size (rval); Py_ssize_t sz = PyByteArray_Size (rval);
return std::vector<char> (cp, cp + sz); return std::vector<char> (cp, cp + sz);
} else { } 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<QByteArray>::operator() (PyObject *rval)
} else if (PyByteArray_Check (rval)) { } else if (PyByteArray_Check (rval)) {
return QByteArray (PyByteArray_AsString (rval), PyByteArray_Size (rval)); return QByteArray (PyByteArray_AsString (rval), PyByteArray_Size (rval));
} else { } 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")));
} }
} }

View File

@ -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 * static PyObject *
method_adaptor (int mid, PyObject *self, PyObject *args) 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"))); 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; void *obj = 0;
if (p) { if (p) {
// Hint: this potentially instantiates the object // 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 retlist (meth->retsize ());
gsi::SerialArgs arglist (meth->argsize ()); gsi::SerialArgs arglist (meth->argsize ());
try { push_args (arglist, meth, args, heap);
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;
}
meth->call (obj, arglist, retlist); 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 retlist (meth->retsize ());
gsi::SerialArgs arglist (meth->argsize ()); gsi::SerialArgs arglist (meth->argsize ());
try { push_args (arglist, meth, args, heap);
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;
}
meth->call (0, arglist, retlist); meth->call (0, arglist, retlist);