dependency manager prototype
This commit is contained in:
0
dependencies/__init__.py
vendored
Normal file
0
dependencies/__init__.py
vendored
Normal file
80
dependencies/manager.py
vendored
Normal file
80
dependencies/manager.py
vendored
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
from abc import ABC
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, Iterable, Self, Set
|
||||||
|
|
||||||
|
import appdaemon.dependency as deps
|
||||||
|
from appdaemon.models.app_config import AllAppConfig, AppConfig
|
||||||
|
from appdaemon.models.internal.file_check import FileCheck
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Dependencies(ABC):
|
||||||
|
files: FileCheck = field(repr=False)
|
||||||
|
ext: str = field(init=False)
|
||||||
|
dep_graph: Dict[str, Set[str]] = field(init=False)
|
||||||
|
rev_graph: Dict[str, Set[str]] = field(init=False)
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
self.rev_graph = deps.reverse_graph(self.dep_graph)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_path(cls: Self, path: Path):
|
||||||
|
return cls.from_paths(path.rglob(f'*{cls.ext}'))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_paths(cls: Self, paths: Iterable[Path]):
|
||||||
|
return cls(files=FileCheck.from_paths(paths))
|
||||||
|
|
||||||
|
def get_dependents(self, items: Iterable[str]) -> Set[str]:
|
||||||
|
items = items if isinstance(items, set) else set(items)
|
||||||
|
items |= deps.find_all_dependents(items, self.rev_graph)
|
||||||
|
return items
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PythonDeps(Dependencies):
|
||||||
|
ext: str = '.py'
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
self.dep_graph = deps.get_dependency_graph(self.files)
|
||||||
|
super().__post_init__()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AppDeps(Dependencies):
|
||||||
|
app_config: AllAppConfig = field(init=False, repr=False)
|
||||||
|
ext: str = '.yaml'
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
self.app_config = AllAppConfig.from_paths(self.files)
|
||||||
|
self.dep_graph = self.app_config.depedency_graph()
|
||||||
|
super().__post_init__()
|
||||||
|
|
||||||
|
def direct_app_deps(self, modules: Iterable[str]):
|
||||||
|
"""Find the apps that directly depend on any of the given modules"""
|
||||||
|
return set(
|
||||||
|
app_name
|
||||||
|
for app_name, app_cfg in self.app_config.root.items()
|
||||||
|
if isinstance(app_cfg, AppConfig) and app_cfg.module_name in modules
|
||||||
|
)
|
||||||
|
|
||||||
|
def cascade_modules(self, modules: Iterable[str]) -> Set[str]:
|
||||||
|
"""Find all the apps that depend on the given modules, even indirectly"""
|
||||||
|
return self.get_dependents(self.direct_app_deps(modules))
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DependencyManager:
|
||||||
|
app_dir: Path
|
||||||
|
python_deps: PythonDeps = field(init=False)
|
||||||
|
app_deps: AppDeps = field(init=False)
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
self.python_deps = PythonDeps.from_path(self.app_dir)
|
||||||
|
self.app_deps = AppDeps.from_path(self.app_dir)
|
||||||
|
|
||||||
|
def get_dependent_apps(self, modules: Iterable[str]) -> Set[str]:
|
||||||
|
"""Finds all of the apps that depend on the given modules, even indirectly"""
|
||||||
|
modules |= self.python_deps.get_dependents(modules)
|
||||||
|
return self.app_deps.cascade_modules(modules)
|
||||||
Reference in New Issue
Block a user