예제 #1
0
 def app_has_dependents(self, name):
     for app in self.app_config:
         if "dependencies" in self.app_config[app]:
             for dep in utils.single_or_list(self.app_config[app]["dependencies"]):
                 if dep == name:
                     return True
     return False
예제 #2
0
 def get_dependent_apps(self, dependee, deps):
     for app in self.app_config:
         if "dependencies" in self.app_config[app]:
             for dep in utils.single_or_list(self.app_config[app]["dependencies"]):
                 #print("app= {} dep = {}, dependee = {} deps = {}".format(app, dep, dependee, deps))
                 if dep == dependee and app not in deps:
                     deps.append(app)
                     new_deps = self.get_dependent_apps(app, deps)
                     if new_deps is not None:
                         deps.append(new_deps)
예제 #3
0
    def get_app_deps_and_prios(self, applist, mode):

        # Build a list of modules and their dependencies

        deplist = []
        for app in applist:
            if app not in deplist:
                deplist.append(app)
            self.get_dependent_apps(app, deplist)

        # Need to give the topological sort a full list of apps or it will fail
        full_list = list(self.app_config.keys())

        deps = []

        for app in full_list:
            dependees = []
            if "dependencies" in self.app_config[app]:
                for dep in utils.single_or_list(
                        self.app_config[app]["dependencies"]):
                    if dep in self.app_config:
                        dependees.append(dep)
                    else:
                        self.logger.warning(
                            "Unable to find app %s in dependencies for %s",
                            dep, app)
                        self.logger.warning("Ignoring app %s", app)
            deps.append((app, dependees))

        prio_apps = {}
        prio = float(50.1)
        try:
            for app in self.topological_sort(deps):
                if "dependencies" in self.app_config[
                        app] or self.app_has_dependents(app):
                    prio_apps[app] = prio
                    prio += float(0.0001)
                else:
                    if mode == "init" and "priority" in self.app_config[app]:
                        prio_apps[app] = float(
                            self.app_config[app]["priority"])
                    else:
                        prio_apps[app] = float(50)
        except ValueError:
            pass

        # now we remove the ones we aren't interested in

        final_apps = {}
        for app in prio_apps:
            if app in deplist:
                final_apps[app] = prio_apps[app]

        return final_apps
예제 #4
0
    def apps_per_global_module(self, module):
        apps = []
        for app in self.app_config:
            if "global_dependencies" in self.app_config[app]:
                for gm in utils.single_or_list(self.app_config[app]["global_dependencies"]):
                    if gm == module:
                        apps.append(app)

        for app, gms in self.global_module_dependencies.items():
            for gm in gms:
                if gm == module:
                    apps.append(app)

        return apps
예제 #5
0
    async def check_app_updates(self, plugin=None, mode="normal"):

        if self.AD.apps is False:
            return

        # Lets add some profiling
        pr = None
        if self.AD.check_app_updates_profile is True:
            pr = cProfile.Profile()
            pr.enable()

        # Process filters

        await utils.run_in_executor(self, self.process_filters)

        # Get list of apps we need to terminate and/or initialize

        apps = await self.check_config()

        found_files = []
        modules = []
        for root, subdirs, files in await utils.run_in_executor(self, os.walk, self.AD.app_dir, topdown=True):
            # print(root, subdirs, files)
            #
            # Prune dir list
            #
            subdirs[:] = [d for d in subdirs if d not in self.AD.exclude_dirs]

            if root[-11:] != "__pycache__":
                if root not in self.module_dirs:
                    self.logger.info("Adding %s to module import path", root)
                    sys.path.insert(0, root)
                    self.module_dirs.append(root)

            for file in files:
                if file[-3:] == ".py":
                    found_files.append(os.path.join(root, file))

        for file in found_files:
            if file == os.path.join(self.AD.app_dir, "__init__.py"):
                continue
            try:

                # check we can actually open the file
                await utils.run_in_executor(self, self.check_file, file)

                modified = await utils.run_in_executor(self, os.path.getmtime, file)
                if file in self.monitored_files:
                    if self.monitored_files[file] < modified:
                        modules.append({"name": file, "reload": True})
                        self.monitored_files[file] = modified
                else:
                    self.logger.debug("Found module %s", file)
                    modules.append({"name": file, "reload": False})
                    self. monitored_files[file] = modified
            except IOError as err:
                self.logger.warning("Unable to read app %s: %s - skipping", file, err)

        # Check for deleted modules and add them to the terminate list
        deleted_modules = []
        for file in self.monitored_files:
            if file not in found_files or mode == "term":
                deleted_modules.append(file)
                self.logger.info("Removing module %s", file)

        for file in deleted_modules:
            del self.monitored_files[file]
            for app in self.apps_per_module(self.get_module_from_path(file)):
                apps["term"][app] = 1

        # Add any apps we need to reload because of file changes

        for module in modules:
            for app in self.apps_per_module(self.get_module_from_path(module["name"])):
                if module["reload"]:
                    apps["term"][app] = 1
                apps["init"][app] = 1

            if "global_modules" in self.app_config:
                for gm in utils.single_or_list(self.app_config["global_modules"]):
                    if gm == self.get_module_from_path(module["name"]):
                        for app in self.apps_per_global_module(gm):
                            if module["reload"]:
                                apps["term"][app] = 1
                            apps["init"][app] = 1

        if plugin is not None:
            self.logger.info("Processing restart for %s", plugin)
            # This is a restart of one of the plugins so check which apps need to be restarted
            for app in self.app_config:
                reload = False
                if app in self.non_apps:
                    continue
                if "plugin" in self.app_config[app]:
                    for this_plugin in utils.single_or_list(self.app_config[app]["plugin"]):
                        if this_plugin == plugin:
                            # We got a match so do the reload
                            reload = True
                            break
                        elif plugin == "__ALL__":
                            reload = True
                            break
                else:
                    # No plugin dependency specified, reload to error on the side of caution
                    reload = True

                if reload is True:
                    apps["term"][app] = 1
                    apps["init"][app] = 1

        # Terminate apps

        if apps is not None and apps["term"]:

            prio_apps = self.get_app_deps_and_prios(apps["term"], mode)

            for app in sorted(prio_apps, key=prio_apps.get, reverse=True):
                await self.stop_app(app)

        # Load/reload modules

        for mod in modules:
            try:
                await utils.run_in_executor(self, self.read_app, mod["name"], mod["reload"])
            except:
                self.error.warning('-' * 60)
                self.error.warning("Unexpected error loading module: %s:", mod["name"])
                self.error.warning('-' * 60)
                self.error.warning(traceback.format_exc())
                self.error.warning('-' * 60)
                if self.AD.logging.separate_error_log() is True:
                    self.logger.warning("Unexpected error loading module: %s:", mod["name"])

                self.logger.warning("Removing associated apps:")
                module = self.get_module_from_path(mod["name"])
                for app in self.app_config:
                    if "module" in self.app_config[app] and self.app_config[app]["module"] == module:
                        if apps["init"] and app in apps["init"]:
                            del apps["init"][app]
                            self.logger.warning("%s", app)
                            await self.set_state(app, state="compile_error")

        if apps is not None and apps["init"]:

            prio_apps = self.get_app_deps_and_prios(apps["init"], mode)

            # Load Apps

            for app in sorted(prio_apps, key=prio_apps.get):
                try:
                    if "disable" in self.app_config[app] and self.app_config[app]["disable"] is True:
                        self.logger.info("%s is disabled", app)
                        await self.set_state(app, state="disabled")
                        await self.increase_inactive_apps(app)
                    else:
                        await self.init_object(app)
                except:
                    error_logger = logging.getLogger("Error.{}".format(app))
                    error_logger.warning('-' * 60)
                    error_logger.warning("Unexpected error initializing app: %s:", app)
                    error_logger.warning('-' * 60)
                    error_logger.warning(traceback.format_exc())
                    error_logger.warning('-' * 60)
                    if self.AD.logging.separate_error_log() is True:
                        self.logger.warning("Logged an error to %s", self.AD.logging.get_filename("error_log"))

            await self.AD.threading.calculate_pin_threads()

            # Call initialize() for apps

            for app in sorted(prio_apps, key=prio_apps.get):
                if "disable" in self.app_config[app] and self.app_config[app]["disable"] is True:
                    pass
                else:
                    await self.initialize_app(app)

        if self.AD.check_app_updates_profile is True:
            pr.disable()

        s = io.StringIO()
        sortby = 'cumulative'
        ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
        ps.print_stats()
        self.check_app_updates_profile_stats = s.getvalue()

        self.apps_initialized = True