mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 06:33:51 +00:00 
			
		
		
		
	Allow substitutions to be valid names (#4726)
This commit is contained in:
		| @@ -1,19 +1,14 @@ | ||||
| import logging | ||||
| import re | ||||
|  | ||||
| import esphome.config_validation as cv | ||||
| from esphome import core | ||||
| from esphome.const import CONF_SUBSTITUTIONS | ||||
| from esphome.const import CONF_SUBSTITUTIONS, VALID_SUBSTITUTIONS_CHARACTERS | ||||
| from esphome.yaml_util import ESPHomeDataBase, make_data_base | ||||
| from esphome.config_helpers import merge_config | ||||
|  | ||||
| CODEOWNERS = ["@esphome/core"] | ||||
| _LOGGER = logging.getLogger(__name__) | ||||
|  | ||||
| VALID_SUBSTITUTIONS_CHARACTERS = ( | ||||
|     "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" | ||||
| ) | ||||
|  | ||||
|  | ||||
| def validate_substitution_key(value): | ||||
|     value = cv.string(value) | ||||
| @@ -42,12 +37,6 @@ async def to_code(config): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| # pylint: disable=consider-using-f-string | ||||
| VARIABLE_PROG = re.compile( | ||||
|     "\\$([{0}]+|\\{{[{0}]*\\}})".format(VALID_SUBSTITUTIONS_CHARACTERS) | ||||
| ) | ||||
|  | ||||
|  | ||||
| def _expand_substitutions(substitutions, value, path, ignore_missing): | ||||
|     if "$" not in value: | ||||
|         return value | ||||
| @@ -56,7 +45,7 @@ def _expand_substitutions(substitutions, value, path, ignore_missing): | ||||
|  | ||||
|     i = 0 | ||||
|     while True: | ||||
|         m = VARIABLE_PROG.search(value, i) | ||||
|         m = cv.VARIABLE_PROG.search(value, i) | ||||
|         if not m: | ||||
|             # Nothing more to match. Done | ||||
|             break | ||||
|   | ||||
| @@ -53,6 +53,7 @@ from esphome.const import ( | ||||
|     KEY_TARGET_PLATFORM, | ||||
|     TYPE_GIT, | ||||
|     TYPE_LOCAL, | ||||
|     VALID_SUBSTITUTIONS_CHARACTERS, | ||||
| ) | ||||
| from esphome.core import ( | ||||
|     CORE, | ||||
| @@ -79,6 +80,11 @@ from esphome.yaml_util import make_data_base | ||||
|  | ||||
| _LOGGER = logging.getLogger(__name__) | ||||
|  | ||||
| # pylint: disable=consider-using-f-string | ||||
| VARIABLE_PROG = re.compile( | ||||
|     "\\$([{0}]+|\\{{[{0}]*\\}})".format(VALID_SUBSTITUTIONS_CHARACTERS) | ||||
| ) | ||||
|  | ||||
| # pylint: disable=invalid-name | ||||
|  | ||||
| Schema = _Schema | ||||
| @@ -265,6 +271,14 @@ def alphanumeric(value): | ||||
|  | ||||
| def valid_name(value): | ||||
|     value = string_strict(value) | ||||
|  | ||||
|     if CORE.vscode: | ||||
|         # If the value is a substitution, it can't be validated until the substitution | ||||
|         # is actually made. | ||||
|         sub_match = VARIABLE_PROG.search(value) | ||||
|         if sub_match: | ||||
|             return value | ||||
|  | ||||
|     for c in value: | ||||
|         if c not in ALLOWED_NAME_CHARS: | ||||
|             raise Invalid( | ||||
| @@ -447,6 +461,14 @@ def validate_id_name(value): | ||||
|         raise Invalid( | ||||
|             "Dashes are not supported in IDs, please use underscores instead." | ||||
|         ) | ||||
|  | ||||
|     if CORE.vscode: | ||||
|         # If the value is a substitution, it can't be validated until the substitution | ||||
|         # is actually made | ||||
|         sub_match = VARIABLE_PROG.match(value) | ||||
|         if sub_match: | ||||
|             return value | ||||
|  | ||||
|     valid_chars = f"{ascii_letters + digits}_" | ||||
|     for char in value: | ||||
|         if char not in valid_chars: | ||||
|   | ||||
| @@ -3,6 +3,9 @@ | ||||
| __version__ = "2023.6.0-dev" | ||||
|  | ||||
| ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" | ||||
| VALID_SUBSTITUTIONS_CHARACTERS = ( | ||||
|     "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" | ||||
| ) | ||||
|  | ||||
| PLATFORM_ESP32 = "esp32" | ||||
| PLATFORM_ESP8266 = "esp8266" | ||||
|   | ||||
| @@ -6,7 +6,7 @@ from hypothesis.strategies import one_of, text, integers, builds | ||||
|  | ||||
| from esphome import config_validation | ||||
| from esphome.config_validation import Invalid | ||||
| from esphome.core import Lambda, HexInt | ||||
| from esphome.core import CORE, Lambda, HexInt | ||||
|  | ||||
|  | ||||
| def test_check_not_templatable__invalid(): | ||||
| @@ -40,6 +40,47 @@ def test_valid_name__invalid(value): | ||||
|         config_validation.valid_name(value) | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize("value", ("${name}", "${NAME}", "$NAME", "${name}_name")) | ||||
| def test_valid_name__substitution_valid(value): | ||||
|     CORE.vscode = True | ||||
|     actual = config_validation.valid_name(value) | ||||
|     assert actual == value | ||||
|  | ||||
|     CORE.vscode = False | ||||
|     with pytest.raises(Invalid): | ||||
|         actual = config_validation.valid_name(value) | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize("value", ("{NAME}", "${A NAME}")) | ||||
| def test_valid_name__substitution_like_invalid(value): | ||||
|     with pytest.raises(Invalid): | ||||
|         config_validation.valid_name(value) | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize("value", ("myid", "anID", "SOME_ID_test", "MYID_99")) | ||||
| def test_validate_id_name__valid(value): | ||||
|     actual = config_validation.validate_id_name(value) | ||||
|  | ||||
|     assert actual == value | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize("value", ("id of mine", "id-4", "{name_id}", "id::name")) | ||||
| def test_validate_id_name__invalid(value): | ||||
|     with pytest.raises(Invalid): | ||||
|         config_validation.validate_id_name(value) | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize("value", ("${id}", "${ID}", "${ID}_test_1", "$MYID")) | ||||
| def test_validate_id_name__substitution_valid(value): | ||||
|     CORE.vscode = True | ||||
|     actual = config_validation.validate_id_name(value) | ||||
|     assert actual == value | ||||
|  | ||||
|     CORE.vscode = False | ||||
|     with pytest.raises(Invalid): | ||||
|         config_validation.validate_id_name(value) | ||||
|  | ||||
|  | ||||
| @given(one_of(integers(), text())) | ||||
| def test_string__valid(value): | ||||
|     actual = config_validation.string(value) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user