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 "tlFileUtils.h"
#include "tlString.h"
#include "tlInternational.h"
#if defined(HAVE_QT)
# include <QCoreApplication>
@ -51,6 +52,38 @@
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:
PythonError (const char *msg, const char *cls, const std::vector <tl::BacktraceElement> &backtrace)
: tl::ScriptError (msg, cls, backtrace)
{ }
PythonError (const char *msg, const char *cls, const std::vector <tl::BacktraceElement> &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)
: tl::ScriptError (d)
{ }
private:
int m_index;
std::string m_name;
};
class PythonModule;

View File

@ -52,7 +52,7 @@ long python2c_func<long>::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<char>::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<unsigned long>::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<long long>::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<unsigned long long>::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<double>::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<std::string>::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<char> python2c_func<std::vector<char> >::operator() (PyObject *rval)
Py_ssize_t sz = PyByteArray_Size (rval);
return std::vector<char> (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<QByteArray>::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")));
}
}

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 *
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);