From c5aa2e93001436f6d63e3e9b7dff3c535397db1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Mon, 12 Nov 2018 16:16:47 +0100 Subject: [PATCH 1/2] Support OSError attributes This adds support for the OSError attributes : errno, strerror, filename and filename2. CPython only sets errno if 2 arguments has been passed in. This has not been implemented here. CPython OSError.args is capped at 2 items for backward compatibility reasons. This has not been implemented here. MICROPY_CPYTHON_COMPAT has to be enabled to get these attributes. mp_common_errno_to_str() has been extended to check mp_errno_to_str() as well. This is done to ease reuse for the strerror argument. --- py/moduerrno.c | 37 +++++++++++++++++++++++-------------- py/mperrno.h | 3 +-- py/objexcept.c | 39 +++++++++++++++++++++++++++++---------- 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/py/moduerrno.c b/py/moduerrno.c index 255c1a73e7..c1feafaa1f 100644 --- a/py/moduerrno.c +++ b/py/moduerrno.c @@ -136,19 +136,28 @@ const char* mp_errno_to_str(mp_obj_t errno_val) { #endif //MICROPY_PY_UERRNO -// For commonly encountered errors, return human readable strings -const compressed_string_t* mp_common_errno_to_str(mp_obj_t errno_val) { - if (MP_OBJ_IS_SMALL_INT(errno_val)) { - switch (MP_OBJ_SMALL_INT_VALUE(errno_val)) { - case EPERM: return translate("Permission denied"); - case ENOENT: return translate("No such file/directory"); - case EIO: return translate("Input/output error"); - case EACCES: return translate("Permission denied"); - case EEXIST: return translate("File exists"); - case ENODEV: return translate("Unsupported operation"); - case EINVAL: return translate("Invalid argument"); - case EROFS: return translate("Read-only filesystem"); - } +// For commonly encountered errors, return human readable strings, otherwise try errno name +const char *mp_common_errno_to_str(mp_obj_t errno_val, char *buf, size_t len) { + if (!MP_OBJ_IS_SMALL_INT(errno_val)) { + return NULL; } - return NULL; + + const compressed_string_t* desc = NULL; + switch (MP_OBJ_SMALL_INT_VALUE(errno_val)) { + case EPERM: desc = translate("Permission denied"); break; + case ENOENT: desc = translate("No such file/directory"); break; + case EIO: desc = translate("Input/output error"); break; + case EACCES: desc = translate("Permission denied"); break; + case EEXIST: desc = translate("File exists"); break; + case ENODEV: desc = translate("Unsupported operation"); break; + case EINVAL: desc = translate("Invalid argument"); break; + case EROFS: desc = translate("Read-only filesystem"); break; + } + if (desc != NULL && desc->length <= len) { + decompress(desc, buf); + return buf; + } + + const char *msg = mp_errno_to_str(errno_val); + return msg[0] != '\0' ? msg : NULL; } diff --git a/py/mperrno.h b/py/mperrno.h index 876b090b5e..911a9b4131 100644 --- a/py/mperrno.h +++ b/py/mperrno.h @@ -141,7 +141,6 @@ #endif const char* mp_errno_to_str(mp_obj_t errno_val); -// For commonly encountered errors, return compressed human readable strings -const compressed_string_t* mp_common_errno_to_str(mp_obj_t errno_val); +const char *mp_common_errno_to_str(mp_obj_t errno_val, char *buf, size_t len); #endif // MICROPY_INCLUDED_PY_MPERRNO_H diff --git a/py/objexcept.c b/py/objexcept.c index 1ddc2174e5..c54e5fd4a5 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -114,17 +114,11 @@ void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kin return; } else if (o->args->len == 1) { // try to provide a nice OSError error message - if (o->base.type == &mp_type_OSError && MP_OBJ_IS_SMALL_INT(o->args->items[0])) { - const compressed_string_t* common = mp_common_errno_to_str(o->args->items[0]); - const char* msg; + if (MP_OBJ_IS_SMALL_INT(o->args->items[0]) && + mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(o->base.type), MP_OBJ_FROM_PTR(&mp_type_OSError))) { char decompressed[50]; - if (common != NULL && common->length <= 50) { - decompress(common, decompressed); - msg = decompressed; - } else { - msg = mp_errno_to_str(o->args->items[0]); - } - if (msg[0] != '\0') { + const char *msg = mp_common_errno_to_str(o->args->items[0], decompressed, sizeof(decompressed)); + if (msg != NULL) { mp_printf(print, "[Errno " INT_FMT "] %s", MP_OBJ_SMALL_INT_VALUE(o->args->items[0]), msg); return; } @@ -215,6 +209,31 @@ void mp_obj_exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { dest[0] = MP_OBJ_FROM_PTR(self->args); } else if (self->base.type == &mp_type_StopIteration && attr == MP_QSTR_value) { dest[0] = mp_obj_exception_get_value(self_in); + #if MICROPY_CPYTHON_COMPAT + } else if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(self->base.type), MP_OBJ_FROM_PTR(&mp_type_OSError))) { + if (attr == MP_QSTR_errno) { + dest[0] = mp_obj_exception_get_value(self_in); + } else if (attr == MP_QSTR_strerror) { + if (self->args->len > 1) { + dest[0] = self->args->items[1]; + } else if (self->args->len > 0) { + char decompressed[50]; + const char *msg = mp_common_errno_to_str(self->args->items[0], decompressed, sizeof(decompressed)); + if (msg != NULL) { + dest[0] = mp_obj_new_str(msg, strlen(msg)); + } else { + dest[0] = mp_const_none; + } + } else { + dest[0] = mp_const_none; + } + } else if (attr == MP_QSTR_filename) { + dest[0] = self->args->len > 2 ? self->args->items[2] : mp_const_none; + // skip winerror + } else if (attr == MP_QSTR_filename2) { + dest[0] = self->args->len > 4 ? self->args->items[4] : mp_const_none; + } + #endif } } From 704d0c606bc4ade60fb8d650d29d74fe17e47463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Mon, 12 Nov 2018 16:17:07 +0100 Subject: [PATCH 2/2] samd51: Support more uerrno errno values Use the default MICROPY_PY_UERRNO_LIST to give libraries access to all the errno values. --- ports/atmel-samd/mpconfigport.h | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/ports/atmel-samd/mpconfigport.h b/ports/atmel-samd/mpconfigport.h index 392b0773e2..258b255ae9 100644 --- a/ports/atmel-samd/mpconfigport.h +++ b/ports/atmel-samd/mpconfigport.h @@ -163,6 +163,18 @@ typedef long mp_off_t; #define MICROPY_PY_IO (0) #define MICROPY_PY_REVERSE_SPECIAL_METHODS (0) #define MICROPY_PY_SYS_EXC_INFO (0) +#define MICROPY_PY_UERRNO_LIST \ + X(EPERM) \ + X(ENOENT) \ + X(EIO) \ + X(EAGAIN) \ + X(ENOMEM) \ + X(EACCES) \ + X(EEXIST) \ + X(ENODEV) \ + X(EISDIR) \ + X(EINVAL) \ + #endif #ifdef SAMD51 @@ -179,6 +191,7 @@ typedef long mp_off_t; #define MICROPY_PY_IO (1) #define MICROPY_PY_REVERSE_SPECIAL_METHODS (1) #define MICROPY_PY_SYS_EXC_INFO (1) +// MICROPY_PY_UERRNO_LIST - Use the default #endif #ifdef LONGINT_IMPL_NONE @@ -396,18 +409,6 @@ extern const struct _mp_obj_module_t wiznet_module; { MP_OBJ_NEW_QSTR(MP_QSTR_uheap),(mp_obj_t)&uheap_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_ustack),(mp_obj_t)&ustack_module } -#define MICROPY_PY_UERRNO_LIST \ - X(EPERM) \ - X(ENOENT) \ - X(EIO) \ - X(EAGAIN) \ - X(ENOMEM) \ - X(EACCES) \ - X(EEXIST) \ - X(ENODEV) \ - X(EISDIR) \ - X(EINVAL) \ - // We need to provide a declaration/definition of alloca() #include