Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ninjaneural
GitHub Repository: ninjaneural/webui
Path: blob/master/misc/direct/v1.5.2/launch_utils.py
3275 views
1
# this scripts installs necessary requirements and launches main program in webui.py
2
import re
3
import subprocess
4
import os
5
import sys
6
import importlib.util
7
import platform
8
import json
9
from functools import lru_cache
10
11
from modules import cmd_args, errors
12
from modules.paths_internal import script_path, extensions_dir
13
from modules import timer
14
15
timer.startup_timer.record("start")
16
17
args, _ = cmd_args.parser.parse_known_args()
18
19
python = sys.executable
20
git = os.environ.get('GIT', "git")
21
index_url = os.environ.get('INDEX_URL', "")
22
dir_repos = "repositories"
23
24
# Whether to default to printing command output
25
default_command_live = (os.environ.get('WEBUI_LAUNCH_LIVE_OUTPUT') == "1")
26
27
if 'GRADIO_ANALYTICS_ENABLED' not in os.environ:
28
os.environ['GRADIO_ANALYTICS_ENABLED'] = 'False'
29
30
31
def check_python_version():
32
is_windows = platform.system() == "Windows"
33
major = sys.version_info.major
34
minor = sys.version_info.minor
35
micro = sys.version_info.micro
36
37
if is_windows:
38
supported_minors = [10]
39
else:
40
supported_minors = [7, 8, 9, 10, 11]
41
42
if not (major == 3 and minor in supported_minors):
43
import modules.errors
44
45
modules.errors.print_error_explanation(f"""
46
INCOMPATIBLE PYTHON VERSION
47
48
This program is tested with 3.10.6 Python, but you have {major}.{minor}.{micro}.
49
If you encounter an error with "RuntimeError: Couldn't install torch." message,
50
or any other error regarding unsuccessful package (library) installation,
51
please downgrade (or upgrade) to the latest version of 3.10 Python
52
and delete current Python and "venv" folder in WebUI's directory.
53
54
You can download 3.10 Python from here: https://www.python.org/downloads/release/python-3106/
55
56
{"Alternatively, use a binary release of WebUI: https://github.com/AUTOMATIC1111/stable-diffusion-webui/releases" if is_windows else ""}
57
58
Use --skip-python-version-check to suppress this warning.
59
""")
60
61
62
@lru_cache()
63
def commit_hash():
64
try:
65
return subprocess.check_output([git, "rev-parse", "HEAD"], shell=False, encoding='utf8').strip()
66
except Exception:
67
return "<none>"
68
69
70
@lru_cache()
71
def git_tag():
72
try:
73
return subprocess.check_output([git, "describe", "--tags"], shell=False, encoding='utf8').strip()
74
except Exception:
75
try:
76
77
changelog_md = os.path.join(os.path.dirname(os.path.dirname(__file__)), "CHANGELOG.md")
78
with open(changelog_md, "r", encoding="utf-8") as file:
79
line = next((line.strip() for line in file if line.strip()), "<none>")
80
line = line.replace("## ", "")
81
return line
82
except Exception:
83
return "<none>"
84
85
86
def run(command, desc=None, errdesc=None, custom_env=None, live: bool = default_command_live) -> str:
87
if desc is not None:
88
print(desc)
89
90
run_kwargs = {
91
"args": command,
92
"shell": True,
93
"env": os.environ if custom_env is None else custom_env,
94
"encoding": 'utf8',
95
"errors": 'ignore',
96
}
97
98
if not live:
99
run_kwargs["stdout"] = run_kwargs["stderr"] = subprocess.PIPE
100
101
result = subprocess.run(**run_kwargs)
102
103
if result.returncode != 0:
104
error_bits = [
105
f"{errdesc or 'Error running command'}.",
106
f"Command: {command}",
107
f"Error code: {result.returncode}",
108
]
109
if result.stdout:
110
error_bits.append(f"stdout: {result.stdout}")
111
if result.stderr:
112
error_bits.append(f"stderr: {result.stderr}")
113
raise RuntimeError("\n".join(error_bits))
114
115
return (result.stdout or "")
116
117
118
def is_installed(package):
119
try:
120
spec = importlib.util.find_spec(package)
121
except ModuleNotFoundError:
122
return False
123
124
return spec is not None
125
126
127
def repo_dir(name):
128
return os.path.join(script_path, dir_repos, name)
129
130
131
def run_pip(command, desc=None, live=default_command_live):
132
if args.skip_install:
133
return
134
135
index_url_line = f' --index-url {index_url}' if index_url != '' else ''
136
return run(f'"{python}" -m pip {command} --prefer-binary{index_url_line}', desc=f"Installing {desc}", errdesc=f"Couldn't install {desc}", live=live)
137
138
139
def check_run_python(code: str) -> bool:
140
result = subprocess.run([python, "-c", code], capture_output=True, shell=False)
141
return result.returncode == 0
142
143
144
def git_clone(url, dir, name, commithash=None):
145
# TODO clone into temporary dir and move if successful
146
147
if os.path.exists(dir):
148
if commithash is None:
149
return
150
151
current_hash = run(f'"{git}" -C "{dir}" rev-parse HEAD', None, f"Couldn't determine {name}'s hash: {commithash}", live=False).strip()
152
if current_hash == commithash:
153
return
154
155
run(f'"{git}" -C "{dir}" fetch', f"Fetching updates for {name}...", f"Couldn't fetch {name}")
156
run(f'"{git}" -C "{dir}" reset --hard {commithash}', f"Checking out commit for {name} with hash: {commithash}...", f"Couldn't checkout commit {commithash} for {name}", live=True)
157
return
158
159
run(f'"{git}" clone "{url}" "{dir}"', f"Cloning {name} into {dir}...", f"Couldn't clone {name}", live=True)
160
161
if commithash is not None:
162
run(f'"{git}" -C "{dir}" reset --hard {commithash}', None, "Couldn't checkout {name}'s hash: {commithash}")
163
164
165
def git_pull_recursive(dir):
166
for subdir, _, _ in os.walk(dir):
167
if os.path.exists(os.path.join(subdir, '.git')):
168
try:
169
output = subprocess.check_output([git, '-C', subdir, 'pull', '--autostash'])
170
print(f"Pulled changes for repository in '{subdir}':\n{output.decode('utf-8').strip()}\n")
171
except subprocess.CalledProcessError as e:
172
print(f"Couldn't perform 'git pull' on repository in '{subdir}':\n{e.output.decode('utf-8').strip()}\n")
173
174
175
def version_check(commit):
176
try:
177
import requests
178
commits = requests.get('https://api.github.com/repos/AUTOMATIC1111/stable-diffusion-webui/branches/master').json()
179
if commit != "<none>" and commits['commit']['sha'] != commit:
180
print("--------------------------------------------------------")
181
print("| You are not up to date with the most recent release. |")
182
print("| Consider running `git pull` to update. |")
183
print("--------------------------------------------------------")
184
elif commits['commit']['sha'] == commit:
185
print("You are up to date with the most recent release.")
186
else:
187
print("Not a git clone, can't perform version check.")
188
except Exception as e:
189
print("version check failed", e)
190
191
192
def run_extension_installer(extension_dir):
193
path_installer = os.path.join(extension_dir, "install.py")
194
if not os.path.isfile(path_installer):
195
return
196
197
try:
198
env = os.environ.copy()
199
env['PYTHONPATH'] = f"{os.path.abspath('.')}{os.pathsep}{env.get('PYTHONPATH', '')}"
200
201
print(run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {extension_dir}", custom_env=env))
202
except Exception as e:
203
errors.report(str(e))
204
205
206
def list_extensions(settings_file):
207
settings = {}
208
209
try:
210
if os.path.isfile(settings_file):
211
with open(settings_file, "r", encoding="utf8") as file:
212
settings = json.load(file)
213
except Exception:
214
errors.report("Could not load settings", exc_info=True)
215
216
disabled_extensions = set(settings.get('disabled_extensions', []))
217
disable_all_extensions = settings.get('disable_all_extensions', 'none')
218
219
if disable_all_extensions != 'none':
220
return []
221
222
return [x for x in os.listdir(extensions_dir) if x not in disabled_extensions]
223
224
225
def run_extensions_installers(settings_file):
226
if not os.path.isdir(extensions_dir):
227
return
228
229
for dirname_extension in list_extensions(settings_file):
230
run_extension_installer(os.path.join(extensions_dir, dirname_extension))
231
232
233
re_requirement = re.compile(r"\s*([-_a-zA-Z0-9]+)\s*(?:==\s*([-+_.a-zA-Z0-9]+))?\s*")
234
235
236
def requirements_met(requirements_file):
237
"""
238
Does a simple parse of a requirements.txt file to determine if all rerqirements in it
239
are already installed. Returns True if so, False if not installed or parsing fails.
240
"""
241
242
import importlib.metadata
243
import packaging.version
244
245
with open(requirements_file, "r", encoding="utf8") as file:
246
for line in file:
247
if line.strip() == "":
248
continue
249
250
m = re.match(re_requirement, line)
251
if m is None:
252
return False
253
254
package = m.group(1).strip()
255
version_required = (m.group(2) or "").strip()
256
257
if version_required == "":
258
continue
259
260
try:
261
version_installed = importlib.metadata.version(package)
262
except Exception:
263
return False
264
265
if packaging.version.parse(version_required) != packaging.version.parse(version_installed):
266
return False
267
268
return True
269
270
271
def prepare_environment():
272
torch_index_url = os.environ.get('TORCH_INDEX_URL', "https://download.pytorch.org/whl/cu118")
273
torch_command = os.environ.get('TORCH_COMMAND', f"pip install torch==2.0.1 torchvision==0.15.2 --extra-index-url {torch_index_url}")
274
requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt")
275
276
xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.20')
277
gfpgan_package = os.environ.get('GFPGAN_PACKAGE', "https://github.com/TencentARC/GFPGAN/archive/8d2447a2d918f8eba5a4a01463fd48e45126a379.zip")
278
clip_package = os.environ.get('CLIP_PACKAGE', "https://github.com/openai/CLIP/archive/d50d76daa670286dd6cacf3bcd80b5e4823fc8e1.zip")
279
openclip_package = os.environ.get('OPENCLIP_PACKAGE', "https://github.com/mlfoundations/open_clip/archive/bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b.zip")
280
281
stable_diffusion_repo = os.environ.get('STABLE_DIFFUSION_REPO', "https://github.com/Stability-AI/stablediffusion.git")
282
stable_diffusion_xl_repo = os.environ.get('STABLE_DIFFUSION_XL_REPO', "https://github.com/Stability-AI/generative-models.git")
283
k_diffusion_repo = os.environ.get('K_DIFFUSION_REPO', 'https://github.com/crowsonkb/k-diffusion.git')
284
codeformer_repo = os.environ.get('CODEFORMER_REPO', 'https://github.com/sczhou/CodeFormer.git')
285
blip_repo = os.environ.get('BLIP_REPO', 'https://github.com/salesforce/BLIP.git')
286
287
stable_diffusion_commit_hash = os.environ.get('STABLE_DIFFUSION_COMMIT_HASH', "cf1d67a6fd5ea1aa600c4df58e5b47da45f6bdbf")
288
stable_diffusion_xl_commit_hash = os.environ.get('STABLE_DIFFUSION_XL_COMMIT_HASH', "5c10deee76adad0032b412294130090932317a87")
289
k_diffusion_commit_hash = os.environ.get('K_DIFFUSION_COMMIT_HASH', "c9fe758757e022f05ca5a53fa8fac28889e4f1cf")
290
codeformer_commit_hash = os.environ.get('CODEFORMER_COMMIT_HASH', "c5b4593074ba6214284d6acd5f1719b6c5d739af")
291
blip_commit_hash = os.environ.get('BLIP_COMMIT_HASH', "48211a1594f1321b00f14c9f7a5b4813144b2fb9")
292
293
try:
294
# the existance of this file is a signal to webui.sh/bat that webui needs to be restarted when it stops execution
295
os.remove(os.path.join(script_path, "tmp", "restart"))
296
os.environ.setdefault('SD_WEBUI_RESTARTING', '1')
297
except OSError:
298
pass
299
300
if not args.skip_python_version_check:
301
check_python_version()
302
303
commit = commit_hash()
304
tag = git_tag()
305
306
print(f"Python {sys.version}")
307
print(f"Version: {tag}")
308
print(f"Commit hash: {commit}")
309
310
if args.reinstall_torch or not is_installed("torch") or not is_installed("torchvision"):
311
run(f'"{python}" -m {torch_command}', "Installing torch and torchvision", "Couldn't install torch", live=True)
312
313
if not args.skip_torch_cuda_test and not check_run_python("import torch; assert torch.cuda.is_available()"):
314
raise RuntimeError(
315
'Torch is not able to use GPU; '
316
'add --skip-torch-cuda-test to COMMANDLINE_ARGS variable to disable this check'
317
)
318
319
if not is_installed("gfpgan"):
320
run_pip(f"install {gfpgan_package}", "gfpgan")
321
322
if not is_installed("clip"):
323
run_pip(f"install {clip_package}", "clip")
324
325
if not is_installed("open_clip"):
326
run_pip(f"install {openclip_package}", "open_clip")
327
328
if (not is_installed("xformers") or args.reinstall_xformers) and args.xformers:
329
if platform.system() == "Windows":
330
if platform.python_version().startswith("3.10"):
331
run_pip(f"install -U -I --no-deps {xformers_package}", "xformers", live=True)
332
else:
333
print("Installation of xformers is not supported in this version of Python.")
334
print("You can also check this and build manually: https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Xformers#building-xformers-on-windows-by-duckness")
335
if not is_installed("xformers"):
336
exit(0)
337
elif platform.system() == "Linux":
338
run_pip(f"install -U -I --no-deps {xformers_package}", "xformers")
339
340
if not is_installed("ngrok") and args.ngrok:
341
run_pip("install ngrok", "ngrok")
342
343
os.makedirs(os.path.join(script_path, dir_repos), exist_ok=True)
344
345
git_clone(stable_diffusion_repo, repo_dir('stable-diffusion-stability-ai'), "Stable Diffusion", stable_diffusion_commit_hash)
346
git_clone(stable_diffusion_xl_repo, repo_dir('generative-models'), "Stable Diffusion XL", stable_diffusion_xl_commit_hash)
347
git_clone(k_diffusion_repo, repo_dir('k-diffusion'), "K-diffusion", k_diffusion_commit_hash)
348
git_clone(codeformer_repo, repo_dir('CodeFormer'), "CodeFormer", codeformer_commit_hash)
349
git_clone(blip_repo, repo_dir('BLIP'), "BLIP", blip_commit_hash)
350
351
if not is_installed("lpips"):
352
run_pip(f"install -r \"{os.path.join(repo_dir('CodeFormer'), 'requirements.txt')}\"", "requirements for CodeFormer")
353
354
if not os.path.isfile(requirements_file):
355
requirements_file = os.path.join(script_path, requirements_file)
356
357
if not requirements_met(requirements_file):
358
run_pip(f"install -r \"{requirements_file}\"", "requirements")
359
360
run_extensions_installers(settings_file=args.ui_settings_file)
361
362
if args.update_check:
363
version_check(commit)
364
365
if args.update_all_extensions:
366
git_pull_recursive(extensions_dir)
367
368
if "--exit" in sys.argv:
369
print("Exiting because of --exit argument")
370
exit(0)
371
372
373
374
def configure_for_tests():
375
if "--api" not in sys.argv:
376
sys.argv.append("--api")
377
if "--ckpt" not in sys.argv:
378
sys.argv.append("--ckpt")
379
sys.argv.append(os.path.join(script_path, "test/test_files/empty.pt"))
380
if "--skip-torch-cuda-test" not in sys.argv:
381
sys.argv.append("--skip-torch-cuda-test")
382
if "--disable-nan-check" not in sys.argv:
383
sys.argv.append("--disable-nan-check")
384
385
os.environ['COMMANDLINE_ARGS'] = ""
386
387
388
def start():
389
print(f"Launching {'API server' if '--nowebui' in sys.argv else 'Web UI'} with arguments: {' '.join(sys.argv[1:])}")
390
import webui
391
if '--nowebui' in sys.argv:
392
webui.api_only()
393
else:
394
webui.webui()
395
396