Skip to content

Commit d7da2db

Browse files
committed
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.
1 parent 4c2fa83 commit d7da2db

File tree

5 files changed

+59
-6
lines changed

5 files changed

+59
-6
lines changed

py/frozenmod.c

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,23 +43,36 @@ extern const char mp_frozen_str_names[];
4343
extern const uint32_t mp_frozen_str_sizes[];
4444
extern const char mp_frozen_str_content[];
4545

46-
STATIC mp_lexer_t *mp_find_frozen_str(const char *str, size_t len) {
46+
// On input, *len contains size of name, on output - size of content
47+
const char *mp_find_frozen_str(const char *str, size_t *len) {
4748
const char *name = mp_frozen_str_names;
4849

4950
size_t offset = 0;
5051
for (int i = 0; *name != 0; i++) {
5152
size_t l = strlen(name);
52-
if (l == len && !memcmp(str, name, l)) {
53-
qstr source = qstr_from_strn(name, l);
54-
mp_lexer_t *lex = MICROPY_MODULE_FROZEN_LEXER(source, mp_frozen_str_content + offset, mp_frozen_str_sizes[i], 0);
55-
return lex;
53+
if (l == *len && !memcmp(str, name, l)) {
54+
*len = mp_frozen_str_sizes[i];
55+
return mp_frozen_str_content + offset;
5656
}
5757
name += l + 1;
5858
offset += mp_frozen_str_sizes[i] + 1;
5959
}
6060
return NULL;
6161
}
6262

63+
STATIC mp_lexer_t *mp_lexer_frozen_str(const char *str, size_t len) {
64+
size_t name_len = len;
65+
const char *content = mp_find_frozen_str(str, &len);
66+
67+
if (content == NULL) {
68+
return NULL;
69+
}
70+
71+
qstr source = qstr_from_strn(str, name_len);
72+
mp_lexer_t *lex = MICROPY_MODULE_FROZEN_LEXER(source, content, len, 0);
73+
return lex;
74+
}
75+
6376
#endif
6477

6578
#if MICROPY_MODULE_FROZEN_MPY
@@ -124,7 +137,7 @@ mp_import_stat_t mp_frozen_stat(const char *str) {
124137

125138
int mp_find_frozen_module(const char *str, size_t len, void **data) {
126139
#if MICROPY_MODULE_FROZEN_STR
127-
mp_lexer_t *lex = mp_find_frozen_str(str, len);
140+
mp_lexer_t *lex = mp_lexer_frozen_str(str, len);
128141
if (lex != NULL) {
129142
*data = lex;
130143
return MP_FROZEN_STR;

py/frozenmod.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@
2424
* THE SOFTWARE.
2525
*/
2626

27+
#include "py/lexer.h"
28+
2729
enum {
2830
MP_FROZEN_NONE,
2931
MP_FROZEN_STR,
3032
MP_FROZEN_MPY,
3133
};
3234

3335
int mp_find_frozen_module(const char *str, size_t len, void **data);
36+
const char *mp_find_frozen_str(const char *str, size_t *len);
3437
mp_import_stat_t mp_frozen_stat(const char *str);

py/modio.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include "py/runtime.h"
3131
#include "py/builtin.h"
3232
#include "py/stream.h"
33+
#include "py/objstringio.h"
34+
#include "py/frozenmod.h"
3335

3436
#if MICROPY_PY_IO
3537

@@ -129,11 +131,38 @@ STATIC const mp_obj_type_t bufwriter_type = {
129131
};
130132
#endif // MICROPY_PY_IO_BUFFEREDWRITER
131133

134+
#if MICROPY_MODULE_FROZEN_STR
135+
STATIC mp_obj_t resource_stream(mp_obj_t package_in, mp_obj_t path_in) {
136+
if (package_in != mp_const_none) {
137+
mp_not_implemented("");
138+
}
139+
140+
size_t len;
141+
const char *path = mp_obj_str_get_data(path_in, &len);
142+
const char *data = mp_find_frozen_str(path, &len);
143+
if (data != NULL) {
144+
mp_obj_stringio_t *o = m_new_obj(mp_obj_stringio_t);
145+
o->base.type = &mp_type_bytesio;
146+
o->vstr = m_new_obj(vstr_t);
147+
vstr_init_fixed_buf(o->vstr, len + 1, (char*)data);
148+
o->vstr->len = len;
149+
o->pos = 0;
150+
return MP_OBJ_FROM_PTR(o);
151+
}
152+
153+
return mp_builtin_open(1, &path_in, (mp_map_t*)&mp_const_empty_map);
154+
}
155+
MP_DEFINE_CONST_FUN_OBJ_2(resource_stream_obj, resource_stream);
156+
#endif
157+
132158
STATIC const mp_rom_map_elem_t mp_module_io_globals_table[] = {
133159
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uio) },
134160
// Note: mp_builtin_open_obj should be defined by port, it's not
135161
// part of the core.
136162
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) },
163+
#if MICROPY_PY_IO_RESOURCE_STREAM
164+
{ MP_ROM_QSTR(MP_QSTR_resource_stream), MP_ROM_PTR(&resource_stream_obj) },
165+
#endif
137166
#if MICROPY_PY_IO_FILEIO
138167
{ MP_ROM_QSTR(MP_QSTR_FileIO), MP_ROM_PTR(&mp_type_fileio) },
139168
#if MICROPY_CPYTHON_COMPAT

py/mpconfig.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,13 @@ typedef double mp_float_t;
886886
#define MICROPY_PY_IO (1)
887887
#endif
888888

889+
// Whether to provide "uio.resource_stream()" function with
890+
// the semantics of CPython's pkg_resources.resource_stream()
891+
// (allows to access resources in frozen packages).
892+
#ifndef MICROPY_PY_IO_RESOURCE_STREAM
893+
#define MICROPY_PY_IO_RESOURCE_STREAM (0)
894+
#endif
895+
889896
// Whether to provide "io.FileIO" class
890897
#ifndef MICROPY_PY_IO_FILEIO
891898
#define MICROPY_PY_IO_FILEIO (0)

unix/mpconfigport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
#endif
102102
#define MICROPY_PY_CMATH (1)
103103
#define MICROPY_PY_IO_FILEIO (1)
104+
#define MICROPY_PY_IO_RESOURCE_STREAM (1)
104105
#define MICROPY_PY_GC_COLLECT_RETVAL (1)
105106
#define MICROPY_MODULE_FROZEN_STR (1)
106107

0 commit comments

Comments
 (0)