Cache AST module parsing in hassfest (#132244)

This commit is contained in:
epenet 2024-12-06 20:55:34 +01:00 committed by GitHub
parent e54d929573
commit 9771998415
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 32 additions and 10 deletions

View File

@ -1 +1,14 @@
"""Manifest validator."""
import ast
from functools import lru_cache
from pathlib import Path
@lru_cache
def ast_parse_module(file_path: Path) -> ast.Module:
"""Parse a module.
Cached to avoid parsing the same file for each plugin.
"""
return ast.parse(file_path.read_text())

View File

@ -6,6 +6,7 @@ import ast
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN
from . import ast_parse_module
from .model import Config, Integration
CONFIG_SCHEMA_IGNORE = {
@ -60,7 +61,7 @@ def _validate_integration(config: Config, integration: Integration) -> None:
# Virtual integrations don't have any implementation
return
init = ast.parse(init_file.read_text())
init = ast_parse_module(init_file)
# No YAML Support
if not _has_function(
@ -81,7 +82,7 @@ def _validate_integration(config: Config, integration: Integration) -> None:
config_file = integration.path / "config.py"
if config_file.is_file():
config_module = ast.parse(config_file.read_text())
config_module = ast_parse_module(config_file)
if _has_function(config_module, ast.AsyncFunctionDef, "async_validate_config"):
return

View File

@ -10,6 +10,7 @@ from pathlib import Path
from homeassistant.const import Platform
from homeassistant.requirements import DISCOVERY_INTEGRATIONS
from . import ast_parse_module
from .model import Config, Integration
@ -33,7 +34,7 @@ class ImportCollector(ast.NodeVisitor):
self._cur_fil_dir = fil.relative_to(self.integration.path)
self.referenced[self._cur_fil_dir] = set()
try:
self.visit(ast.parse(fil.read_text()))
self.visit(ast_parse_module(fil))
except SyntaxError as e:
e.add_note(f"File: {fil}")
raise

View File

@ -5,6 +5,7 @@ https://developers.home-assistant.io/docs/core/integration-quality-scale/rules/c
import ast
from script.hassfest import ast_parse_module
from script.hassfest.model import Integration
@ -20,7 +21,7 @@ def validate(integration: Integration) -> list[str] | None:
"""Validate that the integration has a config flow."""
init_file = integration.path / "__init__.py"
init = ast.parse(init_file.read_text())
init = ast_parse_module(init_file)
if not _has_unload_entry_function(init):
return [

View File

@ -5,6 +5,7 @@ https://developers.home-assistant.io/docs/core/integration-quality-scale/rules/d
import ast
from script.hassfest import ast_parse_module
from script.hassfest.model import Integration
DIAGNOSTICS_FUNCTIONS = {
@ -31,7 +32,7 @@ def validate(integration: Integration) -> list[str] | None:
"(is missing diagnostics.py)",
]
diagnostics = ast.parse(diagnostics_file.read_text())
diagnostics = ast_parse_module(diagnostics_file)
if not _has_diagnostics_function(diagnostics):
return [

View File

@ -5,6 +5,7 @@ https://developers.home-assistant.io/docs/core/integration-quality-scale/rules/d
import ast
from script.hassfest import ast_parse_module
from script.hassfest.model import Integration
MANIFEST_KEYS = [
@ -49,7 +50,7 @@ def validate(integration: Integration) -> list[str] | None:
return None
# Fallback => check config_flow step
config_flow = ast.parse(config_flow_file.read_text())
config_flow = ast_parse_module(config_flow_file)
if not (_has_discovery_function(config_flow)):
return [
f"Integration is missing one of {CONFIG_FLOW_STEPS} "

View File

@ -5,6 +5,7 @@ https://developers.home-assistant.io/docs/core/integration-quality-scale/rules/r
import ast
from script.hassfest import ast_parse_module
from script.hassfest.model import Integration
@ -20,7 +21,7 @@ def validate(integration: Integration) -> list[str] | None:
"""Validate that the integration has a reauthentication flow."""
config_flow_file = integration.path / "config_flow.py"
config_flow = ast.parse(config_flow_file.read_text())
config_flow = ast_parse_module(config_flow_file)
if not _has_step_reauth_function(config_flow):
return [

View File

@ -5,6 +5,7 @@ https://developers.home-assistant.io/docs/core/integration-quality-scale/rules/r
import ast
from script.hassfest import ast_parse_module
from script.hassfest.model import Integration
@ -20,7 +21,7 @@ def validate(integration: Integration) -> list[str] | None:
"""Validate that the integration has a reconfiguration flow."""
config_flow_file = integration.path / "config_flow.py"
config_flow = ast.parse(config_flow_file.read_text())
config_flow = ast_parse_module(config_flow_file)
if not _has_step_reconfigure_function(config_flow):
return [

View File

@ -5,6 +5,7 @@ https://developers.home-assistant.io/docs/core/integration-quality-scale/rules/r
import ast
from script.hassfest import ast_parse_module
from script.hassfest.model import Integration
@ -35,7 +36,7 @@ def _get_setup_entry_function(module: ast.Module) -> ast.AsyncFunctionDef | None
def validate(integration: Integration) -> list[str] | None:
"""Validate correct use of ConfigEntry.runtime_data."""
init_file = integration.path / "__init__.py"
init = ast.parse(init_file.read_text())
init = ast_parse_module(init_file)
# Should not happen, but better to be safe
if not (async_setup_entry := _get_setup_entry_function(init)):

View File

@ -5,6 +5,7 @@ https://developers.home-assistant.io/docs/core/integration-quality-scale/rules/u
import ast
from script.hassfest import ast_parse_module
from script.hassfest.model import Integration
@ -36,7 +37,7 @@ def validate(integration: Integration) -> list[str] | None:
return None
config_flow_file = integration.path / "config_flow.py"
config_flow = ast.parse(config_flow_file.read_text())
config_flow = ast_parse_module(config_flow_file)
if not (
_has_abort_entries_match(config_flow)