Skip to content

Commit

Permalink
Finish moving read_text to executor
Browse files Browse the repository at this point in the history
  • Loading branch information
mdegat01 committed Mar 1, 2025
1 parent 82a6718 commit 6f03d76
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 34 deletions.
8 changes: 5 additions & 3 deletions supervisor/addons/addon.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,7 @@ def __init__(self, coresys: CoreSys, slug: str):
super().__init__(coresys, slug)
self.instance: DockerAddon = DockerAddon(coresys, self)
self._state: AddonState = AddonState.UNKNOWN
self._manual_stop: bool = (
self.sys_hardware.helper.last_boot != self.sys_config.last_boot
)
self._manual_stop: bool = False
self._listeners: list[EventListener] = []
self._startup_event = asyncio.Event()
self._startup_task: asyncio.Task | None = None
Expand Down Expand Up @@ -216,6 +214,10 @@ def in_progress(self) -> bool:

async def load(self) -> None:
"""Async initialize of object."""
self._manual_stop = (
await self.sys_hardware.helper.last_boot() != self.sys_config.last_boot
)

if self.is_detached:
await super().refresh_path_cache()

Expand Down
4 changes: 2 additions & 2 deletions supervisor/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ async def start(self):

try:
# HomeAssistant is already running, only Supervisor restarted
if self.sys_hardware.helper.last_boot == self.sys_config.last_boot:
if await self.sys_hardware.helper.last_boot() == self.sys_config.last_boot:
_LOGGER.info("Detected Supervisor restart")
return

Expand Down Expand Up @@ -362,7 +362,7 @@ async def shutdown(self, *, remove_homeassistant_container: bool = False):

async def _update_last_boot(self):
"""Update last boot time."""
self.sys_config.last_boot = self.sys_hardware.helper.last_boot
self.sys_config.last_boot = await self.sys_hardware.helper.last_boot()
await self.sys_config.save_data()

async def _retrieve_whoami(self, with_ssl: bool) -> WhoamiData | None:
Expand Down
14 changes: 10 additions & 4 deletions supervisor/hardware/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class HwHelper(CoreSysAttributes):
def __init__(self, coresys: CoreSys):
"""Init hardware object."""
self.coresys = coresys
self._last_boot: datetime | None = None

@property
def support_audio(self) -> bool:
Expand All @@ -41,11 +42,15 @@ def support_usb(self) -> bool:
"""Return True if the device have USB ports."""
return bool(self.sys_hardware.filter_devices(subsystem=UdevSubsystem.USB))

@property
def last_boot(self) -> datetime | None:
async def last_boot(self) -> datetime | None:
"""Return last boot time."""
if self._last_boot:
return self._last_boot

try:
stats: str = _PROC_STAT.read_text(encoding="utf-8")
stats: str = await self.sys_run_in_executor(
_PROC_STAT.read_text, encoding="utf-8"
)
except OSError as err:
_LOGGER.error("Can't read stat data: %s", err)
return None
Expand All @@ -56,7 +61,8 @@ def last_boot(self) -> datetime | None:
_LOGGER.error("Can't found last boot time!")
return None

return datetime.fromtimestamp(int(found.group(1)), UTC)
self._last_boot = datetime.fromtimestamp(int(found.group(1)), UTC)
return self._last_boot

def hide_virtual_device(self, udev_device: pyudev.Device) -> bool:
"""Small helper to hide not needed Devices."""
Expand Down
20 changes: 13 additions & 7 deletions supervisor/plugins/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ async def load(self) -> None:
# Initialize Client Template
try:
self.client_template = jinja2.Template(
PULSE_CLIENT_TMPL.read_text(encoding="utf-8")
await self.sys_run_in_executor(
PULSE_CLIENT_TMPL.read_text, encoding="utf-8"
)
)
except OSError as err:
if err.errno == errno.EBADMSG:
Expand All @@ -100,13 +102,17 @@ async def load(self) -> None:

# Setup default asound config
asound = self.sys_config.path_audio.joinpath("asound")
if not asound.exists():
try:

def setup_default_asound():
if not asound.exists():
shutil.copy(ASOUND_TMPL, asound)
except OSError as err:
if err.errno == errno.EBADMSG:
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
_LOGGER.error("Can't create default asound: %s", err)

try:
await self.sys_run_in_executor(setup_default_asound)
except OSError as err:
if err.errno == errno.EBADMSG:
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
_LOGGER.error("Can't create default asound: %s", err)

@Job(
name="plugin_audio_update",
Expand Down
11 changes: 6 additions & 5 deletions supervisor/plugins/dns.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,15 +152,16 @@ async def load(self) -> None:
# Initialize CoreDNS Template
try:
self.resolv_template = jinja2.Template(
RESOLV_TMPL.read_text(encoding="utf-8")
await self.sys_run_in_executor(RESOLV_TMPL.read_text, encoding="utf-8")
)
except OSError as err:
if err.errno == errno.EBADMSG:
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
_LOGGER.error("Can't read resolve.tmpl: %s", err)

try:
self.hosts_template = jinja2.Template(
HOSTS_TMPL.read_text(encoding="utf-8")
await self.sys_run_in_executor(HOSTS_TMPL.read_text, encoding="utf-8")
)
except OSError as err:
if err.errno == errno.EBADMSG:
Expand All @@ -171,7 +172,7 @@ async def load(self) -> None:
await super().load()

# Update supervisor
self._write_resolv(HOST_RESOLV)
await self._write_resolv(HOST_RESOLV)
await self.sys_supervisor.check_connectivity()

async def install(self) -> None:
Expand Down Expand Up @@ -412,7 +413,7 @@ async def repair(self) -> None:
_LOGGER.error("Repair of CoreDNS failed")
await async_capture_exception(err)

def _write_resolv(self, resolv_conf: Path) -> None:
async def _write_resolv(self, resolv_conf: Path) -> None:
"""Update/Write resolv.conf file."""
if not self.resolv_template:
_LOGGER.warning(
Expand All @@ -427,7 +428,7 @@ def _write_resolv(self, resolv_conf: Path) -> None:

# Write config back to resolv
try:
resolv_conf.write_text(data)
await self.sys_run_in_executor(resolv_conf.write_text, data)
except OSError as err:
if err.errno == errno.EBADMSG:
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
Expand Down
5 changes: 4 additions & 1 deletion supervisor/resolution/evaluations/apparmor.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ def states(self) -> list[CoreState]:
async def evaluate(self) -> None:
"""Run evaluation."""
try:
return _APPARMOR_KERNEL.read_text(encoding="utf-8").strip().upper() != "Y"
apparmor = self.sys_run_in_executor(
_APPARMOR_KERNEL.read_text, encoding="utf-8"
)
except OSError:
return True
return apparmor.strip().upper() != "Y"
14 changes: 10 additions & 4 deletions supervisor/resolution/evaluations/lxc.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@ def states(self) -> list[CoreState]:

async def evaluate(self):
"""Run evaluation."""
with suppress(OSError):
if "container=lxc" in Path("/proc/1/environ").read_text(encoding="utf-8"):
return True
return Path("/dev/lxd/sock").exists()

def check_lxc():
with suppress(OSError):
if "container=lxc" in Path("/proc/1/environ").read_text(
encoding="utf-8"
):
return True
return Path("/dev/lxd/sock").exists()

return await self.sys_run_in_executor(check_lxc)
11 changes: 5 additions & 6 deletions tests/addons/test_addon.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from supervisor.docker.const import ContainerState
from supervisor.docker.monitor import DockerContainerStateEvent
from supervisor.exceptions import AddonsError, AddonsJobError, AudioUpdateError
from supervisor.hardware.helper import HwHelper
from supervisor.ingress import Ingress
from supervisor.store.repository import Repository
from supervisor.utils.dt import utcnow
Expand Down Expand Up @@ -250,19 +251,17 @@ async def test_watchdog_during_attach(

with (
patch.object(Addon, "restart") as restart,
patch.object(
type(coresys.hardware.helper),
"last_boot",
new=PropertyMock(return_value=utcnow()),
),
patch.object(HwHelper, "last_boot", return_value=utcnow()),
patch.object(DockerAddon, "attach"),
patch.object(
DockerAddon,
"current_state",
return_value=ContainerState.STOPPED,
),
):
coresys.config.last_boot = coresys.hardware.helper.last_boot + boot_timedelta
coresys.config.last_boot = (
await coresys.hardware.helper.last_boot() + boot_timedelta
)
addon = Addon(coresys, store.slug)
coresys.addons.local[addon.slug] = addon
addon.watchdog = True
Expand Down
4 changes: 2 additions & 2 deletions tests/hardware/test_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,13 @@ def test_hide_virtual_device(coresys: CoreSys):
assert coresys.hardware.helper.hide_virtual_device(udev_device)


def test_last_boot_error(coresys: CoreSys, caplog: LogCaptureFixture):
async def test_last_boot_error(coresys: CoreSys, caplog: LogCaptureFixture):
"""Test error reading last boot."""
with patch(
"supervisor.hardware.helper.Path.read_text", side_effect=(err := OSError())
):
err.errno = errno.EBADMSG
assert coresys.hardware.helper.last_boot is None
assert await coresys.hardware.helper.last_boot() is None

assert coresys.core.healthy is True
assert "Can't read stat data" in caplog.text

0 comments on commit 6f03d76

Please sign in to comment.