From 9e29666bf99c5e5f1cb0246d420b207aacfdf408 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 19 May 2014 20:59:13 +0300 Subject: [PATCH] py: Implement proper separation between io.FileIO and io.TextIOWrapper. io.FileIO is binary I/O, ans actually optional. Default file type is io.TextIOWrapper, which provides str results. CPython3 explicitly describes io.TextIOWrapper as buffered I/O, but we don't have buffering support yet anyway. --- py/modio.c | 9 ++++++ py/mpconfig.h | 4 +++ py/qstrdefs.h | 2 ++ stmhal/file.c | 8 ++--- unix/file.c | 73 ++++++++++++++++++++++++++++++--------------- unix/mpconfigport.h | 1 + 6 files changed, 69 insertions(+), 28 deletions(-) diff --git a/py/modio.c b/py/modio.c index 3a009a7d82..b1f0d880ae 100644 --- a/py/modio.c +++ b/py/modio.c @@ -32,11 +32,20 @@ #if MICROPY_ENABLE_MOD_IO +extern const mp_obj_type_t mp_type_fileio; +extern const mp_obj_type_t mp_type_textio; + STATIC const mp_map_elem_t mp_module_io_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_io) }, // Note: mp_builtin_open_obj should be defined by port, it's not // part of the core. { MP_OBJ_NEW_QSTR(MP_QSTR_open), (mp_obj_t)&mp_builtin_open_obj }, + #if MICROPY_MOD_IO_FILEIO + { MP_OBJ_NEW_QSTR(MP_QSTR_FileIO), (mp_obj_t)&mp_type_fileio }, + #endif + #if MICROPY_CPYTHON_COMPAT + { MP_OBJ_NEW_QSTR(MP_QSTR_TextIOWrapper), (mp_obj_t)&mp_type_textio }, + #endif { MP_OBJ_NEW_QSTR(MP_QSTR_StringIO), (mp_obj_t)&mp_type_stringio }, #if MICROPY_IO_BYTESIO { MP_OBJ_NEW_QSTR(MP_QSTR_BytesIO), (mp_obj_t)&mp_type_bytesio }, diff --git a/py/mpconfig.h b/py/mpconfig.h index 53429f1c15..445fc78b04 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -225,6 +225,10 @@ typedef double mp_float_t; #define MICROPY_ENABLE_MOD_IO (1) #endif +#ifndef MICROPY_MOD_IO_FILEIO +#define MICROPY_MOD_IO_FILEIO (0) +#endif + #ifndef MICROPY_IO_BYTESIO #define MICROPY_IO_BYTESIO (1) #endif diff --git a/py/qstrdefs.h b/py/qstrdefs.h index e33d9967db..cffb64d5ef 100644 --- a/py/qstrdefs.h +++ b/py/qstrdefs.h @@ -354,6 +354,8 @@ Q(io) Q(readall) Q(readline) Q(readlines) +Q(FileIO) +Q(TextIOWrapper) Q(StringIO) Q(BytesIO) Q(getvalue) diff --git a/stmhal/file.c b/stmhal/file.c index 93bd49e669..3fea956530 100644 --- a/stmhal/file.c +++ b/stmhal/file.c @@ -41,7 +41,7 @@ typedef struct _pyb_file_obj_t { } pyb_file_obj_t; void file_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { - printf("", self_in); + printf("", 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) { @@ -94,7 +94,7 @@ STATIC const mp_stream_p_t file_obj_stream_p = { .write = file_write, }; -STATIC const mp_obj_type_t file_obj_type = { +const mp_obj_type_t mp_type_textio = { { &mp_type_type }, .name = MP_QSTR_FileIO, .make_new = file_obj_make_new, @@ -113,7 +113,7 @@ STATIC mp_obj_t file_obj_make_new(mp_obj_t type_in, uint n_args, uint n_kw, cons 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 = &file_obj_type; + self->base.type = &mp_type_textio; if (mode[0] == 'r') { // open for reading FRESULT res = f_open(&self->fp, filename, FA_READ); @@ -138,7 +138,7 @@ STATIC mp_obj_t file_obj_make_new(mp_obj_t type_in, uint n_args, uint n_kw, cons // Factory function for I/O stream classes STATIC mp_obj_t pyb_io_open(uint n_args, const mp_obj_t *args) { // TODO: analyze mode and buffering args and instantiate appropriate type - return file_obj_make_new((mp_obj_t)&file_obj_type, n_args, 0, args); + return file_obj_make_new((mp_obj_t)&mp_type_textio, n_args, 0, args); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_open_obj, 1, 2, pyb_io_open); diff --git a/unix/file.c b/unix/file.c index ada841ea2c..7f45ac4a33 100644 --- a/unix/file.c +++ b/unix/file.c @@ -54,11 +54,12 @@ void check_fd_is_open(const mp_obj_fdfile_t *o) { #define check_fd_is_open(o) #endif -STATIC const mp_obj_type_t rawfile_type; +extern const mp_obj_type_t mp_type_fileio; +extern const mp_obj_type_t mp_type_textio; STATIC void fdfile_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { mp_obj_fdfile_t *self = self_in; - print(env, "", self->fd); + print(env, "", mp_obj_get_type_str(self), self->fd); } STATIC machine_int_t fdfile_read(mp_obj_t o_in, void *buf, machine_uint_t size, int *errcode) { @@ -103,23 +104,10 @@ STATIC mp_obj_t fdfile_fileno(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(fdfile_fileno_obj, fdfile_fileno); -STATIC mp_obj_fdfile_t *fdfile_new(int fd) { - mp_obj_fdfile_t *o = m_new_obj(mp_obj_fdfile_t); - o->base.type = &rawfile_type; - o->fd = fd; - return o; -} - STATIC mp_obj_t fdfile_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { mp_obj_fdfile_t *o = m_new_obj(mp_obj_fdfile_t); - o->base.type = type_in; + mp_const_obj_t type = type_in; - if (MP_OBJ_IS_SMALL_INT(args[0])) { - o->fd = MP_OBJ_SMALL_INT_VALUE(args[0]); - return o; - } - - const char *fname = mp_obj_str_get_str(args[0]); const char *mode_s; if (n_args > 1) { mode_s = mp_obj_str_get_str(args[1]); @@ -143,14 +131,32 @@ STATIC mp_obj_t fdfile_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const case '+': mode |= O_RDWR; break; + #if MICROPY_MOD_IO_FILEIO + // If we don't have io.FileIO, then files are in text mode implicitly + case 'b': + type = &mp_type_fileio; + break; + case 't': + type = &mp_type_textio; + break; + #endif } } + o->base.type = type; + + if (MP_OBJ_IS_SMALL_INT(args[0])) { + o->fd = MP_OBJ_SMALL_INT_VALUE(args[0]); + return o; + } + + const char *fname = mp_obj_str_get_str(args[0]); int fd = open(fname, mode, 0644); if (fd == -1) { nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT((machine_int_t)errno))); } - return fdfile_new(fd); + o->fd = fd; + return o; } STATIC const mp_map_elem_t rawfile_locals_dict_table[] = { @@ -167,29 +173,48 @@ STATIC const mp_map_elem_t rawfile_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(rawfile_locals_dict, rawfile_locals_dict_table); -STATIC const mp_stream_p_t rawfile_stream_p = { +#if MICROPY_MOD_IO_FILEIO +STATIC const mp_stream_p_t fileio_stream_p = { .read = fdfile_read, .write = fdfile_write, + .is_bytes = true, }; -STATIC const mp_obj_type_t rawfile_type = { +const mp_obj_type_t mp_type_fileio = { { &mp_type_type }, .name = MP_QSTR_FileIO, .print = fdfile_print, .make_new = fdfile_make_new, .getiter = mp_identity, .iternext = mp_stream_unbuffered_iter, - .stream_p = &rawfile_stream_p, + .stream_p = &fileio_stream_p, + .locals_dict = (mp_obj_t)&rawfile_locals_dict, +}; +#endif + +STATIC const mp_stream_p_t textio_stream_p = { + .read = fdfile_read, + .write = fdfile_write, +}; + +const mp_obj_type_t mp_type_textio = { + { &mp_type_type }, + .name = MP_QSTR_TextIOWrapper, + .print = fdfile_print, + .make_new = fdfile_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 mp_obj_t mp_builtin_open(uint n_args, const mp_obj_t *args) { // TODO: analyze mode and buffering args and instantiate appropriate type - return fdfile_make_new((mp_obj_t)&rawfile_type, n_args, 0, args); + return fdfile_make_new((mp_obj_t)&mp_type_textio, n_args, 0, args); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_open_obj, 1, 2, mp_builtin_open); -const mp_obj_fdfile_t mp_sys_stdin_obj = { .base = {&rawfile_type}, .fd = STDIN_FILENO }; -const mp_obj_fdfile_t mp_sys_stdout_obj = { .base = {&rawfile_type}, .fd = STDOUT_FILENO }; -const mp_obj_fdfile_t mp_sys_stderr_obj = { .base = {&rawfile_type}, .fd = STDERR_FILENO }; +const mp_obj_fdfile_t mp_sys_stdin_obj = { .base = {&mp_type_textio}, .fd = STDIN_FILENO }; +const mp_obj_fdfile_t mp_sys_stdout_obj = { .base = {&mp_type_textio}, .fd = STDOUT_FILENO }; +const mp_obj_fdfile_t mp_sys_stderr_obj = { .base = {&mp_type_textio}, .fd = STDERR_FILENO }; diff --git a/unix/mpconfigport.h b/unix/mpconfigport.h index a78caf0509..6cac4f4cc7 100644 --- a/unix/mpconfigport.h +++ b/unix/mpconfigport.h @@ -45,6 +45,7 @@ #define MICROPY_MOD_SYS_EXIT (1) #define MICROPY_MOD_SYS_STDFILES (1) #define MICROPY_ENABLE_MOD_CMATH (1) +#define MICROPY_MOD_IO_FILEIO (1) // Define to MICROPY_ERROR_REPORTING_DETAILED to get function, etc. // names in exception messages (may require more RAM). #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_DETAILED)