Skip to content

Commit

Permalink
Add minor version support to storage.Store (home-assistant#59882)
Browse files Browse the repository at this point in the history
  • Loading branch information
emontnemery authored Nov 18, 2021
1 parent cc3f179 commit d18c250
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 13 deletions.
8 changes: 4 additions & 4 deletions homeassistant/components/onboarding/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@
class OnboadingStorage(Store):
"""Store onboarding data."""

async def _async_migrate_func(self, old_version, old_data):
async def _async_migrate_func(self, old_major_version, old_minor_version, old_data):
"""Migrate to the new version."""
# From version 1 -> 2, we automatically mark the integration step done
if old_version < 2:
if old_major_version < 2:
old_data["done"].append(STEP_INTEGRATION)
if old_version < 3:
if old_major_version < 3:
old_data["done"].append(STEP_CORE_CONFIG)
if old_version < 4:
if old_major_version < 4:
old_data["done"].append(STEP_ANALYTICS)
return old_data

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/person/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ async def async_add_user_device_tracker(
class PersonStore(Store):
"""Person storage."""

async def _async_migrate_func(self, old_version, old_data):
async def _async_migrate_func(self, old_major_version, old_minor_version, old_data):
"""Migrate to the new version.
Migrate storage to use format of collection helper.
Expand Down
48 changes: 40 additions & 8 deletions homeassistant/helpers/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import asyncio
from collections.abc import Callable
from contextlib import suppress
import inspect
from json import JSONEncoder
import logging
import os
Expand Down Expand Up @@ -75,11 +76,13 @@ def __init__(
key: str,
private: bool = False,
*,
encoder: type[JSONEncoder] | None = None,
atomic_writes: bool = False,
encoder: type[JSONEncoder] | None = None,
minor_version: int = 1,
) -> None:
"""Initialize storage class."""
self.version = version
self.minor_version = minor_version
self.key = key
self.hass = hass
self._private = private
Expand All @@ -99,8 +102,8 @@ def path(self):
async def async_load(self) -> dict | list | None:
"""Load data.
If the expected version does not match the given version, the migrate
function will be invoked with await migrate_func(version, config).
If the expected version and minor version do not match the given versions, the
migrate function will be invoked with migrate_func(version, minor_version, config).
Will ensure that when a call comes in while another one is in progress,
the second call will wait and return the result of the first call.
Expand Down Expand Up @@ -137,7 +140,15 @@ async def _async_load_data(self):

if data == {}:
return None
if data["version"] == self.version:

# Add minor_version if not set
if "minor_version" not in data:
data["minor_version"] = 1

if (
data["version"] == self.version
and data["minor_version"] == self.minor_version
):
stored = data["data"]
else:
_LOGGER.info(
Expand All @@ -146,13 +157,29 @@ async def _async_load_data(self):
data["version"],
self.version,
)
stored = await self._async_migrate_func(data["version"], data["data"])
if len(inspect.signature(self._async_migrate_func).parameters) == 2:
# pylint: disable-next=no-value-for-parameter
stored = await self._async_migrate_func(data["version"], data["data"])
else:
try:
stored = await self._async_migrate_func(
data["version"], data["minor_version"], data["data"]
)
except NotImplementedError:
if data["version"] != self.version:
raise
stored = data["data"]

return stored

async def async_save(self, data: dict | list) -> None:
"""Save data."""
self._data = {"version": self.version, "key": self.key, "data": data}
self._data = {
"version": self.version,
"minor_version": self.minor_version,
"key": self.key,
"data": data,
}

if self.hass.state == CoreState.stopping:
self._async_ensure_final_write_listener()
Expand All @@ -163,7 +190,12 @@ async def async_save(self, data: dict | list) -> None:
@callback
def async_delay_save(self, data_func: Callable[[], dict], delay: float = 0) -> None:
"""Save data with an optional delay."""
self._data = {"version": self.version, "key": self.key, "data_func": data_func}
self._data = {
"version": self.version,
"minor_version": self.minor_version,
"key": self.key,
"data_func": data_func,
}

self._async_cleanup_delay_listener()
self._async_ensure_final_write_listener()
Expand Down Expand Up @@ -248,7 +280,7 @@ def _write_data(self, path: str, data: dict) -> None:
atomic_writes=self._atomic_writes,
)

async def _async_migrate_func(self, old_version, old_data):
async def _async_migrate_func(self, old_major_version, old_minor_version, old_data):
"""Migrate to the new version."""
raise NotImplementedError

Expand Down
3 changes: 3 additions & 0 deletions tests/components/google_assistant/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ async def test_agent_user_id_storage(hass, hass_storage):

hass_storage["google_assistant"] = {
"version": 1,
"minor_version": 1,
"key": "google_assistant",
"data": {"agent_user_ids": {"agent_1": {}}},
}
Expand All @@ -178,6 +179,7 @@ async def test_agent_user_id_storage(hass, hass_storage):

assert hass_storage["google_assistant"] == {
"version": 1,
"minor_version": 1,
"key": "google_assistant",
"data": {"agent_user_ids": {"agent_1": {}}},
}
Expand All @@ -188,6 +190,7 @@ async def _check_after_delay(data):

assert hass_storage["google_assistant"] == {
"version": 1,
"minor_version": 1,
"key": "google_assistant",
"data": data,
}
Expand Down
Loading

0 comments on commit d18c250

Please sign in to comment.