1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-31 07:03:55 +00:00
This commit is contained in:
J. Nick Koston
2025-10-26 01:03:15 -07:00
parent 9e77ece7ce
commit 23207f0074
6 changed files with 35 additions and 46 deletions

View File

@@ -16,7 +16,12 @@ from esphome.const import (
CONF_UPDATE_INTERVAL,
)
from esphome.core import ID
from esphome.cpp_generator import MockObj, MockObjClass, TemplateArgsType
from esphome.cpp_generator import (
LambdaExpression,
MockObj,
MockObjClass,
TemplateArgsType,
)
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
from esphome.types import ConfigType
from esphome.util import Registry
@@ -102,25 +107,34 @@ StatelessLambdaCondition = cg.esphome_ns.class_("StatelessLambdaCondition", Cond
ForCondition = cg.esphome_ns.class_("ForCondition", Condition, cg.Component)
def use_stateless_lambda_if_applicable(id_obj, lambda_expr, stateless_class):
"""Return appropriate ID for lambda based on whether it has capture.
def new_lambda_pvariable(
id_obj: ID,
lambda_expr: LambdaExpression,
stateless_class: MockObjClass,
template_arg: cg.TemplateArguments | None = None,
) -> MockObj:
"""Create Pvariable for lambda, using stateless class if applicable.
For stateless lambdas (empty capture), returns a copy of id_obj with type
set to stateless_class to use function pointer instead of std::function.
Otherwise returns the original ID unchanged.
Combines ID selection and Pvariable creation in one call. For stateless
lambdas (empty capture), uses function pointer instead of std::function.
Args:
id_obj: The ID object (action_id, condition_id, or filter_id)
lambda_expr: The lambda expression object
stateless_class: The stateless class to use (StatelessLambdaAction, StatelessLambdaCondition, or StatelessLambdaFilter)
stateless_class: The stateless class to use for stateless lambdas
template_arg: Optional template arguments (for actions/conditions)
Returns:
ID to use with cg.new_Pvariable() - either original or modified copy
The created Pvariable
"""
# For stateless lambdas, use function pointer instead of std::function
if lambda_expr.capture == "":
id_obj = id_obj.copy()
id_obj.type = stateless_class
return id_obj
if template_arg is not None:
return cg.new_Pvariable(id_obj, template_arg, lambda_expr)
return cg.new_Pvariable(id_obj, lambda_expr)
def validate_automation(extra_schema=None, extra_validators=None, single=False):
@@ -263,12 +277,8 @@ async def lambda_condition_to_code(
args: TemplateArgsType,
) -> MockObj:
lambda_ = await cg.process_lambda(config, args, return_type=bool)
return cg.new_Pvariable(
use_stateless_lambda_if_applicable(
condition_id, lambda_, StatelessLambdaCondition
),
template_arg,
lambda_,
return new_lambda_pvariable(
condition_id, lambda_, StatelessLambdaCondition, template_arg
)
@@ -435,11 +445,7 @@ async def lambda_action_to_code(
args: TemplateArgsType,
) -> MockObj:
lambda_ = await cg.process_lambda(config, args, return_type=cg.void)
return cg.new_Pvariable(
use_stateless_lambda_if_applicable(action_id, lambda_, StatelessLambdaAction),
template_arg,
lambda_,
)
return new_lambda_pvariable(action_id, lambda_, StatelessLambdaAction, template_arg)
@register_action(

View File

@@ -300,10 +300,7 @@ async def lambda_filter_to_code(config, filter_id):
lambda_ = await cg.process_lambda(
config, [(bool, "x")], return_type=cg.optional.template(bool)
)
filter_id = automation.use_stateless_lambda_if_applicable(
filter_id, lambda_, StatelessLambdaFilter
)
return cg.new_Pvariable(filter_id, lambda_)
return automation.new_lambda_pvariable(filter_id, lambda_, StatelessLambdaFilter)
@register_filter(

View File

@@ -430,12 +430,8 @@ async def logger_log_action_to_code(config, action_id, template_arg, args):
text = str(cg.statement(esp_log(config[CONF_TAG], config[CONF_FORMAT], *args_)))
lambda_ = await cg.process_lambda(Lambda(text), args, return_type=cg.void)
return cg.new_Pvariable(
automation.use_stateless_lambda_if_applicable(
action_id, lambda_, StatelessLambdaAction
),
template_arg,
lambda_,
return automation.new_lambda_pvariable(
action_id, lambda_, StatelessLambdaAction, template_arg
)
@@ -461,12 +457,8 @@ async def logger_set_level_to_code(config, action_id, template_arg, args):
text = str(cg.statement(logger.set_log_level(level)))
lambda_ = await cg.process_lambda(Lambda(text), args, return_type=cg.void)
return cg.new_Pvariable(
automation.use_stateless_lambda_if_applicable(
action_id, lambda_, StatelessLambdaAction
),
template_arg,
lambda_,
return automation.new_lambda_pvariable(
action_id, lambda_, StatelessLambdaAction, template_arg
)

View File

@@ -574,10 +574,7 @@ async def lambda_filter_to_code(config, filter_id):
lambda_ = await cg.process_lambda(
config, [(float, "x")], return_type=cg.optional.template(float)
)
filter_id = automation.use_stateless_lambda_if_applicable(
filter_id, lambda_, StatelessLambdaFilter
)
return cg.new_Pvariable(filter_id, lambda_)
return automation.new_lambda_pvariable(filter_id, lambda_, StatelessLambdaFilter)
DELTA_SCHEMA = cv.Schema(

View File

@@ -71,10 +71,7 @@ async def lambda_filter_to_code(config, filter_id):
lambda_ = await cg.process_lambda(
config, [(cg.std_string, "x")], return_type=cg.optional.template(cg.std_string)
)
filter_id = automation.use_stateless_lambda_if_applicable(
filter_id, lambda_, StatelessLambdaFilter
)
return cg.new_Pvariable(filter_id, lambda_)
return automation.new_lambda_pvariable(filter_id, lambda_, StatelessLambdaFilter)
@FILTER_REGISTRY.register("to_upper", ToUpperFilter, {})

View File

@@ -58,7 +58,7 @@ def test_text_config_value_mode_set(generate_main):
def test_text_config_lamda_is_set(generate_main):
"""
Test if lambda is set for lambda mode
Test if lambda is set for lambda mode (optimized with stateless lambda)
"""
# Given
@@ -66,5 +66,5 @@ def test_text_config_lamda_is_set(generate_main):
main_cpp = generate_main("tests/component_tests/text/test_text.yaml")
# Then
assert "it_4->set_template([=]() -> esphome::optional<std::string> {" in main_cpp
assert "it_4->set_template(+[]() -> esphome::optional<std::string> {" in main_cpp
assert 'return std::string{"Hello"};' in main_cpp