import os import sys from typing import Dict, List, Optional, Tuple def load_plugin_list_if_exists(path: str) -> Optional[List[str]]: if not os.path.exists(path): return None plugins = [] for plugin in load_plugin_list(path): if "@" in plugin: plugin, path = plugin.split("@", 1) sys.path.append(path) plugins.append(plugin) return plugins def load_plugin_list(path: str) -> List[str]: with open(path, "r") as f: data = f.read() return parse_plugins_list(data) def parse_plugins_list(data: str) -> List[str]: plugins: List[str] = [] modules: Dict[str, Tuple[int, str]] = {} for line, entry in enumerate(data.splitlines()): plugin = entry.strip() if "#" in plugin: comment_start = plugin.find("#") plugin = plugin[:comment_start].strip() if not plugin: continue if "@" in plugin: module, path = map(lambda x: x.strip(), plugin.split("@", 1)) plugin = f"{module}@{path}" validate_local_plugin(line, module, path) else: module = plugin if module in modules: first_line, first_entry = modules[module] raise ValueError( f"plugin '{module}' is listed more than once: " f"at line {first_line} ('{first_entry}') and at {line} ('{entry}')" ) modules[module] = line, entry plugins.append(plugin) return plugins def validate_local_plugin(line: int, module: str, path: str): if not module: raise ValueError(f"local plugin entry at line {line} is missing a module name") if not path: raise ValueError(f"local plugin entry at line {line} is missing a path")