From f6679fcc777bf843242d876580b27d13318f50df Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 17 Mar 2023 10:08:07 -0400 Subject: [PATCH] add global exception handler --- README.md | 19 +++- automatic.sh | 8 +- launch.py | 99 +------------------ modules/call_queue.py | 4 +- modules/codeformer_model.py | 3 +- modules/extensions.py | 3 +- modules/gfpgan_model.py | 3 +- modules/hypernetworks/hypernetwork.py | 5 +- modules/images.py | 5 +- modules/img2img.py | 1 - modules/interrogate.py | 3 +- modules/localization.py | 4 +- modules/prompt_parser.py | 3 +- modules/realesrgan_model.py | 8 +- modules/safe.py | 9 +- modules/script_callbacks.py | 4 +- modules/script_loading.py | 4 +- modules/scripts.py | 22 ++--- modules/sd_hijack.py | 18 ++-- modules/sd_hijack_optimizations.py | 4 +- modules/shared.py | 17 +++- .../textual_inversion/textual_inversion.py | 5 +- modules/ui.py | 5 +- modules/ui_extensions.py | 5 +- scripts/prompts_from_file.py | 4 +- webui.py | 33 ------- 26 files changed, 90 insertions(+), 208 deletions(-) diff --git a/README.md b/README.md index bc524b43a..cd45fecdf 100644 --- a/README.md +++ b/README.md @@ -117,22 +117,35 @@ Fork does differ in few things: - Drops compatibility with `python` **3.7** and requires **3.9** Recommended is **Python 3.10** Note that **Python 3.11** or **3.12** are NOT supported +- New global exception handler - Drops localizations - Updated **Python** libraries to latest known compatible versions e.g. `accelerate`, `transformers`, `numpy`, etc. - Includes opinionated **System** and **Options** configuration e.g. `samplers`, `upscalers`, etc. -- Includes reskinned **UI** - Black and orange dark theme with fixed width options panels and larger previews +- Does not rely on `Accelerate` as it only affects distributed systems +- Optimized startup order + Gradio web server will be initialized much earlier which model load is done in the background - Includes **SD2** configuration files - Uses simplified folder structure e.g. `/train`, `/outputs/*` - Modified training templates - Built-in `LoRA`, `LyCORIS`, `Custom Diffusion`, `Dreambooth` training +User Interface: + +- Includes reskinned **UI** + Black and orange dark theme with fixed width options panels and larger previews + +Optimizations: + +- Runs with `SDP` memory attention enabled by default if supported by system +- Fallback to `XFormers` if SDP is not supported +- If either `SDP` or `XFormers` are not supported, falls back to usual cmd line arguments + Only Python library which is not auto-updated is `PyTorch` itself as that is very system specific -Fork is compatible with regular **PyTorch 1.13** as well as pre-releases of **PyTorch** **2.0** and **2.1** +Fork is compatible with regular **PyTorch 1.13**, **PyTorch 2.0** as well as pre-releases of **PyTorch** **2.1** See [Wiki](https://github.com/vladmandic/automatic/wiki/Torch-Optimizations) for **Torch** optimization notes
diff --git a/automatic.sh b/automatic.sh index c5ad0bc03..b2906206d 100755 --- a/automatic.sh +++ b/automatic.sh @@ -17,10 +17,11 @@ if [ "$PYTHON" == "" ]; then fi # Note: Some defaults are changed in shared.py -CMD="launch.py --xformers --skip-install --skip-torch-cuda-test --cors-allow-origins=http://127.0.0.1:7860" +CMD="launch.py --skip-install --cors-allow-origins=http://127.0.0.1:7860" MODE=optimized +# Sanity checks if [[ $(id -u) -eq 0 ]]; then echo "Running as root, aborting" exit 1 @@ -32,6 +33,7 @@ if [ $? -ne 0 ]; then exit 1 fi +# Parse arguments for i in "$@"; do case $i in update) @@ -57,7 +59,6 @@ for i in "$@"; do done echo "SD server: $MODE" - VER=$(git log -1 --pretty=format:"%h %ad") URL=$(git remote get-url origin) LSB=$(lsb_release -ds 2>/dev/null) @@ -70,7 +71,6 @@ echo "Last Merge: $MERGE" echo "System" echo "- Platform: $LSB $UNAME" echo "- nVIDIA: $SMI" -"$PYTHON" -c 'import torch; import platform; print("- Python:", platform.python_version(), "Torch:", torch.__version__, "CUDA:", torch.version.cuda, "cuDNN:", torch.backends.cudnn.version(), "GPU:", torch.cuda.get_device_name(torch.cuda.current_device()), "Arch:", torch.cuda.get_device_capability());' git-version () { pushd $1 >/dev/null @@ -108,6 +108,7 @@ fi if [ "$MODE" == install ]; then "$PYTHON" -m pip --version + "$PYTHON" -c 'import torch; import platform; print("- Python:", platform.python_version(), "Torch:", torch.__version__, "CUDA:", torch.version.cuda, "cuDNN:", torch.backends.cudnn.version(), "GPU:", torch.cuda.get_device_name(torch.cuda.current_device()), "Arch:", torch.cuda.get_device_capability());' echo "Installing general requirements" "$PYTHON" -m pip install --disable-pip-version-check --quiet --no-warn-conflicts --requirement requirements.txt @@ -159,4 +160,3 @@ exec "$PYTHON" $CMD # export LD_PRELOAD=libtcmalloc.so # TORCH_CUDA_ARCH_LIST="8.6" # --opt-channelslast -# --opt-sdp-attention diff --git a/launch.py b/launch.py index a2f74bac4..e2bef4ffe 100644 --- a/launch.py +++ b/launch.py @@ -16,7 +16,6 @@ args, _ = parser.parse_known_args(sys.argv) script_path = os.path.dirname(__file__) data_path = os.getcwd() - dir_repos = "repositories" dir_extensions = "extensions" python = sys.executable @@ -40,22 +39,7 @@ def check_python_version(): if not (major == 3 and minor in supported_minors): import modules.errors - - modules.errors.print_error_explanation(f""" -INCOMPATIBLE PYTHON VERSION - -This program is tested with 3.10.6 Python, but you have {major}.{minor}.{micro}. -If you encounter an error with "RuntimeError: Couldn't install torch." message, -or any other error regarding unsuccessful package (library) installation, -please downgrade (or upgrade) to the latest version of 3.10 Python -and delete current Python and "venv" folder in WebUI's directory. - -You can download 3.10 Python from here: https://www.python.org/downloads/release/python-3109/ - -{"Alternatively, use a binary release of WebUI: https://github.com/AUTOMATIC1111/stable-diffusion-webui/releases" if is_windows else ""} - -Use --skip-python-version-check to suppress this warning. -""") + modules.errors.print_error_explanation(f"Incompatible Python version: {major}.{minor}.{micro} required 3.9-3.11") def commit_hash(): @@ -182,23 +166,6 @@ def git_pull_recursive(dir): print(f"Couldn't perform 'git pull' on repository in '{subdir}':\n{e.output.decode('utf-8').strip()}\n") -def version_check(commit): - try: - import requests - commits = requests.get('https://api.github.com/repos/AUTOMATIC1111/stable-diffusion-webui/branches/master').json() - if commit != "" and commits['commit']['sha'] != commit: - print("--------------------------------------------------------") - print("| You are not up to date with the most recent release. |") - print("| Consider running `git pull` to update. |") - print("--------------------------------------------------------") - elif commits['commit']['sha'] == commit: - print("You are up to date with the most recent release.") - else: - print("Not a git clone, can't perform version check.") - except Exception as e: - print("version check failed", e) - - def run_extension_installer(extension_dir): path_installer = os.path.join(extension_dir, "install.py") if not os.path.isfile(path_installer): @@ -241,11 +208,9 @@ def run_extensions_installers(settings_file): def prepare_environment(): global skip_install - torch_command = os.environ.get('TORCH_COMMAND', "pip install torch==1.13.1+cu117 torchvision==0.14.1+cu117 --extra-index-url https://download.pytorch.org/whl/cu117") requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt") commandline_args = os.environ.get('COMMANDLINE_ARGS', "") - xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.16rc425') gfpgan_package = os.environ.get('GFPGAN_PACKAGE', "git+https://github.com/TencentARC/GFPGAN.git@8d2447a2d918f8eba5a4a01463fd48e45126a379") clip_package = os.environ.get('CLIP_PACKAGE', "git+https://github.com/openai/CLIP.git@d50d76daa670286dd6cacf3bcd80b5e4823fc8e1") openclip_package = os.environ.get('OPENCLIP_PACKAGE', "git+https://github.com/mlfoundations/open_clip.git@bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b") @@ -267,14 +232,8 @@ def prepare_environment(): sys.argv, _ = extract_arg(sys.argv, '-f') sys.argv, update_all_extensions = extract_arg(sys.argv, '--update-all-extensions') - sys.argv, skip_torch_cuda_test = extract_arg(sys.argv, '--skip-torch-cuda-test') sys.argv, skip_python_version_check = extract_arg(sys.argv, '--skip-python-version-check') - sys.argv, reinstall_xformers = extract_arg(sys.argv, '--reinstall-xformers') - sys.argv, reinstall_torch = extract_arg(sys.argv, '--reinstall-torch') - sys.argv, update_check = extract_arg(sys.argv, '--update-check') - sys.argv, run_tests, test_dir = extract_opt(sys.argv, '--tests') sys.argv, skip_install = extract_arg(sys.argv, '--skip-install') - xformers = '--xformers' in sys.argv ngrok = '--ngrok' in sys.argv if not skip_python_version_check: @@ -282,15 +241,6 @@ def prepare_environment(): commit = commit_hash() - # print(f"Python {sys.version}") - # print(f"Commit hash: {commit}") - - if reinstall_torch or not is_installed("torch") or not is_installed("torchvision"): - run(f'"{python}" -m {torch_command}', "Installing torch and torchvision", "Couldn't install torch", live=True) - - if not skip_torch_cuda_test: - run_python("import torch; assert torch.cuda.is_available(), 'Torch is not able to use GPU; add --skip-torch-cuda-test to COMMANDLINE_ARGS variable to disable this check'") - if not is_installed("gfpgan"): run_pip(f"install {gfpgan_package}", "gfpgan") @@ -300,18 +250,6 @@ def prepare_environment(): if not is_installed("open_clip"): run_pip(f"install {openclip_package}", "open_clip") - if (not is_installed("xformers") or reinstall_xformers) and xformers: - if platform.system() == "Windows": - if platform.python_version().startswith("3.10"): - run_pip(f"install -U -I --no-deps {xformers_package}", "xformers") - else: - print("Installation of xformers is not supported in this version of Python.") - print("You can also check this and build manually: https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Xformers#building-xformers-on-windows-by-duckness") - if not is_installed("xformers"): - exit(0) - elif platform.system() == "Linux": - run_pip(f"install {xformers_package}", "xformers") - if not is_installed("pyngrok") and ngrok: run_pip("install pyngrok", "ngrok") @@ -335,51 +273,22 @@ def prepare_environment(): run_extensions_installers(settings_file=args.ui_settings_file) - if update_check: - version_check(commit) - if update_all_extensions: git_pull_recursive(os.path.join(data_path, dir_extensions)) - if run_tests: - exitcode = tests(test_dir) - exit(exitcode) - - -def tests(test_dir): - if "--api" not in sys.argv: - sys.argv.append("--api") - if "--ckpt" not in sys.argv: - sys.argv.append("--ckpt") - sys.argv.append(os.path.join(script_path, "test/test_files/empty.pt")) - if "--skip-torch-cuda-test" not in sys.argv: - sys.argv.append("--skip-torch-cuda-test") - if "--disable-nan-check" not in sys.argv: - sys.argv.append("--disable-nan-check") - - print(f"Launching Web UI in another process for testing with arguments: {' '.join(sys.argv[1:])}") - - os.environ['COMMANDLINE_ARGS'] = "" - with open(os.path.join(script_path, 'test/stdout.txt'), "w", encoding="utf8") as stdout, open(os.path.join(script_path, 'test/stderr.txt'), "w", encoding="utf8") as stderr: - proc = subprocess.Popen([sys.executable, *sys.argv], stdout=stdout, stderr=stderr) - - # import test.server_poll - # exitcode = test.server_poll.run_tests(proc, test_dir) - - print(f"Stopping Web UI process with id {proc.pid}") - proc.kill() - return 0 - def start(): print(f"Launching server with arguments: {' '.join(sys.argv[1:])}") + rich_installed = False try: from rich.traceback import install from rich.console import Console console = Console() install(show_locals=True, max_frames=2, extra_lines=1, word_wrap=False, width=min([console.width, 200])) + rich_installed = True except: + import traceback pass # if rich is not installed do nothing import webui diff --git a/modules/call_queue.py b/modules/call_queue.py index f0c441a09..17f247567 100644 --- a/modules/call_queue.py +++ b/modules/call_queue.py @@ -1,7 +1,6 @@ import html import sys import threading -import traceback import time import cProfile, pstats, io @@ -75,8 +74,7 @@ def wrap_gradio_call(func, extra_outputs=None, add_stats=False): if len(argStr) > max_debug_str_len: print(f"(Argument list truncated at {max_debug_str_len}/{len(argStr)} characters)", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) - + shared.exception() shared.state.job = "" shared.state.job_count = 0 diff --git a/modules/codeformer_model.py b/modules/codeformer_model.py index 8d84bbc90..e9c668b61 100644 --- a/modules/codeformer_model.py +++ b/modules/codeformer_model.py @@ -1,6 +1,5 @@ import os import sys -import traceback import cv2 import torch @@ -138,6 +137,6 @@ def setup_model(dirname): except Exception: print("Error setting up CodeFormer:", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() # sys.path = stored_sys_path diff --git a/modules/extensions.py b/modules/extensions.py index ed4b58fe3..f4f51cd45 100644 --- a/modules/extensions.py +++ b/modules/extensions.py @@ -1,6 +1,5 @@ import os import sys -import traceback import time import git @@ -34,7 +33,7 @@ class Extension: repo = git.Repo(path) except Exception: print(f"Error reading github repository info from {path}:", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() if repo is None or repo.bare: self.remote = None diff --git a/modules/gfpgan_model.py b/modules/gfpgan_model.py index fbe6215a6..80ad6f1c6 100644 --- a/modules/gfpgan_model.py +++ b/modules/gfpgan_model.py @@ -1,6 +1,5 @@ import os import sys -import traceback import facexlib import gfpgan @@ -113,4 +112,4 @@ def setup_model(dirname): shared.face_restorers.append(FaceRestorerGFPGAN()) except Exception: print("Error setting up GFPGAN:", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() diff --git a/modules/hypernetworks/hypernetwork.py b/modules/hypernetworks/hypernetwork.py index f6ef42d5a..b859271d0 100644 --- a/modules/hypernetworks/hypernetwork.py +++ b/modules/hypernetworks/hypernetwork.py @@ -4,7 +4,6 @@ import glob import html import os import sys -import traceback import inspect import modules.textual_inversion.dataset @@ -332,7 +331,7 @@ def load_hypernetwork(name): hypernetwork.load(path) except Exception: print(f"Error loading hypernetwork {path}", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() return None return hypernetwork @@ -771,7 +770,7 @@ Last saved image: {html.escape(last_saved_image)}

""" except Exception: - print(traceback.format_exc(), file=sys.stderr) + shared.exception() finally: pbar.leave = False pbar.close() diff --git a/modules/images.py b/modules/images.py index 3e7c64eb6..2846e7982 100644 --- a/modules/images.py +++ b/modules/images.py @@ -1,6 +1,5 @@ import datetime import sys -import traceback import pytz import io @@ -421,7 +420,7 @@ class FilenameGenerator: except Exception: replacement = None print(f"Error adding [{pattern}] to filename", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() if replacement is not None: res += str(replacement) @@ -666,7 +665,7 @@ Negative prompt: {json_info["uc"]} Steps: {json_info["steps"]}, Sampler: {sampler}, CFG scale: {json_info["scale"]}, Seed: {json_info["seed"]}, Size: {image.width}x{image.height}, Clip skip: 2, ENSD: 31337""" except Exception: print("Error parsing NovelAI image generation parameters:", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() return geninfo, items diff --git a/modules/img2img.py b/modules/img2img.py index c973b7708..622167ce7 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -1,7 +1,6 @@ import math import os import sys -import traceback import numpy as np from PIL import Image, ImageOps, ImageFilter, ImageEnhance, ImageChops diff --git a/modules/interrogate.py b/modules/interrogate.py index cbb806832..31f37db4c 100644 --- a/modules/interrogate.py +++ b/modules/interrogate.py @@ -1,6 +1,5 @@ import os import sys -import traceback from collections import namedtuple from pathlib import Path import re @@ -218,7 +217,7 @@ class InterrogateModels: except Exception: print("Error interrogating", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() res += "" self.unload() diff --git a/modules/localization.py b/modules/localization.py index 353a6baa1..368cecd58 100644 --- a/modules/localization.py +++ b/modules/localization.py @@ -1,7 +1,7 @@ import json import os import sys -import traceback +import modules.shared as shared localizations = {} @@ -33,6 +33,6 @@ def localization_js(current_localization_name): data = json.load(file) except Exception: print(f"Error loading localization from {fn}:", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() return f"var localization = {json.dumps(data)}\n" diff --git a/modules/prompt_parser.py b/modules/prompt_parser.py index 696653725..2a60bd2c9 100644 --- a/modules/prompt_parser.py +++ b/modules/prompt_parser.py @@ -94,8 +94,7 @@ def get_learned_conditioning_prompt_schedules(prompts, steps): tree = schedule_parser.parse(prompt) except lark.exceptions.LarkError as e: if 0: - import traceback - traceback.print_exc() + shared.exception() return [[steps, prompt]] return [[t, at_step(t, tree)] for t in collect_steps(steps, tree)] diff --git a/modules/realesrgan_model.py b/modules/realesrgan_model.py index aad4a6298..7020f8791 100644 --- a/modules/realesrgan_model.py +++ b/modules/realesrgan_model.py @@ -1,6 +1,5 @@ import os import sys -import traceback import numpy as np from PIL import Image @@ -9,6 +8,7 @@ from realesrgan import RealESRGANer from modules.upscaler import Upscaler, UpscalerData from modules.shared import cmd_opts, opts +import modules.shared as shared class UpscalerRealESRGAN(Upscaler): @@ -29,7 +29,7 @@ class UpscalerRealESRGAN(Upscaler): except Exception: print("Error importing Real-ESRGAN:", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() self.enable = False self.scalers = [] @@ -68,7 +68,7 @@ class UpscalerRealESRGAN(Upscaler): return info except Exception as e: print(f"Error making Real-ESRGAN models list: {e}", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() return None def load_models(self, _): @@ -126,4 +126,4 @@ def get_realesrgan_models(scaler): return models except Exception as e: print("Error making Real-ESRGAN models list:", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() diff --git a/modules/safe.py b/modules/safe.py index 82d44be31..1e99e9eb9 100644 --- a/modules/safe.py +++ b/modules/safe.py @@ -4,7 +4,6 @@ import io import pickle import collections import sys -import traceback import torch import numpy @@ -136,16 +135,12 @@ def load_with_extra(filename, extra_handler=None, *args, **kwargs): except pickle.UnpicklingError: print(f"Error verifying pickled file from {filename}:", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) - print("-----> !!!! The file is most likely corrupted !!!! <-----", file=sys.stderr) - print("You can skip this check with --disable-safe-unpickle commandline argument, but that is not going to help you.\n\n", file=sys.stderr) + shared.exception() return None except Exception: print(f"Error verifying pickled file from {filename}:", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) - print("\nThe file may be malicious, so the program is not going to read it.", file=sys.stderr) - print("You can skip this check with --disable-safe-unpickle commandline argument.\n\n", file=sys.stderr) + shared.exception() return None return unsafe_torch_load(filename, *args, **kwargs) diff --git a/modules/script_callbacks.py b/modules/script_callbacks.py index 079118761..8a96839a4 100644 --- a/modules/script_callbacks.py +++ b/modules/script_callbacks.py @@ -1,7 +1,7 @@ import sys -import traceback from collections import namedtuple import inspect +import modules.shared as shared from typing import Optional, Dict, Any from fastapi import FastAPI @@ -10,7 +10,7 @@ from gradio import Blocks def report_exception(c, job): print(f"Error executing callback {job} for {c.script}", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() class ImageSaveParams: diff --git a/modules/script_loading.py b/modules/script_loading.py index a7d2203fc..e919d8c58 100644 --- a/modules/script_loading.py +++ b/modules/script_loading.py @@ -1,6 +1,6 @@ import os import sys -import traceback +import modules.shared as shared import importlib.util from types import ModuleType @@ -29,4 +29,4 @@ def preload_extensions(extensions_dir, parser): except Exception: print(f"Error running preload() for {preload_script}", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() diff --git a/modules/scripts.py b/modules/scripts.py index 8de19884b..99c6531f0 100644 --- a/modules/scripts.py +++ b/modules/scripts.py @@ -1,7 +1,6 @@ import os import re import sys -import traceback from collections import namedtuple import gradio as gr @@ -250,7 +249,8 @@ def load_scripts(): except Exception: print(f"Error loading script: {scriptfile.filename}", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() + finally: sys.path = syspath @@ -263,7 +263,7 @@ def wrap_call(func, filename, funcname, *args, default=None, **kwargs): return res except Exception: print(f"Error calling: {filename}/{funcname}", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() return default @@ -409,7 +409,7 @@ class ScriptRunner: script.process(p, *script_args) except Exception: print(f"Error running process: {script.filename}", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() def before_process_batch(self, p, **kwargs): for script in self.alwayson_scripts: @@ -418,7 +418,7 @@ class ScriptRunner: script.before_process_batch(p, *script_args, **kwargs) except Exception: print(f"Error running before_process_batch: {script.filename}", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() def process_batch(self, p, **kwargs): for script in self.alwayson_scripts: @@ -427,7 +427,7 @@ class ScriptRunner: script.process_batch(p, *script_args, **kwargs) except Exception: print(f"Error running process_batch: {script.filename}", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() def postprocess(self, p, processed): for script in self.alwayson_scripts: @@ -436,7 +436,7 @@ class ScriptRunner: script.postprocess(p, processed, *script_args) except Exception: print(f"Error running postprocess: {script.filename}", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() def postprocess_batch(self, p, images, **kwargs): for script in self.alwayson_scripts: @@ -445,7 +445,7 @@ class ScriptRunner: script.postprocess_batch(p, *script_args, images=images, **kwargs) except Exception: print(f"Error running postprocess_batch: {script.filename}", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() def postprocess_image(self, p, pp: PostprocessImageArgs): for script in self.alwayson_scripts: @@ -454,7 +454,7 @@ class ScriptRunner: script.postprocess_image(p, pp, *script_args) except Exception: print(f"Error running postprocess_batch: {script.filename}", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() def before_component(self, component, **kwargs): for script in self.scripts: @@ -462,7 +462,7 @@ class ScriptRunner: script.before_component(component, **kwargs) except Exception: print(f"Error running before_component: {script.filename}", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() def after_component(self, component, **kwargs): for script in self.scripts: @@ -470,7 +470,7 @@ class ScriptRunner: script.after_component(component, **kwargs) except Exception: print(f"Error running after_component: {script.filename}", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() def reload_sources(self, cache): for si, script in list(enumerate(self.scripts)): diff --git a/modules/sd_hijack.py b/modules/sd_hijack.py index f4bb0266f..4b80bae88 100644 --- a/modules/sd_hijack.py +++ b/modules/sd_hijack.py @@ -39,12 +39,7 @@ def apply_optimizations(): can_use_sdp = hasattr(torch.nn.functional, "scaled_dot_product_attention") and callable(getattr(torch.nn.functional, "scaled_dot_product_attention")) # not everyone has torch 2.x to use sdp - if cmd_opts.force_enable_xformers or (cmd_opts.xformers and shared.xformers_available and torch.version.cuda and (6, 0) <= torch.cuda.get_device_capability(shared.device) <= (9, 0)): - print("Applying xformers cross attention optimization.") - ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.xformers_attention_forward - ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.xformers_attnblock_forward - optimization_method = 'xformers' - elif cmd_opts.opt_sdp_no_mem_attention and can_use_sdp: + if cmd_opts.opt_sdp_no_mem_attention and can_use_sdp: print("Applying scaled dot product cross attention optimization (without memory efficient attention).") ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.scaled_dot_product_no_mem_attention_forward ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.sdp_no_mem_attnblock_forward @@ -54,6 +49,11 @@ def apply_optimizations(): ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.scaled_dot_product_attention_forward ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.sdp_attnblock_forward optimization_method = 'sdp' + elif cmd_opts.force_enable_xformers or (cmd_opts.xformers and shared.xformers_available and torch.version.cuda and (6, 0) <= torch.cuda.get_device_capability(shared.device) <= (9, 0)): + print("Applying xformers cross attention optimization.") + ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.xformers_attention_forward + ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.xformers_attnblock_forward + optimization_method = 'xformers' elif cmd_opts.opt_sub_quad_attention: print("Applying sub-quadratic cross attention optimization.") ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.sub_quad_attention_forward @@ -62,16 +62,16 @@ def apply_optimizations(): elif cmd_opts.opt_split_attention_v1: print("Applying v1 cross attention optimization.") ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.split_cross_attention_forward_v1 - optimization_method = 'V1' + optimization_method = 'v1' elif not cmd_opts.disable_opt_split_attention and (cmd_opts.opt_split_attention_invokeai or not cmd_opts.opt_split_attention and not torch.cuda.is_available()): print("Applying cross attention optimization (InvokeAI).") ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.split_cross_attention_forward_invokeAI - optimization_method = 'InvokeAI' + optimization_method = 'invokeai' elif not cmd_opts.disable_opt_split_attention and (cmd_opts.opt_split_attention or torch.cuda.is_available()): print("Applying cross attention optimization (Doggettx).") ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.split_cross_attention_forward ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.cross_attention_attnblock_forward - optimization_method = 'Doggettx' + optimization_method = 'doggettx' return optimization_method diff --git a/modules/sd_hijack_optimizations.py b/modules/sd_hijack_optimizations.py index 2e307b5d0..394e9619d 100644 --- a/modules/sd_hijack_optimizations.py +++ b/modules/sd_hijack_optimizations.py @@ -1,6 +1,5 @@ import math import sys -import traceback import psutil import torch @@ -20,8 +19,7 @@ if shared.cmd_opts.xformers or shared.cmd_opts.force_enable_xformers: import xformers.ops shared.xformers_available = True except Exception: - print("Cannot import xformers", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + pass def get_available_vram(): diff --git a/modules/shared.py b/modules/shared.py index 2d72e4d4d..bbf7624be 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -58,7 +58,7 @@ parser.add_argument("--esrgan-models-path", type=str, help="Path to directory wi parser.add_argument("--bsrgan-models-path", type=str, help="Path to directory with BSRGAN model file(s).", default=os.path.join(models_path, 'BSRGAN')) parser.add_argument("--realesrgan-models-path", type=str, help="Path to directory with RealESRGAN model file(s).", default=os.path.join(models_path, 'RealESRGAN')) parser.add_argument("--clip-models-path", type=str, help="Path to directory with CLIP model file(s).", default=None) -parser.add_argument("--xformers", action='store_true', help="enable xformers for cross attention layers") +parser.add_argument("--xformers", action='store_true', help="enable xformers for cross attention layers", default=True) parser.add_argument("--force-enable-xformers", action='store_true', help="enable xformers for cross attention layers regardless of whether the checking code thinks you can run it; do not make bug reports if this fails to work") parser.add_argument("--xformers-flash-attention", action='store_true', help="enable xformers with Flash Attention to improve reproducibility (supported for SD2.x or variant only)") parser.add_argument("--deepdanbooru", action='store_true', help="does not do anything") @@ -69,7 +69,7 @@ parser.add_argument("--sub-quad-kv-chunk-size", type=int, help="kv chunk size fo parser.add_argument("--sub-quad-chunk-threshold", type=int, help="the percentage of VRAM threshold for the sub-quadratic cross-attention layer optimization to use chunking", default=None) parser.add_argument("--opt-split-attention-invokeai", action='store_true', help="force-enables InvokeAI's cross-attention layer optimization. By default, it's on when cuda is unavailable.") parser.add_argument("--opt-split-attention-v1", action='store_true', help="enable older version of split attention optimization that does not consume all the VRAM it can find") -parser.add_argument("--opt-sdp-attention", action='store_true', help="enable scaled dot product cross-attention layer optimization; requires PyTorch 2.*") +parser.add_argument("--opt-sdp-attention", action='store_true', help="enable scaled dot product cross-attention layer optimization; requires PyTorch 2.*", default=True) parser.add_argument("--opt-sdp-no-mem-attention", action='store_true', help="enable scaled dot product cross-attention layer optimization without memory efficient attention, makes image generation deterministic; requires PyTorch 2.*") parser.add_argument("--disable-opt-split-attention", action='store_true', help="force-disables cross-attention layer optimization") parser.add_argument("--disable-nan-check", action='store_true', help="do not check if produced images/latent spaces have nans; useful for running without a checkpoint in CI", default=True) @@ -742,3 +742,16 @@ def html(filename): return file.read() return "" + +try: + from rich.console import Console + console = Console() +except: + console = None + import traceback + +def exception(): + if console is not None: + console.print_exception(show_locals=True, max_frames=10, extra_lines=1, suppress=[gr], word_wrap=False, width=min([console.width, 200])) + else: + print(traceback.format_exc(), file=sys.stderr) diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index 995da753e..841110ef4 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -1,6 +1,5 @@ import os import sys -import traceback import inspect from collections import namedtuple @@ -206,7 +205,7 @@ class EmbeddingDatabase: self.load_from_file(fullfn, fn) except Exception: print(f"Error loading embedding {fn}:", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() continue def load_textual_inversion_embeddings(self, force_reload=False): @@ -623,7 +622,7 @@ Last saved image: {html.escape(last_saved_image)}
filename = os.path.join(shared.cmd_opts.embeddings_dir, f'{embedding_name}.pt') save_embedding(embedding, optimizer, checkpoint, embedding_name, filename, remove_cached_checksum=True) except Exception: - print(traceback.format_exc(), file=sys.stderr) + shared.exception() pass finally: pbar.leave = False diff --git a/modules/ui.py b/modules/ui.py index fa5a87633..029d02a62 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -8,7 +8,6 @@ import random import sys import tempfile import time -import traceback from functools import partial, reduce import warnings @@ -1637,7 +1636,7 @@ def create_ui(): results = modules.extras.run_modelmerger(*args) except Exception as e: print("Error loading/saving model file:", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() modules.sd_models.list_models() # to remove the potentially missing models from the list return [*[gr.Dropdown.update(choices=modules.sd_models.checkpoint_tiles()) for _ in range(4)], f"Error merging checkpoints: {e}"] return results @@ -1681,7 +1680,7 @@ def create_ui(): except Exception: error_loading = True print("Error loading settings:", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() def loadsave(path, x): def apply_field(obj, field, condition=None, init_field=None): diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py index 14e29f832..73674af59 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -3,7 +3,6 @@ import os.path import shutil import sys import time -import traceback import git @@ -41,7 +40,7 @@ def apply_and_restart(disable_list, update_list): ext.fetch_and_reset_hard() except Exception: print(f"Error getting updates for {ext.name}:", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() shared.opts.disabled_extensions = disabled shared.opts.save(shared.config_filename) @@ -66,7 +65,7 @@ def check_updates(id_task, disable_list): ext.check_updates() except Exception: print(f"Error checking updates for {ext.name}:", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() shared.state.nextjob() diff --git a/scripts/prompts_from_file.py b/scripts/prompts_from_file.py index 76dc5778b..d5f029362 100644 --- a/scripts/prompts_from_file.py +++ b/scripts/prompts_from_file.py @@ -3,7 +3,7 @@ import math import os import random import sys -import traceback +import modules.shared as shared import shlex import modules.scripts as scripts @@ -141,7 +141,7 @@ class Script(scripts.Script): args = cmdargs(line) except Exception: print(f"Error parsing line {line} as commandline:", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + shared.exception() args = {"prompt": line} else: args = {"prompt": line} diff --git a/webui.py b/webui.py index de6accfe4..36ea1b808 100644 --- a/webui.py +++ b/webui.py @@ -64,40 +64,7 @@ else: server_name = "0.0.0.0" if cmd_opts.listen else None -def check_versions(): - if shared.cmd_opts.skip_version_check: - return - - expected_torch_version = "1.13.1" - - if version.parse(torch.__version__) < version.parse(expected_torch_version): - errors.print_error_explanation(f""" -You are running torch {torch.__version__}. -The program is tested to work with torch {expected_torch_version}. -To reinstall the desired version, run with commandline flag --reinstall-torch. -Beware that this will cause a lot of large files to be downloaded, as well as -there are reports of issues with training tab on the latest version. - -Use --skip-version-check commandline argument to disable this check. - """.strip()) - - expected_xformers_version = "0.0.16rc425" - if shared.xformers_available: - import xformers - - if version.parse(xformers.__version__) < version.parse(expected_xformers_version): - errors.print_error_explanation(f""" -You are running xformers {xformers.__version__}. -The program is tested to work with xformers {expected_xformers_version}. -To reinstall the desired version, run with commandline flag --reinstall-xformers. - -Use --skip-version-check commandline argument to disable this check. - """.strip()) - - def initialize(): - check_versions() - extensions.list_extensions() startup_timer.record("list extensions")