From d0f3ec887e36ef2716fd305bcfb2634e4bbcbbd6 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Fri, 3 Jul 2020 20:06:13 +0200 Subject: [PATCH 01/14] [mpy/files] Added ion.file --- ion/include/ion/storage.h | 4 +- python/Makefile | 1 + python/port/genhdr/qstrdefs.in.h | 4 + python/port/mod/ion/file.cpp | 265 +++++++++++++++++++++++++++ python/port/mod/ion/modion.h | 1 + python/port/mod/ion/modion_table.cpp | 2 + 6 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 python/port/mod/ion/file.cpp diff --git a/ion/include/ion/storage.h b/ion/include/ion/storage.h index e18607612..070cc5366 100644 --- a/ion/include/ion/storage.h +++ b/ion/include/ion/storage.h @@ -120,6 +120,9 @@ public: void destroyRecordWithBaseNameAndExtension(const char * baseName, const char * extension); void destroyRecordsWithExtension(const char * extension); + // Useful + static bool FullNameCompliant(const char * name); + private: constexpr static uint32_t Magic = 0xEE0BDDBA; constexpr static size_t k_maxRecordSize = (1 << sizeof(record_size_t)*8); @@ -149,7 +152,6 @@ private: bool isFullNameTaken(const char * fullName, const Record * recordToExclude = nullptr); bool isBaseNameWithExtensionTaken(const char * baseName, const char * extension, Record * recordToExclude = nullptr); bool isNameOfRecordTaken(Record r, const Record * recordToExclude); - static bool FullNameCompliant(const char * name); char * endBuffer(); size_t sizeOfBaseNameAndExtension(const char * baseName, const char * extension) const; size_t sizeOfRecordWithBaseNameAndExtension(const char * baseName, const char * extension, size_t size) const; diff --git a/python/Makefile b/python/Makefile index 503d7fad1..14a556cd8 100644 --- a/python/Makefile +++ b/python/Makefile @@ -136,6 +136,7 @@ port_src += $(addprefix python/port/,\ helpers.c \ mod/ion/modion.cpp \ mod/ion/modion_table.cpp \ + mod/ion/file.cpp \ mod/kandinsky/modkandinsky.cpp \ mod/kandinsky/modkandinsky_table.c \ mod/matplotlib/modmatplotlib.cpp \ diff --git a/python/port/genhdr/qstrdefs.in.h b/python/port/genhdr/qstrdefs.in.h index 6670adc17..2650ca5ab 100644 --- a/python/port/genhdr/qstrdefs.in.h +++ b/python/port/genhdr/qstrdefs.in.h @@ -493,3 +493,7 @@ Q(colormode) Q(time) Q(sleep) Q(monotonic) + +// file QSTRs +Q(file) + diff --git a/python/port/mod/ion/file.cpp b/python/port/mod/ion/file.cpp new file mode 100644 index 000000000..388cbf07e --- /dev/null +++ b/python/port/mod/ion/file.cpp @@ -0,0 +1,265 @@ +extern "C" { +#include +#include +#include +#include +#include +#include +#include +#include +} + +#include +#include + +STATIC void file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); +STATIC mp_obj_t file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); + +extern const mp_obj_type_t file_type = { + { &mp_type_type }, + 0, + MP_QSTR_file, + file_print, + file_make_new, +}; + +typedef enum _file_location_t { + RAM = 0, FLASH = 1 +} file_location_t; + +typedef enum _file_mode_t { + READ = 0, WRITE = 1, APPEND = 2, CREATE = 3 +} file_mode_t; + +typedef enum _file_bin_t { + TEXT = 0, BINARY = 1 +} file_bin_t; + + +typedef struct _file_obj_t { + mp_obj_base_t base; + + mp_obj_t name; + + file_mode_t open_mode; + bool edit_mode; + file_bin_t binary_mode; + + file_location_t location; + // If location is set to RAM, record is used. + Ion::Storage::Record record; + + uint16_t position; + +} file_obj_t; + +STATIC void file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + file_obj_t *self = (file_obj_t*) MP_OBJ_TO_PTR(self_in); + + size_t l; + const char* file_name = mp_obj_str_get_data(self->name, &l); + + uint8_t offset = 0; + char file_mode[4] = {0, 0, 0, 0}; + + switch(self->open_mode) { + case READ: + file_mode[0] = 'r'; + break; + case WRITE: + file_mode[0] = 'w'; + break; + case APPEND: + file_mode[0] = 'a'; + break; + case CREATE: + file_mode[0] = 'x'; + break; + } + + if (self->edit_mode) { + file_mode[1] = '+'; + offset = 1; + } + + switch(self->binary_mode) { + case TEXT: + file_mode[1 + offset] = 't'; + break; + case BINARY: + file_mode[1 + offset] = 'b'; + break; + } + + + mp_print_str(print, ""); +} + +/* + * File constructor takes two arguments: + * - name: name of the file + * - mode: mode of opening (optional, r by default) + * + * File system organisation: + * - /ram/ + * Contains all files in RAM (python, poincare stuff, etc..) + * - /flash/ + * Contains all files in external's TAR archive + * + * When nme doesn't start with "/", we use RAM. + */ +STATIC mp_obj_t file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 2, true); + + file_obj_t *file = m_new_obj(file_obj_t); + + if (!mp_obj_is_str(args[0])) { + mp_raise_ValueError("First argument must be a string!"); + } + + // Store and parse file name + size_t l; + const char* file_name = mp_obj_str_get_data(args[0], &l); + file->name = mp_obj_new_str(file_name, l); + + // Parses file mode + file->open_mode = READ; + file->edit_mode = false; + file->binary_mode = TEXT; + + if (n_args == 2) { + if (!mp_obj_is_str(args[1])) { + mp_raise_ValueError("Second argument must be a string!"); + } + + const char* file_mode = mp_obj_str_get_data(args[1], &l); + + for(size_t i = 0; i < l; i++) { + char c = file_mode[i]; + + switch(c) { + case 'r': + file->open_mode = READ; + break; + case 'w': + file->open_mode = WRITE; + break; + case 'a': + file->open_mode = APPEND; + break; + case 'x': + file->open_mode = CREATE; + break; + case '+': + file->edit_mode = true; + break; + case 't': + file->binary_mode = TEXT; + break; + case 'b': + file->binary_mode = BINARY; + break; + } + } + } + + // If "", throw file not found + if (l > 0) { + file->location = RAM; + // Check location (RAM/FLASH) + if (file_name[0] == '/') { + if (strncmp(file_name, "/ram/", 5) == 0) { + file->location = RAM; + file_name = file_name + 5; + // } else if (strncmp(file_name, "/flash/", 7)) { + // file->location = FLASH; + // file_name = file_name + 7; + } else { + mp_raise_OSError(2); + } + } + } else { + mp_raise_OSError(2); + } + + if (!Ion::Storage::FullNameCompliant(file_name)) { + mp_raise_OSError(22); + } + + if(file->location == RAM) { + Ion::Storage::Record::ErrorStatus status; + + switch(file->open_mode) { + case READ: + file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); + file->position = 0; + if (file->record == Ion::Storage::Record()) { + mp_raise_OSError(2); + } + break; + case CREATE: + file->position = 0; + status = Ion::Storage::sharedStorage()->createRecordWithFullName(file_name, "", 0); + switch (status) { + case Ion::Storage::Record::ErrorStatus::NameTaken: + mp_raise_OSError(17); + break; + case Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable: + mp_raise_OSError(28); + break; + default: + break; + } + file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); + break; + case WRITE: + file->position = 0; + status = Ion::Storage::sharedStorage()->createRecordWithFullName(file_name, "", 0); + switch (status) { + case Ion::Storage::Record::ErrorStatus::NameTaken: + // setValue messes with empty buffer, so we delete record and re-create it. + file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); + file->record.destroy(); + + status = Ion::Storage::sharedStorage()->createRecordWithFullName(file_name, "", 0); + + if (status == Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable) { + mp_raise_OSError(28); + } + break; + case Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable: + mp_raise_OSError(28); + break; + default: + break; + } + file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); + break; + case APPEND: + file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); + file->position = 0; + if (file->record == Ion::Storage::Record()) { + status = Ion::Storage::sharedStorage()->createRecordWithFullName(file_name, "", 0); + if (status == Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable) { + mp_raise_OSError(28); + } + } + file->position = file->record.value().size; + break; + } + } + + // mp_raise_FileNotFoundError("Test"); + + + file->base.type = &file_type; + return MP_OBJ_FROM_PTR(file); +} + + + diff --git a/python/port/mod/ion/modion.h b/python/port/mod/ion/modion.h index d8ffe219b..4b4558be5 100644 --- a/python/port/mod/ion/modion.h +++ b/python/port/mod/ion/modion.h @@ -1,3 +1,4 @@ #include mp_obj_t modion_keyboard_keydown(mp_obj_t key_o); +extern const mp_obj_type_t file_type; diff --git a/python/port/mod/ion/modion_table.cpp b/python/port/mod/ion/modion_table.cpp index aed204c1f..b1d861f51 100644 --- a/python/port/mod/ion/modion_table.cpp +++ b/python/port/mod/ion/modion_table.cpp @@ -65,6 +65,8 @@ STATIC const mp_rom_map_elem_t modion_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_KEY_EE), MP_OBJ_NEW_SMALL_INT(Ion::Keyboard::Key::EE) }, { MP_ROM_QSTR(MP_QSTR_KEY_ANS), MP_OBJ_NEW_SMALL_INT(Ion::Keyboard::Key::Ans) }, { MP_ROM_QSTR(MP_QSTR_KEY_EXE), MP_OBJ_NEW_SMALL_INT(Ion::Keyboard::Key::EXE) }, + + { MP_ROM_QSTR(MP_QSTR_file), (mp_obj_t)&file_type} }; STATIC MP_DEFINE_CONST_DICT(modion_module_globals, modion_module_globals_table); From a20ac589ec5cc4b2028c1cf8a549da787699c7d3 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sat, 4 Jul 2020 12:06:44 +0200 Subject: [PATCH 02/14] [mpy/files] seek, tell, seekable --- python/port/genhdr/qstrdefs.in.h | 6 ++ python/port/mod/ion/file.cpp | 138 ++++++++++++++++++++++++++++--- 2 files changed, 134 insertions(+), 10 deletions(-) diff --git a/python/port/genhdr/qstrdefs.in.h b/python/port/genhdr/qstrdefs.in.h index 2650ca5ab..7c9d75295 100644 --- a/python/port/genhdr/qstrdefs.in.h +++ b/python/port/genhdr/qstrdefs.in.h @@ -496,4 +496,10 @@ Q(monotonic) // file QSTRs Q(file) +Q(tell) +Q(seek) +Q(seekable) +Q(SEEK_SET) +Q(SEEK_CUR) +Q(SEEK_END) diff --git a/python/port/mod/ion/file.cpp b/python/port/mod/ion/file.cpp index 388cbf07e..76ca1b97f 100644 --- a/python/port/mod/ion/file.cpp +++ b/python/port/mod/ion/file.cpp @@ -15,12 +15,57 @@ extern "C" { STATIC void file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); STATIC mp_obj_t file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); +STATIC mp_obj_t file_tell(mp_obj_t o_in); + +const mp_obj_fun_builtin_fixed_t file_tell_obj = { + {&mp_type_fun_builtin_1}, + {(mp_fun_0_t)file_tell} +}; + +STATIC mp_obj_t file_seek(size_t n_args, const mp_obj_t* args); + +const mp_obj_fun_builtin_var_t file_seek_obj = { + {&mp_type_fun_builtin_var}, + MP_OBJ_FUN_MAKE_SIG(2, 3, false), + {(mp_fun_var_t)file_seek} +}; + +STATIC mp_obj_t file_seekable(mp_obj_t o_in); + +const mp_obj_fun_builtin_fixed_t file_seekable_obj = { + {&mp_type_fun_builtin_1}, + {(mp_fun_0_t)file_seekable} +}; + +STATIC const mp_rom_map_elem_t file_type_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR_SEEK_SET), MP_ROM_INT(0) }, + { MP_ROM_QSTR(MP_QSTR_SEEK_CUR), MP_ROM_INT(1) }, + { MP_ROM_QSTR(MP_QSTR_SEEK_END), MP_ROM_INT(2) }, + + { MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&file_tell_obj)}, + { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&file_seek_obj)}, + { MP_ROM_QSTR(MP_QSTR_seekable), MP_ROM_PTR(&file_seekable_obj)}, +}; + +STATIC MP_DEFINE_CONST_DICT(file_type_globals, file_type_globals_table); + extern const mp_obj_type_t file_type = { - { &mp_type_type }, - 0, - MP_QSTR_file, - file_print, - file_make_new, + { &mp_type_type }, // base + 0, // flags + MP_QSTR_file, // name + file_print, // __repr__, __srt__ + file_make_new, // __new__, __init__ + nullptr, // __call__ + nullptr, // unary operations + nullptr, // binary operations + nullptr, // load, store, delete attributes + nullptr, // load, store, delete subscripting + nullptr, // __iter__ -> TODO! + nullptr, // __next__ + nullptr, // buffer + nullptr, // protocol + nullptr, // parent + (mp_obj_dict_t*) &file_type_globals // globals table }; typedef enum _file_location_t { @@ -119,7 +164,7 @@ STATIC mp_obj_t file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n file_obj_t *file = m_new_obj(file_obj_t); if (!mp_obj_is_str(args[0])) { - mp_raise_ValueError("First argument must be a string!"); + mp_raise_ValueError("path must be a string!"); } // Store and parse file name @@ -134,7 +179,7 @@ STATIC mp_obj_t file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n if (n_args == 2) { if (!mp_obj_is_str(args[1])) { - mp_raise_ValueError("Second argument must be a string!"); + mp_raise_ValueError("mode must be a string!"); } const char* file_mode = mp_obj_str_get_data(args[1], &l); @@ -252,14 +297,87 @@ STATIC mp_obj_t file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n file->position = file->record.value().size; break; } + } else { + mp_raise_OSError(2); } - // mp_raise_FileNotFoundError("Test"); - - file->base.type = &file_type; return MP_OBJ_FROM_PTR(file); } +STATIC mp_obj_t file_tell(mp_obj_t o_in) { + if(!mp_obj_is_type(o_in, &file_type)) { + mp_raise_TypeError("self must be a file!"); + } + + file_obj_t* file = (file_obj_t*) MP_OBJ_TO_PTR(o_in); + + return mp_obj_new_int(file->position); +} + +STATIC mp_obj_t file_seek(size_t n_args, const mp_obj_t* args) { + mp_arg_check_num(n_args, 0, 2, 3, false); + + if(!mp_obj_is_type(args[0], &file_type)) { + mp_raise_TypeError("self must be a file!"); + } + + file_obj_t *file = (file_obj_t*) MP_OBJ_TO_PTR(args[0]); + + if (!mp_obj_is_integer(args[1])) { + mp_raise_ValueError("offset must be an int!"); + } + + mp_int_t position = mp_obj_get_int(args[1]); + mp_int_t whence = 0; + + if (n_args > 2) { + if (!mp_obj_is_integer(args[2])) { + mp_raise_ValueError("whence must be an int!"); + } + + whence = mp_obj_get_int(args[2]); + } + + mp_int_t new_position = file->position; + size_t file_size = 0; + + if(file->location == RAM) { + file_size = file->record.value().size; + } + + switch (whence) { + // SEEK_SET + case 0: + new_position = position; + break; + // SEEK_CUR + case 1: + new_position += position; + break; + // SEEK_END + case 2: + new_position = file_size + position; + break; + default: + mp_raise_ValueError("invalid whence (should be 0, 1 or 2)"); + } + + if (new_position < 0) { + mp_raise_ValueError("negative seek position"); + } else { + file->position = (size_t) new_position; + } + + return mp_obj_new_int(file->position); +} + +STATIC mp_obj_t file_seekable(mp_obj_t o_in) { + if(!mp_obj_is_type(o_in, &file_type)) { + mp_raise_TypeError("self must be a file!"); + } + + return mp_obj_new_bool(1); +} From 420edf609282de3020a2e8156b2cda4cd8aa8a07 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sat, 4 Jul 2020 14:41:57 +0200 Subject: [PATCH 03/14] [mpy/files] close --- python/port/genhdr/qstrdefs.in.h | 2 ++ python/port/mod/ion/file.cpp | 51 ++++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/python/port/genhdr/qstrdefs.in.h b/python/port/genhdr/qstrdefs.in.h index 7c9d75295..dae368ec4 100644 --- a/python/port/genhdr/qstrdefs.in.h +++ b/python/port/genhdr/qstrdefs.in.h @@ -498,6 +498,8 @@ Q(monotonic) Q(file) Q(tell) Q(seek) +Q(close) +Q(closed) Q(seekable) Q(SEEK_SET) Q(SEEK_CUR) diff --git a/python/port/mod/ion/file.cpp b/python/port/mod/ion/file.cpp index 76ca1b97f..f248e6a22 100644 --- a/python/port/mod/ion/file.cpp +++ b/python/port/mod/ion/file.cpp @@ -37,11 +37,19 @@ const mp_obj_fun_builtin_fixed_t file_seekable_obj = { {(mp_fun_0_t)file_seekable} }; +STATIC mp_obj_t file_close(mp_obj_t o_in); + +const mp_obj_fun_builtin_fixed_t file_close_obj = { + {&mp_type_fun_builtin_1}, + {(mp_fun_0_t)file_close} +}; + STATIC const mp_rom_map_elem_t file_type_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SEEK_SET), MP_ROM_INT(0) }, { MP_ROM_QSTR(MP_QSTR_SEEK_CUR), MP_ROM_INT(1) }, { MP_ROM_QSTR(MP_QSTR_SEEK_END), MP_ROM_INT(2) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&file_close_obj)}, { MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&file_tell_obj)}, { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&file_seek_obj)}, { MP_ROM_QSTR(MP_QSTR_seekable), MP_ROM_PTR(&file_seekable_obj)}, @@ -96,6 +104,8 @@ typedef struct _file_obj_t { uint16_t position; + bool closed; + } file_obj_t; STATIC void file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { @@ -142,7 +152,10 @@ STATIC void file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ mp_print_str(print, file_name); mp_print_str(print, "' mode='"); mp_print_str(print, file_mode); - mp_print_str(print, "'>"); + mp_print_str(print, "'"); + if (self->closed) + mp_print_str(print, " closed"); + mp_print_str(print, ">"); } /* @@ -305,6 +318,32 @@ STATIC mp_obj_t file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n return MP_OBJ_FROM_PTR(file); } +void check_closed(file_obj_t* file) { + if (file->closed) + mp_raise_ValueError("I/O operation on closed file"); +} + + +// Methods + +STATIC mp_obj_t file_close(mp_obj_t o_in) { + if(!mp_obj_is_type(o_in, &file_type)) { + mp_raise_TypeError("self must be a file!"); + } + + file_obj_t* file = (file_obj_t*) MP_OBJ_TO_PTR(o_in); + + if (!file->closed) { + if(file->location == RAM) { + file->record = Ion::Storage::Record(); + } + + file->closed = true; + } + + return mp_const_none; +} + STATIC mp_obj_t file_tell(mp_obj_t o_in) { if(!mp_obj_is_type(o_in, &file_type)) { @@ -313,6 +352,8 @@ STATIC mp_obj_t file_tell(mp_obj_t o_in) { file_obj_t* file = (file_obj_t*) MP_OBJ_TO_PTR(o_in); + check_closed(file); + return mp_obj_new_int(file->position); } @@ -325,6 +366,8 @@ STATIC mp_obj_t file_seek(size_t n_args, const mp_obj_t* args) { file_obj_t *file = (file_obj_t*) MP_OBJ_TO_PTR(args[0]); + check_closed(file); + if (!mp_obj_is_integer(args[1])) { mp_raise_ValueError("offset must be an int!"); } @@ -378,6 +421,10 @@ STATIC mp_obj_t file_seekable(mp_obj_t o_in) { mp_raise_TypeError("self must be a file!"); } - return mp_obj_new_bool(1); + file_obj_t *file = (file_obj_t*) MP_OBJ_TO_PTR(o_in); + + check_closed(file); + + return mp_const_true; } From 7d61f867cf2aa559cb577fe6bd9570a67f09038a Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sat, 4 Jul 2020 21:45:30 +0200 Subject: [PATCH 04/14] [mpy/files] closed attribute --- python/port/mod/ion/file.cpp | 39 ++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/python/port/mod/ion/file.cpp b/python/port/mod/ion/file.cpp index f248e6a22..f7e259514 100644 --- a/python/port/mod/ion/file.cpp +++ b/python/port/mod/ion/file.cpp @@ -14,6 +14,7 @@ extern "C" { STATIC void file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); STATIC mp_obj_t file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); +STATIC void file_attr(mp_obj_t self_in, qstr attribute, mp_obj_t *destination); STATIC mp_obj_t file_tell(mp_obj_t o_in); @@ -48,11 +49,6 @@ STATIC const mp_rom_map_elem_t file_type_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SEEK_SET), MP_ROM_INT(0) }, { MP_ROM_QSTR(MP_QSTR_SEEK_CUR), MP_ROM_INT(1) }, { MP_ROM_QSTR(MP_QSTR_SEEK_END), MP_ROM_INT(2) }, - - { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&file_close_obj)}, - { MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&file_tell_obj)}, - { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&file_seek_obj)}, - { MP_ROM_QSTR(MP_QSTR_seekable), MP_ROM_PTR(&file_seekable_obj)}, }; STATIC MP_DEFINE_CONST_DICT(file_type_globals, file_type_globals_table); @@ -66,7 +62,7 @@ extern const mp_obj_type_t file_type = { nullptr, // __call__ nullptr, // unary operations nullptr, // binary operations - nullptr, // load, store, delete attributes + file_attr, // load, store, delete attributes nullptr, // load, store, delete subscripting nullptr, // __iter__ -> TODO! nullptr, // __next__ @@ -108,6 +104,37 @@ typedef struct _file_obj_t { } file_obj_t; +STATIC void file_attr(mp_obj_t self_in, qstr attribute, mp_obj_t *destination) { + file_obj_t *self = (file_obj_t*) MP_OBJ_TO_PTR(self_in); + + if (destination[0] == nullptr) { + switch(attribute) { + case MP_QSTR_closed: + destination[0] = mp_obj_new_bool(self->closed); + break; + case MP_QSTR_close: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_close_obj); + destination[1] = self_in; + break; + case MP_QSTR_tell: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_tell_obj); + destination[1] = self_in; + break; + case MP_QSTR_seek: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_seek_obj); + destination[1] = self_in; + break; + case MP_QSTR_seekable: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_seekable_obj); + destination[1] = self_in; + break; + default: + break; + } + } + +} + STATIC void file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; file_obj_t *self = (file_obj_t*) MP_OBJ_TO_PTR(self_in); From 8178eadd49b1b2cd02ba5ed90dc0e1febfd2b1ed Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sat, 4 Jul 2020 23:14:32 +0200 Subject: [PATCH 05/14] [mpy/files] read --- python/port/genhdr/qstrdefs.in.h | 4 + python/port/mod/ion/file.cpp | 143 +++++++++++++++++++++++++++++-- 2 files changed, 142 insertions(+), 5 deletions(-) diff --git a/python/port/genhdr/qstrdefs.in.h b/python/port/genhdr/qstrdefs.in.h index dae368ec4..194bd5e29 100644 --- a/python/port/genhdr/qstrdefs.in.h +++ b/python/port/genhdr/qstrdefs.in.h @@ -500,6 +500,10 @@ Q(tell) Q(seek) Q(close) Q(closed) +Q(fileno) +Q(flush) +Q(isatty) +Q(readable) Q(seekable) Q(SEEK_SET) Q(SEEK_CUR) diff --git a/python/port/mod/ion/file.cpp b/python/port/mod/ion/file.cpp index f7e259514..87651a5f5 100644 --- a/python/port/mod/ion/file.cpp +++ b/python/port/mod/ion/file.cpp @@ -9,6 +9,7 @@ extern "C" { #include } +#include #include #include @@ -31,6 +32,14 @@ const mp_obj_fun_builtin_var_t file_seek_obj = { {(mp_fun_var_t)file_seek} }; +STATIC mp_obj_t file_read(size_t n_args, const mp_obj_t* args); + +const mp_obj_fun_builtin_var_t file_read_obj = { + {&mp_type_fun_builtin_var}, + MP_OBJ_FUN_MAKE_SIG(1, 2, false), + {(mp_fun_var_t)file_read} +}; + STATIC mp_obj_t file_seekable(mp_obj_t o_in); const mp_obj_fun_builtin_fixed_t file_seekable_obj = { @@ -45,6 +54,34 @@ const mp_obj_fun_builtin_fixed_t file_close_obj = { {(mp_fun_0_t)file_close} }; +STATIC mp_obj_t file_fileno(mp_obj_t o_in); + +const mp_obj_fun_builtin_fixed_t file_fileno_obj = { + {&mp_type_fun_builtin_1}, + {(mp_fun_0_t)file_fileno} +}; + +STATIC mp_obj_t file_flush(mp_obj_t o_in); + +const mp_obj_fun_builtin_fixed_t file_flush_obj = { + {&mp_type_fun_builtin_1}, + {(mp_fun_0_t)file_flush} +}; + +STATIC mp_obj_t file_isatty(mp_obj_t o_in); + +const mp_obj_fun_builtin_fixed_t file_isatty_obj = { + {&mp_type_fun_builtin_1}, + {(mp_fun_0_t)file_isatty} +}; + +STATIC mp_obj_t file_readable(mp_obj_t o_in); + +const mp_obj_fun_builtin_fixed_t file_readable_obj = { + {&mp_type_fun_builtin_1}, + {(mp_fun_0_t)file_readable} +}; + STATIC const mp_rom_map_elem_t file_type_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SEEK_SET), MP_ROM_INT(0) }, { MP_ROM_QSTR(MP_QSTR_SEEK_CUR), MP_ROM_INT(1) }, @@ -105,8 +142,9 @@ typedef struct _file_obj_t { } file_obj_t; STATIC void file_attr(mp_obj_t self_in, qstr attribute, mp_obj_t *destination) { + destination[1] = self_in; file_obj_t *self = (file_obj_t*) MP_OBJ_TO_PTR(self_in); - + if (destination[0] == nullptr) { switch(attribute) { case MP_QSTR_closed: @@ -114,19 +152,30 @@ STATIC void file_attr(mp_obj_t self_in, qstr attribute, mp_obj_t *destination) { break; case MP_QSTR_close: destination[0] = (mp_obj_t) MP_ROM_PTR(&file_close_obj); - destination[1] = self_in; break; case MP_QSTR_tell: destination[0] = (mp_obj_t) MP_ROM_PTR(&file_tell_obj); - destination[1] = self_in; break; case MP_QSTR_seek: destination[0] = (mp_obj_t) MP_ROM_PTR(&file_seek_obj); - destination[1] = self_in; break; case MP_QSTR_seekable: destination[0] = (mp_obj_t) MP_ROM_PTR(&file_seekable_obj); - destination[1] = self_in; + break; + case MP_QSTR_fileno: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_fileno_obj); + break; + case MP_QSTR_flush: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_flush_obj); + break; + case MP_QSTR_isatty: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_isatty_obj); + break; + case MP_QSTR_readable: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_readable_obj); + break; + case MP_QSTR_read: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_read_obj); break; default: break; @@ -455,3 +504,87 @@ STATIC mp_obj_t file_seekable(mp_obj_t o_in) { return mp_const_true; } +STATIC mp_obj_t file_fileno(mp_obj_t o_in) { + file_obj_t *file = (file_obj_t*) MP_OBJ_TO_PTR(o_in); + check_closed(file); + + mp_raise_OSError(1); + return mp_const_none; +} + +STATIC mp_obj_t file_flush(mp_obj_t o_in) { + file_obj_t *file = (file_obj_t*) MP_OBJ_TO_PTR(o_in); + check_closed(file); + + return mp_const_none; +} + +STATIC mp_obj_t file_isatty(mp_obj_t o_in) { + file_obj_t *file = (file_obj_t*) MP_OBJ_TO_PTR(o_in); + check_closed(file); + + return mp_const_false; +} + +STATIC mp_obj_t file_readable(mp_obj_t o_in) { + file_obj_t *file = (file_obj_t*) MP_OBJ_TO_PTR(o_in); + check_closed(file); + + return mp_const_true; +} + +STATIC mp_obj_t file_read(size_t n_args, const mp_obj_t* args) { + mp_arg_check_num(n_args, 0, 1, 2, false); + + if(!mp_obj_is_type(args[0], &file_type)) { + mp_raise_TypeError("self must be a file!"); + } + + file_obj_t *file = (file_obj_t*) MP_OBJ_TO_PTR(args[0]); + + check_closed(file); + + if (file->open_mode != READ && file->edit_mode != true) { + mp_raise_OSError(1); + } + + mp_int_t size = -1; + + if (n_args > 1) { + if (!mp_obj_is_integer(args[1])) { + mp_raise_ValueError("size must be an int!"); + } + + size = mp_obj_get_int(args[1]); + } + + if (file->location == RAM) { + mp_int_t file_size = file->record.value().size; + mp_int_t start = file->position; + if (start >= file_size || size == 0) { + if (file->binary_mode == TEXT) + return mp_obj_new_str("", 0); + if (file->binary_mode == BINARY) + return mp_const_empty_bytes; + } + + mp_int_t end = 0; + + // size == 0 handled earlier. + if (size < 0) { + end = file_size - 1; + } else { + end = std::min(file_size - 1, file->position + size); + } + + file->position = end; + + if (file->binary_mode == TEXT) + return mp_obj_new_str((const char*)file->record.value().buffer + start, end - start); + if (file->binary_mode == BINARY) + return mp_obj_new_bytes((const byte*)file->record.value().buffer + start, end - start); + } + + return mp_const_none; +} + From 1aebec52bb01edc25e0de1be2c537980dbb4ba6a Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sun, 5 Jul 2020 14:38:45 +0200 Subject: [PATCH 06/14] [mpy/files] fixed read, added write --- python/port/genhdr/qstrdefs.in.h | 1 + python/port/mod/ion/file.cpp | 84 +++++++++++++++++++++++++++++--- 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/python/port/genhdr/qstrdefs.in.h b/python/port/genhdr/qstrdefs.in.h index 194bd5e29..4e589dcde 100644 --- a/python/port/genhdr/qstrdefs.in.h +++ b/python/port/genhdr/qstrdefs.in.h @@ -505,6 +505,7 @@ Q(flush) Q(isatty) Q(readable) Q(seekable) +Q(write) Q(SEEK_SET) Q(SEEK_CUR) Q(SEEK_END) diff --git a/python/port/mod/ion/file.cpp b/python/port/mod/ion/file.cpp index 87651a5f5..1b141db5a 100644 --- a/python/port/mod/ion/file.cpp +++ b/python/port/mod/ion/file.cpp @@ -40,6 +40,13 @@ const mp_obj_fun_builtin_var_t file_read_obj = { {(mp_fun_var_t)file_read} }; +STATIC mp_obj_t file_write(mp_obj_t o_in, mp_obj_t o_s); + +const mp_obj_fun_builtin_fixed_t file_write_obj = { + {&mp_type_fun_builtin_2}, + {(mp_fun_0_t)file_write} +}; + STATIC mp_obj_t file_seekable(mp_obj_t o_in); const mp_obj_fun_builtin_fixed_t file_seekable_obj = { @@ -135,7 +142,7 @@ typedef struct _file_obj_t { // If location is set to RAM, record is used. Ion::Storage::Record record; - uint16_t position; + size_t position; bool closed; @@ -177,6 +184,9 @@ STATIC void file_attr(mp_obj_t self_in, qstr attribute, mp_obj_t *destination) { case MP_QSTR_read: destination[0] = (mp_obj_t) MP_ROM_PTR(&file_read_obj); break; + case MP_QSTR_write: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_write_obj); + break; default: break; } @@ -448,7 +458,7 @@ STATIC mp_obj_t file_seek(size_t n_args, const mp_obj_t* args) { mp_raise_ValueError("offset must be an int!"); } - mp_int_t position = mp_obj_get_int(args[1]); + int position = mp_obj_get_int(args[1]); mp_int_t whence = 0; if (n_args > 2) { @@ -459,7 +469,7 @@ STATIC mp_obj_t file_seek(size_t n_args, const mp_obj_t* args) { whence = mp_obj_get_int(args[2]); } - mp_int_t new_position = file->position; + int new_position = file->position; size_t file_size = 0; if(file->location == RAM) { @@ -533,6 +543,64 @@ STATIC mp_obj_t file_readable(mp_obj_t o_in) { return mp_const_true; } +STATIC mp_obj_t file_write(mp_obj_t o_in, mp_obj_t o_s) { + + if(!mp_obj_is_type(o_in, &file_type)) { + mp_raise_TypeError("self must be a file!"); + } + + file_obj_t *file = (file_obj_t*) MP_OBJ_TO_PTR(o_in); + + check_closed(file); + + if (file->open_mode == READ && file->edit_mode != true) { + mp_raise_OSError(1); + } + + if (file->binary_mode == TEXT) { + if (!mp_obj_is_str(o_s)) { + mp_raise_ValueError("s must be a str!"); + } + } else if (file->binary_mode == BINARY) { + if (!mp_obj_is_type(o_s, &mp_type_bytes)) { + mp_raise_ValueError("s must be a bytes!"); + } + } + + size_t len; + const char* buffer; + buffer = mp_obj_str_get_data(o_s, &len); + + if (file->location == RAM) { + size_t previous_size = file->record.value().size; + + // Claim avaliable space. + size_t avaliable_size = Ion::Storage::sharedStorage()->putAvailableSpaceAtEndOfRecord(file->record); + + // Check if there is enough space left + if (file->position + len + 1 > avaliable_size) { + Ion::Storage::sharedStorage()->getAvailableSpaceFromEndOfRecord(file->record, avaliable_size - previous_size); + mp_raise_OSError(28); + } + + // Check if seek pos is higher than file end + // If yes, fill space between there with 0x00 + if (file->position > previous_size) { + memset((uint8_t*)(file->record.value().buffer) + file->position, 0x00, file->position - previous_size); + } + + // Copy buffer to destination + memcpy((uint8_t*)(file->record.value().buffer) + file->position, buffer, len); + + // Set size again + Ion::Storage::sharedStorage()->getAvailableSpaceFromEndOfRecord(file->record, file->record.value().size - std::max(previous_size, file->position + len)); + + file->position += len; + } + + return mp_obj_new_int(len); +} + STATIC mp_obj_t file_read(size_t n_args, const mp_obj_t* args) { mp_arg_check_num(n_args, 0, 1, 2, false); @@ -559,8 +627,8 @@ STATIC mp_obj_t file_read(size_t n_args, const mp_obj_t* args) { } if (file->location == RAM) { - mp_int_t file_size = file->record.value().size; - mp_int_t start = file->position; + size_t file_size = file->record.value().size; + size_t start = file->position; if (start >= file_size || size == 0) { if (file->binary_mode == TEXT) return mp_obj_new_str("", 0); @@ -568,13 +636,13 @@ STATIC mp_obj_t file_read(size_t n_args, const mp_obj_t* args) { return mp_const_empty_bytes; } - mp_int_t end = 0; + size_t end = 0; // size == 0 handled earlier. if (size < 0) { - end = file_size - 1; + end = file_size; } else { - end = std::min(file_size - 1, file->position + size); + end = std::min(file_size, file->position + size); } file->position = end; From a47ef2923664d19d4977e3d4fa46c318073094f7 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sun, 5 Jul 2020 15:08:39 +0200 Subject: [PATCH 07/14] [mpy/files] __enter__, __exit__ --- python/port/genhdr/qstrdefs.in.h | 1 + python/port/mod/ion/file.cpp | 62 ++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/python/port/genhdr/qstrdefs.in.h b/python/port/genhdr/qstrdefs.in.h index 4e589dcde..7d3a3f8be 100644 --- a/python/port/genhdr/qstrdefs.in.h +++ b/python/port/genhdr/qstrdefs.in.h @@ -506,6 +506,7 @@ Q(isatty) Q(readable) Q(seekable) Q(write) +Q(writable) Q(SEEK_SET) Q(SEEK_CUR) Q(SEEK_END) diff --git a/python/port/mod/ion/file.cpp b/python/port/mod/ion/file.cpp index 1b141db5a..bbccad466 100644 --- a/python/port/mod/ion/file.cpp +++ b/python/port/mod/ion/file.cpp @@ -61,6 +61,21 @@ const mp_obj_fun_builtin_fixed_t file_close_obj = { {(mp_fun_0_t)file_close} }; +STATIC mp_obj_t file___enter__(mp_obj_t o_in); + +const mp_obj_fun_builtin_fixed_t file___enter___obj = { + {&mp_type_fun_builtin_1}, + {(mp_fun_0_t)file___enter__} +}; + +STATIC mp_obj_t file___exit__(size_t n_args, const mp_obj_t* args); + +const mp_obj_fun_builtin_var_t file___exit___obj = { + {&mp_type_fun_builtin_var}, + MP_OBJ_FUN_MAKE_SIG(4, 4, false), + {(mp_fun_var_t)file___exit__} +}; + STATIC mp_obj_t file_fileno(mp_obj_t o_in); const mp_obj_fun_builtin_fixed_t file_fileno_obj = { @@ -89,6 +104,13 @@ const mp_obj_fun_builtin_fixed_t file_readable_obj = { {(mp_fun_0_t)file_readable} }; +STATIC mp_obj_t file_writable(mp_obj_t o_in); + +const mp_obj_fun_builtin_fixed_t file_writable_obj = { + {&mp_type_fun_builtin_1}, + {(mp_fun_0_t)file_writable} +}; + STATIC const mp_rom_map_elem_t file_type_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SEEK_SET), MP_ROM_INT(0) }, { MP_ROM_QSTR(MP_QSTR_SEEK_CUR), MP_ROM_INT(1) }, @@ -157,6 +179,12 @@ STATIC void file_attr(mp_obj_t self_in, qstr attribute, mp_obj_t *destination) { case MP_QSTR_closed: destination[0] = mp_obj_new_bool(self->closed); break; + case MP_QSTR___enter__: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file___enter___obj); + break; + case MP_QSTR___exit__: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file___exit___obj); + break; case MP_QSTR_close: destination[0] = (mp_obj_t) MP_ROM_PTR(&file_close_obj); break; @@ -412,6 +440,25 @@ void check_closed(file_obj_t* file) { // Methods +STATIC mp_obj_t file___enter__(mp_obj_t o_in) { + + if(!mp_obj_is_type(o_in, &file_type)) { + mp_raise_TypeError("self must be a file!"); + } + + return o_in; +} + +STATIC mp_obj_t file___exit__(size_t n_args, const mp_obj_t* args) { + mp_arg_check_num(n_args, 0, 4, 4, false); + + if(!mp_obj_is_type(args[0], &file_type)) { + mp_raise_TypeError("self must be a file!"); + } + + return file_close(args[0]); +} + STATIC mp_obj_t file_close(mp_obj_t o_in) { if(!mp_obj_is_type(o_in, &file_type)) { mp_raise_TypeError("self must be a file!"); @@ -536,10 +583,25 @@ STATIC mp_obj_t file_isatty(mp_obj_t o_in) { return mp_const_false; } +STATIC mp_obj_t file_writable(mp_obj_t o_in) { + file_obj_t *file = (file_obj_t*) MP_OBJ_TO_PTR(o_in); + check_closed(file); + + if (file->open_mode == READ && file->edit_mode != true) { + return mp_const_false; + } + + return mp_const_true; +} + STATIC mp_obj_t file_readable(mp_obj_t o_in) { file_obj_t *file = (file_obj_t*) MP_OBJ_TO_PTR(o_in); check_closed(file); + if (file->open_mode != READ && file->edit_mode != true) { + return mp_const_false; + } + return mp_const_true; } From fba5295502b1f6b3913ef6a7b5cdda3deb118f41 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sun, 5 Jul 2020 15:24:31 +0200 Subject: [PATCH 08/14] [mpy/files] readline --- python/port/genhdr/qstrdefs.in.h | 1 + python/port/mod/ion/file.cpp | 110 +++++++++++++++++++++++-------- 2 files changed, 82 insertions(+), 29 deletions(-) diff --git a/python/port/genhdr/qstrdefs.in.h b/python/port/genhdr/qstrdefs.in.h index 7d3a3f8be..52bd80e5a 100644 --- a/python/port/genhdr/qstrdefs.in.h +++ b/python/port/genhdr/qstrdefs.in.h @@ -504,6 +504,7 @@ Q(fileno) Q(flush) Q(isatty) Q(readable) +Q(readline) Q(seekable) Q(write) Q(writable) diff --git a/python/port/mod/ion/file.cpp b/python/port/mod/ion/file.cpp index bbccad466..0fbd9941a 100644 --- a/python/port/mod/ion/file.cpp +++ b/python/port/mod/ion/file.cpp @@ -32,6 +32,14 @@ const mp_obj_fun_builtin_var_t file_seek_obj = { {(mp_fun_var_t)file_seek} }; +STATIC mp_obj_t file_readline(size_t n_args, const mp_obj_t* args); + +const mp_obj_fun_builtin_var_t file_readline_obj = { + {&mp_type_fun_builtin_var}, + MP_OBJ_FUN_MAKE_SIG(1, 2, false), + {(mp_fun_var_t)file_readline} +}; + STATIC mp_obj_t file_read(size_t n_args, const mp_obj_t* args); const mp_obj_fun_builtin_var_t file_read_obj = { @@ -212,6 +220,9 @@ STATIC void file_attr(mp_obj_t self_in, qstr attribute, mp_obj_t *destination) { case MP_QSTR_read: destination[0] = (mp_obj_t) MP_ROM_PTR(&file_read_obj); break; + case MP_QSTR_readline: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_readline_obj); + break; case MP_QSTR_write: destination[0] = (mp_obj_t) MP_ROM_PTR(&file_write_obj); break; @@ -663,6 +674,46 @@ STATIC mp_obj_t file_write(mp_obj_t o_in, mp_obj_t o_s) { return mp_obj_new_int(len); } +STATIC mp_obj_t __file_read_backend(file_obj_t* file, mp_int_t size, bool with_line_sep) { + if (file->location == RAM) { + size_t file_size = file->record.value().size; + size_t start = file->position; + if (start >= file_size || size == 0) { + if (file->binary_mode == TEXT) + return mp_obj_new_str("", 0); + if (file->binary_mode == BINARY) + return mp_const_empty_bytes; + } + + size_t end = 0; + + // size == 0 handled earlier. + if (size < 0) { + end = file_size; + } else { + end = std::min(file_size, file->position + size); + } + + if (with_line_sep) { + for(size_t i = start; i < end; i++) { + if (*((uint8_t*)(file->record.value().buffer) + i) == '\n') { + end = i + 1; + break; + } + } + } + + file->position = end; + + if (file->binary_mode == TEXT) + return mp_obj_new_str((const char*)file->record.value().buffer + start, end - start); + if (file->binary_mode == BINARY) + return mp_obj_new_bytes((const byte*)file->record.value().buffer + start, end - start); + } + + return mp_const_none; +} + STATIC mp_obj_t file_read(size_t n_args, const mp_obj_t* args) { mp_arg_check_num(n_args, 0, 1, 2, false); @@ -687,34 +738,35 @@ STATIC mp_obj_t file_read(size_t n_args, const mp_obj_t* args) { size = mp_obj_get_int(args[1]); } - - if (file->location == RAM) { - size_t file_size = file->record.value().size; - size_t start = file->position; - if (start >= file_size || size == 0) { - if (file->binary_mode == TEXT) - return mp_obj_new_str("", 0); - if (file->binary_mode == BINARY) - return mp_const_empty_bytes; - } - - size_t end = 0; - - // size == 0 handled earlier. - if (size < 0) { - end = file_size; - } else { - end = std::min(file_size, file->position + size); - } - - file->position = end; - - if (file->binary_mode == TEXT) - return mp_obj_new_str((const char*)file->record.value().buffer + start, end - start); - if (file->binary_mode == BINARY) - return mp_obj_new_bytes((const byte*)file->record.value().buffer + start, end - start); - } - - return mp_const_none; + + return __file_read_backend(file, size, false); +} + +STATIC mp_obj_t file_readline(size_t n_args, const mp_obj_t* args) { + mp_arg_check_num(n_args, 0, 1, 2, false); + + if(!mp_obj_is_type(args[0], &file_type)) { + mp_raise_TypeError("self must be a file!"); + } + + file_obj_t *file = (file_obj_t*) MP_OBJ_TO_PTR(args[0]); + + check_closed(file); + + if (file->open_mode != READ && file->edit_mode != true) { + mp_raise_OSError(1); + } + + mp_int_t size = -1; + + if (n_args > 1) { + if (!mp_obj_is_integer(args[1])) { + mp_raise_ValueError("size must be an int!"); + } + + size = mp_obj_get_int(args[1]); + } + + return __file_read_backend(file, size, true); } From 55bc23c6d7a7791c49d1f06fa61b2a42b64822ab Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sun, 5 Jul 2020 15:58:09 +0200 Subject: [PATCH 09/14] [mpy/files] __iter__, __next__ --- python/port/mod/ion/file.cpp | 60 +++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/python/port/mod/ion/file.cpp b/python/port/mod/ion/file.cpp index 0fbd9941a..79a80a934 100644 --- a/python/port/mod/ion/file.cpp +++ b/python/port/mod/ion/file.cpp @@ -16,6 +16,7 @@ extern "C" { STATIC void file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); STATIC mp_obj_t file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); STATIC void file_attr(mp_obj_t self_in, qstr attribute, mp_obj_t *destination); +STATIC mp_obj_t file___iter__(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf); STATIC mp_obj_t file_tell(mp_obj_t o_in); @@ -138,7 +139,7 @@ extern const mp_obj_type_t file_type = { nullptr, // binary operations file_attr, // load, store, delete attributes nullptr, // load, store, delete subscripting - nullptr, // __iter__ -> TODO! + file___iter__, // __iter__ nullptr, // __next__ nullptr, // buffer nullptr, // protocol @@ -178,6 +179,40 @@ typedef struct _file_obj_t { } file_obj_t; +STATIC mp_obj_t __file_read_backend(file_obj_t* file, mp_int_t size, bool with_line_sep); + +// file iterator +typedef struct _file_it_obj_t { + mp_obj_base_t base; + mp_fun_1_t iternext; + mp_obj_t file; +} file_it_obj_t; + +STATIC mp_obj_t file_it___next__(mp_obj_t self_in) { + file_it_obj_t *file_it = (file_it_obj_t*) MP_OBJ_TO_PTR(self_in); + file_obj_t *file = (file_obj_t*) MP_OBJ_TO_PTR(file_it->file); + + mp_obj_t ret = __file_read_backend(file, -1, false); + + if (ret == mp_const_none) { + return MP_OBJ_STOP_ITERATION; + } else { + return ret; + } +} + +STATIC mp_obj_t file___iter__(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { + assert(sizeof(file_it_obj_t) <= sizeof(mp_obj_iter_buf_t)); + + file_obj_t *file = (file_obj_t*) MP_OBJ_TO_PTR(self_in); + + file_it_obj_t *o = (file_it_obj_t*)iter_buf; + o->base.type = &mp_type_polymorph_iter; + o->iternext = file_it___next__; + o->file = file; + return MP_OBJ_FROM_PTR(o); +} + STATIC void file_attr(mp_obj_t self_in, qstr attribute, mp_obj_t *destination) { destination[1] = self_in; file_obj_t *self = (file_obj_t*) MP_OBJ_TO_PTR(self_in); @@ -679,10 +714,7 @@ STATIC mp_obj_t __file_read_backend(file_obj_t* file, mp_int_t size, bool with_l size_t file_size = file->record.value().size; size_t start = file->position; if (start >= file_size || size == 0) { - if (file->binary_mode == TEXT) - return mp_obj_new_str("", 0); - if (file->binary_mode == BINARY) - return mp_const_empty_bytes; + return mp_const_none; } size_t end = 0; @@ -739,7 +771,14 @@ STATIC mp_obj_t file_read(size_t n_args, const mp_obj_t* args) { size = mp_obj_get_int(args[1]); } - return __file_read_backend(file, size, false); + mp_obj_t ret = __file_read_backend(file, size, false); + + if (ret == mp_const_none) { + if (file->binary_mode == TEXT) + return mp_obj_new_str("", 0); + if (file->binary_mode == BINARY) + return mp_const_empty_bytes; + } } STATIC mp_obj_t file_readline(size_t n_args, const mp_obj_t* args) { @@ -767,6 +806,13 @@ STATIC mp_obj_t file_readline(size_t n_args, const mp_obj_t* args) { size = mp_obj_get_int(args[1]); } - return __file_read_backend(file, size, true); + mp_obj_t ret = __file_read_backend(file, size, false); + + if (ret == mp_const_none) { + if (file->binary_mode == TEXT) + return mp_obj_new_str("", 0); + if (file->binary_mode == BINARY) + return mp_const_empty_bytes; + } } From 61a9e492bdbdcf8f3cbfb88d0375c2f2f279619a Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sun, 5 Jul 2020 19:50:34 +0200 Subject: [PATCH 10/14] [mpy/files] truncate, readlines, writelines, name, mode --- python/port/genhdr/qstrdefs.in.h | 20 +- python/port/mod/ion/file.cpp | 348 +++++++++++++++++++++++-------- 2 files changed, 281 insertions(+), 87 deletions(-) diff --git a/python/port/genhdr/qstrdefs.in.h b/python/port/genhdr/qstrdefs.in.h index 52bd80e5a..9eff06b1c 100644 --- a/python/port/genhdr/qstrdefs.in.h +++ b/python/port/genhdr/qstrdefs.in.h @@ -496,18 +496,30 @@ Q(monotonic) // file QSTRs Q(file) -Q(tell) -Q(seek) + Q(close) Q(closed) -Q(fileno) Q(flush) Q(isatty) Q(readable) Q(readline) +Q(readlines) Q(seekable) -Q(write) +Q(tell) Q(writable) +Q(writelines) + +Q(fileno) +Q(seek) +Q(truncate) + +Q(write) + +Q(read) + +Q(name) +Q(mode) + Q(SEEK_SET) Q(SEEK_CUR) Q(SEEK_END) diff --git a/python/port/mod/ion/file.cpp b/python/port/mod/ion/file.cpp index 79a80a934..bf72e0bab 100644 --- a/python/port/mod/ion/file.cpp +++ b/python/port/mod/ion/file.cpp @@ -41,6 +41,22 @@ const mp_obj_fun_builtin_var_t file_readline_obj = { {(mp_fun_var_t)file_readline} }; +STATIC mp_obj_t file_readlines(size_t n_args, const mp_obj_t* args); + +const mp_obj_fun_builtin_var_t file_readlines_obj = { + {&mp_type_fun_builtin_var}, + MP_OBJ_FUN_MAKE_SIG(1, 2, false), + {(mp_fun_var_t)file_readlines} +}; + +STATIC mp_obj_t file_truncate(size_t n_args, const mp_obj_t* args); + +const mp_obj_fun_builtin_var_t file_truncate_obj = { + {&mp_type_fun_builtin_var}, + MP_OBJ_FUN_MAKE_SIG(1, 2, false), + {(mp_fun_var_t)file_truncate} +}; + STATIC mp_obj_t file_read(size_t n_args, const mp_obj_t* args); const mp_obj_fun_builtin_var_t file_read_obj = { @@ -56,6 +72,13 @@ const mp_obj_fun_builtin_fixed_t file_write_obj = { {(mp_fun_0_t)file_write} }; +STATIC mp_obj_t file_writelines(mp_obj_t o_in, mp_obj_t o_lines); + +const mp_obj_fun_builtin_fixed_t file_writelines_obj = { + {&mp_type_fun_builtin_2}, + {(mp_fun_0_t)file_writelines} +}; + STATIC mp_obj_t file_seekable(mp_obj_t o_in); const mp_obj_fun_builtin_fixed_t file_seekable_obj = { @@ -164,6 +187,7 @@ typedef struct _file_obj_t { mp_obj_base_t base; mp_obj_t name; + mp_obj_t mode_repr; file_mode_t open_mode; bool edit_mode; @@ -214,58 +238,85 @@ STATIC mp_obj_t file___iter__(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { } STATIC void file_attr(mp_obj_t self_in, qstr attribute, mp_obj_t *destination) { - destination[1] = self_in; file_obj_t *self = (file_obj_t*) MP_OBJ_TO_PTR(self_in); - if (destination[0] == nullptr) { - switch(attribute) { - case MP_QSTR_closed: - destination[0] = mp_obj_new_bool(self->closed); - break; - case MP_QSTR___enter__: - destination[0] = (mp_obj_t) MP_ROM_PTR(&file___enter___obj); - break; - case MP_QSTR___exit__: - destination[0] = (mp_obj_t) MP_ROM_PTR(&file___exit___obj); - break; - case MP_QSTR_close: - destination[0] = (mp_obj_t) MP_ROM_PTR(&file_close_obj); - break; - case MP_QSTR_tell: - destination[0] = (mp_obj_t) MP_ROM_PTR(&file_tell_obj); - break; - case MP_QSTR_seek: - destination[0] = (mp_obj_t) MP_ROM_PTR(&file_seek_obj); - break; - case MP_QSTR_seekable: - destination[0] = (mp_obj_t) MP_ROM_PTR(&file_seekable_obj); - break; - case MP_QSTR_fileno: - destination[0] = (mp_obj_t) MP_ROM_PTR(&file_fileno_obj); - break; - case MP_QSTR_flush: - destination[0] = (mp_obj_t) MP_ROM_PTR(&file_flush_obj); - break; - case MP_QSTR_isatty: - destination[0] = (mp_obj_t) MP_ROM_PTR(&file_isatty_obj); - break; - case MP_QSTR_readable: - destination[0] = (mp_obj_t) MP_ROM_PTR(&file_readable_obj); - break; - case MP_QSTR_read: - destination[0] = (mp_obj_t) MP_ROM_PTR(&file_read_obj); - break; - case MP_QSTR_readline: - destination[0] = (mp_obj_t) MP_ROM_PTR(&file_readline_obj); - break; - case MP_QSTR_write: - destination[0] = (mp_obj_t) MP_ROM_PTR(&file_write_obj); - break; - default: - break; - } + switch(attribute) { + case MP_QSTR_closed: + destination[0] = mp_obj_new_bool(self->closed); + break; + case MP_QSTR_name: + destination[0] = (mp_obj_t) self->name; + break; + case MP_QSTR_mode: + destination[0] = (mp_obj_t) self->mode_repr; + break; + case MP_QSTR___enter__: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file___enter___obj); + destination[1] = self_in; + break; + case MP_QSTR___exit__: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file___exit___obj); + destination[1] = self_in; + break; + case MP_QSTR_close: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_close_obj); + destination[1] = self_in; + break; + case MP_QSTR_tell: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_tell_obj); + destination[1] = self_in; + break; + case MP_QSTR_seek: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_seek_obj); + destination[1] = self_in; + break; + case MP_QSTR_seekable: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_seekable_obj); + destination[1] = self_in; + break; + case MP_QSTR_fileno: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_fileno_obj); + destination[1] = self_in; + break; + case MP_QSTR_flush: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_flush_obj); + destination[1] = self_in; + break; + case MP_QSTR_isatty: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_isatty_obj); + destination[1] = self_in; + break; + case MP_QSTR_readable: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_readable_obj); + destination[1] = self_in; + break; + case MP_QSTR_read: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_read_obj); + destination[1] = self_in; + break; + case MP_QSTR_readline: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_readline_obj); + destination[1] = self_in; + break; + case MP_QSTR_readlines: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_readlines_obj); + destination[1] = self_in; + break; + case MP_QSTR_write: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_write_obj); + destination[1] = self_in; + break; + case MP_QSTR_writelines: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_writelines_obj); + destination[1] = self_in; + break; + case MP_QSTR_truncate: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_truncate_obj); + destination[1] = self_in; + break; + default: + break; } - } STATIC void file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { @@ -274,38 +325,7 @@ STATIC void file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ size_t l; const char* file_name = mp_obj_str_get_data(self->name, &l); - - uint8_t offset = 0; - char file_mode[4] = {0, 0, 0, 0}; - - switch(self->open_mode) { - case READ: - file_mode[0] = 'r'; - break; - case WRITE: - file_mode[0] = 'w'; - break; - case APPEND: - file_mode[0] = 'a'; - break; - case CREATE: - file_mode[0] = 'x'; - break; - } - - if (self->edit_mode) { - file_mode[1] = '+'; - offset = 1; - } - - switch(self->binary_mode) { - case TEXT: - file_mode[1 + offset] = 't'; - break; - case BINARY: - file_mode[1 + offset] = 'b'; - break; - } + const char* file_mode = mp_obj_str_get_data(self->mode_repr, &l); mp_print_str(print, "edit_mode) { + file_mode[1] = '+'; + offset = 1; + } + + switch(file->binary_mode) { + case TEXT: + file_mode[1 + offset] = 't'; + break; + case BINARY: + file_mode[1 + offset] = 'b'; + break; + } + + file->mode_repr = mp_obj_new_str(file_mode, 2 + offset); + + return MP_OBJ_FROM_PTR(file); } @@ -686,7 +743,7 @@ STATIC mp_obj_t file_write(mp_obj_t o_in, mp_obj_t o_s) { size_t avaliable_size = Ion::Storage::sharedStorage()->putAvailableSpaceAtEndOfRecord(file->record); // Check if there is enough space left - if (file->position + len + 1 > avaliable_size) { + if (file->position + len > avaliable_size) { Ion::Storage::sharedStorage()->getAvailableSpaceFromEndOfRecord(file->record, avaliable_size - previous_size); mp_raise_OSError(28); } @@ -709,6 +766,16 @@ STATIC mp_obj_t file_write(mp_obj_t o_in, mp_obj_t o_s) { return mp_obj_new_int(len); } +STATIC mp_obj_t file_writelines(mp_obj_t o_in, mp_obj_t o_lines) { + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(o_lines, &iter_buf); + mp_obj_t item; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + file_write(o_in, item); + } + return mp_const_none; +} + STATIC mp_obj_t __file_read_backend(file_obj_t* file, mp_int_t size, bool with_line_sep) { if (file->location == RAM) { size_t file_size = file->record.value().size; @@ -778,7 +845,11 @@ STATIC mp_obj_t file_read(size_t n_args, const mp_obj_t* args) { return mp_obj_new_str("", 0); if (file->binary_mode == BINARY) return mp_const_empty_bytes; + } else { + return ret; } + + return mp_const_none; } STATIC mp_obj_t file_readline(size_t n_args, const mp_obj_t* args) { @@ -806,13 +877,124 @@ STATIC mp_obj_t file_readline(size_t n_args, const mp_obj_t* args) { size = mp_obj_get_int(args[1]); } - mp_obj_t ret = __file_read_backend(file, size, false); + mp_obj_t ret = __file_read_backend(file, size, true); if (ret == mp_const_none) { if (file->binary_mode == TEXT) return mp_obj_new_str("", 0); if (file->binary_mode == BINARY) return mp_const_empty_bytes; + } else { + return ret; } + + return mp_const_none; +} + +STATIC mp_obj_t file_readlines(size_t n_args, const mp_obj_t* args) { + mp_arg_check_num(n_args, 0, 1, 2, false); + + if(!mp_obj_is_type(args[0], &file_type)) { + mp_raise_TypeError("self must be a file!"); + } + + file_obj_t *file = (file_obj_t*) MP_OBJ_TO_PTR(args[0]); + + check_closed(file); + + if (file->open_mode != READ && file->edit_mode != true) { + mp_raise_OSError(1); + } + + mp_int_t hint = -1; + + if (n_args > 1) { + if (!mp_obj_is_integer(args[1])) { + mp_raise_ValueError("hint must be an int!"); + } + + hint = mp_obj_get_int(args[1]); + } + + mp_obj_t list = mp_obj_new_list(0, NULL); + + if (hint <= 0) { + mp_obj_t ret = __file_read_backend(file, -1, true); + while(ret != mp_const_none) { + mp_obj_list_append(list, ret); + ret = __file_read_backend(file, -1, true); + } + } else { + mp_int_t curr_len = 0; + + mp_obj_t ret = __file_read_backend(file, -1, true); + while(ret != mp_const_none && curr_len <= hint) { + mp_obj_list_append(list, ret); + size_t l = 0; + mp_obj_str_get_data(ret, &l); + curr_len += l; + ret = __file_read_backend(file, -1, true); + } + } + + return list; +} + +STATIC mp_obj_t file_truncate(size_t n_args, const mp_obj_t* args) { + mp_arg_check_num(n_args, 0, 1, 2, false); + + if(!mp_obj_is_type(args[0], &file_type)) { + mp_raise_TypeError("self must be a file!"); + } + + file_obj_t *file = (file_obj_t*) MP_OBJ_TO_PTR(args[0]); + + check_closed(file); + + if (file->open_mode == READ && file->edit_mode != true) { + mp_raise_OSError(1); + } + + if(!mp_obj_is_integer(args[1]) && args[1] != mp_const_none) { + mp_raise_TypeError("size must be an integer!"); + } + + mp_int_t temp_new_end = file->position; + + if (args[1] != mp_const_none) { + temp_new_end = mp_obj_get_int(args[1]); + } + + if (temp_new_end < 0) { + mp_raise_OSError(22); + } + + size_t new_end = (size_t) temp_new_end; + + if (file->location == RAM) { + size_t previous_size = file->record.value().size; + + // Claim avaliable space. + size_t avaliable_size = Ion::Storage::sharedStorage()->putAvailableSpaceAtEndOfRecord(file->record); + + // Check if there is enough space left + if (new_end > avaliable_size) { + Ion::Storage::sharedStorage()->getAvailableSpaceFromEndOfRecord(file->record, avaliable_size - previous_size); + mp_raise_OSError(28); + } + + // Check if new_end is higher than file end + // If yes, fill space between there with 0x00 + if (new_end > previous_size) { + memset((uint8_t*)(file->record.value().buffer) + new_end, 0x00, new_end - previous_size); + } + + // Set new size + Ion::Storage::sharedStorage()->getAvailableSpaceFromEndOfRecord(file->record, file->record.value().size - new_end); + + return mp_obj_new_int(new_end); + } + + return mp_const_none; } From efcf9903a9cb3095199f36ff9d2405bdfc0776cd Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sun, 5 Jul 2020 21:15:58 +0200 Subject: [PATCH 11/14] [mpy/files] implemented open --- python/port/builtins.c | 10 ++++++++++ python/port/mod/ion/file.cpp | 20 +++++++++++++++++++- python/port/mod/ion/file.h | 9 +++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 python/port/mod/ion/file.h diff --git a/python/port/builtins.c b/python/port/builtins.c index fa0e1f3be..fbe121d30 100644 --- a/python/port/builtins.c +++ b/python/port/builtins.c @@ -1,9 +1,19 @@ #include "py/builtin.h" #include "py/obj.h" +#include "py/runtime.h" +#include "mod/ion/file.h" #include #include "mphalport.h" mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + mp_arg_check_num(n_args, kwargs->used, 1, 2, false); + + if (n_args == 2) { + return file_open_mode(args[0], args[1]); + } else { + return file_open(args[0]); + } + return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); diff --git a/python/port/mod/ion/file.cpp b/python/port/mod/ion/file.cpp index bf72e0bab..325fc5c9d 100644 --- a/python/port/mod/ion/file.cpp +++ b/python/port/mod/ion/file.cpp @@ -7,6 +7,8 @@ extern "C" { #include #include #include +#include +#include "file.h" } #include @@ -156,7 +158,8 @@ extern const mp_obj_type_t file_type = { 0, // flags MP_QSTR_file, // name file_print, // __repr__, __srt__ - file_make_new, // __new__, __init__ +// file_make_new, // __new__, __init__ + nullptr, // __new__, __init__ nullptr, // __call__ nullptr, // unary operations nullptr, // binary operations @@ -998,3 +1001,18 @@ STATIC mp_obj_t file_truncate(size_t n_args, const mp_obj_t* args) { return mp_const_none; } +mp_obj_t file_open(mp_obj_t file_name) { + mp_obj_t args[1]; + args[0] = file_name; + + return file_make_new(nullptr, 1, 0, args); +} + +mp_obj_t file_open_mode(mp_obj_t file_name, mp_obj_t file_mode) { + mp_obj_t args[2]; + args[0] = file_name; + args[1] = file_mode; + + return file_make_new(nullptr, 2, 0, args); +} + diff --git a/python/port/mod/ion/file.h b/python/port/mod/ion/file.h new file mode 100644 index 000000000..ea9bfded0 --- /dev/null +++ b/python/port/mod/ion/file.h @@ -0,0 +1,9 @@ +#ifndef MP_MOD_FILE_H +#define MP_MOD_FILE_H + +#include + +mp_obj_t file_open(mp_obj_t file_name); +mp_obj_t file_open_mode(mp_obj_t file_name, mp_obj_t file_mode); + +#endif From 646088e4294a97eadf5a1ac81a41fe0ca9d79637 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Mon, 6 Jul 2020 10:05:55 +0200 Subject: [PATCH 12/14] [mpy/files] Removed RAM/FLASH logic, added a bit of doc. --- python/port/mod/ion/file.cpp | 334 +++++++++++++++++------------------ 1 file changed, 158 insertions(+), 176 deletions(-) diff --git a/python/port/mod/ion/file.cpp b/python/port/mod/ion/file.cpp index 325fc5c9d..1cc8ff8de 100644 --- a/python/port/mod/ion/file.cpp +++ b/python/port/mod/ion/file.cpp @@ -153,12 +153,18 @@ STATIC const mp_rom_map_elem_t file_type_globals_table[] = { STATIC MP_DEFINE_CONST_DICT(file_type_globals, file_type_globals_table); +/* + * File type creation. + * + * Constructor is set to nullptr, forcing users to use "open". + * attr is used to return methods ands attributes. + * file___iter__ return an iterable object. + */ extern const mp_obj_type_t file_type = { { &mp_type_type }, // base 0, // flags MP_QSTR_file, // name file_print, // __repr__, __srt__ -// file_make_new, // __new__, __init__ nullptr, // __new__, __init__ nullptr, // __call__ nullptr, // unary operations @@ -173,10 +179,6 @@ extern const mp_obj_type_t file_type = { (mp_obj_dict_t*) &file_type_globals // globals table }; -typedef enum _file_location_t { - RAM = 0, FLASH = 1 -} file_location_t; - typedef enum _file_mode_t { READ = 0, WRITE = 1, APPEND = 2, CREATE = 3 } file_mode_t; @@ -196,8 +198,6 @@ typedef struct _file_obj_t { bool edit_mode; file_bin_t binary_mode; - file_location_t location; - // If location is set to RAM, record is used. Ion::Storage::Record record; size_t position; @@ -208,7 +208,10 @@ typedef struct _file_obj_t { STATIC mp_obj_t __file_read_backend(file_obj_t* file, mp_int_t size, bool with_line_sep); -// file iterator +/* + * Definition of the file iterator object. + * iternext gets the next line of the file. + */ typedef struct _file_it_obj_t { mp_obj_base_t base; mp_fun_1_t iternext; @@ -240,6 +243,8 @@ STATIC mp_obj_t file___iter__(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { return MP_OBJ_FROM_PTR(o); } +// Gets attributs and methods for file object. +// destination[0] is set to object itself, destination[1] is set to self (only for methods). STATIC void file_attr(mp_obj_t self_in, qstr attribute, mp_obj_t *destination) { file_obj_t *self = (file_obj_t*) MP_OBJ_TO_PTR(self_in); @@ -346,13 +351,6 @@ STATIC void file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ * - name: name of the file * - mode: mode of opening (optional, r by default) * - * File system organisation: - * - /ram/ - * Contains all files in RAM (python, poincare stuff, etc..) - * - /flash/ - * Contains all files in external's TAR archive - * - * When nme doesn't start with "/", we use RAM. */ STATIC mp_obj_t file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, 2, true); @@ -409,92 +407,69 @@ STATIC mp_obj_t file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n } } - // If "", throw file not found - if (l > 0) { - file->location = RAM; - // Check location (RAM/FLASH) - if (file_name[0] == '/') { - if (strncmp(file_name, "/ram/", 5) == 0) { - file->location = RAM; - file_name = file_name + 5; - // } else if (strncmp(file_name, "/flash/", 7)) { - // file->location = FLASH; - // file_name = file_name + 7; - } else { - mp_raise_OSError(2); - } - } - } else { - mp_raise_OSError(2); - } - if (!Ion::Storage::FullNameCompliant(file_name)) { mp_raise_OSError(22); } - if(file->location == RAM) { - Ion::Storage::Record::ErrorStatus status; - - switch(file->open_mode) { - case READ: - file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); - file->position = 0; - if (file->record == Ion::Storage::Record()) { - mp_raise_OSError(2); - } - break; - case CREATE: - file->position = 0; - status = Ion::Storage::sharedStorage()->createRecordWithFullName(file_name, "", 0); - switch (status) { - case Ion::Storage::Record::ErrorStatus::NameTaken: - mp_raise_OSError(17); - break; - case Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable: - mp_raise_OSError(28); - break; - default: - break; - } - file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); - break; - case WRITE: - file->position = 0; - status = Ion::Storage::sharedStorage()->createRecordWithFullName(file_name, "", 0); - switch (status) { - case Ion::Storage::Record::ErrorStatus::NameTaken: - // setValue messes with empty buffer, so we delete record and re-create it. - file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); - file->record.destroy(); - - status = Ion::Storage::sharedStorage()->createRecordWithFullName(file_name, "", 0); - - if (status == Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable) { - mp_raise_OSError(28); - } - break; - case Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable: - mp_raise_OSError(28); - break; - default: - break; - } - file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); - break; - case APPEND: - file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); - file->position = 0; - if (file->record == Ion::Storage::Record()) { + Ion::Storage::Record::ErrorStatus status; + + switch(file->open_mode) { + case READ: + file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); + file->position = 0; + if (file->record == Ion::Storage::Record()) { + mp_raise_OSError(2); + } + break; + case CREATE: + file->position = 0; + status = Ion::Storage::sharedStorage()->createRecordWithFullName(file_name, "", 0); + switch (status) { + case Ion::Storage::Record::ErrorStatus::NameTaken: + mp_raise_OSError(17); + break; + case Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable: + mp_raise_OSError(28); + break; + default: + break; + } + file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); + break; + case WRITE: + file->position = 0; + status = Ion::Storage::sharedStorage()->createRecordWithFullName(file_name, "", 0); + switch (status) { + case Ion::Storage::Record::ErrorStatus::NameTaken: + // setValue messes with empty buffer, so we delete record and re-create it. + file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); + file->record.destroy(); + status = Ion::Storage::sharedStorage()->createRecordWithFullName(file_name, "", 0); + if (status == Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable) { mp_raise_OSError(28); } + break; + case Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable: + mp_raise_OSError(28); + break; + default: + break; + } + file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); + break; + case APPEND: + file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); + file->position = 0; + if (file->record == Ion::Storage::Record()) { + status = Ion::Storage::sharedStorage()->createRecordWithFullName(file_name, "", 0); + if (status == Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable) { + mp_raise_OSError(28); } - file->position = file->record.value().size; - break; - } - } else { - mp_raise_OSError(2); + } + file->position = file->record.value().size; + break; } file->base.type = &file_type; @@ -573,10 +548,7 @@ STATIC mp_obj_t file_close(mp_obj_t o_in) { file_obj_t* file = (file_obj_t*) MP_OBJ_TO_PTR(o_in); if (!file->closed) { - if(file->location == RAM) { - file->record = Ion::Storage::Record(); - } - + file->record = Ion::Storage::Record(); file->closed = true; } @@ -623,11 +595,7 @@ STATIC mp_obj_t file_seek(size_t n_args, const mp_obj_t* args) { } int new_position = file->position; - size_t file_size = 0; - - if(file->location == RAM) { - file_size = file->record.value().size; - } + size_t file_size = file->record.value().size; switch (whence) { // SEEK_SET @@ -738,34 +706,32 @@ STATIC mp_obj_t file_write(mp_obj_t o_in, mp_obj_t o_s) { size_t len; const char* buffer; buffer = mp_obj_str_get_data(o_s, &len); + + size_t previous_size = file->record.value().size; - if (file->location == RAM) { - size_t previous_size = file->record.value().size; - - // Claim avaliable space. - size_t avaliable_size = Ion::Storage::sharedStorage()->putAvailableSpaceAtEndOfRecord(file->record); - - // Check if there is enough space left - if (file->position + len > avaliable_size) { - Ion::Storage::sharedStorage()->getAvailableSpaceFromEndOfRecord(file->record, avaliable_size - previous_size); - mp_raise_OSError(28); - } - - // Check if seek pos is higher than file end - // If yes, fill space between there with 0x00 - if (file->position > previous_size) { - memset((uint8_t*)(file->record.value().buffer) + file->position, 0x00, file->position - previous_size); - } - - // Copy buffer to destination - memcpy((uint8_t*)(file->record.value().buffer) + file->position, buffer, len); - - // Set size again - Ion::Storage::sharedStorage()->getAvailableSpaceFromEndOfRecord(file->record, file->record.value().size - std::max(previous_size, file->position + len)); - - file->position += len; + // Claim avaliable space. + size_t avaliable_size = Ion::Storage::sharedStorage()->putAvailableSpaceAtEndOfRecord(file->record); + + // Check if there is enough space left + if (file->position + len > avaliable_size) { + Ion::Storage::sharedStorage()->getAvailableSpaceFromEndOfRecord(file->record, avaliable_size - previous_size); + mp_raise_OSError(28); } + // Check if seek pos is higher than file end + // If yes, fill space between there with 0x00 + if (file->position > previous_size) { + memset((uint8_t*)(file->record.value().buffer) + file->position, 0x00, file->position - previous_size); + } + + // Copy buffer to destination + memcpy((uint8_t*)(file->record.value().buffer) + file->position, buffer, len); + + // Set size again + Ion::Storage::sharedStorage()->getAvailableSpaceFromEndOfRecord(file->record, file->record.value().size - std::max(previous_size, file->position + len)); + + file->position += len; + return mp_obj_new_int(len); } @@ -779,46 +745,55 @@ STATIC mp_obj_t file_writelines(mp_obj_t o_in, mp_obj_t o_lines) { return mp_const_none; } +/* + * Simpler read function usef by read and readline. + */ STATIC mp_obj_t __file_read_backend(file_obj_t* file, mp_int_t size, bool with_line_sep) { - if (file->location == RAM) { - size_t file_size = file->record.value().size; - size_t start = file->position; - if (start >= file_size || size == 0) { - return mp_const_none; - } - - size_t end = 0; - - // size == 0 handled earlier. - if (size < 0) { - end = file_size; - } else { - end = std::min(file_size, file->position + size); - } - - if (with_line_sep) { - for(size_t i = start; i < end; i++) { - if (*((uint8_t*)(file->record.value().buffer) + i) == '\n') { - end = i + 1; - break; - } + size_t file_size = file->record.value().size; + size_t start = file->position; + + // Handle seek pos > file size + // And size = 0 + if (start >= file_size || size == 0) { + return mp_const_none; + } + + size_t end = 0; + + // size == 0 handled earlier. + if (size < 0) { + end = file_size; + } else { + end = std::min(file_size, file->position + size); + } + + // Handle line separator case. + // Always use \n, because simpler. + if (with_line_sep) { + for(size_t i = start; i < end; i++) { + if (*((uint8_t*)(file->record.value().buffer) + i) == '\n') { + end = i + 1; + break; } } - - file->position = end; - - if (file->binary_mode == TEXT) - return mp_obj_new_str((const char*)file->record.value().buffer + start, end - start); - if (file->binary_mode == BINARY) - return mp_obj_new_bytes((const byte*)file->record.value().buffer + start, end - start); } + file->position = end; + + + // Return different type based on mode. + if (file->binary_mode == TEXT) + return mp_obj_new_str((const char*)file->record.value().buffer + start, end - start); + if (file->binary_mode == BINARY) + return mp_obj_new_bytes((const byte*)file->record.value().buffer + start, end - start); + return mp_const_none; } STATIC mp_obj_t file_read(size_t n_args, const mp_obj_t* args) { mp_arg_check_num(n_args, 0, 1, 2, false); + // Check type of self if(!mp_obj_is_type(args[0], &file_type)) { mp_raise_TypeError("self must be a file!"); } @@ -827,12 +802,14 @@ STATIC mp_obj_t file_read(size_t n_args, const mp_obj_t* args) { check_closed(file); + // Check mode if (file->open_mode != READ && file->edit_mode != true) { mp_raise_OSError(1); } mp_int_t size = -1; + // Check size arg if (n_args > 1) { if (!mp_obj_is_integer(args[1])) { mp_raise_ValueError("size must be an int!"); @@ -841,8 +818,10 @@ STATIC mp_obj_t file_read(size_t n_args, const mp_obj_t* args) { size = mp_obj_get_int(args[1]); } + // Call the actual read function. mp_obj_t ret = __file_read_backend(file, size, false); + // Handle none return if (ret == mp_const_none) { if (file->binary_mode == TEXT) return mp_obj_new_str("", 0); @@ -922,14 +901,17 @@ STATIC mp_obj_t file_readlines(size_t n_args, const mp_obj_t* args) { mp_obj_t list = mp_obj_new_list(0, NULL); if (hint <= 0) { + // Read until there is no new lines. mp_obj_t ret = __file_read_backend(file, -1, true); while(ret != mp_const_none) { + // Append to list. mp_obj_list_append(list, ret); ret = __file_read_backend(file, -1, true); } } else { mp_int_t curr_len = 0; + // Read until total read > hint. mp_obj_t ret = __file_read_backend(file, -1, true); while(ret != mp_const_none && curr_len <= hint) { mp_obj_list_append(list, ret); @@ -974,33 +956,32 @@ STATIC mp_obj_t file_truncate(size_t n_args, const mp_obj_t* args) { size_t new_end = (size_t) temp_new_end; - if (file->location == RAM) { - size_t previous_size = file->record.value().size; + size_t previous_size = file->record.value().size; + + // Claim avaliable space. + size_t avaliable_size = Ion::Storage::sharedStorage()->putAvailableSpaceAtEndOfRecord(file->record); - // Claim avaliable space. - size_t avaliable_size = Ion::Storage::sharedStorage()->putAvailableSpaceAtEndOfRecord(file->record); - - // Check if there is enough space left - if (new_end > avaliable_size) { - Ion::Storage::sharedStorage()->getAvailableSpaceFromEndOfRecord(file->record, avaliable_size - previous_size); - mp_raise_OSError(28); - } - - // Check if new_end is higher than file end - // If yes, fill space between there with 0x00 - if (new_end > previous_size) { - memset((uint8_t*)(file->record.value().buffer) + new_end, 0x00, new_end - previous_size); - } - - // Set new size - Ion::Storage::sharedStorage()->getAvailableSpaceFromEndOfRecord(file->record, file->record.value().size - new_end); - - return mp_obj_new_int(new_end); + // Check if there is enough space left + if (new_end > avaliable_size) { + Ion::Storage::sharedStorage()->getAvailableSpaceFromEndOfRecord(file->record, avaliable_size - previous_size); + mp_raise_OSError(28); } + // Check if new_end is higher than file end + // If yes, fill space between there with 0x00 + if (new_end > previous_size) { + memset((uint8_t*)(file->record.value().buffer) + new_end, 0x00, new_end - previous_size); + } + + // Set new size + Ion::Storage::sharedStorage()->getAvailableSpaceFromEndOfRecord(file->record, file->record.value().size - new_end); + + return mp_obj_new_int(new_end); + return mp_const_none; } +// Open method, with only name. Calls constructor. mp_obj_t file_open(mp_obj_t file_name) { mp_obj_t args[1]; args[0] = file_name; @@ -1008,6 +989,7 @@ mp_obj_t file_open(mp_obj_t file_name) { return file_make_new(nullptr, 1, 0, args); } +// Open method, with name and mode. Calls constructor. mp_obj_t file_open_mode(mp_obj_t file_name, mp_obj_t file_mode) { mp_obj_t args[2]; args[0] = file_name; From 73688343a076e492c50b70ce9b57741ab26e2045 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Mon, 6 Jul 2020 11:56:11 +0200 Subject: [PATCH 13/14] [apps/code] Added File category to python toolbox --- apps/code/catalog.de.i18n | 16 ++++++++++++++++ apps/code/catalog.en.i18n | 16 ++++++++++++++++ apps/code/catalog.es.i18n | 16 ++++++++++++++++ apps/code/catalog.fr.i18n | 16 ++++++++++++++++ apps/code/catalog.hu.i18n | 16 ++++++++++++++++ apps/code/catalog.pt.i18n | 16 ++++++++++++++++ apps/code/catalog.universal.i18n | 32 ++++++++++++++++++++++++++++++++ apps/code/python_toolbox.cpp | 22 +++++++++++++++++++++- apps/code/toolbox.de.i18n | 1 + apps/code/toolbox.en.i18n | 1 + apps/code/toolbox.es.i18n | 1 + apps/code/toolbox.fr.i18n | 1 + apps/code/toolbox.hu.i18n | 1 + apps/code/toolbox.pt.i18n | 1 + python/port/mod/ion/file.cpp | 4 ++++ 15 files changed, 159 insertions(+), 1 deletion(-) diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n index ba5d4676d..4bd73ab07 100644 --- a/apps/code/catalog.de.i18n +++ b/apps/code/catalog.de.i18n @@ -199,3 +199,19 @@ PythonTimeImport = "Import time module" PythonTimePrefix = "time module function prefix" PythonTimeSleep = "Warten Sie n Sekunden lang" PythonTimeMonotonic = "Monotone Zeit zurückgeben" +PythonFileOpen = "Öffnet eine Datei" +PythonFileSeekable = "Ist eine Datei durchsuchbar?" +PythonFileSeek = "Dateicursor verschieben" +PythonFileTell = "Cursorposition der Datei abrufen" +PythonFileClose = "Schließt eine Datei" +PythonFileClosed = "Wenn Datei geschlossen wurde" +PythonFileRead = "Bis zu size Bytes lesen" +PythonFileWrite = "Schreibe b in die Datei" +PythonFileReadline = "Lies eine Zeile" +PythonFileReadlines = "Liest eine Liste von Zeilen" +PythonFileTruncate = "Größe der Datei ändern" +PythonFileWritelines = "Schreibt eine Liste von Zeilen" +PythonFileName = "Dateiname" +PythonFileMode = "Dateiöffnungsmodus" +PythonFileReadable = "Ist die Datei lesbar?" +PythonFileWritable = "Ist die Datei beschreibbar?" diff --git a/apps/code/catalog.en.i18n b/apps/code/catalog.en.i18n index f833a5464..080d8462f 100644 --- a/apps/code/catalog.en.i18n +++ b/apps/code/catalog.en.i18n @@ -199,3 +199,19 @@ PythonTimeImport = "Import time module" PythonTimePrefix = "time module function prefix" PythonTimeSleep = "Wait for n second" PythonTimeMonotonic = "Return monotonic time" +PythonFileOpen = "Opens a file" +PythonFileSeekable = "Tells if seek can be used on a file" +PythonFileSeek = "Move file's cursor" +PythonFileTell = "Get file's cursor location" +PythonFileClose = "Closes a file" +PythonFileClosed = "True if file was closed" +PythonFileRead = "Read up to size bytes" +PythonFileWrite = "Write b into file" +PythonFileReadline = "Reads a line or up to size bytes" +PythonFileReadlines = "Reads a list of lines" +PythonFileTruncate = "Resize the file to size" +PythonFileWritelines = "Writes a list of lines" +PythonFileName = "Contains file's name" +PythonFileMode = "Contains file's open mode" +PythonFileReadable = "Tells if read can be used on a file" +PythonFileWritable = "Tells if write can be used on a file" diff --git a/apps/code/catalog.es.i18n b/apps/code/catalog.es.i18n index 0eefedac4..e0bc6c137 100644 --- a/apps/code/catalog.es.i18n +++ b/apps/code/catalog.es.i18n @@ -199,3 +199,19 @@ PythonTimeImport = "Import time module" PythonTimePrefix = "time module function prefix" PythonTimeSleep = "Esperar n segundos" PythonTimeMonotonic = "Tiempo monótono de retorno" +PythonFileOpen = "Opens a file" +PythonFileSeekable = "Tells if seek can be used on a file" +PythonFileSeek = "Move file's internal cursor" +PythonFileTell = "Get file's internal cursor location" +PythonFileClose = "Closes a file" +PythonFileClosed = "True if file was closed" +PythonFileRead = "Read up to size bytes" +PythonFileWrite = "Write b into file" +PythonFileReadline = "Reads a line or up to size bytes" +PythonFileReadlines = "Reads a list of lines" +PythonFileTruncate = "Resize the file to size" +PythonFileWritelines = "Writes a list of lines" +PythonFileName = "Contains file's name" +PythonFileMode = "Contains file's open mode" +PythonFileReadable = "Tells if read can be used on a file" +PythonFileWritable = "Tells if write can be used on a file" diff --git a/apps/code/catalog.fr.i18n b/apps/code/catalog.fr.i18n index 2a98a9105..2dfad76da 100644 --- a/apps/code/catalog.fr.i18n +++ b/apps/code/catalog.fr.i18n @@ -199,3 +199,19 @@ PythonTimeImport = "Importation du module temps" PythonTimePrefix = "Préfixe fonction du module temps" PythonTimeSleep = "Attendre n secondes" PythonTimeMonotonic = "Retourne le temps monotonic" +PythonFileOpen = "Ouvre un fichier" +PythonFileSeekable = "Indique si seek peut être utilisé" +PythonFileSeek = "Déplace le curseur interne" +PythonFileTell = "Donne la posititon du curseur" +PythonFileClose = "Ferme un fichier" +PythonFileClosed = "True si le fichier a été fermé" +PythonFileRead = "Lis jusqu'à size bytes" +PythonFileWrite = "Écris b dans le fichier" +PythonFileReadline = "Lis une ligne ou jusqu'à size bytes" +PythonFileReadlines = "Lis une liste de lignes" +PythonFileTruncate = "Redimensionne le fichier" +PythonFileWritelines = "Écris une liste de lignes" +PythonFileName = "Nom du fichier" +PythonFileMode = "Mode d'ouverture du fichier" +PythonFileReadable = "Indique si read peut être utilisé" +PythonFileWritable = "Indique si write peut être utilisé" diff --git a/apps/code/catalog.hu.i18n b/apps/code/catalog.hu.i18n index 862d85bd4..a82621ec8 100644 --- a/apps/code/catalog.hu.i18n +++ b/apps/code/catalog.hu.i18n @@ -199,3 +199,19 @@ PythonTimeImport = "Idömodul importálása" PythonTimePrefix = "idömodul funkció elötag" PythonTimeSleep = "Várj n másodpercet" PythonTimeMonotonic = "Vissza a monoton idö" +PythonFileOpen = "Fájl megnyitása" +PythonFileSeekable = "A fájl kereshető?" +PythonFileSeek = "A fájl kurzorának áthelyezése" +PythonFileTell = "A fájl kurzorának helye" +PythonFileClose = "Bezár egy fájlt" +PythonFileClosed = "Igaz, ha a fájl bezárt" +PythonFileRead = "Olvasson méretbájtig" +PythonFileWrite = "B beírása fájlba" +PythonFileReadline = "Olvas egy sort" +PythonFileReadlines = "Olvassa a sorok listáját" +PythonFileTruncate = "A fájl átméretezése méretre" +PythonFileWritelines = "Sorok listáját írja" +PythonFileName = "a fájl neve" +PythonFileMode = "a fájl nyitott módja" +PythonFileReadable = "A fájl olvasható?" +PythonFileWritable = "A fájl írható?" diff --git a/apps/code/catalog.pt.i18n b/apps/code/catalog.pt.i18n index 7294e19e0..97ef2861e 100644 --- a/apps/code/catalog.pt.i18n +++ b/apps/code/catalog.pt.i18n @@ -199,3 +199,19 @@ PythonTimeImport = "Import time module" PythonTimePrefix = "time module function prefix" PythonTimeSleep = "Aguardar n segundos" PythonTimeMonotonic = "Retornar tempo monotônico" +PythonFileOpen = "Opens a file" +PythonFileSeekable = "Tells if seek can be used on a file" +PythonFileSeek = "Move file's internal cursor" +PythonFileTell = "Get file's internal cursor location" +PythonFileClose = "Closes a file" +PythonFileClosed = "True if file was closed" +PythonFileRead = "Read up to size bytes" +PythonFileWrite = "Write b into file" +PythonFileReadline = "Reads a line or up to size bytes" +PythonFileReadlines = "Reads a list of lines" +PythonFileTruncate = "Resize the file to size" +PythonFileWritelines = "Writes a list of lines" +PythonFileName = "Contains file's name" +PythonFileMode = "Contains file's open mode" +PythonFileReadable = "Tells if read can be used on a file" +PythonFileWritable = "Tells if write can be used on a file" diff --git a/apps/code/catalog.universal.i18n b/apps/code/catalog.universal.i18n index c0fd92a28..bd028fe81 100644 --- a/apps/code/catalog.universal.i18n +++ b/apps/code/catalog.universal.i18n @@ -238,3 +238,35 @@ PythonTimeCommandImportFrom = "from time import *" PythonTimeCommandSleep = "sleep()" PythonTimeCommandSleepDemo = "sleep(n)" PythonTimeCommandMonotonic = "monotonic()" +PythonCommandFileOpen = "open(name, [mode])" +PythonCommandFileOpenWithoutArg = "open(\x11)" +PythonCommandFileSeek = "file.seek(offset, [whence])" +PythonCommandFileSeekWithoutArg = ".seek(\x11)" +PythonCommandFileTell = "file.tell()" +PythonCommandFileTellWithoutArg = ".tell()" +PythonCommandFileSeekable = "file.seekable()" +PythonCommandFileSeekableWithoutArg = ".seekable()" +PythonCommandFileClose = "file.close()" +PythonCommandFileCloseWithoutArg = ".close()" +PythonCommandFileClosed = "file.closed" +PythonCommandFileClosedWithoutArg = ".closed" +PythonCommandFileRead = "file.read([size])" +PythonCommandFileReadWithoutArg = ".read(\x11)" +PythonCommandFileWrite = "file.write(b)" +PythonCommandFileWriteWithoutArg = ".write(\x11)" +PythonCommandFileReadline = "file.readline([size])" +PythonCommandFileReadlineWithoutArg = ".readline(\x11)" +PythonCommandFileReadlines = "file.readlines([hint])" +PythonCommandFileReadlinesWithoutArg = ".readlines(\x11)" +PythonCommandFileTruncate = "file.truncate([size])" +PythonCommandFileTruncateWithoutArg = ".truncate(\x11)" +PythonCommandFileWritelines = "file.writelines(lines)" +PythonCommandFileWritelinesWithoutArg = ".writelines(\x11)" +PythonCommandFileName = "file.name" +PythonCommandFileNameWithoutArg = ".name" +PythonCommandFileMode = "file.mode" +PythonCommandFileModeWithoutArg = ".mode" +PythonCommandFileReadable = "file.readable()" +PythonCommandFileReadableWithoutArg = ".readable()" +PythonCommandFileWritable = "file.writable()" +PythonCommandFileWritableWithoutArg = ".writable()" diff --git a/apps/code/python_toolbox.cpp b/apps/code/python_toolbox.cpp index bdb79c4db..8ee79fbf1 100644 --- a/apps/code/python_toolbox.cpp +++ b/apps/code/python_toolbox.cpp @@ -422,11 +422,31 @@ const ToolboxMessageTree functionsChildren[] = { ToolboxMessageTree::Leaf(I18n::Message::PythonCommandReturn, I18n::Message::Default) }; +const ToolboxMessageTree fileChildren[] { + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileOpen, I18n::Message::PythonFileOpen, false, I18n::Message::PythonCommandFileOpenWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileClose, I18n::Message::PythonFileClose, false, I18n::Message::PythonCommandFileCloseWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileClosed, I18n::Message::PythonFileClosed, false, I18n::Message::PythonCommandFileClosedWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileMode, I18n::Message::PythonFileMode, false, I18n::Message::PythonCommandFileModeWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileName, I18n::Message::PythonFileName, false, I18n::Message::PythonCommandFileNameWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileRead, I18n::Message::PythonFileRead, false, I18n::Message::PythonCommandFileReadWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileReadable, I18n::Message::PythonFileReadable, false, I18n::Message::PythonCommandFileReadableWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileReadline, I18n::Message::PythonFileReadline, false, I18n::Message::PythonCommandFileReadlineWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileReadlines, I18n::Message::PythonFileReadlines, false, I18n::Message::PythonCommandFileReadlinesWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileSeek, I18n::Message::PythonFileSeek, false, I18n::Message::PythonCommandFileSeekWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileSeekable, I18n::Message::PythonFileSeekable, false, I18n::Message::PythonCommandFileSeekableWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileTell, I18n::Message::PythonFileTell, false, I18n::Message::PythonCommandFileTellWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileTruncate, I18n::Message::PythonFileTruncate, false, I18n::Message::PythonCommandFileTruncateWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileWrite, I18n::Message::PythonFileWrite, false, I18n::Message::PythonCommandFileWriteWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileWritable, I18n::Message::PythonFileWritable, false, I18n::Message::PythonCommandFileWritableWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileWritelines, I18n::Message::PythonFileWritelines, false, I18n::Message::PythonCommandFileWritelinesWithoutArg), +}; + const ToolboxMessageTree menu[] = { ToolboxMessageTree::Node(I18n::Message::LoopsAndTests, loopsAndTestsChildren), ToolboxMessageTree::Node(I18n::Message::Modules, modulesChildren), ToolboxMessageTree::Node(I18n::Message::Catalog, catalogChildren), - ToolboxMessageTree::Node(I18n::Message::Functions, functionsChildren) + ToolboxMessageTree::Node(I18n::Message::Functions, functionsChildren), + ToolboxMessageTree::Node(I18n::Message::Files, fileChildren) }; const ToolboxMessageTree toolboxModel = ToolboxMessageTree::Node(I18n::Message::Toolbox, menu); diff --git a/apps/code/toolbox.de.i18n b/apps/code/toolbox.de.i18n index 41775c90c..fedc22d19 100644 --- a/apps/code/toolbox.de.i18n +++ b/apps/code/toolbox.de.i18n @@ -2,3 +2,4 @@ Functions = "Funktionen" Catalog = "Katalog" Modules = "Module" LoopsAndTests = "Schleifen und Tests" +Files = "Dateien" diff --git a/apps/code/toolbox.en.i18n b/apps/code/toolbox.en.i18n index 178d42b7b..40c50d42d 100644 --- a/apps/code/toolbox.en.i18n +++ b/apps/code/toolbox.en.i18n @@ -2,3 +2,4 @@ Functions = "Functions" Catalog = "Catalog" Modules = "Modules" LoopsAndTests = "Loops and tests" +Files = "Files" diff --git a/apps/code/toolbox.es.i18n b/apps/code/toolbox.es.i18n index 178d42b7b..40c50d42d 100644 --- a/apps/code/toolbox.es.i18n +++ b/apps/code/toolbox.es.i18n @@ -2,3 +2,4 @@ Functions = "Functions" Catalog = "Catalog" Modules = "Modules" LoopsAndTests = "Loops and tests" +Files = "Files" diff --git a/apps/code/toolbox.fr.i18n b/apps/code/toolbox.fr.i18n index 6442521cc..0b16add7c 100644 --- a/apps/code/toolbox.fr.i18n +++ b/apps/code/toolbox.fr.i18n @@ -2,3 +2,4 @@ Functions = "Fonctions" Catalog = "Catalogue" Modules = "Modules" LoopsAndTests = "Boucles et tests" +Files = "Fichiers" diff --git a/apps/code/toolbox.hu.i18n b/apps/code/toolbox.hu.i18n index 930be1430..8730fb185 100644 --- a/apps/code/toolbox.hu.i18n +++ b/apps/code/toolbox.hu.i18n @@ -2,3 +2,4 @@ Functions = "Funkciók" Catalog = "Katalógus" Modules = "Modulok" LoopsAndTests = "Hurkok és tesztek" +Files = "Fájlok" diff --git a/apps/code/toolbox.pt.i18n b/apps/code/toolbox.pt.i18n index 178d42b7b..40c50d42d 100644 --- a/apps/code/toolbox.pt.i18n +++ b/apps/code/toolbox.pt.i18n @@ -2,3 +2,4 @@ Functions = "Functions" Catalog = "Catalog" Modules = "Modules" LoopsAndTests = "Loops and tests" +Files = "Files" diff --git a/python/port/mod/ion/file.cpp b/python/port/mod/ion/file.cpp index 1cc8ff8de..df53bdc83 100644 --- a/python/port/mod/ion/file.cpp +++ b/python/port/mod/ion/file.cpp @@ -298,6 +298,10 @@ STATIC void file_attr(mp_obj_t self_in, qstr attribute, mp_obj_t *destination) { destination[0] = (mp_obj_t) MP_ROM_PTR(&file_readable_obj); destination[1] = self_in; break; + case MP_QSTR_writable: + destination[0] = (mp_obj_t) MP_ROM_PTR(&file_writable_obj); + destination[1] = self_in; + break; case MP_QSTR_read: destination[0] = (mp_obj_t) MP_ROM_PTR(&file_read_obj); destination[1] = self_in; From c4363103f6e6727d68d90f1df02911470ae778bc Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Mon, 6 Jul 2020 12:45:47 +0200 Subject: [PATCH 14/14] [apps/code] Added exceptions handling in toolbox --- apps/code/python_toolbox.cpp | 12 ++++++++++-- apps/code/toolbox.de.i18n | 1 + apps/code/toolbox.en.i18n | 1 + apps/code/toolbox.es.i18n | 1 + apps/code/toolbox.fr.i18n | 1 + apps/code/toolbox.hu.i18n | 1 + apps/code/toolbox.pt.i18n | 1 + apps/code/toolbox.universal.i18n | 8 ++++++++ 8 files changed, 24 insertions(+), 2 deletions(-) diff --git a/apps/code/python_toolbox.cpp b/apps/code/python_toolbox.cpp index 8ee79fbf1..9c30d04d6 100644 --- a/apps/code/python_toolbox.cpp +++ b/apps/code/python_toolbox.cpp @@ -441,12 +441,20 @@ const ToolboxMessageTree fileChildren[] { ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileWritelines, I18n::Message::PythonFileWritelines, false, I18n::Message::PythonCommandFileWritelinesWithoutArg), }; +const ToolboxMessageTree exceptionsChildren[] = { + ToolboxMessageTree::Leaf(I18n::Message::TryExcept1ErrorWithArg, I18n::Message::Default, false, I18n::Message::TryExcept1Error), + ToolboxMessageTree::Leaf(I18n::Message::TryExcept1ErrorElseWithArg, I18n::Message::Default, false, I18n::Message::TryExcept1ErrorElse), + ToolboxMessageTree::Leaf(I18n::Message::TryExcept2ErrorWithArg, I18n::Message::Default, false, I18n::Message::TryExcept2Error), + ToolboxMessageTree::Leaf(I18n::Message::WithInstructionWithArg, I18n::Message::Default, false, I18n::Message::WithInstruction), +}; + const ToolboxMessageTree menu[] = { ToolboxMessageTree::Node(I18n::Message::LoopsAndTests, loopsAndTestsChildren), ToolboxMessageTree::Node(I18n::Message::Modules, modulesChildren), ToolboxMessageTree::Node(I18n::Message::Catalog, catalogChildren), ToolboxMessageTree::Node(I18n::Message::Functions, functionsChildren), - ToolboxMessageTree::Node(I18n::Message::Files, fileChildren) + ToolboxMessageTree::Node(I18n::Message::Files, fileChildren), + ToolboxMessageTree::Node(I18n::Message::Exceptions, exceptionsChildren) }; const ToolboxMessageTree toolboxModel = ToolboxMessageTree::Node(I18n::Message::Toolbox, menu); @@ -472,7 +480,7 @@ bool PythonToolbox::handleEvent(Ion::Events::Event event) { } KDCoordinate PythonToolbox::rowHeight(int j) { - if (typeAtLocation(0, j) == Toolbox::LeafCellType && m_messageTreeModel->label() == I18n::Message::IfStatementMenu) { + if (typeAtLocation(0, j) == Toolbox::LeafCellType && (m_messageTreeModel->label() == I18n::Message::IfStatementMenu || m_messageTreeModel->label() == I18n::Message::Exceptions)) { /* To get the exact height needed for each cell, we have to compute its * text size, which means scan the text char by char to look for '\n' * chars. This is very costly and ruins the speed performance when diff --git a/apps/code/toolbox.de.i18n b/apps/code/toolbox.de.i18n index fedc22d19..34840329b 100644 --- a/apps/code/toolbox.de.i18n +++ b/apps/code/toolbox.de.i18n @@ -3,3 +3,4 @@ Catalog = "Katalog" Modules = "Module" LoopsAndTests = "Schleifen und Tests" Files = "Dateien" +Exceptions = "Ausnahmen" diff --git a/apps/code/toolbox.en.i18n b/apps/code/toolbox.en.i18n index 40c50d42d..81e60c7da 100644 --- a/apps/code/toolbox.en.i18n +++ b/apps/code/toolbox.en.i18n @@ -3,3 +3,4 @@ Catalog = "Catalog" Modules = "Modules" LoopsAndTests = "Loops and tests" Files = "Files" +Exceptions = "Exceptions" diff --git a/apps/code/toolbox.es.i18n b/apps/code/toolbox.es.i18n index 40c50d42d..81e60c7da 100644 --- a/apps/code/toolbox.es.i18n +++ b/apps/code/toolbox.es.i18n @@ -3,3 +3,4 @@ Catalog = "Catalog" Modules = "Modules" LoopsAndTests = "Loops and tests" Files = "Files" +Exceptions = "Exceptions" diff --git a/apps/code/toolbox.fr.i18n b/apps/code/toolbox.fr.i18n index 0b16add7c..724abb7a5 100644 --- a/apps/code/toolbox.fr.i18n +++ b/apps/code/toolbox.fr.i18n @@ -3,3 +3,4 @@ Catalog = "Catalogue" Modules = "Modules" LoopsAndTests = "Boucles et tests" Files = "Fichiers" +Exceptions = "Exceptions" diff --git a/apps/code/toolbox.hu.i18n b/apps/code/toolbox.hu.i18n index 8730fb185..890151220 100644 --- a/apps/code/toolbox.hu.i18n +++ b/apps/code/toolbox.hu.i18n @@ -3,3 +3,4 @@ Catalog = "Katalógus" Modules = "Modulok" LoopsAndTests = "Hurkok és tesztek" Files = "Fájlok" +Exceptions = "Kivételek" diff --git a/apps/code/toolbox.pt.i18n b/apps/code/toolbox.pt.i18n index 40c50d42d..81e60c7da 100644 --- a/apps/code/toolbox.pt.i18n +++ b/apps/code/toolbox.pt.i18n @@ -3,3 +3,4 @@ Catalog = "Catalog" Modules = "Modules" LoopsAndTests = "Loops and tests" Files = "Files" +Exceptions = "Exceptions" diff --git a/apps/code/toolbox.universal.i18n b/apps/code/toolbox.universal.i18n index 5a5072475..0600a4dfd 100644 --- a/apps/code/toolbox.universal.i18n +++ b/apps/code/toolbox.universal.i18n @@ -44,6 +44,14 @@ ForInRange2ArgsLoop = "for i in range(\x11,):\n " ForInRange2ArgsLoopWithArg = "for i in range(start, stop):\n instruction" ForInRange1ArgLoop = "for i in range(\x11):\n " ForInRange1ArgLoopWithArg = "for i in range(size):\n instruction" +TryExcept1Error = "try:\n \nexcept \x11:\n " +TryExcept1ErrorWithArg = "try:\n instruction\nexcept Error:\n reaction" +TryExcept1ErrorElse = "try:\n \nexcept \x11:\n \nelse:\n " +TryExcept1ErrorElseWithArg = "try:\n instruction\nexcept Error:\n reaction\nelse:\n clean" +TryExcept2Error = "try:\n \nexcept (\x11):\n " +TryExcept2ErrorWithArg = "try:\n instruction\nexcept (Error1, Error2):\n reaction" +WithInstruction = "with \x11 as :\n " +WithInstructionWithArg = "with expression as target:\n instructions" PythonCommandDef = "def \x11():\n " PythonCommandDefWithArg = "def function(x):" PythonCommandReturn = "return "