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