launch.py 15.1 KB
Newer Older
1 2 3 4 5 6
# this scripts installs necessary requirements and launches main program in webui.py
import subprocess
import os
import sys
import importlib.util
import shlex
C
C43H66N12O12S2 已提交
7
import platform
8 9
import argparse
import json
10 11 12 13 14 15 16

parser = argparse.ArgumentParser(add_help=False)
parser.add_argument("--ui-settings-file", type=str, default='config.json')
parser.add_argument("--data-dir", type=str, default=os.path.dirname(os.path.realpath(__file__)))
args, _ = parser.parse_known_args(sys.argv)

script_path = os.path.dirname(__file__)
17
data_path = args.data_dir
18

19
dir_repos = "repositories"
20
dir_extensions = "extensions"
21 22
python = sys.executable
git = os.environ.get('GIT', "git")
A
AUTOMATIC 已提交
23
index_url = os.environ.get('INDEX_URL', "")
A
AUTOMATIC 已提交
24
stored_commit_hash = None
25
skip_install = False
A
AUTOMATIC 已提交
26

H
high_byte 已提交
27
os.environ['GRADIO_ANALYTICS_ENABLED'] = 'False'
A
AUTOMATIC 已提交
28

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

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

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

        modules.errors.print_error_explanation(f"""
44 45 46
INCOMPATIBLE PYTHON VERSION

This program is tested with 3.10.6 Python, but you have {major}.{minor}.{micro}.
S
Spaceginner 已提交
47 48 49 50 51
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.

52 53 54 55 56
You can download 3.10 Python from here: https://www.python.org/downloads/release/python-3109/

{"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.
57
""")
S
Spaceginner 已提交
58 59


A
AUTOMATIC 已提交
60 61 62 63 64 65 66 67 68 69 70 71
def commit_hash():
    global stored_commit_hash

    if stored_commit_hash is not None:
        return stored_commit_hash

    try:
        stored_commit_hash = run(f"{git} rev-parse HEAD").strip()
    except Exception:
        stored_commit_hash = "<none>"

    return stored_commit_hash
72

73 74 75 76 77

def extract_arg(args, name):
    return [x for x in args if x != name], name in args


78 79 80 81 82 83 84 85 86 87 88 89 90
def extract_opt(args, name):
    opt = None
    is_present = False
    if name in args:
        is_present = True
        idx = args.index(name)
        del args[idx]
        if idx < len(args) and args[idx][0] != "-":
            opt = args[idx]
            del args[idx]
    return args, is_present, opt


A
AUTOMATIC 已提交
91
def run(command, desc=None, errdesc=None, custom_env=None, live=False):
92 93 94
    if desc is not None:
        print(desc)

A
AUTOMATIC 已提交
95 96 97 98 99 100 101 102 103
    if live:
        result = subprocess.run(command, shell=True, env=os.environ if custom_env is None else custom_env)
        if result.returncode != 0:
            raise RuntimeError(f"""{errdesc or 'Error running command'}.
Command: {command}
Error code: {result.returncode}""")

        return ""

104
    result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, env=os.environ if custom_env is None else custom_env)
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132

    if result.returncode != 0:

        message = f"""{errdesc or 'Error running command'}.
Command: {command}
Error code: {result.returncode}
stdout: {result.stdout.decode(encoding="utf8", errors="ignore") if len(result.stdout)>0 else '<empty>'}
stderr: {result.stderr.decode(encoding="utf8", errors="ignore") if len(result.stderr)>0 else '<empty>'}
"""
        raise RuntimeError(message)

    return result.stdout.decode(encoding="utf8", errors="ignore")


def check_run(command):
    result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
    return result.returncode == 0


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

    return spec is not None


133
def repo_dir(name):
134
    return os.path.join(script_path, dir_repos, name)
135 136 137 138 139 140 141


def run_python(code, desc=None, errdesc=None):
    return run(f'"{python}" -c "{code}"', desc, errdesc)


def run_pip(args, desc=None):
142 143 144
    if skip_install:
        return

A
AUTOMATIC 已提交
145 146
    index_url_line = f' --index-url {index_url}' if index_url != '' else ''
    return run(f'"{python}" -m pip {args} --prefer-binary{index_url_line}', desc=f"Installing {desc}", errdesc=f"Couldn't install {desc}")
147 148 149 150 151 152 153 154 155 156 157 158 159


def check_run_python(code):
    return check_run(f'"{python}" -c "{code}"')


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

160
        current_hash = run(f'"{git}" -C "{dir}" rev-parse HEAD', None, f"Couldn't determine {name}'s hash: {commithash}").strip()
161 162 163
        if current_hash == commithash:
            return

164 165
        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}")
166
        return
167

168 169 170
    run(f'"{git}" clone "{url}" "{dir}"', f"Cloning {name} into {dir}...", f"Couldn't clone {name}")

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

173

174 175 176 177
def git_pull_recursive(dir):
    for subdir, _, _ in os.walk(dir):
        if os.path.exists(os.path.join(subdir, '.git')):
            try:
178
                output = subprocess.check_output([git, '-C', subdir, 'pull', '--autostash'])
179 180 181 182
                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")

183

184 185 186 187 188 189 190 191 192 193 194 195 196 197
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:
198 199 200
        print("version check failed", e)


201 202 203 204 205 206 207 208 209 210 211 212 213 214
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:
        print(e, file=sys.stderr)


215 216 217 218 219 220 221 222 223 224 225 226
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)
    except Exception as e:
        print(e, file=sys.stderr)

    disabled_extensions = set(settings.get('disabled_extensions', []))

227
    return [x for x in os.listdir(os.path.join(data_path, dir_extensions)) if x not in disabled_extensions]
228 229 230


def run_extensions_installers(settings_file):
231 232 233
    if not os.path.isdir(dir_extensions):
        return

234
    for dirname_extension in list_extensions(settings_file):
235
        run_extension_installer(os.path.join(data_path, dir_extensions, dirname_extension))
236

237

H
Hsiang-Cheng Yang 已提交
238
def prepare_environment():
239 240
    global skip_install

241
    torch_command = os.environ.get('TORCH_COMMAND', "pip install torch==1.13.1+cu117 torchvision==0.14.1+cu117 --extra-index-url https://download.pytorch.org/whl/cu117")
242 243
    requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt")
    commandline_args = os.environ.get('COMMANDLINE_ARGS', "")
244

245
    xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.16rc425')
246 247
    gfpgan_package = os.environ.get('GFPGAN_PACKAGE', "git+https://github.com/TencentARC/GFPGAN.git@8d2447a2d918f8eba5a4a01463fd48e45126a379")
    clip_package = os.environ.get('CLIP_PACKAGE', "git+https://github.com/openai/CLIP.git@d50d76daa670286dd6cacf3bcd80b5e4823fc8e1")
A
AUTOMATIC 已提交
248
    openclip_package = os.environ.get('OPENCLIP_PACKAGE', "git+https://github.com/mlfoundations/open_clip.git@bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b")
249

A
AUTOMATIC 已提交
250
    stable_diffusion_repo = os.environ.get('STABLE_DIFFUSION_REPO', "https://github.com/Stability-AI/stablediffusion.git")
A
Anas Abou Allaban 已提交
251
    taming_transformers_repo = os.environ.get('TAMING_TRANSFORMERS_REPO', "https://github.com/CompVis/taming-transformers.git")
252
    k_diffusion_repo = os.environ.get('K_DIFFUSION_REPO', 'https://github.com/crowsonkb/k-diffusion.git')
A
Anas Abou Allaban 已提交
253
    codeformer_repo = os.environ.get('CODEFORMER_REPO', 'https://github.com/sczhou/CodeFormer.git')
254
    blip_repo = os.environ.get('BLIP_REPO', 'https://github.com/salesforce/BLIP.git')
255

A
AUTOMATIC 已提交
256
    stable_diffusion_commit_hash = os.environ.get('STABLE_DIFFUSION_COMMIT_HASH', "47b6b607fdd31875c9279cd2f4f16b92e4ea958e")
257
    taming_transformers_commit_hash = os.environ.get('TAMING_TRANSFORMERS_COMMIT_HASH', "24268930bf1dce879235a7fddd0b2355b84d7ea6")
A
AUTOMATIC 已提交
258
    k_diffusion_commit_hash = os.environ.get('K_DIFFUSION_COMMIT_HASH', "5b3af030dd83e0297272d861c19477735d0317ec")
259 260 261
    codeformer_commit_hash = os.environ.get('CODEFORMER_COMMIT_HASH', "c5b4593074ba6214284d6acd5f1719b6c5d739af")
    blip_commit_hash = os.environ.get('BLIP_COMMIT_HASH', "48211a1594f1321b00f14c9f7a5b4813144b2fb9")

262
    sys.argv += shlex.split(commandline_args)
263

B
brkirch 已提交
264
    sys.argv, _ = extract_arg(sys.argv, '-f')
265
    sys.argv, update_all_extensions = extract_arg(sys.argv, '--update-all-extensions')
266
    sys.argv, skip_torch_cuda_test = extract_arg(sys.argv, '--skip-torch-cuda-test')
267
    sys.argv, skip_python_version_check = extract_arg(sys.argv, '--skip-python-version-check')
268
    sys.argv, reinstall_xformers = extract_arg(sys.argv, '--reinstall-xformers')
269
    sys.argv, reinstall_torch = extract_arg(sys.argv, '--reinstall-torch')
270
    sys.argv, update_check = extract_arg(sys.argv, '--update-check')
271
    sys.argv, run_tests, test_dir = extract_opt(sys.argv, '--tests')
272
    sys.argv, skip_install = extract_arg(sys.argv, '--skip-install')
273 274
    xformers = '--xformers' in sys.argv
    ngrok = '--ngrok' in sys.argv
275

276 277 278
    if not skip_python_version_check:
        check_python_version()

A
AUTOMATIC 已提交
279
    commit = commit_hash()
280

281 282
    print(f"Python {sys.version}")
    print(f"Commit hash: {commit}")
A
AUTOMATIC 已提交
283

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

287
    if not skip_torch_cuda_test:
288
        run_python("import torch; assert torch.cuda.is_available(), 'Torch is not able to use GPU; add --skip-torch-cuda-test to COMMANDLINE_ARGS variable to disable this check'")
289

290
    if not is_installed("gfpgan"):
C
Cassy-Lee 已提交
291
        run_pip(f"install {gfpgan_package}", "gfpgan")
292

293
    if not is_installed("clip"):
C
Cassy-Lee 已提交
294
        run_pip(f"install {clip_package}", "clip")
C
C43H66N12O12S2 已提交
295

A
AUTOMATIC 已提交
296 297 298
    if not is_installed("open_clip"):
        run_pip(f"install {openclip_package}", "open_clip")

ふぁ 已提交
299
    if (not is_installed("xformers") or reinstall_xformers) and xformers:
300
        if platform.system() == "Windows":
ふぁ 已提交
301
            if platform.python_version().startswith("3.10"):
302
                run_pip(f"install -U -I --no-deps {xformers_package}", "xformers")
ふぁ 已提交
303 304 305 306 307
            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)
308
        elif platform.system() == "Linux":
A
AUTOMATIC 已提交
309
            run_pip(f"install {xformers_package}", "xformers")
310

J
JamnedZ 已提交
311
    if not is_installed("pyngrok") and ngrok:
C
Cassy-Lee 已提交
312
        run_pip("install pyngrok", "ngrok")
J
JamnedZ 已提交
313

314
    os.makedirs(os.path.join(script_path, dir_repos), exist_ok=True)
315

A
AUTOMATIC 已提交
316
    git_clone(stable_diffusion_repo, repo_dir('stable-diffusion-stability-ai'), "Stable Diffusion", stable_diffusion_commit_hash)
317 318 319 320
    git_clone(taming_transformers_repo, repo_dir('taming-transformers'), "Taming Transformers", taming_transformers_commit_hash)
    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)
321

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

325 326 327
    if not os.path.isfile(requirements_file):
        requirements_file = os.path.join(script_path, requirements_file)
    run_pip(f"install -r \"{requirements_file}\"", "requirements for Web UI")
328

329
    run_extensions_installers(settings_file=args.ui_settings_file)
330

331
    if update_check:
332
        version_check(commit)
333 334

    if update_all_extensions:
335
        git_pull_recursive(os.path.join(data_path, dir_extensions))
336
    
337
    if "--exit" in sys.argv:
338 339
        print("Exiting because of --exit argument")
        exit(0)
340

341
    if run_tests:
342 343
        exitcode = tests(test_dir)
        exit(exitcode)
344 345


346 347 348 349 350
def tests(test_dir):
    if "--api" not in sys.argv:
        sys.argv.append("--api")
    if "--ckpt" not in sys.argv:
        sys.argv.append("--ckpt")
351
        sys.argv.append(os.path.join(script_path, "test/test_files/empty.pt"))
352 353
    if "--skip-torch-cuda-test" not in sys.argv:
        sys.argv.append("--skip-torch-cuda-test")
A
AUTOMATIC 已提交
354 355
    if "--disable-nan-check" not in sys.argv:
        sys.argv.append("--disable-nan-check")
A
AUTOMATIC 已提交
356

357
    print(f"Launching Web UI in another process for testing with arguments: {' '.join(sys.argv[1:])}")
358

V
Vladimir Repin 已提交
359
    os.environ['COMMANDLINE_ARGS'] = ""
360
    with open(os.path.join(script_path, 'test/stdout.txt'), "w", encoding="utf8") as stdout, open(os.path.join(script_path, 'test/stderr.txt'), "w", encoding="utf8") as stderr:
361
        proc = subprocess.Popen([sys.executable, *sys.argv], stdout=stdout, stderr=stderr)
362 363

    import test.server_poll
364
    exitcode = test.server_poll.run_tests(proc, test_dir)
365 366 367

    print(f"Stopping Web UI process with id {proc.pid}")
    proc.kill()
368
    return exitcode
369

370

371 372
def start():
    print(f"Launching {'API server' if '--nowebui' in sys.argv else 'Web UI'} with arguments: {' '.join(sys.argv[1:])}")
373
    import webui
374 375 376 377
    if '--nowebui' in sys.argv:
        webui.api_only()
    else:
        webui.webui()
378

379

380
if __name__ == "__main__":
H
Hsiang-Cheng Yang 已提交
381
    prepare_environment()
382
    start()