diff --git a/install.py b/install.py index 86c0973..f5ebeb0 100644 --- a/install.py +++ b/install.py @@ -1,14 +1,14 @@ +import os +import sys + if __name__ == "__main__": try: from sd_dynamic_prompts.version_tools import install_requirements except ImportError: # This patching shouldn't be necessary, but who knows... See issue #486. - import os - import sys - extension_dir = os.path.dirname(os.path.abspath(__file__)) if extension_dir not in sys.path: sys.path.insert(0, extension_dir) from sd_dynamic_prompts.version_tools import install_requirements - install_requirements() + install_requirements(force=("-f" in sys.argv)) diff --git a/sd_dynamic_prompts/version_tools.py b/sd_dynamic_prompts/version_tools.py index bd0a9fa..8e8a6ce 100644 --- a/sd_dynamic_prompts/version_tools.py +++ b/sd_dynamic_prompts/version_tools.py @@ -3,10 +3,12 @@ from __future__ import annotations import dataclasses +import importlib.metadata import logging import shlex import subprocess import sys +from collections.abc import Iterable from functools import lru_cache from pathlib import Path @@ -37,25 +39,31 @@ logger = logging.getLogger(__name__) @dataclasses.dataclass class InstallResult: - requirement: str - specifier: str + requirement: Requirement installed: str - correct: bool @property def message(self) -> str | None: if self.correct: return None return ( - f"You have dynamicprompts {self.installed} installed, " - f"but this extension requires {self.specifier}. " + f"You have {self.requirement.name} version {self.installed or 'not'} installed, " + f"but this extension requires {self.specifier_str}. " f"Please run `install.py` from the sd-dynamic-prompts extension directory, " f"or `{self.pip_install_command}`." ) + @property + def specifier_str(self) -> str: + return str(self.requirement.specifier) + + @property + def correct(self) -> bool: + return self.installed and self.requirement.specifier.contains(self.installed) + @property def pip_install_command(self) -> str: - return f"pip install {self.requirement}" + return f"pip install {self.specifier_str}" def raise_if_incorrect(self) -> None: message = self.message @@ -69,28 +77,34 @@ def get_requirements() -> tuple[str]: return tuple(tomli.loads(toml_text)["project"]["dependencies"]) -def get_dynamic_prompts_requirement() -> Requirement | None: - for req in get_requirements(): - if req.startswith("dynamicprompts"): - return Requirement(req) - return None +def get_install_result(req_str: str) -> InstallResult: + req = Requirement(req_str) + try: + installed_version = importlib.metadata.version(req.name) + except ImportError: + installed_version = None + res = InstallResult(requirement=req, installed=installed_version) + return res + + +def get_requirements_install_results() -> Iterable[InstallResult]: + """ + Get InstallResult objects for all requirements. + """ + return (get_install_result(req_str) for req_str in get_requirements()) def get_dynamicprompts_install_result() -> InstallResult: - import dynamicprompts - - dp_req = get_dynamic_prompts_requirement() - if not dp_req: - raise RuntimeError("dynamicprompts requirement not found") - return InstallResult( - requirement=str(dp_req), - specifier=str(dp_req.specifier), - installed=dynamicprompts.__version__, - correct=(dynamicprompts.__version__ in dp_req.specifier), - ) + """ + Get the InstallResult for the dynamicprompts requirement. + """ + for req in get_requirements(): + if req.startswith("dynamicprompts"): + return get_install_result(req) + raise RuntimeError("dynamicprompts requirement not found") -def install_requirements() -> None: +def install_requirements(force=False) -> None: """ Invoke pip to install the requirements for the extension. """ @@ -104,21 +118,30 @@ def install_requirements() -> None: return except ImportError: pass + + requirements_to_install = [ + str(ires.requirement) + for ires in get_requirements_install_results() + if (force or not ires.correct) + ] + + if not requirements_to_install: + return + command = [ sys.executable, "-m", "pip", "install", - *get_requirements(), + *requirements_to_install, ] print(f"sd-dynamic-prompts installer: running {shlex.join(command)}") subprocess.check_call(command) def selftest() -> None: - res = get_dynamicprompts_install_result() - print(res) - res.raise_if_incorrect() + for res in get_requirements_install_results(): + print("[OK]" if res.correct else "????", res.requirement, res) if __name__ == "__main__":