Refactor StorageJSON to keep loaded_integrations a set until its converted to JSON (#5793)

* Refactor StorageJSON to keep loaded_integrations a set until its converted to a dict

after #5792 we will be checking loaded_integrations often. ESPHome
core keep uses a set, but it would get converted to a list when
passed through StorageJSON. Keep it a set until its needed to
be read/write to JSON so we do not have to linear searches on it
since they have a time complexity of O(n) vs O(1)

* legacy
This commit is contained in:
J. Nick Koston 2023-11-19 21:31:00 -06:00 committed by GitHub
parent cd9bf29df1
commit 2aaee81313
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 45 additions and 54 deletions

View file

@ -285,7 +285,7 @@ class DashboardEntry:
"name": self.name, "name": self.name,
"friendly_name": self.friendly_name, "friendly_name": self.friendly_name,
"configuration": self.filename, "configuration": self.filename,
"loaded_integrations": self.loaded_integrations, "loaded_integrations": sorted(self.loaded_integrations),
"deployed_version": self.update_old, "deployed_version": self.update_old,
"current_version": self.update_new, "current_version": self.update_new,
"path": self.path, "path": self.path,
@ -381,7 +381,7 @@ class DashboardEntry:
return const.__version__ return const.__version__
@property @property
def loaded_integrations(self) -> list[str]: def loaded_integrations(self) -> set[str]:
if self.storage is None: if self.storage is None:
return [] return []
return self.storage.loaded_integrations return self.storage.loaded_integrations

View file

@ -1,21 +1,15 @@
from __future__ import annotations
import binascii import binascii
import codecs import codecs
from datetime import datetime
import json import json
import logging import logging
import os import os
from typing import Optional from datetime import datetime
from esphome import const from esphome import const
from esphome.const import CONF_DISABLED, CONF_MDNS
from esphome.core import CORE from esphome.core import CORE
from esphome.helpers import write_file_if_changed from esphome.helpers import write_file_if_changed
from esphome.const import (
CONF_MDNS,
CONF_DISABLED,
)
from esphome.types import CoreType from esphome.types import CoreType
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -40,48 +34,47 @@ def trash_storage_path() -> str:
class StorageJSON: class StorageJSON:
def __init__( def __init__(
self, self,
storage_version, storage_version: int,
name, name: str,
friendly_name, friendly_name: str,
comment, comment: str,
esphome_version, esphome_version: str,
src_version, src_version: int | None,
address, address: str,
web_port, web_port: int | None,
target_platform, target_platform: str,
build_path, build_path: str,
firmware_bin_path, firmware_bin_path: str,
loaded_integrations, loaded_integrations: set[str],
no_mdns, no_mdns: bool,
): ) -> None:
# Version of the storage JSON schema # Version of the storage JSON schema
assert storage_version is None or isinstance(storage_version, int) assert storage_version is None or isinstance(storage_version, int)
self.storage_version: int = storage_version self.storage_version = storage_version
# The name of the node # The name of the node
self.name: str = name self.name = name
# The friendly name of the node # The friendly name of the node
self.friendly_name: str = friendly_name self.friendly_name = friendly_name
# The comment of the node # The comment of the node
self.comment: str = comment self.comment = comment
# The esphome version this was compiled with # The esphome version this was compiled with
self.esphome_version: str = esphome_version self.esphome_version = esphome_version
# The version of the file in src/main.cpp - Used to migrate the file # The version of the file in src/main.cpp - Used to migrate the file
assert src_version is None or isinstance(src_version, int) assert src_version is None or isinstance(src_version, int)
self.src_version: int = src_version self.src_version = src_version
# Address of the ESP, for example livingroom.local or a static IP # Address of the ESP, for example livingroom.local or a static IP
self.address: str = address self.address = address
# Web server port of the ESP, for example 80 # Web server port of the ESP, for example 80
assert web_port is None or isinstance(web_port, int) assert web_port is None or isinstance(web_port, int)
self.web_port: int = web_port self.web_port = web_port
# The type of hardware in use, like "ESP32", "ESP32C3", "ESP8266", etc. # The type of hardware in use, like "ESP32", "ESP32C3", "ESP8266", etc.
self.target_platform: str = target_platform self.target_platform = target_platform
# The absolute path to the platformio project # The absolute path to the platformio project
self.build_path: str = build_path self.build_path = build_path
# The absolute path to the firmware binary # The absolute path to the firmware binary
self.firmware_bin_path: str = firmware_bin_path self.firmware_bin_path = firmware_bin_path
# A list of strings of names of loaded integrations # A set of strings of names of loaded integrations
self.loaded_integrations: list[str] = loaded_integrations self.loaded_integrations = loaded_integrations
self.loaded_integrations.sort()
# Is mDNS disabled # Is mDNS disabled
self.no_mdns = no_mdns self.no_mdns = no_mdns
@ -98,7 +91,7 @@ class StorageJSON:
"esp_platform": self.target_platform, "esp_platform": self.target_platform,
"build_path": self.build_path, "build_path": self.build_path,
"firmware_bin_path": self.firmware_bin_path, "firmware_bin_path": self.firmware_bin_path,
"loaded_integrations": self.loaded_integrations, "loaded_integrations": sorted(self.loaded_integrations),
"no_mdns": self.no_mdns, "no_mdns": self.no_mdns,
} }
@ -109,9 +102,7 @@ class StorageJSON:
write_file_if_changed(path, self.to_json()) write_file_if_changed(path, self.to_json())
@staticmethod @staticmethod
def from_esphome_core( def from_esphome_core(esph: CoreType, old: StorageJSON | None) -> StorageJSON:
esph: CoreType, old: Optional["StorageJSON"]
) -> "StorageJSON":
hardware = esph.target_platform.upper() hardware = esph.target_platform.upper()
if esph.is_esp32: if esph.is_esp32:
from esphome.components import esp32 from esphome.components import esp32
@ -129,7 +120,7 @@ class StorageJSON:
target_platform=hardware, target_platform=hardware,
build_path=esph.build_path, build_path=esph.build_path,
firmware_bin_path=esph.firmware_bin, firmware_bin_path=esph.firmware_bin,
loaded_integrations=list(esph.loaded_integrations), loaded_integrations=esph.loaded_integrations,
no_mdns=( no_mdns=(
CONF_MDNS in esph.config CONF_MDNS in esph.config
and CONF_DISABLED in esph.config[CONF_MDNS] and CONF_DISABLED in esph.config[CONF_MDNS]
@ -140,7 +131,7 @@ class StorageJSON:
@staticmethod @staticmethod
def from_wizard( def from_wizard(
name: str, friendly_name: str, address: str, platform: str name: str, friendly_name: str, address: str, platform: str
) -> "StorageJSON": ) -> StorageJSON:
return StorageJSON( return StorageJSON(
storage_version=1, storage_version=1,
name=name, name=name,
@ -153,12 +144,12 @@ class StorageJSON:
target_platform=platform, target_platform=platform,
build_path=None, build_path=None,
firmware_bin_path=None, firmware_bin_path=None,
loaded_integrations=[], loaded_integrations=set(),
no_mdns=False, no_mdns=False,
) )
@staticmethod @staticmethod
def _load_impl(path: str) -> Optional["StorageJSON"]: def _load_impl(path: str) -> StorageJSON | None:
with codecs.open(path, "r", encoding="utf-8") as f_handle: with codecs.open(path, "r", encoding="utf-8") as f_handle:
storage = json.load(f_handle) storage = json.load(f_handle)
storage_version = storage["storage_version"] storage_version = storage["storage_version"]
@ -174,7 +165,7 @@ class StorageJSON:
esp_platform = storage.get("esp_platform") esp_platform = storage.get("esp_platform")
build_path = storage.get("build_path") build_path = storage.get("build_path")
firmware_bin_path = storage.get("firmware_bin_path") firmware_bin_path = storage.get("firmware_bin_path")
loaded_integrations = storage.get("loaded_integrations", []) loaded_integrations = set(storage.get("loaded_integrations", []))
no_mdns = storage.get("no_mdns", False) no_mdns = storage.get("no_mdns", False)
return StorageJSON( return StorageJSON(
storage_version, storage_version,
@ -193,7 +184,7 @@ class StorageJSON:
) )
@staticmethod @staticmethod
def load(path: str) -> Optional["StorageJSON"]: def load(path: str) -> StorageJSON | None:
try: try:
return StorageJSON._load_impl(path) return StorageJSON._load_impl(path)
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
@ -215,7 +206,7 @@ class EsphomeStorageJSON:
# The last time ESPHome checked for an update as an isoformat encoded str # The last time ESPHome checked for an update as an isoformat encoded str
self.last_update_check_str: str = last_update_check self.last_update_check_str: str = last_update_check
# Cache of the version gotten in the last version check # Cache of the version gotten in the last version check
self.remote_version: Optional[str] = remote_version self.remote_version: str | None = remote_version
def as_dict(self) -> dict: def as_dict(self) -> dict:
return { return {
@ -226,7 +217,7 @@ class EsphomeStorageJSON:
} }
@property @property
def last_update_check(self) -> Optional[datetime]: def last_update_check(self) -> datetime | None:
try: try:
return datetime.strptime(self.last_update_check_str, "%Y-%m-%dT%H:%M:%S") return datetime.strptime(self.last_update_check_str, "%Y-%m-%dT%H:%M:%S")
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
@ -243,7 +234,7 @@ class EsphomeStorageJSON:
write_file_if_changed(path, self.to_json()) write_file_if_changed(path, self.to_json())
@staticmethod @staticmethod
def _load_impl(path: str) -> Optional["EsphomeStorageJSON"]: def _load_impl(path: str) -> EsphomeStorageJSON | None:
with codecs.open(path, "r", encoding="utf-8") as f_handle: with codecs.open(path, "r", encoding="utf-8") as f_handle:
storage = json.load(f_handle) storage = json.load(f_handle)
storage_version = storage["storage_version"] storage_version = storage["storage_version"]
@ -255,14 +246,14 @@ class EsphomeStorageJSON:
) )
@staticmethod @staticmethod
def load(path: str) -> Optional["EsphomeStorageJSON"]: def load(path: str) -> EsphomeStorageJSON | None:
try: try:
return EsphomeStorageJSON._load_impl(path) return EsphomeStorageJSON._load_impl(path)
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
return None return None
@staticmethod @staticmethod
def get_default() -> "EsphomeStorageJSON": def get_default() -> EsphomeStorageJSON:
return EsphomeStorageJSON( return EsphomeStorageJSON(
storage_version=1, storage_version=1,
cookie_secret=binascii.hexlify(os.urandom(64)).decode(), cookie_secret=binascii.hexlify(os.urandom(64)).decode(),