From 28e39f7f765e91355d7d837630f32e965026c4d8 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Sat, 6 Feb 2021 12:09:15 -0300 Subject: [PATCH] Add config validator location (#1490) * show validation source location for id * show validation source location for lambda * refactor lambda #line position * account content offset on made lambdas * lint * remove redundant check --- esphome/config.py | 4 ++++ esphome/config_validation.py | 14 +++++--------- esphome/core.py | 15 +-------------- esphome/cpp_generator.py | 9 ++++++++- esphome/yaml_util.py | 19 ++++++++++++------- 5 files changed, 30 insertions(+), 31 deletions(-) diff --git a/esphome/config.py b/esphome/config.py index 0484414929..5dc539f828 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -268,6 +268,8 @@ class Config(OrderedDict): data = data[item_index] except (KeyError, IndexError, TypeError): return doc_range + if isinstance(data, core.ID): + data = data.id if isinstance(data, ESPHomeDataBase) and data.esp_range is not None: doc_range = data.esp_range @@ -700,6 +702,8 @@ def line_info(obj, highlight=True): """Display line config source.""" if not highlight: return None + if isinstance(obj, core.ID): + obj = obj.id if isinstance(obj, ESPHomeDataBase) and obj.esp_range is not None: mark = obj.esp_range.start_mark source = "[source {}:{}]".format(mark.document, mark.line + 1) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index e0d4d088a9..046e9af185 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -17,10 +17,10 @@ from esphome.const import ALLOWED_NAME_CHARS, CONF_AVAILABILITY, CONF_COMMAND_TO CONF_HOUR, CONF_MINUTE, CONF_SECOND, CONF_VALUE, CONF_UPDATE_INTERVAL, CONF_TYPE_ID, \ CONF_TYPE, CONF_PACKAGES from esphome.core import CORE, HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \ - TimePeriodMilliseconds, TimePeriodSeconds, TimePeriodMinutes, DocumentLocation + TimePeriodMilliseconds, TimePeriodSeconds, TimePeriodMinutes from esphome.helpers import list_starts_with, add_class_to_obj from esphome.voluptuous_schema import _Schema -from esphome.yaml_util import ESPHomeDataBase +from esphome.yaml_util import make_data_base _LOGGER = logging.getLogger(__name__) @@ -983,11 +983,7 @@ LAMBDA_ENTITY_ID_PROG = re.compile(r'id\(\s*([a-zA-Z0-9_]+\.[.a-zA-Z0-9_]+)\s*\) def lambda_(value): """Coerce this configuration option to a lambda.""" if not isinstance(value, Lambda): - start_mark = None - if isinstance(value, ESPHomeDataBase) and value.esp_range is not None: - start_mark = DocumentLocation.copy(value.esp_range.start_mark) - start_mark.line += value.content_offset - value = Lambda(string_strict(value), start_mark) + value = make_data_base(Lambda(string_strict(value)), value) entity_id_parts = re.split(LAMBDA_ENTITY_ID_PROG, value.value) if len(entity_id_parts) != 1: entity_ids = ' '.join("'{}'".format(entity_id_parts[i]) @@ -1182,8 +1178,8 @@ class OnlyWith(Optional): # pylint: disable=unsupported-membership-test if (self._component in CORE.raw_config or (CONF_PACKAGES in CORE.raw_config and - self._component in - {list(x.keys())[0] for x in CORE.raw_config[CONF_PACKAGES].values()})): + self._component in + {list(x.keys())[0] for x in CORE.raw_config[CONF_PACKAGES].values()})): return self._default return vol.UNDEFINED diff --git a/esphome/core.py b/esphome/core.py index c9f7222a0c..57b065c488 100644 --- a/esphome/core.py +++ b/esphome/core.py @@ -227,7 +227,7 @@ LAMBDA_PROG = re.compile(r'id\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)(\.?)') class Lambda: - def __init__(self, value, start_mark=None): + def __init__(self, value): # pylint: disable=protected-access if isinstance(value, Lambda): self._value = value._value @@ -235,7 +235,6 @@ class Lambda: self._value = value self._parts = None self._requires_ids = None - self._source_location = start_mark # https://stackoverflow.com/a/241506/229052 def comment_remover(self, text): @@ -278,10 +277,6 @@ class Lambda: def __repr__(self): return f'Lambda<{self.value}>' - @property - def source_location(self): - return self._source_location - class ID: def __init__(self, id, is_declaration=False, type=None, is_manual=None): @@ -339,14 +334,6 @@ class DocumentLocation: mark.column ) - @classmethod - def copy(cls, location): - return cls( - location.document, - location.line, - location.column - ) - def __str__(self): return f'{self.document} {self.line}:{self.column}' diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index a82edfd3f7..cfa178c4f9 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -2,6 +2,7 @@ import abc import inspect import math import re +from esphome.yaml_util import ESPHomeDataBase # pylint: disable=unused-import, wrong-import-order from typing import Any, Generator, List, Optional, Tuple, Type, Union, Sequence @@ -560,7 +561,13 @@ def process_lambda( else: parts[i * 3 + 1] = var parts[i * 3 + 2] = '' - yield LambdaExpression(parts, parameters, capture, return_type, value.source_location) + + if isinstance(value, ESPHomeDataBase) and value.esp_range is not None: + location = value.esp_range.start_mark + location.line += value.content_offset + else: + location = None + yield LambdaExpression(parts, parameters, capture, return_type, location) def is_template(value): diff --git a/esphome/yaml_util.py b/esphome/yaml_util.py index 1efe179011..5909e99cb2 100644 --- a/esphome/yaml_util.py +++ b/esphome/yaml_util.py @@ -12,7 +12,7 @@ import yaml.constructor from esphome import core from esphome.config_helpers import read_config_file from esphome.core import EsphomeError, IPAddress, Lambda, MACAddress, TimePeriod, \ - DocumentRange, DocumentLocation + DocumentRange from esphome.helpers import add_class_to_obj from esphome.util import OrderedDict, filter_yaml_files @@ -42,14 +42,22 @@ class ESPHomeDataBase: if node.style is not None and node.style in '|>': self._content_offset = 1 + def from_database(self, database): + # pylint: disable=attribute-defined-outside-init + self._esp_range = database.esp_range + self._content_offset = database.content_offset + class ESPForceValue: pass -def make_data_base(value): +def make_data_base(value, from_database: ESPHomeDataBase = None): try: - return add_class_to_obj(value, ESPHomeDataBase) + value = add_class_to_obj(value, ESPHomeDataBase) + if from_database is not None: + value.from_database(from_database) + return value except TypeError: # Adding class failed, ignore error return value @@ -265,10 +273,7 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors @_add_data_ref def construct_lambda(self, node): - start_mark = DocumentLocation.from_mark(node.start_mark) - if node.style is not None and node.style in '|>': - start_mark.line += 1 - return Lambda(str(node.value), start_mark) + return Lambda(str(node.value)) @_add_data_ref def construct_force(self, node):