Skip to content

Commit

Permalink
580 error messages from python plugins (#585)
Browse files Browse the repository at this point in the history
# Description
Improves changes in PR #584 when addressing issue #580. 
Error messages from within Python plugins are now properly showed. The
error type is also shown.

## Type of change
- [x] Bug fix
- [x] New feature
- [ ] Documentation update
- [ ] Test update

## Checklist for the reviewer
This checklist should be used as a help for the reviewer.

- [ ] Is the change limited to one issue?
- [ ] Does this PR close the issue?
- [ ] Is the code easy to read and understand?
- [ ] Do all new feature have an accompanying new test?
- [ ] Has the documentation been updated as necessary?
  • Loading branch information
jesper-friis authored Aug 12, 2023
2 parents 6b0fc01 + 8b68959 commit 3678431
Show file tree
Hide file tree
Showing 12 changed files with 321 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
15 changes: 15 additions & 0 deletions src/pyembed/dlite-python-singletons.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,18 @@ PyObject *dlite_python_mapping_base(void)
{
return dlite_python_mainclass("DLiteMappingBase");
}


/*
Returns a singleton Python exception object for the given DLite error code.
The singleton object is created the first time this function is called
with a given `code`. All following calles with the same `code` will return
a reference to the same object.
Returns NULL if `code` is out of range.
*/
//PyObject *dlite_python_error(DLiteErrors code)
//{
//
//}
Loading

0 comments on commit 3678431

Please sign in to comment.