Path: blob/master/misc/direct/v1.6.0/launch_utils_org.py
3275 views
# this scripts installs necessary requirements and launches main program in webui.py1import logging2import re3import subprocess4import os5import shutil6import sys7import importlib.util8import platform9import json10from functools import lru_cache1112from modules import cmd_args, errors13from modules.paths_internal import script_path, extensions_dir14from modules.timer import startup_timer15from modules import logging_config1617args, _ = cmd_args.parser.parse_known_args()18logging_config.setup_logging(args.loglevel)1920python = sys.executable21git = os.environ.get('GIT', "git")22index_url = os.environ.get('INDEX_URL', "")23dir_repos = "repositories"2425# Whether to default to printing command output26default_command_live = (os.environ.get('WEBUI_LAUNCH_LIVE_OUTPUT') == "1")2728if 'GRADIO_ANALYTICS_ENABLED' not in os.environ:29os.environ['GRADIO_ANALYTICS_ENABLED'] = 'False'303132def check_python_version():33is_windows = platform.system() == "Windows"34major = sys.version_info.major35minor = sys.version_info.minor36micro = sys.version_info.micro3738if is_windows:39supported_minors = [10]40else:41supported_minors = [7, 8, 9, 10, 11]4243if not (major == 3 and minor in supported_minors):44import modules.errors4546modules.errors.print_error_explanation(f"""47INCOMPATIBLE PYTHON VERSION4849This program is tested with 3.10.6 Python, but you have {major}.{minor}.{micro}.50If you encounter an error with "RuntimeError: Couldn't install torch." message,51or any other error regarding unsuccessful package (library) installation,52please downgrade (or upgrade) to the latest version of 3.10 Python53and delete current Python and "venv" folder in WebUI's directory.5455You can download 3.10 Python from here: https://www.python.org/downloads/release/python-3106/5657{"Alternatively, use a binary release of WebUI: https://github.com/AUTOMATIC1111/stable-diffusion-webui/releases" if is_windows else ""}5859Use --skip-python-version-check to suppress this warning.60""")616263@lru_cache()64def commit_hash():65try:66return subprocess.check_output([git, "rev-parse", "HEAD"], shell=False, encoding='utf8').strip()67except Exception:68return "<none>"697071@lru_cache()72def git_tag():73try:74return subprocess.check_output([git, "describe", "--tags"], shell=False, encoding='utf8').strip()75except Exception:76try:7778changelog_md = os.path.join(os.path.dirname(os.path.dirname(__file__)), "CHANGELOG.md")79with open(changelog_md, "r", encoding="utf-8") as file:80line = next((line.strip() for line in file if line.strip()), "<none>")81line = line.replace("## ", "")82return line83except Exception:84return "<none>"858687def run(command, desc=None, errdesc=None, custom_env=None, live: bool = default_command_live) -> str:88if desc is not None:89print(desc)9091run_kwargs = {92"args": command,93"shell": True,94"env": os.environ if custom_env is None else custom_env,95"encoding": 'utf8',96"errors": 'ignore',97}9899if not live:100run_kwargs["stdout"] = run_kwargs["stderr"] = subprocess.PIPE101102result = subprocess.run(**run_kwargs)103104if result.returncode != 0:105error_bits = [106f"{errdesc or 'Error running command'}.",107f"Command: {command}",108f"Error code: {result.returncode}",109]110if result.stdout:111error_bits.append(f"stdout: {result.stdout}")112if result.stderr:113error_bits.append(f"stderr: {result.stderr}")114raise RuntimeError("\n".join(error_bits))115116return (result.stdout or "")117118119def is_installed(package):120try:121spec = importlib.util.find_spec(package)122except ModuleNotFoundError:123return False124125return spec is not None126127128def repo_dir(name):129return os.path.join(script_path, dir_repos, name)130131132def run_pip(command, desc=None, live=default_command_live):133if args.skip_install:134return135136index_url_line = f' --index-url {index_url}' if index_url != '' else ''137return run(f'"{python}" -m pip {command} --prefer-binary{index_url_line}', desc=f"Installing {desc}", errdesc=f"Couldn't install {desc}", live=live)138139140def check_run_python(code: str) -> bool:141result = subprocess.run([python, "-c", code], capture_output=True, shell=False)142return result.returncode == 0143144145def git_fix_workspace(dir, name):146run(f'"{git}" -C "{dir}" fetch --refetch --no-auto-gc', f"Fetching all contents for {name}", f"Couldn't fetch {name}", live=True)147run(f'"{git}" -C "{dir}" gc --aggressive --prune=now', f"Pruning {name}", f"Couldn't prune {name}", live=True)148return149150151def run_git(dir, name, command, desc=None, errdesc=None, custom_env=None, live: bool = default_command_live, autofix=True):152try:153return run(f'"{git}" -C "{dir}" {command}', desc=desc, errdesc=errdesc, custom_env=custom_env, live=live)154except RuntimeError:155if not autofix:156raise157158print(f"{errdesc}, attempting autofix...")159git_fix_workspace(dir, name)160161return run(f'"{git}" -C "{dir}" {command}', desc=desc, errdesc=errdesc, custom_env=custom_env, live=live)162163164def git_clone(url, dir, name, commithash=None):165# TODO clone into temporary dir and move if successful166167if os.path.exists(dir):168if commithash is None:169return170171current_hash = run_git(dir, name, 'rev-parse HEAD', None, f"Couldn't determine {name}'s hash: {commithash}", live=False).strip()172if current_hash == commithash:173return174175if run_git(dir, name, 'config --get remote.origin.url', None, f"Couldn't determine {name}'s origin URL", live=False).strip() != url:176run_git(dir, name, f'remote set-url origin "{url}"', None, f"Failed to set {name}'s origin URL", live=False)177178run_git(dir, name, 'fetch', f"Fetching updates for {name}...", f"Couldn't fetch {name}", autofix=False)179180run_git(dir, name, f'checkout {commithash}', f"Checking out commit for {name} with hash: {commithash}...", f"Couldn't checkout commit {commithash} for {name}", live=True)181182return183184try:185run(f'"{git}" clone "{url}" "{dir}"', f"Cloning {name} into {dir}...", f"Couldn't clone {name}", live=True)186except RuntimeError:187shutil.rmtree(dir, ignore_errors=True)188raise189190if commithash is not None:191run(f'"{git}" -C "{dir}" checkout {commithash}', None, "Couldn't checkout {name}'s hash: {commithash}")192193194def git_pull_recursive(dir):195for subdir, _, _ in os.walk(dir):196if os.path.exists(os.path.join(subdir, '.git')):197try:198output = subprocess.check_output([git, '-C', subdir, 'pull', '--autostash'])199print(f"Pulled changes for repository in '{subdir}':\n{output.decode('utf-8').strip()}\n")200except subprocess.CalledProcessError as e:201print(f"Couldn't perform 'git pull' on repository in '{subdir}':\n{e.output.decode('utf-8').strip()}\n")202203204def version_check(commit):205try:206import requests207commits = requests.get('https://api.github.com/repos/AUTOMATIC1111/stable-diffusion-webui/branches/master').json()208if commit != "<none>" and commits['commit']['sha'] != commit:209print("--------------------------------------------------------")210print("| You are not up to date with the most recent release. |")211print("| Consider running `git pull` to update. |")212print("--------------------------------------------------------")213elif commits['commit']['sha'] == commit:214print("You are up to date with the most recent release.")215else:216print("Not a git clone, can't perform version check.")217except Exception as e:218print("version check failed", e)219220221def run_extension_installer(extension_dir):222path_installer = os.path.join(extension_dir, "install.py")223if not os.path.isfile(path_installer):224return225226try:227env = os.environ.copy()228env['PYTHONPATH'] = f"{os.path.abspath('.')}{os.pathsep}{env.get('PYTHONPATH', '')}"229230stdout = run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {extension_dir}", custom_env=env).strip()231if stdout:232print(stdout)233except Exception as e:234errors.report(str(e))235236237def list_extensions(settings_file):238settings = {}239240try:241if os.path.isfile(settings_file):242with open(settings_file, "r", encoding="utf8") as file:243settings = json.load(file)244except Exception:245errors.report("Could not load settings", exc_info=True)246247disabled_extensions = set(settings.get('disabled_extensions', []))248disable_all_extensions = settings.get('disable_all_extensions', 'none')249250if disable_all_extensions != 'none' or args.disable_extra_extensions or args.disable_all_extensions or not os.path.isdir(extensions_dir):251return []252253return [x for x in os.listdir(extensions_dir) if x not in disabled_extensions]254255256def run_extensions_installers(settings_file):257if not os.path.isdir(extensions_dir):258return259260with startup_timer.subcategory("run extensions installers"):261for dirname_extension in list_extensions(settings_file):262logging.debug(f"Installing {dirname_extension}")263264path = os.path.join(extensions_dir, dirname_extension)265266if os.path.isdir(path):267run_extension_installer(path)268startup_timer.record(dirname_extension)269270271re_requirement = re.compile(r"\s*([-_a-zA-Z0-9]+)\s*(?:==\s*([-+_.a-zA-Z0-9]+))?\s*")272273274def requirements_met(requirements_file):275"""276Does a simple parse of a requirements.txt file to determine if all rerqirements in it277are already installed. Returns True if so, False if not installed or parsing fails.278"""279280import importlib.metadata281import packaging.version282283with open(requirements_file, "r", encoding="utf8") as file:284for line in file:285if line.strip() == "":286continue287288m = re.match(re_requirement, line)289if m is None:290return False291292package = m.group(1).strip()293version_required = (m.group(2) or "").strip()294295if version_required == "":296continue297298try:299version_installed = importlib.metadata.version(package)300except Exception:301return False302303if packaging.version.parse(version_required) != packaging.version.parse(version_installed):304return False305306return True307308309def prepare_environment():310torch_index_url = os.environ.get('TORCH_INDEX_URL', "https://download.pytorch.org/whl/cu118")311torch_command = os.environ.get('TORCH_COMMAND', f"pip install torch==2.0.1 torchvision==0.15.2 --extra-index-url {torch_index_url}")312requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt")313314xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.20')315clip_package = os.environ.get('CLIP_PACKAGE', "https://github.com/openai/CLIP/archive/d50d76daa670286dd6cacf3bcd80b5e4823fc8e1.zip")316openclip_package = os.environ.get('OPENCLIP_PACKAGE', "https://github.com/mlfoundations/open_clip/archive/bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b.zip")317318stable_diffusion_repo = os.environ.get('STABLE_DIFFUSION_REPO', "https://github.com/Stability-AI/stablediffusion.git")319stable_diffusion_xl_repo = os.environ.get('STABLE_DIFFUSION_XL_REPO', "https://github.com/Stability-AI/generative-models.git")320k_diffusion_repo = os.environ.get('K_DIFFUSION_REPO', 'https://github.com/crowsonkb/k-diffusion.git')321codeformer_repo = os.environ.get('CODEFORMER_REPO', 'https://github.com/sczhou/CodeFormer.git')322blip_repo = os.environ.get('BLIP_REPO', 'https://github.com/salesforce/BLIP.git')323324stable_diffusion_commit_hash = os.environ.get('STABLE_DIFFUSION_COMMIT_HASH', "cf1d67a6fd5ea1aa600c4df58e5b47da45f6bdbf")325stable_diffusion_xl_commit_hash = os.environ.get('STABLE_DIFFUSION_XL_COMMIT_HASH', "45c443b316737a4ab6e40413d7794a7f5657c19f")326k_diffusion_commit_hash = os.environ.get('K_DIFFUSION_COMMIT_HASH', "ab527a9a6d347f364e3d185ba6d714e22d80cb3c")327codeformer_commit_hash = os.environ.get('CODEFORMER_COMMIT_HASH', "c5b4593074ba6214284d6acd5f1719b6c5d739af")328blip_commit_hash = os.environ.get('BLIP_COMMIT_HASH', "48211a1594f1321b00f14c9f7a5b4813144b2fb9")329330try:331# the existence of this file is a signal to webui.sh/bat that webui needs to be restarted when it stops execution332os.remove(os.path.join(script_path, "tmp", "restart"))333os.environ.setdefault('SD_WEBUI_RESTARTING', '1')334except OSError:335pass336337if not args.skip_python_version_check:338check_python_version()339340startup_timer.record("checks")341342commit = commit_hash()343tag = git_tag()344startup_timer.record("git version info")345346print(f"Python {sys.version}")347print(f"Version: {tag}")348print(f"Commit hash: {commit}")349350if args.reinstall_torch or not is_installed("torch") or not is_installed("torchvision"):351run(f'"{python}" -m {torch_command}', "Installing torch and torchvision", "Couldn't install torch", live=True)352startup_timer.record("install torch")353354if not args.skip_torch_cuda_test and not check_run_python("import torch; assert torch.cuda.is_available()"):355raise RuntimeError(356'Torch is not able to use GPU; '357'add --skip-torch-cuda-test to COMMANDLINE_ARGS variable to disable this check'358)359startup_timer.record("torch GPU test")360361if not is_installed("clip"):362run_pip(f"install {clip_package}", "clip")363startup_timer.record("install clip")364365if not is_installed("open_clip"):366run_pip(f"install {openclip_package}", "open_clip")367startup_timer.record("install open_clip")368369if (not is_installed("xformers") or args.reinstall_xformers) and args.xformers:370run_pip(f"install -U -I --no-deps {xformers_package}", "xformers")371startup_timer.record("install xformers")372373if not is_installed("ngrok") and args.ngrok:374run_pip("install ngrok", "ngrok")375startup_timer.record("install ngrok")376377os.makedirs(os.path.join(script_path, dir_repos), exist_ok=True)378379git_clone(stable_diffusion_repo, repo_dir('stable-diffusion-stability-ai'), "Stable Diffusion", stable_diffusion_commit_hash)380git_clone(stable_diffusion_xl_repo, repo_dir('generative-models'), "Stable Diffusion XL", stable_diffusion_xl_commit_hash)381git_clone(k_diffusion_repo, repo_dir('k-diffusion'), "K-diffusion", k_diffusion_commit_hash)382git_clone(codeformer_repo, repo_dir('CodeFormer'), "CodeFormer", codeformer_commit_hash)383git_clone(blip_repo, repo_dir('BLIP'), "BLIP", blip_commit_hash)384385startup_timer.record("clone repositores")386387if not is_installed("lpips"):388run_pip(f"install -r \"{os.path.join(repo_dir('CodeFormer'), 'requirements.txt')}\"", "requirements for CodeFormer")389startup_timer.record("install CodeFormer requirements")390391if not os.path.isfile(requirements_file):392requirements_file = os.path.join(script_path, requirements_file)393394if not requirements_met(requirements_file):395run_pip(f"install -r \"{requirements_file}\"", "requirements")396startup_timer.record("install requirements")397398if not args.skip_install:399run_extensions_installers(settings_file=args.ui_settings_file)400401if args.update_check:402version_check(commit)403startup_timer.record("check version")404405if args.update_all_extensions:406git_pull_recursive(extensions_dir)407startup_timer.record("update extensions")408409if "--exit" in sys.argv:410print("Exiting because of --exit argument")411exit(0)412413414415def configure_for_tests():416if "--api" not in sys.argv:417sys.argv.append("--api")418if "--ckpt" not in sys.argv:419sys.argv.append("--ckpt")420sys.argv.append(os.path.join(script_path, "test/test_files/empty.pt"))421if "--skip-torch-cuda-test" not in sys.argv:422sys.argv.append("--skip-torch-cuda-test")423if "--disable-nan-check" not in sys.argv:424sys.argv.append("--disable-nan-check")425426os.environ['COMMANDLINE_ARGS'] = ""427428429def start():430print(f"Launching {'API server' if '--nowebui' in sys.argv else 'Web UI'} with arguments: {' '.join(sys.argv[1:])}")431import webui432if '--nowebui' in sys.argv:433webui.api_only()434else:435webui.webui()436437438def dump_sysinfo():439from modules import sysinfo440import datetime441442text = sysinfo.get()443filename = f"sysinfo-{datetime.datetime.utcnow().strftime('%Y-%m-%d-%H-%M')}.txt"444445with open(filename, "w", encoding="utf8") as file:446file.write(text)447448return filename449450451