Path: blob/master/misc/direct/v1.9.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 importlib.metadata9import platform10import json11from functools import lru_cache1213from modules import cmd_args, errors14from modules.paths_internal import script_path, extensions_dir15from modules.timer import startup_timer16from modules import logging_config1718args, _ = cmd_args.parser.parse_known_args()19logging_config.setup_logging(args.loglevel)2021python = sys.executable22git = os.environ.get('GIT', "git")23index_url = os.environ.get('INDEX_URL', "")24dir_repos = "repositories"2526# Whether to default to printing command output27default_command_live = (os.environ.get('WEBUI_LAUNCH_LIVE_OUTPUT') == "1")2829os.environ.setdefault('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/tag/v1.0.0-pre" 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, "-C", script_path, "rev-parse", "HEAD"], shell=False, encoding='utf8').strip()67except Exception:68return "<none>"697071@lru_cache()72def git_tag():73try:74return subprocess.check_output([git, "-C", script_path, "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:121dist = importlib.metadata.distribution(package)122except importlib.metadata.PackageNotFoundError:123try:124spec = importlib.util.find_spec(package)125except ModuleNotFoundError:126return False127128return spec is not None129130return dist is not None131132133def repo_dir(name):134return os.path.join(script_path, dir_repos, name)135136137def run_pip(command, desc=None, live=default_command_live):138if args.skip_install:139return140141index_url_line = f' --index-url {index_url}' if index_url != '' else ''142return run(f'"{python}" -m pip {command} --prefer-binary{index_url_line}', desc=f"Installing {desc}", errdesc=f"Couldn't install {desc}", live=live)143144145def check_run_python(code: str) -> bool:146result = subprocess.run([python, "-c", code], capture_output=True, shell=False)147return result.returncode == 0148149150def git_fix_workspace(dir, name):151run(f'"{git}" -C "{dir}" fetch --refetch --no-auto-gc', f"Fetching all contents for {name}", f"Couldn't fetch {name}", live=True)152run(f'"{git}" -C "{dir}" gc --aggressive --prune=now', f"Pruning {name}", f"Couldn't prune {name}", live=True)153return154155156def run_git(dir, name, command, desc=None, errdesc=None, custom_env=None, live: bool = default_command_live, autofix=True):157try:158return run(f'"{git}" -C "{dir}" {command}', desc=desc, errdesc=errdesc, custom_env=custom_env, live=live)159except RuntimeError:160if not autofix:161raise162163print(f"{errdesc}, attempting autofix...")164git_fix_workspace(dir, name)165166return run(f'"{git}" -C "{dir}" {command}', desc=desc, errdesc=errdesc, custom_env=custom_env, live=live)167168169def git_clone(url, dir, name, commithash=None):170# TODO clone into temporary dir and move if successful171172if os.path.exists(dir):173if commithash is None:174return175176current_hash = run_git(dir, name, 'rev-parse HEAD', None, f"Couldn't determine {name}'s hash: {commithash}", live=False).strip()177if current_hash == commithash:178return179180if run_git(dir, name, 'config --get remote.origin.url', None, f"Couldn't determine {name}'s origin URL", live=False).strip() != url:181run_git(dir, name, f'remote set-url origin "{url}"', None, f"Failed to set {name}'s origin URL", live=False)182183run_git(dir, name, 'fetch', f"Fetching updates for {name}...", f"Couldn't fetch {name}", autofix=False)184185run_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)186187return188189try:190run(f'"{git}" clone --config core.filemode=false "{url}" "{dir}"', f"Cloning {name} into {dir}...", f"Couldn't clone {name}", live=True)191except RuntimeError:192shutil.rmtree(dir, ignore_errors=True)193raise194195if commithash is not None:196run(f'"{git}" -C "{dir}" checkout {commithash}', None, "Couldn't checkout {name}'s hash: {commithash}")197198199def git_pull_recursive(dir):200for subdir, _, _ in os.walk(dir):201if os.path.exists(os.path.join(subdir, '.git')):202try:203output = subprocess.check_output([git, '-C', subdir, 'pull', '--autostash'])204print(f"Pulled changes for repository in '{subdir}':\n{output.decode('utf-8').strip()}\n")205except subprocess.CalledProcessError as e:206print(f"Couldn't perform 'git pull' on repository in '{subdir}':\n{e.output.decode('utf-8').strip()}\n")207208209def version_check(commit):210try:211import requests212commits = requests.get('https://api.github.com/repos/AUTOMATIC1111/stable-diffusion-webui/branches/master').json()213if commit != "<none>" and commits['commit']['sha'] != commit:214print("--------------------------------------------------------")215print("| You are not up to date with the most recent release. |")216print("| Consider running `git pull` to update. |")217print("--------------------------------------------------------")218elif commits['commit']['sha'] == commit:219print("You are up to date with the most recent release.")220else:221print("Not a git clone, can't perform version check.")222except Exception as e:223print("version check failed", e)224225226def run_extension_installer(extension_dir):227path_installer = os.path.join(extension_dir, "install.py")228if not os.path.isfile(path_installer):229return230231try:232env = os.environ.copy()233env['PYTHONPATH'] = f"{os.path.abspath('.')}{os.pathsep}{env.get('PYTHONPATH', '')}"234235stdout = run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {extension_dir}", custom_env=env).strip()236if stdout:237print(stdout)238except Exception as e:239errors.report(str(e))240241242def list_extensions(settings_file):243settings = {}244245try:246with open(settings_file, "r", encoding="utf8") as file:247settings = json.load(file)248except FileNotFoundError:249pass250except Exception:251errors.report(f'\nCould not load settings\nThe config file "{settings_file}" is likely corrupted\nIt has been moved to the "tmp/config.json"\nReverting config to default\n\n''', exc_info=True)252os.replace(settings_file, os.path.join(script_path, "tmp", "config.json"))253254disabled_extensions = set(settings.get('disabled_extensions', []))255disable_all_extensions = settings.get('disable_all_extensions', 'none')256257if disable_all_extensions != 'none' or args.disable_extra_extensions or args.disable_all_extensions or not os.path.isdir(extensions_dir):258return []259260return [x for x in os.listdir(extensions_dir) if x not in disabled_extensions]261262263def run_extensions_installers(settings_file):264if not os.path.isdir(extensions_dir):265return266267with startup_timer.subcategory("run extensions installers"):268for dirname_extension in list_extensions(settings_file):269logging.debug(f"Installing {dirname_extension}")270271path = os.path.join(extensions_dir, dirname_extension)272273if os.path.isdir(path):274run_extension_installer(path)275startup_timer.record(dirname_extension)276277278re_requirement = re.compile(r"\s*([-_a-zA-Z0-9]+)\s*(?:==\s*([-+_.a-zA-Z0-9]+))?\s*")279280281def requirements_met(requirements_file):282"""283Does a simple parse of a requirements.txt file to determine if all rerqirements in it284are already installed. Returns True if so, False if not installed or parsing fails.285"""286287import importlib.metadata288import packaging.version289290with open(requirements_file, "r", encoding="utf8") as file:291for line in file:292if line.strip() == "":293continue294295m = re.match(re_requirement, line)296if m is None:297return False298299package = m.group(1).strip()300version_required = (m.group(2) or "").strip()301302if version_required == "":303continue304305try:306version_installed = importlib.metadata.version(package)307except Exception:308return False309310if packaging.version.parse(version_required) != packaging.version.parse(version_installed):311return False312313return True314315316def prepare_environment():317torch_index_url = os.environ.get('TORCH_INDEX_URL', "https://download.pytorch.org/whl/cu121")318torch_command = os.environ.get('TORCH_COMMAND', f"pip install torch==2.1.2 torchvision==0.16.2 --extra-index-url {torch_index_url}")319if args.use_ipex:320if platform.system() == "Windows":321# The "Nuullll/intel-extension-for-pytorch" wheels were built from IPEX source for Intel Arc GPU: https://github.com/intel/intel-extension-for-pytorch/tree/xpu-main322# This is NOT an Intel official release so please use it at your own risk!!323# See https://github.com/Nuullll/intel-extension-for-pytorch/releases/tag/v2.0.110%2Bxpu-master%2Bdll-bundle for details.324#325# Strengths (over official IPEX 2.0.110 windows release):326# - AOT build (for Arc GPU only) to eliminate JIT compilation overhead: https://github.com/intel/intel-extension-for-pytorch/issues/399327# - Bundles minimal oneAPI 2023.2 dependencies into the python wheels, so users don't need to install oneAPI for the whole system.328# - Provides a compatible torchvision wheel: https://github.com/intel/intel-extension-for-pytorch/issues/465329# Limitation:330# - Only works for python 3.10331url_prefix = "https://github.com/Nuullll/intel-extension-for-pytorch/releases/download/v2.0.110%2Bxpu-master%2Bdll-bundle"332torch_command = os.environ.get('TORCH_COMMAND', f"pip install {url_prefix}/torch-2.0.0a0+gite9ebda2-cp310-cp310-win_amd64.whl {url_prefix}/torchvision-0.15.2a0+fa99a53-cp310-cp310-win_amd64.whl {url_prefix}/intel_extension_for_pytorch-2.0.110+gitc6ea20b-cp310-cp310-win_amd64.whl")333else:334# Using official IPEX release for linux since it's already an AOT build.335# However, users still have to install oneAPI toolkit and activate oneAPI environment manually.336# See https://intel.github.io/intel-extension-for-pytorch/index.html#installation for details.337torch_index_url = os.environ.get('TORCH_INDEX_URL', "https://pytorch-extension.intel.com/release-whl/stable/xpu/us/")338torch_command = os.environ.get('TORCH_COMMAND', f"pip install torch==2.0.0a0 intel-extension-for-pytorch==2.0.110+gitba7f6c1 --extra-index-url {torch_index_url}")339requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt")340requirements_file_for_npu = os.environ.get('REQS_FILE_FOR_NPU', "requirements_npu.txt")341342xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.23.post1')343clip_package = os.environ.get('CLIP_PACKAGE', "https://github.com/openai/CLIP/archive/d50d76daa670286dd6cacf3bcd80b5e4823fc8e1.zip")344openclip_package = os.environ.get('OPENCLIP_PACKAGE', "https://github.com/mlfoundations/open_clip/archive/bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b.zip")345346assets_repo = os.environ.get('ASSETS_REPO', "https://github.com/AUTOMATIC1111/stable-diffusion-webui-assets.git")347stable_diffusion_repo = os.environ.get('STABLE_DIFFUSION_REPO', "https://github.com/Stability-AI/stablediffusion.git")348stable_diffusion_xl_repo = os.environ.get('STABLE_DIFFUSION_XL_REPO', "https://github.com/Stability-AI/generative-models.git")349k_diffusion_repo = os.environ.get('K_DIFFUSION_REPO', 'https://github.com/crowsonkb/k-diffusion.git')350blip_repo = os.environ.get('BLIP_REPO', 'https://github.com/salesforce/BLIP.git')351352assets_commit_hash = os.environ.get('ASSETS_COMMIT_HASH', "6f7db241d2f8ba7457bac5ca9753331f0c266917")353stable_diffusion_commit_hash = os.environ.get('STABLE_DIFFUSION_COMMIT_HASH', "cf1d67a6fd5ea1aa600c4df58e5b47da45f6bdbf")354stable_diffusion_xl_commit_hash = os.environ.get('STABLE_DIFFUSION_XL_COMMIT_HASH', "45c443b316737a4ab6e40413d7794a7f5657c19f")355k_diffusion_commit_hash = os.environ.get('K_DIFFUSION_COMMIT_HASH', "ab527a9a6d347f364e3d185ba6d714e22d80cb3c")356blip_commit_hash = os.environ.get('BLIP_COMMIT_HASH', "48211a1594f1321b00f14c9f7a5b4813144b2fb9")357358try:359# the existence of this file is a signal to webui.sh/bat that webui needs to be restarted when it stops execution360os.remove(os.path.join(script_path, "tmp", "restart"))361os.environ.setdefault('SD_WEBUI_RESTARTING', '1')362except OSError:363pass364365if not args.skip_python_version_check:366check_python_version()367368startup_timer.record("checks")369370commit = commit_hash()371tag = git_tag()372startup_timer.record("git version info")373374print(f"Python {sys.version}")375print(f"Version: {tag}")376print(f"Commit hash: {commit}")377378if args.reinstall_torch or not is_installed("torch") or not is_installed("torchvision"):379run(f'"{python}" -m {torch_command}', "Installing torch and torchvision", "Couldn't install torch", live=True)380startup_timer.record("install torch")381382if args.use_ipex:383args.skip_torch_cuda_test = True384if not args.skip_torch_cuda_test and not check_run_python("import torch; assert torch.cuda.is_available()"):385raise RuntimeError(386'Torch is not able to use GPU; '387'add --skip-torch-cuda-test to COMMANDLINE_ARGS variable to disable this check'388)389startup_timer.record("torch GPU test")390391if not is_installed("clip"):392run_pip(f"install {clip_package}", "clip")393startup_timer.record("install clip")394395if not is_installed("open_clip"):396run_pip(f"install {openclip_package}", "open_clip")397startup_timer.record("install open_clip")398399if (not is_installed("xformers") or args.reinstall_xformers) and args.xformers:400run_pip(f"install -U -I --no-deps {xformers_package}", "xformers")401startup_timer.record("install xformers")402403if not is_installed("ngrok") and args.ngrok:404run_pip("install ngrok", "ngrok")405startup_timer.record("install ngrok")406407os.makedirs(os.path.join(script_path, dir_repos), exist_ok=True)408409git_clone(assets_repo, repo_dir('stable-diffusion-webui-assets'), "assets", assets_commit_hash)410git_clone(stable_diffusion_repo, repo_dir('stable-diffusion-stability-ai'), "Stable Diffusion", stable_diffusion_commit_hash)411git_clone(stable_diffusion_xl_repo, repo_dir('generative-models'), "Stable Diffusion XL", stable_diffusion_xl_commit_hash)412git_clone(k_diffusion_repo, repo_dir('k-diffusion'), "K-diffusion", k_diffusion_commit_hash)413git_clone(blip_repo, repo_dir('BLIP'), "BLIP", blip_commit_hash)414415startup_timer.record("clone repositores")416417if not os.path.isfile(requirements_file):418requirements_file = os.path.join(script_path, requirements_file)419420if not requirements_met(requirements_file):421run_pip(f"install -r \"{requirements_file}\"", "requirements")422startup_timer.record("install requirements")423424if not os.path.isfile(requirements_file_for_npu):425requirements_file_for_npu = os.path.join(script_path, requirements_file_for_npu)426427if "torch_npu" in torch_command and not requirements_met(requirements_file_for_npu):428run_pip(f"install -r \"{requirements_file_for_npu}\"", "requirements_for_npu")429startup_timer.record("install requirements_for_npu")430431if not args.skip_install:432run_extensions_installers(settings_file=args.ui_settings_file)433434if args.update_check:435version_check(commit)436startup_timer.record("check version")437438if args.update_all_extensions:439git_pull_recursive(extensions_dir)440startup_timer.record("update extensions")441442if "--exit" in sys.argv:443print("Exiting because of --exit argument")444exit(0)445446447448def configure_for_tests():449if "--api" not in sys.argv:450sys.argv.append("--api")451if "--ckpt" not in sys.argv:452sys.argv.append("--ckpt")453sys.argv.append(os.path.join(script_path, "test/test_files/empty.pt"))454if "--skip-torch-cuda-test" not in sys.argv:455sys.argv.append("--skip-torch-cuda-test")456if "--disable-nan-check" not in sys.argv:457sys.argv.append("--disable-nan-check")458459os.environ['COMMANDLINE_ARGS'] = ""460461462def start():463print(f"Launching {'API server' if '--nowebui' in sys.argv else 'Web UI'} with arguments: {' '.join(sys.argv[1:])}")464import webui465if '--nowebui' in sys.argv:466webui.api_only()467else:468webui.webui()469470471def dump_sysinfo():472from modules import sysinfo473import datetime474475text = sysinfo.get()476filename = f"sysinfo-{datetime.datetime.utcnow().strftime('%Y-%m-%d-%H-%M')}.json"477478with open(filename, "w", encoding="utf8") as file:479file.write(text)480481return filename482483484