diff --git a/.gitignore b/.gitignore index 2fdfe207a2..9cb1e9ea5e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ !atmel-samd/asf/**/*.a *.elf *.bin +!*.toml.bin *.map *.hex *.dis diff --git a/devices/ble_hci/common-hal/_bleio/Adapter.c b/devices/ble_hci/common-hal/_bleio/Adapter.c index 8c7b8f67eb..f558d66d40 100644 --- a/devices/ble_hci/common-hal/_bleio/Adapter.c +++ b/devices/ble_hci/common-hal/_bleio/Adapter.c @@ -49,8 +49,8 @@ #include "shared-bindings/_bleio/ScanEntry.h" #include "shared-bindings/time/__init__.h" -#if CIRCUITPY_DOTENV -#include "shared-module/dotenv/__init__.h" +#if CIRCUITPY_OS_GETENV +#include "shared-bindings/os/__init__.h" #endif #define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION)) @@ -284,15 +284,15 @@ char default_ble_name[] = { 'C', 'I', 'R', 'C', 'U', 'I', 'T', 'P', 'Y', 0, 0, 0 STATIC void bleio_adapter_hci_init(bleio_adapter_obj_t *self) { mp_int_t name_len = 0; - #if CIRCUITPY_DOTENV - char ble_name[32]; - name_len = dotenv_get_key("/.env", "CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name) - 1); - if (name_len > 0) { - self->name = mp_obj_new_str(ble_name, (size_t)name_len); + #if CIRCUITPY_OS_GETENV + mp_obj_t name = common_hal_os_getenv("CIRCUITPY_BLE_NAME", mp_const_none); + if (name != mp_const_none) { + mp_arg_validate_type_string(name, MP_QSTR_CIRCUITPY_BLE_NAME); + self->name = name; } #endif - if (name_len <= 0) { + if (!self->name) { name_len = sizeof(default_ble_name); bt_addr_t addr; hci_check_error(hci_read_bd_addr(&addr)); diff --git a/docs/environment.rst b/docs/environment.rst index 5766b54380..58ada402c6 100644 --- a/docs/environment.rst +++ b/docs/environment.rst @@ -6,24 +6,49 @@ variables are commonly used to store "secrets" such as Wi-Fi passwords and API keys. This method *does not* make them secure. It only separates them from the code. -CircuitPython supports these by mimicking the `dotenv `_ -CPython library. Other languages such as Javascript, PHP and Ruby also have -dotenv libraries. +CircuitPython uses a file called ``settings.toml`` at the drive root (no +folder) as the environment. User code can access the values from the file +using `os.getenv()`. It is recommended to save any values used repeatedly in a +variable because `os.getenv()` will parse the ``settings.toml`` file contents +on every access. -These libraries store environment variables in a ``.env`` file. Here is a simple -example: +CircuitPython only supports a subset of the full toml specification, see below +for more details. The subset is very "Python-like", which is a key reason we +selected the format. -.. code-block:: bash +Due to technical limitations it probably also accepts some files that are +not valid TOML files; bugs of this nature are subject to change (i.e., be +fixed) without the usual deprecation period for incompatible changes. - KEY1='value1' - # Comment - KEY2='value2 - is multiple lines' +File format example: -CircuitPython uses the ``.env`` at the drive root (no folder) as the environment. -User code can access the values from the file using `os.getenv()`. It is -recommended to save any values used repeatedly in a variable because `os.getenv()` -will parse the ``/.env`` on every access. +.. code-block:: + + str_key="Hello world" # with trailing comment + int_key = 7 + unicode_key="œuvre" + unicode_key2="\\u0153uvre" # same as above + unicode_key3="\\U00000153uvre" # same as above + escape_codes="supported, including \\r\\n\\"\\\\" + # comment + [subtable] + subvalue="cannot retrieve this using getenv" + + +Details of the toml language subset +----------------------------------- + +* The content is required to be in UTF-8 encoding +* The supported data types are string and integer +* Only basic strings are supported, not triple-quoted strings +* Only integers supported by strtol. (no 0o, no 0b, no underscores 1_000, 011 + is 9, not 11) +* Only bare keys are supported +* Duplicate keys are not diagnosed. +* Comments are supported +* Only values from the "root table" can be retrieved +* due to technical limitations, the content of multi-line + strings can erroneously be parsed as a value. CircuitPython behavior ---------------------- diff --git a/docs/workflows.md b/docs/workflows.md index 26f5179b51..29229bd00e 100644 --- a/docs/workflows.md +++ b/docs/workflows.md @@ -46,7 +46,7 @@ connection, the central device can discover two default services. One for file t CircuitPython specifically that includes serial characteristics. To change the default BLE advertising name without (or before) running user code, the desired name -can be put in the `/.env` file. The key is `CIRCUITPY_BLE_NAME`. It's limited to approximately +can be put in the `settings.toml` file. The key is `CIRCUITPY_BLE_NAME`. It's limited to approximately 30 characters depending on the port's settings and will be truncated if longer. ### File Transfer API @@ -69,21 +69,21 @@ Read-only characteristic that returns the UTF-8 encoded version string. ## Web -The web workflow is depends on adding Wi-Fi credentials into the `/.env` file. The keys are +The web workflow is depends on adding Wi-Fi credentials into the `settings.toml` file. The keys are `CIRCUITPY_WIFI_SSID` and `CIRCUITPY_WIFI_PASSWORD`. Once these are defined, CircuitPython will automatically connect to the network and start the webserver used for the workflow. The webserver is on port 80 unless overridden by `CIRCUITPY_WEB_API_PORT`. It also enables MDNS. -Here is an example `/.env`: +Here is an example `/settings.toml`: ```bash # To auto-connect to Wi-Fi -CIRCUITPY_WIFI_SSID='scottswifi' -CIRCUITPY_WIFI_PASSWORD='secretpassword' +CIRCUITPY_WIFI_SSID="scottswifi" +CIRCUITPY_WIFI_PASSWORD="secretpassword" # To enable modifying files from the web. Change this too! # Leave the User field blank in the browser. -CIRCUITPY_WEB_API_PASSWORD='passw0rd' +CIRCUITPY_WEB_API_PASSWORD="passw0rd" CIRCUITPY_WEB_API_PORT=80 ``` @@ -124,7 +124,7 @@ All file system related APIs are protected by HTTP basic authentication. It is * hopefully prevent some griefing in shared settings. The password is sent unencrypted so do not reuse a password with something important. The user field is left blank. -The password is taken from `/.env` with the key `CIRCUITPY_WEB_API_PASSWORD`. If this is unset, the +The password is taken from `settings.toml` with the key `CIRCUITPY_WEB_API_PASSWORD`. If this is unset, the server will respond with `403 Forbidden`. When a password is set, but not provided in a request, it will respond `401 Unauthorized`. diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 450b2eabd7..56c2396d46 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -1002,6 +1002,10 @@ msgstr "" msgid "File exists" msgstr "" +#: shared-module/os/getenv.c +msgid "File not found" +msgstr "" + #: ports/atmel-samd/common-hal/canio/Listener.c #: ports/espressif/common-hal/canio/Listener.c #: ports/stm/common-hal/canio/Listener.c @@ -1058,6 +1062,8 @@ msgstr "" #: shared-bindings/displayio/Display.c #: shared-bindings/displayio/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-module/displayio/Display.c +#: shared-module/framebufferio/FramebufferDisplay.c msgid "Group already used" msgstr "" @@ -1184,6 +1190,7 @@ msgid "Internal define error" msgstr "" #: ports/espressif/common-hal/paralleldisplay/ParallelBus.c +#: shared-module/os/getenv.c msgid "Internal error" msgstr "" @@ -1230,6 +1237,11 @@ msgstr "" msgid "Invalid bits per value" msgstr "" +#: shared-module/os/getenv.c +#, c-format +msgid "Invalid byte %.*s" +msgstr "" + #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" @@ -1260,10 +1272,18 @@ msgstr "" msgid "Invalid state" msgstr "" +#: shared-module/os/getenv.c +msgid "Invalid unicode escape" +msgstr "" + #: shared-bindings/aesio/aes.c msgid "Key must be 16, 24, or 32 bytes long" msgstr "" +#: shared-module/os/getenv.c +msgid "Key not found" +msgstr "" + #: shared-module/is31fl3741/FrameBuffer.c msgid "LED mappings must match display size" msgstr "" @@ -2262,7 +2282,7 @@ msgid "Unkown error code %d" msgstr "" #: shared-bindings/adafruit_pixelbuf/PixelBuf.c -#: shared-module/adafruit_pixelbuf/PixelMap.c +#: shared-module/_pixelmap/PixelMap.c #, c-format msgid "Unmatched number of items on RHS (expected %d, got %d)." msgstr "" @@ -3143,7 +3163,7 @@ msgstr "" msgid "index is out of bounds" msgstr "" -#: shared-bindings/adafruit_pixelbuf/PixelMap.c +#: shared-bindings/_pixelmap/PixelMap.c msgid "index must be tuple or int" msgstr "" @@ -3522,7 +3542,7 @@ msgstr "" msgid "negative shift count" msgstr "" -#: shared-bindings/adafruit_pixelbuf/PixelMap.c +#: shared-bindings/_pixelmap/PixelMap.c msgid "nested index must be int" msgstr "" diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk index 05aed89eb2..57f7ca339f 100644 --- a/ports/atmel-samd/mpconfigport.mk +++ b/ports/atmel-samd/mpconfigport.mk @@ -24,13 +24,13 @@ ifeq ($(CHIP_FAMILY),samd21) CIRCUITPY_AESIO ?= 0 CIRCUITPY_ATEXIT ?= 0 CIRCUITPY_AUDIOMIXER ?= 0 +CIRCUITPY_AUDIOMP3 ?= 0 CIRCUITPY_BINASCII ?= 0 CIRCUITPY_BITBANGIO ?= 0 CIRCUITPY_BITMAPTOOLS ?= 0 -CIRCUITPY_BUSDEVICE ?= 0 -CIRCUITPY_AUDIOMP3 ?= 0 CIRCUITPY_BLEIO_HCI = 0 CIRCUITPY_BUILTINS_POW3 ?= 0 +CIRCUITPY_BUSDEVICE ?= 0 CIRCUITPY_COMPUTED_GOTO_SAVE_SPACE ?= 1 CIRCUITPY_COUNTIO ?= 0 # Not enough RAM for framebuffers @@ -42,6 +42,7 @@ CIRCUITPY_I2CTARGET ?= 0 CIRCUITPY_JSON ?= 0 CIRCUITPY_KEYPAD ?= 0 CIRCUITPY_MSGPACK ?= 0 +CIRCUITPY_OS_GETENV ?= 0 CIRCUITPY_PIXELMAP ?= 0 CIRCUITPY_RE ?= 0 CIRCUITPY_SDCARDIO ?= 0 diff --git a/ports/cxd56/Makefile b/ports/cxd56/Makefile index 5d06143f73..f90b6dd460 100644 --- a/ports/cxd56/Makefile +++ b/ports/cxd56/Makefile @@ -134,7 +134,8 @@ SRC_COMMON_HAL_EXPANDED = $(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \ $(addprefix common-hal/, $(SRC_COMMON_HAL)) SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE)) \ - $(addprefix shared-module/, $(SRC_SHARED_MODULE)) + $(addprefix shared-module/, $(SRC_SHARED_MODULE)) \ + $(addprefix shared-module/, $(SRC_SHARED_MODULE_INTERNAL)) SRC_S = supervisor/cpu.s diff --git a/ports/espressif/bindings/esp32_camera/Camera.c b/ports/espressif/bindings/esp32_camera/Camera.c index a770ea8ff7..9bcd398612 100644 --- a/ports/espressif/bindings/esp32_camera/Camera.c +++ b/ports/espressif/bindings/esp32_camera/Camera.c @@ -63,7 +63,7 @@ //| """ //| Configure and initialize a camera with the given properties //| -//| This driver requires that the ``CIRCUITPY_RESERVED_PSRAM`` in ``/.env`` be large enough to hold the camera frambuffer(s). Generally, boards with built-in cameras will have a default setting that is large enough. If the constructor raises a MemoryError or an IDFError, this probably indicates the setting is too small and should be increased. +//| This driver requires that the ``CIRCUITPY_RESERVED_PSRAM`` in ``settings.toml`` be large enough to hold the camera frambuffer(s). Generally, boards with built-in cameras will have a default setting that is large enough. If the constructor raises a MemoryError or an IDFError, this probably indicates the setting is too small and should be increased. //| //| //| .. important:: diff --git a/ports/espressif/bindings/espidf/__init__.c b/ports/espressif/bindings/espidf/__init__.c index c42a9e549d..12781000ae 100644 --- a/ports/espressif/bindings/espidf/__init__.c +++ b/ports/espressif/bindings/espidf/__init__.c @@ -132,7 +132,7 @@ STATIC mp_obj_t espidf_get_total_psram(void) { MP_DEFINE_CONST_FUN_OBJ_0(espidf_get_total_psram_obj, espidf_get_total_psram); //| def get_reserved_psram() -> int: -//| """Returns number of bytes of psram reserved for use by esp-idf, either a board-specific default value or the value defined in ``/.env``.""" +//| """Returns number of bytes of psram reserved for use by esp-idf, either a board-specific default value or the value defined in ``settings.toml``.""" //| STATIC mp_obj_t espidf_get_reserved_psram(void) { return MP_OBJ_NEW_SMALL_INT(common_hal_espidf_get_reserved_psram()); diff --git a/ports/espressif/common-hal/_bleio/Adapter.c b/ports/espressif/common-hal/_bleio/Adapter.c index 5eaf6e9cf4..3239c501cc 100644 --- a/ports/espressif/common-hal/_bleio/Adapter.c +++ b/ports/espressif/common-hal/_bleio/Adapter.c @@ -59,8 +59,8 @@ #include "esp_bt.h" #include "esp_nimble_hci.h" -#if CIRCUITPY_DOTENV -#include "shared-module/dotenv/__init__.h" +#if CIRCUITPY_OS_GETENV +#include "shared-module/os/__init__.h" #endif bleio_connection_internal_t bleio_connections[BLEIO_TOTAL_CONNECTION_COUNT]; @@ -101,22 +101,16 @@ void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enable ble_hs_cfg.sync_cb = _on_sync; // ble_hs_cfg.store_status_cb = ble_store_util_status_rr; - #if CIRCUITPY_DOTENV - mp_int_t name_len = 0; - char ble_name[32]; - name_len = dotenv_get_key("/.env", "CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name) - 1); - if (name_len > 0) { - if (name_len > MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH) { - name_len = MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH; - } - ble_name[name_len] = '\0'; + #if CIRCUITPY_OS_GETENV + char ble_name[1 + MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH]; + os_getenv_err_t result = common_hal_os_getenv_str("CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name)); + if (result == GETENV_OK) { ble_svc_gap_device_name_set(ble_name); - } else { + } else + #endif + { ble_svc_gap_device_name_set("CIRCUITPY"); } - #else - ble_svc_gap_device_name_set("CIRCUITPY"); - #endif // Clear all of the internal connection objects. for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index 3106f4b991..baeba5ce90 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -55,7 +55,7 @@ #include "shared-bindings/microcontroller/RunMode.h" #include "shared-bindings/rtc/__init__.h" #include "shared-bindings/socketpool/__init__.h" -#include "shared-module/dotenv/__init__.h" +#include "shared-module/os/__init__.h" #include "peripherals/rmt.h" #include "peripherals/timer.h" @@ -519,7 +519,7 @@ void port_idle_until_interrupt(void) { void port_post_boot_py(bool heap_valid) { if (!heap_valid && filesystem_present()) { mp_int_t reserved; - if (dotenv_get_key_int("/.env", "CIRCUITPY_RESERVED_PSRAM", &reserved)) { + if (common_hal_os_getenv_int("CIRCUITPY_RESERVED_PSRAM", &reserved) == GETENV_OK) { common_hal_espidf_set_reserved_psram(reserved); } common_hal_espidf_reserve_psram(); diff --git a/ports/nrf/common-hal/_bleio/Adapter.c b/ports/nrf/common-hal/_bleio/Adapter.c index 49b5d638f3..2c0c343877 100644 --- a/ports/nrf/common-hal/_bleio/Adapter.c +++ b/ports/nrf/common-hal/_bleio/Adapter.c @@ -52,8 +52,9 @@ #include "shared-bindings/_bleio/ScanEntry.h" #include "shared-bindings/time/__init__.h" -#if CIRCUITPY_DOTENV -#include "shared-module/dotenv/__init__.h" +#if CIRCUITPY_OS_GETENV +#include "shared-bindings/os/__init__.h" +#include "shared-module/os/__init__.h" #endif #define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS) @@ -343,20 +344,17 @@ STATIC void bleio_adapter_reset_name(bleio_adapter_obj_t *self) { default_ble_name[len - 1] = nibble_to_hex_lower[addr.addr[0] & 0xf]; default_ble_name[len] = '\0'; // for now we add null for compatibility with C ASCIIZ strings - mp_int_t name_len = 0; - - #if CIRCUITPY_DOTENV + #if CIRCUITPY_OS_GETENV char ble_name[32]; - name_len = dotenv_get_key("/.env", "CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name) - 1); - if (name_len > 0) { - ble_name[name_len] = '\0'; - common_hal_bleio_adapter_set_name(self, (char *)ble_name); + + os_getenv_err_t result = common_hal_os_getenv_str("CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name)); + if (result == GETENV_OK) { + common_hal_bleio_adapter_set_name(self, ble_name); + return; } #endif - if (name_len <= 0) { - common_hal_bleio_adapter_set_name(self, (char *)default_ble_name); - } + common_hal_bleio_adapter_set_name(self, (char *)default_ble_name); } static void bluetooth_adapter_background(void *data) { diff --git a/ports/raspberrypi/bindings/rp2pio/StateMachine.c b/ports/raspberrypi/bindings/rp2pio/StateMachine.c index f45dc07357..041d02e5cd 100644 --- a/ports/raspberrypi/bindings/rp2pio/StateMachine.c +++ b/ports/raspberrypi/bindings/rp2pio/StateMachine.c @@ -422,7 +422,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(rp2pio_statemachine_write_obj, 2, rp2pio_statemachine //| ) -> None: //| """Write data to the TX fifo in the background, with optional looping. //| -//| First, if any previous ``once`` or ``loop`` buffer has not been started, this function blocks until they have. +//| First, if any previous ``once`` or ``loop`` buffer has not been started, this function blocks until they have been started. //| This means that any ``once`` or ``loop`` buffer will be written at least once. //| Then the ``once`` and/or ``loop`` buffers are queued. and the function returns. //| The ``once`` buffer (if specified) will be written just once. diff --git a/ports/raspberrypi/boards/raspberry_pi_pico_w/mpconfigboard.h b/ports/raspberrypi/boards/raspberry_pi_pico_w/mpconfigboard.h index e5d8660b2b..a31ee7327c 100644 --- a/ports/raspberrypi/boards/raspberry_pi_pico_w/mpconfigboard.h +++ b/ports/raspberrypi/boards/raspberry_pi_pico_w/mpconfigboard.h @@ -4,5 +4,7 @@ #define CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL (1) #define CIRCUITPY_DIGITALIO_HAVE_INVALID_DRIVE_MODE (1) +#define MICROPY_HW_LED_STATUS (&pin_CYW0) + #define CIRCUITPY_BOARD_I2C (1) #define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO5, .sda = &pin_GPIO4}} diff --git a/ports/raspberrypi/common-hal/wifi/Radio.c b/ports/raspberrypi/common-hal/wifi/Radio.c index 313363dd32..4cd80fb4b7 100644 --- a/ports/raspberrypi/common-hal/wifi/Radio.c +++ b/ports/raspberrypi/common-hal/wifi/Radio.c @@ -206,6 +206,19 @@ void common_hal_wifi_radio_stop_ap(wifi_radio_obj_t *self) { */ } +static bool connection_unchanged(wifi_radio_obj_t *self, const uint8_t *ssid, size_t ssid_len) { + if (cyw43_tcpip_link_status(&cyw43_state, CYW43_ITF_STA) != CYW43_LINK_UP) { + return false; + } + if (ssid_len != self->connected_ssid_len) { + return false; + } + if (memcmp(ssid, self->connected_ssid, self->connected_ssid_len)) { + return false; + } + return true; +} + wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t *ssid, size_t ssid_len, uint8_t *password, size_t password_len, uint8_t channel, mp_float_t timeout, uint8_t *bssid, size_t bssid_len) { if (!common_hal_wifi_radio_get_enabled(self)) { mp_raise_RuntimeError(translate("Wifi is not enabled")); @@ -215,11 +228,18 @@ wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t mp_raise_RuntimeError(translate("Wifi is in access point mode.")); } + if (ssid_len > 32) { + return WIFI_RADIO_ERROR_CONNECTION_FAIL; + } size_t timeout_ms = timeout <= 0 ? 8000 : (size_t)MICROPY_FLOAT_C_FUN(ceil)(timeout * 1000); uint64_t start = port_get_raw_ticks(NULL); uint64_t deadline = start + timeout_ms; + if (connection_unchanged(self, ssid, ssid_len)) { + return WIFI_RADIO_ERROR_NONE; + } + // disconnect common_hal_wifi_radio_stop_station(self); @@ -237,6 +257,8 @@ wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t switch (result) { case CYW43_LINK_UP: + memcpy(self->connected_ssid, ssid, ssid_len); + self->connected_ssid_len = ssid_len; bindings_cyw43_wifi_enforce_pm(); return WIFI_RADIO_ERROR_NONE; case CYW43_LINK_FAIL: diff --git a/ports/raspberrypi/common-hal/wifi/Radio.h b/ports/raspberrypi/common-hal/wifi/Radio.h index b2e9df9e3a..a4125fe7ba 100644 --- a/ports/raspberrypi/common-hal/wifi/Radio.h +++ b/ports/raspberrypi/common-hal/wifi/Radio.h @@ -35,6 +35,8 @@ typedef struct { mp_obj_base_t base; char hostname[254]; // hostname max is 253 chars, + 1 for trailing NUL wifi_scannednetworks_obj_t *current_scan; + uint8_t connected_ssid[32]; + uint8_t connected_ssid_len; bool enabled; } wifi_radio_obj_t; diff --git a/ports/raspberrypi/supervisor/port.c b/ports/raspberrypi/supervisor/port.c index 05d53fcc8d..27012f2298 100644 --- a/ports/raspberrypi/supervisor/port.c +++ b/ports/raspberrypi/supervisor/port.c @@ -127,6 +127,13 @@ safe_mode_t port_init(void) { (&_ld_dtcm_bss_start)[i] = 0; } + #if CIRCUITPY_CYW43 + never_reset_pin_number(23); + never_reset_pin_number(24); + never_reset_pin_number(25); + never_reset_pin_number(29); + #endif + // Reset everything into a known state before board_init. reset_port(); @@ -140,10 +147,6 @@ safe_mode_t port_init(void) { // Check brownout. #if CIRCUITPY_CYW43 - never_reset_pin_number(23); - never_reset_pin_number(24); - never_reset_pin_number(25); - never_reset_pin_number(29); // A small number of samples of pico w need an additional delay before // initializing the cyw43 chip. Delays inside cyw43_arch_init_with_country // are intended to meet the power on timing requirements, but apparently diff --git a/ports/unix/modos.c b/ports/unix/modos.c index 6241dfa6c3..deea3bfc91 100644 --- a/ports/unix/modos.c +++ b/ports/unix/modos.c @@ -193,7 +193,12 @@ STATIC mp_obj_t mod_os_system(mp_obj_t cmd_in) { } MP_DEFINE_CONST_FUN_OBJ_1(mod_os_system_obj, mod_os_system); +mp_obj_t common_hal_os_getenv(const char *key, mp_obj_t default_); STATIC mp_obj_t mod_os_getenv(mp_obj_t var_in) { + mp_obj_t result = common_hal_os_getenv(mp_obj_str_get_str(var_in), mp_const_none); + if (result != mp_const_none) { + return result; + } const char *s = getenv(mp_obj_str_get_str(var_in)); if (s == NULL) { return mp_const_none; diff --git a/ports/unix/variants/coverage/mpconfigvariant.mk b/ports/unix/variants/coverage/mpconfigvariant.mk index 1e8e59f9a9..e351fc0836 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.mk +++ b/ports/unix/variants/coverage/mpconfigvariant.mk @@ -33,7 +33,6 @@ SRC_BITMAP := \ shared-bindings/aesio/__init__.c \ shared-bindings/bitmaptools/__init__.c \ shared-bindings/displayio/Bitmap.c \ - shared-bindings/dotenv/__init__.c \ shared-bindings/rainbowio/__init__.c \ shared-bindings/traceback/__init__.c \ shared-bindings/util.c \ @@ -45,7 +44,7 @@ SRC_BITMAP := \ shared-module/displayio/Bitmap.c \ shared-module/displayio/ColorConverter.c \ shared-module/displayio/ColorConverter.c \ - shared-module/dotenv/__init__.c \ + shared-module/os/getenv.c \ shared-module/rainbowio/__init__.c \ shared-module/traceback/__init__.c \ shared-module/zlib/__init__.c \ @@ -56,7 +55,7 @@ CFLAGS += \ -DCIRCUITPY_AESIO=1 \ -DCIRCUITPY_BITMAPTOOLS=1 \ -DCIRCUITPY_DISPLAYIO_UNIX=1 \ - -DCIRCUITPY_DOTENV=1 \ + -DCIRCUITPY_OS_GETENV=1 \ -DCIRCUITPY_GIFIO=1 \ -DCIRCUITPY_RAINBOWIO=1 \ -DCIRCUITPY_TRACEBACK=1 \ diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 70310106f0..22ea67d05e 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -182,9 +182,6 @@ endif ifeq ($(CIRCUITPY_DISPLAYIO),1) SRC_PATTERNS += displayio/% endif -ifeq ($(CIRCUITPY_DOTENV),1) -SRC_PATTERNS += dotenv/% -endif ifeq ($(CIRCUITPY__EVE),1) SRC_PATTERNS += _eve/% endif @@ -589,7 +586,6 @@ SRC_SHARED_MODULE_ALL = \ displayio/TileGrid.c \ displayio/area.c \ displayio/__init__.c \ - dotenv/__init__.c \ floppyio/__init__.c \ fontio/BuiltinFont.c \ fontio/__init__.c \ @@ -718,6 +714,7 @@ endif SRC_SHARED_MODULE_INTERNAL = \ $(filter $(SRC_PATTERNS), \ displayio/display_core.c \ + os/getenv.c \ usb/utf16le.c \ ) diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 3ca23263d4..f0e145998b 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -208,9 +208,6 @@ CFLAGS += -DCIRCUITPY_BITMAPTOOLS=$(CIRCUITPY_BITMAPTOOLS) CFLAGS += -DCIRCUITPY_FRAMEBUFFERIO=$(CIRCUITPY_FRAMEBUFFERIO) CFLAGS += -DCIRCUITPY_VECTORIO=$(CIRCUITPY_VECTORIO) -CIRCUITPY_DOTENV ?= $(CIRCUITPY_FULL_BUILD) -CFLAGS += -DCIRCUITPY_DOTENV=$(CIRCUITPY_DOTENV) - CIRCUITPY_DUALBANK ?= 0 CFLAGS += -DCIRCUITPY_DUALBANK=$(CIRCUITPY_DUALBANK) @@ -218,6 +215,9 @@ CFLAGS += -DCIRCUITPY_DUALBANK=$(CIRCUITPY_DUALBANK) CIRCUITPY_ENABLE_MPY_NATIVE ?= 0 CFLAGS += -DCIRCUITPY_ENABLE_MPY_NATIVE=$(CIRCUITPY_ENABLE_MPY_NATIVE) +CIRCUITPY_OS_GETENV ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_OS_GETENV=$(CIRCUITPY_OS_GETENV) + CIRCUITPY_ERRNO ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_ERRNO=$(CIRCUITPY_ERRNO) diff --git a/shared-bindings/dotenv/__init__.c b/shared-bindings/dotenv/__init__.c deleted file mode 100644 index 193338c7c2..0000000000 --- a/shared-bindings/dotenv/__init__.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * SPDX-FileCopyrightText: Copyright (c) 2022 Scott Shawcroft for Adafruit Industries - * - * 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 - -#include "extmod/vfs.h" -#include "lib/oofatfs/ff.h" -#include "lib/oofatfs/diskio.h" -#include "py/mpstate.h" -#include "py/obj.h" -#include "py/objstr.h" -#include "py/runtime.h" -#include "shared-bindings/dotenv/__init__.h" - -//| """Functions to manage environment variables from a .env file. -//| -//| A subset of the CPython `dotenv library `_. It does -//| not support variables or double quotes. -//| -//| Keys and values may be put in single quotes. -//| ``\`` and ``'`` are escaped by ``\`` in single quotes. Newlines can occur in quotes for multiline values. -//| Comments start with ``#`` and apply for the rest of the line. -//| A ``#`` immediately following an ``=`` is part of the value, not the start of a comment, -//| and a ``#`` embedded in a value without whitespace will be part of that value. -//| This corresponds to how assignments and comments work in most Unix shells. -//| -//| -//| File format example: -//| -//| .. code-block:: -//| -//| key=value -//| key2 = value2 -//| 'key3' = 'value with spaces' -//| # comment -//| key4 = value3 # comment 2 -//| 'key5'=value4 -//| key=value5 # overrides the first one -//| multiline = 'hello -//| world -//| how are you?' -//| # The #'s below will be included in the value. They do not start a comment. -//| key6=#value -//| key7=abc#def -//| -//| """ -//| -//| import typing -//| - -//| def get_key(dotenv_path: str, key_to_get: str) -> Optional[str]: -//| """Get the value for the given key from the given .env file. If the key occurs multiple -//| times in the file, then the last value will be returned. -//| -//| Returns None if the key isn't found or doesn't have a value.""" -//| ... -//| -STATIC mp_obj_t _dotenv_get_key(mp_obj_t path_in, mp_obj_t key_to_get_in) { - return common_hal_dotenv_get_key(mp_obj_str_get_str(path_in), - mp_obj_str_get_str(key_to_get_in)); -} -MP_DEFINE_CONST_FUN_OBJ_2(dotenv_get_key_obj, _dotenv_get_key); - -//| def load_dotenv() -> None: -//| """Does nothing in CircuitPython because os.getenv will automatically read .env when -//| available. -//| -//| Present in CircuitPython so CPython-compatible code can use it without error.""" -//| ... -//| -STATIC mp_obj_t dotenv_load_dotenv(void) { - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_0(dotenv_load_dotenv_obj, dotenv_load_dotenv); - -STATIC const mp_rom_map_elem_t dotenv_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_dotenv) }, - - { MP_ROM_QSTR(MP_QSTR_get_key), MP_ROM_PTR(&dotenv_get_key_obj) }, - { MP_ROM_QSTR(MP_QSTR_load_dotenv), MP_ROM_PTR(&dotenv_load_dotenv_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(dotenv_module_globals, dotenv_module_globals_table); - -const mp_obj_module_t dotenv_module = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&dotenv_module_globals, -}; - -MP_REGISTER_MODULE(MP_QSTR_dotenv, dotenv_module, CIRCUITPY_DOTENV); diff --git a/shared-bindings/dotenv/__init__.h b/shared-bindings/dotenv/__init__.h deleted file mode 100644 index 18a6c280dd..0000000000 --- a/shared-bindings/dotenv/__init__.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2022 Scott Shawcroft for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DOTENV___INIT___H -#define MICROPY_INCLUDED_SHARED_BINDINGS_DOTENV___INIT___H - -#include -#include - -#include "py/objtuple.h" - -#include "shared-module/dotenv/__init__.h" - -mp_obj_t common_hal_dotenv_get_key(const char *path, const char *key); - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DOTENV___INIT___H diff --git a/shared-bindings/os/__init__.c b/shared-bindings/os/__init__.c index 1260903203..b2fb8ff095 100644 --- a/shared-bindings/os/__init__.c +++ b/shared-bindings/os/__init__.c @@ -92,6 +92,7 @@ MP_DEFINE_CONST_FUN_OBJ_0(os_getcwd_obj, os_getcwd); //| ... //| STATIC mp_obj_t os_getenv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + #if CIRCUITPY_OS_GETENV enum { ARG_key, ARG_default }; static const mp_arg_t allowed_args[] = { { MP_QSTR_key, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -101,6 +102,9 @@ STATIC mp_obj_t os_getenv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); return common_hal_os_getenv(mp_obj_str_get_str(args[ARG_key].u_obj), args[ARG_default].u_obj); + #else + return mp_const_none; + #endif } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(os_getenv_obj, 1, os_getenv); diff --git a/shared-bindings/os/__init__.h b/shared-bindings/os/__init__.h index 00d8c28a50..49b12cd52a 100644 --- a/shared-bindings/os/__init__.h +++ b/shared-bindings/os/__init__.h @@ -38,6 +38,8 @@ mp_obj_t common_hal_os_uname(void); void common_hal_os_chdir(const char *path); mp_obj_t common_hal_os_getcwd(void); mp_obj_t common_hal_os_getenv(const char *key, mp_obj_t default_); +mp_obj_t common_hal_os_getenv_path(const char *path, const char *key, mp_obj_t default_); + mp_obj_t common_hal_os_listdir(const char *path); void common_hal_os_mkdir(const char *path); void common_hal_os_remove(const char *path); diff --git a/shared-module/dotenv/__init__.c b/shared-module/dotenv/__init__.c deleted file mode 100644 index ead7c6561b..0000000000 --- a/shared-module/dotenv/__init__.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * SPDX-FileCopyrightText: Copyright (c) 2022 Scott Shawcroft for Adafruit Industries - * - * 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 -#include - -#include "shared-bindings/dotenv/__init__.h" - -#include "py/misc.h" -#include "py/mpstate.h" -#include "py/objstr.h" -#include "supervisor/filesystem.h" - -#if defined(UNIX) -typedef FILE *file_arg; -STATIC bool open_file(const char *name, file_arg *active_file) { - FILE *result = fopen(name, "r"); - if (result) { - *active_file = result; - } - return result != NULL; -} -STATIC void close_file(file_arg *active_file) { - fclose(*active_file); -} -STATIC uint8_t get_next_character(file_arg *active_file) { - int value = fgetc(*active_file); - if (value == EOF) { - return 0; - } - return value; -} -STATIC void seek_minus_one(file_arg *active_file) { - fseek(*active_file, -1, SEEK_CUR); -} -#else -#include "extmod/vfs.h" -#include "extmod/vfs_fat.h" -typedef FIL file_arg; -STATIC bool open_file(const char *name, file_arg *active_file) { - FATFS *fs = filesystem_circuitpy(); - FRESULT result = f_open(fs, active_file, name, FA_READ); - return result == FR_OK; -} -STATIC void close_file(file_arg *active_file) { - // nothing -} - -// Return 0 if there is no next character (EOF). -STATIC uint8_t get_next_character(FIL *active_file) { - uint8_t character = 0; - UINT quantity_read; - // If there's an error or quantity_read is 0, character will remain 0. - f_read(active_file, &character, 1, &quantity_read); - return character; -} -STATIC void seek_minus_one(file_arg *active_file) { - f_lseek(active_file, f_tell(active_file) - 1); -} -#endif - -// Discard whitespace, except for newlines, returning the next character after the whitespace. -// Return 0 if there is no next character (EOF). -STATIC uint8_t consume_whitespace(file_arg *active_file) { - uint8_t character; - do { - character = get_next_character(active_file); - } while (character != '\n' && character != 0 && unichar_isspace(character)); - return character; -} - -// Starting at the start of a new line, determines if the key matches the given -// key. File pointer is set to be just before the = after the key. -STATIC bool key_matches(file_arg *active_file, const char *key) { - uint8_t character; - character = consume_whitespace(active_file); - if (character == 0) { - return false; - } - bool quoted = false; - if (character == '\'') { - // Beginning of single-quoted string. - quoted = true; - character = get_next_character(active_file); - } - size_t key_pos = 0; - bool escaped = false; - bool matches = true; - size_t key_len = strlen(key); - while (character != 0) { - if (character == '\\' && !escaped && quoted) { - escaped = true; - } else if (!escaped && quoted && character == '\'') { - quoted = false; - // End of quoted key. Skip over the ending quote. - character = get_next_character(active_file); - break; - } else if (!quoted && (unichar_isspace(character) || character == '=' || character == '\n' || character == '#' || character == 0)) { - // End of unquoted key. - break; - } else { - // Still on tentative key; see if it matches the next supplied key character, - // but don't run off the end of the supplied key. - if (key_pos < key_len) { - matches = matches && (unsigned char)key[key_pos] == character; - escaped = false; - key_pos++; - } else { - // Key on line is too long. - matches = false; - } - } - character = get_next_character(active_file); - } - if (character == '=' || character == '\n' || character == '#' || character == 0) { - // Rewind one so the value, if any, can be found. - seek_minus_one(active_file); - } else if (!unichar_isspace(character)) { - // We're followed by something else that is invalid syntax. - matches = false; - } - - return matches && key_pos == key_len; -} - -STATIC bool next_line(file_arg *active_file) { - uint8_t character; - bool quoted = false; - bool escaped = false; - // Track comments because they last until the end of the line. - bool comment = false; - // Consume all characters while quoted or others up to \n. - do { - character = get_next_character(active_file); - - if ((!quoted || character == '#') || comment) { - // Comments consume any escaping. - comment = true; - } else if (!escaped) { - if (character == '\'') { - quoted = !quoted; - } else if (character == '\\') { - escaped = true; - } - } else { - escaped = false; - } - } while (character != 0 && (quoted || character != '\n')); - - return character != 0; -} - -STATIC mp_int_t read_value(file_arg *active_file, char *value, size_t value_len) { - uint8_t character; - // Consume spaces before "=", and get first character of interest. - character = consume_whitespace(active_file); - if (character != '=') { - if (character == '#' || character == '\n') { - // Keys without an = after them are valid with the value None. - return -1; - } - // All other characters are invalid. - return -1; - } - // Consume space after = - if (character != '#') { - // a # immediately after = is part of the value! - character = consume_whitespace(active_file); - } - bool quoted = false; - if (character == '\'') { - quoted = true; - character = get_next_character(active_file); - } - if (character == '"') { - // We don't support double quoted values. - return -1; - } - // Copy the value over. - size_t value_pos = 0; - bool escaped = false; - // Count trailing spaces so we can ignore them at the end of unquoted - // values. - size_t trailing_spaces = 0; - bool first_char = true; - while (character != 0) { - // Consume the first \ if the value is quoted. - if (quoted && character == '\\' && !escaped) { - escaped = true; - // Drop this backslash by short circuiting the rest of the loop. - character = get_next_character(active_file); - continue; - } - if (quoted && !escaped && character == '\'') { - // trailing ' means the value is done. - break; - } - // Unquoted values are ended by a newline or comment. - if (!quoted && (character == '\n' || (character == '#' && !first_char))) { - if (character == '\n') { - // Rewind one so the next_line can find the \n. - seek_minus_one(active_file); - } - break; - } - if (!quoted && unichar_isspace(character)) { - trailing_spaces += 1; - } else { - trailing_spaces = 0; - } - escaped = false; - // Only copy the value over if we have space. Otherwise, we'll just - // count the overall length. - if (value_pos < value_len) { - value[value_pos] = character; - } - value_pos++; - character = get_next_character(active_file); - first_char = false; - } - - return value_pos - trailing_spaces; -} - -mp_int_t dotenv_get_key(const char *path, const char *key, char *value, mp_int_t value_len) { - file_arg active_file; - if (!open_file(path, &active_file)) { - return -1; - } - - mp_int_t actual_value_len = -1; - bool read_ok = true; - while (read_ok) { - if (key_matches(&active_file, key)) { - actual_value_len = read_value(&active_file, value, value_len); - } - - read_ok = next_line(&active_file); - } - close_file(&active_file); - return actual_value_len; -} - -mp_obj_t common_hal_dotenv_get_key(const char *path, const char *key) { - // Use the stack for short values. Longer values will require a heap allocation after we know - // the length. - char value[64]; - mp_int_t actual_len = dotenv_get_key(path, key, value, sizeof(value)); - if (actual_len < 0) { - return mp_const_none; - } - if ((size_t)actual_len >= sizeof(value)) { - byte *buf = m_new(byte, actual_len + 1); - dotenv_get_key(path, key, (char *)buf, actual_len); - buf[actual_len] = 0; - - mp_obj_str_t *o = m_new_obj(mp_obj_str_t); - o->base.type = &mp_type_str; - o->len = actual_len; - o->data = buf; - o->hash = qstr_compute_hash(buf, actual_len); - - return MP_OBJ_FROM_PTR(o); - } - return mp_obj_new_str(value, actual_len); -} - -bool dotenv_get_key_terminated(const char *path, const char *key, char *value, mp_int_t value_len) { - mp_int_t actual_len = dotenv_get_key(path, key, value, value_len - 1); - if (actual_len >= value_len) { - return false; - } - value[actual_len] = '\0'; // terminate string - return true; -} - -bool dotenv_get_key_int(const char *path, const char *key, mp_int_t *value) { - char buf[16]; - if (!dotenv_get_key_terminated(path, key, buf, (mp_int_t)sizeof(buf))) { - return false; - } - char *end; - long result = strtol(buf, &end, 0); - if (end == buf || *end) { // If the whole buffer was not consumed it's an error - return false; - } - *value = (mp_int_t)result; - return true; -} diff --git a/shared-module/os/__init__.c b/shared-module/os/__init__.c index 200fcb2f61..4a4c02e636 100644 --- a/shared-module/os/__init__.c +++ b/shared-module/os/__init__.c @@ -36,10 +36,6 @@ #include "py/runtime.h" #include "shared-bindings/os/__init__.h" -#if CIRCUITPY_DOTENV -#include "shared-bindings/dotenv/__init__.h" -#endif - // This provides all VFS related OS functions so that ports can share the code // as needed. It does not provide uname. @@ -111,16 +107,6 @@ mp_obj_t common_hal_os_getcwd(void) { return mp_vfs_getcwd(); } -mp_obj_t common_hal_os_getenv(const char *key, mp_obj_t default_) { - #if CIRCUITPY_DOTENV - mp_obj_t env_obj = common_hal_dotenv_get_key("/.env", key); - if (env_obj != mp_const_none) { - return env_obj; - } - #endif - return default_; -} - mp_obj_t common_hal_os_listdir(const char *path) { mp_obj_t path_out; mp_vfs_mount_t *vfs = lookup_dir_path(path, &path_out); diff --git a/shared-module/dotenv/__init__.h b/shared-module/os/__init__.h similarity index 64% rename from shared-module/dotenv/__init__.h rename to shared-module/os/__init__.h index fb27233692..0e1d78c53b 100644 --- a/shared-module/dotenv/__init__.h +++ b/shared-module/os/__init__.h @@ -24,13 +24,23 @@ * THE SOFTWARE. */ +#pragma once + +typedef enum { + GETENV_OK = 0, + GETENV_ERR_OPEN, + GETENV_ERR_UNICODE, + GETENV_ERR_LENGTH, + GETENV_ERR_NOT_FOUND, + GETENV_ERR_UNEXPECTED = 0xff00, // logical or'd with the byte value +} os_getenv_err_t; + // Allocation free version that returns the full length of the value. -mp_int_t dotenv_get_key(const char *path, const char *key, char *value, mp_int_t value_len); +// If it fits, the return value is 0-terminated. The passed in buffer +// may be modified even if an error is returned. Allocation free. +os_getenv_err_t common_hal_os_getenv_str(const char *key, char *value, size_t value_len); -// Returns true and sets value to a '\0'-terminated string if key is present -// and the value (including the terminating '\0') fits strictly within -// value_len bytes. -bool dotenv_get_key_terminated(const char *path, const char *key, char *value, mp_int_t value_len); - -// Returns true and sets value to the read value. Returns false if the value was not numeric. -bool dotenv_get_key_int(const char *path, const char *key, mp_int_t *value); +// Returns GETENV_OK and sets value to the read value. Returns +// GETENV_ERR_... if the value was not numeric. allocation-free. +// If any error code is returned, value is guaranteed not modified +os_getenv_err_t common_hal_os_getenv_int(const char *key, mp_int_t *value); diff --git a/shared-module/os/getenv.c b/shared-module/os/getenv.c new file mode 100644 index 0000000000..d96b1fb306 --- /dev/null +++ b/shared-module/os/getenv.c @@ -0,0 +1,391 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2022 Scott Shawcroft for Adafruit Industries + * + * 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. + */ + +// These functions are separate from __init__.c so that os.getenv() can be +// tested in the unix "coverage" build, without bringing in "our" os module + +#include +#include + +#include "shared-bindings/os/__init__.h" +#include "shared-module/os/__init__.h" + +#include "py/gc.h" +#include "py/misc.h" +#include "py/mpstate.h" +#include "py/objstr.h" +#include "py/parsenum.h" +#include "py/runtime.h" +#include "supervisor/filesystem.h" +#include "supervisor/memory.h" + +#define GETENV_PATH "/settings.toml" + +#include "extmod/vfs.h" +#include "extmod/vfs_fat.h" +typedef FIL file_arg; +STATIC bool open_file(const char *name, file_arg *active_file) { + #if defined(UNIX) + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_obj_t file_obj = mp_call_function_2(MP_OBJ_FROM_PTR(&mp_builtin_open_obj), mp_obj_new_str(name, strlen(name)), MP_ROM_QSTR(MP_QSTR_rb)); + mp_arg_validate_type(file_obj, &mp_type_vfs_fat_fileio, MP_QSTR_file); + pyb_file_obj_t *file = MP_OBJ_TO_PTR(file_obj); + *active_file = file->fp; + nlr_pop(); + return true; + } else { + return false; + } + #else + FATFS *fs = filesystem_circuitpy(); + FRESULT result = f_open(fs, active_file, name, FA_READ); + return result == FR_OK; + #endif +} +STATIC void close_file(file_arg *active_file) { + // nothing +} +STATIC bool is_eof(file_arg *active_file) { + return f_eof(active_file); +} + +// Return 0 if there is no next character (EOF). +STATIC uint8_t get_next_byte(FIL *active_file) { + uint8_t character = 0; + UINT quantity_read; + // If there's an error or quantity_read is 0, character will remain 0. + f_read(active_file, &character, 1, &quantity_read); + return character; +} +STATIC void seek_eof(file_arg *active_file) { + f_lseek(active_file, f_size(active_file)); +} + +// For a fixed buffer, record the required size rather than throwing +STATIC void vstr_add_byte_nonstd(vstr_t *vstr, byte b) { + if (!vstr->fixed_buf || vstr->alloc > vstr->len) { + vstr_add_byte(vstr, b); + } else { + vstr->len++; + } +} + +// For a fixed buffer, record the required size rather than throwing +STATIC void vstr_add_char_nonstd(vstr_t *vstr, unichar c) { + size_t ulen = + (c < 0x80) ? 1 : + (c < 0x800) ? 2 : + (c < 0x10000) ? 3 : 4; + if (!vstr->fixed_buf || vstr->alloc > vstr->len + ulen) { + vstr_add_char(vstr, c); + } else { + vstr->len += ulen; + } +} + +STATIC void next_line(file_arg *active_file) { + uint8_t character; + do { + character = get_next_byte(active_file); + } while (character != 0 && character != '\n'); +} + +// Discard whitespace, except for newlines, returning the next character after the whitespace. +// Return 0 if there is no next character (EOF). +STATIC uint8_t consume_whitespace(file_arg *active_file) { + uint8_t character; + do { + character = get_next_byte(active_file); + } while (character != '\n' && character != 0 && unichar_isspace(character)); + return character; +} + +// Starting at the start of a new line, determines if the key matches the given +// key. +// +// If result is true, the key matches and file pointer is pointing just after the "=". +// If the result is false, the key does NOT match and the file pointer is +// pointing at the start of the next line, if any +STATIC bool key_matches(file_arg *active_file, const char *key) { + uint8_t character; + character = consume_whitespace(active_file); + if (character == '[' || character == 0) { + seek_eof(active_file); + return false; + } + while (*key) { + if (character != *key++) { + // A character didn't match the key, so it's not a match + // If the non-matching char was not the end of the line, + // then consume the rest of the line + if (character != '\n') { + next_line(active_file); + } + return false; + } + character = get_next_byte(active_file); + } + // the next character could be whitespace; consume if necessary + if (unichar_isspace(character)) { + character = consume_whitespace(active_file); + } + // If we're not looking at the "=" then the key didn't match + if (character != '=') { + // A character didn't match the key, so it's not a match + // If the non-matching char was not the end of the line, + // then consume the rest of the line + if (character != '\n') { + next_line(active_file); + } + return false; + } + return true; +} + +STATIC os_getenv_err_t read_unicode_escape(file_arg *active_file, int sz, vstr_t *buf) { + char hex_buf[sz + 1]; + for (int i = 0; i < sz; i++) { + hex_buf[i] = get_next_byte(active_file); + } + hex_buf[sz] = 0; + char *end; + unsigned long c = strtoul(hex_buf, &end, 16); + if (end != &hex_buf[sz]) { + return GETENV_ERR_UNEXPECTED | *end; + } + if (c >= 0x110000) { + return GETENV_ERR_UNICODE; + } + vstr_add_char_nonstd(buf, c); + return GETENV_OK; +} + +// Read a quoted string +STATIC os_getenv_err_t read_string_value(file_arg *active_file, vstr_t *buf) { + while (true) { + int character = get_next_byte(active_file); + switch (character) { + case 0: + case '\n': + return GETENV_ERR_UNEXPECTED | character; + + case '"': + character = consume_whitespace(active_file); + switch (character) { + case '#': + next_line(active_file); + MP_FALLTHROUGH; + case '\n': + return GETENV_OK; + default: + return GETENV_ERR_UNEXPECTED | character; + } + + case '\\': + character = get_next_byte(active_file); + switch (character) { + case 0: + case '\n': + return GETENV_ERR_UNEXPECTED | character; + case 'b': + character = '\b'; + break; + case 'r': + character = '\r'; + break; + case 'n': + character = '\n'; + break; + case 't': + character = '\t'; + break; + case 'v': + character = '\v'; + break; + case 'f': + character = '\f'; + break; + case 'U': + case 'u': { + int sz = (character == 'u') ? 4 : 8; + os_getenv_err_t res; + res = read_unicode_escape(active_file, sz, buf); + if (res != GETENV_OK) { + return res; + } + continue; + } + // default falls through, other escaped characters + // represent themselves + } + MP_FALLTHROUGH; + default: + vstr_add_byte_nonstd(buf, character); + } + } +} + +// Read a numeric value (non-quoted value) as a string +STATIC os_getenv_err_t read_bare_value(file_arg *active_file, vstr_t *buf, int first_character) { + int character = first_character; + while (true) { + switch (character) { + case 0: + return GETENV_ERR_UNEXPECTED | character; + case '\n': + return GETENV_OK; + case '#': + next_line(active_file); + return GETENV_OK; + default: + vstr_add_byte_nonstd(buf, character); + } + character = get_next_byte(active_file); + } +} + +STATIC mp_int_t read_value(file_arg *active_file, vstr_t *buf, bool *quoted) { + uint8_t character; + character = consume_whitespace(active_file); + *quoted = (character == '"'); + + if (*quoted) { + return read_string_value(active_file, buf); + } else { + return read_bare_value(active_file, buf, character); + } +} + +STATIC os_getenv_err_t os_getenv_vstr(const char *path, const char *key, vstr_t *buf, bool *quoted) { + file_arg active_file; + if (!open_file(path, &active_file)) { + return GETENV_ERR_OPEN; + } + + os_getenv_err_t result = GETENV_ERR_NOT_FOUND; + while (!is_eof(&active_file)) { + if (key_matches(&active_file, key)) { + result = read_value(&active_file, buf, quoted); + } + } + close_file(&active_file); + return result; +} + +STATIC os_getenv_err_t os_getenv_buf_terminated(const char *key, char *value, size_t value_len, bool *quoted) { + vstr_t buf; + vstr_init_fixed_buf(&buf, value_len, value); + os_getenv_err_t result = os_getenv_vstr(GETENV_PATH, key, &buf, quoted); + + if (result == GETENV_OK) { + vstr_add_byte_nonstd(&buf, 0); + memcpy(value, buf.buf, MIN(buf.len, value_len)); + if (buf.len > value_len) { // this length includes trailing NUL + result = GETENV_ERR_LENGTH; + } + } + return result; +} + +os_getenv_err_t common_hal_os_getenv_str(const char *key, char *value, size_t value_len) { + bool quoted; + os_getenv_err_t result = os_getenv_buf_terminated(key, value, value_len, "ed); + if (result == GETENV_OK && !quoted) { + result = GETENV_ERR_UNEXPECTED | value[0]; + } + return result; +} + +STATIC void throw_getenv_error(os_getenv_err_t error) { + if (error == GETENV_OK) { + return; + } + if (error & GETENV_ERR_UNEXPECTED) { + byte character = (error & 0xff); + mp_print_t print; + vstr_t vstr; + vstr_init_print(&vstr, 8 + 4 + 1, &print); + if (character) { + mp_str_print_quoted(&print, &character, 1, true); + } else { + mp_str_print_quoted(&print, (byte *)"EOF", 3, true); + } + mp_raise_ValueError_varg(translate("Invalid byte %.*s"), + vstr.len, vstr.buf); + } + switch (error) { + case GETENV_ERR_OPEN: + mp_raise_ValueError(translate("File not found")); + case GETENV_ERR_UNICODE: + mp_raise_ValueError(translate("Invalid unicode escape")); + case GETENV_ERR_NOT_FOUND: + mp_raise_ValueError(translate("Key not found")); + default: + mp_raise_RuntimeError(translate("Internal error")); + } +} + +mp_obj_t common_hal_os_getenv_path(const char *path, const char *key, mp_obj_t default_) { + vstr_t buf; + bool quoted; + + vstr_init(&buf, 64); + os_getenv_err_t result = os_getenv_vstr(path, key, &buf, "ed); + if (result == GETENV_ERR_NOT_FOUND || result == GETENV_ERR_OPEN) { + return default_; + } + throw_getenv_error(result); + + if (quoted) { + return mp_obj_new_str_from_vstr(&mp_type_str, &buf); + } else { + return mp_parse_num_integer(buf.buf, buf.len, 0, NULL); + } +} + +mp_obj_t common_hal_os_getenv(const char *key, mp_obj_t default_) { + return common_hal_os_getenv_path(GETENV_PATH, key, default_); +} + +os_getenv_err_t common_hal_os_getenv_int(const char *key, mp_int_t *value) { + char buf[16]; + bool quoted; + os_getenv_err_t result = os_getenv_buf_terminated(key, buf, sizeof(buf), "ed); + if (result != GETENV_OK) { + return result; + } + if (quoted) { + return GETENV_ERR_UNEXPECTED | '"'; + } + char *end; + long num = strtol(buf, &end, 0); + if (end == buf || *end) { // If the whole buffer was not consumed it's an error + return GETENV_ERR_UNEXPECTED | *end; + } + *value = (mp_int_t)num; + return GETENV_OK; +} diff --git a/supervisor/shared/filesystem.c b/supervisor/shared/filesystem.c index 01aba0a9ac..1eab59c384 100644 --- a/supervisor/shared/filesystem.c +++ b/supervisor/shared/filesystem.c @@ -129,6 +129,9 @@ bool filesystem_init(bool create_allowed, bool force_create) { make_empty_file(&vfs_fat->fatfs, "/.metadata_never_index"); make_empty_file(&vfs_fat->fatfs, "/.Trashes"); make_empty_file(&vfs_fat->fatfs, "/.fseventsd/no_log"); + #if CIRCUITPY_OS_GETENV + make_empty_file(&vfs_fat->fatfs, "/settings.toml"); + #endif // make a sample code.py file make_sample_code_file(&vfs_fat->fatfs); diff --git a/supervisor/shared/web_workflow/static/directory.js b/supervisor/shared/web_workflow/static/directory.js index c1d934c1a8..a89d5e5602 100644 --- a/supervisor/shared/web_workflow/static/directory.js +++ b/supervisor/shared/web_workflow/static/directory.js @@ -95,7 +95,7 @@ async function refresh_list() { if (f.directory) { icon = "📁"; } else if(f.name.endsWith(".txt") || - f.name.endsWith(".env") || + f.name.endsWith(".toml") || f.name.endsWith(".py") || f.name.endsWith(".js") || f.name.endsWith(".json")) { diff --git a/supervisor/shared/web_workflow/static/welcome.html b/supervisor/shared/web_workflow/static/welcome.html index e773bb6a2e..efbb30e312 100644 --- a/supervisor/shared/web_workflow/static/welcome.html +++ b/supervisor/shared/web_workflow/static/welcome.html @@ -12,7 +12,7 @@

 Welcome!

-

Welcome to CircuitPython's Web API. Go to the file browser to work with files in the CIRCUITPY drive. Go to the full code editor for a more enriching experience which requires an active internet connection. Go to the serial terminal to see code output and interact with the REPL. Make sure you've set CIRCUITPY_WEB_API_PASSWORD='somepassword' in /.env. Provide the password when the browser prompts for it. Leave the username blank.

+

Welcome to CircuitPython's Web API. Go to the file browser to work with files in the CIRCUITPY drive. Go to the full code editor for a more enriching experience which requires an active internet connection. Go to the serial terminal to see code output and interact with the REPL. Make sure you've set CIRCUITPY_WEB_API_PASSWORD='somepassword' in /settings.toml. Provide the password when the browser prompts for it. Leave the username blank.

Device Info:

diff --git a/supervisor/shared/web_workflow/web_workflow.c b/supervisor/shared/web_workflow/web_workflow.c index 231b1e8ffb..35d5c46a94 100644 --- a/supervisor/shared/web_workflow/web_workflow.c +++ b/supervisor/shared/web_workflow/web_workflow.c @@ -64,8 +64,8 @@ #include "shared-bindings/wifi/__init__.h" #endif -#if CIRCUITPY_DOTENV -#include "shared-module/dotenv/__init__.h" +#if CIRCUITPY_OS_GETENV +#include "shared-module/os/__init__.h" #endif enum request_state { @@ -115,7 +115,7 @@ static wifi_radio_error_t _last_wifi_status = WIFI_RADIO_ERROR_NONE; static mdns_server_obj_t mdns; #endif -static uint32_t web_api_port = 80; +static mp_int_t web_api_port = 80; static socketpool_socketpool_obj_t pool; static socketpool_socket_obj_t listening; @@ -244,22 +244,21 @@ void supervisor_web_workflow_status(void) { #endif void supervisor_start_web_workflow(void) { - #if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI - + #if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI && CIRCUITPY_OS_GETENV char ssid[33]; char password[64]; - mp_int_t ssid_len = 0; - mp_int_t password_len = 0; - #if CIRCUITPY_DOTENV - ssid_len = dotenv_get_key("/.env", "CIRCUITPY_WIFI_SSID", ssid, sizeof(ssid) - 1); - password_len = dotenv_get_key("/.env", "CIRCUITPY_WIFI_PASSWORD", password, sizeof(password) - 1); - #endif - if (ssid_len <= 0 || (size_t)ssid_len >= sizeof(ssid) || - password_len <= 0 || (size_t)password_len >= sizeof(password)) { + os_getenv_err_t result = common_hal_os_getenv_str("CIRCUITPY_WIFI_SSID", ssid, sizeof(ssid)); + if (result != GETENV_OK) { return; } + + result = common_hal_os_getenv_str("CIRCUITPY_WIFI_PASSWORD", password, sizeof(password)); + if (result != GETENV_OK) { + return; + } + if (!common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj)) { common_hal_wifi_init(false); common_hal_wifi_radio_set_enabled(&common_hal_wifi_radio_obj, true); @@ -268,14 +267,11 @@ void supervisor_start_web_workflow(void) { // TODO: Do our own scan so that we can find the channel we want before calling connect. // Otherwise, connect will do a full slow scan to pick the best AP. - // NUL terminate the strings because dotenv doesn't. - ssid[ssid_len] = '\0'; - password[password_len] = '\0'; // We can all connect again because it will return early if we're already connected to the // network. If we are connected to a different network, then it will disconnect before // attempting to connect to the given network. _wifi_status = common_hal_wifi_radio_connect( - &common_hal_wifi_radio_obj, (uint8_t *)ssid, ssid_len, (uint8_t *)password, password_len, + &common_hal_wifi_radio_obj, (uint8_t *)ssid, strlen(ssid), (uint8_t *)password, strlen(password), 0, 8, NULL, 0); if (_wifi_status != WIFI_RADIO_ERROR_NONE) { @@ -283,21 +279,15 @@ void supervisor_start_web_workflow(void) { return; } - char port_encoded[6]; - size_t port_len = 0; - size_t new_port = web_api_port; - #if CIRCUITPY_DOTENV - port_len = dotenv_get_key("/.env", "CIRCUITPY_WEB_API_PORT", port_encoded, sizeof(port_encoded) - 1); - #endif - if (0 < port_len && port_len < sizeof(port_encoded)) { - port_encoded[port_len] = '\0'; - new_port = strtoul(port_encoded, NULL, 10); - } + mp_int_t new_port = web_api_port; + // (leaves new_port unchanged on any failure) + (void)common_hal_os_getenv_int("CIRCUITPY_WEB_API_PORT", &new_port); bool first_start = pool.base.type != &socketpool_socketpool_type; bool port_changed = new_port != web_api_port; if (first_start) { + port_changed = false; #if CIRCUITPY_MDNS mdns_server_construct(&mdns, true); mdns.base.type = &mdns_server_type; @@ -326,11 +316,12 @@ void supervisor_start_web_workflow(void) { common_hal_socketpool_socket_listen(&listening, 1); } - mp_int_t api_password_len = dotenv_get_key("/.env", "CIRCUITPY_WEB_API_PASSWORD", _api_password + 1, sizeof(_api_password) - 2); - if (api_password_len > 0) { + + const size_t api_password_len = sizeof(_api_password) - 1; + result = common_hal_os_getenv_str("CIRCUITPY_WEB_API_PASSWORD", _api_password + 1, api_password_len); + if (result == GETENV_OK) { _api_password[0] = ':'; - _api_password[api_password_len + 1] = '\0'; - _base64_in_place(_api_password, api_password_len + 1, sizeof(_api_password)); + _base64_in_place(_api_password, strlen(_api_password), sizeof(_api_password) - 1); } #endif } @@ -360,7 +351,7 @@ static void _send_str(socketpool_socket_obj_t *socket, const char *str) { } // The last argument must be NULL! Otherwise, it won't stop. -static void _send_strs(socketpool_socket_obj_t *socket, ...) { +static __attribute__((sentinel)) void _send_strs(socketpool_socket_obj_t *socket, ...) { va_list ap; va_start(ap, socket); @@ -691,16 +682,16 @@ static void _reply_with_file(socketpool_socket_obj_t *socket, _request *request, mp_print_t _socket_print = {socket, _print_raw}; mp_printf(&_socket_print, "Content-Length: %d\r\n", total_length); // TODO: Make this a table to save space. - if (_endswith(filename, ".txt") || _endswith(filename, ".py")) { - _send_strs(socket, "Content-Type: text/plain", ";charset=UTF-8\r\n", NULL); + if (_endswith(filename, ".txt") || _endswith(filename, ".py") || _endswith(filename, ".toml")) { + _send_strs(socket, "Content-Type:", "text/plain", ";charset=UTF-8\r\n", NULL); } else if (_endswith(filename, ".js")) { - _send_strs(socket, "Content-Type: text/javascript", ";charset=UTF-8\r\n", NULL); + _send_strs(socket, "Content-Type:", "text/javascript", ";charset=UTF-8\r\n", NULL); } else if (_endswith(filename, ".html")) { - _send_strs(socket, "Content-Type: text/html", ";charset=UTF-8\r\n", NULL); + _send_strs(socket, "Content-Type:", "text/html", ";charset=UTF-8\r\n", NULL); } else if (_endswith(filename, ".json")) { - _send_strs(socket, "Content-Type: application/json", ";charset=UTF-8\r\n", NULL); + _send_strs(socket, "Content-Type:", "application/json", ";charset=UTF-8\r\n", NULL); } else { - _send_str(socket, "Content-Type: application/octet-stream\r\n"); + _send_strs(socket, "Content-Type:", "application/octet-stream\r\n", NULL); } _cors_header(socket, request); _send_str(socket, "\r\n"); diff --git a/tests/circuitpython/dotenv_test.env b/tests/circuitpython/dotenv_test.env deleted file mode 100644 index 63753b0ea4..0000000000 --- a/tests/circuitpython/dotenv_test.env +++ /dev/null @@ -1,37 +0,0 @@ -# Key "notpresent" is not present - # comment preceded by spaces -plain_value=value -value_with_comment=value # value followed by a comment -quoted_value='value' -quoted_value_with_comment='value' # quoted value followed by a comment -should_be_none -should_be_empty_string= -should_be_hash=# -quoted_should_be_empty_string='' -duplicate_key=wrong -duplicate_key=right -value_with_hash=value#value -quoted_value_with_hash='value#value' -multi_line_value='multi -line' - space_before_key=value -space_before_value= value -space_before_hash_value= #value -space_after_key =value -space_after_key_before_value = value -quoted_then_comment='value'#comment -hash_with_spaces=#value value -aa🐍bb=key with emoji -value_with_emoji=aa🐍bb -sz0=x -sz1=xx -sz2=xxxx -sz3=xxxxxxxx -sz4=xxxxxxxxxxxxxxxx -sz5=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -sz6=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -sz7=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -sz8=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -sz9=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -sz10=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -sz11=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/tests/circuitpython/dotenv_test.py b/tests/circuitpython/dotenv_test.py deleted file mode 100644 index 109a3bf221..0000000000 --- a/tests/circuitpython/dotenv_test.py +++ /dev/null @@ -1,31 +0,0 @@ -import dotenv - -FILE = __file__.rsplit(".", 1)[0] + ".env" - -print(f"notpresent={dotenv.get_key(FILE, 'notpresent')}") -print(f"plain_value={dotenv.get_key(FILE, 'plain_value')}") -print(f"value_with_comment={dotenv.get_key(FILE, 'value_with_comment')}") -print(f"quoted_value={dotenv.get_key(FILE, 'quoted_value')}") -print(f"quoted_value_with_comment={dotenv.get_key(FILE, 'quoted_value_with_comment')}") -print(f"should_be_none={dotenv.get_key(FILE, 'should_be_none')}") -print(f"should_be_empty_string={dotenv.get_key(FILE, 'should_be_empty_string')}") -print(f"should_be_hash={dotenv.get_key(FILE, 'should_be_hash')}") -print(f"quoted_should_be_empty_string={dotenv.get_key(FILE, 'quoted_should_be_empty_string')}") -print(f"duplicate_key={dotenv.get_key(FILE, 'duplicate_key')}") -### This is the a difference from CPython dotenv. The trailing #value is taken as a comment. -print(f"value_with_hash={dotenv.get_key(FILE, 'value_with_hash')}") -print(f"quoted_value_with_hash={dotenv.get_key(FILE, 'quoted_value_with_hash')}") -print(f"multi_line_value={dotenv.get_key(FILE, 'multi_line_value')}") -print(f"space_before_key={dotenv.get_key(FILE, 'space_before_key')}") -print(f"space_before_value={dotenv.get_key(FILE, 'space_before_value')}") -print(f"space_before_hash_value={dotenv.get_key(FILE, 'space_before_hash_value')}") -print(f"space_after_key={dotenv.get_key(FILE, 'space_after_key')}") -print(f"space_after_key_before_value={dotenv.get_key(FILE, 'space_after_key_before_value')}") -print(f"quoted_then_comment={dotenv.get_key(FILE, 'quoted_then_comment')}") -print(f"hash_with_spaces={dotenv.get_key(FILE, 'hash_with_spaces')}") -print(f"aa🐍bb={dotenv.get_key(FILE, 'aa🐍bb')}") -print(f"value_with_emoji={dotenv.get_key(FILE, 'value_with_emoji')}") - -for i in range(12): - key = f"sz{i}" - print(f"len({key})={len(dotenv.get_key(FILE, key))}") diff --git a/tests/circuitpython/dotenv_test.py.exp b/tests/circuitpython/dotenv_test.py.exp deleted file mode 100644 index e0ed5f1505..0000000000 --- a/tests/circuitpython/dotenv_test.py.exp +++ /dev/null @@ -1,35 +0,0 @@ -notpresent=None -plain_value=value -value_with_comment=value -quoted_value=value -quoted_value_with_comment=value -should_be_none=None -should_be_empty_string= -should_be_hash=# -quoted_should_be_empty_string= -duplicate_key=right -value_with_hash=value -quoted_value_with_hash=value#value -multi_line_value=multi -line -space_before_key=value -space_before_value=value -space_before_hash_value=#value -space_after_key=value -space_after_key_before_value=value -quoted_then_comment=value -hash_with_spaces=#value value -aa🐍bb=key with emoji -value_with_emoji=aa🐍bb -len(sz0)=1 -len(sz1)=2 -len(sz2)=4 -len(sz3)=8 -len(sz4)=16 -len(sz5)=32 -len(sz6)=64 -len(sz7)=128 -len(sz8)=256 -len(sz9)=512 -len(sz10)=1024 -len(sz11)=2048 diff --git a/tests/circuitpython/getenv.py b/tests/circuitpython/getenv.py new file mode 100644 index 0000000000..ad8fdbfeeb --- /dev/null +++ b/tests/circuitpython/getenv.py @@ -0,0 +1,79 @@ +import uos + +uos.umount("/") + + +class RAMBlockDevice: + ERASE_BLOCK_SIZE = 512 + + def __init__(self, blocks): + self.data = bytearray(blocks * self.ERASE_BLOCK_SIZE) + + def readblocks(self, block, buf, off=0): + addr = block * self.ERASE_BLOCK_SIZE + off + for i in range(len(buf)): + buf[i] = self.data[addr + i] + + def writeblocks(self, block, buf, off=None): + if off is None: + # erase, then write + off = 0 + addr = block * self.ERASE_BLOCK_SIZE + off + for i in range(len(buf)): + self.data[addr + i] = buf[i] + + def ioctl(self, op, arg): + if op == 4: # block count + return len(self.data) // self.ERASE_BLOCK_SIZE + if op == 5: # block size + return self.ERASE_BLOCK_SIZE + if op == 6: # erase block + return 0 + + +bdev = RAMBlockDevice(64) +uos.VfsFat.mkfs(bdev) +uos.mount(uos.VfsFat(bdev), "/") + +content_good = """ +# comment +key0 = "hello world" +key1 = 7 +key2= "\n" +key3 ="\u00c1x" +key4 = "\U000000c1x" +key5 = "\f\"\\" +key6 = "\t\r\b" +key7 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +key8 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + key9 = "hello comment" # comment + key10 = 0x7f # comment +key11 = 0 +[section] +subvalue = "hi" +""" + +content_bad = [ + 'key = "\n', + 'key = """\n', + "key =\n", + 'key="', +] + + +def run_test(key, content): + with open("/settings.toml", "w") as f: + f.write(content) + + try: + v = uos.getenv(key) + print(key, repr(v)) + except Exception as e: + print(key, str(e)) + + +for i in range(13): + run_test(f"key{i}", content_good) + +for content in content_bad: + run_test("key", content) diff --git a/tests/circuitpython/getenv.py.exp b/tests/circuitpython/getenv.py.exp new file mode 100644 index 0000000000..f83143c80b --- /dev/null +++ b/tests/circuitpython/getenv.py.exp @@ -0,0 +1,17 @@ +key0 'hello world' +key1 7 +key2 Invalid byte '\n' +key3 'Áx' +key4 'Áx' +key5 Invalid byte '\\' +key6 '\t\r\x08' +key7 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' +key8 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' +key9 'hello comment' +key10 127 +key11 0 +key12 None +key Invalid byte '\n' +key Invalid byte '"' +key invalid syntax for integer with base 10: '' +key Invalid byte 'EOF' diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index 782ab95cc8..582d90e1bc 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -32,19 +32,18 @@ mport builtins micropython _asyncio _thread _uasyncio aesio array binascii bitmaptools btree cexample cmath -collections cppexample displayio dotenv -errno ffi framebuf gc -gifio hashlib json math -qrio rainbowio re sys -termios traceback ubinascii uctypes -uerrno uheapq uio ujson -ulab ulab.numpy ulab.numpy.fft -ulab.numpy.linalg ulab.scipy -ulab.scipy.linalg ulab.scipy.optimize -ulab.scipy.signal ulab.scipy.special -ulab.utils uos urandom ure -uselect ustruct utime utimeq -uzlib zlib +collections cppexample displayio errno +ffi framebuf gc gifio +hashlib json math qrio +rainbowio re sys termios +traceback ubinascii uctypes uerrno +uheapq uio ujson ulab +ulab.numpy ulab.numpy.fft ulab.numpy.linalg +ulab.scipy ulab.scipy.linalg +ulab.scipy.optimize ulab.scipy.signal +ulab.scipy.special ulab.utils uos +urandom ure uselect ustruct +utime utimeq uzlib zlib ime utime utimeq