Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ninjaneural
GitHub Repository: ninjaneural/webui
Path: blob/master/misc/direct/v1.6.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 platform
10
import json
11
from functools import lru_cache
12
13
from modules import cmd_args, errors
14
from modules.paths_internal import script_path, extensions_dir
15
from modules.timer import startup_timer
16
from modules import logging_config
17
18
args, _ = cmd_args.parser.parse_known_args()
19
logging_config.setup_logging(args.loglevel)
20
21
python = sys.executable
22
git = os.environ.get('GIT', "git")
23
index_url = os.environ.get('INDEX_URL', "")
24
dir_repos = "repositories"
25
26
# Whether to default to printing command output
27
default_command_live = (os.environ.get('WEBUI_LAUNCH_LIVE_OUTPUT') == "1")
28
29
if 'GRADIO_ANALYTICS_ENABLED' not in os.environ:
30
os.environ['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, "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, "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
spec = importlib.util.find_spec(package)
123
except ModuleNotFoundError:
124
return False
125
126
return spec is not None
127
128
129
def repo_dir(name):
130
return os.path.join(script_path, dir_repos, name)
131
132
133
def run_pip(command, desc=None, live=default_command_live):
134
if args.skip_install:
135
return
136
137
index_url_line = f' --index-url {index_url}' if index_url != '' else ''
138
return run(f'"{python}" -m pip {command} --prefer-binary{index_url_line}', desc=f"Installing {desc}", errdesc=f"Couldn't install {desc}", live=live)
139
140
141
def check_run_python(code: str) -> bool:
142
result = subprocess.run([python, "-c", code], capture_output=True, shell=False)
143
return result.returncode == 0
144
145
146
def git_fix_workspace(dir, name):
147
run(f'"{git}" -C "{dir}" fetch --refetch --no-auto-gc', f"Fetching all contents for {name}", f"Couldn't fetch {name}", live=True)
148
run(f'"{git}" -C "{dir}" gc --aggressive --prune=now', f"Pruning {name}", f"Couldn't prune {name}", live=True)
149
return
150
151
152
def run_git(dir, name, command, desc=None, errdesc=None, custom_env=None, live: bool = default_command_live, autofix=True):
153
try:
154
return run(f'"{git}" -C "{dir}" {command}', desc=desc, errdesc=errdesc, custom_env=custom_env, live=live)
155
except RuntimeError:
156
if not autofix:
157
raise
158
159
print(f"{errdesc}, attempting autofix...")
160
git_fix_workspace(dir, name)
161
162
return run(f'"{git}" -C "{dir}" {command}', desc=desc, errdesc=errdesc, custom_env=custom_env, live=live)
163
164
165
def git_clone(url, dir, name, commithash=None):
166
# TODO clone into temporary dir and move if successful
167
168
if os.path.exists(dir):
169
if commithash is None:
170
return
171
172
current_hash = run_git(dir, name, 'rev-parse HEAD', None, f"Couldn't determine {name}'s hash: {commithash}", live=False).strip()
173
if current_hash == commithash:
174
return
175
176
if run_git(dir, name, 'config --get remote.origin.url', None, f"Couldn't determine {name}'s origin URL", live=False).strip() != url:
177
run_git(dir, name, f'remote set-url origin "{url}"', None, f"Failed to set {name}'s origin URL", live=False)
178
179
run_git(dir, name, 'fetch', f"Fetching updates for {name}...", f"Couldn't fetch {name}", autofix=False)
180
181
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)
182
183
return
184
185
try:
186
run(f'"{git}" clone "{url}" "{dir}"', f"Cloning {name} into {dir}...", f"Couldn't clone {name}", live=True)
187
except RuntimeError:
188
shutil.rmtree(dir, ignore_errors=True)
189
raise
190
191
if commithash is not None:
192
run(f'"{git}" -C "{dir}" checkout {commithash}', None, "Couldn't checkout {name}'s hash: {commithash}")
193
194
195
def git_pull_recursive(dir):
196
for subdir, _, _ in os.walk(dir):
197
if os.path.exists(os.path.join(subdir, '.git')):
198
try:
199
output = subprocess.check_output([git, '-C', subdir, 'pull', '--autostash'])
200
print(f"Pulled changes for repository in '{subdir}':\n{output.decode('utf-8').strip()}\n")
201
except subprocess.CalledProcessError as e:
202
print(f"Couldn't perform 'git pull' on repository in '{subdir}':\n{e.output.decode('utf-8').strip()}\n")
203
204
205
def version_check(commit):
206
try:
207
import requests
208
commits = requests.get('https://api.github.com/repos/AUTOMATIC1111/stable-diffusion-webui/branches/master').json()
209
if commit != "<none>" and commits['commit']['sha'] != commit:
210
print("--------------------------------------------------------")
211
print("| You are not up to date with the most recent release. |")
212
print("| Consider running `git pull` to update. |")
213
print("--------------------------------------------------------")
214
elif commits['commit']['sha'] == commit:
215
print("You are up to date with the most recent release.")
216
else:
217
print("Not a git clone, can't perform version check.")
218
except Exception as e:
219
print("version check failed", e)
220
221
222
def run_extension_installer(extension_dir):
223
path_installer = os.path.join(extension_dir, "install.py")
224
if not os.path.isfile(path_installer):
225
return
226
227
try:
228
env = os.environ.copy()
229
env['PYTHONPATH'] = f"{os.path.abspath('.')}{os.pathsep}{env.get('PYTHONPATH', '')}"
230
231
stdout = run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {extension_dir}", custom_env=env).strip()
232
if stdout:
233
print(stdout)
234
except Exception as e:
235
errors.report(str(e))
236
237
238
def list_extensions(settings_file):
239
settings = {}
240
241
try:
242
if os.path.isfile(settings_file):
243
with open(settings_file, "r", encoding="utf8") as file:
244
settings = json.load(file)
245
except Exception:
246
errors.report("Could not load settings", exc_info=True)
247
248
disabled_extensions = set(settings.get('disabled_extensions', []))
249
disable_all_extensions = settings.get('disable_all_extensions', 'none')
250
251
if disable_all_extensions != 'none' or args.disable_extra_extensions or args.disable_all_extensions or not os.path.isdir(extensions_dir):
252
return []
253
254
return [x for x in os.listdir(extensions_dir) if x not in disabled_extensions]
255
256
257
def run_extensions_installers(settings_file):
258
if not os.path.isdir(extensions_dir):
259
return
260
261
with startup_timer.subcategory("run extensions installers"):
262
for dirname_extension in list_extensions(settings_file):
263
logging.debug(f"Installing {dirname_extension}")
264
265
path = os.path.join(extensions_dir, dirname_extension)
266
267
if os.path.isdir(path):
268
run_extension_installer(path)
269
startup_timer.record(dirname_extension)
270
271
272
re_requirement = re.compile(r"\s*([-_a-zA-Z0-9]+)\s*(?:==\s*([-+_.a-zA-Z0-9]+))?\s*")
273
274
275
def requirements_met(requirements_file):
276
"""
277
Does a simple parse of a requirements.txt file to determine if all rerqirements in it
278
are already installed. Returns True if so, False if not installed or parsing fails.
279
"""
280
281
import importlib.metadata
282
import packaging.version
283
284
with open(requirements_file, "r", encoding="utf8") as file:
285
for line in file:
286
if line.strip() == "":
287
continue
288
289
m = re.match(re_requirement, line)
290
if m is None:
291
return False
292
293
package = m.group(1).strip()
294
version_required = (m.group(2) or "").strip()
295
296
if version_required == "":
297
continue
298
299
try:
300
version_installed = importlib.metadata.version(package)
301
except Exception:
302
return False
303
304
if packaging.version.parse(version_required) != packaging.version.parse(version_installed):
305
return False
306
307
return True
308
309
310
def prepare_environment():
311
torch_index_url = os.environ.get('TORCH_INDEX_URL', "https://download.pytorch.org/whl/cu118")
312
torch_command = os.environ.get('TORCH_COMMAND', f"pip install torch==2.0.1 torchvision==0.15.2 --extra-index-url {torch_index_url}")
313
requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt")
314
315
xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.20')
316
clip_package = os.environ.get('CLIP_PACKAGE', "https://github.com/openai/CLIP/archive/d50d76daa670286dd6cacf3bcd80b5e4823fc8e1.zip")
317
openclip_package = os.environ.get('OPENCLIP_PACKAGE', "https://github.com/mlfoundations/open_clip/archive/bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b.zip")
318
319
stable_diffusion_repo = os.environ.get('STABLE_DIFFUSION_REPO', "https://github.com/Stability-AI/stablediffusion.git")
320
stable_diffusion_xl_repo = os.environ.get('STABLE_DIFFUSION_XL_REPO', "https://github.com/Stability-AI/generative-models.git")
321
k_diffusion_repo = os.environ.get('K_DIFFUSION_REPO', 'https://github.com/crowsonkb/k-diffusion.git')
322
codeformer_repo = os.environ.get('CODEFORMER_REPO', 'https://github.com/sczhou/CodeFormer.git')
323
blip_repo = os.environ.get('BLIP_REPO', 'https://github.com/salesforce/BLIP.git')
324
325
stable_diffusion_commit_hash = os.environ.get('STABLE_DIFFUSION_COMMIT_HASH', "cf1d67a6fd5ea1aa600c4df58e5b47da45f6bdbf")
326
stable_diffusion_xl_commit_hash = os.environ.get('STABLE_DIFFUSION_XL_COMMIT_HASH', "45c443b316737a4ab6e40413d7794a7f5657c19f")
327
k_diffusion_commit_hash = os.environ.get('K_DIFFUSION_COMMIT_HASH', "ab527a9a6d347f364e3d185ba6d714e22d80cb3c")
328
codeformer_commit_hash = os.environ.get('CODEFORMER_COMMIT_HASH', "c5b4593074ba6214284d6acd5f1719b6c5d739af")
329
blip_commit_hash = os.environ.get('BLIP_COMMIT_HASH', "48211a1594f1321b00f14c9f7a5b4813144b2fb9")
330
331
try:
332
# the existence of this file is a signal to webui.sh/bat that webui needs to be restarted when it stops execution
333
os.remove(os.path.join(script_path, "tmp", "restart"))
334
os.environ.setdefault('SD_WEBUI_RESTARTING', '1')
335
except OSError:
336
pass
337
338
if not args.skip_python_version_check:
339
check_python_version()
340
341
startup_timer.record("checks")
342
343
commit = commit_hash()
344
tag = git_tag()
345
startup_timer.record("git version info")
346
347
print(f"Python {sys.version}")
348
print(f"Version: {tag}")
349
print(f"Commit hash: {commit}")
350
351
if args.reinstall_torch or not is_installed("torch") or not is_installed("torchvision"):
352
run(f'"{python}" -m {torch_command}', "Installing torch and torchvision", "Couldn't install torch", live=True)
353
startup_timer.record("install torch")
354
355
if not args.skip_torch_cuda_test and not check_run_python("import torch; assert torch.cuda.is_available()"):
356
raise RuntimeError(
357
'Torch is not able to use GPU; '
358
'add --skip-torch-cuda-test to COMMANDLINE_ARGS variable to disable this check'
359
)
360
startup_timer.record("torch GPU test")
361
362
if not is_installed("clip"):
363
run_pip(f"install {clip_package}", "clip")
364
startup_timer.record("install clip")
365
366
if not is_installed("open_clip"):
367
run_pip(f"install {openclip_package}", "open_clip")
368
startup_timer.record("install open_clip")
369
370
if (not is_installed("xformers") or args.reinstall_xformers) and args.xformers:
371
run_pip(f"install -U -I --no-deps {xformers_package}", "xformers")
372
startup_timer.record("install xformers")
373
374
if not is_installed("ngrok") and args.ngrok:
375
run_pip("install ngrok", "ngrok")
376
startup_timer.record("install ngrok")
377
378
os.makedirs(os.path.join(script_path, dir_repos), exist_ok=True)
379
380
git_clone(stable_diffusion_repo, repo_dir('stable-diffusion-stability-ai'), "Stable Diffusion", stable_diffusion_commit_hash)
381
git_clone(stable_diffusion_xl_repo, repo_dir('generative-models'), "Stable Diffusion XL", stable_diffusion_xl_commit_hash)
382
git_clone(k_diffusion_repo, repo_dir('k-diffusion'), "K-diffusion", k_diffusion_commit_hash)
383
git_clone(codeformer_repo, repo_dir('CodeFormer'), "CodeFormer", codeformer_commit_hash)
384
git_clone(blip_repo, repo_dir('BLIP'), "BLIP", blip_commit_hash)
385
386
startup_timer.record("clone repositores")
387
388
if not is_installed("lpips"):
389
run_pip(f"install -r \"{os.path.join(repo_dir('CodeFormer'), 'requirements.txt')}\"", "requirements for CodeFormer")
390
startup_timer.record("install CodeFormer requirements")
391
392
if not os.path.isfile(requirements_file):
393
requirements_file = os.path.join(script_path, requirements_file)
394
395
if not requirements_met(requirements_file):
396
run_pip(f"install -r \"{requirements_file}\"", "requirements")
397
startup_timer.record("install requirements")
398
399
if not args.skip_install:
400
run_extensions_installers(settings_file=args.ui_settings_file)
401
402
if args.update_check:
403
version_check(commit)
404
startup_timer.record("check version")
405
406
if args.update_all_extensions:
407
git_pull_recursive(extensions_dir)
408
startup_timer.record("update extensions")
409
410
if "--exit" in sys.argv:
411
print("Exiting because of --exit argument")
412
exit(0)
413
414
415
416
def configure_for_tests():
417
if "--api" not in sys.argv:
418
sys.argv.append("--api")
419
if "--ckpt" not in sys.argv:
420
sys.argv.append("--ckpt")
421
sys.argv.append(os.path.join(script_path, "test/test_files/empty.pt"))
422
if "--skip-torch-cuda-test" not in sys.argv:
423
sys.argv.append("--skip-torch-cuda-test")
424
if "--disable-nan-check" not in sys.argv:
425
sys.argv.append("--disable-nan-check")
426
427
os.environ['COMMANDLINE_ARGS'] = ""
428
429
430
def start():
431
print(f"Launching {'API server' if '--nowebui' in sys.argv else 'Web UI'} with arguments: {' '.join(sys.argv[1:])}")
432
import webui
433
if '--nowebui' in sys.argv:
434
webui.api_only()
435
else:
436
webui.webui()
437
438
439
def dump_sysinfo():
440
from modules import sysinfo
441
import datetime
442
443
text = sysinfo.get()
444
filename = f"sysinfo-{datetime.datetime.utcnow().strftime('%Y-%m-%d-%H-%M')}.txt"
445
446
with open(filename, "w", encoding="utf8") as file:
447
file.write(text)
448
449
return filename
450
451