From d7da2dba07778fa3c0c7873b7c253bde48066e9c Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Wed, 3 May 2017 01:47:08 +0300 Subject: [PATCH] py/modio: Implement uio.resource_stream(package, resource_path). The with semantics of this function is close to pkg_resources.resource_stream() function from setuptools, which is the canonical way to access non-source files belonging to a package (resources), regardless of what medium the package uses (e.g. individual source files vs zip archive). In the case of MicroPython, this function allows to access resources which are frozen into the executable, besides accessing resources in the file system. This is initial stage of the implementation, which actually doesn't implement "package" part of the semantics, just accesses frozen resources from "root", or filesystem resource - from current dir. --- py/frozenmod.c | 25 +++++++++++++++++++------ py/frozenmod.h | 3 +++ py/modio.c | 29 +++++++++++++++++++++++++++++ py/mpconfig.h | 7 +++++++ unix/mpconfigport.h | 1 + 5 files changed, 59 insertions(+), 6 deletions(-) diff --git a/py/frozenmod.c b/py/frozenmod.c index 660167eed4..1eaaf574a2 100644 --- a/py/frozenmod.c +++ b/py/frozenmod.c @@ -43,16 +43,16 @@ extern const char mp_frozen_str_names[]; extern const uint32_t mp_frozen_str_sizes[]; extern const char mp_frozen_str_content[]; -STATIC mp_lexer_t *mp_find_frozen_str(const char *str, size_t len) { +// On input, *len contains size of name, on output - size of content +const char *mp_find_frozen_str(const char *str, size_t *len) { const char *name = mp_frozen_str_names; size_t offset = 0; for (int i = 0; *name != 0; i++) { size_t l = strlen(name); - if (l == len && !memcmp(str, name, l)) { - qstr source = qstr_from_strn(name, l); - mp_lexer_t *lex = MICROPY_MODULE_FROZEN_LEXER(source, mp_frozen_str_content + offset, mp_frozen_str_sizes[i], 0); - return lex; + if (l == *len && !memcmp(str, name, l)) { + *len = mp_frozen_str_sizes[i]; + return mp_frozen_str_content + offset; } name += l + 1; offset += mp_frozen_str_sizes[i] + 1; @@ -60,6 +60,19 @@ STATIC mp_lexer_t *mp_find_frozen_str(const char *str, size_t len) { return NULL; } +STATIC mp_lexer_t *mp_lexer_frozen_str(const char *str, size_t len) { + size_t name_len = len; + const char *content = mp_find_frozen_str(str, &len); + + if (content == NULL) { + return NULL; + } + + qstr source = qstr_from_strn(str, name_len); + mp_lexer_t *lex = MICROPY_MODULE_FROZEN_LEXER(source, content, len, 0); + return lex; +} + #endif #if MICROPY_MODULE_FROZEN_MPY @@ -124,7 +137,7 @@ mp_import_stat_t mp_frozen_stat(const char *str) { int mp_find_frozen_module(const char *str, size_t len, void **data) { #if MICROPY_MODULE_FROZEN_STR - mp_lexer_t *lex = mp_find_frozen_str(str, len); + mp_lexer_t *lex = mp_lexer_frozen_str(str, len); if (lex != NULL) { *data = lex; return MP_FROZEN_STR; diff --git a/py/frozenmod.h b/py/frozenmod.h index f08cb5e321..4b125ff247 100644 --- a/py/frozenmod.h +++ b/py/frozenmod.h @@ -24,6 +24,8 @@ * THE SOFTWARE. */ +#include "py/lexer.h" + enum { MP_FROZEN_NONE, MP_FROZEN_STR, @@ -31,4 +33,5 @@ enum { }; int mp_find_frozen_module(const char *str, size_t len, void **data); +const char *mp_find_frozen_str(const char *str, size_t *len); mp_import_stat_t mp_frozen_stat(const char *str); diff --git a/py/modio.c b/py/modio.c index d5da0b1db7..6bf5129e6a 100644 --- a/py/modio.c +++ b/py/modio.c @@ -30,6 +30,8 @@ #include "py/runtime.h" #include "py/builtin.h" #include "py/stream.h" +#include "py/objstringio.h" +#include "py/frozenmod.h" #if MICROPY_PY_IO @@ -129,11 +131,38 @@ STATIC const mp_obj_type_t bufwriter_type = { }; #endif // MICROPY_PY_IO_BUFFEREDWRITER +#if MICROPY_MODULE_FROZEN_STR +STATIC mp_obj_t resource_stream(mp_obj_t package_in, mp_obj_t path_in) { + if (package_in != mp_const_none) { + mp_not_implemented(""); + } + + size_t len; + const char *path = mp_obj_str_get_data(path_in, &len); + const char *data = mp_find_frozen_str(path, &len); + if (data != NULL) { + mp_obj_stringio_t *o = m_new_obj(mp_obj_stringio_t); + o->base.type = &mp_type_bytesio; + o->vstr = m_new_obj(vstr_t); + vstr_init_fixed_buf(o->vstr, len + 1, (char*)data); + o->vstr->len = len; + o->pos = 0; + return MP_OBJ_FROM_PTR(o); + } + + return mp_builtin_open(1, &path_in, (mp_map_t*)&mp_const_empty_map); +} +MP_DEFINE_CONST_FUN_OBJ_2(resource_stream_obj, resource_stream); +#endif + STATIC const mp_rom_map_elem_t mp_module_io_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uio) }, // Note: mp_builtin_open_obj should be defined by port, it's not // part of the core. { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, + #if MICROPY_PY_IO_RESOURCE_STREAM + { MP_ROM_QSTR(MP_QSTR_resource_stream), MP_ROM_PTR(&resource_stream_obj) }, + #endif #if MICROPY_PY_IO_FILEIO { MP_ROM_QSTR(MP_QSTR_FileIO), MP_ROM_PTR(&mp_type_fileio) }, #if MICROPY_CPYTHON_COMPAT diff --git a/py/mpconfig.h b/py/mpconfig.h index 9122c99163..a61d431e5a 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -886,6 +886,13 @@ typedef double mp_float_t; #define MICROPY_PY_IO (1) #endif +// Whether to provide "uio.resource_stream()" function with +// the semantics of CPython's pkg_resources.resource_stream() +// (allows to access resources in frozen packages). +#ifndef MICROPY_PY_IO_RESOURCE_STREAM +#define MICROPY_PY_IO_RESOURCE_STREAM (0) +#endif + // Whether to provide "io.FileIO" class #ifndef MICROPY_PY_IO_FILEIO #define MICROPY_PY_IO_FILEIO (0) diff --git a/unix/mpconfigport.h b/unix/mpconfigport.h index d197f4503d..d8b8e9d1e8 100644 --- a/unix/mpconfigport.h +++ b/unix/mpconfigport.h @@ -101,6 +101,7 @@ #endif #define MICROPY_PY_CMATH (1) #define MICROPY_PY_IO_FILEIO (1) +#define MICROPY_PY_IO_RESOURCE_STREAM (1) #define MICROPY_PY_GC_COLLECT_RETVAL (1) #define MICROPY_MODULE_FROZEN_STR (1)