Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ninjaneural
GitHub Repository: ninjaneural/webui
Path: blob/master/misc/direct/v1.7.0/launch_utils_org.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(f"""
49
INCOMPATIBLE PYTHON VERSION
50
51
This program is tested with 3.10.6 Python, but you have {major}.{minor}.{micro}.
52
If you encounter an error with "RuntimeError: Couldn't install torch." message,
53
or any other error regarding unsuccessful package (library) installation,
54
please downgrade (or upgrade) to the latest version of 3.10 Python
55
and delete current Python and "venv" folder in WebUI's directory.
56
57
You can download 3.10 Python from here: https://www.python.org/downloads/release/python-3106/
58
59
{"Alternatively, use a binary release of WebUI: https://github.com/AUTOMATIC1111/stable-diffusion-webui/releases" if is_windows else ""}
60
61
Use --skip-python-version-check to suppress this warning.
62
""")
63
64
65
@lru_cache()
66
def commit_hash():
67
try:
68
return subprocess.check_output([git, "-C", script_path, "rev-parse", "HEAD"], shell=False, encoding='utf8').strip()
69
except Exception:
70
return "<none>"
71
72
73
@lru_cache()
74
def git_tag():
75
try:
76
return subprocess.check_output([git, "-C", script_path, "describe", "--tags"], shell=False, encoding='utf8').strip()
77
except Exception:
78
try:
79
80
changelog_md = os.path.join(os.path.dirname(os.path.dirname(__file__)), "CHANGELOG.md")
81
with open(changelog_md, "r", encoding="utf-8") as file:
82
line = next((line.strip() for line in file if line.strip()), "<none>")
83
line = line.replace("## ", "")
84
return line
85
except Exception:
86
return "<none>"
87
88
89
def run(command, desc=None, errdesc=None, custom_env=None, live: bool = default_command_live) -> str:
90
if desc is not None:
91
print(desc)
92
93
run_kwargs = {
94
"args": command,
95
"shell": True,
96
"env": os.environ if custom_env is None else custom_env,
97
"encoding": 'utf8',
98
"errors": 'ignore',
99
}
100
101
if not live:
102
run_kwargs["stdout"] = run_kwargs["stderr"] = subprocess.PIPE
103
104
result = subprocess.run(**run_kwargs)
105
106
if result.returncode != 0:
107
error_bits = [
108
f"{errdesc or 'Error running command'}.",
109
f"Command: {command}",
110
f"Error code: {result.returncode}",
111
]
112
if result.stdout:
113
error_bits.append(f"stdout: {result.stdout}")
114
if result.stderr:
115
error_bits.append(f"stderr: {result.stderr}")
116
raise RuntimeError("\n".join(error_bits))
117
118
return (result.stdout or "")
119
120
121
def is_installed(package):
122
try:
123
dist = importlib.metadata.distribution(package)
124
except importlib.metadata.PackageNotFoundError:
125
try:
126
spec = importlib.util.find_spec(package)
127
except ModuleNotFoundError:
128
return False
129
130
return spec is not None
131
132
return dist is not None
133
134
135
def repo_dir(name):
136
return os.path.join(script_path, dir_repos, name)
137
138
139
def run_pip(command, desc=None, live=default_command_live):
140
if args.skip_install:
141
return
142
143
index_url_line = f' --index-url {index_url}' if index_url != '' else ''
144
return run(f'"{python}" -m pip {command} --prefer-binary{index_url_line}', desc=f"Installing {desc}", errdesc=f"Couldn't install {desc}", live=live)
145
146
147
def check_run_python(code: str) -> bool:
148
result = subprocess.run([python, "-c", code], capture_output=True, shell=False)
149
return result.returncode == 0
150
151
152
def git_fix_workspace(dir, name):
153
run(f'"{git}" -C "{dir}" fetch --refetch --no-auto-gc', f"Fetching all contents for {name}", f"Couldn't fetch {name}", live=True)
154
run(f'"{git}" -C "{dir}" gc --aggressive --prune=now', f"Pruning {name}", f"Couldn't prune {name}", live=True)
155
return
156
157
158
def run_git(dir, name, command, desc=None, errdesc=None, custom_env=None, live: bool = default_command_live, autofix=True):
159
try:
160
return run(f'"{git}" -C "{dir}" {command}', desc=desc, errdesc=errdesc, custom_env=custom_env, live=live)
161
except RuntimeError:
162
if not autofix:
163
raise
164
165
print(f"{errdesc}, attempting autofix...")
166
git_fix_workspace(dir, name)
167
168
return run(f'"{git}" -C "{dir}" {command}', desc=desc, errdesc=errdesc, custom_env=custom_env, live=live)
169
170
171
def git_clone(url, dir, name, commithash=None):
172
# TODO clone into temporary dir and move if successful
173
174
if os.path.exists(dir):
175
if commithash is None:
176
return
177
178
current_hash = run_git(dir, name, 'rev-parse HEAD', None, f"Couldn't determine {name}'s hash: {commithash}", live=False).strip()
179
if current_hash == commithash:
180
return
181
182
if run_git(dir, name, 'config --get remote.origin.url', None, f"Couldn't determine {name}'s origin URL", live=False).strip() != url:
183
run_git(dir, name, f'remote set-url origin "{url}"', None, f"Failed to set {name}'s origin URL", live=False)
184
185
run_git(dir, name, 'fetch', f"Fetching updates for {name}...", f"Couldn't fetch {name}", autofix=False)
186
187
run_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)
188
189
return
190
191
try:
192
run(f'"{git}" clone "{url}" "{dir}"', f"Cloning {name} into {dir}...", f"Couldn't clone {name}", live=True)
193
except RuntimeError:
194
shutil.rmtree(dir, ignore_errors=True)
195
raise
196
197
if commithash is not None:
198
run(f'"{git}" -C "{dir}" checkout {commithash}', None, "Couldn't checkout {name}'s hash: {commithash}")
199
200
201
def git_pull_recursive(dir):
202
for subdir, _, _ in os.walk(dir):
203
if os.path.exists(os.path.join(subdir, '.git')):
204
try:
205
output = subprocess.check_output([git, '-C', subdir, 'pull', '--autostash'])
206
print(f"Pulled changes for repository in '{subdir}':\n{output.decode('utf-8').strip()}\n")
207
except subprocess.CalledProcessError as e:
208
print(f"Couldn't perform 'git pull' on repository in '{subdir}':\n{e.output.decode('utf-8').strip()}\n")
209
210
211
def version_check(commit):
212
try:
213
import requests
214
commits = requests.get('https://api.github.com/repos/AUTOMATIC1111/stable-diffusion-webui/branches/master').json()
215
if commit != "<none>" and commits['commit']['sha'] != commit:
216
print("--------------------------------------------------------")
217
print("| You are not up to date with the most recent release. |")
218
print("| Consider running `git pull` to update. |")
219
print("--------------------------------------------------------")
220
elif commits['commit']['sha'] == commit:
221
print("You are up to date with the most recent release.")
222
else:
223
print("Not a git clone, can't perform version check.")
224
except Exception as e:
225
print("version check failed", e)
226
227
228
def run_extension_installer(extension_dir):
229
path_installer = os.path.join(extension_dir, "install.py")
230
if not os.path.isfile(path_installer):
231
return
232
233
try:
234
env = os.environ.copy()
235
env['PYTHONPATH'] = f"{os.path.abspath('.')}{os.pathsep}{env.get('PYTHONPATH', '')}"
236
237
stdout = run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {extension_dir}", custom_env=env).strip()
238
if stdout:
239
print(stdout)
240
except Exception as e:
241
errors.report(str(e))
242
243
244
def list_extensions(settings_file):
245
settings = {}
246
247
try:
248
if os.path.isfile(settings_file):
249
with open(settings_file, "r", encoding="utf8") as file:
250
settings = json.load(file)
251
except Exception:
252
errors.report("Could not load settings", exc_info=True)
253
254
disabled_extensions = set(settings.get('disabled_extensions', []))
255
disable_all_extensions = settings.get('disable_all_extensions', 'none')
256
257
if disable_all_extensions != 'none' or args.disable_extra_extensions or args.disable_all_extensions or not os.path.isdir(extensions_dir):
258
return []
259
260
return [x for x in os.listdir(extensions_dir) if x not in disabled_extensions]
261
262
263
def run_extensions_installers(settings_file):
264
if not os.path.isdir(extensions_dir):
265
return
266
267
with startup_timer.subcategory("run extensions installers"):
268
for dirname_extension in list_extensions(settings_file):
269
logging.debug(f"Installing {dirname_extension}")
270
271
path = os.path.join(extensions_dir, dirname_extension)
272
273
if os.path.isdir(path):
274
run_extension_installer(path)
275
startup_timer.record(dirname_extension)
276
277
278
re_requirement = re.compile(r"\s*([-_a-zA-Z0-9]+)\s*(?:==\s*([-+_.a-zA-Z0-9]+))?\s*")
279
280
281
def requirements_met(requirements_file):
282
"""
283
Does a simple parse of a requirements.txt file to determine if all rerqirements in it
284
are already installed. Returns True if so, False if not installed or parsing fails.
285
"""
286
287
import importlib.metadata
288
import packaging.version
289
290
with open(requirements_file, "r", encoding="utf8") as file:
291
for line in file:
292
if line.strip() == "":
293
continue
294
295
m = re.match(re_requirement, line)
296
if m is None:
297
return False
298
299
package = m.group(1).strip()
300
version_required = (m.group(2) or "").strip()
301
302
if version_required == "":
303
continue
304
305
try:
306
version_installed = importlib.metadata.version(package)
307
except Exception:
308
return False
309
310
if packaging.version.parse(version_required) != packaging.version.parse(version_installed):
311
return False
312
313
return True
314
315
316
def prepare_environment():
317
torch_index_url = os.environ.get('TORCH_INDEX_URL', "https://download.pytorch.org/whl/cu118")
318
torch_command = os.environ.get('TORCH_COMMAND', f"pip install torch==2.0.1 torchvision==0.15.2 --extra-index-url {torch_index_url}")
319
if args.use_ipex:
320
if 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-main
322
# 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/399
327
# - 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/465
329
# Limitation:
330
# - Only works for python 3.10
331
url_prefix = "https://github.com/Nuullll/intel-extension-for-pytorch/releases/download/v2.0.110%2Bxpu-master%2Bdll-bundle"
332
torch_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")
333
else:
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.
337
torch_index_url = os.environ.get('TORCH_INDEX_URL', "https://pytorch-extension.intel.com/release-whl/stable/xpu/us/")
338
torch_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}")
339
requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt")
340
341
xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.20')
342
clip_package = os.environ.get('CLIP_PACKAGE', "https://github.com/openai/CLIP/archive/d50d76daa670286dd6cacf3bcd80b5e4823fc8e1.zip")
343
openclip_package = os.environ.get('OPENCLIP_PACKAGE', "https://github.com/mlfoundations/open_clip/archive/bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b.zip")
344
345
stable_diffusion_repo = os.environ.get('STABLE_DIFFUSION_REPO', "https://github.com/Stability-AI/stablediffusion.git")
346
stable_diffusion_xl_repo = os.environ.get('STABLE_DIFFUSION_XL_REPO', "https://github.com/Stability-AI/generative-models.git")
347
k_diffusion_repo = os.environ.get('K_DIFFUSION_REPO', 'https://github.com/crowsonkb/k-diffusion.git')
348
codeformer_repo = os.environ.get('CODEFORMER_REPO', 'https://github.com/sczhou/CodeFormer.git')
349
blip_repo = os.environ.get('BLIP_REPO', 'https://github.com/salesforce/BLIP.git')
350
351
stable_diffusion_commit_hash = os.environ.get('STABLE_DIFFUSION_COMMIT_HASH', "cf1d67a6fd5ea1aa600c4df58e5b47da45f6bdbf")
352
stable_diffusion_xl_commit_hash = os.environ.get('STABLE_DIFFUSION_XL_COMMIT_HASH', "45c443b316737a4ab6e40413d7794a7f5657c19f")
353
k_diffusion_commit_hash = os.environ.get('K_DIFFUSION_COMMIT_HASH', "ab527a9a6d347f364e3d185ba6d714e22d80cb3c")
354
codeformer_commit_hash = os.environ.get('CODEFORMER_COMMIT_HASH', "c5b4593074ba6214284d6acd5f1719b6c5d739af")
355
blip_commit_hash = os.environ.get('BLIP_COMMIT_HASH', "48211a1594f1321b00f14c9f7a5b4813144b2fb9")
356
357
try:
358
# the existence of this file is a signal to webui.sh/bat that webui needs to be restarted when it stops execution
359
os.remove(os.path.join(script_path, "tmp", "restart"))
360
os.environ.setdefault('SD_WEBUI_RESTARTING', '1')
361
except OSError:
362
pass
363
364
if not args.skip_python_version_check:
365
check_python_version()
366
367
startup_timer.record("checks")
368
369
commit = commit_hash()
370
tag = git_tag()
371
startup_timer.record("git version info")
372
373
print(f"Python {sys.version}")
374
print(f"Version: {tag}")
375
print(f"Commit hash: {commit}")
376
377
if args.reinstall_torch or not is_installed("torch") or not is_installed("torchvision"):
378
run(f'"{python}" -m {torch_command}', "Installing torch and torchvision", "Couldn't install torch", live=True)
379
startup_timer.record("install torch")
380
381
if args.use_ipex:
382
args.skip_torch_cuda_test = True
383
if not args.skip_torch_cuda_test and not check_run_python("import torch; assert torch.cuda.is_available()"):
384
raise RuntimeError(
385
'Torch is not able to use GPU; '
386
'add --skip-torch-cuda-test to COMMANDLINE_ARGS variable to disable this check'
387
)
388
startup_timer.record("torch GPU test")
389
390
if not is_installed("clip"):
391
run_pip(f"install {clip_package}", "clip")
392
startup_timer.record("install clip")
393
394
if not is_installed("open_clip"):
395
run_pip(f"install {openclip_package}", "open_clip")
396
startup_timer.record("install open_clip")
397
398
if (not is_installed("xformers") or args.reinstall_xformers) and args.xformers:
399
run_pip(f"install -U -I --no-deps {xformers_package}", "xformers")
400
startup_timer.record("install xformers")
401
402
if not is_installed("ngrok") and args.ngrok:
403
run_pip("install ngrok", "ngrok")
404
startup_timer.record("install ngrok")
405
406
os.makedirs(os.path.join(script_path, dir_repos), exist_ok=True)
407
408
git_clone(stable_diffusion_repo, repo_dir('stable-diffusion-stability-ai'), "Stable Diffusion", stable_diffusion_commit_hash)
409
git_clone(stable_diffusion_xl_repo, repo_dir('generative-models'), "Stable Diffusion XL", stable_diffusion_xl_commit_hash)
410
git_clone(k_diffusion_repo, repo_dir('k-diffusion'), "K-diffusion", k_diffusion_commit_hash)
411
git_clone(codeformer_repo, repo_dir('CodeFormer'), "CodeFormer", codeformer_commit_hash)
412
git_clone(blip_repo, repo_dir('BLIP'), "BLIP", blip_commit_hash)
413
414
startup_timer.record("clone repositores")
415
416
if not is_installed("lpips"):
417
run_pip(f"install -r \"{os.path.join(repo_dir('CodeFormer'), 'requirements.txt')}\"", "requirements for CodeFormer")
418
startup_timer.record("install CodeFormer requirements")
419
420
if not os.path.isfile(requirements_file):
421
requirements_file = os.path.join(script_path, requirements_file)
422
423
if not requirements_met(requirements_file):
424
run_pip(f"install -r \"{requirements_file}\"", "requirements")
425
startup_timer.record("install requirements")
426
427
if not args.skip_install:
428
run_extensions_installers(settings_file=args.ui_settings_file)
429
430
if args.update_check:
431
version_check(commit)
432
startup_timer.record("check version")
433
434
if args.update_all_extensions:
435
git_pull_recursive(extensions_dir)
436
startup_timer.record("update extensions")
437
438
if "--exit" in sys.argv:
439
print("Exiting because of --exit argument")
440
exit(0)
441
442
443
444
def configure_for_tests():
445
if "--api" not in sys.argv:
446
sys.argv.append("--api")
447
if "--ckpt" not in sys.argv:
448
sys.argv.append("--ckpt")
449
sys.argv.append(os.path.join(script_path, "test/test_files/empty.pt"))
450
if "--skip-torch-cuda-test" not in sys.argv:
451
sys.argv.append("--skip-torch-cuda-test")
452
if "--disable-nan-check" not in sys.argv:
453
sys.argv.append("--disable-nan-check")
454
455
os.environ['COMMANDLINE_ARGS'] = ""
456
457
458
def start():
459
print(f"Launching {'API server' if '--nowebui' in sys.argv else 'Web UI'} with arguments: {' '.join(sys.argv[1:])}")
460
import webui
461
if '--nowebui' in sys.argv:
462
webui.api_only()
463
else:
464
webui.webui()
465
466
467
def dump_sysinfo():
468
from modules import sysinfo
469
import datetime
470
471
text = sysinfo.get()
472
filename = f"sysinfo-{datetime.datetime.utcnow().strftime('%Y-%m-%d-%H-%M')}.json"
473
474
with open(filename, "w", encoding="utf8") as file:
475
file.write(text)
476
477
return filename
478
479