Browse Source
Define buffer and stream protocols, and other starting bits of io.* framework, with io.FileIO-like implementation for Unixpull/120/head
Damien George
11 years ago
7 changed files with 220 additions and 0 deletions
@ -0,0 +1,55 @@ |
|||
#include <string.h> |
|||
|
|||
#include "nlr.h" |
|||
#include "misc.h" |
|||
#include "mpconfig.h" |
|||
#include "mpqstr.h" |
|||
#include "obj.h" |
|||
#include "stream.h" |
|||
|
|||
// This file defines generic Python stream read/write methods which
|
|||
// dispatch to the underlying stream interface of an object.
|
|||
|
|||
static mp_obj_t stream_read(mp_obj_t self_in, mp_obj_t arg) { |
|||
struct _mp_obj_base_t *o = (struct _mp_obj_base_t *)self_in; |
|||
if (o->type->stream_p.read == NULL) { |
|||
// CPython: io.UnsupportedOperation, OSError subclass
|
|||
nlr_jump(mp_obj_new_exception_msg(MP_QSTR_OSError, "Operation not supported")); |
|||
} |
|||
|
|||
machine_int_t sz = mp_obj_get_int(arg); |
|||
// +1 because so far we mark end of string with \0
|
|||
char *buf = m_new(char, sz + 1); |
|||
int error; |
|||
machine_int_t out_sz = o->type->stream_p.read(self_in, buf, sz, &error); |
|||
if (out_sz == -1) { |
|||
nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_OSError, "[Errno %d]", (const char *)error)); |
|||
} else { |
|||
buf[out_sz] = 0; |
|||
return mp_obj_new_str(qstr_from_str_take(buf, /*out_sz,*/ sz + 1)); |
|||
} |
|||
} |
|||
|
|||
static mp_obj_t stream_write(mp_obj_t self_in, mp_obj_t arg) { |
|||
struct _mp_obj_base_t *o = (struct _mp_obj_base_t *)self_in; |
|||
if (o->type->stream_p.write == NULL) { |
|||
// CPython: io.UnsupportedOperation, OSError subclass
|
|||
nlr_jump(mp_obj_new_exception_msg(MP_QSTR_OSError, "Operation not supported")); |
|||
} |
|||
|
|||
const char *buf = qstr_str(mp_obj_get_qstr(arg)); |
|||
machine_int_t sz = strlen(buf); |
|||
int error; |
|||
machine_int_t out_sz = o->type->stream_p.write(self_in, buf, sz, &error); |
|||
if (out_sz == -1) { |
|||
nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_OSError, "[Errno %d]", (const char *)error)); |
|||
} else { |
|||
// http://docs.python.org/3/library/io.html#io.RawIOBase.write
|
|||
// "None is returned if the raw stream is set not to block and no single byte could be readily written to it."
|
|||
// Do they mean that instead of 0 they return None?
|
|||
return MP_OBJ_NEW_SMALL_INT(out_sz); |
|||
} |
|||
} |
|||
|
|||
MP_DEFINE_CONST_FUN_OBJ_2(mp_stream_read_obj, stream_read); |
|||
MP_DEFINE_CONST_FUN_OBJ_2(mp_stream_write_obj, stream_write); |
@ -0,0 +1,2 @@ |
|||
extern const mp_obj_fun_native_t mp_stream_read_obj; |
|||
extern const mp_obj_fun_native_t mp_stream_write_obj; |
@ -0,0 +1,117 @@ |
|||
#include <string.h> |
|||
#include <unistd.h> |
|||
#include <fcntl.h> |
|||
#include <errno.h> |
|||
|
|||
#include "nlr.h" |
|||
#include "misc.h" |
|||
#include "mpconfig.h" |
|||
#include "mpqstr.h" |
|||
#include "obj.h" |
|||
#include "stream.h" |
|||
|
|||
typedef struct _mp_obj_fdfile_t { |
|||
mp_obj_base_t base; |
|||
int fd; |
|||
} mp_obj_fdfile_t; |
|||
|
|||
static void fdfile_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) { |
|||
mp_obj_fdfile_t *self = self_in; |
|||
print(env, "<io.FileIO %d>", self->fd); |
|||
} |
|||
|
|||
static machine_int_t fdfile_read(mp_obj_t o_in, void *buf, machine_uint_t size, int *errcode) { |
|||
mp_obj_fdfile_t *o = o_in; |
|||
machine_int_t r = read(o->fd, buf, size); |
|||
if (r == -1) { |
|||
*errcode = errno; |
|||
} |
|||
return r; |
|||
} |
|||
|
|||
static machine_int_t fdfile_write(mp_obj_t o_in, const void *buf, machine_uint_t size, int *errcode) { |
|||
mp_obj_fdfile_t *o = o_in; |
|||
machine_int_t r = write(o->fd, buf, size); |
|||
if (r == -1) { |
|||
*errcode = errno; |
|||
} |
|||
return r; |
|||
} |
|||
|
|||
static mp_obj_t fdfile_close(mp_obj_t self_in) { |
|||
mp_obj_fdfile_t *self = self_in; |
|||
close(self->fd); |
|||
return mp_const_none; |
|||
} |
|||
static MP_DEFINE_CONST_FUN_OBJ_1(fdfile_close_obj, fdfile_close); |
|||
|
|||
static mp_obj_t fdfile_make_new(mp_obj_t type_in, int n_args, const mp_obj_t *args) { |
|||
mp_obj_fdfile_t *o = m_new_obj(mp_obj_fdfile_t); |
|||
o->base.type = type_in; |
|||
|
|||
if (MP_OBJ_IS_SMALL_INT(args[0])) { |
|||
o->fd = MP_OBJ_SMALL_INT_VALUE(args[0]); |
|||
return o; |
|||
} |
|||
|
|||
const char *fname = qstr_str(mp_obj_get_qstr(args[0])); |
|||
const char *mode_s; |
|||
if (n_args > 1) { |
|||
mode_s = qstr_str(mp_obj_get_qstr(args[1])); |
|||
} else { |
|||
mode_s = "r"; |
|||
} |
|||
|
|||
int mode = 0; |
|||
while (*mode_s) { |
|||
switch (*mode_s++) { |
|||
// Note: these assume O_RDWR = O_RDONLY | O_WRONLY
|
|||
case 'r': |
|||
mode |= O_RDONLY; |
|||
break; |
|||
case 'w': |
|||
mode |= O_WRONLY | O_CREAT | O_TRUNC; |
|||
break; |
|||
case 'a': |
|||
mode |= O_APPEND; |
|||
break; |
|||
case '+': |
|||
mode |= O_RDWR; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
o->fd = open(fname, mode, 0644); |
|||
if (o->fd == -1) { |
|||
nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_OSError, "[Errno %d]", (const char *)errno)); |
|||
} |
|||
return o; |
|||
} |
|||
|
|||
static const mp_method_t rawfile_type_methods[] = { |
|||
{ "read", &mp_stream_read_obj }, |
|||
{ "write", &mp_stream_write_obj }, |
|||
{ "close", &fdfile_close_obj }, |
|||
{ NULL, NULL }, |
|||
}; |
|||
|
|||
static const mp_obj_type_t rawfile_type = { |
|||
{ &mp_const_type }, |
|||
"io.FileIO", |
|||
.print = fdfile_print, |
|||
.make_new = fdfile_make_new, |
|||
.getiter = NULL, |
|||
.iternext = NULL, |
|||
.stream_p = { |
|||
.read = fdfile_read, |
|||
.write = fdfile_write, |
|||
}, |
|||
.methods = rawfile_type_methods, |
|||
}; |
|||
|
|||
// Factory function for I/O stream classes
|
|||
mp_obj_t mp_builtin_open(int n_args, const mp_obj_t *args) { |
|||
// TODO: analyze mode and buffering args and instantiate appropriate type
|
|||
return fdfile_make_new((mp_obj_t)&rawfile_type, n_args, args); |
|||
} |
|||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_open_obj, 1, 2, mp_builtin_open); |
Loading…
Reference in new issue