summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--ports/esp32/modesp32.c4
-rw-r--r--ports/rp2/CMakeLists.txt20
-rw-r--r--ports/rp2/rp2_flash.c18
-rw-r--r--py/modio.c9
-rw-r--r--py/repl.c4
-rw-r--r--pyproject.toml2
-rw-r--r--tests/basics/io_buffered_writer.py24
-rw-r--r--tests/basics/io_buffered_writer.py.exp5
-rw-r--r--tests/cmdline/repl_autocomplete_underscore.py33
-rw-r--r--tests/cmdline/repl_autocomplete_underscore.py.exp41
-rw-r--r--tests/cmdline/repl_paste.py90
-rw-r--r--tests/cmdline/repl_paste.py.exp133
-rw-r--r--tests/extmod/platform_basic.py8
-rw-r--r--tests/ports/unix/extra_coverage.py.exp20
-rwxr-xr-xtests/run-tests.py4
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;
}
}
diff --git a/py/repl.c b/py/repl.c
index 87c171cc87..b0ccfa383a 100644
--- a/py/repl.c
+++ b/py/repl.c
@@ -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()