install: only invoke install for missing deps

Fixes #499
pull/509/head
Aarni Koskela 2023-06-05 21:53:05 +03:00
parent 45b21373c0
commit 5b4769156b
2 changed files with 54 additions and 31 deletions

View File

@ -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))

View File

@ -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__":