Skip to content

Commit a3028a2

Browse files
authored
Merge pull request #2342 from AppDaemon/app-reload
App reload
2 parents be67c27 + 47a6626 commit a3028a2

File tree

3 files changed

+54
-37
lines changed

3 files changed

+54
-37
lines changed

appdaemon/app_management.py

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from functools import partial, reduce, wraps
1616
from logging import Logger
1717
from pathlib import Path
18-
from typing import TYPE_CHECKING, Literal, TypeVar
18+
from typing import TYPE_CHECKING, Any, Literal, TypeVar
1919

2020
from pydantic import ValidationError
2121

@@ -432,7 +432,7 @@ async def stop_app(self, app_name: str, delete: bool = False) -> bool:
432432
else:
433433
return True
434434

435-
async def restart_app(self, app):
435+
async def restart_app(self, app: str) -> None:
436436
await self.stop_app(app, delete=False)
437437
try:
438438
await self.start_app(app)
@@ -513,8 +513,11 @@ async def create_app_object(self, app_name: str) -> None:
513513
def get_managed_app_names(self, include_globals: bool = False) -> set[str]:
514514
apps = set(name for name, o in self.objects.items() if o.type == "app")
515515
if include_globals:
516-
apps |= set(name for name, cfg in self.app_config.root.items()
517-
if isinstance(cfg, GlobalModule))
516+
globals = set(
517+
name for name, cfg in self.app_config.root.items()
518+
if isinstance(cfg, GlobalModule)
519+
)
520+
apps |= globals
518521
return apps
519522

520523
def add_plugin_object(self, name: str, object: "PluginBase", use_dictionary_unpacking: bool = False) -> None:
@@ -763,7 +766,7 @@ async def wrapper(*args, **kwargs):
763766
return wrapper
764767

765768
# @utils.timeit
766-
async def check_app_updates(self, plugin_ns: str = None, mode: UpdateMode = UpdateMode.NORMAL):
769+
async def check_app_updates(self, plugin_ns: str | None = None, mode: UpdateMode = UpdateMode.NORMAL):
767770
"""Checks the states of the Python files that define the apps, reloading when necessary.
768771
769772
Called as part of :meth:`.utility_loop.Utility.loop`
@@ -778,12 +781,18 @@ async def check_app_updates(self, plugin_ns: str = None, mode: UpdateMode = Upda
778781
async with self.check_updates_lock:
779782
await self._process_filters()
780783

781-
if mode == UpdateMode.INIT:
782-
await self._process_import_paths()
783-
await self._init_dep_manager()
784-
785784
update_actions = UpdateActions()
786785

786+
match mode:
787+
case UpdateMode.INIT:
788+
await self._process_import_paths()
789+
await self._init_dep_manager()
790+
case UpdateMode.RELOAD_APPS:
791+
all_apps = self.get_managed_app_names(include_globals=False)
792+
modules = self.dependency_manager.modules_from_apps(all_apps)
793+
update_actions.apps.reload |= all_apps
794+
update_actions.modules.reload |= modules
795+
787796
await self.check_app_config_files(update_actions)
788797

789798
await self._handle_sequence_change(update_actions, mode)
@@ -1285,40 +1294,39 @@ def get_app_file(self, app: str) -> str:
12851294
self.get_state(app, attribute="config_path")
12861295
)
12871296

1288-
async def manage_services(self,
1289-
namespace: str,
1290-
domain: str,
1291-
service: str,
1292-
app: str | None = None,
1293-
__name: str | None = None,
1294-
**kwargs):
1297+
async def manage_services(
1298+
self,
1299+
namespace: str,
1300+
domain: str,
1301+
service: Literal["start", "stop", "restart", "reload", "enable", "disable", "create", "edit", "remove"],
1302+
app: str | None = None,
1303+
__name: str | None = None,
1304+
**kwargs
1305+
) -> None | bool | Any:
12951306
assert namespace == 'admin' and domain == 'app'
1296-
1297-
if app is None and service != "reload":
1298-
self.logger.warning(
1299-
"App not specified when calling '%s' service from %s. Specify App", service, __name)
1300-
return
1301-
13021307
match service:
13031308
case "reload" | "create":
13041309
pass
13051310
case _:
1306-
if app not in self.app_config:
1311+
if app not in self.get_managed_app_names(include_globals=False):
13071312
self.logger.warning(
1308-
"Specified App '%s' is not a valid App from %s", app, __name)
1313+
"Specified app '%s' for service '%s' is not valid from %s",
1314+
app,
1315+
service,
1316+
__name
1317+
)
13091318
return
13101319

1311-
match service:
1312-
case "start":
1313-
asyncio.ensure_future(self.start_app(app))
1314-
case "stop":
1315-
asyncio.ensure_future(self.stop_app(app, delete=False))
1316-
case "restart":
1317-
asyncio.ensure_future(self.restart_app(app))
1318-
case "reload":
1319-
asyncio.ensure_future(
1320-
self.check_app_updates(mode=UpdateMode.INIT))
1321-
case _:
1320+
match (service, app):
1321+
case ("start", str()):
1322+
asyncio.create_task(self.start_app(app))
1323+
case ("stop", str()):
1324+
asyncio.create_task(self.stop_app(app, delete=False))
1325+
case ("restart", str()):
1326+
asyncio.create_task(self.restart_app(app))
1327+
case ("reload", _):
1328+
asyncio.create_task(self.check_app_updates(mode=UpdateMode.RELOAD_APPS))
1329+
case (_, str()):
13221330
# first the check app updates needs to be stopped if on
13231331
mode = copy.deepcopy(self.AD.production_mode)
13241332

@@ -1343,3 +1351,10 @@ async def manage_services(self,
13431351
self.AD.production_mode = mode
13441352

13451353
return result
1354+
case _:
1355+
self.logger.warning(
1356+
"Invalid app service call '%s' with app '%s' from app %s.",
1357+
service,
1358+
app,
1359+
__name
1360+
)

appdaemon/dependency_manager.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,9 @@ def dependent_modules(self, modules: str | Iterable[str]):
173173
return self.python_deps.get_dependents(modules)
174174

175175
def modules_from_apps(self, apps: str | Iterable[str], dependents: bool = True) -> set[str]:
176-
"""Find the importable names of all the python modules that depend on the given apps. Includes
177-
the transitive closure by default.
176+
"""Find the importable names of all the python modules that the given apps depend on.
177+
178+
This includes the transitive closure by default.
178179
179180
Args:
180181
apps (str | Iterable[str]):

appdaemon/models/internal/app_management.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class UpdateMode(Enum):
2424

2525
INIT = auto()
2626
NORMAL = auto()
27+
RELOAD_APPS = auto()
2728
PLUGIN_FAILED = auto()
2829
PLUGIN_RESTART = auto()
2930
TERMINATE = auto()

0 commit comments

Comments
 (0)