Skip to content

Commit

Permalink
Python errors (#589)
Browse files Browse the repository at this point in the history
# Description
Create and raise Python exceptions corresponding to the dlite error
codes.
    
Individual DLite errors can now be catched from Python based on their
type.

## Type of change
- [ ] 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 13, 2023
2 parents 986f13a + 26a964c commit e95df2c
Show file tree
Hide file tree
Showing 11 changed files with 280 additions and 70 deletions.
8 changes: 4 additions & 4 deletions bindings/python/dlite-collection-python.i
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ class Collection(Instance):
def remove(self, label):
"""Remove instance with given label from collection."""
if _collection_remove(self._coll, label):
raise DLiteError(f'No such label in collection: "{label}"')
raise _dlite.DLiteError(f'No such label in collection: "{label}"')

def get(self, label, metaid=None):
"""Return instance with given label.
Expand Down Expand Up @@ -189,12 +189,12 @@ class Collection(Instance):
def add_relation(self, s, p, o):
"""Add (subject, predicate, object) RDF triple to collection."""
if _collection_add_relation(self._coll, s, p, o) != 0:
raise DLiteError(f'Error adding relation ({s}, {p}, {o})')
raise _dlite.DLiteError(f'Error adding relation ({s}, {p}, {o})')

def remove_relations(self, s=None, p=None, o=None):
"""Remove all relations matching `s`, `p` and `o`."""
if _collection_remove_relations(self._coll, s, p, o) < 0:
raise DLiteError(
raise _dlite.DLiteError(
f'Error removing relations matching ({s}, {p}, {o})')

def get_first_relation(self, s=None, p=None, o=None):
Expand Down Expand Up @@ -280,7 +280,7 @@ class Collection(Instance):
):
yield inst
elif property_mappings:
raise DLiteError(
raise _dlite.DLiteError(
'`metaid` is required when `property_mappings` is true')
else:
for inst in iter:
Expand Down
16 changes: 9 additions & 7 deletions bindings/python/dlite-entity-python.i
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ class Metadata(Instance):
lst = [p for p in self.properties["properties"] if p.name == name]
if lst:
return lst[0]
raise DLiteError(f"Metadata {self.uri} has no such property: {name}")
raise _dlite.DLiteError(
f"Metadata {self.uri} has no such property: {name}")
def dimnames(self):
"""Returns a list of all dimension names in this metadata."""
Expand Down Expand Up @@ -115,7 +116,7 @@ def get_instance(id: "str", metaid: "str" = None, check_storages: "bool" = True)
inst = _dlite.get_instance(id, metaid, check_storages)

if inst is None:
raise DLiteError(f"no such instance: {id}")
raise _dlite.DLiteError(f"no such instance: {id}")
elif inst.is_meta:
inst.__class__ = Metadata
elif inst.meta.uri == COLLECTION_ENTITY:
Expand Down Expand Up @@ -267,13 +268,13 @@ def get_instance(id: "str", metaid: "str" = None, check_storages: "bool" = True)
# Override default generated __init__() method
def __init__(self, *args, **kwargs):
if self is None:
raise DLiteError(f"invalid dlite.Instance")
raise _dlite.DLiteError(f"invalid dlite.Instance")
_dlite.errclr()
_dlite.Instance_swiginit(self, _dlite.new_Instance(*args, **kwargs))
if not hasattr(self, 'this') or not getattr(self, 'this'):
raise DLiteError(f"cannot initiate dlite.Instance")
raise _dlite.DLiteError(f"cannot initiate dlite.Instance")
elif self.is_meta:
self.__class__ = Metadata
elif self.meta.uri == COLLECTION_ENTITY:
Expand Down Expand Up @@ -487,7 +488,7 @@ def get_instance(id: "str", metaid: "str" = None, check_storages: "bool" = True)
elif isinstance(dest, str):
self.save_to_url(dest)
else:
raise DLiteError('Arguments do not match any call signature')
raise _dlite.DLiteError('Arguments do not match any call signature')

def __getitem__(self, ind):
if isinstance(ind, int):
Expand All @@ -504,7 +505,8 @@ def get_instance(id: "str", metaid: "str" = None, check_storages: "bool" = True)

def __setitem__(self, ind, value):
if self.is_frozen():
raise DLiteError('frozen instance does not support item assignment')
raise _dlite.DLiteError(
'frozen instance does not support item assignment')
if isinstance(ind, int):
self.set_property_by_index(ind, value)
elif self.has_property(ind):
Expand Down Expand Up @@ -537,7 +539,7 @@ def get_instance(id: "str", metaid: "str" = None, check_storages: "bool" = True)
if name == 'this':
object.__setattr__(self, name, value)
elif self.is_frozen():
raise DLiteError(
raise _dlite.DLiteError(
'frozen instance does not support attribute assignment')
elif _has_property(self, name):
_set_property(self, name, value)
Expand Down
3 changes: 2 additions & 1 deletion bindings/python/dlite-entity.i
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
%{
#include "dlite-mapping.h"
#include "dlite-bson.h"
#include "dlite-errors.h"
%}


Expand Down Expand Up @@ -469,7 +470,7 @@ Call signatures:
hashp = data;
}
if (dlite_instance_verify_hash($self, hashp, recursive))
dlite_swig_exception = DLiteVerifyError;
dlite_swig_exception = dlite_python_module_error(dliteVerifyError);
}

%feature("docstring",
Expand Down
37 changes: 9 additions & 28 deletions bindings/python/dlite-python.i
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@
count. */
PyObject *dlite_swig_exception = NULL;

/* 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 Expand Up @@ -64,21 +60,7 @@ int dlite_swig_set_scalar(void *ptr, DLiteType type, size_t size, obj_t *obj);

%init %{
Py_Initialize(); /* should already be called, but just in case... */
import_array(); /* Initialize numpy */

DLiteError = PyErr_NewExceptionWithDoc(
"dlite.DLiteError", // name
"Base exception for the dlite module.", // doc
NULL, // base
NULL // dict
);

DLiteVerifyError = PyErr_NewExceptionWithDoc(
"dlite.DLiteVerifyError", // name
"Object cannot be verified.", // doc
DLiteError, // base
NULL // dict
);
import_array(); /* Initialize numpy */
%}

%numpy_typemaps(unsigned char, NPY_UBYTE, size_t)
Expand All @@ -94,11 +76,8 @@ int dlite_swig_set_scalar(void *ptr, DLiteType type, size_t size, obj_t *obj);
**********************************************/
%{

PyObject *_get_DLiteError(void) {
return DLiteError;
}
PyObject *_get_DLiteVerifyError(void) {
return DLiteVerifyError;
int _get_number_of_errors(void) {
return -dliteLastError;
}

/* Free's array of allocated strings. */
Expand Down Expand Up @@ -1382,12 +1361,14 @@ PyObject *dlite_python_mapping_base(void);
/* ------------------
* Expose generic api
* ------------------ */
PyObject *_get_DLiteError(void);
PyObject *_get_DLiteVerifyError(void);
%rename(_get_dlite_error) dlite_python_module_error;
PyObject *dlite_python_module_error(int code);
int _get_number_of_errors(void);

%pythoncode %{
DLiteError = _dlite._get_DLiteError()
DLiteVerifyError = _dlite._get_DLiteVerifyError()
for n in range(_dlite._get_number_of_errors()):
exc = _get_dlite_error(-n)
setattr(_dlite, exc.__name__, exc)
DLiteStorageBase = _dlite._get_storage_base()
DLiteMappingBase = _dlite._get_mapping_base()
%}
8 changes: 5 additions & 3 deletions bindings/python/dlite.i
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,18 @@
dlite_swig_errclr();
$action
#ifdef SWIGPYTHON
int errval = dlite_errval();
if (dlite_swig_exception) {
PyErr_SetString(dlite_swig_exception, dlite_errmsg());
dlite_swig_exception = NULL;
SWIG_fail;
} else if (dlite_errval()) {
PyErr_SetString(DLiteError, dlite_errmsg());
} else if (errval) {
PyObject *exc = dlite_python_module_error(errval);
PyErr_SetString(exc, dlite_errmsg());
SWIG_fail;
}
#else
if (dlite_errval())
if (errval)
SWIG_exception_fail(SWIG_RuntimeError, dlite_errmsg());
#endif
}
Expand Down
51 changes: 49 additions & 2 deletions src/dlite-errors.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#include <stdlib.h>

#include "dlite-errors.h"


/*
Returns the name corresponding to error code
Returns the name corresponding to error code (with the final "Error" stripped
off).
*/
const char *dlite_errname(DLiteErrors code)
{
Expand All @@ -22,20 +25,64 @@ const char *dlite_errname(DLiteErrors code)
case dliteMemoryError: return "DLiteMemory";
case dliteNullReferenceError: return "DLiteNullReference";

case dliteOSError: return "DLiteOS";
case dliteKeyError: return "DLiteKey";
case dliteParseError: return "DLiteParse";
case dlitePrintError: return "DLitePrint";
case dliteSerialiseError: return "DLiteSerialise";
case dliteUnsupportedError: return "DLiteUnsupported";
case dliteVerifyError: return "DLiteVerify";
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 dlitePythonError: return "DLitePython";

case dliteLastError: return "DLiteLast";
}
if (code < 0) return "DLiteUndefined";
return "DLiteOther";
}


/*
Returns a description of the corresponding to error code
*/
const char *dlite_errdescr(DLiteErrors code)
{
switch (code) {
case dliteSuccess: return "Sussess";
case dliteUnknownError: return "Generic unknown error";
case dliteIOError: return "I/O related error";
case dliteRuntimeError: return "Unspecified run-time error";
case dliteIndexError: return "Index out of range";
case dliteTypeError: return "Inappropriate argument type";
case dliteDivisionByZero: return "Division by zero";
case dliteOverflowError: return "Result too large to be represented";
case dliteSyntaxError: return "Invalid syntax";
case dliteValueError: return "Inappropriate argument value (of correct type)";
case dliteSystemError: return "Internal error in DLite. Please report this";
case dliteAttributeError: return "Cannot refer to or assign attribute or variable";
case dliteMemoryError: return "Out of memory";
case dliteNullReferenceError: return "Unexpected NULL pointer when converting bindings";

case dliteOSError: return "Error calling a system function";
case dliteKeyError: return "Mapping key is not found";
case dliteParseError: return "Cannot parse input";
case dliteSerialiseError: return "Cannot serialise output";
case dliteUnsupportedError: return "Feature is not implemented/supported";
case dliteVerifyError: return "Object cannot be verified";
case dliteInconsistentDataError: return "Inconsistent data";
case dliteStorageOpenError: return "Cannot open storage plugin";
case dliteStorageLoadError: return "Cannot load storage plugin";
case dliteStorageSaveError: return "Cannot save storage plugin";
case dliteMissingInstanceError: return "No instance with given id";
case dliteMissingMetadataError: return "No metadata with given id";
case dliteMetadataExistError: return "Metadata with given id already exists";
case dlitePythonError: return "Error calling Python API";
case dliteLastError: return NULL;
}
return NULL;
}
34 changes: 21 additions & 13 deletions src/dlite-errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,28 @@ typedef enum {
dliteSyntaxError=-8, /*!< Invalid syntax */
dliteValueError=-9, /*!< Inappropriate argument value */
dliteSystemError=-10, /*!< Internal error in DLite. Please report this */
dliteAttributeError=-11,/*!< Attribute not found */
dliteAttributeError=-11,/*!< Attribute or variable not found */
dliteMemoryError=-12, /*!< Out of memory */
dliteNullReferenceError=-13, /*!< Unexpected NULL argument */

/* Additional DLite-specific errors */
dliteKeyError=-14, /*!< Mapping key not found */
dliteParseError=-15, /*!< Cannot parse input */
dlitePrintError=-16, /*!< Cannot print format string */
dliteUnsupportedError=-17, /*!< Feature is not implemented/supported */
dliteInconsistentDataError=-18,/*!< Inconsistent data */
dliteStorageOpenError=-19, /*!< Cannot open storage plugin */
dliteStorageLoadError=-20, /*!< Cannot load storage plugin */
dliteStorageSaveError=-21, /*!< Cannot save storage plugin */
dliteMissingInstanceError=-22, /*!< No instance with given id can be found */
dliteMissingMetadataError=-23, /*!< No metadata with given id can be found */
dliteMetadataExistError=-24, /*!< Metadata with given id already exists */
dliteOSError=-14, /*!< Error calling a system function */
dliteKeyError=-15, /*!< Mapping key not found */
dliteParseError=-16, /*!< Cannot parse input */
dliteSerialiseError=-17, /*!< Cannot serialise output */
dliteUnsupportedError=-18, /*!< Feature is not implemented/supported */
dliteVerifyError=-19, /*!< Object cannot be verified */
dliteInconsistentDataError=-20,/*!< Inconsistent data */
dliteStorageOpenError=-21, /*!< Cannot open storage plugin */
dliteStorageLoadError=-22, /*!< Cannot load storage plugin */
dliteStorageSaveError=-23, /*!< Cannot save storage plugin */
dliteMissingInstanceError=-24, /*!< No instance with given id can be found */
dliteMissingMetadataError=-25, /*!< No metadata with given id can be found */
dliteMetadataExistError=-26, /*!< Metadata with given id already exists */
dlitePythonError=-27, /*!< Python error */

/* Should always be the last error */
dliteLastError=-25
dliteLastError=-28
} DLiteErrors;


Expand All @@ -44,5 +47,10 @@ typedef enum {
const char *dlite_errname(DLiteErrors code);


/**
Returns a description of the corresponding to error code
*/
const char *dlite_errdescr(DLiteErrors code);


#endif /* _DLITE_ERRORS_H */
8 changes: 4 additions & 4 deletions src/dlite-type.c
Original file line number Diff line number Diff line change
Expand Up @@ -722,19 +722,19 @@ int dlite_type_print(char *dest, size_t n, const void *p, DLiteType dtype,
case dliteBlob:
if (!(qflags & strquoteNoQuote)) {
int v = snprintf(dest+m, PDIFF(n, m), "\"");
if (v < 0) return err(dlitePrintError,
if (v < 0) return err(dliteSerialiseError,
"error printing initial quote for blob");
m += v;
}
for (i=0; i<size; i++) {
int v = snprintf(dest+m, PDIFF(n, m), "%02x",
*((unsigned char *)p+i));
if (v < 0) return err(dlitePrintError, "error printing blob");
if (v < 0) return err(dliteSerialiseError, "error printing blob");
m += v;
}
if (!(qflags & strquoteNoQuote)) {
int v = snprintf(dest+m, PDIFF(n, m), "\"");
if (v < 0) return err(dlitePrintError,
if (v < 0) return err(dliteSerialiseError,
"error printing final quote for blob");
m += v;
}
Expand Down Expand Up @@ -871,7 +871,7 @@ int dlite_type_print(char *dest, size_t n, const void *p, DLiteType dtype,
if (m < 0) {
char buf[32];
dlite_type_set_typename(dtype, size, buf, sizeof(buf));
return errx(dlitePrintError, "error printing type %s", buf);
return errx(dliteSerialiseError, "error printing type %s", buf);
}
return m;
}
Expand Down
Loading

0 comments on commit e95df2c

Please sign in to comment.