Skip to content

Commit b7572ad

Browse files
committed
stmhal, file: Implement a,x,+ open modes, seek and tell.
Also now returns correct POSIX errno when an IO operation fails. Addresses issues adafruit#516 and adafruit#676.
1 parent 58cbb4d commit b7572ad

3 files changed

Lines changed: 176 additions & 47 deletions

File tree

stmhal/file.c

Lines changed: 170 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,45 @@
2525
*/
2626

2727
#include <stdio.h>
28+
#include <errno.h>
2829

29-
#include "misc.h"
3030
#include "mpconfig.h"
31+
#include "nlr.h"
32+
#include "misc.h"
3133
#include "qstr.h"
3234
#include "obj.h"
3335
#include "runtime.h"
3436
#include "stream.h"
3537
#include "file.h"
3638
#include "ff.h"
3739

40+
extern const mp_obj_type_t mp_type_fileio;
41+
extern const mp_obj_type_t mp_type_textio;
42+
43+
// this table converts from FRESULT to POSIX errno
44+
STATIC const byte fresult_to_errno_table[] = {
45+
[FR_OK] = 0,
46+
[FR_DISK_ERR] = EIO,
47+
[FR_INT_ERR] = EIO,
48+
[FR_NOT_READY] = EBUSY,
49+
[FR_NO_FILE] = ENOENT,
50+
[FR_NO_PATH] = ENOENT,
51+
[FR_INVALID_NAME] = EINVAL,
52+
[FR_DENIED] = EACCES,
53+
[FR_EXIST] = EEXIST,
54+
[FR_INVALID_OBJECT] = EINVAL,
55+
[FR_WRITE_PROTECTED] = EROFS,
56+
[FR_INVALID_DRIVE] = ENODEV,
57+
[FR_NOT_ENABLED] = ENODEV,
58+
[FR_NO_FILESYSTEM] = ENODEV,
59+
[FR_MKFS_ABORTED] = EIO,
60+
[FR_TIMEOUT] = EIO,
61+
[FR_LOCKED] = EIO,
62+
[FR_NOT_ENOUGH_CORE] = ENOMEM,
63+
[FR_TOO_MANY_OPEN_FILES] = EMFILE,
64+
[FR_INVALID_PARAMETER] = EINVAL,
65+
};
66+
3867
typedef struct _pyb_file_obj_t {
3968
mp_obj_base_t base;
4069
FIL fp;
@@ -44,17 +73,25 @@ void file_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, m
4473
printf("<io.%s %p>", mp_obj_get_type_str(self_in), self_in);
4574
}
4675

47-
STATIC machine_int_t file_read(mp_obj_t self_in, void *buf, machine_uint_t size, int *errcode) {
76+
STATIC machine_int_t file_obj_read(mp_obj_t self_in, void *buf, machine_uint_t size, int *errcode) {
4877
pyb_file_obj_t *self = self_in;
4978
UINT sz_out;
50-
*errcode = f_read(&self->fp, buf, size, &sz_out);
79+
FRESULT res = f_read(&self->fp, buf, size, &sz_out);
80+
if (res != FR_OK) {
81+
*errcode = fresult_to_errno_table[res];
82+
return -1;
83+
}
5184
return sz_out;
5285
}
5386

54-
STATIC machine_int_t file_write(mp_obj_t self_in, const void *buf, machine_uint_t size, int *errcode) {
87+
STATIC machine_int_t file_obj_write(mp_obj_t self_in, const void *buf, machine_uint_t size, int *errcode) {
5588
pyb_file_obj_t *self = self_in;
5689
UINT sz_out;
57-
*errcode = f_write(&self->fp, buf, size, &sz_out);
90+
FRESULT res = f_write(&self->fp, buf, size, &sz_out);
91+
if (res != FR_OK) {
92+
*errcode = fresult_to_errno_table[res];
93+
return -1;
94+
}
5895
return sz_out;
5996
}
6097

@@ -63,77 +100,163 @@ mp_obj_t file_obj_close(mp_obj_t self_in) {
63100
f_close(&self->fp);
64101
return mp_const_none;
65102
}
66-
67103
STATIC MP_DEFINE_CONST_FUN_OBJ_1(file_obj_close_obj, file_obj_close);
68104

69105
mp_obj_t file_obj___exit__(uint n_args, const mp_obj_t *args) {
70106
return file_obj_close(args[0]);
71107
}
72-
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(file_obj___exit___obj, 4, 4, file_obj___exit__);
108+
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(file_obj___exit___obj, 4, 4, file_obj___exit__);
109+
110+
mp_obj_t file_obj_seek(uint n_args, const mp_obj_t *args) {
111+
pyb_file_obj_t *self = args[0];
112+
machine_int_t offset = mp_obj_get_int(args[1]);
113+
machine_int_t whence = 0;
114+
if (n_args == 3) {
115+
whence = mp_obj_get_int(args[2]);
116+
}
117+
118+
switch (whence) {
119+
case 0: // SEEK_SET
120+
f_lseek(&self->fp, offset);
121+
break;
122+
123+
case 1: // SEEK_CUR
124+
if (offset != 0) {
125+
goto error;
126+
}
127+
// no-operation
128+
break;
129+
130+
case 2: // SEEK_END
131+
if (offset != 0) {
132+
goto error;
133+
}
134+
f_lseek(&self->fp, f_size(&self->fp));
135+
break;
136+
137+
default:
138+
goto error;
139+
}
140+
141+
return mp_obj_new_int_from_uint(f_tell(&self->fp));
142+
143+
error:
144+
// A bad whence is a ValueError, while offset!=0 is an io.UnsupportedOperation.
145+
// But the latter inherits ValueError (as well as IOError), so we just raise ValueError.
146+
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "invalid whence and/or offset"));
147+
}
148+
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(file_obj_seek_obj, 2, 3, file_obj_seek);
149+
150+
mp_obj_t file_obj_tell(mp_obj_t self_in) {
151+
pyb_file_obj_t *self = self_in;
152+
return mp_obj_new_int_from_uint(f_tell(&self->fp));
153+
}
154+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(file_obj_tell_obj, file_obj_tell);
155+
156+
STATIC mp_obj_t file_obj_make_new(mp_obj_t type, uint n_args, uint n_kw, const mp_obj_t *args) {
157+
mp_arg_check_num(n_args, n_kw, 1, 2, false);
158+
159+
const char *fname = mp_obj_str_get_str(args[0]);
160+
161+
int mode = 0;
162+
if (n_args == 1) {
163+
mode = FA_READ;
164+
} else {
165+
const char *mode_s = mp_obj_str_get_str(args[1]);
166+
// TODO make sure only one of r, w, x, a, and b, t are specified
167+
while (*mode_s) {
168+
switch (*mode_s++) {
169+
case 'r':
170+
mode |= FA_READ;
171+
break;
172+
case 'w':
173+
mode |= FA_WRITE | FA_CREATE_ALWAYS;
174+
break;
175+
case 'x':
176+
mode |= FA_WRITE | FA_CREATE_NEW;
177+
break;
178+
case 'a':
179+
mode |= FA_WRITE | FA_OPEN_ALWAYS;
180+
break;
181+
case '+':
182+
mode |= FA_READ | FA_WRITE;
183+
break;
184+
#if MICROPY_PY_IO_FILEIO
185+
case 'b':
186+
type = (mp_obj_t)&mp_type_fileio;
187+
break;
188+
#endif
189+
case 't':
190+
type = (mp_obj_t)&mp_type_textio;
191+
break;
192+
}
193+
}
194+
}
195+
196+
pyb_file_obj_t *o = m_new_obj_with_finaliser(pyb_file_obj_t);
197+
o->base.type = type;
198+
199+
FRESULT res = f_open(&o->fp, fname, mode);
200+
if (res != FR_OK) {
201+
m_del_obj(pyb_file_obj_t, o);
202+
nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT((machine_int_t)fresult_to_errno_table[res])));
203+
}
204+
205+
return o;
206+
}
73207

74208
// TODO gc hook to close the file if not already closed
75209

76-
STATIC const mp_map_elem_t file_locals_dict_table[] = {
210+
STATIC const mp_map_elem_t rawfile_locals_dict_table[] = {
77211
{ MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj },
78212
{ MP_OBJ_NEW_QSTR(MP_QSTR_readall), (mp_obj_t)&mp_stream_readall_obj },
79213
{ MP_OBJ_NEW_QSTR(MP_QSTR_readline), (mp_obj_t)&mp_stream_unbuffered_readline_obj},
80214
{ MP_OBJ_NEW_QSTR(MP_QSTR_readlines), (mp_obj_t)&mp_stream_unbuffered_readlines_obj},
81215
{ MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&mp_stream_write_obj },
82216
{ MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t)&file_obj_close_obj },
217+
{ MP_OBJ_NEW_QSTR(MP_QSTR_seek), (mp_obj_t)&file_obj_seek_obj },
218+
{ MP_OBJ_NEW_QSTR(MP_QSTR_tell), (mp_obj_t)&file_obj_tell_obj },
83219
{ MP_OBJ_NEW_QSTR(MP_QSTR___del__), (mp_obj_t)&file_obj_close_obj },
84220
{ MP_OBJ_NEW_QSTR(MP_QSTR___enter__), (mp_obj_t)&mp_identity_obj },
85221
{ MP_OBJ_NEW_QSTR(MP_QSTR___exit__), (mp_obj_t)&file_obj___exit___obj },
86222
};
87223

88-
STATIC MP_DEFINE_CONST_DICT(file_locals_dict, file_locals_dict_table);
89-
90-
STATIC mp_obj_t file_obj_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args);
224+
STATIC MP_DEFINE_CONST_DICT(rawfile_locals_dict, rawfile_locals_dict_table);
91225

92-
STATIC const mp_stream_p_t file_obj_stream_p = {
93-
.read = file_read,
94-
.write = file_write,
226+
#if MICROPY_PY_IO_FILEIO
227+
STATIC const mp_stream_p_t fileio_stream_p = {
228+
.read = file_obj_read,
229+
.write = file_obj_write,
230+
.is_bytes = true,
95231
};
96232

97-
const mp_obj_type_t mp_type_textio = {
233+
const mp_obj_type_t mp_type_fileio = {
98234
{ &mp_type_type },
99235
.name = MP_QSTR_FileIO,
100-
.make_new = file_obj_make_new,
101236
.print = file_obj_print,
237+
.make_new = file_obj_make_new,
102238
.getiter = mp_identity,
103239
.iternext = mp_stream_unbuffered_iter,
104-
.stream_p = &file_obj_stream_p,
105-
.locals_dict = (mp_obj_t)&file_locals_dict,
240+
.stream_p = &fileio_stream_p,
241+
.locals_dict = (mp_obj_t)&rawfile_locals_dict,
106242
};
243+
#endif
107244

108-
STATIC mp_obj_t file_obj_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
109-
mp_arg_check_num(n_args, n_kw, 1, 2, false);
110-
const char *filename = mp_obj_str_get_str(args[0]);
111-
const char *mode = "r";
112-
if (n_args > 1) {
113-
mode = mp_obj_str_get_str(args[1]);
114-
}
115-
pyb_file_obj_t *self = m_new_obj_with_finaliser(pyb_file_obj_t);
116-
self->base.type = &mp_type_textio;
117-
if (mode[0] == 'r') {
118-
// open for reading
119-
FRESULT res = f_open(&self->fp, filename, FA_READ);
120-
if (res != FR_OK) {
121-
printf("FileNotFoundError: [Errno 2] No such file or directory: '%s'\n", filename);
122-
return mp_const_none;
123-
}
124-
} else if (mode[0] == 'w') {
125-
// open for writing, truncate the file first
126-
FRESULT res = f_open(&self->fp, filename, FA_WRITE | FA_CREATE_ALWAYS);
127-
if (res != FR_OK) {
128-
printf("?FileError: could not create file: '%s'\n", filename);
129-
return mp_const_none;
130-
}
131-
} else {
132-
printf("ValueError: invalid mode: '%s'\n", mode);
133-
return mp_const_none;
134-
}
135-
return self;
136-
}
245+
STATIC const mp_stream_p_t textio_stream_p = {
246+
.read = file_obj_read,
247+
.write = file_obj_write,
248+
};
249+
250+
const mp_obj_type_t mp_type_textio = {
251+
{ &mp_type_type },
252+
.name = MP_QSTR_TextIOWrapper,
253+
.print = file_obj_print,
254+
.make_new = file_obj_make_new,
255+
.getiter = mp_identity,
256+
.iternext = mp_stream_unbuffered_iter,
257+
.stream_p = &textio_stream_p,
258+
.locals_dict = (mp_obj_t)&rawfile_locals_dict,
259+
};
137260

138261
// Factory function for I/O stream classes
139262
STATIC mp_obj_t pyb_io_open(uint n_args, const mp_obj_t *args) {

stmhal/mpconfigport.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
#define MICROPY_PY_SYS_EXIT (1)
4949
#define MICROPY_PY_SYS_STDFILES (1)
5050
#define MICROPY_PY_CMATH (1)
51+
#define MICROPY_PY_IO (1)
52+
#define MICROPY_PY_IO_FILEIO (1)
5153

5254
// extra built in names to add to the global namespace
5355
extern const struct _mp_obj_fun_native_t mp_builtin_help_obj;

stmhal/qstrdefsport.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ Q(0:/)
6666
Q(0:/lib)
6767
Q(millis)
6868

69+
// for file class
70+
Q(seek)
71+
Q(tell)
72+
6973
// for RTC class
7074
Q(RTC)
7175
Q(info)

0 commit comments

Comments
 (0)