8000 RFC: Add zipfile support by dhylands · Pull Request #1797 · micropython/micropython · GitHub
[go: up one dir, main page]

Skip to content

RFC: Add zipfile support #1797

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
667 changes: 667 additions & 0 deletions extmod/moduzipfile.c

Large diffs are not rendered by default.

40 changes: 40 additions & 0 deletions extmod/moduzipfile.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Dave Hylands
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#ifndef __MICROPY_INCLUDED_EXTMOD_MODUZIPFILE_H__
#define __MICROPY_INCLUDED_EXTMOD_MODUZIPFILE_H__

#include <stdbool.h>
#include "py/lexer.h"

// C API for importing archives from zipfiles.

mp_import_stat_t zipfile_import_stat(const char *zip_archive_path);
mp_obj_t zipfile_import_open_archive(const char *zip_archive_path);

#endif // __MICROPY_INCLUDED_EXTMOD_MODUZIPFILE_H__


1 change: 1 addition & 0 deletions py/builtin.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ extern const mp_obj_dict_t mp_module_builtins_globals;

// extmod modules
extern const mp_obj_module_t mp_module_uctypes;
extern const mp_obj_module_t mp_module_uzipfile;
extern const mp_obj_module_t mp_module_uzlib;
extern const mp_obj_module_t mp_module_ujson;
extern const mp_obj_module_t mp_module_ure;
Expand Down
58 changes: 52 additions & 6 deletions py/builtinimport.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
#include "py/builtin.h"
#include "py/frozenmod.h"

#include "extmod/moduzipfile.h"

#if 0 // print debugging info
#define DEBUG_PRINT (1)
#define DEBUG_printf DEBUG_printf
Expand All @@ -60,22 +62,42 @@ bool mp_obj_is_package(mp_obj_t module) {
return dest[0] != MP_OBJ_NULL;
}

#if DEBUG_PRINT
STATIC const char *stat_str[] = { "doesn't exit", "dir", "file" };
#endif

STATIC mp_import_stat_t import_stat(const char *path) {
mp_import_stat_t stat;

#if MICROPY_PY_ZIPIMPORT
stat = zipfile_import_stat(path);
if (stat != MP_IMPORT_STAT_NO_EXIST) {
DEBUG_printf("zipimport import_stat('%s') returned %s\n", path, stat_str[stat]);
return stat;
}
#endif

stat = mp_import_stat(path);
DEBUG_printf("import_stat('%s') returned %s\n", path, stat_str[stat]);
return stat;
}

STATIC mp_import_stat_t stat_dir_or_file(vstr_t *path) {
mp_import_stat_t stat = mp_import_stat(vstr_null_terminated_str(path));
mp_import_stat_t stat = import_stat(vstr_null_terminated_str(path));
DEBUG_printf("stat %s: %d\n", vstr_str(path), stat);
if (sta 10000 t == MP_IMPORT_STAT_DIR) {
return stat;
}

vstr_add_str(path, ".py");
stat = mp_import_stat(vstr_null_terminated_str(path));
stat = import_stat(vstr_null_terminated_str(path));
if (stat == MP_IMPORT_STAT_FILE) {
return stat;
}

#if MICROPY_PERSISTENT_CODE_LOAD
vstr_ins_byte(path, path->len - 2, 'm');
stat = mp_import_stat(vstr_null_terminated_str(path));
stat = import_stat(vstr_null_terminated_str(path));
if (stat == MP_IMPORT_STAT_FILE) {
return stat;
}
Expand All @@ -85,6 +107,7 @@ STATIC mp_import_stat_t stat_dir_or_file(vstr_t *path) {
}

STATIC mp_import_stat_t find_file(const char *file_str, uint file_len, vstr_t *dest) {
DEBUG_printf("find_file(%s)\n", file_str);
#if MICROPY_PY_SYS
// extract the list of paths
mp_uint_t path_num;
Expand All @@ -103,6 +126,9 @@ STATIC mp_import_stat_t find_file(const char *file_str, uint file_len, vstr_t *d
vstr_reset(dest);
mp_uint_t p_len;
const char *p = mp_obj_str_get_data(path_items[i], &p_len);

DEBUG_printf("find_file: Checking sys.path entry '%s' file_str = '%s'\n", p, file_str);

if (p_len > 0) {
vstr_add_strn(dest, p, p_len);
vstr_add_char(dest, PATH_SEP_CHAR);
Expand Down Expand Up @@ -187,15 +213,35 @@ STATIC void do_load(mp_obj_t module_obj, vstr_t *file) {

#if MICROPY_PERSISTENT_CODE_LOAD
if (file_str[file->len - 3] == 'm') {
mp_raw_code_t *raw_code = mp_raw_code_load_file(file_str);
mp_raw_code_t *raw_code;

#if MICROPY_PY_ZIPIMPORT
mp_obj_t zip_file = zipfile_import_open_archive(file_str);
if (zip_file != mp_const_none && MP_OBJ_IS_OBJ(zip_file)) {
raw_code = mp_raw_code_load_file_obj(zip_file);
} else
#endif
{
raw_code = mp_raw_code_load_file(file_str);
}
do_execute_raw_code(module_obj, raw_code);
return;
}
#endif

#if MICROPY_ENABLE_COMPILER
{
mp_lexer_t *lex = mp_lexer_new_from_file(file_str);
mp_lexer_t *lex;

#if MICROPY_PY_ZIPIMPORT
mp_obj_t zip_file = zipfile_import_open_archive(file_str);
if (zip_file != mp_const_none && MP_OBJ_IS_OBJ(zip_file)) {
lex = mp_lexer_new_from_file_obj(qstr_from_str(file_str), zip_file);
} else
#endif
{
lex = mp_lexer_new_from_file(file_str);
}
do_load_from_lexer(module_obj, lex, file_str);
}
#else
Expand Down Expand Up @@ -434,7 +480,7 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) {
mp_store_attr(module_obj, MP_QSTR___path__, mp_obj_new_str(vstr_str(&path), vstr_len(&path), false));
vstr_add_char(&path, PATH_SEP_CHAR);
vstr_add_str(&path, "__init__.py");
if (mp_import_stat(vstr_null_terminated_str(&path)) != MP_IMPORT_STAT_FILE) {
if (import_stat(vstr_null_terminated_str(&path)) != MP_IMPORT_STAT_FILE) {
vstr_cut_tail_bytes(&path, sizeof("/__init__.py") - 1); // cut off /__init__.py
mp_warning("%s is imported as namespace package", vstr_str(&path));
} else {
Expand Down
53 changes: 52 additions & 1 deletion py/emitglue.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "py/emitglue.h"
#include "py/runtime0.h"
#include "py/bc.h"
#include "py/mpfile.h"

#if 0 // print debugging info
#define DEBUG_PRINT (1)
Expand Down Expand Up @@ -396,7 +397,57 @@ mp_raw_code_t *mp_raw_code_load_mem(const byte *buf, size_t len) {
// here we define mp_raw_code_load_file depending on the port
// TODO abstract this away properly

#if defined(__i386__) || defined(__x86_64__)
#if defined(MICROPY_PY_FILE_LIKE)
// Universal file reader

typedef struct _mp_lexer_file_buf_t {
mp_file_t *file;
byte buf[20];
mp_uint_t len;
mp_uint_t pos;
} mp_lexer_file_buf_t;

STATIC mp_uint_t mp_file_buf_next_byte(void *fb_in) {
mp_lexer_file_buf_t *fb = fb_in;
if (fb->pos >= fb->len) {
if (fb->len == 0) {
return (mp_uint_t)-1;
} else {
int n = mp_readinto(fb->file, fb->buf, sizeof(fb->buf));
if (n <= 0) {
fb->len = 0;
return (mp_uint_t)-1;
}
fb->len = n;
fb->pos = 0;
}
}
return fb->buf[fb->pos++];
}

STATIC mp_raw_code_t *mp_raw_code_load_mp_file(mp_file_t *file) {
mp_lexer_file_buf_t fb;
fb.file = file;
int n = mp_readinto(fb.file, fb.buf, sizeof(fb.buf));
fb.len = n;
fb.pos = 0;
mp_reader_t reader;
reader.data = &fb;
reader.read_byte = mp_file_buf_next_byte;
mp_raw_code_t *rc = mp_raw_code_load(&reader);
mp_close(fb.file);
return rc;
}

mp_raw_code_t *mp_raw_code_load_file(const char *filename) {
return mp_raw_code_load_mp_file(mp_open(filename, "rb"));
}

mp_raw_code_t *mp_raw_code_load_file_obj(mp_obj_t file_obj) {
return mp_raw_code_load_mp_file(mp_file_from_file_obj(file_obj));
}

#elif defined(__i386__) || defined(__x86_64__)
// unix file reader

#include <sys/stat.h>
Expand Down
1 change: 1 addition & 0 deletions py/emitglue.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ typedef struct _mp_reader_t {
mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader);
mp_raw_code_t *mp_raw_code_load_mem(const byte *buf, size_t len);
mp_raw_code_t *mp_raw_code_load_file(const char *filename);
mp_raw_code_t *mp_raw_code_load_file_obj(mp_obj_t file_obj);
#endif

#if MICROPY_PERSISTENT_CODE_SAVE
Expand Down
5 changes: 5 additions & 0 deletions py/lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include "py/mpconfig.h"
#include "py/qstr.h"
#include "py/obj.h"

/* lexer.h -- simple tokeniser for Micro Python
*
Expand Down Expand Up @@ -198,4 +199,8 @@ mp_lexer_t *mp_lexer_new_from_file(const char *filename);
mp_lexer_t *mp_lexer_new_from_fd(qstr filename, int fd, bool close_fd);
#endif

#if MICROPY_HELPER_LEXER_MPFILE
mp_lexer_t *mp_lexer_new_from_file_obj(qstr filename, mp_obj_t file_obj);
#endif

#endif // __MICROPY_INCLUDED_PY_LEXER_H__
82 changes: 82 additions & 0 deletions py/lexermpfile.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include "py/mpconfig.h"

#if MICROPY_HELPER_LEXER_MPFILE

#include <stdio.h>
#include <stdint.h>

#include "py/lexer.h"
#include "py/mpfile.h"

typedef struct _mp_lexer_file_buf_t {
mp_file_t *file;
byte buf[20];
mp_uint_t len;
mp_uint_t pos;
} mp_lexer_file_buf_t;

STATIC mp_uint_t mp_file_buf_next_byte(void *lexer_fb) {
mp_lexer_file_buf_t *fb = lexer_fb;
if (fb->pos >= fb->len) {
if (fb->len == 0) {
return MP_LEXER_EOF;
}

int n = mp_readinto(fb->file, fb->buf, sizeof(fb->buf));
if (n <= 0) {
fb->len = 0;
return MP_LEXER_EOF;
}
fb->len = n;
fb->pos = 0;
}
return fb->buf[fb->pos++];
}

STATIC void mp_file_buf_close(void *lexer_fb) {
mp_lexer_file_buf_t *fb = lexer_fb;
mp_close(fb->file);
fb->file = NULL;
m_del_obj(mp_lexer_file_buf_t, fb);
}

mp_lexer_t *mp_lexer_new_from_file_obj(qstr filename, mp_obj_t file_obj) {
mp_lexer_file_buf_t *fb = m_new_obj_maybe(mp_lexer_file_buf_t);
if (fb == NULL) {
return NULL;
}
fb->file = mp_file_from_file_obj(file_obj);
int n = mp_readinto(fb->file, fb->buf, sizeof(fb->buf));
fb->len = n;
fb->pos = 0;

return mp_lexer_new(filename, fb, mp_file_buf_next_byte, mp_file_buf_close);
}

#endif // MICROPY_HELPER_LEXER_MPFILE
18 changes: 18 additions & 0 deletions py/mpconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,11 @@
#define MICROPY_HELPER_LEXER_UNIX (0)
#endif

// Whether to include lexer helper function for mpfile (needed for zipimport)
#ifndef MICROPY_HELPER_LEXER_MPFILE
#define MICROPY_HELPER_LEXER_MPFILE (0)
#endif

// Long int implementation
#define MICROPY_LONGINT_IMPL_NONE (0)
#define MICROPY_LONGINT_IMPL_LONGLONG (1)
Expand Down Expand Up @@ -696,6 +701,11 @@ typedef double mp_float_t;
#define MICROPY_PY_GC_COLLECT_RETVAL (0)
#endif

// Whether to provide "file" or "file-like" API support
#ifndef MICROPY_PY_FILE_LIKE
#define MICROPY_PY_FILE_LIKE (0)
#endif

// Whether to provide "io" module
#ifndef MICROPY_PY_IO
#define MICROPY_PY_IO (1)
Expand Down Expand Up @@ -783,6 +793,14 @@ typedef double mp_float_t;
#define MICROPY_PY_UBINASCII (0)
#endif

#ifndef MICROPY_PY_UZIPFILE
#define MICROPY_PY_UZIPFILE (0)
#endif

#ifndef MICROPY_PY_ZIPIMPORT
#define MICROPY_PY_ZIPIMPORT (0)
#endif

#ifndef MICROPY_PY_URANDOM
#define MICROPY_PY_URANDOM (0)
#endif
Expand Down
Loading
0