From ddb56a0a843c21841d3226f93505ba4d7857e38a Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Wed, 8 Mar 2017 00:27:03 +0100 Subject: [PATCH 01/65] README: Explicitly mention "await" support, and formatting for keywords. --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 61ffce27ca..9e747e82c2 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,13 @@ WARNING: this project is in beta stage and is subject to changes of the code-base, including project-wide name changes and API changes. MicroPython implements the entire Python 3.4 syntax (including exceptions, -"with", "yield from", etc., and additionally "async" keyword from Python 3.5). -The following core datatypes are provided: str (including basic Unicode -support), bytes, bytearray, tuple, list, dict, set, frozenset, array.array, -collections.namedtuple, classes and instances. Builtin modules include sys, -time, and struct, etc. Select ports have support for _thread module -(multithreading). Note that only subset of Python 3.4 functionality -implemented for the data types and modules. +`with`, `yield from`, etc., and additionally `async`/`await` keywords from +Python 3.5). The following core datatypes are provided: `str` (including +basic Unicode support), `bytes`, `bytearray`, `tuple`, `list`, `dict`, `set`, +`frozenset`, `array.array`, `collections.namedtuple`, classes and instances. +Builtin modules include `sys`, `time`, and `struct`, etc. Select ports have +support for `_thread` module (multithreading). Note that only a subset of +Python 3 functionality is implemented for the data types and modules. See the repository www.github.com/micropython/pyboard for the MicroPython board (PyBoard), the officially supported reference electronic circuit board. From a64a0276b353cf13043d6892ac74d50c251719aa Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Wed, 8 Mar 2017 08:53:10 +0100 Subject: [PATCH 02/65] zephyr/modzephyr: Add a module for Zephyr-specific things. Mostly intended to ease experimentation, no particular plans for APIs so far (far less their stability), is_preempt_thread() provided is mostly an example. --- zephyr/Makefile | 1 + zephyr/modzephyr.c | 51 +++++++++++++++++++++++++++++++++++++++++++ zephyr/mpconfigport.h | 9 ++++++++ 3 files changed, 61 insertions(+) create mode 100644 zephyr/modzephyr.c diff --git a/zephyr/Makefile b/zephyr/Makefile index 3779765fb5..d020ab5ce0 100644 --- a/zephyr/Makefile +++ b/zephyr/Makefile @@ -42,6 +42,7 @@ INC += -I$(ZEPHYR_BASE)/net/ip/contiki/os SRC_C = main.c \ help.c \ modutime.c \ + modzephyr.c \ modmachine.c \ machine_pin.c \ uart_core.c \ diff --git a/zephyr/modzephyr.c b/zephyr/modzephyr.c new file mode 100644 index 0000000000..1edfa7f3b9 --- /dev/null +++ b/zephyr/modzephyr.c @@ -0,0 +1,51 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Linaro Limited + * + * 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_PY_ZEPHYR + +#include + +#include "py/runtime.h" + +STATIC mp_obj_t mod_is_preempt_thread(void) { + return mp_obj_new_bool(k_is_preempt_thread()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_is_preempt_thread_obj, mod_is_preempt_thread); + +STATIC const mp_rom_map_elem_t mp_module_time_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_zephy) }, + { MP_ROM_QSTR(MP_QSTR_is_preempt_thread), MP_ROM_PTR(&mod_is_preempt_thread_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_time_globals, mp_module_time_globals_table); + +const mp_obj_module_t mp_module_zephyr = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_time_globals, +}; + +#endif // MICROPY_PY_ZEPHYR diff --git a/zephyr/mpconfigport.h b/zephyr/mpconfigport.h index 2cd5ffcecc..e1e85df9cd 100644 --- a/zephyr/mpconfigport.h +++ b/zephyr/mpconfigport.h @@ -60,6 +60,7 @@ #define MICROPY_PY_STRUCT (0) #define MICROPY_PY_UTIME (1) #define MICROPY_PY_UTIME_MP_HAL (1) +#define MICROPY_PY_ZEPHYR (1) #define MICROPY_PY_SYS_MODULES (0) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) @@ -100,6 +101,7 @@ typedef long mp_off_t; extern const struct _mp_obj_module_t mp_module_machine; extern const struct _mp_obj_module_t mp_module_time; +extern const struct _mp_obj_module_t mp_module_zephyr; #if MICROPY_PY_UTIME #define MICROPY_PY_UTIME_DEF { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_time) }, @@ -107,9 +109,16 @@ extern const struct _mp_obj_module_t mp_module_time; #define MICROPY_PY_UTIME_DEF #endif +#if MICROPY_PY_ZEPHYR +#define MICROPY_PY_ZEPHYR_DEF { MP_ROM_QSTR(MP_QSTR_zephyr), MP_ROM_PTR(&mp_module_zephyr) }, +#else +#define MICROPY_PY_ZEPHYR_DEF +#endif + #define MICROPY_PORT_BUILTIN_MODULES \ { MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&mp_module_machine }, \ MICROPY_PY_UTIME_DEF \ + MICROPY_PY_ZEPHYR_DEF \ #define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \ { MP_OBJ_NEW_QSTR(MP_QSTR_time), MP_ROM_PTR(&mp_module_time) }, \ From 52b6764894755324db036731ae1d6ec77172419a Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 7 Mar 2017 17:05:08 +1100 Subject: [PATCH 03/65] py/nlrx64: Fixes to support Mac OS. Two independent fixes: - need to prefix symbols referenced from asm with underscore; - need to undo the C-function prelude. --- py/nlrx64.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/py/nlrx64.c b/py/nlrx64.c index 845aac67af..c23fd8fc6a 100644 --- a/py/nlrx64.c +++ b/py/nlrx64.c @@ -61,6 +61,9 @@ unsigned int nlr_push(nlr_buf_t *nlr) { #else __asm volatile ( + #if defined(__APPLE__) || defined(__MACH__) + "pop %rbp \n" // undo function's prelude + #endif "movq (%rsp), %rax \n" // load return %rip "movq %rax, 16(%rdi) \n" // store %rip into nlr_buf "movq %rbp, 24(%rdi) \n" // store %rbp into nlr_buf @@ -70,7 +73,11 @@ unsigned int nlr_push(nlr_buf_t *nlr) { "movq %r13, 56(%rdi) \n" // store %r13 into nlr_buf "movq %r14, 64(%rdi) \n" // store %r14 into nlr_buf "movq %r15, 72(%rdi) \n" // store %r15 into nlr_buf + #if defined(__APPLE__) || defined(__MACH__) + "jmp _nlr_push_tail \n" // do the rest in C + #else "jmp nlr_push_tail \n" // do the rest in C + #endif ); #endif From 983144404b6526c22e5c4224807ea4214e11e248 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 9 Mar 2017 00:07:19 +0100 Subject: [PATCH 04/65] tests/basic: Make various tests skippable. --- tests/basics/builtin_property.py | 6 ++++++ tests/basics/builtin_sorted.py | 7 +++++++ tests/basics/bytearray_construct.py | 6 ------ tests/basics/bytearray_construct_array.py | 11 +++++++++++ tests/basics/bytearray_construct_endian.py | 8 ++++++-- tests/basics/bytes_add.py | 7 ------- tests/basics/bytes_add_array.py | 12 ++++++++++++ tests/basics/bytes_add_endian.py | 8 ++++++-- tests/basics/bytes_compare2.py | 6 ------ tests/basics/bytes_compare_array.py | 10 ++++++++++ tests/basics/bytes_construct.py | 6 ------ tests/basics/bytes_construct_array.py | 11 +++++++++++ tests/basics/bytes_construct_endian.py | 7 ++++++- 13 files changed, 75 insertions(+), 30 deletions(-) create mode 100644 tests/basics/bytearray_construct_array.py create mode 100644 tests/basics/bytes_add_array.py create mode 100644 tests/basics/bytes_compare_array.py create mode 100644 tests/basics/bytes_construct_array.py diff --git a/tests/basics/builtin_property.py b/tests/basics/builtin_property.py index 403abd62f4..ff4ff073c4 100644 --- a/tests/basics/builtin_property.py +++ b/tests/basics/builtin_property.py @@ -1,4 +1,10 @@ # test builtin property +try: + property +except: + import sys + print("SKIP") + sys.exit() # create a property object explicitly property() diff --git a/tests/basics/builtin_sorted.py b/tests/basics/builtin_sorted.py index a4f71a15eb..68855b61ba 100644 --- a/tests/basics/builtin_sorted.py +++ b/tests/basics/builtin_sorted.py @@ -1,4 +1,11 @@ # test builtin sorted +try: + sorted + set +except: + import sys + print("SKIP") + sys.exit() print(sorted(set(range(100)))) print(sorted(set(range(100)), key=lambda x: x + 100*(x % 2))) diff --git a/tests/basics/bytearray_construct.py b/tests/basics/bytearray_construct.py index 1c45f6fcf5..9c8f3adaaa 100644 --- a/tests/basics/bytearray_construct.py +++ b/tests/basics/bytearray_construct.py @@ -1,12 +1,6 @@ # test construction of bytearray from different objects -from array import array - # bytes, tuple, list print(bytearray(b'123')) print(bytearray((1, 2))) print(bytearray([1, 2])) - -# arrays -print(bytearray(array('b', [1, 2]))) -print(bytearray(array('h', [0x101, 0x202]))) diff --git a/tests/basics/bytearray_construct_array.py b/tests/basics/bytearray_construct_array.py new file mode 100644 index 0000000000..6d45cafda3 --- /dev/null +++ b/tests/basics/bytearray_construct_array.py @@ -0,0 +1,11 @@ +# test construction of bytearray from different objects +try: + from array import array +except ImportError: + import sys + print("SKIP") + sys.exit() + +# arrays +print(bytearray(array('b', [1, 2]))) +print(bytearray(array('h', [0x101, 0x202]))) diff --git a/tests/basics/bytearray_construct_endian.py b/tests/basics/bytearray_construct_endian.py index dbd635c0c9..f68f9b89d1 100644 --- a/tests/basics/bytearray_construct_endian.py +++ b/tests/basics/bytearray_construct_endian.py @@ -1,6 +1,10 @@ # test construction of bytearray from different objects - -from array import array +try: + from array import array +except ImportError: + import sys + print("SKIP") + sys.exit() # arrays print(bytearray(array('h', [1, 2]))) diff --git a/tests/basics/bytes_add.py b/tests/basics/bytes_add.py index 7a887db231..5432d01e58 100644 --- a/tests/basics/bytes_add.py +++ b/tests/basics/bytes_add.py @@ -2,10 +2,3 @@ print(b"123" + b"456") print(b"123" + bytearray(2)) - -import array - -# should be byteorder-neutral -print(b"123" + array.array('h', [0x1515])) - -print(b"\x01\x02" + array.array('b', [1, 2])) diff --git a/tests/basics/bytes_add_array.py b/tests/basics/bytes_add_array.py new file mode 100644 index 0000000000..2b8cbccef5 --- /dev/null +++ b/tests/basics/bytes_add_array.py @@ -0,0 +1,12 @@ +# test bytes + other +try: + import array +except ImportError: + import sys + print("SKIP") + sys.exit() + +# should be byteorder-neutral +print(b"123" + array.array('h', [0x1515])) + +print(b"\x01\x02" + array.array('b', [1, 2])) diff --git a/tests/basics/bytes_add_endian.py b/tests/basics/bytes_add_endian.py index 5471280d93..1bbd0f2c3a 100644 --- a/tests/basics/bytes_add_endian.py +++ b/tests/basics/bytes_add_endian.py @@ -1,5 +1,9 @@ # test bytes + other - -import array +try: + import array +except ImportError: + import sys + print("SKIP") + sys.exit() print(b"123" + array.array('i', [1])) diff --git a/tests/basics/bytes_compare2.py b/tests/basics/bytes_compare2.py index 8959da3ae7..4d5de21d21 100644 --- a/tests/basics/bytes_compare2.py +++ b/tests/basics/bytes_compare2.py @@ -3,9 +3,3 @@ print(b"123" == bytearray(b"123")) print(b'123' < bytearray(b"124")) print(b'123' > bytearray(b"122")) print(bytearray(b"23") in b"1234") - -import array - -print(array.array('b', [1, 2]) in b'\x01\x02\x03') -# CPython gives False here -#print(b"\x01\x02\x03" == array.array("B", [1, 2, 3])) diff --git a/tests/basics/bytes_compare_array.py b/tests/basics/bytes_compare_array.py new file mode 100644 index 0000000000..ad41d1d375 --- /dev/null +++ b/tests/basics/bytes_compare_array.py @@ -0,0 +1,10 @@ +try: + import array +except ImportError: + import sys + print("SKIP") + sys.exit() + +print(array.array('b', [1, 2]) in b'\x01\x02\x03') +# CPython gives False here +#print(b"\x01\x02\x03" == array.array("B", [1, 2, 3])) diff --git a/tests/basics/bytes_construct.py b/tests/basics/bytes_construct.py index 1647387675..0d638c08f2 100644 --- a/tests/basics/bytes_construct.py +++ b/tests/basics/bytes_construct.py @@ -1,16 +1,10 @@ # test construction of bytes from different objects -from array import array - # tuple, list, bytearray print(bytes((1, 2))) print(bytes([1, 2])) print(bytes(bytearray(4))) -# arrays -print(bytes(array('b', [1, 2]))) -print(bytes(array('h', [0x101, 0x202]))) - # constructor value out of range try: bytes([-1]) diff --git a/tests/basics/bytes_construct_array.py b/tests/basics/bytes_construct_array.py new file mode 100644 index 0000000000..72c2d0c585 --- /dev/null +++ b/tests/basics/bytes_construct_array.py @@ -0,0 +1,11 @@ +# test construction of bytes from different objects +try: + from array import array +except ImportError: + import sys + print("SKIP") + sys.exit() + +# arrays +print(bytes(array('b', [1, 2]))) +print(bytes(array('h', [0x101, 0x202]))) diff --git a/tests/basics/bytes_construct_endian.py b/tests/basics/bytes_construct_endian.py index 1912f63a4d..77e0eaaa56 100644 --- a/tests/basics/bytes_construct_endian.py +++ b/tests/basics/bytes_construct_endian.py @@ -1,6 +1,11 @@ # test construction of bytes from different objects -from array import array +try: + from array import array +except ImportError: + import sys + print("SKIP") + sys.exit() # arrays print(bytes(array('h', [1, 2]))) From a0cbc108ba9dbdd4cc223486561f3e6a347e2d0a Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 9 Mar 2017 00:11:05 +0100 Subject: [PATCH 05/65] tests/float: Make various tests skippable. --- tests/float/array_construct.py | 7 ++++++- tests/float/builtin_float_minmax.py | 7 +++++++ tests/float/float_array.py | 7 ++++++- tests/float/float_struct.py | 12 ++++++++---- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/tests/float/array_construct.py b/tests/float/array_construct.py index a25cc72c80..7e01fd4768 100644 --- a/tests/float/array_construct.py +++ b/tests/float/array_construct.py @@ -1,6 +1,11 @@ # test construction of array from array with float type -from array import array +try: + from array import array +except ImportError: + import sys + print("SKIP") + sys.exit() print(array('f', array('h', [1, 2]))) print(array('d', array('f', [1, 2]))) diff --git a/tests/float/builtin_float_minmax.py b/tests/float/builtin_float_minmax.py index ce45a768a5..42cfa63822 100644 --- a/tests/float/builtin_float_minmax.py +++ b/tests/float/builtin_float_minmax.py @@ -1,4 +1,11 @@ # test builtin min and max functions with float args +try: + min + max +except: + import sys + print("SKIP") + sys.exit() print(min(0,1.0)) print(min(1.0,0)) diff --git a/tests/float/float_array.py b/tests/float/float_array.py index 033877db3c..8bc9634449 100644 --- a/tests/float/float_array.py +++ b/tests/float/float_array.py @@ -1,4 +1,9 @@ -from array import array +try: + from array import array +except ImportError: + import sys + print("SKIP") + sys.exit() def test(a): print(a) diff --git a/tests/float/float_struct.py b/tests/float/float_struct.py index e55890a2cd..a36ccce38b 100644 --- a/tests/float/float_struct.py +++ b/tests/float/float_struct.py @@ -1,9 +1,13 @@ # test struct package with floats - try: - import ustruct as struct -except: - import struct + try: + import ustruct as struct + except: + import struct +except ImportError: + import sys + print("SKIP") + sys.exit() i = 1. + 1/2 # TODO: it looks like '=' format modifier is not yet supported From 38f063ea72632e395ea59b644552bb98c962393f Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 9 Mar 2017 13:42:34 +1100 Subject: [PATCH 06/65] tests/extmod: Add very basic feature test for ussl module. This test just tests that the basic functions/methods can be called with the appropriate arguments. There is no real test of underlying functionality. Thanks to @hosaka for the initial implementation of this test. --- tests/extmod/ussl_basic.py | 52 ++++++++++++++++++++++++++++++++++ tests/extmod/ussl_basic.py.exp | 8 ++++++ 2 files changed, 60 insertions(+) create mode 100644 tests/extmod/ussl_basic.py create mode 100644 tests/extmod/ussl_basic.py.exp diff --git a/tests/extmod/ussl_basic.py b/tests/extmod/ussl_basic.py new file mode 100644 index 0000000000..e9d435bca8 --- /dev/null +++ b/tests/extmod/ussl_basic.py @@ -0,0 +1,52 @@ +# very basic test of ssl module, just to test the methods exist + +try: + import uio as io + import ussl as ssl +except ImportError: + print("SKIP") + import sys + sys.exit() + +# create in client mode +try: + ss = ssl.wrap_socket(io.BytesIO()) +except OSError as er: + print('wrap_socket:', repr(er)) + +# create in server mode (can use this object for further tests) +socket = io.BytesIO() +ss = ssl.wrap_socket(socket, server_side=1) + +# print +print(repr(ss)[:12]) + +# setblocking +try: + ss.setblocking(False) +except NotImplementedError: + print('setblocking: NotImplementedError') +ss.setblocking(True) + +# write +print(ss.write(b'aaaa')) + +# read (underlying socket has no data) +print(ss.read(8)) + +# read (underlying socket has data, but it's bad data) +socket.write(b'aaaaaaaaaaaaaaaa') +socket.seek(0) +try: + ss.read(8) +except OSError as er: + print('read:', repr(er)) + +# close +ss.close() + +# write on closed socket +try: + ss.write(b'aaaa') +except OSError as er: + print('write:', repr(er)) diff --git a/tests/extmod/ussl_basic.py.exp b/tests/extmod/ussl_basic.py.exp new file mode 100644 index 0000000000..b4dd038606 --- /dev/null +++ b/tests/extmod/ussl_basic.py.exp @@ -0,0 +1,8 @@ +ssl_handshake_status: -256 +wrap_socket: OSError(5,) +<_SSLSocket +setblocking: NotImplementedError +4 +b'' +read: OSError(-261,) +write: OSError(-256,) From ce0b5e078b376dadcc33226647c91c73d6c73600 Mon Sep 17 00:00:00 2001 From: Alex March Date: Tue, 1 Nov 2016 16:43:18 +0000 Subject: [PATCH 07/65] tests/extmod: Add websocket tests. These short unit tests test the base uPy methods as well as parts of the websocket protocol, as implemented by uPy. @dpgeorge converted the original socket based tests by @hosaka to ones that only require io.BytesIO. --- tests/extmod/websocket.py | 61 +++++++++++++++++++++++++++++++++++ tests/extmod/websocket.py.exp | 14 ++++++++ 2 files changed, 75 insertions(+) create mode 100644 tests/extmod/websocket.py create mode 100644 tests/extmod/websocket.py.exp diff --git a/tests/extmod/websocket.py b/tests/extmod/websocket.py new file mode 100644 index 0000000000..770836c8eb --- /dev/null +++ b/tests/extmod/websocket.py @@ -0,0 +1,61 @@ +try: + import uio + import uerrno + import websocket +except ImportError: + import sys + print("SKIP") + sys.exit() + +# put raw data in the stream and do a websocket read +def ws_read(msg, sz): + ws = websocket.websocket(uio.BytesIO(msg)) + return ws.read(sz) + +# do a websocket write and then return the raw data from the stream +def ws_write(msg, sz): + s = uio.BytesIO() + ws = websocket.websocket(s) + ws.write(msg) + s.seek(0) + return s.read(sz) + +# basic frame +print(ws_read(b"\x81\x04ping", 4)) +print(ws_read(b"\x80\x04ping", 4)) # FRAME_CONT +print(ws_write(b"pong", 6)) + +# split frames are not supported +# print(ws_read(b"\x01\x04ping", 4)) + +# extended payloads +print(ws_read(b'\x81~\x00\x80' + b'ping' * 32, 128)) +print(ws_write(b"pong" * 32, 132)) + +# mask (returned data will be 'mask' ^ 'mask') +print(ws_read(b"\x81\x84maskmask", 4)) + +# close control frame +s = uio.BytesIO(b'\x88\x00') # FRAME_CLOSE +ws = websocket.websocket(s) +print(ws.read(1)) +s.seek(2) +print(s.read(4)) + +# misc control frames +print(ws_read(b"\x89\x00\x81\x04ping", 4)) # FRAME_PING +print(ws_read(b"\x8a\x00\x81\x04pong", 4)) # FRAME_PONG + +# close method +ws = websocket.websocket(uio.BytesIO()) +ws.close() + +# ioctl +ws = websocket.websocket(uio.BytesIO()) +print(ws.ioctl(8)) # GET_DATA_OPTS +print(ws.ioctl(9, 2)) # SET_DATA_OPTS +print(ws.ioctl(9)) +try: + ws.ioctl(-1) +except OSError as e: + print("ioctl: EINVAL:", e.args[0] == uerrno.EINVAL) diff --git a/tests/extmod/websocket.py.exp b/tests/extmod/websocket.py.exp new file mode 100644 index 0000000000..2d7657b535 --- /dev/null +++ b/tests/extmod/websocket.py.exp @@ -0,0 +1,14 @@ +b'ping' +b'ping' +b'\x81\x04pong' +b'pingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingping' +b'\x81~\x00\x80pongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpong' +b'\x00\x00\x00\x00' +b'' +b'\x81\x02\x88\x00' +b'ping' +b'pong' +0 +1 +2 +ioctl: EINVAL: True From ce63a95a85cf323d608329daa264e15fc21aa258 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 9 Mar 2017 08:31:35 +0100 Subject: [PATCH 08/65] tests/dict_fromkeys: Split out skippable part. --- tests/basics/dict_fromkeys.py | 5 ----- tests/basics/dict_fromkeys2.py | 11 +++++++++++ 2 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 tests/basics/dict_fromkeys2.py diff --git a/tests/basics/dict_fromkeys.py b/tests/basics/dict_fromkeys.py index 118d0ffd95..7b11319a23 100644 --- a/tests/basics/dict_fromkeys.py +++ b/tests/basics/dict_fromkeys.py @@ -7,8 +7,3 @@ d = dict.fromkeys([1, 2, 3, 4], 42) l = list(d.values()) l.sort() print(l) - -# argument to fromkeys has no __len__ -d = dict.fromkeys(reversed(range(1))) -#d = dict.fromkeys((x for x in range(1))) -print(d) diff --git a/tests/basics/dict_fromkeys2.py b/tests/basics/dict_fromkeys2.py new file mode 100644 index 0000000000..7ea0cc5b36 --- /dev/null +++ b/tests/basics/dict_fromkeys2.py @@ -0,0 +1,11 @@ +try: + reversed +except: + import sys + print("SKIP") + sys.exit() + +# argument to fromkeys has no __len__ +d = dict.fromkeys(reversed(range(1))) +#d = dict.fromkeys((x for x in range(1))) +print(d) From e8d4527996261f69e0d6898f963f0be9647cdbf4 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 9 Mar 2017 10:17:30 +0100 Subject: [PATCH 09/65] zephyr/modzephyr: Fix typo in identifier. --- zephyr/modzephyr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zephyr/modzephyr.c b/zephyr/modzephyr.c index 1edfa7f3b9..4bac5c970f 100644 --- a/zephyr/modzephyr.c +++ b/zephyr/modzephyr.c @@ -37,7 +37,7 @@ STATIC mp_obj_t mod_is_preempt_thread(void) { STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_is_preempt_thread_obj, mod_is_preempt_thread); STATIC const mp_rom_map_elem_t mp_module_time_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_zephy) }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_zephyr) }, { MP_ROM_QSTR(MP_QSTR_is_preempt_thread), MP_ROM_PTR(&mod_is_preempt_thread_obj) }, }; From 1a71d30fb8fad17ea44de635d52e858f066ef308 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 9 Mar 2017 10:26:31 +0100 Subject: [PATCH 10/65] tests/micropython: Make uio-using tests skippable. --- tests/micropython/heapalloc_bytesio.py | 8 +++++++- tests/micropython/heapalloc_traceback.py | 7 ++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/micropython/heapalloc_bytesio.py b/tests/micropython/heapalloc_bytesio.py index 3116328518..2a8d50abe3 100644 --- a/tests/micropython/heapalloc_bytesio.py +++ b/tests/micropython/heapalloc_bytesio.py @@ -1,4 +1,10 @@ -import uio +try: + import uio +except ImportError: + import sys + print("SKIP") + sys.exit() + import micropython data = b"1234" * 16 diff --git a/tests/micropython/heapalloc_traceback.py b/tests/micropython/heapalloc_traceback.py index 808df0225c..b3795293f3 100644 --- a/tests/micropython/heapalloc_traceback.py +++ b/tests/micropython/heapalloc_traceback.py @@ -2,7 +2,12 @@ import micropython import sys -import uio +try: + import uio +except ImportError: + import sys + print("SKIP") + sys.exit() # preallocate exception instance with some room for a traceback global_exc = StopIteration() From 53018d5ad23c642a36efb67a862db5143080299d Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 9 Mar 2017 12:51:45 +0100 Subject: [PATCH 11/65] tests/micropython/heapalloc_traceback: Fix backtrace line # after refactor. --- tests/micropython/heapalloc_traceback.py.exp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/micropython/heapalloc_traceback.py.exp b/tests/micropython/heapalloc_traceback.py.exp index 9ba10948d5..facd0af137 100644 --- a/tests/micropython/heapalloc_traceback.py.exp +++ b/tests/micropython/heapalloc_traceback.py.exp @@ -1,5 +1,5 @@ StopIteration Traceback (most recent call last): - File , line 18, in test + File , line 23, in test StopIteration: From bc5bffbf65dee144744449a5ec60a3627830caa1 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 9 Mar 2017 23:21:37 +0100 Subject: [PATCH 12/65] tests/micropython/opt_level: Clarify the expected output for opt_level == 3. --- tests/micropython/opt_level.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/micropython/opt_level.py b/tests/micropython/opt_level.py index e067ec6512..5a10047f04 100644 --- a/tests/micropython/opt_level.py +++ b/tests/micropython/opt_level.py @@ -14,5 +14,6 @@ exec('print(__debug__)') exec('assert 0') # check that level 3 doesn't store line numbers +# the expected output is that any line is printed as "line 1" micropython.opt_level(3) exec('try:\n xyz\nexcept NameError as er:\n import sys\n sys.print_exception(er)') From 776883cb80be675fad1fee1ffae5dcdb75f14f64 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 10 Mar 2017 00:22:53 +0100 Subject: [PATCH 13/65] py/objint_longlong: Implement mp_obj_int_from_bytes_impl(). This makes int.from_bytes() work for MICROPY_LONGINT_IMPL_LONGLONG. --- py/objint_longlong.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/py/objint_longlong.c b/py/objint_longlong.c index b798c91cdf..4ab49f337d 100644 --- a/py/objint_longlong.c +++ b/py/objint_longlong.c @@ -54,7 +54,17 @@ const mp_obj_int_t mp_maxsize_obj = {{&mp_type_int}, MP_SSIZE_MAX}; #endif mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf) { - mp_not_implemented(""); + int delta = 1; + if (!big_endian) { + buf += len - 1; + delta = -1; + } + + mp_longint_impl_t value = 0; + for (; len--; buf += delta) { + value = (value << 8) | *buf; + } + return mp_obj_new_int_from_ll(value); } void mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf) { From 854bb322bf9e20f1c5afe5e8a796ebeca238fa95 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 10 Mar 2017 02:11:43 +0100 Subject: [PATCH 14/65] tests/feature_check/int_big: Rework "big int" detection. MICROPY_LONGINT_IMPL_LONGLONG doesn't have overflow detection, so just parsing a large number won't give an error, we need to print it out to check that the whole number was parsed. --- tests/feature_check/int_big.py | 2 +- tests/feature_check/int_big.py.exp | 1 + tests/run-tests | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/feature_check/int_big.py b/tests/feature_check/int_big.py index 8b82814aee..f30285a98f 100644 --- a/tests/feature_check/int_big.py +++ b/tests/feature_check/int_big.py @@ -1,2 +1,2 @@ # Check whether arbitrary-precision integers (MPZ) are supported -1000000000000000000000000000000000000000000000 +print(1000000000000000000000000000000000000000000000) diff --git a/tests/feature_check/int_big.py.exp b/tests/feature_check/int_big.py.exp index e69de29bb2..9dfe3354d5 100644 --- a/tests/feature_check/int_big.py.exp +++ b/tests/feature_check/int_big.py.exp @@ -0,0 +1 @@ +1000000000000000000000000000000000000000000000 diff --git a/tests/run-tests b/tests/run-tests index bacaebea81..6a78c27649 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -209,7 +209,7 @@ def run_tests(pyb, tests, args): # Check if arbitrary-precision integers are supported, and skip such tests if it's not native = run_micropython(pyb, args, 'feature_check/int_big.py') - if native == b'CRASH': + if native != b'1000000000000000000000000000000000000000000000\n': skip_int_big = True # Check if set type (and set literals) is supported, and skip such tests if it's not From c9705cff6860c9d77ea009ce6a6c0e9e946a0b21 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 10 Mar 2017 02:22:56 +0100 Subject: [PATCH 15/65] tests/basics/fun_error: Split out skippable test. --- tests/basics/fun_error.py | 3 --- tests/basics/fun_error2.py | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 tests/basics/fun_error2.py diff --git a/tests/basics/fun_error.py b/tests/basics/fun_error.py index 305b249111..367fe0b7fa 100644 --- a/tests/basics/fun_error.py +++ b/tests/basics/fun_error.py @@ -27,8 +27,5 @@ test_exc("[].sort(1)", TypeError) # function with keyword args given extra keyword args test_exc("[].sort(noexist=1)", TypeError) -# function with keyword args not given a specific keyword arg -test_exc("enumerate()", TypeError) - # kw given for positional, but a different positional is missing test_exc("def f(x, y): pass\nf(x=1)", TypeError) diff --git a/tests/basics/fun_error2.py b/tests/basics/fun_error2.py new file mode 100644 index 0000000000..c4d2c0b64d --- /dev/null +++ b/tests/basics/fun_error2.py @@ -0,0 +1,19 @@ +# test errors from bad function calls +try: + enumerate +except: + print("SKIP") + import sys + sys.exit() + +def test_exc(code, exc): + try: + exec(code) + print("no exception") + except exc: + print("right exception") + except: + print("wrong exception") + +# function with keyword args not given a specific keyword arg +test_exc("enumerate()", TypeError) From 4351d16e62183720464dc9b7c910b87a36aa270b Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 10 Mar 2017 14:58:26 +1100 Subject: [PATCH 16/65] stmhal/mphalport: Get ticks_cpu() working on F7 MCUs. --- stmhal/mphalport.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stmhal/mphalport.c b/stmhal/mphalport.c index 64c1164cc8..dff781ff29 100644 --- a/stmhal/mphalport.c +++ b/stmhal/mphalport.c @@ -72,6 +72,10 @@ void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { void mp_hal_ticks_cpu_enable(void) { if (!mp_hal_ticks_cpu_enabled) { CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + #if defined(__CORTEX_M) && __CORTEX_M == 7 + // on Cortex-M7 we must unlock the DWT before writing to its registers + DWT->LAR = 0xc5acce55; + #endif DWT->CYCCNT = 0; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; mp_hal_ticks_cpu_enabled = true; From f07a56fa3b10b767960029c82bb2ab7af29e7a04 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 10 Mar 2017 15:05:08 +1100 Subject: [PATCH 17/65] tests/extmod: Rename websocket test to websocket_basic. This is so that the filename of the test doesn't clash with the module name itself (being "websocket"), and lead to potential problems executing the test. --- tests/extmod/{websocket.py => websocket_basic.py} | 0 tests/extmod/{websocket.py.exp => websocket_basic.py.exp} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/extmod/{websocket.py => websocket_basic.py} (100%) rename tests/extmod/{websocket.py.exp => websocket_basic.py.exp} (100%) diff --git a/tests/extmod/websocket.py b/tests/extmod/websocket_basic.py similarity index 100% rename from tests/extmod/websocket.py rename to tests/extmod/websocket_basic.py diff --git a/tests/extmod/websocket.py.exp b/tests/extmod/websocket_basic.py.exp similarity index 100% rename from tests/extmod/websocket.py.exp rename to tests/extmod/websocket_basic.py.exp From 8236d18338f6d8db25dcc5e81b176b006d56f39d Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 10 Mar 2017 19:02:20 +1100 Subject: [PATCH 18/65] stmhal/main: Allocate flash's VFS struct on the heap to trace root ptrs. --- stmhal/main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/stmhal/main.c b/stmhal/main.c index e07f6cf8c5..3c9906ad26 100644 --- a/stmhal/main.c +++ b/stmhal/main.c @@ -65,7 +65,6 @@ void SystemClock_Config(void); pyb_thread_t pyb_thread_main; fs_user_mount_t fs_user_mount_flash; -mp_vfs_mount_t mp_vfs_mount_flash; void flash_error(int n) { for (int i = 0; i < n; i++) { @@ -219,12 +218,17 @@ MP_NOINLINE STATIC bool init_flash_fs(uint reset_mode) { } else if (res == FR_OK) { // mount sucessful } else { + fail: printf("PYB: can't mount flash\n"); return false; } // mount the flash device (there should be no other devices mounted at this point) - mp_vfs_mount_t *vfs = &mp_vfs_mount_flash; + // we allocate this structure on the heap because vfs->next is a root pointer + mp_vfs_mount_t *vfs = m_new_obj_maybe(mp_vfs_mount_t); + if (vfs == NULL) { + goto fail; + } vfs->str = "/flash"; vfs->len = 6; vfs->obj = MP_OBJ_FROM_PTR(vfs_fat); From 70201f40386c42fec8bd20af06fe31a69f3af7db Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 10 Mar 2017 19:09:19 +1100 Subject: [PATCH 19/65] cc3200/mptask: Allocate flash VFS struct on the heap to trace root ptrs. --- cc3200/mptask.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cc3200/mptask.c b/cc3200/mptask.c index c7c1832edb..41264fbd06 100644 --- a/cc3200/mptask.c +++ b/cc3200/mptask.c @@ -98,7 +98,6 @@ OsiTaskHandle svTaskHandle; DECLARE PRIVATE DATA ******************************************************************************/ static fs_user_mount_t *sflash_vfs_fat; -static mp_vfs_mount_t sflash_vfs_mount; static const char fresh_main_py[] = "# main.py -- put your code here!\r\n"; static const char fresh_boot_py[] = "# boot.py -- run on boot-up\r\n" @@ -328,11 +327,16 @@ STATIC void mptask_init_sflash_filesystem (void) { mptask_create_main_py(); } } else { + fail: __fatal_error("failed to create /flash"); } // mount the flash device (there should be no other devices mounted at this point) - mp_vfs_mount_t *vfs = &sflash_vfs_mount; + // we allocate this structure on the heap because vfs->next is a root pointer + mp_vfs_mount_t *vfs = m_new_obj_maybe(mp_vfs_mount_t); + if (vfs == NULL) { + goto fail; + } vfs->str = "/flash"; vfs->len = 6; vfs->obj = MP_OBJ_FROM_PTR(vfs_fat); From 12d0731b91d8e58ba20ec28adf2d6c1aa995d74a Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 10 Mar 2017 19:09:42 +1100 Subject: [PATCH 20/65] extmod/vfs_fat: Remove obsolete and unused str/len members. --- cc3200/mptask.c | 2 -- extmod/vfs_fat.c | 2 -- extmod/vfs_fat.h | 2 -- stmhal/main.c | 4 ---- 4 files changed, 10 deletions(-) diff --git a/cc3200/mptask.c b/cc3200/mptask.c index 41264fbd06..3c49a56035 100644 --- a/cc3200/mptask.c +++ b/cc3200/mptask.c @@ -302,8 +302,6 @@ STATIC void mptask_init_sflash_filesystem (void) { // Initialise the local flash filesystem. // init the vfs object fs_user_mount_t *vfs_fat = sflash_vfs_fat; - vfs_fat->str = NULL; - vfs_fat->len = 0; vfs_fat->flags = 0; pyb_flash_init_vfs(vfs_fat); diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index 82dd312b8f..8cd5a4674a 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -55,8 +55,6 @@ STATIC mp_obj_t fat_vfs_make_new(const mp_obj_type_t *type, size_t n_args, size_ fs_user_mount_t *vfs = m_new_obj(fs_user_mount_t); vfs->base.type = type; vfs->flags = FSUSER_FREE_OBJ; - vfs->str = NULL; - vfs->len = 0; vfs->fatfs.drv = vfs; // load block protocol methods diff --git a/extmod/vfs_fat.h b/extmod/vfs_fat.h index a5e3c604bf..7eb865254f 100644 --- a/extmod/vfs_fat.h +++ b/extmod/vfs_fat.h @@ -36,8 +36,6 @@ typedef struct _fs_user_mount_t { mp_obj_base_t base; - const char *str; - uint16_t len; // length of str uint16_t flags; mp_obj_t readblocks[4]; mp_obj_t writeblocks[4]; diff --git a/stmhal/main.c b/stmhal/main.c index 3c9906ad26..8d076a08b1 100644 --- a/stmhal/main.c +++ b/stmhal/main.c @@ -167,8 +167,6 @@ static const char fresh_readme_txt[] = MP_NOINLINE STATIC bool init_flash_fs(uint reset_mode) { // init the vfs object fs_user_mount_t *vfs_fat = &fs_user_mount_flash; - vfs_fat->str = NULL; - vfs_fat->len = 0; vfs_fat->flags = 0; pyb_flash_init_vfs(vfs_fat); @@ -274,8 +272,6 @@ STATIC bool init_sdcard_fs(bool first_soft_reset) { if (vfs == NULL || vfs_fat == NULL) { break; } - vfs_fat->str = NULL; - vfs_fat->len = 0; vfs_fat->flags = FSUSER_FREE_OBJ; sdcard_init_vfs(vfs_fat, part_num); From 3e321f1724b4e2deae2a7750c789c6423f5201c2 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 11 Mar 2017 01:16:26 +0100 Subject: [PATCH 21/65] tests/misc/: Make few tests skippable. --- tests/misc/non_compliant.py | 9 +++++++-- tests/misc/print_exception.py | 13 +++++++++---- tests/misc/recursive_data.py | 7 ++++++- tests/misc/recursive_iternext.py | 10 ++++++++++ 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/tests/misc/non_compliant.py b/tests/misc/non_compliant.py index 8e5d586a9e..31074ab016 100644 --- a/tests/misc/non_compliant.py +++ b/tests/misc/non_compliant.py @@ -1,7 +1,12 @@ # tests for things that are not implemented, or have non-compliant behaviour -import array -import ustruct +try: + import array + import ustruct +except ImportError: + import sys + print("SKIP") + sys.exit() # when super can't find self try: diff --git a/tests/misc/print_exception.py b/tests/misc/print_exception.py index 30a732106c..b833a79816 100644 --- a/tests/misc/print_exception.py +++ b/tests/misc/print_exception.py @@ -1,8 +1,13 @@ -try: - import uio as io -except ImportError: - import io import sys +try: + try: + import uio as io + except ImportError: + import io +except ImportError: + print("SKIP") + sys.exit() + if hasattr(sys, 'print_exception'): print_exception = sys.print_exception else: diff --git a/tests/misc/recursive_data.py b/tests/misc/recursive_data.py index 0de93acb89..3830189453 100644 --- a/tests/misc/recursive_data.py +++ b/tests/misc/recursive_data.py @@ -1,5 +1,10 @@ # This tests that printing recursive data structure doesn't lead to segfault. -import uio as io +try: + import uio as io +except ImportError: + import sys + print("SKIP") + sys.exit() l = [1, 2, 3, None] l[-1] = l diff --git a/tests/misc/recursive_iternext.py b/tests/misc/recursive_iternext.py index 025fa425b5..d90f177168 100644 --- a/tests/misc/recursive_iternext.py +++ b/tests/misc/recursive_iternext.py @@ -1,4 +1,14 @@ # This tests that recursion with iternext doesn't lead to segfault. +try: + enumerate + filter + map + max + zip +except: + import sys + print("SKIP") + sys.exit() # We need to pick an N that is large enough to hit the recursion # limit, but not too large that we run out of heap memory. From 736a8a8ac7abfeab5edb8dfd73ecf0b2c0282e61 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 12 Mar 2017 22:28:45 +0300 Subject: [PATCH 22/65] zephyr: Make sure that generated prj.conf is updated only on content changes. This is a typical problem with make: we want to trigger rebuilds only if file actually changed, not if its timestamp changed. In this case, it's aggravated by the fact that prj.conf depends on the value of BOARD variable, so we need to do some tricks anyway. We still don't try to detect if just BOARD changed, just try to generate new prj.conf.tmp every time (quick), but do actual replacement of prj.conf only if its content changed. --- zephyr/Makefile | 3 +-- zephyr/makeprj.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 zephyr/makeprj.py diff --git a/zephyr/Makefile b/zephyr/Makefile index d020ab5ce0..7d4d6674ef 100644 --- a/zephyr/Makefile +++ b/zephyr/Makefile @@ -102,5 +102,4 @@ z_clean: .PHONY: prj.conf prj.conf: prj_base.conf - cat $< >$@ - if [ -f prj_$(BOARD).conf ]; then cat prj_$(BOARD).conf >>$@; fi + $(PYTHON) makeprj.py prj_base.conf prj_$(BOARD).conf $@ diff --git a/zephyr/makeprj.py b/zephyr/makeprj.py new file mode 100644 index 0000000000..239c877cd6 --- /dev/null +++ b/zephyr/makeprj.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +import sys +import os +import hashlib + + +def hash_file(fname): + if not os.path.exists(fname): + return b"" + hasher = hashlib.md5() + with open(fname, "rb") as f: + hasher.update(f.read()) + return hasher.digest() + + +old_digest = hash_file(sys.argv[3]) + +with open(sys.argv[3] + ".tmp", "wb") as f: + f.write(open(sys.argv[1], "rb").read()) + if os.path.exists(sys.argv[2]): + f.write(open(sys.argv[2], "rb").read()) + +new_digest = hash_file(sys.argv[3] + ".tmp") + +if new_digest != old_digest: + print("Replacing") + os.rename(sys.argv[3] + ".tmp", sys.argv[3]) +else: + os.remove(sys.argv[3] + ".tmp") From 40e541063faf15bd8965f2d65d5a5be08fdef851 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 12 Mar 2017 23:41:19 +0300 Subject: [PATCH 23/65] zephyr: Move "minimal" configuration building to a separate wrapper script. Minimal config can be now build with: ./make-minimal BOARD=... This is required because of Makefile.exports magic, which in its turn depends on PROJ_CONF to be set correctly at the beginning of Makefile parsing at all times. Instead of adding more and more workarounds for that, it's better to just move minimal support to a separate wrapper. Also, remove Zephyr 1.5 era cruft from Makefile, and add support for Zephyr's "run" target which supercedes older "qemu" target in upstream. --- zephyr/Makefile | 17 ++--------------- zephyr/README.md | 12 ++++++------ zephyr/make-minimal | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 21 deletions(-) create mode 100755 zephyr/make-minimal diff --git a/zephyr/Makefile b/zephyr/Makefile index 7d4d6674ef..7a0d052989 100644 --- a/zephyr/Makefile +++ b/zephyr/Makefile @@ -5,17 +5,10 @@ # recursively Makefile.zephyr to build complete application binary # using Zephyr build system. # +# To build a "minimal" configuration, use "make-minimal" wrapper. BOARD ?= qemu_x86 -ifeq ($(MAKECMDGOALS), minimal) -# For minimal, CONF_FILE must be overriden early due to $(Z_EXPORTS) target -CONF_FILE = prj_minimal.conf -else CONF_FILE = prj.conf -endif -# Zephyr 1.5.0 -#OUTDIR_PREFIX = -# Zephyr 1.6.0 OUTDIR_PREFIX = $(BOARD) # Default heap size is 16KB, which is on conservative side, to let @@ -66,7 +59,7 @@ include ../py/mkrules.mk $(Z_EXPORTS): $(CONF_FILE) $(MAKE) -f Makefile.zephyr BOARD=$(BOARD) CONF_FILE=$(CONF_FILE) initconfig outputexports -GENERIC_TARGETS = all zephyr qemu qemugdb flash debug +GENERIC_TARGETS = all zephyr run qemu qemugdb flash debug KCONFIG_TARGETS = \ initconfig config nconfig menuconfig xconfig gconfig \ oldconfig silentoldconfig defconfig savedefconfig \ @@ -88,12 +81,6 @@ $(Z_SYSGEN_H): rm -f $(LIBMICROPYTHON) -$(MAKE) -f Makefile.zephyr BOARD=$(BOARD) CONF_FILE=$(CONF_FILE) -minimal: - $(MAKE) BOARD=$(BOARD) CONF_FILE=prj_minimal.conf CFLAGS_EXTRA='-DMP_CONFIGFILE=""' FROZEN_DIR= - -qemu-minimal: - $(MAKE) -f Makefile.zephyr BOARD=$(BOARD) CONF_FILE=prj_minimal.conf QEMU_NET=0 run - # Clean Zephyr things too clean: z_clean diff --git a/zephyr/README.md b/zephyr/README.md index 6e9e5d8492..4fbf3d4019 100644 --- a/zephyr/README.md +++ b/zephyr/README.md @@ -98,17 +98,17 @@ below 128KB, as long as Zephyr project is committed to maintain stable minimal size of their kernel (which they appear to be). Note that at such size, there is no support for any Zephyr features beyond REPL over UART, and only very minimal set of builtin Python modules. Thus, this build -is more suitable for code size control and quick demonstrations even on +is more suitable for code size control and quick demonstrations on smaller systems. It's also suitable for careful enabling of features one -by one to achieve needed functionality and code size. This is in contrast -to the "default" build, which may get more and more features enabled by -default over time. +by one to achieve needed functionality and code size. This is in a +contrast to the "default" build, which may get more and more features +enabled over time. To make a minimal build: - make BOARD= minimal + ./make-minimal BOARD= To run a minimal build in QEMU without requiring TAP networking setup run the following after you built image with the previous command: - make BOARD= qemu-minimal + ./make-minimal BOARD= qemu diff --git a/zephyr/make-minimal b/zephyr/make-minimal new file mode 100755 index 0000000000..1fc143e4d6 --- /dev/null +++ b/zephyr/make-minimal @@ -0,0 +1,16 @@ +#!/bin/sh +# +# This is a wrapper for make to build a "minimal" Zephyr port. +# It should be run just like make (i.e. extra vars can be passed on the +# command line, etc.), e.g.: +# +# ./make-minimal BOARD=qemu_cortex_m3 +# ./make-minimal BOARD=qemu_cortex_m3 run +# + +make \ + CONF_FILE=prj_minimal.conf \ + CFLAGS_EXTRA='-DMP_CONFIGFILE=""' \ + FROZEN_DIR= \ + QEMU_NET=0 \ + "$@" From f5aac7d33fa04c1709123f8b5667476e0d32c1ff Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 13 Mar 2017 00:43:36 +0300 Subject: [PATCH 24/65] zephyr/main: nlr_jump_fail: Fix noreturn warning. --- zephyr/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zephyr/main.c b/zephyr/main.c index d6980ad295..61de6e168e 100644 --- a/zephyr/main.c +++ b/zephyr/main.c @@ -142,7 +142,8 @@ mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) } MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); -void nlr_jump_fail(void *val) { +NORETURN void nlr_jump_fail(void *val) { + while (1); } void NORETURN __fatal_error(const char *msg) { From 643876fb77d45540e5b82f450a7907f39ec95c6a Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 13 Mar 2017 21:23:31 +1100 Subject: [PATCH 25/65] extmod/vfs_fat: Allow to compile with MICROPY_VFS_FAT disabled. Some ports may want to compile with generic MICROPY_VFS support but without the VfsFat class. This patch allows such a thing. --- extmod/vfs_fat_diskio.c | 4 ++-- extmod/vfs_fat_file.c | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/extmod/vfs_fat_diskio.c b/extmod/vfs_fat_diskio.c index 7efcc22f20..24c00ffba1 100644 --- a/extmod/vfs_fat_diskio.c +++ b/extmod/vfs_fat_diskio.c @@ -28,7 +28,7 @@ */ #include "py/mpconfig.h" -#if MICROPY_VFS +#if MICROPY_VFS && MICROPY_VFS_FAT #include #include @@ -277,4 +277,4 @@ DRESULT disk_ioctl ( } } -#endif // MICROPY_VFS +#endif // MICROPY_VFS && MICROPY_VFS_FAT diff --git a/extmod/vfs_fat_file.c b/extmod/vfs_fat_file.c index 6263492cbd..edffa37c76 100644 --- a/extmod/vfs_fat_file.c +++ b/extmod/vfs_fat_file.c @@ -25,7 +25,7 @@ */ #include "py/mpconfig.h" -#if MICROPY_VFS +#if MICROPY_VFS && MICROPY_VFS_FAT #include #include @@ -37,10 +37,8 @@ #include "lib/oofatfs/ff.h" #include "extmod/vfs_fat.h" -#if MICROPY_VFS_FAT #define mp_type_fileio fatfs_type_fileio #define mp_type_textio fatfs_type_textio -#endif extern const mp_obj_type_t mp_type_fileio; extern const mp_obj_type_t mp_type_textio; @@ -300,4 +298,4 @@ mp_obj_t fatfs_builtin_open_self(mp_obj_t self_in, mp_obj_t path, mp_obj_t mode) return file_open(self, &mp_type_textio, arg_vals); } -#endif // MICROPY_VFS +#endif // MICROPY_VFS && MICROPY_VFS_FAT From a7a2344c9df8ed8cba48e6947d15653450f953ba Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 13 Mar 2017 21:24:21 +1100 Subject: [PATCH 26/65] qemu-arm: Add basic uos module with generic VFS capabilities. --- qemu-arm/Makefile | 2 ++ qemu-arm/moduos.c | 52 +++++++++++++++++++++++++++++++++++++++++ qemu-arm/mpconfigport.h | 5 ++++ 3 files changed, 59 insertions(+) create mode 100644 qemu-arm/moduos.c diff --git a/qemu-arm/Makefile b/qemu-arm/Makefile index dce739fc90..61fc243642 100644 --- a/qemu-arm/Makefile +++ b/qemu-arm/Makefile @@ -36,10 +36,12 @@ LDFLAGS= --specs=nano.specs --specs=rdimon.specs -Wl,--gc-sections -Wl,-Map=$(@: SRC_C = \ main.c \ + moduos.c \ modmachine.c \ SRC_TEST_C = \ test_main.c \ + moduos.c \ modmachine.c \ LIB_SRC_C = $(addprefix lib/,\ diff --git a/qemu-arm/moduos.c b/qemu-arm/moduos.c new file mode 100644 index 0000000000..dcb67396b5 --- /dev/null +++ b/qemu-arm/moduos.c @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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 "extmod/vfs.h" + +STATIC const mp_rom_map_elem_t os_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, + + { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, + { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, + { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mp_vfs_rename_obj) }, + { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_vfs_stat_obj) }, + { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mp_vfs_statvfs_obj) }, + + // MicroPython extensions + { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, + { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); + +const mp_obj_module_t mp_module_uos = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&os_module_globals, +}; diff --git a/qemu-arm/mpconfigport.h b/qemu-arm/mpconfigport.h index 3452266f4b..f642c64287 100644 --- a/qemu-arm/mpconfigport.h +++ b/qemu-arm/mpconfigport.h @@ -35,6 +35,7 @@ #define MICROPY_PY_UHASHLIB (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_USE_INTERNAL_PRINTF (0) +#define MICROPY_VFS (1) // type definitions for the specific machine @@ -58,7 +59,11 @@ typedef long mp_off_t; #define MICROPY_PORT_BUILTINS \ { MP_OBJ_NEW_QSTR(MP_QSTR_open), (mp_obj_t)&mp_builtin_open_obj }, +// extra built-in modules to add to the list of known ones +extern const struct _mp_obj_module_t mp_module_uos; + #define MICROPY_PORT_BUILTIN_MODULES \ + { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, \ { MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&mp_module_machine) }, \ // We need to provide a declaration/definition of alloca() From 0a3ac07ec79acf580ccf3c6188f25cde2f77bbe5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 13 Mar 2017 21:37:21 +1100 Subject: [PATCH 27/65] extmod/vfs: Rewrite path lookup algo to support relative paths from root. For example, if the current directory is the root dir then this patch allows one to do uos.listdir('mnt'), where 'mnt' is a valid mount point. Previous to this patch such a thing would not work, on needed to do uos.listdir('/mnt') instead. --- extmod/vfs.c | 49 +++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/extmod/vfs.c b/extmod/vfs.c index c968345b86..3ab8261dd2 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -43,33 +43,38 @@ // Returns MP_VFS_ROOT for root dir (and then path_out is undefined) and // MP_VFS_NONE for path not found. mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out) { - if (path[0] == '/' && path[1] == 0) { - return MP_VFS_ROOT; - } else if (MP_STATE_VM(vfs_cur) == MP_VFS_ROOT) { - // in root dir - if (path[0] == 0) { - return MP_VFS_ROOT; + if (*path == '/' || MP_STATE_VM(vfs_cur) == MP_VFS_ROOT) { + // an absolute path, or the current volume is root, so search root dir + bool is_abs = 0; + if (*path == '/') { + ++path; + is_abs = 1; } - } else if (*path != '/') { - // a relative path within a mounted device - *path_out = path; - return MP_STATE_VM(vfs_cur); - } - - for (mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) { - if (strncmp(path, vfs->str, vfs->len) == 0) { - if (path[vfs->len] == '/') { - *path_out = path + vfs->len; - return vfs; - } else if (path[vfs->len] == '\0') { - *path_out = "/"; - return vfs; + for (mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) { + size_t len = vfs->len - 1; + if (strncmp(path, vfs->str + 1, len) == 0) { + if (path[len] == '/') { + *path_out = path + len; + return vfs; + } else if (path[len] == '\0') { + *path_out = "/"; + return vfs; + } } } + if (*path == '\0') { + // path was "" or "/" so return virtual root + return MP_VFS_ROOT; + } + if (is_abs) { + // path began with / and was not found + return MP_VFS_NONE; + } } - // mount point not found - return MP_VFS_NONE; + // a relative path within a mounted device + *path_out = path; + return MP_STATE_VM(vfs_cur); } // Version of mp_vfs_lookup_path that takes and returns uPy string objects. From 8891b2e7006334e5333a9a35602ae01f0700b12f Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 13 Mar 2017 21:42:02 +1100 Subject: [PATCH 28/65] tests/extmod: Add a test for core VFS functionality, sans any filesystem. --- tests/extmod/vfs_basic.py | 55 +++++++++++++++++++++++++++++++++++ tests/extmod/vfs_basic.py.exp | 17 +++++++++++ 2 files changed, 72 insertions(+) create mode 100644 tests/extmod/vfs_basic.py create mode 100644 tests/extmod/vfs_basic.py.exp diff --git a/tests/extmod/vfs_basic.py b/tests/extmod/vfs_basic.py new file mode 100644 index 0000000000..b481841e62 --- /dev/null +++ b/tests/extmod/vfs_basic.py @@ -0,0 +1,55 @@ +# test VFS functionality without any particular filesystem type + +try: + try: + import uos_vfs as uos + open = uos.vfs_open + except ImportError: + import uos + uos.mount +except (ImportError, AttributeError): + print("SKIP") + import sys + sys.exit() + + +class Filesystem: + def __init__(self, id): + self.id = id + def mount(self, readonly, mkfs): + print(self.id, 'mount', readonly, mkfs) + def umount(self): + print(self.id, 'umount') + def listdir(self, dir): + print(self.id, 'listdir', dir) + return ['a%d' % self.id] + def chdir(self, dir): + print(self.id, 'chdir', dir) + def open(self, file, mode): + print(self.id, 'open', file, mode) + + +# basic mounting and listdir +uos.mount(Filesystem(1), '/test_mnt') +print(uos.listdir()) + +# referencing the mount point in different ways +print(uos.listdir('test_mnt')) +print(uos.listdir('/test_mnt')) + +# mounting another filesystem +uos.mount(Filesystem(2), '/test_mnt2', readonly=True) +print(uos.listdir()) +print(uos.listdir('/test_mnt2')) + +# chdir +uos.chdir('test_mnt') +print(uos.listdir()) + +# open +open('test_file') +open('test_file', 'wb') + +# umount +uos.umount('/test_mnt') +uos.umount('/test_mnt2') diff --git a/tests/extmod/vfs_basic.py.exp b/tests/extmod/vfs_basic.py.exp new file mode 100644 index 0000000000..c9ed65191b --- /dev/null +++ b/tests/extmod/vfs_basic.py.exp @@ -0,0 +1,17 @@ +1 mount False False +['test_mnt'] +1 listdir / +['a1'] +1 listdir / +['a1'] +2 mount True False +['test_mnt', 'test_mnt2'] +2 listdir / +['a2'] +1 chdir / +1 listdir +['a1'] +1 open test_file r +1 open test_file wb +1 umount +2 umount From 9c9674a32599b8f9268fb479fc1cb3d62a5ec2f0 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 14 Mar 2017 00:19:35 +0300 Subject: [PATCH 29/65] zephyr/main: Remove unused __fatal_error(). --- zephyr/main.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/zephyr/main.c b/zephyr/main.c index 61de6e168e..25bd88f768 100644 --- a/zephyr/main.c +++ b/zephyr/main.c @@ -146,10 +146,6 @@ NORETURN void nlr_jump_fail(void *val) { while (1); } -void NORETURN __fatal_error(const char *msg) { - while (1); -} - #ifndef NDEBUG void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr) { printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line); From 9773506ab131422433c830e18ab044f0c7d3e0b0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 11:50:50 +1100 Subject: [PATCH 30/65] pic16bit/main: Make nlr_jump_fail never return. --- pic16bit/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/pic16bit/main.c b/pic16bit/main.c index fcc51b6617..5c58d5c68b 100644 --- a/pic16bit/main.c +++ b/pic16bit/main.c @@ -111,6 +111,7 @@ mp_obj_t mp_builtin_open(uint n_args, const mp_obj_t *args, mp_map_t *kwargs) { MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); void nlr_jump_fail(void *val) { + while (1); } void NORETURN __fatal_error(const char *msg) { From 1831034be13fef5344583c557ff089df31788251 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 11:16:31 +1100 Subject: [PATCH 31/65] py: Allow lexer to raise exceptions during construction. This patch refactors the error handling in the lexer, to simplify it (ie reduce code size). A long time ago, when the lexer/parser/compiler were first written, the lexer and parser were designed so they didn't use exceptions (ie nlr) to report errors but rather returned an error code. Over time that has gradually changed, the parser in particular has more and more ways of raising exceptions. Also, the lexer never really handled all errors without raising, eg there were some memory errors which could raise an exception (and in these rare cases one would get a fatal nlr-not-handled fault). This patch accepts the fact that the lexer can raise exceptions in some cases and allows it to raise exceptions to handle all its errors, which are for the most part just out-of-memory errors during construction of the lexer. This makes the lexer a bit simpler, and also the persistent code stuff is simplified. What this means for users of the lexer is that calls to it must be wrapped in a nlr handler. But all uses of the lexer already have such an nlr handler for the parser (and compiler) so that doesn't put any extra burden on the callers. --- extmod/vfs_reader.c | 29 +++++++++-------------------- py/builtinevex.c | 3 --- py/builtinimport.c | 17 +++-------------- py/lexer.c | 31 +++++-------------------------- py/persistentcode.c | 9 ++------- py/reader.c | 28 +++++++++------------------- py/reader.h | 6 +++--- 7 files changed, 31 insertions(+), 92 deletions(-) diff --git a/extmod/vfs_reader.c b/extmod/vfs_reader.c index 9509582d22..891098aa1e 100644 --- a/extmod/vfs_reader.c +++ b/extmod/vfs_reader.c @@ -27,7 +27,7 @@ #include #include -#include "py/nlr.h" +#include "py/runtime.h" #include "py/stream.h" #include "py/reader.h" #include "extmod/vfs.h" @@ -69,30 +69,19 @@ STATIC void mp_reader_vfs_close(void *data) { m_del_obj(mp_reader_vfs_t, reader); } -int mp_reader_new_file(mp_reader_t *reader, const char *filename) { - mp_reader_vfs_t *rf = m_new_obj_maybe(mp_reader_vfs_t); - if (rf == NULL) { - return MP_ENOMEM; - } - // TODO we really should just let this function raise a uPy exception - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - mp_obj_t arg = mp_obj_new_str(filename, strlen(filename), false); - rf->file = mp_vfs_open(1, &arg, (mp_map_t*)&mp_const_empty_map); - int errcode; - rf->len = mp_stream_rw(rf->file, rf->buf, sizeof(rf->buf), &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); - nlr_pop(); - if (errcode != 0) { - return errcode; - } - } else { - return MP_ENOENT; // assume error was "file not found" +void mp_reader_new_file(mp_reader_t *reader, const char *filename) { + mp_reader_vfs_t *rf = m_new_obj(mp_reader_vfs_t); + mp_obj_t arg = mp_obj_new_str(filename, strlen(filename), false); + rf->file = mp_vfs_open(1, &arg, (mp_map_t*)&mp_const_empty_map); + int errcode; + rf->len = mp_stream_rw(rf->file, rf->buf, sizeof(rf->buf), &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); + if (errcode != 0) { + mp_raise_OSError(errcode); } rf->pos = 0; reader->data = rf; reader->readbyte = mp_reader_vfs_readbyte; reader->close = mp_reader_vfs_close; - return 0; // success } #endif // MICROPY_READER_VFS diff --git a/py/builtinevex.c b/py/builtinevex.c index 636f869300..b0514ee994 100644 --- a/py/builtinevex.c +++ b/py/builtinevex.c @@ -136,9 +136,6 @@ STATIC mp_obj_t eval_exec_helper(size_t n_args, const mp_obj_t *args, mp_parse_i mp_lexer_t *lex; if (MICROPY_PY_BUILTINS_EXECFILE && parse_input_kind == MP_PARSE_SINGLE_INPUT) { lex = mp_lexer_new_from_file(str); - if (lex == NULL) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "could not open file '%s'", str)); - } parse_input_kind = MP_PARSE_FILE_INPUT; } else { lex = mp_lexer_new_from_str_len(MP_QSTR__lt_string_gt_, str, str_len, 0); diff --git a/py/builtinimport.c b/py/builtinimport.c index 0a917c6f83..96846b9636 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -131,18 +131,7 @@ STATIC mp_import_stat_t find_file(const char *file_str, uint file_len, vstr_t *d } #if MICROPY_ENABLE_COMPILER -STATIC void do_load_from_lexer(mp_obj_t module_obj, mp_lexer_t *lex, const char *fname) { - - if (lex == NULL) { - // we verified the file exists using stat, but lexer could still fail - if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { - mp_raise_msg(&mp_type_ImportError, "module not found"); - } else { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ImportError, - "no module named '%s'", fname)); - } - } - +STATIC void do_load_from_lexer(mp_obj_t module_obj, mp_lexer_t *lex) { #if MICROPY_PY___FILE__ qstr source_name = lex->source_name; mp_store_attr(module_obj, MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); @@ -207,7 +196,7 @@ STATIC void do_load(mp_obj_t module_obj, vstr_t *file) { // found the filename in the list of frozen files, then load and execute it. #if MICROPY_MODULE_FROZEN_STR if (frozen_type == MP_FROZEN_STR) { - do_load_from_lexer(module_obj, modref, file_str); + do_load_from_lexer(module_obj, modref); return; } #endif @@ -235,7 +224,7 @@ STATIC void do_load(mp_obj_t module_obj, vstr_t *file) { #if MICROPY_ENABLE_COMPILER { mp_lexer_t *lex = mp_lexer_new_from_file(file_str); - do_load_from_lexer(module_obj, lex, file_str); + do_load_from_lexer(module_obj, lex); return; } #endif diff --git a/py/lexer.c b/py/lexer.c index 9dcdd19eb5..fadaee6f35 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -699,13 +699,7 @@ void mp_lexer_to_next(mp_lexer_t *lex) { } mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader) { - mp_lexer_t *lex = m_new_obj_maybe(mp_lexer_t); - - // check for memory allocation error - if (lex == NULL) { - reader.close(reader.data); - return NULL; - } + mp_lexer_t *lex = m_new_obj(mp_lexer_t); lex->source_name = src_name; lex->reader = reader; @@ -715,16 +709,9 @@ mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader) { lex->nested_bracket_level = 0; lex->alloc_indent_level = MICROPY_ALLOC_LEXER_INDENT_INIT; lex->num_indent_level = 1; - lex->indent_level = m_new_maybe(uint16_t, lex->alloc_indent_level); + lex->indent_level = m_new(uint16_t, lex->alloc_indent_level); vstr_init(&lex->vstr, 32); - // check for memory allocation error - // note: vstr_init above may fail on malloc, but so may mp_lexer_to_next below - if (lex->indent_level == NULL) { - mp_lexer_free(lex); - return NULL; - } - // store sentinel for first indentation level lex->indent_level[0] = 0; @@ -764,9 +751,7 @@ mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader) { mp_lexer_t *mp_lexer_new_from_str_len(qstr src_name, const char *str, size_t len, size_t free_len) { mp_reader_t reader; - if (!mp_reader_new_mem(&reader, (const byte*)str, len, free_len)) { - return NULL; - } + mp_reader_new_mem(&reader, (const byte*)str, len, free_len); return mp_lexer_new(src_name, reader); } @@ -774,10 +759,7 @@ mp_lexer_t *mp_lexer_new_from_str_len(qstr src_name, const char *str, size_t len mp_lexer_t *mp_lexer_new_from_file(const char *filename) { mp_reader_t reader; - int ret = mp_reader_new_file(&reader, filename); - if (ret != 0) { - return NULL; - } + mp_reader_new_file(&reader, filename); return mp_lexer_new(qstr_from_str(filename), reader); } @@ -785,10 +767,7 @@ 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) { mp_reader_t reader; - int ret = mp_reader_new_file_from_fd(&reader, fd, close_fd); - if (ret != 0) { - return NULL; - } + mp_reader_new_file_from_fd(&reader, fd, close_fd); return mp_lexer_new(filename, reader); } diff --git a/py/persistentcode.c b/py/persistentcode.c index 5cb5117093..2a9a5b7cc0 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -225,18 +225,13 @@ 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_reader_t reader; - if (!mp_reader_new_mem(&reader, buf, len, 0)) { - m_malloc_fail(BYTES_PER_WORD); // we need to raise a MemoryError - } + mp_reader_new_mem(&reader, buf, len, 0); return mp_raw_code_load(&reader); } mp_raw_code_t *mp_raw_code_load_file(const char *filename) { mp_reader_t reader; - int ret = mp_reader_new_file(&reader, filename); - if (ret != 0) { - mp_raise_OSError(ret); - } + mp_reader_new_file(&reader, filename); return mp_raw_code_load(&reader); } diff --git a/py/reader.c b/py/reader.c index d7de7aa6c4..5df45c4957 100644 --- a/py/reader.c +++ b/py/reader.c @@ -27,6 +27,7 @@ #include #include +#include "py/runtime.h" #include "py/mperrno.h" #include "py/reader.h" @@ -54,11 +55,8 @@ STATIC void mp_reader_mem_close(void *data) { m_del_obj(mp_reader_mem_t, reader); } -bool mp_reader_new_mem(mp_reader_t *reader, const byte *buf, size_t len, size_t free_len) { - mp_reader_mem_t *rm = m_new_obj_maybe(mp_reader_mem_t); - if (rm == NULL) { - return false; - } +void mp_reader_new_mem(mp_reader_t *reader, const byte *buf, size_t len, size_t free_len) { + mp_reader_mem_t *rm = m_new_obj(mp_reader_mem_t); rm->free_len = free_len; rm->beg = buf; rm->cur = buf; @@ -66,7 +64,6 @@ bool mp_reader_new_mem(mp_reader_t *reader, const byte *buf, size_t len, size_t reader->data = rm; reader->readbyte = mp_reader_mem_readbyte; reader->close = mp_reader_mem_close; - return true; } #if MICROPY_READER_POSIX @@ -110,14 +107,8 @@ STATIC void mp_reader_posix_close(void *data) { m_del_obj(mp_reader_posix_t, reader); } -int mp_reader_new_file_from_fd(mp_reader_t *reader, int fd, bool close_fd) { - mp_reader_posix_t *rp = m_new_obj_maybe(mp_reader_posix_t); - if (rp == NULL) { - if (close_fd) { - close(fd); - } - return MP_ENOMEM; - } +void mp_reader_new_file_from_fd(mp_reader_t *reader, int fd, bool close_fd) { + mp_reader_posix_t *rp = m_new_obj(mp_reader_posix_t); rp->close_fd = close_fd; rp->fd = fd; int n = read(rp->fd, rp->buf, sizeof(rp->buf)); @@ -125,22 +116,21 @@ int mp_reader_new_file_from_fd(mp_reader_t *reader, int fd, bool close_fd) { if (close_fd) { close(fd); } - return errno; + mp_raise_OSError(errno); } rp->len = n; rp->pos = 0; reader->data = rp; reader->readbyte = mp_reader_posix_readbyte; reader->close = mp_reader_posix_close; - return 0; // success } -int mp_reader_new_file(mp_reader_t *reader, const char *filename) { +void mp_reader_new_file(mp_reader_t *reader, const char *filename) { int fd = open(filename, O_RDONLY, 0644); if (fd < 0) { - return errno; + mp_raise_OSError(errno); } - return mp_reader_new_file_from_fd(reader, fd, true); + mp_reader_new_file_from_fd(reader, fd, true); } #endif diff --git a/py/reader.h b/py/reader.h index b02d96149b..8511c72ce5 100644 --- a/py/reader.h +++ b/py/reader.h @@ -39,8 +39,8 @@ typedef struct _mp_reader_t { void (*close)(void *data); } mp_reader_t; -bool mp_reader_new_mem(mp_reader_t *reader, const byte *buf, size_t len, size_t free_len); -int mp_reader_new_file(mp_reader_t *reader, const char *filename); -int mp_reader_new_file_from_fd(mp_reader_t *reader, int fd, bool close_fd); +void mp_reader_new_mem(mp_reader_t *reader, const byte *buf, size_t len, size_t free_len); +void mp_reader_new_file(mp_reader_t *reader, const char *filename); +void mp_reader_new_file_from_fd(mp_reader_t *reader, int fd, bool close_fd); #endif // MICROPY_INCLUDED_PY_READER_H From 56b238393b8c4172c7054011216792a9546ce7db Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 11:21:29 +1100 Subject: [PATCH 32/65] lib/utils/pyexec: Refactor to put lexer constructors all in one place. The lexer can now raise an exception on construction so it must go within an nlr handler block. --- lib/utils/pyexec.c | 67 +++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 42 deletions(-) diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index 0d20c19178..80a7282b14 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -52,13 +52,15 @@ STATIC bool repl_display_debugging_info = 0; #define EXEC_FLAG_ALLOW_DEBUGGING (2) #define EXEC_FLAG_IS_REPL (4) #define EXEC_FLAG_SOURCE_IS_RAW_CODE (8) +#define EXEC_FLAG_SOURCE_IS_VSTR (16) +#define EXEC_FLAG_SOURCE_IS_FILENAME (32) // parses, compiles and executes the code in the lexer // frees the lexer before returning // EXEC_FLAG_PRINT_EOF prints 2 EOF chars: 1 after normal output, 1 after exception output // EXEC_FLAG_ALLOW_DEBUGGING allows debugging info to be printed after executing the code // EXEC_FLAG_IS_REPL is used for REPL inputs (flag passed on to mp_compile) -STATIC int parse_compile_execute(void *source, mp_parse_input_kind_t input_kind, int exec_flags) { +STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input_kind, int exec_flags) { int ret = 0; uint32_t start = 0; @@ -76,8 +78,16 @@ STATIC int parse_compile_execute(void *source, mp_parse_input_kind_t input_kind, #endif { #if MICROPY_ENABLE_COMPILER + mp_lexer_t *lex; + if (exec_flags & EXEC_FLAG_SOURCE_IS_VSTR) { + const vstr_t *vstr = source; + lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, 0); + } else if (exec_flags & EXEC_FLAG_SOURCE_IS_FILENAME) { + lex = mp_lexer_new_from_file(source); + } else { + lex = (mp_lexer_t*)source; + } // source is a lexer, parse and compile the script - mp_lexer_t *lex = source; qstr source_name = lex->source_name; mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, exec_flags & EXEC_FLAG_IS_REPL); @@ -202,14 +212,9 @@ STATIC int pyexec_raw_repl_process_char(int c) { return PYEXEC_FORCED_EXIT; } - mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, MP_STATE_VM(repl_line)->buf, MP_STATE_VM(repl_line)->len, 0); - if (lex == NULL) { - mp_hal_stdout_tx_str("\x04MemoryError\r\n\x04"); - } else { - int ret = parse_compile_execute(lex, MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF); - if (ret & PYEXEC_FORCED_EXIT) { - return ret; - } + int ret = parse_compile_execute(MP_STATE_VM(repl_line), MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_VSTR); + if (ret & PYEXEC_FORCED_EXIT) { + return ret; } reset: @@ -285,14 +290,9 @@ STATIC int pyexec_friendly_repl_process_char(int c) { } exec: ; - mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr_str(MP_STATE_VM(repl_line)), vstr_len(MP_STATE_VM(repl_line)), 0); - if (lex == NULL) { - printf("MemoryError\n"); - } else { - int ret = parse_compile_execute(lex, MP_PARSE_SINGLE_INPUT, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL); - if (ret & PYEXEC_FORCED_EXIT) { - return ret; - } + int ret = parse_compile_execute(MP_STATE_VM(repl_line), MP_PARSE_SINGLE_INPUT, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR); + if (ret & PYEXEC_FORCED_EXIT) { + return ret; } input_restart: @@ -361,14 +361,9 @@ raw_repl_reset: return PYEXEC_FORCED_EXIT; } - mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line.buf, line.len, 0); - if (lex == NULL) { - printf("\x04MemoryError\n\x04"); - } else { - int ret = parse_compile_execute(lex, MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF); - if (ret & PYEXEC_FORCED_EXIT) { - return ret; - } + int ret = parse_compile_execute(&line, MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_VSTR); + if (ret & PYEXEC_FORCED_EXIT) { + return ret; } } } @@ -489,14 +484,9 @@ friendly_repl_reset: } } - mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr_str(&line), vstr_len(&line), 0); - if (lex == NULL) { - printf("MemoryError\n"); - } else { - ret = parse_compile_execute(lex, parse_input_kind, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL); - if (ret & PYEXEC_FORCED_EXIT) { - return ret; - } + ret = parse_compile_execute(&line, parse_input_kind, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR); + if (ret & PYEXEC_FORCED_EXIT) { + return ret; } } } @@ -505,14 +495,7 @@ friendly_repl_reset: #endif // MICROPY_ENABLE_COMPILER int pyexec_file(const char *filename) { - mp_lexer_t *lex = mp_lexer_new_from_file(filename); - - if (lex == NULL) { - printf("could not open file '%s' for reading\n", filename); - return false; - } - - return parse_compile_execute(lex, MP_PARSE_FILE_INPUT, 0); + return parse_compile_execute(filename, MP_PARSE_FILE_INPUT, 0); } #if MICROPY_MODULE_FROZEN From 33a77ea25f1959cb21789d7e1677753d8b7f9179 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 11:23:54 +1100 Subject: [PATCH 33/65] unix/main: Refactor to put lexer constructors all in one place. The lexer can now raise an exception on construction so it must go within an nlr handler block. --- unix/main.c | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/unix/main.c b/unix/main.c index d916cdeb88..01ebdce175 100644 --- a/unix/main.c +++ b/unix/main.c @@ -90,19 +90,33 @@ STATIC int handle_uncaught_exception(mp_obj_base_t *exc) { return 1; } +#define LEX_SRC_STR (1) +#define LEX_SRC_VSTR (2) +#define LEX_SRC_FILENAME (3) +#define LEX_SRC_STDIN (4) + // Returns standard error codes: 0 for success, 1 for all other errors, // except if FORCED_EXIT bit is set then script raised SystemExit and the // value of the exit is in the lower 8 bits of the return value -STATIC int execute_from_lexer(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, bool is_repl) { - if (lex == NULL) { - printf("MemoryError: lexer could not allocate memory\n"); - return 1; - } - +STATIC int execute_from_lexer(int source_kind, const void *source, mp_parse_input_kind_t input_kind, bool is_repl) { mp_hal_set_interrupt_char(CHAR_CTRL_C); nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { + // create lexer based on source kind + mp_lexer_t *lex; + if (source_kind == LEX_SRC_STR) { + const char *line = source; + lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line, strlen(line), false); + } else if (source_kind == LEX_SRC_VSTR) { + const vstr_t *vstr = source; + lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, false); + } else if (source_kind == LEX_SRC_FILENAME) { + lex = mp_lexer_new_from_file((const char*)source); + } else { // LEX_SRC_STDIN + lex = mp_lexer_new_from_fd(MP_QSTR__lt_stdin_gt_, 0, false); + } + qstr source_name = lex->source_name; #if MICROPY_PY___FILE__ @@ -240,8 +254,7 @@ STATIC int do_repl(void) { mp_hal_stdio_mode_orig(); - mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line.buf, line.len, false); - ret = execute_from_lexer(lex, parse_input_kind, true); + ret = execute_from_lexer(LEX_SRC_VSTR, &line, parse_input_kind, true); if (ret & FORCED_EXIT) { return ret; } @@ -268,8 +281,7 @@ STATIC int do_repl(void) { line = line3; } - mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line, strlen(line), false); - int ret = execute_from_lexer(lex, MP_PARSE_SINGLE_INPUT, true); + int ret = execute_from_lexer(LEX_SRC_STR, line, MP_PARSE_SINGLE_INPUT, true); if (ret & FORCED_EXIT) { return ret; } @@ -280,13 +292,11 @@ STATIC int do_repl(void) { } STATIC int do_file(const char *file) { - mp_lexer_t *lex = mp_lexer_new_from_file(file); - return execute_from_lexer(lex, MP_PARSE_FILE_INPUT, false); + return execute_from_lexer(LEX_SRC_FILENAME, file, MP_PARSE_FILE_INPUT, false); } STATIC int do_str(const char *str) { - mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, str, strlen(str), false); - return execute_from_lexer(lex, MP_PARSE_FILE_INPUT, false); + return execute_from_lexer(LEX_SRC_STR, str, MP_PARSE_FILE_INPUT, false); } STATIC int usage(char **argv) { @@ -585,8 +595,7 @@ MP_NOINLINE int main_(int argc, char **argv) { ret = do_repl(); prompt_write_history(); } else { - mp_lexer_t *lex = mp_lexer_new_from_fd(MP_QSTR__lt_stdin_gt_, 0, false); - ret = execute_from_lexer(lex, MP_PARSE_FILE_INPUT, false); + ret = execute_from_lexer(LEX_SRC_STDIN, NULL, MP_PARSE_FILE_INPUT, false); } } From c72a5f8c79514ac93b62e1a767a5d413105d7dd6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 11:24:26 +1100 Subject: [PATCH 34/65] bare-arm/main: Move lexer constructor to within NLR handler block. And raise an exception when mp_lexer_new_from_file is called. --- bare-arm/main.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/bare-arm/main.c b/bare-arm/main.c index 99a7f926ef..938414dfe8 100644 --- a/bare-arm/main.c +++ b/bare-arm/main.c @@ -6,15 +6,12 @@ #include "py/compile.h" #include "py/runtime.h" #include "py/repl.h" +#include "py/mperrno.h" void do_str(const char *src, mp_parse_input_kind_t input_kind) { - mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); - if (lex == NULL) { - return; - } - nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { + mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); qstr source_name = lex->source_name; mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); mp_obj_t module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, true); @@ -35,7 +32,7 @@ int main(int argc, char **argv) { } mp_lexer_t *mp_lexer_new_from_file(const char *filename) { - return NULL; + mp_raise_OSError(MP_ENOENT); } mp_import_stat_t mp_import_stat(const char *path) { From bcd5adc65e341180bd6411d7008ea36e225bddf9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 11:25:21 +1100 Subject: [PATCH 35/65] minimal/main: Move lexer constructor to within NLR handler block. And raise an exception when mp_lexer_new_from_file is called. --- minimal/main.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/minimal/main.c b/minimal/main.c index 766ad6c1b4..496d925e77 100644 --- a/minimal/main.c +++ b/minimal/main.c @@ -7,18 +7,14 @@ #include "py/runtime.h" #include "py/repl.h" #include "py/gc.h" +#include "py/mperrno.h" #include "lib/utils/pyexec.h" #if MICROPY_ENABLE_COMPILER void do_str(const char *src, mp_parse_input_kind_t input_kind) { - mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); - if (lex == NULL) { - printf("MemoryError: lexer could not allocate memory\n"); - return; - } - nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { + mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); qstr source_name = lex->source_name; mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); mp_obj_t module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, true); @@ -74,7 +70,7 @@ void gc_collect(void) { } mp_lexer_t *mp_lexer_new_from_file(const char *filename) { - return NULL; + mp_raise_OSError(MP_ENOENT); } mp_import_stat_t mp_import_stat(const char *path) { From 25b6b62562e56fc2aaa78b72f5dfffc35bc2430c Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 11:25:52 +1100 Subject: [PATCH 36/65] qemu-arm: Move lexer constructors to within NLR handler block. And raise an exception when mp_lexer_new_from_file is called. --- qemu-arm/main.c | 9 +++------ qemu-arm/test_main.c | 10 +++------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/qemu-arm/main.c b/qemu-arm/main.c index aa7247b446..d5fbcd84bc 100644 --- a/qemu-arm/main.c +++ b/qemu-arm/main.c @@ -12,15 +12,12 @@ #include "py/stackctrl.h" #include "py/gc.h" #include "py/repl.h" +#include "py/mperrno.h" void do_str(const char *src, mp_parse_input_kind_t input_kind) { - mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); - if (lex == NULL) { - return; - } - nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { + mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); qstr source_name = lex->source_name; mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); mp_obj_t module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, true); @@ -47,7 +44,7 @@ void gc_collect(void) { } mp_lexer_t *mp_lexer_new_from_file(const char *filename) { - return NULL; + mp_raise_OSError(MP_ENOENT); } mp_import_stat_t mp_import_stat(const char *path) { diff --git a/qemu-arm/test_main.c b/qemu-arm/test_main.c index 4d89930906..5c07d16076 100644 --- a/qemu-arm/test_main.c +++ b/qemu-arm/test_main.c @@ -11,7 +11,7 @@ #include "py/runtime.h" #include "py/stackctrl.h" #include "py/gc.h" -#include "py/repl.h" +#include "py/mperrno.h" #include "tinytest.h" #include "tinytest_macros.h" @@ -24,13 +24,9 @@ inline void do_str(const char *src) { gc_init(heap, (char*)heap + HEAP_SIZE); mp_init(); - mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); - if (lex == NULL) { - tt_abort_msg("Lexer initialization error"); - } - nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { + mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); qstr source_name = lex->source_name; mp_parse_tree_t parse_tree = mp_parse(lex, MP_PARSE_FILE_INPUT); mp_obj_t module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, false); @@ -80,7 +76,7 @@ void gc_collect(void) { } mp_lexer_t *mp_lexer_new_from_file(const char *filename) { - return NULL; + mp_raise_OSError(MP_ENOENT); } mp_import_stat_t mp_import_stat(const char *path) { From 180045bce9282c4a658284e01348f57841e39cdd Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 11:26:43 +1100 Subject: [PATCH 37/65] zephyr/main: Move lexer constructor to within NLR handler block. And raise an exception when mp_lexer_new_from_file is called. --- zephyr/main.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/zephyr/main.c b/zephyr/main.c index 25bd88f768..1f8589a6c9 100644 --- a/zephyr/main.c +++ b/zephyr/main.c @@ -44,14 +44,9 @@ #include "lib/mp-readline/readline.h" void do_str(const char *src, mp_parse_input_kind_t input_kind) { - mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); - if (lex == NULL) { - printf("MemoryError: lexer could not allocate memory\n"); - return; - } - nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { + mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); qstr source_name = lex->source_name; mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); mp_obj_t module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, true); @@ -130,7 +125,7 @@ void gc_collect(void) { } mp_lexer_t *mp_lexer_new_from_file(const char *filename) { - return NULL; + mp_raise_OSError(ENOENT); } mp_import_stat_t mp_import_stat(const char *path) { From 52f8f5666a496e17306c7cc6f53c0c14cf003a57 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 11:30:05 +1100 Subject: [PATCH 38/65] esp8266: Update lexer constructors so they can raise exceptions. --- esp8266/lexerstr32.c | 5 +---- esp8266/main.c | 3 ++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/esp8266/lexerstr32.c b/esp8266/lexerstr32.c index 3fc62399e7..6fb84bb74e 100644 --- a/esp8266/lexerstr32.c +++ b/esp8266/lexerstr32.c @@ -58,10 +58,7 @@ STATIC void str32_buf_free(void *sb_in) { } mp_lexer_t *mp_lexer_new_from_str32(qstr src_name, const char *str, mp_uint_t len, mp_uint_t free_len) { - mp_lexer_str32_buf_t *sb = m_new_obj_maybe(mp_lexer_str32_buf_t); - if (sb == NULL) { - return NULL; - } + mp_lexer_str32_buf_t *sb = m_new_obj(mp_lexer_str32_buf_t); sb->byte_off = (uint32_t)str & 3; sb->src_cur = (uint32_t*)(str - sb->byte_off); sb->val = *sb->src_cur++ >> sb->byte_off * 8; diff --git a/esp8266/main.c b/esp8266/main.c index 0099486750..fd07efcbf2 100644 --- a/esp8266/main.c +++ b/esp8266/main.c @@ -32,6 +32,7 @@ #include "py/runtime0.h" #include "py/runtime.h" #include "py/stackctrl.h" +#include "py/mperrno.h" #include "py/mphal.h" #include "py/gc.h" #include "lib/mp-readline/readline.h" @@ -111,7 +112,7 @@ void user_init(void) { #if !MICROPY_VFS mp_lexer_t *mp_lexer_new_from_file(const char *filename) { - return NULL; + mp_raise_OSError(MP_ENOENT); } mp_import_stat_t mp_import_stat(const char *path) { From 21420b13c011ca5a89a224ff7108b98fd3aeafd9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 11:33:06 +1100 Subject: [PATCH 39/65] examples/embedding: Place lexer constructor within NLR handler block. The lexer constructor may now raise an exception and it needs to be caught. --- examples/embedding/hello-embed.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/embedding/hello-embed.c b/examples/embedding/hello-embed.c index 1949305184..e3a4847835 100644 --- a/examples/embedding/hello-embed.c +++ b/examples/embedding/hello-embed.c @@ -35,9 +35,10 @@ static char heap[16384]; -mp_obj_t execute_from_lexer(mp_lexer_t *lex) { +mp_obj_t execute_from_str(const char *str) { nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { + mp_lexer_t *lex = mp_lexer_new_from_str_len(0/*MP_QSTR_*/, str, strlen(str), false); mp_parse_tree_t pt = mp_parse(lex, MP_PARSE_FILE_INPUT); mp_obj_t module_fun = mp_compile(&pt, lex->source_name, MP_EMIT_OPT_NONE, false); mp_call_function_0(module_fun); @@ -58,8 +59,7 @@ int main() { mp_init(); const char str[] = "print('Hello world of easy embedding!')"; - mp_lexer_t *lex = mp_lexer_new_from_str_len(0/*MP_QSTR_*/, str, strlen(str), false); - if (execute_from_lexer(lex)) { + if (execute_from_str(str)) { printf("Error\n"); } } From 41b1df604617bdde59bb722b9247c16fa4677d94 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 11:36:24 +1100 Subject: [PATCH 40/65] lib/memzip: Make lexer constructor raise exception when file not found. --- lib/memzip/lexermemzip.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/memzip/lexermemzip.c b/lib/memzip/lexermemzip.c index 72fe6b1c6b..6b26961bdc 100644 --- a/lib/memzip/lexermemzip.c +++ b/lib/memzip/lexermemzip.c @@ -1,6 +1,8 @@ #include #include "py/lexer.h" +#include "py/runtime.h" +#include "py/mperrno.h" #include "memzip.h" mp_lexer_t *mp_lexer_new_from_file(const char *filename) @@ -9,7 +11,7 @@ mp_lexer_t *mp_lexer_new_from_file(const char *filename) size_t len; if (memzip_locate(filename, &data, &len) != MZ_OK) { - return NULL; + mp_raise_OSError(MP_ENOENT); } return mp_lexer_new_from_str_len(qstr_from_str(filename), (const char *)data, (mp_uint_t)len, 0); From 68e1c4f0684de5dd5cefaf8d775657b42ce09dbe Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 11:37:31 +1100 Subject: [PATCH 41/65] pic16bit/main: Make mp_lexer_new_from_file raise an exception. --- pic16bit/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pic16bit/main.c b/pic16bit/main.c index 5c58d5c68b..f2a9debab1 100644 --- a/pic16bit/main.c +++ b/pic16bit/main.c @@ -33,6 +33,7 @@ #include "py/runtime.h" #include "py/gc.h" #include "py/mphal.h" +#include "py/mperrno.h" #include "lib/utils/pyexec.h" #include "readline.h" #include "board.h" @@ -98,7 +99,7 @@ void gc_collect(void) { } mp_lexer_t *mp_lexer_new_from_file(const char *filename) { - return NULL; + mp_raise_OSError(MP_ENOENT); } mp_import_stat_t mp_import_stat(const char *path) { From 97142000f78fab8d1646efc17d60ac0e20919bd5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 11:43:28 +1100 Subject: [PATCH 42/65] mpy-cross/main: Move lexer constructor to within NLR handler block. --- mpy-cross/main.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/mpy-cross/main.c b/mpy-cross/main.c index 378c013c8d..4c88c72224 100644 --- a/mpy-cross/main.c +++ b/mpy-cross/main.c @@ -57,14 +57,10 @@ STATIC void stderr_print_strn(void *env, const char *str, mp_uint_t len) { STATIC const mp_print_t mp_stderr_print = {NULL, stderr_print_strn}; STATIC int compile_and_save(const char *file, const char *output_file, const char *source_file) { - mp_lexer_t *lex = mp_lexer_new_from_file(file); - if (lex == NULL) { - printf("could not open file '%s' for reading\n", file); - return 1; - } - nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { + mp_lexer_t *lex = mp_lexer_new_from_file(file); + qstr source_name; if (source_file == NULL) { source_name = lex->source_name; From e1782042f5f8281c940d697d8aa08e23a35916ae Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 11:45:09 +1100 Subject: [PATCH 43/65] teensy/lexerfrozen: Make mp_lexer_new_from_file raise an exception. --- teensy/lexerfrozen.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/teensy/lexerfrozen.c b/teensy/lexerfrozen.c index 03ef800876..21e978dc79 100644 --- a/teensy/lexerfrozen.c +++ b/teensy/lexerfrozen.c @@ -1,11 +1,13 @@ #include #include "py/lexer.h" +#include "py/runtime.h" +#include "py/mperrno.h" mp_import_stat_t mp_import_stat(const char *path) { return MP_IMPORT_STAT_NO_EXIST; } mp_lexer_t *mp_lexer_new_from_file(const char *filename) { - return NULL; + mp_raise_OSError(MP_ENOENT); } From 4f29b315a6900445d2139652914f81168763816f Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 6 Mar 2017 17:41:34 +1100 Subject: [PATCH 44/65] esp8266: Only execute main.py if in friendly REPL mode. --- esp8266/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esp8266/main.c b/esp8266/main.c index fd07efcbf2..888e589704 100644 --- a/esp8266/main.c +++ b/esp8266/main.c @@ -65,7 +65,9 @@ STATIC void mp_reset(void) { #if MICROPY_MODULE_FROZEN pyexec_frozen_module("_boot.py"); pyexec_file("boot.py"); - pyexec_file("main.py"); + if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL) { + pyexec_file("main.py"); + } #endif } From d1ae6ae080e1475b50aa2c6580d1ea588cfbdf64 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 14:54:20 +1100 Subject: [PATCH 45/65] py/objint: Allow to print long-long ints without using the heap. Some stack is allocated to format ints, and when the int implementation uses long-long there should be additional stack allocated compared with the other cases. This patch uses the existing "fmt_int_t" type to determine the amount of stack to allocate. --- py/objint.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/py/objint.c b/py/objint.c index 2a7e3f3fd9..5aa6a95cc0 100644 --- a/py/objint.c +++ b/py/objint.c @@ -127,11 +127,17 @@ mp_fp_as_int_class_t mp_classify_fp_as_int(mp_float_t val) { #undef MP_FLOAT_EXP_SHIFT_I32 #endif +#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG +typedef mp_longint_impl_t fmt_int_t; +#else +typedef mp_int_t fmt_int_t; +#endif + void mp_obj_int_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; // The size of this buffer is rather arbitrary. If it's not large // enough, a dynamic one will be allocated. - char stack_buf[sizeof(mp_int_t) * 4]; + char stack_buf[sizeof(fmt_int_t) * 4]; char *buf = stack_buf; size_t buf_size = sizeof(stack_buf); size_t fmt_size; @@ -144,12 +150,6 @@ void mp_obj_int_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t } } -#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG -typedef mp_longint_impl_t fmt_int_t; -#else -typedef mp_int_t fmt_int_t; -#endif - STATIC const uint8_t log_base2_floor[] = { 0, 1, 1, 2, 2, 2, 2, 3, From 773b0bac416907d21f0aa0a0322f355e5588c8ad Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 16:07:30 +1100 Subject: [PATCH 46/65] tests/extmod/vfs_basic: Add more tests for basic VFS functionality. --- tests/extmod/vfs_basic.py | 58 +++++++++++++++++++++++++++++++++-- tests/extmod/vfs_basic.py.exp | 17 ++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/tests/extmod/vfs_basic.py b/tests/extmod/vfs_basic.py index b481841e62..c8f4eaee92 100644 --- a/tests/extmod/vfs_basic.py +++ b/tests/extmod/vfs_basic.py @@ -25,10 +25,33 @@ class Filesystem: return ['a%d' % self.id] def chdir(self, dir): print(self.id, 'chdir', dir) + def getcwd(self): + print(self.id, 'getcwd') + return 'dir%d' % self.id + def mkdir(self, path): + print(self.id, 'mkdir', path) + def remove(self, path): + print(self.id, 'remove', path) + def rename(self, old_path, new_path): + print(self.id, 'rename', old_path, new_path) + def rmdir(self, path): + print(self.id, 'rmdir', path) + def stat(self, path): + print(self.id, 'stat', path) + return (self.id,) + def statvfs(self, path): + print(self.id, 'statvfs', path) + return (self.id,) def open(self, file, mode): print(self.id, 'open', file, mode) +# stat root dir +print(uos.stat('/')) + +# getcwd when in root dir +print(uos.getcwd()) + # basic mounting and listdir uos.mount(Filesystem(1), '/test_mnt') print(uos.listdir()) @@ -42,14 +65,43 @@ uos.mount(Filesystem(2), '/test_mnt2', readonly=True) print(uos.listdir()) print(uos.listdir('/test_mnt2')) -# chdir +# mounting over an existing mount point +try: + uos.mount(Filesystem(3), '/test_mnt2') +except OSError: + print('OSError') + +# mkdir of a mount point +try: + uos.mkdir('/test_mnt') +except OSError: + print('OSError') + +# rename across a filesystem +try: + uos.rename('/test_mnt/a', '/test_mnt2/b') +except OSError: + print('OSError') + +# delegating to mounted filesystem uos.chdir('test_mnt') print(uos.listdir()) - -# open +print(uos.getcwd()) +uos.mkdir('test_dir') +uos.remove('test_file') +uos.rename('test_file', 'test_file2') +uos.rmdir('test_dir') +print(uos.stat('test_file')) +print(uos.statvfs('/test_mnt')) open('test_file') open('test_file', 'wb') # umount uos.umount('/test_mnt') uos.umount('/test_mnt2') + +# umount a non-existent mount point +try: + uos.umount('/test_mnt') +except OSError: + print('OSError') diff --git a/tests/extmod/vfs_basic.py.exp b/tests/extmod/vfs_basic.py.exp index c9ed65191b..5104a16a69 100644 --- a/tests/extmod/vfs_basic.py.exp +++ b/tests/extmod/vfs_basic.py.exp @@ -1,3 +1,5 @@ +(16384, 0, 0, 0, 0, 0, 0, 0, 0, 0) +/ 1 mount False False ['test_mnt'] 1 listdir / @@ -8,10 +10,25 @@ ['test_mnt', 'test_mnt2'] 2 listdir / ['a2'] +3 mount False False +OSError +OSError +OSError 1 chdir / 1 listdir ['a1'] +1 getcwd +/test_mntdir1 +1 mkdir test_dir +1 remove test_file +1 rename test_file test_file2 +1 rmdir test_dir +1 stat test_file +(1,) +1 statvfs / +(1,) 1 open test_file r 1 open test_file wb 1 umount 2 umount +OSError From 4e86ca398f64ceb0351414fd91408fc8f9494907 Mon Sep 17 00:00:00 2001 From: Rami Ali Date: Tue, 14 Mar 2017 16:11:25 +1100 Subject: [PATCH 47/65] tests/extmod: Improve re1.5/recursiveloop.c test coverage. --- tests/extmod/ure1.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/extmod/ure1.py b/tests/extmod/ure1.py index b5edeeace3..a867f17515 100644 --- a/tests/extmod/ure1.py +++ b/tests/extmod/ure1.py @@ -72,6 +72,11 @@ m = re.match('^ab$', 'ab'); print(m.group(0)) m = re.match('a|b', 'b'); print(m.group(0)) m = re.match('a|b|c', 'c'); print(m.group(0)) +# Case where anchors fail to match +r = re.compile("^b|b$") +m = r.search("abc") +print(m) + try: re.compile("*") except: From 77cbd173df449e8e6eb79cba75b341f1721be377 Mon Sep 17 00:00:00 2001 From: Rami Ali Date: Tue, 14 Mar 2017 17:58:43 +1100 Subject: [PATCH 48/65] tests: Improve binary.c test coverage. --- tests/unix/extra_coverage.py.exp | 3 +++ unix/coverage.c | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index ac59971a50..32117aba44 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -43,6 +43,9 @@ Warning: test ? +1e+00 +1e+00 +# binary +122 +456 0123456789 b'0123456789' 7300 7300 diff --git a/unix/coverage.c b/unix/coverage.c index 5b0c8d7a06..ca236c4303 100644 --- a/unix/coverage.c +++ b/unix/coverage.c @@ -10,6 +10,7 @@ #include "py/emit.h" #include "py/formatfloat.h" #include "py/stream.h" +#include "py/binary.h" #if defined(MICROPY_UNIX_COVERAGE) @@ -278,6 +279,19 @@ STATIC mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "%s\n", buf2); } + // binary + { + mp_printf(&mp_plat_print, "# binary\n"); + + // call function with float and double typecodes + float far[1]; + double dar[1]; + mp_binary_set_val_array_from_int('f', far, 0, 123); + mp_printf(&mp_plat_print, "%.0f\n", (double)far[0]); + mp_binary_set_val_array_from_int('d', dar, 0, 456); + mp_printf(&mp_plat_print, "%.0lf\n", dar[0]); + } + mp_obj_streamtest_t *s = m_new_obj(mp_obj_streamtest_t); s->base.type = &mp_type_stest_fileio; s->buf = NULL; From 05fec17d9b126ee680095110fd520162669a6ce7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 18:27:43 +1100 Subject: [PATCH 49/65] tests/basics/struct_micropython: Add test for 'S' typecode in ustruct. The 'S' typecode is a uPy extension so it should be grouped with the other extension (namely 'O' typecode). Testing 'S' needs uctypes which is an extmod module and not always available, so this test is made optional and will only be run on ports that have (u)struct and uctypes. Otherwise it will be silently skipped. --- tests/basics/struct_micropython.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/basics/struct_micropython.py b/tests/basics/struct_micropython.py index e3b0ea5086..53306dad67 100644 --- a/tests/basics/struct_micropython.py +++ b/tests/basics/struct_micropython.py @@ -18,3 +18,16 @@ o = A() s = struct.pack(" Date: Tue, 14 Mar 2017 21:53:46 +1100 Subject: [PATCH 50/65] lib/utils/pyexec: Fix bug with pyexec_file not setting flag for source. --- lib/utils/pyexec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index 80a7282b14..112cfe5925 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -495,7 +495,7 @@ friendly_repl_reset: #endif // MICROPY_ENABLE_COMPILER int pyexec_file(const char *filename) { - return parse_compile_execute(filename, MP_PARSE_FILE_INPUT, 0); + return parse_compile_execute(filename, MP_PARSE_FILE_INPUT, EXEC_FLAG_SOURCE_IS_FILENAME); } #if MICROPY_MODULE_FROZEN From 923ec1169f2c14bd97ffbc01c5a78d31c6e9eded Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 22:07:52 +1100 Subject: [PATCH 51/65] tests/run-tests: Re-instate skipping of doubleprec test on pyboard. --- tests/run-tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-tests b/tests/run-tests index 6a78c27649..b1bb7dd846 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -261,7 +261,7 @@ def run_tests(pyb, tests, args): if pyb is not None: skip_tests.add('basics/exception_chain.py') # warning is not printed skip_tests.add('float/float_divmod.py') # tested by float/float_divmod_relaxed.py instead - skip_tests.add('float/float2int_doubleprec.py') # requires double precision floating point to work + skip_tests.add('float/float2int_doubleprec_intbig.py') # requires double precision floating point to work skip_tests.add('micropython/meminfo.py') # output is very different to PC output skip_tests.add('extmod/machine_mem.py') # raw memory access not supported From a49a96bb5d683bb2a721da70c4c3f049cf6ba2f3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 22:08:37 +1100 Subject: [PATCH 52/65] tests/extmod/vfs_basic: Unmount all existing devices before doing test. This is so the test can run successfully on targets that already have something mounted. --- tests/extmod/vfs_basic.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/extmod/vfs_basic.py b/tests/extmod/vfs_basic.py index c8f4eaee92..83c83fd227 100644 --- a/tests/extmod/vfs_basic.py +++ b/tests/extmod/vfs_basic.py @@ -46,6 +46,10 @@ class Filesystem: print(self.id, 'open', file, mode) +# first we umount any existing mount points the target may have +for path in uos.listdir('/'): + uos.umount('/' + path) + # stat root dir print(uos.stat('/')) From 8a57cacd78d3449694f2abbffd6e0ec4444aa8b1 Mon Sep 17 00:00:00 2001 From: Rami Ali Date: Tue, 14 Mar 2017 19:00:56 +1100 Subject: [PATCH 53/65] tests/extmod: Improve tinfgzip.c test coverage. --- tests/extmod/uzlib_decompio_gz.py | 10 ++++++++++ tests/extmod/uzlib_decompio_gz.py.exp | 2 ++ 2 files changed, 12 insertions(+) diff --git a/tests/extmod/uzlib_decompio_gz.py b/tests/extmod/uzlib_decompio_gz.py index 5ab25354cd..7572e96939 100644 --- a/tests/extmod/uzlib_decompio_gz.py +++ b/tests/extmod/uzlib_decompio_gz.py @@ -20,6 +20,16 @@ print(inp.read(1)) print(inp.read()) print(buf.seek(0, 1)) +# Check FHCRC field +buf = io.BytesIO(b'\x1f\x8b\x08\x02\x99\x0c\xe5W\x00\x03\x00\x00\xcbH\xcd\xc9\xc9\x07\x00\x86\xa6\x106\x05\x00\x00\x00') +inp = zlib.DecompIO(buf, 16 + 8) +print(inp.read()) + +# Check FEXTRA field +buf = io.BytesIO(b'\x1f\x8b\x08\x04\x99\x0c\xe5W\x00\x03\x01\x00X\xcbH\xcd\xc9\xc9\x07\x00\x86\xa6\x106\x05\x00\x00\x00') +inp = zlib.DecompIO(buf, 16 + 8) +print(inp.read()) + # broken header buf = io.BytesIO(b'\x1f\x8c\x08\x08\x99\x0c\xe5W\x00\x03hello\x00\xcbH\xcd\xc9\xc9\x07\x00\x86\xa6\x106\x05\x00\x00\x00') try: diff --git a/tests/extmod/uzlib_decompio_gz.py.exp b/tests/extmod/uzlib_decompio_gz.py.exp index 2330580f8c..20a30c82a3 100644 --- a/tests/extmod/uzlib_decompio_gz.py.exp +++ b/tests/extmod/uzlib_decompio_gz.py.exp @@ -7,5 +7,7 @@ b'lo' b'' b'' 31 +b'hello' +b'hello' ValueError OSError(22,) From a5a84e1f85e795338d462a32632709d7b297d05c Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 23:00:53 +1100 Subject: [PATCH 54/65] py/emitnative: Use assertions and mp_not_implemented correctly. Assertions are used to check expressions that should always be true, and mp_not_implemented is used for code that can be reached. --- py/emitnative.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/py/emitnative.c b/py/emitnative.c index 9176f0a3c2..e4c95df623 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -385,11 +385,9 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop ASM_MOV_REG_REG(emit->as, REG_LOCAL_2, REG_ARG_2); } else if (i == 2) { ASM_MOV_REG_REG(emit->as, REG_LOCAL_3, REG_ARG_3); - } else if (i == 3) { - ASM_MOV_REG_TO_LOCAL(emit->as, REG_ARG_4, i - REG_LOCAL_NUM); } else { - // TODO not implemented - mp_not_implemented("more than 4 viper args"); + assert(i == 3); // should be true; max 4 args is checked above + ASM_MOV_REG_TO_LOCAL(emit->as, REG_ARG_4, i - REG_LOCAL_NUM); } } #endif @@ -527,9 +525,7 @@ STATIC void emit_native_end_pass(emit_t *emit) { ASM_END_PASS(emit->as); // check stack is back to zero size - if (emit->stack_size != 0) { - mp_printf(&mp_plat_print, "ERROR: stack size not back to zero; got %d\n", emit->stack_size); - } + assert(emit->stack_size == 0); if (emit->pass == MP_PASS_EMIT) { void *f = mp_asm_base_get_code(&emit->as->base); @@ -867,7 +863,7 @@ STATIC void emit_get_stack_pointer_to_reg_for_pop(emit_t *emit, mp_uint_t reg_de break; default: // not handled - assert(0); + mp_not_implemented("conversion to object"); } } @@ -2202,7 +2198,8 @@ STATIC void emit_native_call_function(emit_t *emit, mp_uint_t n_positional, mp_u emit_post_top_set_vtype(emit, vtype_cast); break; default: - assert(!"TODO: convert obj to int"); + // this can happen when casting a cast: int(int) + mp_not_implemented("casting"); } } else { assert(vtype_fun == VTYPE_PYOBJ); From e29f704b6776ac9f7b95bb35d53d65948e995b38 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Mar 2017 22:52:50 +1100 Subject: [PATCH 55/65] tests/micropython/viper_error: Add more tests to improve coverage. --- tests/micropython/viper_error.py | 24 +++++++++++++++++++++++- tests/micropython/viper_error.py.exp | 10 ++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/tests/micropython/viper_error.py b/tests/micropython/viper_error.py index 116bd4ea03..8472572854 100644 --- a/tests/micropython/viper_error.py +++ b/tests/micropython/viper_error.py @@ -3,13 +3,19 @@ def test(code): try: exec(code) - except (SyntaxError, ViperTypeError) as e: + except (SyntaxError, ViperTypeError, NotImplementedError) as e: print(repr(e)) # viper: annotations must be identifiers test("@micropython.viper\ndef f(a:1): pass") test("@micropython.viper\ndef f() -> 1: pass") +# unknown type +test("@micropython.viper\ndef f(x:unknown_type): pass") + +# too many arguments +test("@micropython.viper\ndef f(a, b, c, d, e): pass") + # local used before type known test(""" @micropython.viper @@ -49,6 +55,9 @@ test("@micropython.viper\ndef f(): 1[x]") # can't store test("@micropython.viper\ndef f(): 1[0] = 1") test("@micropython.viper\ndef f(): 1[x] = 1") +test("@micropython.viper\ndef f(x:int): x[0] = x") +test("@micropython.viper\ndef f(x:ptr32): x[0] = None") +test("@micropython.viper\ndef f(x:ptr32): x[x] = None") # must raise an object test("@micropython.viper\ndef f(): raise 1") @@ -57,3 +66,16 @@ test("@micropython.viper\ndef f(): raise 1") test("@micropython.viper\ndef f(x:int): +x") test("@micropython.viper\ndef f(x:int): -x") test("@micropython.viper\ndef f(x:int): ~x") + +# binary op not implemented +test("@micropython.viper\ndef f(x:int): res = x in x") + +# yield (from) not implemented +test("@micropython.viper\ndef f(): yield") +test("@micropython.viper\ndef f(): yield from f") + +# passing a ptr to a Python function not implemented +test("@micropython.viper\ndef f(): print(ptr(1))") + +# cast of a casting identifier not implemented +test("@micropython.viper\ndef f(): int(int)") diff --git a/tests/micropython/viper_error.py.exp b/tests/micropython/viper_error.py.exp index 1afcd4bdbe..96be5a5902 100644 --- a/tests/micropython/viper_error.py.exp +++ b/tests/micropython/viper_error.py.exp @@ -1,5 +1,7 @@ SyntaxError('parameter annotation must be an identifier',) SyntaxError('return annotation must be an identifier',) +ViperTypeError("unknown type 'unknown_type'",) +ViperTypeError("Viper functions don't currently support more than 4 arguments",) ViperTypeError("local 'x' used before type known",) ViperTypeError("local 'x' has type 'int' but source is 'object'",) ViperTypeError("can't implicitly convert 'ptr' to 'bool'",) @@ -9,7 +11,15 @@ ViperTypeError("can't load from 'int'",) ViperTypeError("can't load from 'int'",) ViperTypeError("can't store to 'int'",) ViperTypeError("can't store to 'int'",) +ViperTypeError("can't store to 'int'",) +ViperTypeError("can't store 'None'",) +ViperTypeError("can't store 'None'",) ViperTypeError('must raise an object',) ViperTypeError('unary op __pos__ not implemented',) ViperTypeError('unary op __neg__ not implemented',) ViperTypeError('unary op __invert__ not implemented',) +ViperTypeError('binary op __contains__ not implemented',) +NotImplementedError('native yield',) +NotImplementedError('native yield from',) +NotImplementedError('conversion to object',) +NotImplementedError('casting',) From 8cd4911e636092b25dbb6817e67a008d3faeeda9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 15 Mar 2017 10:03:22 +1100 Subject: [PATCH 56/65] py/emitnative: Remove obsolete commented out code. --- py/emitnative.c | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/py/emitnative.c b/py/emitnative.c index e4c95df623..55eb6cadfc 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -591,38 +591,9 @@ STATIC void emit_native_set_source_line(emit_t *emit, mp_uint_t source_line) { (void)source_line; } -/* -STATIC void emit_pre_raw(emit_t *emit, int stack_size_delta) { - adjust_stack(emit, stack_size_delta); - emit->last_emit_was_return_value = false; -} -*/ - // this must be called at start of emit functions STATIC void emit_native_pre(emit_t *emit) { emit->last_emit_was_return_value = false; - // settle the stack - /* - if (regs_needed != 0) { - for (int i = 0; i < emit->stack_size; i++) { - switch (emit->stack_info[i].kind) { - case STACK_VALUE: - break; - - case STACK_REG: - // TODO only push reg if in regs_needed - emit->stack_info[i].kind = STACK_VALUE; - ASM_MOV_REG_TO_LOCAL(emit->as, emit->stack_info[i].data.u_reg, emit->stack_start + i); - break; - - case STACK_IMM: - // don't think we ever need to push imms for settling - //ASM_MOV_IMM_TO_LOCAL(emit->last_imm, emit->stack_start + i); - break; - } - } - } - */ } // depth==0 is top, depth==1 is before top, etc @@ -1007,9 +978,7 @@ STATIC void emit_native_load_const_str(emit_t *emit, qstr qst) { // do native array access. For now we just load them as any other object. /* if (emit->do_viper_types) { - // not implemented properly // load a pointer to the asciiz string? - assert(0); emit_post_push_imm(emit, VTYPE_PTR, (mp_uint_t)qstr_str(qst)); } else */ @@ -1839,12 +1808,6 @@ STATIC void emit_native_pop_block(emit_t *emit) { STATIC void emit_native_pop_except(emit_t *emit) { (void)emit; - /* - emit_native_pre(emit); - emit_call(emit, MP_F_NLR_POP); - adjust_stack(emit, -(mp_int_t)(sizeof(nlr_buf_t) / sizeof(mp_uint_t))); - emit_post(emit); - */ } STATIC void emit_native_unary_op(emit_t *emit, mp_unary_op_t op) { @@ -2256,7 +2219,6 @@ STATIC void emit_native_return_value(emit_t *emit) { assert(vtype == VTYPE_PYOBJ); } emit->last_emit_was_return_value = true; - //ASM_BREAK_POINT(emit->as); // to insert a break-point for debugging ASM_EXIT(emit->as); } From d65371538d8ecdc64691f1c522b5b9ecc1299c7f Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 15 Mar 2017 17:25:12 +1100 Subject: [PATCH 57/65] py/mpprint: Fix int formatting so "+" is printed for 0-valued integer. --- py/mpprint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/mpprint.c b/py/mpprint.c index 72d1c55ca0..4bc45fef4d 100644 --- a/py/mpprint.c +++ b/py/mpprint.c @@ -222,7 +222,7 @@ int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char char prefix_buf[4]; char *prefix = prefix_buf; - if (mp_obj_int_sign(x) > 0) { + if (mp_obj_int_sign(x) >= 0) { if (flags & PF_FLAG_SHOW_SIGN) { *prefix++ = '+'; } else if (flags & PF_FLAG_SPACE_SIGN) { From 3a0b2be6e28b5bd8f44bd4d05fa99850630a9338 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 15 Mar 2017 17:25:46 +1100 Subject: [PATCH 58/65] tests/basics/string_format2: Adjust comment now that tests succeed. --- tests/basics/string_format2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/basics/string_format2.py b/tests/basics/string_format2.py index e211535be4..881ff4f804 100644 --- a/tests/basics/string_format2.py +++ b/tests/basics/string_format2.py @@ -1,6 +1,6 @@ # comprehensive functionality test for {} format string -int_tests = False # these take a while, and some give wrong results +int_tests = False # these take a while char_tests = True str_tests = True From b154468b085dad53de8fdef09ec42c8518475556 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 15 Mar 2017 17:31:17 +1100 Subject: [PATCH 59/65] tests/basics: Add test for string module formatting with int argument. --- tests/basics/string_format_modulo_int.py | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/basics/string_format_modulo_int.py diff --git a/tests/basics/string_format_modulo_int.py b/tests/basics/string_format_modulo_int.py new file mode 100644 index 0000000000..c97bca9bdb --- /dev/null +++ b/tests/basics/string_format_modulo_int.py @@ -0,0 +1,7 @@ +# test string modulo formatting with int values + +# test + option with various amount of padding +for pad in ('', ' ', '0'): + for n in (1, 2, 3): + for val in (-1, 0, 1): + print(('%+' + pad + str(n) + 'd') % val) From ecb4357fe1c417e7349a56affcee5e2ccdf0d421 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 15 Mar 2017 17:34:47 +1100 Subject: [PATCH 60/65] tests/basics: Move string-modulo-format int tests to dedicated file. --- tests/basics/string_format_modulo.py | 32 ---------------------- tests/basics/string_format_modulo_int.py | 34 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/tests/basics/string_format_modulo.py b/tests/basics/string_format_modulo.py index f00502457e..77bbcfbe36 100644 --- a/tests/basics/string_format_modulo.py +++ b/tests/basics/string_format_modulo.py @@ -33,38 +33,6 @@ print("%c" % 48) print("%c" % 'a') print("%10s" % 'abc') print("%-10s" % 'abc') -print("%d" % 10) -print("%+d" % 10) -print("% d" % 10) -print("%d" % -10) -print("%d" % True) -print("%i" % -10) -print("%i" % True) -print("%u" % -10) -print("%u" % True) -print("%x" % 18) -print("%o" % 18) -print("%X" % 18) -print("%#x" % 18) -print("%#X" % 18) -print("%#6o" % 18) -print("%#6x" % 18) -print("%#06x" % 18) - -print("%*d" % (5, 10)) -print("%*.*d" % (2, 2, 20)) -print("%*.*d" % (5, 8, 20)) - -print(">%8.4d<" % -12) -print(">% 8.4d<" % -12) -print(">%+8.4d<" % 12) -print(">%+8.4d<" % -12) -print(">%08.4d<" % -12) -print(">%08.4d<" % 12) -print(">%-8.4d<" % -12) -print(">%-08.4d<" % -12) -print(">%-+08.4d<" % -12) -print(">%-+08.4d<" % 12) # Should be able to print dicts; in this case they aren't used # to lookup keywords in formats like %(foo)s diff --git a/tests/basics/string_format_modulo_int.py b/tests/basics/string_format_modulo_int.py index c97bca9bdb..d1f29db220 100644 --- a/tests/basics/string_format_modulo_int.py +++ b/tests/basics/string_format_modulo_int.py @@ -1,5 +1,39 @@ # test string modulo formatting with int values +# basic cases +print("%d" % 10) +print("%+d" % 10) +print("% d" % 10) +print("%d" % -10) +print("%d" % True) +print("%i" % -10) +print("%i" % True) +print("%u" % -10) +print("%u" % True) +print("%x" % 18) +print("%o" % 18) +print("%X" % 18) +print("%#x" % 18) +print("%#X" % 18) +print("%#6o" % 18) +print("%#6x" % 18) +print("%#06x" % 18) + +# with * +print("%*d" % (5, 10)) +print("%*.*d" % (2, 2, 20)) +print("%*.*d" % (5, 8, 20)) + +# precision +for val in (-12, 12): + print(">%8.4d<" % val) + print(">% 8.4d<" % val) + print(">%+8.4d<" % val) + print(">%08.4d<" % val) + print(">%-8.4d<" % val) + print(">%-08.4d<" % val) + print(">%-+08.4d<" % val) + # test + option with various amount of padding for pad in ('', ' ', '0'): for n in (1, 2, 3): From 23a693ec2d8c2a194f61482dc0e1adb070fb6ad4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 15 Mar 2017 21:50:48 +1100 Subject: [PATCH 61/65] py/mkrules.mk: Remove special check for "-B" in qstr auto generation. When make is passed "-B" it seems that everything is considered out-of-date and so $? expands to all prerequisites. Thus there is no need for a special check to see if $? is emtpy. --- py/mkrules.mk | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/py/mkrules.mk b/py/mkrules.mk index b71450a21d..bc6389144b 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -71,12 +71,7 @@ $(OBJ): | $(HEADER_BUILD)/qstrdefs.generated.h $(HEADER_BUILD)/mpversion.h $(HEADER_BUILD)/qstr.i.last: $(SRC_QSTR) | $(HEADER_BUILD)/mpversion.h $(ECHO) "GEN $@" - $(Q)if [ "$?" = "" ]; then \ - echo "QSTR Looks like -B used, trying to emulate"; \ - $(CPP) $(QSTR_GEN_EXTRA_CFLAGS) $(CFLAGS) $^ >$(HEADER_BUILD)/qstr.i.last; \ - else \ - $(CPP) $(QSTR_GEN_EXTRA_CFLAGS) $(CFLAGS) $? >$(HEADER_BUILD)/qstr.i.last; \ - fi + $(Q)$(CPP) $(QSTR_GEN_EXTRA_CFLAGS) $(CFLAGS) $? >$(HEADER_BUILD)/qstr.i.last; $(HEADER_BUILD)/qstr.split: $(HEADER_BUILD)/qstr.i.last $(ECHO) "GEN $@" From fcab4356077435f0f4f9b068acb76e21d6cc20a6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 15 Mar 2017 21:54:56 +1100 Subject: [PATCH 62/65] docs/library/framebuf: Fix typo in bit-width for MVLSB description. --- docs/library/framebuf.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/library/framebuf.rst b/docs/library/framebuf.rst index 657ad461be..91fc362fdd 100644 --- a/docs/library/framebuf.rst +++ b/docs/library/framebuf.rst @@ -39,7 +39,7 @@ Constructors - `height` is the height of the FrameBuffer in pixels - `format` specifies the type of pixel used in the FrameBuffer; valid values are ``framebuf.MVLSB``, ``framebuf.RGB565`` - and ``framebuf.GS4_HMSB``. MVLSB is monochrome 8-bit color, + and ``framebuf.GS4_HMSB``. MVLSB is monochrome 1-bit color, RGB565 is RGB 16-bit color, and GS4_HMSB is grayscale 4-bit color. Where a color value c is passed to a method, c is a small integer with an encoding that is dependent on the format of the FrameBuffer. From 7b7ff60f91889f8d601d4b4c6a36e9511fdd40dd Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 15 Mar 2017 22:20:30 +1100 Subject: [PATCH 63/65] travis: Change an stmhal rule to build PYBV11 instead of default PYBV10. This allows to test the PYBV11 target as well as the network drivers without adding another rule. It also removes the need to use -B, side-stepping the issue of whether or not -B works with qstr auto generation. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2a5c597eb7..76aa965d2d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,7 @@ script: - make -C bare-arm - make -C qemu-arm test - make -C stmhal - - make -C stmhal -B MICROPY_PY_WIZNET5K=1 MICROPY_PY_CC3K=1 + - make -C stmhal BOARD=PYBV11 MICROPY_PY_WIZNET5K=1 MICROPY_PY_CC3K=1 - make -C stmhal BOARD=STM32F7DISC - make -C stmhal BOARD=STM32L476DISC - make -C teensy From d279bcff8a18c2153557c2e83cd397f266b5199d Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 16 Mar 2017 14:30:04 +1100 Subject: [PATCH 64/65] py/objstr: Fix eager optimisation of str/bytes addition. The RHS can only be returned if it is the same type as the LHS. --- py/objstr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/objstr.c b/py/objstr.c index 17d06f88e8..2331004a5b 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -354,7 +354,7 @@ mp_obj_t mp_obj_str_binary_op(mp_uint_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { switch (op) { case MP_BINARY_OP_ADD: case MP_BINARY_OP_INPLACE_ADD: { - if (lhs_len == 0) { + if (lhs_len == 0 && mp_obj_get_type(rhs_in) == lhs_type) { return rhs_in; } if (rhs_len == 0) { From eeff0c352845649a3ae7b2e325361744d2db114b Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 16 Mar 2017 14:31:03 +1100 Subject: [PATCH 65/65] tests/basics/bytes_add: Add tests for optimised bytes addition. --- tests/basics/bytes_add.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/basics/bytes_add.py b/tests/basics/bytes_add.py index 5432d01e58..ebccf0662d 100644 --- a/tests/basics/bytes_add.py +++ b/tests/basics/bytes_add.py @@ -2,3 +2,7 @@ print(b"123" + b"456") print(b"123" + bytearray(2)) + +print(b"123" + b"") # RHS is empty, can be optimised +print(b"" + b"123") # LHS is empty, can be optimised +print(b"" + bytearray(1)) # LHS is empty but can't be optimised