Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ninjaneural
GitHub Repository: ninjaneural/webui
Path: blob/master/misc/direct/v1.7.0/launch_utils.py
3275 views
1
# this scripts installs necessary requirements and launches main program in webui.py
2
import logging
3
import re
4
import subprocess
5
import os
6
import shutil
7
import sys
8
import importlib.util
9
import importlib.metadata
10
import platform
11
import json
12
from functools import lru_cache
13
14
from modules import cmd_args, errors
15
from modules.paths_internal import script_path, extensions_dir
16
from modules.timer import startup_timer
17
from modules import logging_config
18
19
args, _ = cmd_args.parser.parse_known_args()
20
logging_config.setup_logging(args.loglevel)
21
22
python = sys.executable
23
git = os.environ.get("GIT", "git")
24
index_url = os.environ.get("INDEX_URL", "")
25
dir_repos = "repositories"
26
27
# Whether to default to printing command output
28
default_command_live = os.environ.get("WEBUI_LAUNCH_LIVE_OUTPUT") == "1"
29
30
if "GRADIO_ANALYTICS_ENABLED" not in os.environ:
31
os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
32
33
34
def check_python_version():
35
is_windows = platform.system() == "Windows"
36
major = sys.version_info.major
37
minor = sys.version_info.minor
38
micro = sys.version_info.micro
39
40
if is_windows:
41
supported_minors = [10]
42
else:
43
supported_minors = [7, 8, 9, 10, 11]
44
45
if not (major == 3 and minor in supported_minors):
46
import modules.errors
47
48
modules.errors.print_error_explanation(
49
f"""
50
INCOMPATIBLE PYTHON VERSION
51
52
This program is tested with 3.10.6 Python, but you have {major}.{minor}.{micro}.
53
If you encounter an error with "RuntimeError: Couldn't install torch." message,
54
or any other error regarding unsuccessful package (library) installation,
55
please downgrade (or upgrade) to the latest version of 3.10 Python
56
and delete current Python and "venv" folder in WebUI's directory.
57
58
You can download 3.10 Python from here: https://www.python.org/downloads/release/python-3106/
59
60
{"Alternatively, use a binary release of WebUI: https://github.com/AUTOMATIC1111/stable-diffusion-webui/releases" if is_windows else ""}
61
62
Use --skip-python-version-check to suppress this warning.
63
"""
64
)
65
66
67
@lru_cache()
68
def commit_hash():
69
try:
70
return subprocess.check_output(
71
[git, "-C", script_path, "rev-parse", "HEAD"], shell=False, encoding="utf8"
72
).strip()
73
except Exception:
74
return "<none>"
75
76
77
@lru_cache()
78
def git_tag():
79
try:
80
return subprocess.check_output(
81
[git, "-C", script_path, "describe", "--tags"], shell=False, encoding="utf8"
82
).strip()
83
except Exception:
84
try:
85
changelog_md = os.path.join(
86
os.path.dirname(os.path.dirname(__file__)), "CHANGELOG.md"
87
)
88
with open(changelog_md, "r", encoding="utf-8") as file:
89
line = next((line.strip() for line in file if line.strip()), "<none>")
90
line = line.replace("## ", "")
91
return line
92
except Exception:
93
return "<none>"
94
95
96
def run(
97
command, desc=None, errdesc=None, custom_env=None, live: bool = default_command_live
98
) -> str:
99
if desc is not None:
100
print(desc)
101
102
run_kwargs = {
103
"args": command,
104
"shell": True,
105
"env": os.environ if custom_env is None else custom_env,
106
"encoding": "utf8",
107
"errors": "ignore",
108
}
109
110
if not live:
111
run_kwargs["stdout"] = run_kwargs["stderr"] = subprocess.PIPE
112
113
result = subprocess.run(**run_kwargs)
114
115
if result.returncode != 0:
116
error_bits = [
117
f"{errdesc or 'Error running command'}.",
118
f"Command: {command}",
119
f"Error code: {result.returncode}",
120
]
121
if result.stdout:
122
error_bits.append(f"stdout: {result.stdout}")
123
if result.stderr:
124
error_bits.append(f"stderr: {result.stderr}")
125
raise RuntimeError("\n".join(error_bits))
126
127
return result.stdout or ""
128
129
130
def is_installed(package):
131
try:
132
dist = importlib.metadata.distribution(package)
133
except importlib.metadata.PackageNotFoundError:
134
try:
135
spec = importlib.util.find_spec(package)
136
except ModuleNotFoundError:
137
return False
138
139
return spec is not None
140
141
return dist is not None
142
143
144
def repo_dir(name):
145
return os.path.join(script_path, dir_repos, name)
146
147
148
def run_pip(command, desc=None, live=default_command_live):
149
if args.skip_install:
150
return
151
152
index_url_line = f" --index-url {index_url}" if index_url != "" else ""
153
return run(
154
f'"{python}" -m pip {command} --prefer-binary{index_url_line}',
155
desc=f"Installing {desc}",
156
errdesc=f"Couldn't install {desc}",
157
live=live,
158
)
159
160
161
def check_run_python(code: str) -> bool:
162
result = subprocess.run([python, "-c", code], capture_output=True, shell=False)
163
return result.returncode == 0
164
165
166
def git_fix_workspace(dir, name):
167
run(
168
f'"{git}" -C "{dir}" fetch --refetch --no-auto-gc',
169
f"Fetching all contents for {name}",
170
f"Couldn't fetch {name}",
171
live=True,
172
)
173
run(
174
f'"{git}" -C "{dir}" gc --aggressive --prune=now',
175
f"Pruning {name}",
176
f"Couldn't prune {name}",
177
live=True,
178
)
179
return
180
181
182
def run_git(
183
dir,
184
name,
185
command,
186
desc=None,
187
errdesc=None,
188
custom_env=None,
189
live: bool = default_command_live,
190
autofix=True,
191
):
192
try:
193
return run(
194
f'"{git}" -C "{dir}" {command}',
195
desc=desc,
196
errdesc=errdesc,
197
custom_env=custom_env,
198
live=live,
199
)
200
except RuntimeError:
201
if not autofix:
202
raise
203
204
print(f"{errdesc}, attempting autofix...")
205
git_fix_workspace(dir, name)
206
207
return run(
208
f'"{git}" -C "{dir}" {command}',
209
desc=desc,
210
errdesc=errdesc,
211
custom_env=custom_env,
212
live=live,
213
)
214
215
216
def git_clone(url, dir, name, commithash=None):
217
# TODO clone into temporary dir and move if successful
218
219
if os.path.exists(dir):
220
if commithash is None:
221
return
222
223
current_hash = run_git(
224
dir,
225
name,
226
"rev-parse HEAD",
227
None,
228
f"Couldn't determine {name}'s hash: {commithash}",
229
live=False,
230
).strip()
231
if current_hash == commithash:
232
return
233
234
if (
235
run_git(
236
dir,
237
name,
238
"config --get remote.origin.url",
239
None,
240
f"Couldn't determine {name}'s origin URL",
241
live=False,
242
).strip()
243
!= url
244
):
245
run_git(
246
dir,
247
name,
248
f'remote set-url origin "{url}"',
249
None,
250
f"Failed to set {name}'s origin URL",
251
live=False,
252
)
253
254
run_git(
255
dir,
256
name,
257
"fetch",
258
f"Fetching updates for {name}...",
259
f"Couldn't fetch {name}",
260
autofix=False,
261
)
262
263
run_git(
264
dir,
265
name,
266
f"reset --hard {commithash}",
267
f"Checking out commit for {name} with hash: {commithash}...",
268
f"Couldn't checkout commit {commithash} for {name}",
269
live=True,
270
)
271
272
return
273
274
try:
275
run(
276
f'"{git}" clone "{url}" "{dir}"',
277
f"Cloning {name} into {dir}...",
278
f"Couldn't clone {name}",
279
live=True,
280
)
281
except RuntimeError:
282
shutil.rmtree(dir, ignore_errors=True)
283
raise
284
285
if commithash is not None:
286
run(
287
f'"{git}" -C "{dir}" reset --hard {commithash}',
288
None,
289
"Couldn't checkout {name}'s hash: {commithash}",
290
)
291
292
293
def git_pull_recursive(dir):
294
for subdir, _, _ in os.walk(dir):
295
if os.path.exists(os.path.join(subdir, ".git")):
296
try:
297
output = subprocess.check_output(
298
[git, "-C", subdir, "pull", "--autostash"]
299
)
300
print(
301
f"Pulled changes for repository in '{subdir}':\n{output.decode('utf-8').strip()}\n"
302
)
303
except subprocess.CalledProcessError as e:
304
print(
305
f"Couldn't perform 'git pull' on repository in '{subdir}':\n{e.output.decode('utf-8').strip()}\n"
306
)
307
308
309
def version_check(commit):
310
try:
311
import requests
312
313
commits = requests.get(
314
"https://api.github.com/repos/AUTOMATIC1111/stable-diffusion-webui/branches/master"
315
).json()
316
if commit != "<none>" and commits["commit"]["sha"] != commit:
317
print("--------------------------------------------------------")
318
print("| You are not up to date with the most recent release. |")
319
print("| Consider running `git pull` to update. |")
320
print("--------------------------------------------------------")
321
elif commits["commit"]["sha"] == commit:
322
print("You are up to date with the most recent release.")
323
else:
324
print("Not a git clone, can't perform version check.")
325
except Exception as e:
326
print("version check failed", e)
327
328
329
def run_extension_installer(extension_dir):
330
path_installer = os.path.join(extension_dir, "install.py")
331
if not os.path.isfile(path_installer):
332
return
333
334
try:
335
env = os.environ.copy()
336
env[
337
"PYTHONPATH"
338
] = f"{os.path.abspath('.')}{os.pathsep}{env.get('PYTHONPATH', '')}"
339
340
stdout = run(
341
f'"{python}" "{path_installer}"',
342
errdesc=f"Error running install.py for extension {extension_dir}",
343
custom_env=env,
344
).strip()
345
if stdout:
346
print(stdout)
347
except Exception as e:
348
errors.report(str(e))
349
350
351
def list_extensions(settings_file):
352
settings = {}
353
354
try:
355
if os.path.isfile(settings_file):
356
with open(settings_file, "r", encoding="utf8") as file:
357
settings = json.load(file)
358
except Exception:
359
errors.report("Could not load settings", exc_info=True)
360
361
disabled_extensions = set(settings.get("disabled_extensions", []))
362
disable_all_extensions = settings.get("disable_all_extensions", "none")
363
364
if (
365
disable_all_extensions != "none"
366
or args.disable_extra_extensions
367
or args.disable_all_extensions
368
or not os.path.isdir(extensions_dir)
369
):
370
return []
371
372
return [x for x in os.listdir(extensions_dir) if x not in disabled_extensions]
373
374
375
def run_extensions_installers(settings_file):
376
if not os.path.isdir(extensions_dir):
377
return
378
379
with startup_timer.subcategory("run extensions installers"):
380
for dirname_extension in list_extensions(settings_file):
381
logging.debug(f"Installing {dirname_extension}")
382
383
path = os.path.join(extensions_dir, dirname_extension)
384
385
if os.path.isdir(path):
386
run_extension_installer(path)
387
startup_timer.record(dirname_extension)
388
389
390
re_requirement = re.compile(r"\s*([-_a-zA-Z0-9]+)\s*(?:==\s*([-+_.a-zA-Z0-9]+))?\s*")
391
392
393
def requirements_met(requirements_file):
394
"""
395
Does a simple parse of a requirements.txt file to determine if all rerqirements in it
396
are already installed. Returns True if so, False if not installed or parsing fails.
397
"""
398
399
import importlib.metadata
400
import packaging.version
401
402
with open(requirements_file, "r", encoding="utf8") as file:
403
for line in file:
404
if line.strip() == "":
405
continue
406
407
m = re.match(re_requirement, line)
408
if m is None:
409
return False
410
411
package = m.group(1).strip()
412
version_required = (m.group(2) or "").strip()
413
414
if version_required == "":
415
continue
416
417
try:
418
version_installed = importlib.metadata.version(package)
419
except Exception:
420
return False
421
422
if packaging.version.parse(version_required) != packaging.version.parse(
423
version_installed
424
):
425
return False
426
427
return True
428
429
430
def prepare_environment():
431
torch_index_url = os.environ.get(
432
"TORCH_INDEX_URL", "https://download.pytorch.org/whl/cu118"
433
)
434
torch_command = os.environ.get(
435
"TORCH_COMMAND",
436
f"pip install torch==2.0.1 torchvision==0.15.2 --extra-index-url {torch_index_url}",
437
)
438
if args.use_ipex:
439
if platform.system() == "Windows":
440
# 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-main
441
# This is NOT an Intel official release so please use it at your own risk!!
442
# See https://github.com/Nuullll/intel-extension-for-pytorch/releases/tag/v2.0.110%2Bxpu-master%2Bdll-bundle for details.
443
#
444
# Strengths (over official IPEX 2.0.110 windows release):
445
# - AOT build (for Arc GPU only) to eliminate JIT compilation overhead: https://github.com/intel/intel-extension-for-pytorch/issues/399
446
# - Bundles minimal oneAPI 2023.2 dependencies into the python wheels, so users don't need to install oneAPI for the whole system.
447
# - Provides a compatible torchvision wheel: https://github.com/intel/intel-extension-for-pytorch/issues/465
448
# Limitation:
449
# - Only works for python 3.10
450
url_prefix = "https://github.com/Nuullll/intel-extension-for-pytorch/releases/download/v2.0.110%2Bxpu-master%2Bdll-bundle"
451
torch_command = os.environ.get(
452
"TORCH_COMMAND",
453
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",
454
)
455
else:
456
# Using official IPEX release for linux since it's already an AOT build.
457
# However, users still have to install oneAPI toolkit and activate oneAPI environment manually.
458
# See https://intel.github.io/intel-extension-for-pytorch/index.html#installation for details.
459
torch_index_url = os.environ.get(
460
"TORCH_INDEX_URL",
461
"https://pytorch-extension.intel.com/release-whl/stable/xpu/us/",
462
)
463
torch_command = os.environ.get(
464
"TORCH_COMMAND",
465
f"pip install torch==2.0.0a0 intel-extension-for-pytorch==2.0.110+gitba7f6c1 --extra-index-url {torch_index_url}",
466
)
467
requirements_file = os.environ.get("REQS_FILE", "requirements_versions.txt")
468
469
xformers_package = os.environ.get("XFORMERS_PACKAGE", "xformers==0.0.20")
470
clip_package = os.environ.get(
471
"CLIP_PACKAGE",
472
"https://github.com/openai/CLIP/archive/d50d76daa670286dd6cacf3bcd80b5e4823fc8e1.zip",
473
)
474
openclip_package = os.environ.get(
475
"OPENCLIP_PACKAGE",
476
"https://github.com/mlfoundations/open_clip/archive/bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b.zip",
477
)
478
479
stable_diffusion_repo = os.environ.get(
480
"STABLE_DIFFUSION_REPO", "https://github.com/Stability-AI/stablediffusion.git"
481
)
482
stable_diffusion_xl_repo = os.environ.get(
483
"STABLE_DIFFUSION_XL_REPO",
484
"https://github.com/Stability-AI/generative-models.git",
485
)
486
k_diffusion_repo = os.environ.get(
487
"K_DIFFUSION_REPO", "https://github.com/crowsonkb/k-diffusion.git"
488
)
489
codeformer_repo = os.environ.get(
490
"CODEFORMER_REPO", "https://github.com/sczhou/CodeFormer.git"
491
)
492
blip_repo = os.environ.get("BLIP_REPO", "https://github.com/salesforce/BLIP.git")
493
494
stable_diffusion_commit_hash = os.environ.get(
495
"STABLE_DIFFUSION_COMMIT_HASH", "cf1d67a6fd5ea1aa600c4df58e5b47da45f6bdbf"
496
)
497
stable_diffusion_xl_commit_hash = os.environ.get(
498
"STABLE_DIFFUSION_XL_COMMIT_HASH", "45c443b316737a4ab6e40413d7794a7f5657c19f"
499
)
500
k_diffusion_commit_hash = os.environ.get(
501
"K_DIFFUSION_COMMIT_HASH", "ab527a9a6d347f364e3d185ba6d714e22d80cb3c"
502
)
503
codeformer_commit_hash = os.environ.get(
504
"CODEFORMER_COMMIT_HASH", "c5b4593074ba6214284d6acd5f1719b6c5d739af"
505
)
506
blip_commit_hash = os.environ.get(
507
"BLIP_COMMIT_HASH", "48211a1594f1321b00f14c9f7a5b4813144b2fb9"
508
)
509
510
try:
511
# the existence of this file is a signal to webui.sh/bat that webui needs to be restarted when it stops execution
512
os.remove(os.path.join(script_path, "tmp", "restart"))
513
os.environ.setdefault("SD_WEBUI_RESTARTING", "1")
514
except OSError:
515
pass
516
517
if not args.skip_python_version_check:
518
check_python_version()
519
520
startup_timer.record("checks")
521
522
commit = commit_hash()
523
tag = git_tag()
524
startup_timer.record("git version info")
525
526
print(f"Python {sys.version}")
527
print(f"Version: {tag}")
528
print(f"Commit hash: {commit}")
529
530
if (
531
args.reinstall_torch
532
or not is_installed("torch")
533
or not is_installed("torchvision")
534
):
535
run(
536
f'"{python}" -m {torch_command}',
537
"Installing torch and torchvision",
538
"Couldn't install torch",
539
live=True,
540
)
541
startup_timer.record("install torch")
542
543
if args.use_ipex:
544
args.skip_torch_cuda_test = True
545
if not args.skip_torch_cuda_test and not check_run_python(
546
"import torch; assert torch.cuda.is_available()"
547
):
548
raise RuntimeError(
549
"Torch is not able to use GPU; "
550
"add --skip-torch-cuda-test to COMMANDLINE_ARGS variable to disable this check"
551
)
552
startup_timer.record("torch GPU test")
553
554
if not is_installed("clip"):
555
run_pip(f"install {clip_package}", "clip")
556
startup_timer.record("install clip")
557
558
if not is_installed("open_clip"):
559
run_pip(f"install {openclip_package}", "open_clip")
560
startup_timer.record("install open_clip")
561
562
if (not is_installed("xformers") or args.reinstall_xformers) and args.xformers:
563
run_pip(f"install -U -I --no-deps {xformers_package}", "xformers")
564
startup_timer.record("install xformers")
565
566
if not is_installed("ngrok") and args.ngrok:
567
run_pip("install ngrok", "ngrok")
568
startup_timer.record("install ngrok")
569
570
os.makedirs(os.path.join(script_path, dir_repos), exist_ok=True)
571
572
git_clone(
573
stable_diffusion_repo,
574
repo_dir("stable-diffusion-stability-ai"),
575
"Stable Diffusion",
576
stable_diffusion_commit_hash,
577
)
578
git_clone(
579
stable_diffusion_xl_repo,
580
repo_dir("generative-models"),
581
"Stable Diffusion XL",
582
stable_diffusion_xl_commit_hash,
583
)
584
git_clone(
585
k_diffusion_repo,
586
repo_dir("k-diffusion"),
587
"K-diffusion",
588
k_diffusion_commit_hash,
589
)
590
git_clone(
591
codeformer_repo, repo_dir("CodeFormer"), "CodeFormer", codeformer_commit_hash
592
)
593
git_clone(blip_repo, repo_dir("BLIP"), "BLIP", blip_commit_hash)
594
595
startup_timer.record("clone repositores")
596
597
if not is_installed("lpips"):
598
run_pip(
599
f"install -r \"{os.path.join(repo_dir('CodeFormer'), 'requirements.txt')}\"",
600
"requirements for CodeFormer",
601
)
602
startup_timer.record("install CodeFormer requirements")
603
604
if not os.path.isfile(requirements_file):
605
requirements_file = os.path.join(script_path, requirements_file)
606
607
if not requirements_met(requirements_file):
608
run_pip(f'install -r "{requirements_file}"', "requirements")
609
startup_timer.record("install requirements")
610
611
if not args.skip_install:
612
run_extensions_installers(settings_file=args.ui_settings_file)
613
614
if args.update_check:
615
version_check(commit)
616
startup_timer.record("check version")
617
618
if args.update_all_extensions:
619
git_pull_recursive(extensions_dir)
620
startup_timer.record("update extensions")
621
622
if "--exit" in sys.argv:
623
print("Exiting because of --exit argument")
624
exit(0)
625
626
627
def configure_for_tests():
628
if "--api" not in sys.argv:
629
sys.argv.append("--api")
630
if "--ckpt" not in sys.argv:
631
sys.argv.append("--ckpt")
632
sys.argv.append(os.path.join(script_path, "test/test_files/empty.pt"))
633
if "--skip-torch-cuda-test" not in sys.argv:
634
sys.argv.append("--skip-torch-cuda-test")
635
if "--disable-nan-check" not in sys.argv:
636
sys.argv.append("--disable-nan-check")
637
638
os.environ["COMMANDLINE_ARGS"] = ""
639
640
641
def start():
642
print(
643
f"Launching {'API server' if '--nowebui' in sys.argv else 'Web UI'} with arguments: {' '.join(sys.argv[1:])}"
644
)
645
import webui
646
647
if "--nowebui" in sys.argv:
648
webui.api_only()
649
else:
650
webui.webui()
651
652
653
def dump_sysinfo():
654
from modules import sysinfo
655
import datetime
656
657
text = sysinfo.get()
658
filename = f"sysinfo-{datetime.datetime.utcnow().strftime('%Y-%m-%d-%H-%M')}.json"
659
660
with open(filename, "w", encoding="utf8") as file:
661
file.write(text)
662
663
return filename
664
665