diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 2385f15bf6a..fd1ef7a4778 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -2337,9 +2337,15 @@ async def _add_sscanf_stub(enable_full_scanf: bool | None, variant: str) -> None FINAL priority so every request_bluetooth() call has happened. """ - if variant in ROM_SSCANF_VARIANTS or not _network_sdkconfig().bluetooth: + if ( + variant in ROM_SSCANF_VARIANTS + or idf_version() >= cv.Version(6, 0, 0) # picolibc, unmeasured + or not _network_sdkconfig().bluetooth + ): return - if keep_float_scanf(enable_full_scanf, CORE.config, "~13KB flash"): + if keep_float_scanf( + enable_full_scanf, CORE.config, "~13KB flash", "that call will abort the device" + ): return _add_wrap_stub("sscanf", "USE_ESP32_SSCANF_STUB") @@ -2702,12 +2708,12 @@ async def to_code(config): # ROM. See vasprintf_stubs.cpp. if variant in ROM_VSNPRINTF_WITHOUT_VASPRINTF_VARIANTS: _add_wrap_stub("vasprintf", "USE_ESP32_VASPRINTF_STUB") - # bluedroid's sscanf calls; see sscanf_stubs.cpp - CORE.add_job( - _add_sscanf_stub, - conf[CONF_ADVANCED].get(CONF_ENABLE_FULL_SCANF), - variant, - ) + # bluedroid's sscanf calls; see sscanf_stubs.cpp + CORE.add_job( + _add_sscanf_stub, + conf[CONF_ADVANCED].get(CONF_ENABLE_FULL_SCANF), + variant, + ) else: cg.add_build_flag("-DUSE_ARDUINO") cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ARDUINO") diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index 0acb500910c..b7129982d1f 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -274,7 +274,10 @@ async def to_code(config: ConfigType) -> None: cg.add_define("USE_ESP8266_CRASH_HANDLER") enable_scanf_float = keep_float_scanf( - config.get(CONF_ENABLE_SCANF_FLOAT), CORE.config, "~8KB flash" + config.get(CONF_ENABLE_SCANF_FLOAT), + CORE.config, + "~8KB flash", + "that call will not parse", ) extra_scripts = [ diff --git a/esphome/config_helpers.py b/esphome/config_helpers.py index f97f47683d2..986c916aacc 100644 --- a/esphome/config_helpers.py +++ b/esphome/config_helpers.py @@ -278,10 +278,13 @@ def user_code_uses_scanf_float(config: ConfigType) -> bool: return lambdas_use_scanf_float(config) or includes_use_scanf_float(config) -def keep_float_scanf(option: bool | None, config: ConfigType, flash_note: str) -> bool: +def keep_float_scanf( + option: bool | None, config: ConfigType, flash_note: str, override_note: str +) -> bool: """Resolve a tri-state float-scanf option: unset means "only if user code scans a float". - Logs when user code decides it, or when an explicit ``false`` overrides it. + Logs when user code decides it, or (with ``override_note``, the consequence) + when an explicit ``false`` overrides it. """ if option is None: if not user_code_uses_scanf_float(config): @@ -295,6 +298,7 @@ def keep_float_scanf(option: bool | None, config: ConfigType, flash_note: str) - if not option and user_code_uses_scanf_float(config): _LOGGER.warning( "Float scanf support is disabled but a lambda or include uses scanf " - "with a float format specifier; that call will not parse" + "with a float format specifier; %s", + override_note, ) return option diff --git a/tests/component_tests/esp32/config/sscanf_stub_ble_full_printf.yaml b/tests/component_tests/esp32/config/sscanf_stub_ble_full_printf.yaml new file mode 100644 index 00000000000..b346899f420 --- /dev/null +++ b/tests/component_tests/esp32/config/sscanf_stub_ble_full_printf.yaml @@ -0,0 +1,11 @@ +esphome: + name: test + +esp32: + board: esp32dev + framework: + type: esp-idf + advanced: + enable_full_printf: true + +esp32_ble_tracker: diff --git a/tests/component_tests/esp32/test_esp32.py b/tests/component_tests/esp32/test_esp32.py index 7958bd4ea25..e4e35eafeb5 100644 --- a/tests/component_tests/esp32/test_esp32.py +++ b/tests/component_tests/esp32/test_esp32.py @@ -1449,6 +1449,9 @@ def test_mbedtls_tls_openthread_requires_server_and_extras( id="sscanf_lambda_float_forced", ), pytest.param("sscanf_stub_ble_c2.yaml", "sscanf", False, id="sscanf_c2_rom"), + pytest.param( + "sscanf_stub_ble_full_printf.yaml", "sscanf", True, id="sscanf_full_printf" + ), pytest.param("exclusion_reincludes.yaml", "sscanf", False, id="sscanf_no_ble"), ], ) diff --git a/tests/unit_tests/test_config_helpers.py b/tests/unit_tests/test_config_helpers.py index 1f4f550850f..f4a1bc1d99a 100644 --- a/tests/unit_tests/test_config_helpers.py +++ b/tests/unit_tests/test_config_helpers.py @@ -288,7 +288,7 @@ def test_keep_float_scanf() -> None: """Explicit values win; unset follows whether user code scans a float.""" float_config: ConfigType = {"test": [Lambda('sscanf(b, "%f", &v)')]} int_config: ConfigType = {"test": [Lambda('sscanf(b, "%d", &v)')]} - assert keep_float_scanf(True, int_config, "x") is True - assert keep_float_scanf(False, float_config, "x") is False - assert keep_float_scanf(None, float_config, "x") is True - assert keep_float_scanf(None, int_config, "x") is False + assert keep_float_scanf(True, int_config, "x", "y") is True + assert keep_float_scanf(False, float_config, "x", "y") is False + assert keep_float_scanf(None, float_config, "x", "y") is True + assert keep_float_scanf(None, int_config, "x", "y") is False