Skip to content

Commit

Permalink
Improved error messages from Python plugins.
Browse files Browse the repository at this point in the history
Also include the error type in the error message.
  • Loading branch information
jesper-friis committed Aug 12, 2023
1 parent 0468c76 commit 92a773c
Show file tree
Hide file tree
Showing 12 changed files with 313 additions and 84 deletions.
4 changes: 3 additions & 1 deletion bindings/python/dlite-python.i
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
count. */
PyObject *dlite_swig_exception = NULL;

/* forward declarations */
/* Global DLite exceptions - they will be initialised in %init */
static PyObject *DLiteError = NULL;
static PyObject *DLiteVerifyError = NULL;

/* forward declarations */
char *strndup(const char *s, size_t n);
%}

Expand Down
2 changes: 1 addition & 1 deletion bindings/python/tests/test_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@
entity = dlite.Instance.from_json(json_repr)
except dlite.DLiteError as exc:
assert str(exc) == (
"Error 1: metadata does not confirm to schema, please check "
"DLiteOtherError: metadata does not confirm to schema, please check "
"dimensions, properties and/or relations: "
"http://onto-ns.com/ex/0.1/test"
)
Expand Down
56 changes: 28 additions & 28 deletions src/dlite-errors.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,35 @@
const char *dlite_errname(DLiteErrors code)
{
switch (code) {
case dliteSuccess: return "Sussess";
case dliteUnknownError: return "UnknownError";
case dliteIOError: return "IOError";
case dliteRuntimeError: return "RuntimeError";
case dliteIndexError: return "IndexError";
case dliteTypeError: return "TypeError";
case dliteDivisionByZero: return "DivisionByZero";
case dliteOverflowError: return "OverflowError";
case dliteSyntaxError: return "SyntaxError";
case dliteValueError: return "ValueError";
case dliteSystemError: return "SystemError";
case dliteAttributeError: return "AttributeError";
case dliteMemoryError: return "MemoryError";
case dliteNullReferenceError: return "NullReferenceError";
case dliteSuccess: return "DLiteSussess";
case dliteUnknownError: return "DLiteUnknown";
case dliteIOError: return "DLiteIO";
case dliteRuntimeError: return "DLiteRuntime";
case dliteIndexError: return "DLiteIndex";
case dliteTypeError: return "DLiteType";
case dliteDivisionByZero: return "DLiteDivisionByZero";
case dliteOverflowError: return "DLiteOverflow";
case dliteSyntaxError: return "DLiteSyntax";
case dliteValueError: return "DLiteValue";
case dliteSystemError: return "DLiteSystem";
case dliteAttributeError: return "DLiteAttribute";
case dliteMemoryError: return "DLiteMemory";
case dliteNullReferenceError: return "DLiteNullReference";

case dliteKeyError: return "KeyError";
case dliteParseError: return "ParseError";
case dlitePrintError: return "PrintError";
case dliteUnsupportedError: return "UnsupportedError";
case dliteInconsistentDataError: return "InconsistentDataError";
case dliteStorageOpenError: return "StorageOpenError";
case dliteStorageLoadError: return "StorageLoadError";
case dliteStorageSaveError: return "StorageSaveError";
case dliteMissingInstanceError: return "MissingInstanceError";
case dliteMissingMetadataError: return "MissingMetadataError";
case dliteMetadataExistError: return "MetadataExistError";
case dliteKeyError: return "DLiteKey";
case dliteParseError: return "DLiteParse";
case dlitePrintError: return "DLitePrint";
case dliteUnsupportedError: return "DLiteUnsupported";
case dliteInconsistentDataError: return "DLiteInconsistentData";
case dliteStorageOpenError: return "DLiteStorageOpen";
case dliteStorageLoadError: return "DLiteStorageLoad";
case dliteStorageSaveError: return "DLiteStorageSave";
case dliteMissingInstanceError: return "DLiteMissingInstance";
case dliteMissingMetadataError: return "DLiteMissingMetadata";
case dliteMetadataExistError: return "DLiteMetadataExist";

case dliteLastError: return "LastError";
case dliteLastError: return "DLiteLast";
}
if (code < 0) return "UndefinedError";
return "Successful";
if (code < 0) return "DLiteUndefined";
return "DLiteOther";
}
14 changes: 13 additions & 1 deletion src/dlite-misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,15 @@ void dlite_globals_set(DLiteGlobals *globals_handler)
err_set_state(g);
}


/* Error handler for DLite. */
static void dlite_err_handler(const ErrRecord *record)
{
FILE *stream = err_get_stream();
if (stream) fprintf(stream, "** %s\n", record->msg);
}


/*
Initialises dlite. This function may be called several times.
*/
Expand All @@ -532,8 +541,11 @@ void dlite_init(void)
/* Set up global state for utils/err.c */
if (!dlite_globals_get_state(ERR_STATE_ID))
dlite_globals_add_state(ERR_STATE_ID, err_get_state(), NULL);
}

/* Set up error handling */
err_set_handler(dlite_err_handler);
err_set_nameconv(dlite_errname);
}
}


Expand Down
106 changes: 102 additions & 4 deletions src/pyembed/dlite-pyembed.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include "dlite-python-singletons.h"
#include "config-paths.h"

#define GLOBALS_ID "dlite-pyembed-globals"

/* Get rid of MSVS warnings */
#if defined WIN32 || defined _WIN32 || defined __WIN32__
# pragma warning(disable: 4273 4996)
Expand All @@ -17,6 +19,67 @@

static int python_initialized = 0;


/* Struct correlating Python exceptions with DLite errors */
typedef struct {
PyObject *exc; /* Python exception */
DLiteErrors errcode; /* DLite error */
} ErrorCorrelation;

/* Global state for this module */
typedef struct {
ErrorCorrelation *errcorr; /* NULL-terminated array */
} PyembedGlobals;


/* Free global state for this module */
static void free_globals(void *globals)
{
PyembedGlobals *g = (PyembedGlobals *)globals;;
if (g->errcorr) free(g->errcorr);
free(g);
}

/* Return a pointer to global state for this module */
static PyembedGlobals *get_globals(void)
{
PyembedGlobals *g = dlite_globals_get_state(GLOBALS_ID);
if (!g) {
if (!(g = calloc(1, sizeof(PyembedGlobals))))
return dlite_err(dliteMemoryError, "allocation failure"), NULL;
dlite_globals_add_state(GLOBALS_ID, g, free_globals);
}
return g;
}

/* Help function returning a constant pointer to a NULL-terminated
array of ErrorCorrelation records. */
static const ErrorCorrelation *error_correlations(void)
{
PyembedGlobals *g = get_globals();
if (!g->errcorr) {
ErrorCorrelation corr[] = {
{PyExc_KeyError, dliteKeyError},
{PyExc_MemoryError, dliteMemoryError},
{PyExc_AttributeError, dliteAttributeError},
{PyExc_SystemError, dliteSystemError},
{PyExc_ValueError, dliteValueError},
{PyExc_SyntaxError, dliteSyntaxError},
{PyExc_OverflowError, dliteOverflowError},
{PyExc_ZeroDivisionError, dliteDivisionByZero},
{PyExc_TypeError, dliteTypeError},
{PyExc_IndexError, dliteIndexError},
{PyExc_RuntimeError, dliteRuntimeError},
{PyExc_IOError, dliteIOError},
{NULL, 0}
};
if (!(g->errcorr = malloc(sizeof(corr))))
return dlite_err(dliteMemoryError, "allocation failure"), NULL;
memcpy(g->errcorr, corr, sizeof(corr));
}
return g->errcorr;
}

/* Initialises the embedded Python environment. */
void dlite_pyembed_initialise(void)
{
Expand Down Expand Up @@ -112,7 +175,6 @@ int dlite_pyembed_finalise(void)
return status;
}


/*
Returns a static pointer to the class name of python object cls or
NULL on error.
Expand All @@ -130,6 +192,37 @@ const char *dlite_pyembed_classname(PyObject *cls)
}


/*
Return DLite error code given Python exception type.
*/
DLiteErrors dlite_pyembed_errcode(PyObject *type)
{
const ErrorCorrelation *corr = error_correlations();
if (!type) return dliteSuccess;
while (corr->exc) {
if (PyErr_GivenExceptionMatches(type, corr->exc))
return corr->errcode;
corr++;
}
return dliteUnknownError;
}

/*
Return Python exception class corresponding to given DLite error code.
Returns NULL if `code` is zero.
*/
PyObject *dlite_pyembed_exception(DLiteErrors code)
{
const ErrorCorrelation *corr = error_correlations();
if (!code) return NULL;
while (corr->exc) {
if (code == corr->errcode) return corr->exc;
corr++;
}
return PyExc_Exception;
}


/*
Writes Python error message to `errmsg` (of length `len`) if an
Python error has occured.
Expand Down Expand Up @@ -267,9 +360,14 @@ int dlite_pyembed_err_check(const char *msg, ...)
*/
int dlite_pyembed_verr_check(const char *msg, va_list ap)
{
/* TODO: can we correlate the return value to Python error type? */
if (PyErr_Occurred())
return dlite_pyembed_verr(1, msg, ap);
PyObject *err;
//PyGILState_STATE state = PyGILState_STATE();
err = PyErr_Occurred();
//PyGILState_Release(state);
if (err) {
int eval = dlite_pyembed_errcode(err);
return dlite_pyembed_verr(eval, msg, ap);
}
return 0;
}

Expand Down
27 changes: 23 additions & 4 deletions src/pyembed/dlite-pyembed.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
#include "utils/plugin.h"
#include "dlite.h"

/* Remove __attribute__ when we are not compiling with gcc */
#ifndef __GNUC__
# define __attribute__(x)
#endif


/**
Initialises the embedded Python environment.
Expand All @@ -35,6 +40,16 @@ int dlite_pyembed_finalise(void);
*/
const char *dlite_pyembed_classname(PyObject *cls);

/**
Return DLite error code given Python exception type.
*/
DLiteErrors dlite_pyembed_errcode(PyObject *type);

/**
Return Python exception class corresponding to given DLite error code.
Returns NULL if `code` is zero.
*/
PyObject *dlite_pyembed_exception(DLiteErrors code);

/**
Writes Python error message to `errmsg` (of length `len`) if an
Expand All @@ -56,24 +71,28 @@ int dlite_pyembed_errmsg(char *errmsg, size_t errlen);
Returns `eval`.
*/
int dlite_pyembed_err(int eval, const char *msg, ...);
int dlite_pyembed_err(int eval, const char *msg, ...)
__attribute__ ((__format__ (__printf__, 2, 3)));

/**
Like dlite_pyembed_err() but takes a `va_list` as input.
*/
int dlite_pyembed_verr(int eval, const char *msg, va_list ap);
int dlite_pyembed_verr(int eval, const char *msg, va_list ap)
__attribute__ ((__format__ (__printf__, 2, 0)));

/**
Checks if an Python error has occured. Returns zero if no error has
occured. Otherwise dlite_pyembed_err() is called and non-zero is
returned.
*/
int dlite_pyembed_err_check(const char *msg, ...);
int dlite_pyembed_err_check(const char *msg, ...)
__attribute__ ((__format__ (__printf__, 1, 2)));

/**
Like dlite_pyembed_err_check() but takes a `va_list` as input.
*/
int dlite_pyembed_verr_check(const char *msg, va_list ap);
int dlite_pyembed_verr_check(const char *msg, va_list ap)
__attribute__ ((__format__ (__printf__, 1, 0)));


/**
Expand Down
6 changes: 6 additions & 0 deletions src/pyembed/dlite-python-singletons.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,9 @@ PyObject *dlite_python_mapping_base(void)
{
return dlite_python_mainclass("DLiteMappingBase");
}


//PyObject *dlite_python_error(void)
//{
//
//}
Loading

0 comments on commit 92a773c

Please sign in to comment.