summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorDamien George <damien.p.george@gmail.com>2014-06-11 15:41:14 +0100
committerDamien George <damien.p.george@gmail.com>2014-06-11 15:41:14 +0100
commitb7572ad11b31d4e357139e877a0815ebd6ae515e (patch)
tree393ba374c13c2e0f13cbcc3b3a4221ed1d4543d5
parent58cbb4d661ee40af5ef2b6a6a7f15b1b2ee5b4e5 (diff)
downloadmicropython-b7572ad11b31d4e357139e877a0815ebd6ae515e.tar.gz
micropython-b7572ad11b31d4e357139e877a0815ebd6ae515e.zip
stmhal, file: Implement a,x,+ open modes, seek and tell.
Also now returns correct POSIX errno when an IO operation fails. Addresses issues #516 and #676.
-rw-r--r--stmhal/file.c217
-rw-r--r--stmhal/mpconfigport.h2
-rw-r--r--stmhal/qstrdefsport.h4
3 files changed, 176 insertions, 47 deletions
diff --git a/stmhal/file.c b/stmhal/file.c
index 3fea956530..fbaff879f4 100644
--- a/stmhal/file.c
+++ b/stmhal/file.c
@@ -25,9 +25,11 @@
*/
#include <stdio.h>
+#include <errno.h>
-#include "misc.h"
#include "mpconfig.h"
+#include "nlr.h"
+#include "misc.h"
#include "qstr.h"
#include "obj.h"
#include "runtime.h"
@@ -35,6 +37,33 @@
#include "file.h"
#include "ff.h"
+extern const mp_obj_type_t mp_type_fileio;
+extern const mp_obj_type_t mp_type_textio;
+
+// this table converts from FRESULT to POSIX errno
+STATIC const byte fresult_to_errno_table[] = {
+ [FR_OK] = 0,
+ [FR_DISK_ERR] = EIO,
+ [FR_INT_ERR] = EIO,
+ [FR_NOT_READY] = EBUSY,
+ [FR_NO_FILE] = ENOENT,
+ [FR_NO_PATH] = ENOENT,
+ [FR_INVALID_NAME] = EINVAL,
+ [FR_DENIED] = EACCES,
+ [FR_EXIST] = EEXIST,
+ [FR_INVALID_OBJECT] = EINVAL,
+ [FR_WRITE_PROTECTED] = EROFS,
+ [FR_INVALID_DRIVE] = ENODEV,
+ [FR_NOT_ENABLED] = ENODEV,
+ [FR_NO_FILESYSTEM] = ENODEV,
+ [FR_MKFS_ABORTED] = EIO,
+ [FR_TIMEOUT] = EIO,
+ [FR_LOCKED] = EIO,
+ [FR_NOT_ENOUGH_CORE] = ENOMEM,
+ [FR_TOO_MANY_OPEN_FILES] = EMFILE,
+ [FR_INVALID_PARAMETER] = EINVAL,
+};
+
typedef struct _pyb_file_obj_t {
mp_obj_base_t base;
FIL fp;
@@ -44,17 +73,25 @@ void file_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, m
printf("<io.%s %p>", mp_obj_get_type_str(self_in), self_in);
}
-STATIC machine_int_t file_read(mp_obj_t self_in, void *buf, machine_uint_t size, int *errcode) {
+STATIC machine_int_t file_obj_read(mp_obj_t self_in, void *buf, machine_uint_t size, int *errcode) {
pyb_file_obj_t *self = self_in;
UINT sz_out;
- *errcode = f_read(&self->fp, buf, size, &sz_out);
+ FRESULT res = f_read(&self->fp, buf, size, &sz_out);
+ if (res != FR_OK) {
+ *errcode = fresult_to_errno_table[res];
+ return -1;
+ }
return sz_out;
}
-STATIC machine_int_t file_write(mp_obj_t self_in, const void *buf, machine_uint_t size, int *errcode) {
+STATIC machine_int_t file_obj_write(mp_obj_t self_in, const void *buf, machine_uint_t size, int *errcode) {
pyb_file_obj_t *self = self_in;
UINT sz_out;
- *errcode = f_write(&self->fp, buf, size, &sz_out);
+ FRESULT res = f_write(&self->fp, buf, size, &sz_out);
+ if (res != FR_OK) {
+ *errcode = fresult_to_errno_table[res];
+ return -1;
+ }
return sz_out;
}
@@ -63,77 +100,163 @@ mp_obj_t file_obj_close(mp_obj_t self_in) {
f_close(&self->fp);
return mp_const_none;
}
-
STATIC MP_DEFINE_CONST_FUN_OBJ_1(file_obj_close_obj, file_obj_close);
mp_obj_t file_obj___exit__(uint n_args, const mp_obj_t *args) {
return file_obj_close(args[0]);
}
-static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(file_obj___exit___obj, 4, 4, file_obj___exit__);
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(file_obj___exit___obj, 4, 4, file_obj___exit__);
+
+mp_obj_t file_obj_seek(uint n_args, const mp_obj_t *args) {
+ pyb_file_obj_t *self = args[0];
+ machine_int_t offset = mp_obj_get_int(args[1]);
+ machine_int_t whence = 0;
+ if (n_args == 3) {
+ whence = mp_obj_get_int(args[2]);
+ }
+
+ switch (whence) {
+ case 0: // SEEK_SET
+ f_lseek(&self->fp, offset);
+ break;
+
+ case 1: // SEEK_CUR
+ if (offset != 0) {
+ goto error;
+ }
+ // no-operation
+ break;
+
+ case 2: // SEEK_END
+ if (offset != 0) {
+ goto error;
+ }
+ f_lseek(&self->fp, f_size(&self->fp));
+ break;
+
+ default:
+ goto error;
+ }
+
+ return mp_obj_new_int_from_uint(f_tell(&self->fp));
+
+error:
+ // A bad whence is a ValueError, while offset!=0 is an io.UnsupportedOperation.
+ // But the latter inherits ValueError (as well as IOError), so we just raise ValueError.
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "invalid whence and/or offset"));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(file_obj_seek_obj, 2, 3, file_obj_seek);
+
+mp_obj_t file_obj_tell(mp_obj_t self_in) {
+ pyb_file_obj_t *self = self_in;
+ return mp_obj_new_int_from_uint(f_tell(&self->fp));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(file_obj_tell_obj, file_obj_tell);
+
+STATIC mp_obj_t file_obj_make_new(mp_obj_t type, uint n_args, uint n_kw, const mp_obj_t *args) {
+ mp_arg_check_num(n_args, n_kw, 1, 2, false);
+
+ const char *fname = mp_obj_str_get_str(args[0]);
+
+ int mode = 0;
+ if (n_args == 1) {
+ mode = FA_READ;
+ } else {
+ const char *mode_s = mp_obj_str_get_str(args[1]);
+ // TODO make sure only one of r, w, x, a, and b, t are specified
+ while (*mode_s) {
+ switch (*mode_s++) {
+ case 'r':
+ mode |= FA_READ;
+ break;
+ case 'w':
+ mode |= FA_WRITE | FA_CREATE_ALWAYS;
+ break;
+ case 'x':
+ mode |= FA_WRITE | FA_CREATE_NEW;
+ break;
+ case 'a':
+ mode |= FA_WRITE | FA_OPEN_ALWAYS;
+ break;
+ case '+':
+ mode |= FA_READ | FA_WRITE;
+ break;
+ #if MICROPY_PY_IO_FILEIO
+ case 'b':
+ type = (mp_obj_t)&mp_type_fileio;
+ break;
+ #endif
+ case 't':
+ type = (mp_obj_t)&mp_type_textio;
+ break;
+ }
+ }
+ }
+
+ pyb_file_obj_t *o = m_new_obj_with_finaliser(pyb_file_obj_t);
+ o->base.type = type;
+
+ FRESULT res = f_open(&o->fp, fname, mode);
+ if (res != FR_OK) {
+ m_del_obj(pyb_file_obj_t, o);
+ nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT((machine_int_t)fresult_to_errno_table[res])));
+ }
+
+ return o;
+}
// TODO gc hook to close the file if not already closed
-STATIC const mp_map_elem_t file_locals_dict_table[] = {
+STATIC const mp_map_elem_t rawfile_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_readall), (mp_obj_t)&mp_stream_readall_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_readline), (mp_obj_t)&mp_stream_unbuffered_readline_obj},
{ MP_OBJ_NEW_QSTR(MP_QSTR_readlines), (mp_obj_t)&mp_stream_unbuffered_readlines_obj},
{ MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&mp_stream_write_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t)&file_obj_close_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_seek), (mp_obj_t)&file_obj_seek_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_tell), (mp_obj_t)&file_obj_tell_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR___del__), (mp_obj_t)&file_obj_close_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR___enter__), (mp_obj_t)&mp_identity_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR___exit__), (mp_obj_t)&file_obj___exit___obj },
};
-STATIC MP_DEFINE_CONST_DICT(file_locals_dict, file_locals_dict_table);
-
-STATIC mp_obj_t file_obj_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args);
+STATIC MP_DEFINE_CONST_DICT(rawfile_locals_dict, rawfile_locals_dict_table);
-STATIC const mp_stream_p_t file_obj_stream_p = {
- .read = file_read,
- .write = file_write,
+#if MICROPY_PY_IO_FILEIO
+STATIC const mp_stream_p_t fileio_stream_p = {
+ .read = file_obj_read,
+ .write = file_obj_write,
+ .is_bytes = true,
};
-const mp_obj_type_t mp_type_textio = {
+const mp_obj_type_t mp_type_fileio = {
{ &mp_type_type },
.name = MP_QSTR_FileIO,
- .make_new = file_obj_make_new,
.print = file_obj_print,
+ .make_new = file_obj_make_new,
.getiter = mp_identity,
.iternext = mp_stream_unbuffered_iter,
- .stream_p = &file_obj_stream_p,
- .locals_dict = (mp_obj_t)&file_locals_dict,
+ .stream_p = &fileio_stream_p,
+ .locals_dict = (mp_obj_t)&rawfile_locals_dict,
};
+#endif
-STATIC mp_obj_t file_obj_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
- mp_arg_check_num(n_args, n_kw, 1, 2, false);
- const char *filename = mp_obj_str_get_str(args[0]);
- const char *mode = "r";
- if (n_args > 1) {
- mode = mp_obj_str_get_str(args[1]);
- }
- pyb_file_obj_t *self = m_new_obj_with_finaliser(pyb_file_obj_t);
- self->base.type = &mp_type_textio;
- if (mode[0] == 'r') {
- // open for reading
- FRESULT res = f_open(&self->fp, filename, FA_READ);
- if (res != FR_OK) {
- printf("FileNotFoundError: [Errno 2] No such file or directory: '%s'\n", filename);
- return mp_const_none;
- }
- } else if (mode[0] == 'w') {
- // open for writing, truncate the file first
- FRESULT res = f_open(&self->fp, filename, FA_WRITE | FA_CREATE_ALWAYS);
- if (res != FR_OK) {
- printf("?FileError: could not create file: '%s'\n", filename);
- return mp_const_none;
- }
- } else {
- printf("ValueError: invalid mode: '%s'\n", mode);
- return mp_const_none;
- }
- return self;
-}
+STATIC const mp_stream_p_t textio_stream_p = {
+ .read = file_obj_read,
+ .write = file_obj_write,
+};
+
+const mp_obj_type_t mp_type_textio = {
+ { &mp_type_type },
+ .name = MP_QSTR_TextIOWrapper,
+ .print = file_obj_print,
+ .make_new = file_obj_make_new,
+ .getiter = mp_identity,
+ .iternext = mp_stream_unbuffered_iter,
+ .stream_p = &textio_stream_p,
+ .locals_dict = (mp_obj_t)&rawfile_locals_dict,
+};
// Factory function for I/O stream classes
STATIC mp_obj_t pyb_io_open(uint n_args, const mp_obj_t *args) {
diff --git a/stmhal/mpconfigport.h b/stmhal/mpconfigport.h
index 5b99ffca55..28cd90bb01 100644
--- a/stmhal/mpconfigport.h
+++ b/stmhal/mpconfigport.h
@@ -48,6 +48,8 @@
#define MICROPY_PY_SYS_EXIT (1)
#define MICROPY_PY_SYS_STDFILES (1)
#define MICROPY_PY_CMATH (1)
+#define MICROPY_PY_IO (1)
+#define MICROPY_PY_IO_FILEIO (1)
// extra built in names to add to the global namespace
extern const struct _mp_obj_fun_native_t mp_builtin_help_obj;
diff --git a/stmhal/qstrdefsport.h b/stmhal/qstrdefsport.h
index d822a542f0..19a5d7c863 100644
--- a/stmhal/qstrdefsport.h
+++ b/stmhal/qstrdefsport.h
@@ -66,6 +66,10 @@ Q(0:/)
Q(0:/lib)
Q(millis)
+// for file class
+Q(seek)
+Q(tell)
+
// for RTC class
Q(RTC)
Q(info)