Merge pull request #7321 from jepler/dotenv-becomes-toml
Dotenv becomes toml
This commit is contained in:
commit
398b7c26ca
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@
|
|||||||
!atmel-samd/asf/**/*.a
|
!atmel-samd/asf/**/*.a
|
||||||
*.elf
|
*.elf
|
||||||
*.bin
|
*.bin
|
||||||
|
!*.toml.bin
|
||||||
*.map
|
*.map
|
||||||
*.hex
|
*.hex
|
||||||
*.dis
|
*.dis
|
||||||
|
@ -49,8 +49,8 @@
|
|||||||
#include "shared-bindings/_bleio/ScanEntry.h"
|
#include "shared-bindings/_bleio/ScanEntry.h"
|
||||||
#include "shared-bindings/time/__init__.h"
|
#include "shared-bindings/time/__init__.h"
|
||||||
|
|
||||||
#if CIRCUITPY_DOTENV
|
#if CIRCUITPY_OS_GETENV
|
||||||
#include "shared-module/dotenv/__init__.h"
|
#include "shared-bindings/os/__init__.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION))
|
#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) {
|
STATIC void bleio_adapter_hci_init(bleio_adapter_obj_t *self) {
|
||||||
mp_int_t name_len = 0;
|
mp_int_t name_len = 0;
|
||||||
|
|
||||||
#if CIRCUITPY_DOTENV
|
#if CIRCUITPY_OS_GETENV
|
||||||
char ble_name[32];
|
mp_obj_t name = common_hal_os_getenv("CIRCUITPY_BLE_NAME", mp_const_none);
|
||||||
name_len = dotenv_get_key("/.env", "CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name) - 1);
|
if (name != mp_const_none) {
|
||||||
if (name_len > 0) {
|
mp_arg_validate_type_string(name, MP_QSTR_CIRCUITPY_BLE_NAME);
|
||||||
self->name = mp_obj_new_str(ble_name, (size_t)name_len);
|
self->name = name;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (name_len <= 0) {
|
if (!self->name) {
|
||||||
name_len = sizeof(default_ble_name);
|
name_len = sizeof(default_ble_name);
|
||||||
bt_addr_t addr;
|
bt_addr_t addr;
|
||||||
hci_check_error(hci_read_bd_addr(&addr));
|
hci_check_error(hci_read_bd_addr(&addr));
|
||||||
|
@ -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
|
keys. This method *does not* make them secure. It only separates them from the
|
||||||
code.
|
code.
|
||||||
|
|
||||||
CircuitPython supports these by mimicking the `dotenv <https://github.com/theskumar/python-dotenv>`_
|
CircuitPython uses a file called ``settings.toml`` at the drive root (no
|
||||||
CPython library. Other languages such as Javascript, PHP and Ruby also have
|
folder) as the environment. User code can access the values from the file
|
||||||
dotenv libraries.
|
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
|
CircuitPython only supports a subset of the full toml specification, see below
|
||||||
example:
|
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'
|
File format example:
|
||||||
# Comment
|
|
||||||
KEY2='value2
|
|
||||||
is multiple lines'
|
|
||||||
|
|
||||||
CircuitPython uses the ``.env`` at the drive root (no folder) as the environment.
|
.. code-block::
|
||||||
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()`
|
str_key="Hello world" # with trailing comment
|
||||||
will parse the ``/.env`` on every access.
|
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
|
CircuitPython behavior
|
||||||
----------------------
|
----------------------
|
||||||
|
@ -46,7 +46,7 @@ connection, the central device can discover two default services. One for file t
|
|||||||
CircuitPython specifically that includes serial characteristics.
|
CircuitPython specifically that includes serial characteristics.
|
||||||
|
|
||||||
To change the default BLE advertising name without (or before) running user code, the desired name
|
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.
|
30 characters depending on the port's settings and will be truncated if longer.
|
||||||
|
|
||||||
### File Transfer API
|
### File Transfer API
|
||||||
@ -69,21 +69,21 @@ Read-only characteristic that returns the UTF-8 encoded version string.
|
|||||||
|
|
||||||
## Web
|
## 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
|
`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
|
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.
|
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
|
```bash
|
||||||
# To auto-connect to Wi-Fi
|
# To auto-connect to Wi-Fi
|
||||||
CIRCUITPY_WIFI_SSID='scottswifi'
|
CIRCUITPY_WIFI_SSID="scottswifi"
|
||||||
CIRCUITPY_WIFI_PASSWORD='secretpassword'
|
CIRCUITPY_WIFI_PASSWORD="secretpassword"
|
||||||
|
|
||||||
# To enable modifying files from the web. Change this too!
|
# To enable modifying files from the web. Change this too!
|
||||||
# Leave the User field blank in the browser.
|
# Leave the User field blank in the browser.
|
||||||
CIRCUITPY_WEB_API_PASSWORD='passw0rd'
|
CIRCUITPY_WEB_API_PASSWORD="passw0rd"
|
||||||
|
|
||||||
CIRCUITPY_WEB_API_PORT=80
|
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
|
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.
|
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
|
server will respond with `403 Forbidden`. When a password is set, but not provided in a request, it
|
||||||
will respond `401 Unauthorized`.
|
will respond `401 Unauthorized`.
|
||||||
|
|
||||||
|
@ -1002,6 +1002,10 @@ msgstr ""
|
|||||||
msgid "File exists"
|
msgid "File exists"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: shared-module/os/getenv.c
|
||||||
|
msgid "File not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: ports/atmel-samd/common-hal/canio/Listener.c
|
#: ports/atmel-samd/common-hal/canio/Listener.c
|
||||||
#: ports/espressif/common-hal/canio/Listener.c
|
#: ports/espressif/common-hal/canio/Listener.c
|
||||||
#: ports/stm/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/Display.c
|
||||||
#: shared-bindings/displayio/EPaperDisplay.c
|
#: shared-bindings/displayio/EPaperDisplay.c
|
||||||
#: shared-bindings/framebufferio/FramebufferDisplay.c
|
#: shared-bindings/framebufferio/FramebufferDisplay.c
|
||||||
|
#: shared-module/displayio/Display.c
|
||||||
|
#: shared-module/framebufferio/FramebufferDisplay.c
|
||||||
msgid "Group already used"
|
msgid "Group already used"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1184,6 +1190,7 @@ msgid "Internal define error"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ports/espressif/common-hal/paralleldisplay/ParallelBus.c
|
#: ports/espressif/common-hal/paralleldisplay/ParallelBus.c
|
||||||
|
#: shared-module/os/getenv.c
|
||||||
msgid "Internal error"
|
msgid "Internal error"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1230,6 +1237,11 @@ msgstr ""
|
|||||||
msgid "Invalid bits per value"
|
msgid "Invalid bits per value"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: shared-module/os/getenv.c
|
||||||
|
#, c-format
|
||||||
|
msgid "Invalid byte %.*s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c
|
#: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Invalid data_pins[%d]"
|
msgid "Invalid data_pins[%d]"
|
||||||
@ -1260,10 +1272,18 @@ msgstr ""
|
|||||||
msgid "Invalid state"
|
msgid "Invalid state"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: shared-module/os/getenv.c
|
||||||
|
msgid "Invalid unicode escape"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: shared-bindings/aesio/aes.c
|
#: shared-bindings/aesio/aes.c
|
||||||
msgid "Key must be 16, 24, or 32 bytes long"
|
msgid "Key must be 16, 24, or 32 bytes long"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: shared-module/os/getenv.c
|
||||||
|
msgid "Key not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: shared-module/is31fl3741/FrameBuffer.c
|
#: shared-module/is31fl3741/FrameBuffer.c
|
||||||
msgid "LED mappings must match display size"
|
msgid "LED mappings must match display size"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2262,7 +2282,7 @@ msgid "Unkown error code %d"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: shared-bindings/adafruit_pixelbuf/PixelBuf.c
|
#: shared-bindings/adafruit_pixelbuf/PixelBuf.c
|
||||||
#: shared-module/adafruit_pixelbuf/PixelMap.c
|
#: shared-module/_pixelmap/PixelMap.c
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Unmatched number of items on RHS (expected %d, got %d)."
|
msgid "Unmatched number of items on RHS (expected %d, got %d)."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -3143,7 +3163,7 @@ msgstr ""
|
|||||||
msgid "index is out of bounds"
|
msgid "index is out of bounds"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: shared-bindings/adafruit_pixelbuf/PixelMap.c
|
#: shared-bindings/_pixelmap/PixelMap.c
|
||||||
msgid "index must be tuple or int"
|
msgid "index must be tuple or int"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -3522,7 +3542,7 @@ msgstr ""
|
|||||||
msgid "negative shift count"
|
msgid "negative shift count"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: shared-bindings/adafruit_pixelbuf/PixelMap.c
|
#: shared-bindings/_pixelmap/PixelMap.c
|
||||||
msgid "nested index must be int"
|
msgid "nested index must be int"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -24,13 +24,13 @@ ifeq ($(CHIP_FAMILY),samd21)
|
|||||||
CIRCUITPY_AESIO ?= 0
|
CIRCUITPY_AESIO ?= 0
|
||||||
CIRCUITPY_ATEXIT ?= 0
|
CIRCUITPY_ATEXIT ?= 0
|
||||||
CIRCUITPY_AUDIOMIXER ?= 0
|
CIRCUITPY_AUDIOMIXER ?= 0
|
||||||
|
CIRCUITPY_AUDIOMP3 ?= 0
|
||||||
CIRCUITPY_BINASCII ?= 0
|
CIRCUITPY_BINASCII ?= 0
|
||||||
CIRCUITPY_BITBANGIO ?= 0
|
CIRCUITPY_BITBANGIO ?= 0
|
||||||
CIRCUITPY_BITMAPTOOLS ?= 0
|
CIRCUITPY_BITMAPTOOLS ?= 0
|
||||||
CIRCUITPY_BUSDEVICE ?= 0
|
|
||||||
CIRCUITPY_AUDIOMP3 ?= 0
|
|
||||||
CIRCUITPY_BLEIO_HCI = 0
|
CIRCUITPY_BLEIO_HCI = 0
|
||||||
CIRCUITPY_BUILTINS_POW3 ?= 0
|
CIRCUITPY_BUILTINS_POW3 ?= 0
|
||||||
|
CIRCUITPY_BUSDEVICE ?= 0
|
||||||
CIRCUITPY_COMPUTED_GOTO_SAVE_SPACE ?= 1
|
CIRCUITPY_COMPUTED_GOTO_SAVE_SPACE ?= 1
|
||||||
CIRCUITPY_COUNTIO ?= 0
|
CIRCUITPY_COUNTIO ?= 0
|
||||||
# Not enough RAM for framebuffers
|
# Not enough RAM for framebuffers
|
||||||
@ -42,6 +42,7 @@ CIRCUITPY_I2CTARGET ?= 0
|
|||||||
CIRCUITPY_JSON ?= 0
|
CIRCUITPY_JSON ?= 0
|
||||||
CIRCUITPY_KEYPAD ?= 0
|
CIRCUITPY_KEYPAD ?= 0
|
||||||
CIRCUITPY_MSGPACK ?= 0
|
CIRCUITPY_MSGPACK ?= 0
|
||||||
|
CIRCUITPY_OS_GETENV ?= 0
|
||||||
CIRCUITPY_PIXELMAP ?= 0
|
CIRCUITPY_PIXELMAP ?= 0
|
||||||
CIRCUITPY_RE ?= 0
|
CIRCUITPY_RE ?= 0
|
||||||
CIRCUITPY_SDCARDIO ?= 0
|
CIRCUITPY_SDCARDIO ?= 0
|
||||||
|
@ -134,7 +134,8 @@ SRC_COMMON_HAL_EXPANDED = $(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \
|
|||||||
$(addprefix common-hal/, $(SRC_COMMON_HAL))
|
$(addprefix common-hal/, $(SRC_COMMON_HAL))
|
||||||
|
|
||||||
SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE)) \
|
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
|
SRC_S = supervisor/cpu.s
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@
|
|||||||
//| """
|
//| """
|
||||||
//| Configure and initialize a camera with the given properties
|
//| 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::
|
//| .. important::
|
||||||
|
@ -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);
|
MP_DEFINE_CONST_FUN_OBJ_0(espidf_get_total_psram_obj, espidf_get_total_psram);
|
||||||
|
|
||||||
//| def get_reserved_psram() -> int:
|
//| 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) {
|
STATIC mp_obj_t espidf_get_reserved_psram(void) {
|
||||||
return MP_OBJ_NEW_SMALL_INT(common_hal_espidf_get_reserved_psram());
|
return MP_OBJ_NEW_SMALL_INT(common_hal_espidf_get_reserved_psram());
|
||||||
|
@ -59,8 +59,8 @@
|
|||||||
#include "esp_bt.h"
|
#include "esp_bt.h"
|
||||||
#include "esp_nimble_hci.h"
|
#include "esp_nimble_hci.h"
|
||||||
|
|
||||||
#if CIRCUITPY_DOTENV
|
#if CIRCUITPY_OS_GETENV
|
||||||
#include "shared-module/dotenv/__init__.h"
|
#include "shared-module/os/__init__.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bleio_connection_internal_t bleio_connections[BLEIO_TOTAL_CONNECTION_COUNT];
|
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.sync_cb = _on_sync;
|
||||||
// ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
// ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||||
|
|
||||||
#if CIRCUITPY_DOTENV
|
#if CIRCUITPY_OS_GETENV
|
||||||
mp_int_t name_len = 0;
|
char ble_name[1 + MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH];
|
||||||
char ble_name[32];
|
os_getenv_err_t result = common_hal_os_getenv_str("CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name));
|
||||||
name_len = dotenv_get_key("/.env", "CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name) - 1);
|
if (result == GETENV_OK) {
|
||||||
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';
|
|
||||||
ble_svc_gap_device_name_set(ble_name);
|
ble_svc_gap_device_name_set(ble_name);
|
||||||
} else {
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
ble_svc_gap_device_name_set("CIRCUITPY");
|
ble_svc_gap_device_name_set("CIRCUITPY");
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
ble_svc_gap_device_name_set("CIRCUITPY");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Clear all of the internal connection objects.
|
// Clear all of the internal connection objects.
|
||||||
for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
|
for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
#include "shared-bindings/microcontroller/RunMode.h"
|
#include "shared-bindings/microcontroller/RunMode.h"
|
||||||
#include "shared-bindings/rtc/__init__.h"
|
#include "shared-bindings/rtc/__init__.h"
|
||||||
#include "shared-bindings/socketpool/__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/rmt.h"
|
||||||
#include "peripherals/timer.h"
|
#include "peripherals/timer.h"
|
||||||
@ -519,7 +519,7 @@ void port_idle_until_interrupt(void) {
|
|||||||
void port_post_boot_py(bool heap_valid) {
|
void port_post_boot_py(bool heap_valid) {
|
||||||
if (!heap_valid && filesystem_present()) {
|
if (!heap_valid && filesystem_present()) {
|
||||||
mp_int_t reserved;
|
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_set_reserved_psram(reserved);
|
||||||
}
|
}
|
||||||
common_hal_espidf_reserve_psram();
|
common_hal_espidf_reserve_psram();
|
||||||
|
@ -52,8 +52,9 @@
|
|||||||
#include "shared-bindings/_bleio/ScanEntry.h"
|
#include "shared-bindings/_bleio/ScanEntry.h"
|
||||||
#include "shared-bindings/time/__init__.h"
|
#include "shared-bindings/time/__init__.h"
|
||||||
|
|
||||||
#if CIRCUITPY_DOTENV
|
#if CIRCUITPY_OS_GETENV
|
||||||
#include "shared-module/dotenv/__init__.h"
|
#include "shared-bindings/os/__init__.h"
|
||||||
|
#include "shared-module/os/__init__.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS)
|
#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 - 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
|
default_ble_name[len] = '\0'; // for now we add null for compatibility with C ASCIIZ strings
|
||||||
|
|
||||||
mp_int_t name_len = 0;
|
#if CIRCUITPY_OS_GETENV
|
||||||
|
|
||||||
#if CIRCUITPY_DOTENV
|
|
||||||
char ble_name[32];
|
char ble_name[32];
|
||||||
name_len = dotenv_get_key("/.env", "CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name) - 1);
|
|
||||||
if (name_len > 0) {
|
os_getenv_err_t result = common_hal_os_getenv_str("CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name));
|
||||||
ble_name[name_len] = '\0';
|
if (result == GETENV_OK) {
|
||||||
common_hal_bleio_adapter_set_name(self, (char *)ble_name);
|
common_hal_bleio_adapter_set_name(self, ble_name);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#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) {
|
static void bluetooth_adapter_background(void *data) {
|
||||||
|
@ -422,7 +422,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(rp2pio_statemachine_write_obj, 2, rp2pio_statemachine
|
|||||||
//| ) -> None:
|
//| ) -> None:
|
||||||
//| """Write data to the TX fifo in the background, with optional looping.
|
//| """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.
|
//| 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.
|
//| Then the ``once`` and/or ``loop`` buffers are queued. and the function returns.
|
||||||
//| The ``once`` buffer (if specified) will be written just once.
|
//| The ``once`` buffer (if specified) will be written just once.
|
||||||
|
@ -4,5 +4,7 @@
|
|||||||
#define CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL (1)
|
#define CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL (1)
|
||||||
#define CIRCUITPY_DIGITALIO_HAVE_INVALID_DRIVE_MODE (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 (1)
|
||||||
#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO5, .sda = &pin_GPIO4}}
|
#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO5, .sda = &pin_GPIO4}}
|
||||||
|
@ -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) {
|
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)) {
|
if (!common_hal_wifi_radio_get_enabled(self)) {
|
||||||
mp_raise_RuntimeError(translate("Wifi is not enabled"));
|
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."));
|
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);
|
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 start = port_get_raw_ticks(NULL);
|
||||||
uint64_t deadline = start + timeout_ms;
|
uint64_t deadline = start + timeout_ms;
|
||||||
|
|
||||||
|
if (connection_unchanged(self, ssid, ssid_len)) {
|
||||||
|
return WIFI_RADIO_ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
// disconnect
|
// disconnect
|
||||||
common_hal_wifi_radio_stop_station(self);
|
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) {
|
switch (result) {
|
||||||
case CYW43_LINK_UP:
|
case CYW43_LINK_UP:
|
||||||
|
memcpy(self->connected_ssid, ssid, ssid_len);
|
||||||
|
self->connected_ssid_len = ssid_len;
|
||||||
bindings_cyw43_wifi_enforce_pm();
|
bindings_cyw43_wifi_enforce_pm();
|
||||||
return WIFI_RADIO_ERROR_NONE;
|
return WIFI_RADIO_ERROR_NONE;
|
||||||
case CYW43_LINK_FAIL:
|
case CYW43_LINK_FAIL:
|
||||||
|
@ -35,6 +35,8 @@ typedef struct {
|
|||||||
mp_obj_base_t base;
|
mp_obj_base_t base;
|
||||||
char hostname[254]; // hostname max is 253 chars, + 1 for trailing NUL
|
char hostname[254]; // hostname max is 253 chars, + 1 for trailing NUL
|
||||||
wifi_scannednetworks_obj_t *current_scan;
|
wifi_scannednetworks_obj_t *current_scan;
|
||||||
|
uint8_t connected_ssid[32];
|
||||||
|
uint8_t connected_ssid_len;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
} wifi_radio_obj_t;
|
} wifi_radio_obj_t;
|
||||||
|
|
||||||
|
@ -127,6 +127,13 @@ safe_mode_t port_init(void) {
|
|||||||
(&_ld_dtcm_bss_start)[i] = 0;
|
(&_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 everything into a known state before board_init.
|
||||||
reset_port();
|
reset_port();
|
||||||
|
|
||||||
@ -140,10 +147,6 @@ safe_mode_t port_init(void) {
|
|||||||
// Check brownout.
|
// Check brownout.
|
||||||
|
|
||||||
#if CIRCUITPY_CYW43
|
#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
|
// A small number of samples of pico w need an additional delay before
|
||||||
// initializing the cyw43 chip. Delays inside cyw43_arch_init_with_country
|
// initializing the cyw43 chip. Delays inside cyw43_arch_init_with_country
|
||||||
// are intended to meet the power on timing requirements, but apparently
|
// are intended to meet the power on timing requirements, but apparently
|
||||||
|
@ -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_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) {
|
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));
|
const char *s = getenv(mp_obj_str_get_str(var_in));
|
||||||
if (s == NULL) {
|
if (s == NULL) {
|
||||||
return mp_const_none;
|
return mp_const_none;
|
||||||
|
@ -33,7 +33,6 @@ SRC_BITMAP := \
|
|||||||
shared-bindings/aesio/__init__.c \
|
shared-bindings/aesio/__init__.c \
|
||||||
shared-bindings/bitmaptools/__init__.c \
|
shared-bindings/bitmaptools/__init__.c \
|
||||||
shared-bindings/displayio/Bitmap.c \
|
shared-bindings/displayio/Bitmap.c \
|
||||||
shared-bindings/dotenv/__init__.c \
|
|
||||||
shared-bindings/rainbowio/__init__.c \
|
shared-bindings/rainbowio/__init__.c \
|
||||||
shared-bindings/traceback/__init__.c \
|
shared-bindings/traceback/__init__.c \
|
||||||
shared-bindings/util.c \
|
shared-bindings/util.c \
|
||||||
@ -45,7 +44,7 @@ SRC_BITMAP := \
|
|||||||
shared-module/displayio/Bitmap.c \
|
shared-module/displayio/Bitmap.c \
|
||||||
shared-module/displayio/ColorConverter.c \
|
shared-module/displayio/ColorConverter.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/rainbowio/__init__.c \
|
||||||
shared-module/traceback/__init__.c \
|
shared-module/traceback/__init__.c \
|
||||||
shared-module/zlib/__init__.c \
|
shared-module/zlib/__init__.c \
|
||||||
@ -56,7 +55,7 @@ CFLAGS += \
|
|||||||
-DCIRCUITPY_AESIO=1 \
|
-DCIRCUITPY_AESIO=1 \
|
||||||
-DCIRCUITPY_BITMAPTOOLS=1 \
|
-DCIRCUITPY_BITMAPTOOLS=1 \
|
||||||
-DCIRCUITPY_DISPLAYIO_UNIX=1 \
|
-DCIRCUITPY_DISPLAYIO_UNIX=1 \
|
||||||
-DCIRCUITPY_DOTENV=1 \
|
-DCIRCUITPY_OS_GETENV=1 \
|
||||||
-DCIRCUITPY_GIFIO=1 \
|
-DCIRCUITPY_GIFIO=1 \
|
||||||
-DCIRCUITPY_RAINBOWIO=1 \
|
-DCIRCUITPY_RAINBOWIO=1 \
|
||||||
-DCIRCUITPY_TRACEBACK=1 \
|
-DCIRCUITPY_TRACEBACK=1 \
|
||||||
|
@ -182,9 +182,6 @@ endif
|
|||||||
ifeq ($(CIRCUITPY_DISPLAYIO),1)
|
ifeq ($(CIRCUITPY_DISPLAYIO),1)
|
||||||
SRC_PATTERNS += displayio/%
|
SRC_PATTERNS += displayio/%
|
||||||
endif
|
endif
|
||||||
ifeq ($(CIRCUITPY_DOTENV),1)
|
|
||||||
SRC_PATTERNS += dotenv/%
|
|
||||||
endif
|
|
||||||
ifeq ($(CIRCUITPY__EVE),1)
|
ifeq ($(CIRCUITPY__EVE),1)
|
||||||
SRC_PATTERNS += _eve/%
|
SRC_PATTERNS += _eve/%
|
||||||
endif
|
endif
|
||||||
@ -589,7 +586,6 @@ SRC_SHARED_MODULE_ALL = \
|
|||||||
displayio/TileGrid.c \
|
displayio/TileGrid.c \
|
||||||
displayio/area.c \
|
displayio/area.c \
|
||||||
displayio/__init__.c \
|
displayio/__init__.c \
|
||||||
dotenv/__init__.c \
|
|
||||||
floppyio/__init__.c \
|
floppyio/__init__.c \
|
||||||
fontio/BuiltinFont.c \
|
fontio/BuiltinFont.c \
|
||||||
fontio/__init__.c \
|
fontio/__init__.c \
|
||||||
@ -718,6 +714,7 @@ endif
|
|||||||
SRC_SHARED_MODULE_INTERNAL = \
|
SRC_SHARED_MODULE_INTERNAL = \
|
||||||
$(filter $(SRC_PATTERNS), \
|
$(filter $(SRC_PATTERNS), \
|
||||||
displayio/display_core.c \
|
displayio/display_core.c \
|
||||||
|
os/getenv.c \
|
||||||
usb/utf16le.c \
|
usb/utf16le.c \
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -208,9 +208,6 @@ CFLAGS += -DCIRCUITPY_BITMAPTOOLS=$(CIRCUITPY_BITMAPTOOLS)
|
|||||||
CFLAGS += -DCIRCUITPY_FRAMEBUFFERIO=$(CIRCUITPY_FRAMEBUFFERIO)
|
CFLAGS += -DCIRCUITPY_FRAMEBUFFERIO=$(CIRCUITPY_FRAMEBUFFERIO)
|
||||||
CFLAGS += -DCIRCUITPY_VECTORIO=$(CIRCUITPY_VECTORIO)
|
CFLAGS += -DCIRCUITPY_VECTORIO=$(CIRCUITPY_VECTORIO)
|
||||||
|
|
||||||
CIRCUITPY_DOTENV ?= $(CIRCUITPY_FULL_BUILD)
|
|
||||||
CFLAGS += -DCIRCUITPY_DOTENV=$(CIRCUITPY_DOTENV)
|
|
||||||
|
|
||||||
CIRCUITPY_DUALBANK ?= 0
|
CIRCUITPY_DUALBANK ?= 0
|
||||||
CFLAGS += -DCIRCUITPY_DUALBANK=$(CIRCUITPY_DUALBANK)
|
CFLAGS += -DCIRCUITPY_DUALBANK=$(CIRCUITPY_DUALBANK)
|
||||||
|
|
||||||
@ -218,6 +215,9 @@ CFLAGS += -DCIRCUITPY_DUALBANK=$(CIRCUITPY_DUALBANK)
|
|||||||
CIRCUITPY_ENABLE_MPY_NATIVE ?= 0
|
CIRCUITPY_ENABLE_MPY_NATIVE ?= 0
|
||||||
CFLAGS += -DCIRCUITPY_ENABLE_MPY_NATIVE=$(CIRCUITPY_ENABLE_MPY_NATIVE)
|
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)
|
CIRCUITPY_ERRNO ?= $(CIRCUITPY_FULL_BUILD)
|
||||||
CFLAGS += -DCIRCUITPY_ERRNO=$(CIRCUITPY_ERRNO)
|
CFLAGS += -DCIRCUITPY_ERRNO=$(CIRCUITPY_ERRNO)
|
||||||
|
|
||||||
|
@ -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 <string.h>
|
|
||||||
|
|
||||||
#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 <https://saurabh-kumar.com/python-dotenv/>`_. 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);
|
|
@ -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 <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#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
|
|
@ -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) {
|
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 };
|
enum { ARG_key, ARG_default };
|
||||||
static const mp_arg_t allowed_args[] = {
|
static const mp_arg_t allowed_args[] = {
|
||||||
{ MP_QSTR_key, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
{ 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);
|
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);
|
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);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(os_getenv_obj, 1, os_getenv);
|
||||||
|
|
||||||
|
@ -38,6 +38,8 @@ mp_obj_t common_hal_os_uname(void);
|
|||||||
void common_hal_os_chdir(const char *path);
|
void common_hal_os_chdir(const char *path);
|
||||||
mp_obj_t common_hal_os_getcwd(void);
|
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(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);
|
mp_obj_t common_hal_os_listdir(const char *path);
|
||||||
void common_hal_os_mkdir(const char *path);
|
void common_hal_os_mkdir(const char *path);
|
||||||
void common_hal_os_remove(const char *path);
|
void common_hal_os_remove(const char *path);
|
||||||
|
@ -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 <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
@ -36,10 +36,6 @@
|
|||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
#include "shared-bindings/os/__init__.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
|
// This provides all VFS related OS functions so that ports can share the code
|
||||||
// as needed. It does not provide uname.
|
// as needed. It does not provide uname.
|
||||||
|
|
||||||
@ -111,16 +107,6 @@ mp_obj_t common_hal_os_getcwd(void) {
|
|||||||
return mp_vfs_getcwd();
|
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 common_hal_os_listdir(const char *path) {
|
||||||
mp_obj_t path_out;
|
mp_obj_t path_out;
|
||||||
mp_vfs_mount_t *vfs = lookup_dir_path(path, &path_out);
|
mp_vfs_mount_t *vfs = lookup_dir_path(path, &path_out);
|
||||||
|
@ -24,13 +24,23 @@
|
|||||||
* THE SOFTWARE.
|
* 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.
|
// 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
|
// Returns GETENV_OK and sets value to the read value. Returns
|
||||||
// and the value (including the terminating '\0') fits strictly within
|
// GETENV_ERR_... if the value was not numeric. allocation-free.
|
||||||
// value_len bytes.
|
// If any error code is returned, value is guaranteed not modified
|
||||||
bool dotenv_get_key_terminated(const char *path, const char *key, char *value, mp_int_t value_len);
|
os_getenv_err_t common_hal_os_getenv_int(const char *key, mp_int_t *value);
|
||||||
|
|
||||||
// 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);
|
|
391
shared-module/os/getenv.c
Normal file
391
shared-module/os/getenv.c
Normal file
@ -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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
@ -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, "/.metadata_never_index");
|
||||||
make_empty_file(&vfs_fat->fatfs, "/.Trashes");
|
make_empty_file(&vfs_fat->fatfs, "/.Trashes");
|
||||||
make_empty_file(&vfs_fat->fatfs, "/.fseventsd/no_log");
|
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 a sample code.py file
|
||||||
make_sample_code_file(&vfs_fat->fatfs);
|
make_sample_code_file(&vfs_fat->fatfs);
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ async function refresh_list() {
|
|||||||
if (f.directory) {
|
if (f.directory) {
|
||||||
icon = "📁";
|
icon = "📁";
|
||||||
} else if(f.name.endsWith(".txt") ||
|
} else if(f.name.endsWith(".txt") ||
|
||||||
f.name.endsWith(".env") ||
|
f.name.endsWith(".toml") ||
|
||||||
f.name.endsWith(".py") ||
|
f.name.endsWith(".py") ||
|
||||||
f.name.endsWith(".js") ||
|
f.name.endsWith(".js") ||
|
||||||
f.name.endsWith(".json")) {
|
f.name.endsWith(".json")) {
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<h1><a href="/"><img src="/favicon.ico"/></a> Welcome!</h1>
|
<h1><a href="/"><img src="/favicon.ico"/></a> Welcome!</h1>
|
||||||
|
|
||||||
<p>Welcome to CircuitPython's Web API. Go to the <a href="/fs/">file browser</a> to work with files in the CIRCUITPY drive. Go to the <a href="/code/">full code editor</a> for a more enriching experience which requires an active internet connection. Go to the <a href="/cp/serial/">serial terminal</a> to see code output and interact with the REPL. Make sure you've set <code>CIRCUITPY_WEB_API_PASSWORD='somepassword'</code> in <code>/.env</code>. Provide the password when the browser prompts for it. <strong>Leave the username blank.</strong></p>
|
<p>Welcome to CircuitPython's Web API. Go to the <a href="/fs/">file browser</a> to work with files in the CIRCUITPY drive. Go to the <a href="/code/">full code editor</a> for a more enriching experience which requires an active internet connection. Go to the <a href="/cp/serial/">serial terminal</a> to see code output and interact with the REPL. Make sure you've set <code>CIRCUITPY_WEB_API_PASSWORD='somepassword'</code> in <code>/settings.toml</code>. Provide the password when the browser prompts for it. <strong>Leave the username blank.</strong></p>
|
||||||
|
|
||||||
<h2>Device Info:</h2>
|
<h2>Device Info:</h2>
|
||||||
|
|
||||||
|
@ -64,8 +64,8 @@
|
|||||||
#include "shared-bindings/wifi/__init__.h"
|
#include "shared-bindings/wifi/__init__.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if CIRCUITPY_DOTENV
|
#if CIRCUITPY_OS_GETENV
|
||||||
#include "shared-module/dotenv/__init__.h"
|
#include "shared-module/os/__init__.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum request_state {
|
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;
|
static mdns_server_obj_t mdns;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static uint32_t web_api_port = 80;
|
static mp_int_t web_api_port = 80;
|
||||||
|
|
||||||
static socketpool_socketpool_obj_t pool;
|
static socketpool_socketpool_obj_t pool;
|
||||||
static socketpool_socket_obj_t listening;
|
static socketpool_socket_obj_t listening;
|
||||||
@ -244,22 +244,21 @@ void supervisor_web_workflow_status(void) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void supervisor_start_web_workflow(void) {
|
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 ssid[33];
|
||||||
char password[64];
|
char password[64];
|
||||||
mp_int_t ssid_len = 0;
|
|
||||||
mp_int_t password_len = 0;
|
|
||||||
|
|
||||||
#if CIRCUITPY_DOTENV
|
os_getenv_err_t result = common_hal_os_getenv_str("CIRCUITPY_WIFI_SSID", ssid, sizeof(ssid));
|
||||||
ssid_len = dotenv_get_key("/.env", "CIRCUITPY_WIFI_SSID", ssid, sizeof(ssid) - 1);
|
if (result != GETENV_OK) {
|
||||||
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)) {
|
|
||||||
return;
|
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)) {
|
if (!common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj)) {
|
||||||
common_hal_wifi_init(false);
|
common_hal_wifi_init(false);
|
||||||
common_hal_wifi_radio_set_enabled(&common_hal_wifi_radio_obj, true);
|
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.
|
// 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.
|
// 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
|
// 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
|
// network. If we are connected to a different network, then it will disconnect before
|
||||||
// attempting to connect to the given network.
|
// attempting to connect to the given network.
|
||||||
_wifi_status = common_hal_wifi_radio_connect(
|
_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);
|
0, 8, NULL, 0);
|
||||||
|
|
||||||
if (_wifi_status != WIFI_RADIO_ERROR_NONE) {
|
if (_wifi_status != WIFI_RADIO_ERROR_NONE) {
|
||||||
@ -283,21 +279,15 @@ void supervisor_start_web_workflow(void) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char port_encoded[6];
|
mp_int_t new_port = web_api_port;
|
||||||
size_t port_len = 0;
|
// (leaves new_port unchanged on any failure)
|
||||||
size_t new_port = web_api_port;
|
(void)common_hal_os_getenv_int("CIRCUITPY_WEB_API_PORT", &new_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool first_start = pool.base.type != &socketpool_socketpool_type;
|
bool first_start = pool.base.type != &socketpool_socketpool_type;
|
||||||
bool port_changed = new_port != web_api_port;
|
bool port_changed = new_port != web_api_port;
|
||||||
|
|
||||||
if (first_start) {
|
if (first_start) {
|
||||||
|
port_changed = false;
|
||||||
#if CIRCUITPY_MDNS
|
#if CIRCUITPY_MDNS
|
||||||
mdns_server_construct(&mdns, true);
|
mdns_server_construct(&mdns, true);
|
||||||
mdns.base.type = &mdns_server_type;
|
mdns.base.type = &mdns_server_type;
|
||||||
@ -326,11 +316,12 @@ void supervisor_start_web_workflow(void) {
|
|||||||
common_hal_socketpool_socket_listen(&listening, 1);
|
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[0] = ':';
|
||||||
_api_password[api_password_len + 1] = '\0';
|
_base64_in_place(_api_password, strlen(_api_password), sizeof(_api_password) - 1);
|
||||||
_base64_in_place(_api_password, api_password_len + 1, sizeof(_api_password));
|
|
||||||
}
|
}
|
||||||
#endif
|
#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.
|
// 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_list ap;
|
||||||
va_start(ap, socket);
|
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_print_t _socket_print = {socket, _print_raw};
|
||||||
mp_printf(&_socket_print, "Content-Length: %d\r\n", total_length);
|
mp_printf(&_socket_print, "Content-Length: %d\r\n", total_length);
|
||||||
// TODO: Make this a table to save space.
|
// TODO: Make this a table to save space.
|
||||||
if (_endswith(filename, ".txt") || _endswith(filename, ".py")) {
|
if (_endswith(filename, ".txt") || _endswith(filename, ".py") || _endswith(filename, ".toml")) {
|
||||||
_send_strs(socket, "Content-Type: text/plain", ";charset=UTF-8\r\n", NULL);
|
_send_strs(socket, "Content-Type:", "text/plain", ";charset=UTF-8\r\n", NULL);
|
||||||
} else if (_endswith(filename, ".js")) {
|
} 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")) {
|
} 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")) {
|
} 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 {
|
} 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);
|
_cors_header(socket, request);
|
||||||
_send_str(socket, "\r\n");
|
_send_str(socket, "\r\n");
|
||||||
|
@ -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
|
|
@ -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))}")
|
|
@ -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
|
|
79
tests/circuitpython/getenv.py
Normal file
79
tests/circuitpython/getenv.py
Normal file
@ -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)
|
17
tests/circuitpython/getenv.py.exp
Normal file
17
tests/circuitpython/getenv.py.exp
Normal file
@ -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'
|
@ -32,19 +32,18 @@ mport
|
|||||||
builtins micropython _asyncio _thread
|
builtins micropython _asyncio _thread
|
||||||
_uasyncio aesio array binascii
|
_uasyncio aesio array binascii
|
||||||
bitmaptools btree cexample cmath
|
bitmaptools btree cexample cmath
|
||||||
collections cppexample displayio dotenv
|
collections cppexample displayio errno
|
||||||
errno ffi framebuf gc
|
ffi framebuf gc gifio
|
||||||
gifio hashlib json math
|
hashlib json math qrio
|
||||||
qrio rainbowio re sys
|
rainbowio re sys termios
|
||||||
termios traceback ubinascii uctypes
|
traceback ubinascii uctypes uerrno
|
||||||
uerrno uheapq uio ujson
|
uheapq uio ujson ulab
|
||||||
ulab ulab.numpy ulab.numpy.fft
|
ulab.numpy ulab.numpy.fft ulab.numpy.linalg
|
||||||
ulab.numpy.linalg ulab.scipy
|
ulab.scipy ulab.scipy.linalg
|
||||||
ulab.scipy.linalg ulab.scipy.optimize
|
ulab.scipy.optimize ulab.scipy.signal
|
||||||
ulab.scipy.signal ulab.scipy.special
|
ulab.scipy.special ulab.utils uos
|
||||||
ulab.utils uos urandom ure
|
urandom ure uselect ustruct
|
||||||
uselect ustruct utime utimeq
|
utime utimeq uzlib zlib
|
||||||
uzlib zlib
|
|
||||||
ime
|
ime
|
||||||
|
|
||||||
utime utimeq
|
utime utimeq
|
||||||
|
Loading…
Reference in New Issue
Block a user