launch_utils.py 13.5 KB
Newer Older
1 2 3 4 5
# this scripts installs necessary requirements and launches main program in webui.py
import subprocess
import os
import sys
import importlib.util
C
C43H66N12O12S2 已提交
6
import platform
7
import json
8
from functools import lru_cache
9

10
from modules import cmd_args, errors
11
from modules.paths_internal import script_path, extensions_dir
12

13
args, _ = cmd_args.parser.parse_known_args()
14

15 16
python = sys.executable
git = os.environ.get('GIT', "git")
A
AUTOMATIC 已提交
17
index_url = os.environ.get('INDEX_URL', "")
18
dir_repos = "repositories"
A
AUTOMATIC 已提交
19

20 21 22
# Whether to default to printing command output
default_command_live = (os.environ.get('WEBUI_LAUNCH_LIVE_OUTPUT') == "1")

23 24
if 'GRADIO_ANALYTICS_ENABLED' not in os.environ:
    os.environ['GRADIO_ANALYTICS_ENABLED'] = 'False'
A
AUTOMATIC 已提交
25

26

S
Spaceginner 已提交
27
def check_python_version():
28 29 30 31 32 33
    is_windows = platform.system() == "Windows"
    major = sys.version_info.major
    minor = sys.version_info.minor
    micro = sys.version_info.micro

    if is_windows:
34 35 36 37
        supported_minors = [10]
    else:
        supported_minors = [7, 8, 9, 10, 11]

38
    if not (major == 3 and minor in supported_minors):
39 40 41
        import modules.errors

        modules.errors.print_error_explanation(f"""
42 43 44
INCOMPATIBLE PYTHON VERSION

This program is tested with 3.10.6 Python, but you have {major}.{minor}.{micro}.
S
Spaceginner 已提交
45 46 47 48 49
If you encounter an error with "RuntimeError: Couldn't install torch." message,
or any other error regarding unsuccessful package (library) installation,
please downgrade (or upgrade) to the latest version of 3.10 Python
and delete current Python and "venv" folder in WebUI's directory.

T
Thierry 已提交
50
You can download 3.10 Python from here: https://www.python.org/downloads/release/python-3106/
51 52 53 54

{"Alternatively, use a binary release of WebUI: https://github.com/AUTOMATIC1111/stable-diffusion-webui/releases" if is_windows else ""}

Use --skip-python-version-check to suppress this warning.
55
""")
S
Spaceginner 已提交
56 57


58
@lru_cache()
A
AUTOMATIC 已提交
59 60
def commit_hash():
    try:
61
        return subprocess.check_output([git, "rev-parse", "HEAD"], shell=False, encoding='utf8').strip()
A
AUTOMATIC 已提交
62
    except Exception:
63
        return "<none>"
64

65

66
@lru_cache()
67 68
def git_tag():
    try:
69
        return subprocess.check_output([git, "describe", "--tags"], shell=False, encoding='utf8').strip()
70
    except Exception:
W
w-e-w 已提交
71
        try:
W
w-e-w 已提交
72 73
            from pathlib import Path
            changelog_md = Path(__file__).parent.parent / "CHANGELOG.md"
W
w-e-w 已提交
74 75 76 77
            with changelog_md.open(encoding="utf-8") as file:
                return next((line.strip() for line in file if line.strip()), "<none>")
        except Exception:
            return "<none>"
78 79


A
Aarni Koskela 已提交
80
def run(command, desc=None, errdesc=None, custom_env=None, live: bool = default_command_live) -> str:
81 82 83
    if desc is not None:
        print(desc)

A
Aarni Koskela 已提交
84 85 86 87 88 89 90
    run_kwargs = {
        "args": command,
        "shell": True,
        "env": os.environ if custom_env is None else custom_env,
        "encoding": 'utf8',
        "errors": 'ignore',
    }
A
AUTOMATIC 已提交
91

A
Aarni Koskela 已提交
92 93
    if not live:
        run_kwargs["stdout"] = run_kwargs["stderr"] = subprocess.PIPE
A
AUTOMATIC 已提交
94

A
Aarni Koskela 已提交
95
    result = subprocess.run(**run_kwargs)
96 97

    if result.returncode != 0:
A
Aarni Koskela 已提交
98 99 100 101 102 103 104 105 106 107
        error_bits = [
            f"{errdesc or 'Error running command'}.",
            f"Command: {command}",
            f"Error code: {result.returncode}",
        ]
        if result.stdout:
            error_bits.append(f"stdout: {result.stdout}")
        if result.stderr:
            error_bits.append(f"stderr: {result.stderr}")
        raise RuntimeError("\n".join(error_bits))
108

A
Aarni Koskela 已提交
109
    return (result.stdout or "")
110 111 112 113 114 115 116 117 118 119 120


def is_installed(package):
    try:
        spec = importlib.util.find_spec(package)
    except ModuleNotFoundError:
        return False

    return spec is not None


121
def repo_dir(name):
122
    return os.path.join(script_path, dir_repos, name)
123 124


125
def run_pip(command, desc=None, live=default_command_live):
126
    if args.skip_install:
127 128
        return

A
AUTOMATIC 已提交
129
    index_url_line = f' --index-url {index_url}' if index_url != '' else ''
130
    return run(f'"{python}" -m pip {command} --prefer-binary{index_url_line}', desc=f"Installing {desc}", errdesc=f"Couldn't install {desc}", live=live)
131 132


133
def check_run_python(code: str) -> bool:
134
    result = subprocess.run([python, "-c", code], capture_output=True, shell=False)
135
    return result.returncode == 0
136 137 138 139 140 141 142 143 144


def git_clone(url, dir, name, commithash=None):
    # TODO clone into temporary dir and move if successful

    if os.path.exists(dir):
        if commithash is None:
            return

145
        current_hash = run(f'"{git}" -C "{dir}" rev-parse HEAD', None, f"Couldn't determine {name}'s hash: {commithash}").strip()
146 147 148
        if current_hash == commithash:
            return

149 150
        run(f'"{git}" -C "{dir}" fetch', f"Fetching updates for {name}...", f"Couldn't fetch {name}")
        run(f'"{git}" -C "{dir}" checkout {commithash}', f"Checking out commit for {name} with hash: {commithash}...", f"Couldn't checkout commit {commithash} for {name}")
151
        return
152

153 154 155
    run(f'"{git}" clone "{url}" "{dir}"', f"Cloning {name} into {dir}...", f"Couldn't clone {name}")

    if commithash is not None:
156
        run(f'"{git}" -C "{dir}" checkout {commithash}', None, "Couldn't checkout {name}'s hash: {commithash}")
157

158

159 160 161 162
def git_pull_recursive(dir):
    for subdir, _, _ in os.walk(dir):
        if os.path.exists(os.path.join(subdir, '.git')):
            try:
163
                output = subprocess.check_output([git, '-C', subdir, 'pull', '--autostash'])
164 165 166 167
                print(f"Pulled changes for repository in '{subdir}':\n{output.decode('utf-8').strip()}\n")
            except subprocess.CalledProcessError as e:
                print(f"Couldn't perform 'git pull' on repository in '{subdir}':\n{e.output.decode('utf-8').strip()}\n")

168

169 170 171 172 173 174 175 176 177 178 179 180 181 182
def version_check(commit):
    try:
        import requests
        commits = requests.get('https://api.github.com/repos/AUTOMATIC1111/stable-diffusion-webui/branches/master').json()
        if commit != "<none>" and commits['commit']['sha'] != commit:
            print("--------------------------------------------------------")
            print("| You are not up to date with the most recent release. |")
            print("| Consider running `git pull` to update.               |")
            print("--------------------------------------------------------")
        elif commits['commit']['sha'] == commit:
            print("You are up to date with the most recent release.")
        else:
            print("Not a git clone, can't perform version check.")
    except Exception as e:
183 184 185
        print("version check failed", e)


186 187 188 189 190 191 192 193 194 195 196
def run_extension_installer(extension_dir):
    path_installer = os.path.join(extension_dir, "install.py")
    if not os.path.isfile(path_installer):
        return

    try:
        env = os.environ.copy()
        env['PYTHONPATH'] = os.path.abspath(".")

        print(run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {extension_dir}", custom_env=env))
    except Exception as e:
197
        errors.report(str(e))
198 199


200 201 202 203 204 205 206
def list_extensions(settings_file):
    settings = {}

    try:
        if os.path.isfile(settings_file):
            with open(settings_file, "r", encoding="utf8") as file:
                settings = json.load(file)
207
    except Exception:
208
        errors.report("Could not load settings", exc_info=True)
209 210

    disabled_extensions = set(settings.get('disabled_extensions', []))
211 212 213 214
    disable_all_extensions = settings.get('disable_all_extensions', 'none')

    if disable_all_extensions != 'none':
        return []
215

216
    return [x for x in os.listdir(extensions_dir) if x not in disabled_extensions]
217 218 219


def run_extensions_installers(settings_file):
220
    if not os.path.isdir(extensions_dir):
221 222
        return

223
    for dirname_extension in list_extensions(settings_file):
224
        run_extension_installer(os.path.join(extensions_dir, dirname_extension))
225

226

H
Hsiang-Cheng Yang 已提交
227
def prepare_environment():
228 229
    torch_index_url = os.environ.get('TORCH_INDEX_URL', "https://download.pytorch.org/whl/cu118")
    torch_command = os.environ.get('TORCH_COMMAND', f"pip install torch==2.0.1 torchvision==0.15.2 --extra-index-url {torch_index_url}")
230
    requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt")
231

S
Sakura-Luna 已提交
232
    xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.20')
233 234 235
    gfpgan_package = os.environ.get('GFPGAN_PACKAGE', "https://github.com/TencentARC/GFPGAN/archive/8d2447a2d918f8eba5a4a01463fd48e45126a379.zip")
    clip_package = os.environ.get('CLIP_PACKAGE', "https://github.com/openai/CLIP/archive/d50d76daa670286dd6cacf3bcd80b5e4823fc8e1.zip")
    openclip_package = os.environ.get('OPENCLIP_PACKAGE', "https://github.com/mlfoundations/open_clip/archive/bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b.zip")
236

A
AUTOMATIC 已提交
237
    stable_diffusion_repo = os.environ.get('STABLE_DIFFUSION_REPO', "https://github.com/Stability-AI/stablediffusion.git")
238
    k_diffusion_repo = os.environ.get('K_DIFFUSION_REPO', 'https://github.com/crowsonkb/k-diffusion.git')
A
Anas Abou Allaban 已提交
239
    codeformer_repo = os.environ.get('CODEFORMER_REPO', 'https://github.com/sczhou/CodeFormer.git')
240
    blip_repo = os.environ.get('BLIP_REPO', 'https://github.com/salesforce/BLIP.git')
241

242
    stable_diffusion_commit_hash = os.environ.get('STABLE_DIFFUSION_COMMIT_HASH', "cf1d67a6fd5ea1aa600c4df58e5b47da45f6bdbf")
243
    k_diffusion_commit_hash = os.environ.get('K_DIFFUSION_COMMIT_HASH', "c9fe758757e022f05ca5a53fa8fac28889e4f1cf")
244 245 246
    codeformer_commit_hash = os.environ.get('CODEFORMER_COMMIT_HASH', "c5b4593074ba6214284d6acd5f1719b6c5d739af")
    blip_commit_hash = os.environ.get('BLIP_COMMIT_HASH', "48211a1594f1321b00f14c9f7a5b4813144b2fb9")

247 248 249
    try:
        # the existance of this file is a signal to webui.sh/bat that webui needs to be restarted when it stops execution
        os.remove(os.path.join(script_path, "tmp", "restart"))
W
w-e-w 已提交
250
        os.environ.setdefault('SD_WEBUI_RESTARTING ', '1')
251 252 253
    except OSError:
        pass

254
    if not args.skip_python_version_check:
255 256
        check_python_version()

A
AUTOMATIC 已提交
257
    commit = commit_hash()
258
    tag = git_tag()
259

260
    print(f"Python {sys.version}")
261
    print(f"Version: {tag}")
262
    print(f"Commit hash: {commit}")
A
AUTOMATIC 已提交
263

264
    if args.reinstall_torch or not is_installed("torch") or not is_installed("torchvision"):
A
AUTOMATIC 已提交
265
        run(f'"{python}" -m {torch_command}', "Installing torch and torchvision", "Couldn't install torch", live=True)
266

267 268 269 270 271
    if not args.skip_torch_cuda_test and not check_run_python("import torch; assert torch.cuda.is_available()"):
        raise RuntimeError(
            'Torch is not able to use GPU; '
            'add --skip-torch-cuda-test to COMMANDLINE_ARGS variable to disable this check'
        )
272

273
    if not is_installed("gfpgan"):
C
Cassy-Lee 已提交
274
        run_pip(f"install {gfpgan_package}", "gfpgan")
275

276
    if not is_installed("clip"):
C
Cassy-Lee 已提交
277
        run_pip(f"install {clip_package}", "clip")
C
C43H66N12O12S2 已提交
278

A
AUTOMATIC 已提交
279 280 281
    if not is_installed("open_clip"):
        run_pip(f"install {openclip_package}", "open_clip")

282
    if (not is_installed("xformers") or args.reinstall_xformers) and args.xformers:
283
        if platform.system() == "Windows":
ふぁ 已提交
284
            if platform.python_version().startswith("3.10"):
285
                run_pip(f"install -U -I --no-deps {xformers_package}", "xformers", live=True)
ふぁ 已提交
286 287 288 289 290
            else:
                print("Installation of xformers is not supported in this version of Python.")
                print("You can also check this and build manually: https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Xformers#building-xformers-on-windows-by-duckness")
                if not is_installed("xformers"):
                    exit(0)
291
        elif platform.system() == "Linux":
S
Sakura-Luna 已提交
292
            run_pip(f"install -U -I --no-deps {xformers_package}", "xformers")
293

B
bobzilladev 已提交
294 295
    if not is_installed("ngrok") and args.ngrok:
        run_pip("install ngrok", "ngrok")
J
JamnedZ 已提交
296

297
    os.makedirs(os.path.join(script_path, dir_repos), exist_ok=True)
298

A
AUTOMATIC 已提交
299
    git_clone(stable_diffusion_repo, repo_dir('stable-diffusion-stability-ai'), "Stable Diffusion", stable_diffusion_commit_hash)
300 301 302
    git_clone(k_diffusion_repo, repo_dir('k-diffusion'), "K-diffusion", k_diffusion_commit_hash)
    git_clone(codeformer_repo, repo_dir('CodeFormer'), "CodeFormer", codeformer_commit_hash)
    git_clone(blip_repo, repo_dir('BLIP'), "BLIP", blip_commit_hash)
303

304
    if not is_installed("lpips"):
305
        run_pip(f"install -r \"{os.path.join(repo_dir('CodeFormer'), 'requirements.txt')}\"", "requirements for CodeFormer")
306

307 308
    if not os.path.isfile(requirements_file):
        requirements_file = os.path.join(script_path, requirements_file)
V
Vladimir Mandic 已提交
309
    run_pip(f"install -r \"{requirements_file}\"", "requirements")
310

311
    run_extensions_installers(settings_file=args.ui_settings_file)
312

313
    if args.update_check:
314
        version_check(commit)
315

316 317
    if args.update_all_extensions:
        git_pull_recursive(extensions_dir)
318

319
    if "--exit" in sys.argv:
320 321
        print("Exiting because of --exit argument")
        exit(0)
322

323

A
Aarni Koskela 已提交
324
def configure_for_tests():
325 326 327 328
    if "--api" not in sys.argv:
        sys.argv.append("--api")
    if "--ckpt" not in sys.argv:
        sys.argv.append("--ckpt")
329
        sys.argv.append(os.path.join(script_path, "test/test_files/empty.pt"))
330 331
    if "--skip-torch-cuda-test" not in sys.argv:
        sys.argv.append("--skip-torch-cuda-test")
A
AUTOMATIC 已提交
332 333
    if "--disable-nan-check" not in sys.argv:
        sys.argv.append("--disable-nan-check")
334

V
Vladimir Repin 已提交
335
    os.environ['COMMANDLINE_ARGS'] = ""
336

337

338 339
def start():
    print(f"Launching {'API server' if '--nowebui' in sys.argv else 'Web UI'} with arguments: {' '.join(sys.argv[1:])}")
340
    import webui
341 342 343 344
    if '--nowebui' in sys.argv:
        webui.api_only()
    else:
        webui.webui()