diff options
-rw-r--r-- | ports/esp32/modesp32.c | 4 | ||||
-rw-r--r-- | ports/rp2/CMakeLists.txt | 20 | ||||
-rw-r--r-- | ports/rp2/rp2_flash.c | 18 | ||||
-rw-r--r-- | py/modio.c | 9 | ||||
-rw-r--r-- | py/repl.c | 4 | ||||
-rw-r--r-- | pyproject.toml | 2 | ||||
-rw-r--r-- | tests/basics/io_buffered_writer.py | 24 | ||||
-rw-r--r-- | tests/basics/io_buffered_writer.py.exp | 5 | ||||
-rw-r--r-- | tests/cmdline/repl_autocomplete_underscore.py | 33 | ||||
-rw-r--r-- | tests/cmdline/repl_autocomplete_underscore.py.exp | 41 | ||||
-rw-r--r-- | tests/cmdline/repl_paste.py | 90 | ||||
-rw-r--r-- | tests/cmdline/repl_paste.py.exp | 133 | ||||
-rw-r--r-- | tests/extmod/platform_basic.py | 8 | ||||
-rw-r--r-- | tests/ports/unix/extra_coverage.py.exp | 20 | ||||
-rwxr-xr-x | tests/run-tests.py | 4 |
15 files changed, 392 insertions, 23 deletions
diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index 26a39d4451..fcd6ed9fa8 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -50,9 +50,11 @@ #if SOC_TOUCH_SENSOR_SUPPORTED static mp_obj_t esp32_wake_on_touch(const mp_obj_t wake) { + #if SOC_PM_SUPPORT_EXT0_WAKEUP if (machine_rtc_config.ext0_pin != -1) { mp_raise_ValueError(MP_ERROR_TEXT("no resources")); } + #endif machine_rtc_config.wake_on_touch = mp_obj_is_true(wake); return mp_const_none; @@ -137,9 +139,11 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(esp32_wake_on_ext1_obj, 0, esp32_wake_on_ext1) #if SOC_ULP_SUPPORTED static mp_obj_t esp32_wake_on_ulp(const mp_obj_t wake) { + #if SOC_PM_SUPPORT_EXT0_WAKEUP if (machine_rtc_config.ext0_pin != -1) { mp_raise_ValueError(MP_ERROR_TEXT("no resources")); } + #endif machine_rtc_config.wake_on_ulp = mp_obj_is_true(wake); return mp_const_none; } diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index cf9f180792..49ba8d77d6 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -610,14 +610,18 @@ endif() # todo this is a bit brittle, but we want to move a few source files into RAM (which requires # a linker script modification) until we explicitly add macro calls around the function # defs to move them into RAM. -if (PICO_ON_DEVICE AND NOT PICO_NO_FLASH AND NOT PICO_COPY_TO_RAM) +if (NOT MICROPY_BOARD_LINKER_SCRIPT) if(PICO_RP2040) - pico_set_linker_script(${MICROPY_TARGET} ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2040.ld) + set(MICROPY_BOARD_LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2040.ld) elseif(PICO_RP2350) - pico_set_linker_script(${MICROPY_TARGET} ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2350.ld) + set(MICROPY_BOARD_LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2350.ld) endif() endif() +if (PICO_ON_DEVICE AND NOT PICO_NO_FLASH AND NOT PICO_COPY_TO_RAM) + pico_set_linker_script(${MICROPY_TARGET} ${MICROPY_BOARD_LINKER_SCRIPT}) +endif() + pico_add_extra_outputs(${MICROPY_TARGET}) pico_find_compiler_with_triples(PICO_COMPILER_SIZE "${PICO_GCC_TRIPLE}" size) @@ -654,9 +658,13 @@ if(NOT PICO_NUM_EXT_GPIOS) set(PICO_NUM_EXT_GPIOS 10) endif() -if(EXISTS "${MICROPY_BOARD_DIR}/pins.csv") - set(GEN_PINS_BOARD_CSV "${MICROPY_BOARD_DIR}/pins.csv") - set(GEN_PINS_CSV_ARG --board-csv "${GEN_PINS_BOARD_CSV}") +if(NOT MICROPY_BOARD_PINS) + set(MICROPY_BOARD_PINS "${MICROPY_BOARD_DIR}/pins.csv") +endif() + +if(EXISTS "${MICROPY_BOARD_PINS}") + set(GEN_PINS_BOARD_CSV "${MICROPY_BOARD_PINS}") + set(GEN_PINS_CSV_ARG --board-csv "${MICROPY_BOARD_PINS}") endif() target_sources(${MICROPY_TARGET} PRIVATE diff --git a/ports/rp2/rp2_flash.c b/ports/rp2/rp2_flash.c index a9beabf051..d6b9e13653 100644 --- a/ports/rp2/rp2_flash.c +++ b/ports/rp2/rp2_flash.c @@ -49,6 +49,19 @@ static_assert(MICROPY_HW_ROMFS_BYTES % 4096 == 0, "ROMFS size must be a multiple of 4K"); static_assert(MICROPY_HW_FLASH_STORAGE_BYTES % 4096 == 0, "Flash storage size must be a multiple of 4K"); +#ifndef MICROPY_HW_FLASH_MAX_FREQ +// Emulate Pico SDK's SYS_CLK_HZ / PICO_FLASH_SPI_CLKDIV behaviour by default. +// On RP2040 if PICO_USE_FASTEST_SUPPORTED_CLOCK is set then SYS_CLK_HZ can be +// 200MHz, potentially putting timings derived from PICO_FLASH_SPI_CLKDIV +// out of range. +#ifdef PICO_FLASH_SPI_CLKDIV +#define MICROPY_HW_FLASH_MAX_FREQ (SYS_CLK_HZ / PICO_FLASH_SPI_CLKDIV) +#else +// A default PICO_FLASH_SPI_CLKDIV of 4 is set in boot2_generic_03h.S +#define MICROPY_HW_FLASH_MAX_FREQ (SYS_CLK_HZ / 4) +#endif +#endif + #ifndef MICROPY_HW_FLASH_STORAGE_BASE #define MICROPY_HW_FLASH_STORAGE_BASE (PICO_FLASH_SIZE_BYTES - MICROPY_HW_FLASH_STORAGE_BYTES) #endif @@ -104,9 +117,8 @@ static bool use_multicore_lockout(void) { // and core1 locked out if relevant. static void __no_inline_not_in_flash_func(rp2_flash_set_timing_internal)(int clock_hz) { - // Use the minimum divisor assuming a 133MHz flash. - const int max_flash_freq = 133000000; - int divisor = (clock_hz + max_flash_freq - 1) / max_flash_freq; + // Use the minimum divisor based upon our target MICROPY_HW_FLASH_MAX_FREQ + int divisor = (clock_hz + MICROPY_HW_FLASH_MAX_FREQ - 1) / MICROPY_HW_FLASH_MAX_FREQ; #if PICO_RP2350 // Make sure flash is deselected - QMI doesn't appear to have a busy flag(!) diff --git a/py/modio.c b/py/modio.c index d3e563dbcf..9aeb42d30a 100644 --- a/py/modio.c +++ b/py/modio.c @@ -169,12 +169,13 @@ static mp_obj_t bufwriter_flush(mp_obj_t self_in) { int err; mp_uint_t out_sz = mp_stream_write_exactly(self->stream, self->buf, self->len, &err); (void)out_sz; - // TODO: try to recover from a case of non-blocking stream, e.g. move - // remaining chunk to the beginning of buffer. - assert(out_sz == self->len); - self->len = 0; if (err != 0) { mp_raise_OSError(err); + } else { + // TODO: try to recover from a case of non-blocking stream, e.g. move + // remaining chunk to the beginning of buffer. + assert(out_sz == self->len); + self->len = 0; } } @@ -218,6 +218,10 @@ static void print_completions(const mp_print_t *print, for (qstr q = q_first; q <= q_last; ++q) { size_t d_len; const char *d_str = (const char *)qstr_data(q, &d_len); + // filter out words that begin with underscore unless there's already a partial match + if (s_len == 0 && d_str[0] == '_') { + continue; + } if (s_len <= d_len && strncmp(s_start, d_str, s_len) == 0) { if (test_qstr(obj, q)) { int gap = (line_len + WORD_SLOT_LEN - 1) / WORD_SLOT_LEN * WORD_SLOT_LEN - line_len; diff --git a/pyproject.toml b/pyproject.toml index 263b120d3f..0dd15d06c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,10 +29,12 @@ target-version = "py37" [tool.ruff.lint] exclude = [ # Ruff finds Python SyntaxError in these files "tests/cmdline/repl_autocomplete.py", + "tests/cmdline/repl_autocomplete_underscore.py", "tests/cmdline/repl_autoindent.py", "tests/cmdline/repl_basic.py", "tests/cmdline/repl_cont.py", "tests/cmdline/repl_emacs_keys.py", + "tests/cmdline/repl_paste.py", "tests/cmdline/repl_words_move.py", "tests/feature_check/repl_emacs_check.py", "tests/feature_check/repl_words_move_check.py", diff --git a/tests/basics/io_buffered_writer.py b/tests/basics/io_buffered_writer.py index 5c065f158a..60cf2c837d 100644 --- a/tests/basics/io_buffered_writer.py +++ b/tests/basics/io_buffered_writer.py @@ -28,3 +28,27 @@ print(bts.getvalue()) # hashing a BufferedWriter print(type(hash(buf))) + +# Test failing flush() +class MyIO(io.IOBase): + def __init__(self): + self.count = 0 + + def write(self, buf): + self.count += 1 + if self.count < 3: + return None + print("writing", buf) + return len(buf) + + +buf = io.BufferedWriter(MyIO(), 8) + +buf.write(b"foobar") + +for _ in range(4): + try: + buf.flush() + print("flushed") + except OSError: + print("OSError") diff --git a/tests/basics/io_buffered_writer.py.exp b/tests/basics/io_buffered_writer.py.exp index 2209348f5a..d61eb148b4 100644 --- a/tests/basics/io_buffered_writer.py.exp +++ b/tests/basics/io_buffered_writer.py.exp @@ -4,3 +4,8 @@ b'foobarfoobar' b'foobarfoobar' b'foo' <class 'int'> +OSError +OSError +writing bytearray(b'foobar') +flushed +flushed diff --git a/tests/cmdline/repl_autocomplete_underscore.py b/tests/cmdline/repl_autocomplete_underscore.py new file mode 100644 index 0000000000..98bbb69920 --- /dev/null +++ b/tests/cmdline/repl_autocomplete_underscore.py @@ -0,0 +1,33 @@ +# Test REPL autocompletion filtering of underscore attributes + +# Start paste mode +{\x05} +class TestClass: + def __init__(self): + self.public_attr = 1 + self._private_attr = 2 + self.__very_private = 3 + + def public_method(self): + pass + + def _private_method(self): + pass + + @property + def public_property(self): + return 42 + + @property + def _private_property(self): + return 99 + +{\x04} +# Paste executed + +# Create an instance +obj = TestClass() + +# Test tab completion on the instance +# The tab character after `obj.` and 'a' below triggers the completions +obj.{\x09}{\x09}a{\x09} diff --git a/tests/cmdline/repl_autocomplete_underscore.py.exp b/tests/cmdline/repl_autocomplete_underscore.py.exp new file mode 100644 index 0000000000..35617554f5 --- /dev/null +++ b/tests/cmdline/repl_autocomplete_underscore.py.exp @@ -0,0 +1,41 @@ +MicroPython \.\+ version +Use Ctrl-D to exit, Ctrl-E for paste mode +>>> # Test REPL autocompletion filtering of underscore attributes +>>> +>>> # Start paste mode +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== class TestClass: +=== def __init__(self): +=== self.public_attr = 1 +=== self._private_attr = 2 +=== self.__very_private = 3 +=== +=== def public_method(self): +=== pass +=== +=== def _private_method(self): +=== pass +=== +=== @property +=== def public_property(self): +=== return 42 +=== +=== @property +=== def _private_property(self): +=== return 99 +=== +=== +>>> # Paste executed +>>> +>>> # Create an instance +>>> obj = TestClass() +>>> +>>> # Test tab completion on the instance +>>> # The tab character after `obj.` and 'a' below triggers the completions +>>> obj.public_ +public_attr public_method public_property +>>> obj.public_attr +1 +>>> diff --git a/tests/cmdline/repl_paste.py b/tests/cmdline/repl_paste.py new file mode 100644 index 0000000000..7cec450fce --- /dev/null +++ b/tests/cmdline/repl_paste.py @@ -0,0 +1,90 @@ +# Test REPL paste mode functionality + +# Basic paste mode with a simple function +{\x05} +def hello(): + print('Hello from paste mode!') +hello() +{\x04} + +# Paste mode with multiple indentation levels +{\x05} +def calculate(n): + if n > 0: + for i in range(n): + if i % 2 == 0: + print(f'Even: {i}') + else: + print(f'Odd: {i}') + else: + print('n must be positive') + +calculate(5) +{\x04} + +# Paste mode with blank lines +{\x05} +def function_with_blanks(): + print('First line') + + print('After blank line') + + + print('After two blank lines') + +function_with_blanks() +{\x04} + +# Paste mode with class definition and multiple methods +{\x05} +class TestClass: + def __init__(self, value): + self.value = value + + def display(self): + print(f'Value is: {self.value}') + + def double(self): + self.value *= 2 + return self.value + +obj = TestClass(21) +obj.display() +print(f'Doubled: {obj.double()}') +obj.display() +{\x04} + +# Paste mode with exception handling +{\x05} +try: + x = 1 / 0 +except ZeroDivisionError: + print('Caught division by zero') +finally: + print('Finally block executed') +{\x04} + +# Cancel paste mode with Ctrl-C +{\x05} +print('This should not execute') +{\x03} + +# Normal REPL still works after cancelled paste +print('Back to normal REPL') + +# Paste mode with syntax error +{\x05} +def bad_syntax(: + print('Missing parameter') +{\x04} + +# Paste mode with runtime error +{\x05} +def will_error(): + undefined_variable + +will_error() +{\x04} + +# Final test to show REPL is still functioning +1 + 2 + 3 diff --git a/tests/cmdline/repl_paste.py.exp b/tests/cmdline/repl_paste.py.exp new file mode 100644 index 0000000000..22d9bd5740 --- /dev/null +++ b/tests/cmdline/repl_paste.py.exp @@ -0,0 +1,133 @@ +MicroPython \.\+ version +Use Ctrl-D to exit, Ctrl-E for paste mode +>>> # Test REPL paste mode functionality +>>> +>>> # Basic paste mode with a simple function +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== def hello(): +=== print('Hello from paste mode!') +=== hello() +=== +Hello from paste mode! +>>> +>>> # Paste mode with multiple indentation levels +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== def calculate(n): +=== if n > 0: +=== for i in range(n): +=== if i % 2 == 0: +=== print(f'Even: {i}') +=== else: +=== print(f'Odd: {i}') +=== else: +=== print('n must be positive') +=== +=== calculate(5) +=== +Even: 0 +Odd: 1 +Even: 2 +Odd: 3 +Even: 4 +>>> +>>> # Paste mode with blank lines +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== def function_with_blanks(): +=== print('First line') +=== +=== print('After blank line') +=== +=== +=== print('After two blank lines') +=== +=== function_with_blanks() +=== +First line +After blank line +After two blank lines +>>> +>>> # Paste mode with class definition and multiple methods +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== class TestClass: +=== def __init__(self, value): +=== self.value = value +=== +=== def display(self): +=== print(f'Value is: {self.value}') +=== +=== def double(self): +=== self.value *= 2 +=== return self.value +=== +=== obj = TestClass(21) +=== obj.display() +=== print(f'Doubled: {obj.double()}') +=== obj.display() +=== +Value is: 21 +Doubled: 42 +Value is: 42 +>>> +>>> # Paste mode with exception handling +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== try: +=== x = 1 / 0 +=== except ZeroDivisionError: +=== print('Caught division by zero') +=== finally: +=== print('Finally block executed') +=== +Caught division by zero +Finally block executed +>>> +>>> # Cancel paste mode with Ctrl-C +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== print('This should not execute') +=== +>>> +>>> +>>> # Normal REPL still works after cancelled paste +>>> print('Back to normal REPL') +Back to normal REPL +>>> +>>> # Paste mode with syntax error +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== def bad_syntax(: +=== print('Missing parameter') +=== +Traceback (most recent call last): + File "<stdin>", line 2 +SyntaxError: invalid syntax +>>> +>>> # Paste mode with runtime error +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== def will_error(): +=== undefined_variable +=== +=== will_error() +=== +Traceback (most recent call last): + File "<stdin>", line 5, in <module> + File "<stdin>", line 3, in will_error +NameError: name 'undefined_variable' isn't defined +>>> +>>> # Final test to show REPL is still functioning +>>> 1 + 2 + 3 +6 +>>> diff --git a/tests/extmod/platform_basic.py b/tests/extmod/platform_basic.py new file mode 100644 index 0000000000..eb6f2be13c --- /dev/null +++ b/tests/extmod/platform_basic.py @@ -0,0 +1,8 @@ +try: + import platform +except ImportError: + print("SKIP") + raise SystemExit + +print(type(platform.python_compiler())) +print(type(platform.libc_ver())) diff --git a/tests/ports/unix/extra_coverage.py.exp b/tests/ports/unix/extra_coverage.py.exp index 3775036bb3..ac64edde69 100644 --- a/tests/ports/unix/extra_coverage.py.exp +++ b/tests/ports/unix/extra_coverage.py.exp @@ -51,16 +51,16 @@ RuntimeError: ame__ port -builtins micropython _asyncio _thread -array binascii btree cexample -cmath collections cppexample cryptolib -deflate errno example_package -ffi framebuf gc hashlib -heapq io json machine -marshal math os platform -random re select socket -struct sys termios time -tls uctypes vfs websocket +builtins micropython array binascii +btree cexample cmath collections +cppexample cryptolib deflate errno +example_package ffi framebuf +gc hashlib heapq io +json machine marshal math +os platform random re +select socket struct sys +termios time tls uctypes +vfs websocket me micropython machine marshal math diff --git a/tests/run-tests.py b/tests/run-tests.py index 5eebc72460..628fde9d30 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -405,6 +405,10 @@ def run_micropython(pyb, args, test_file, test_file_abspath, is_special=False): return rv def send_get(what): + # Detect {\x00} pattern and convert to ctrl-key codes. + ctrl_code = lambda m: bytes([int(m.group(1))]) + what = re.sub(rb'{\\x(\d\d)}', ctrl_code, what) + os.write(master, what) return get() |