Home Assistant Unofficial Reference 2024.12.1
normalized_name_base_registry.py
Go to the documentation of this file.
1 """Provide a base class for registries that use a normalized name index."""
2 
3 from dataclasses import dataclass, field
4 from datetime import datetime
5 from functools import lru_cache
6 
7 from homeassistant.util import dt as dt_util, slugify
8 
9 from .registry import BaseRegistryItems
10 
11 
12 @dataclass(slots=True, frozen=True, kw_only=True)
14  """Normalized Name Base Registry Entry."""
15 
16  name: str
17  normalized_name: str = field(init=False)
18  created_at: datetime = field(default_factory=dt_util.utcnow)
19  modified_at: datetime = field(default_factory=dt_util.utcnow)
20 
21  def __post_init__(self) -> None:
22  """Post init."""
23  object.__setattr__(self, "normalized_name", normalize_name(self.name))
24 
25 
26 @lru_cache(maxsize=1024)
27 def normalize_name(name: str) -> str:
28  """Normalize a name by removing whitespace and case folding."""
29  return name.casefold().replace(" ", "")
30 
31 
32 class NormalizedNameBaseRegistryItems[_VT: NormalizedNameBaseRegistryEntry](
33  BaseRegistryItems[_VT]
34 ):
35  """Base container for normalized name registry items, maps key -> entry.
36 
37  Maintains an additional index:
38  - normalized name -> entry
39  """
40 
41  def __init__(self) -> None:
42  """Initialize the container."""
43  super().__init__()
44  self._normalized_names: dict[str, _VT] = {}
45 
46  def _unindex_entry(self, key: str, replacement_entry: _VT | None = None) -> None:
47  old_entry = self.data[key]
48  if (
49  replacement_entry is not None
50  and (normalized_name := replacement_entry.normalized_name)
51  != old_entry.normalized_name
52  and normalized_name in self._normalized_names
53  ):
54  raise ValueError(
55  f"The name {replacement_entry.name} ({normalized_name}) is already in use"
56  )
57  del self._normalized_names[old_entry.normalized_name]
58 
59  def _index_entry(self, key: str, entry: _VT) -> None:
60  self._normalized_names[entry.normalized_name] = entry
61 
62  def get_by_name(self, name: str) -> _VT | None:
63  """Get entry by name."""
64  return self._normalized_names.get(normalize_name(name))
65 
66  def generate_id_from_name(self, name: str) -> str:
67  """Generate ID from name."""
68  suggestion = suggestion_base = slugify(name)
69  tries = 1
70  while suggestion in self:
71  tries += 1
72  suggestion = f"{suggestion_base}_{tries}"
73  return suggestion
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
None _unindex_entry(self, str key, _VT|None replacement_entry=None)