From bee6d87e7af337c8b98d4cc862ca9ca006d7f27e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 24 Aug 2020 10:21:30 -0500 Subject: [PATCH] Standardize uuid generation for events/storage/registry (#39184) --- homeassistant/config_entries.py | 4 ++-- homeassistant/core.py | 11 ++--------- homeassistant/helpers/area_registry.py | 4 ++-- homeassistant/helpers/device_registry.py | 4 ++-- homeassistant/util/uuid.py | 15 +++++++++++++++ tests/util/test_uuid.py | 11 +++++++++++ 6 files changed, 34 insertions(+), 15 deletions(-) create mode 100644 homeassistant/util/uuid.py create mode 100644 tests/util/test_uuid.py diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 9bfc9a1f1d0e..04bdbf236a50 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -4,7 +4,6 @@ import functools import logging from types import MappingProxyType from typing import Any, Callable, Dict, List, Optional, Set, Union, cast -import uuid import weakref import attr @@ -16,6 +15,7 @@ from homeassistant.helpers import entity_registry from homeassistant.helpers.event import Event from homeassistant.setup import async_process_deps_reqs, async_setup_component from homeassistant.util.decorator import Registry +import homeassistant.util.uuid as uuid_util _LOGGER = logging.getLogger(__name__) _UNDEF: dict = {} @@ -135,7 +135,7 @@ class ConfigEntry: ) -> None: """Initialize a config entry.""" # Unique id of the config entry - self.entry_id = entry_id or uuid.uuid4().hex + self.entry_id = entry_id or uuid_util.uuid_v1mc_hex() # Version of the configuration. self.version = version diff --git a/homeassistant/core.py b/homeassistant/core.py index 7b812096fcb6..31e132eec1aa 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -12,7 +12,6 @@ from ipaddress import ip_address import logging import os import pathlib -import random import re import threading from time import monotonic @@ -33,7 +32,6 @@ from typing import ( Union, cast, ) -import uuid import attr import voluptuous as vol @@ -77,6 +75,7 @@ import homeassistant.util.dt as dt_util from homeassistant.util.thread import fix_threading_exception_logging from homeassistant.util.timeout import TimeoutManager from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM, UnitSystem +import homeassistant.util.uuid as uuid_util # Typing imports that create a circular dependency if TYPE_CHECKING: @@ -510,13 +509,7 @@ class Context: user_id: str = attr.ib(default=None) parent_id: Optional[str] = attr.ib(default=None) - # The uuid1 uses a random multicast MAC address instead of the real MAC address - # of the machine without the overhead of calling the getrandom() system call. - # - # This is effectively equivalent to PostgreSQL's uuid_generate_v1mc() function - id: str = attr.ib( - factory=lambda: uuid.uuid1(node=random.getrandbits(48) | (1 << 40)).hex - ) + id: str = attr.ib(factory=uuid_util.uuid_v1mc_hex) def as_dict(self) -> dict: """Return a dictionary representation of the context.""" diff --git a/homeassistant/helpers/area_registry.py b/homeassistant/helpers/area_registry.py index 5def290766ff..3e3a3e03f6a7 100644 --- a/homeassistant/helpers/area_registry.py +++ b/homeassistant/helpers/area_registry.py @@ -3,12 +3,12 @@ from asyncio import Event from collections import OrderedDict import logging from typing import Dict, Iterable, List, MutableMapping, Optional, cast -import uuid import attr from homeassistant.core import callback from homeassistant.loader import bind_hass +import homeassistant.util.uuid as uuid_util from .typing import HomeAssistantType @@ -26,7 +26,7 @@ class AreaEntry: """Area Registry Entry.""" name: Optional[str] = attr.ib(default=None) - id: str = attr.ib(factory=lambda: uuid.uuid4().hex) + id: str = attr.ib(factory=uuid_util.uuid_v1mc_hex) class AreaRegistry: diff --git a/homeassistant/helpers/device_registry.py b/homeassistant/helpers/device_registry.py index a52a3837868d..7c990aa397d6 100644 --- a/homeassistant/helpers/device_registry.py +++ b/homeassistant/helpers/device_registry.py @@ -2,12 +2,12 @@ from collections import OrderedDict import logging from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple, Union -import uuid import attr from homeassistant.const import EVENT_HOMEASSISTANT_STARTED from homeassistant.core import Event, callback +import homeassistant.util.uuid as uuid_util from .debounce import Debouncer from .singleton import singleton @@ -73,7 +73,7 @@ class DeviceEntry: area_id: str = attr.ib(default=None) name_by_user: str = attr.ib(default=None) entry_type: str = attr.ib(default=None) - id: str = attr.ib(factory=lambda: uuid.uuid4().hex) + id: str = attr.ib(factory=uuid_util.uuid_v1mc_hex) # This value is not stored, just used to keep track of events to fire. is_new: bool = attr.ib(default=False) diff --git a/homeassistant/util/uuid.py b/homeassistant/util/uuid.py new file mode 100644 index 000000000000..c91cfe0dd125 --- /dev/null +++ b/homeassistant/util/uuid.py @@ -0,0 +1,15 @@ +"""Helpers to generate uuids.""" + +import random +import uuid + + +def uuid_v1mc_hex() -> str: + """Generate a uuid1 with a random multicast MAC address. + + The uuid1 uses a random multicast MAC address instead of the real MAC address + of the machine without the overhead of calling the getrandom() system call. + + This is effectively equivalent to PostgreSQL's uuid_generate_v1mc() function + """ + return uuid.uuid1(node=random.getrandbits(48) | (1 << 40)).hex diff --git a/tests/util/test_uuid.py b/tests/util/test_uuid.py new file mode 100644 index 000000000000..0debb8673414 --- /dev/null +++ b/tests/util/test_uuid.py @@ -0,0 +1,11 @@ +"""Test Home Assistant uuid util methods.""" + +import uuid + +import homeassistant.util.uuid as uuid_util + + +async def test_uuid_v1mc_hex(): + """Verify we can generate a uuid_v1mc and return hex.""" + assert len(uuid_util.uuid_v1mc_hex()) == 32 + assert uuid.UUID(uuid_util.uuid_v1mc_hex())