diff --git a/TODO.md b/TODO.md index 53ede1a74..07a86e948 100644 --- a/TODO.md +++ b/TODO.md @@ -15,6 +15,13 @@ Stuff to be fixed... - Support mupliple folders for models - Add compatibility for extensions using removed `shared.cmd_opts` - Revisit `torch.compile` +- Ask to download default model +- Support UI restart on-the-fly +- Re-add seed buttons +- Stream-load as option +- Verify env variables +- Configurable LORA directory +- Dont spawn extensions installer ## Integration diff --git a/extensions-builtin/sd-extension-system-info b/extensions-builtin/sd-extension-system-info index f3499ddd8..be495d02e 160000 --- a/extensions-builtin/sd-extension-system-info +++ b/extensions-builtin/sd-extension-system-info @@ -1 +1 @@ -Subproject commit f3499ddd8bebd4a743b8f63e58cce934b04cb8d2 +Subproject commit be495d02ef8eac8c16542080426ba74c3a42839e diff --git a/extensions-builtin/sd-webui-controlnet b/extensions-builtin/sd-webui-controlnet index 0f549888f..e5b565e27 160000 --- a/extensions-builtin/sd-webui-controlnet +++ b/extensions-builtin/sd-webui-controlnet @@ -1 +1 @@ -Subproject commit 0f549888fd49aea48a4a5049f75c2e87ad3affad +Subproject commit e5b565e27f0fdda34d1ccbdbe4d6ab07d3d2fc67 diff --git a/extensions-builtin/stable-diffusion-webui-images-browser b/extensions-builtin/stable-diffusion-webui-images-browser index 57040c311..0029d95a5 160000 --- a/extensions-builtin/stable-diffusion-webui-images-browser +++ b/extensions-builtin/stable-diffusion-webui-images-browser @@ -1 +1 @@ -Subproject commit 57040c311fc540bca9ca9d5ada2a9dd521f03f95 +Subproject commit 0029d95a5f720b387c530e916a9e166277aa66e2 diff --git a/modules/api/api.py b/modules/api/api.py index a5a864e25..cb4cee07f 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -14,8 +14,7 @@ from fastapi.responses import JSONResponse from fastapi.encoders import jsonable_encoder from secrets import compare_digest -import modules.shared as shared -from modules import sd_samplers, deepbooru, sd_hijack, images, scripts, ui, postprocessing +from modules import shared, errors, sd_samplers, deepbooru, sd_hijack, images, scripts, ui, postprocessing from modules.api.models import * from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images from modules.textual_inversion.textual_inversion import create_embedding, train_embedding @@ -94,15 +93,6 @@ def encode_pil_to_base64(image): return base64.b64encode(bytes_data) def api_middleware(app: FastAPI): - rich_available = True - try: - import anyio # importing just so it can be placed on silent list - import starlette # importing just so it can be placed on silent list - from rich.console import Console - console = Console() - except: - import traceback - rich_available = False @app.middleware("http") async def log_and_time(req: Request, call_next): @@ -133,10 +123,7 @@ def api_middleware(app: FastAPI): } print(f"API error: {request.method}: {request.url} {err}") if not isinstance(e, HTTPException): # do not print backtrace on known httpexceptions - if rich_available: - console.print_exception(show_locals=False, max_frames=2, extra_lines=1, suppress=[anyio, starlette], word_wrap=False, width=min([console.width, 200])) - else: - traceback.print_exc() + errors.display(e, 'http api') return JSONResponse(status_code=vars(e).get('status_code', 500), content=jsonable_encoder(err)) @app.middleware("http") diff --git a/modules/call_queue.py b/modules/call_queue.py index 17f247567..833a0bb2f 100644 --- a/modules/call_queue.py +++ b/modules/call_queue.py @@ -1,10 +1,9 @@ import html -import sys import threading import time import cProfile, pstats, io -from modules import shared, progress +from modules import shared, progress, errors queue_lock = threading.Lock() @@ -65,22 +64,11 @@ def wrap_gradio_call(func, extra_outputs=None, add_stats=False): ps.print_stats(15) print('Profile:', s.getvalue()) except Exception as e: - # When printing out our debug argument list, do not print out more than a MB of text - max_debug_str_len = 131072 # (1024*1024)/8 - - print("Error completing request", file=sys.stderr) - argStr = f"Arguments: {str(args)} {str(kwargs)}" - print(argStr[:max_debug_str_len], file=sys.stderr) - if len(argStr) > max_debug_str_len: - print(f"(Argument list truncated at {max_debug_str_len}/{len(argStr)} characters)", file=sys.stderr) - - shared.exception() + errors.display(e, 'gradio call') shared.state.job = "" shared.state.job_count = 0 - if extra_outputs_array is None: extra_outputs_array = [None, ''] - res = extra_outputs_array + [f"
{html.escape(type(e).__name__+': '+str(e))}
"] shared.state.skipped = False diff --git a/modules/codeformer_model.py b/modules/codeformer_model.py index e9c668b61..c33f12773 100644 --- a/modules/codeformer_model.py +++ b/modules/codeformer_model.py @@ -6,7 +6,7 @@ import torch import modules.face_restoration import modules.shared -from modules import shared, devices, modelloader +from modules import shared, devices, modelloader, errors from modules.paths import models_path # codeformer people made a choice to include modified basicsr library to their project which makes @@ -135,8 +135,7 @@ def setup_model(dirname): codeformer = FaceRestorerCodeFormer(dirname) shared.face_restorers.append(codeformer) - except Exception: - print("Error setting up CodeFormer:", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, 'codeformer') # sys.path = stored_sys_path diff --git a/modules/errors.py b/modules/errors.py index 017269174..dba20b6e4 100644 --- a/modules/errors.py +++ b/modules/errors.py @@ -1,7 +1,15 @@ import sys -import traceback +import anyio +import starlette +import gradio +from rich import print from rich.console import Console +from rich.pretty import install as pretty_install +from rich.traceback import install as traceback_install +console = Console(log_time=True, log_time_format='%H:%M:%S-%f') +pretty_install(console=console) +traceback_install(console=console, extra_lines=1, width=console.width, word_wrap=False, indent_guides=False, suppress=[anyio, starlette, gradio]) already_displayed = {} @@ -16,8 +24,7 @@ def print_error_explanation(message): def display(e: Exception, task): print(f"{task or 'error'}: {type(e).__name__}", file=sys.stderr) - console = Console() - console.print_exception(show_locals=False, max_frames=2, extra_lines=1, suppress=[], word_wrap=False, width=min([console.width, 200])) + console.print_exception(show_locals=False, max_frames=2, extra_lines=1, suppress=[anyio, starlette, gradio], word_wrap=False, width=min([console.width, 200])) def display_once(e: Exception, task): @@ -32,3 +39,7 @@ def run(code, task): code() except Exception as e: display(task, e) + + +def exception(): + console.print_exception(show_locals=False, max_frames=10, extra_lines=2, suppress=[anyio, starlette, gradio], word_wrap=False, width=min([console.width, 200])) diff --git a/modules/extensions.py b/modules/extensions.py index 2ad7f66e2..ea9d8a937 100644 --- a/modules/extensions.py +++ b/modules/extensions.py @@ -4,7 +4,7 @@ import sys import time import git -from modules import shared +from modules import shared, errors from modules.paths_internal import extensions_dir, extensions_builtin_dir extensions = [] @@ -44,9 +44,8 @@ class Extension: try: if os.path.exists(os.path.join(self.path, ".git")): repo = git.Repo(self.path) - except Exception: - print(f"Error reading github repository info from {self.path}:", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, f'github info from {self.path}') if repo is None or repo.bare: self.remote = None @@ -101,13 +100,12 @@ def list_extensions(): if not os.path.isdir(extensions_dir): return - if shared.opts.disable_all_extensions == "all": - print("*** \"Disable all extensions\" option was set, will not load any extensions ***") - elif shared.opts.disable_all_extensions == "extra": - print("*** \"Disable all extensions\" option was set, will only load built-in extensions ***") + if shared.opts.disable_all_extensions == "all" or shared.opts.disable_all_extensions == "extra": + shared.log.warning("Option set: Disable all extensions") extension_paths = [] - for dirname in [extensions_dir, extensions_builtin_dir]: + extension_names = [] + for dirname in [extensions_builtin_dir, extensions_dir]: if not os.path.isdir(dirname): return @@ -115,7 +113,10 @@ def list_extensions(): path = os.path.join(dirname, extension_dirname) if not os.path.isdir(path): continue - + if extension_dirname in extension_names: + shared.log.info(f'Skipping conflicting extension: {path}') + continue + extension_names.append(extension_dirname) extension_paths.append((extension_dirname, path, dirname == extensions_builtin_dir)) for dirname, path, is_builtin in extension_paths: diff --git a/modules/gfpgan_model.py b/modules/gfpgan_model.py index 14adfa32f..e68b50dbd 100644 --- a/modules/gfpgan_model.py +++ b/modules/gfpgan_model.py @@ -2,7 +2,7 @@ import os import sys import modules.face_restoration -from modules import paths, shared, devices, modelloader +from modules import paths, shared, devices, modelloader, errors model_dir = "GFPGAN" user_path = None @@ -110,6 +110,5 @@ def setup_model(dirname): return gfpgan_fix_faces(np_image) shared.face_restorers.append(FaceRestorerGFPGAN()) - except Exception: - print("Error setting up GFPGAN:", file=sys.stderr) - shared.exception() + except Exception as e: + errors.exception(e, 'gfpgan') diff --git a/modules/hypernetworks/hypernetwork.py b/modules/hypernetworks/hypernetwork.py index 1ac29c8f1..4aa5ffcdc 100644 --- a/modules/hypernetworks/hypernetwork.py +++ b/modules/hypernetworks/hypernetwork.py @@ -11,7 +11,7 @@ import torch import tqdm from einops import rearrange, repeat from ldm.util import default -from modules import devices, processing, sd_models, shared, sd_samplers, hashes, sd_hijack_checkpoint +from modules import devices, processing, sd_models, shared, sd_samplers, hashes, sd_hijack_checkpoint, errors from modules.textual_inversion import textual_inversion, logging from modules.textual_inversion.learn_schedule import LearnRateScheduler from torch import einsum @@ -329,9 +329,8 @@ def load_hypernetwork(name): try: hypernetwork.load(path) - except Exception: - print(f"Error loading hypernetwork {path}", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, f'hypernetwork load: {path}') return None return hypernetwork @@ -769,8 +768,8 @@ Last saved hypernetwork: {html.escape(last_saved_file)}
Last saved image: {html.escape(last_saved_image)}

""" - except Exception: - shared.exception() + except Exception as e: + errors.display(e, 'hypernetwork train') finally: pbar.leave = False pbar.close() diff --git a/modules/images.py b/modules/images.py index 6fab5ba9f..046ae37c2 100644 --- a/modules/images.py +++ b/modules/images.py @@ -419,10 +419,9 @@ class FilenameGenerator: if fun is not None: try: replacement = fun(self, *pattern_args) - except Exception: + except Exception as e: replacement = None - print(f"Error adding [{pattern}] to filename", file=sys.stderr) - shared.exception() + errors.display(e, 'filename pattern') if replacement is not None: res += str(replacement) @@ -651,9 +650,8 @@ def read_info_from_image(image): geninfo = f"""{items["Description"]} 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) - shared.exception() + except Exception as e: + errors.display(e, 'novelai image parser') return geninfo, items diff --git a/modules/interrogate.py b/modules/interrogate.py index 80b6122f8..66ae32e85 100644 --- a/modules/interrogate.py +++ b/modules/interrogate.py @@ -215,9 +215,8 @@ class InterrogateModels: else: res += ", " + match - except Exception: - print("Error interrogating", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, 'interrogate') res += "" self.unload() diff --git a/modules/localization.py b/modules/localization.py index 368cecd58..9aed5a971 100644 --- a/modules/localization.py +++ b/modules/localization.py @@ -2,6 +2,7 @@ import json import os import sys import modules.shared as shared +import modules.errors as errors localizations = {} @@ -31,8 +32,8 @@ def localization_js(current_localization_name): try: with open(fn, "r", encoding="utf8") as file: data = json.load(file) - except Exception: + except Exception as e: print(f"Error loading localization from {fn}:", file=sys.stderr) - shared.exception() + errors.display(e, 'localization') return f"var localization = {json.dumps(data)}\n" diff --git a/modules/lora b/modules/lora index 7ad7cac0c..6d5f847ed 160000 --- a/modules/lora +++ b/modules/lora @@ -1 +1 @@ -Subproject commit 7ad7cac0c272211abb688168d4c2d4400bef7c51 +Subproject commit 6d5f847edc7eaf858aa6fe060c68875b8da7392a diff --git a/modules/paths.py b/modules/paths.py index 0e1e00e72..4a3073295 100644 --- a/modules/paths.py +++ b/modules/paths.py @@ -34,10 +34,11 @@ for d, must_exist, what, options in path_dirs: print(f"Warning: {what} not found at path {must_exist_path}", file=sys.stderr) else: d = os.path.abspath(d) - if "atstart" in options: - sys.path.insert(0, d) - else: - sys.path.append(d) + # if "atstart" in options: + # sys.path.insert(0, d) + # else: + # sys.path.append(d) + sys.path.append(d) paths[what] = d diff --git a/modules/realesrgan_model.py b/modules/realesrgan_model.py index f919d5d2a..a69f22a74 100644 --- a/modules/realesrgan_model.py +++ b/modules/realesrgan_model.py @@ -8,6 +8,7 @@ from basicsr.utils.download_util import load_file_from_url from modules.upscaler import Upscaler, UpscalerData from modules.shared import cmd_opts, opts import modules.shared as shared +import modules.errors as errors class UpscalerRealESRGAN(Upscaler): @@ -26,9 +27,8 @@ class UpscalerRealESRGAN(Upscaler): if scaler.name in opts.realesrgan_enabled_models: self.scalers.append(scaler) - except Exception: - print("Error importing Real-ESRGAN:", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, 'real-esrgan') self.enable = False self.scalers = [] @@ -72,8 +72,7 @@ class UpscalerRealESRGAN(Upscaler): info.local_data_path = load_file_from_url(url=info.data_path, model_dir=self.model_path, progress=True) return info except Exception as e: - print(f"Error making Real-ESRGAN models list: {e}", file=sys.stderr) - shared.exception() + errors.display(e, 'real-esrgan model list') return None def load_models(self, _): diff --git a/modules/safe.py b/modules/safe.py index 1e99e9eb9..775f3699d 100644 --- a/modules/safe.py +++ b/modules/safe.py @@ -11,7 +11,6 @@ import _codecs import zipfile import re - # PyTorch 1.13 and later have _TypedStorage renamed to TypedStorage TypedStorage = torch.storage.TypedStorage if hasattr(torch.storage, 'TypedStorage') else torch.storage._TypedStorage @@ -127,20 +126,13 @@ def load_with_extra(filename, extra_handler=None, *args, **kwargs): definitely unsafe. """ - from modules import shared + from modules import shared, errors try: if not shared.cmd_opts.disable_safe_unpickle: check_pt(filename, extra_handler) - - except pickle.UnpicklingError: - print(f"Error verifying pickled file from {filename}:", file=sys.stderr) - shared.exception() - return None - - except Exception: - print(f"Error verifying pickled file from {filename}:", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, f'verifying pickled file {filename}') return None return unsafe_torch_load(filename, *args, **kwargs) diff --git a/modules/script_callbacks.py b/modules/script_callbacks.py index 8a96839a4..a339614a8 100644 --- a/modules/script_callbacks.py +++ b/modules/script_callbacks.py @@ -2,15 +2,15 @@ import sys from collections import namedtuple import inspect import modules.shared as shared +import modules.errors as errors from typing import Optional, Dict, Any from fastapi import FastAPI from gradio import Blocks -def report_exception(c, job): - print(f"Error executing callback {job} for {c.script}", file=sys.stderr) - shared.exception() +def report_exception(e, c, job): + errors.display(e, f'executing callback: {c.script} {job}') class ImageSaveParams: @@ -105,16 +105,16 @@ def app_started_callback(demo: Optional[Blocks], app: FastAPI): for c in callback_map['callbacks_app_started']: try: c.callback(demo, app) - except Exception: - report_exception(c, 'app_started_callback') + except Exception as e: + report_exception(e, c, 'app_started_callback') def model_loaded_callback(sd_model): for c in callback_map['callbacks_model_loaded']: try: c.callback(sd_model) - except Exception: - report_exception(c, 'model_loaded_callback') + except Exception as e: + report_exception(e, c, 'model_loaded_callback') def ui_tabs_callback(): @@ -123,8 +123,8 @@ def ui_tabs_callback(): for c in callback_map['callbacks_ui_tabs']: try: res += c.callback() or [] - except Exception: - report_exception(c, 'ui_tabs_callback') + except Exception as e: + report_exception(e, c, 'ui_tabs_callback') return res @@ -133,96 +133,96 @@ def ui_train_tabs_callback(params: UiTrainTabParams): for c in callback_map['callbacks_ui_train_tabs']: try: c.callback(params) - except Exception: - report_exception(c, 'callbacks_ui_train_tabs') + except Exception as e: + report_exception(e, c, 'callbacks_ui_train_tabs') def ui_settings_callback(): for c in callback_map['callbacks_ui_settings']: try: c.callback() - except Exception: - report_exception(c, 'ui_settings_callback') + except Exception as e: + report_exception(e, c, 'ui_settings_callback') def before_image_saved_callback(params: ImageSaveParams): for c in callback_map['callbacks_before_image_saved']: try: c.callback(params) - except Exception: - report_exception(c, 'before_image_saved_callback') + except Exception as e: + report_exception(e, c, 'before_image_saved_callback') def image_saved_callback(params: ImageSaveParams): for c in callback_map['callbacks_image_saved']: try: c.callback(params) - except Exception: - report_exception(c, 'image_saved_callback') + except Exception as e: + report_exception(e, c, 'image_saved_callback') def cfg_denoiser_callback(params: CFGDenoiserParams): for c in callback_map['callbacks_cfg_denoiser']: try: c.callback(params) - except Exception: - report_exception(c, 'cfg_denoiser_callback') + except Exception as e: + report_exception(e, c, 'cfg_denoiser_callback') def cfg_denoised_callback(params: CFGDenoisedParams): for c in callback_map['callbacks_cfg_denoised']: try: c.callback(params) - except Exception: - report_exception(c, 'cfg_denoised_callback') + except Exception as e: + report_exception(e, c, 'cfg_denoised_callback') def before_component_callback(component, **kwargs): for c in callback_map['callbacks_before_component']: try: c.callback(component, **kwargs) - except Exception: - report_exception(c, 'before_component_callback') + except Exception as e: + report_exception(e, c, 'before_component_callback') def after_component_callback(component, **kwargs): for c in callback_map['callbacks_after_component']: try: c.callback(component, **kwargs) - except Exception: - report_exception(c, 'after_component_callback') + except Exception as e: + report_exception(e, c, 'after_component_callback') def image_grid_callback(params: ImageGridLoopParams): for c in callback_map['callbacks_image_grid']: try: c.callback(params) - except Exception: - report_exception(c, 'image_grid') + except Exception as e: + report_exception(e, c, 'image_grid') def infotext_pasted_callback(infotext: str, params: Dict[str, Any]): for c in callback_map['callbacks_infotext_pasted']: try: c.callback(infotext, params) - except Exception: - report_exception(c, 'infotext_pasted') + except Exception as e: + report_exception(e, c, 'infotext_pasted') def script_unloaded_callback(): for c in reversed(callback_map['callbacks_script_unloaded']): try: c.callback() - except Exception: - report_exception(c, 'script_unloaded') + except Exception as e: + report_exception(e, c, 'script_unloaded') def before_ui_callback(): for c in reversed(callback_map['callbacks_before_ui']): try: c.callback() - except Exception: - report_exception(c, 'before_ui') + except Exception as e: + report_exception(e, c, 'before_ui') def add_callback(callbacks, fun): diff --git a/modules/script_loading.py b/modules/script_loading.py index e919d8c58..cbfb214c2 100644 --- a/modules/script_loading.py +++ b/modules/script_loading.py @@ -1,6 +1,7 @@ import os import sys import modules.shared as shared +import modules.errors as errors import importlib.util from types import ModuleType @@ -27,6 +28,5 @@ def preload_extensions(extensions_dir, parser): if hasattr(module, 'preload'): module.preload(parser) - except Exception: - print(f"Error running preload() for {preload_script}", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, f'extension preload: {preload_script}') diff --git a/modules/scripts.py b/modules/scripts.py index 233fa83df..06235344b 100644 --- a/modules/scripts.py +++ b/modules/scripts.py @@ -5,7 +5,7 @@ from collections import namedtuple import gradio as gr -from modules import shared, paths, script_callbacks, extensions, script_loading, scripts_postprocessing +from modules import shared, paths, script_callbacks, extensions, script_loading, scripts_postprocessing, errors AlwaysVisible = object() @@ -255,9 +255,8 @@ def load_scripts(): script_module = script_loading.load_module(scriptfile.path) register_scripts_from_module(script_module) - except Exception: - print(f"Error loading script: {scriptfile.filename}", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, f'loading script: {scriptfile.filename}') finally: @@ -269,9 +268,8 @@ def wrap_call(func, filename, funcname, *args, default=None, **kwargs): try: res = func(*args, **kwargs) return res - except Exception: - print(f"Error calling: {filename}/{funcname}", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, f'calling script: {filename}/{funcname}') return default @@ -415,70 +413,62 @@ class ScriptRunner: try: script_args = p.script_args[script.args_from:script.args_to] script.process(p, *script_args) - except Exception: - print(f"Error running process: {script.filename}", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, f'running script process: {script.filename}') def before_process_batch(self, p, **kwargs): for script in self.alwayson_scripts: try: script_args = p.script_args[script.args_from:script.args_to] script.before_process_batch(p, *script_args, **kwargs) - except Exception: - print(f"Error running before_process_batch: {script.filename}", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, f'running script before process batch: {script.filename}') def process_batch(self, p, **kwargs): for script in self.alwayson_scripts: try: script_args = p.script_args[script.args_from:script.args_to] script.process_batch(p, *script_args, **kwargs) - except Exception: - print(f"Error running process_batch: {script.filename}", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, f'running script process batch: {script.filename}') def postprocess(self, p, processed): for script in self.alwayson_scripts: try: script_args = p.script_args[script.args_from:script.args_to] script.postprocess(p, processed, *script_args) - except Exception: - print(f"Error running postprocess: {script.filename}", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, f'running script postprocess: {script.filename}') def postprocess_batch(self, p, images, **kwargs): for script in self.alwayson_scripts: try: script_args = p.script_args[script.args_from:script.args_to] script.postprocess_batch(p, *script_args, images=images, **kwargs) - except Exception: - print(f"Error running postprocess_batch: {script.filename}", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, f'running script before postprocess batch: {script.filename}') def postprocess_image(self, p, pp: PostprocessImageArgs): for script in self.alwayson_scripts: try: script_args = p.script_args[script.args_from:script.args_to] script.postprocess_image(p, pp, *script_args) - except Exception: - print(f"Error running postprocess_batch: {script.filename}", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, f'running script postprocess image: {script.filename}') def before_component(self, component, **kwargs): for script in self.scripts: try: script.before_component(component, **kwargs) - except Exception: - print(f"Error running before_component: {script.filename}", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, f'running script before component: {script.filename}') def after_component(self, component, **kwargs): for script in self.scripts: try: script.after_component(component, **kwargs) - except Exception: - print(f"Error running after_component: {script.filename}", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, f'running script after component: {script.filename}') def reload_sources(self, cache): for si, script in list(enumerate(self.scripts)): diff --git a/modules/sd_models.py b/modules/sd_models.py index 8ce39868f..97202c8f5 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -120,7 +120,6 @@ def list_models(): if os.path.exists(cmd_ckpt): checkpoint_info = CheckpointInfo(cmd_ckpt) checkpoint_info.register() - shared.opts.data['sd_model_checkpoint'] = checkpoint_info.title elif cmd_ckpt is not None and cmd_ckpt != shared.default_sd_model_file: print("Checkpoint in --ckpt argument not found", file=sys.stderr) @@ -129,7 +128,7 @@ def list_models(): checkpoint_info = CheckpointInfo(filename) checkpoint_info.register() - print('Available models:', len(checkpoints_list)) + shared.log.info(f'Available models: {shared.opts.ckpt_dir} {len(checkpoints_list)}') def get_closet_checkpoint_match(search_string): @@ -257,9 +256,7 @@ def read_state_dict(checkpoint_file): pl_sd = torch.load(buffer, map_location='cpu') sd = get_state_dict_from_checkpoint(pl_sd) except Exception as e: - from rich.console import Console - console = Console() - console.print_exception(show_locals=False, max_frames=2, extra_lines=1, suppress=[], word_wrap=False, width=min([console.width, 200])) + errors.display(e, f'loading model: {checkpoint_file}') sd = None return sd diff --git a/modules/shared.py b/modules/shared.py index 35e7d1427..d241e31f1 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -5,31 +5,23 @@ import os import sys import time +from setup import log as setup_log + import gradio as gr import tqdm -from rich import print import modules.interrogate import modules.memmon import modules.styles import modules.devices as devices -from modules import script_loading, errors, ui_components, shared_items, cmd_args +from modules import script_loading, errors, ui_components, shared_items, cmd_args, errors from modules.paths_internal import models_path, script_path, data_path, sd_configs_path, sd_default_config, sd_model_file, default_sd_model_file, extensions_dir, extensions_builtin_dir demo = None +log = setup_log parser = cmd_args.parser -try: - from rich.pretty import install as pretty_install - from rich.traceback import install as traceback_install - from rich.console import Console - console = Console(log_time=True, log_time_format='%H:%M:%S-%f') - pretty_install(console=console) - traceback_install(console=console, extra_lines=1, width=console.width, word_wrap=False, indent_guides=False, show_locals=False, max_frames=2) -except: - console = None - script_loading.preload_extensions(extensions_dir, parser) script_loading.preload_extensions(extensions_builtin_dir, parser) @@ -522,11 +514,11 @@ class Options: for k, v in self.data.items(): info = self.data_labels.get(k, None) if info is not None and not self.same_type(info.default, v): - print(f"Warning: bad setting value: {k}: {v} ({type(v).__name__}; expected {type(info.default).__name__})", file=sys.stderr) + log.error(f"Warning: bad setting value: {k}: {v} ({type(v).__name__}; expected {type(info.default).__name__})", file=sys.stderr) bad_settings += 1 if bad_settings > 0: - print(f"The program is likely to not work with bad settings.\nSettings file: {filename}\nEither fix the file, or delete it and restart.", file=sys.stderr) + log.error(f"The program is likely to not work with bad settings.\nSettings file: {filename}\nEither fix the file, or delete it and restart.", file=sys.stderr) def onchange(self, key, func, call=True): item = self.data_labels.get(key) @@ -663,10 +655,3 @@ def html(filename): return file.read() return "" - -def exception(): - if console is not None: - console.print_exception(show_locals=False, max_frames=10, extra_lines=2, suppress=[gr], word_wrap=False, width=min([console.width, 200])) - else: - import traceback - print(traceback.format_exc(), file=sys.stderr) diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index 685f33f8d..dd3a4fc08 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -14,7 +14,7 @@ import numpy as np from PIL import Image, PngImagePlugin from torch.utils.tensorboard import SummaryWriter -from modules import shared, devices, sd_hijack, processing, sd_models, images, sd_samplers, sd_hijack_checkpoint +from modules import shared, devices, sd_hijack, processing, sd_models, images, sd_samplers, sd_hijack_checkpoint, errors import modules.textual_inversion.dataset from modules.textual_inversion.learn_schedule import LearnRateScheduler @@ -207,9 +207,8 @@ class EmbeddingDatabase: continue self.load_from_file(fullfn, fn) - except Exception: - print(f"Error loading embedding {fn}:", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, f'embedding load {fn}') continue def load_textual_inversion_embeddings(self, force_reload=False): @@ -625,8 +624,8 @@ Last saved image: {html.escape(last_saved_image)}
""" filename = os.path.join(shared.opts.embeddings_dir, f'{embedding_name}.pt') save_embedding(embedding, optimizer, checkpoint, embedding_name, filename, remove_cached_checksum=True) - except Exception: - shared.exception() + except Exception as e: + errors.display(e, 'embedding train') pass finally: pbar.leave = False diff --git a/modules/ui.py b/modules/ui.py index db3c36515..f8a8adfe9 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 @@ -31,6 +30,7 @@ import modules.gfpgan_model import modules.hypernetworks.ui import modules.scripts import modules.shared as shared +import modules.errors as errors import modules.styles import modules.textual_inversion.ui from modules import prompt_parser @@ -1581,8 +1581,7 @@ def create_ui(): try: 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) + errors.display(e, 'model merge') 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 @@ -1623,10 +1622,9 @@ def create_ui(): if os.path.exists(ui_config_file): with open(ui_config_file, "r", encoding="utf8") as file: ui_settings = json.load(file) - except Exception: + except Exception as e: error_loading = True - print("Error loading settings:", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) + errors.display(e, 'loading ui settings') 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 ca92ccee0..74770bc97 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -10,7 +10,7 @@ import html import shutil import errno -from modules import extensions, shared, paths +from modules import extensions, shared, paths, errors from modules.call_queue import wrap_gradio_gpu_call available_extensions = {"extensions": []} @@ -37,9 +37,8 @@ def apply_and_restart(disable_list, update_list, disable_all): try: ext.fetch_and_reset_hard() - except Exception: - print(f"Error getting updates for {ext.name}:", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, f'extensions apply update: {ext.name}') shared.opts.disabled_extensions = disabled shared.opts.disable_all_extensions = disable_all @@ -67,8 +66,7 @@ def check_updates(id_task, disable_list): if 'FETCH_HEAD' not in str(e): raise except Exception: - print(f"Error checking updates for {ext.name}:", file=sys.stderr) - shared.exception() + errors.display(e, f'extensions check update: {ext.name}') shared.state.nextjob() diff --git a/scripts/prompts_from_file.py b/scripts/prompts_from_file.py index d5f029362..e6d5c5a39 100644 --- a/scripts/prompts_from_file.py +++ b/scripts/prompts_from_file.py @@ -9,7 +9,7 @@ import shlex import modules.scripts as scripts import gradio as gr -from modules import sd_samplers +from modules import sd_samplers, errors from modules.processing import Processed, process_images from PIL import Image from modules.shared import opts, cmd_opts, state @@ -139,9 +139,8 @@ class Script(scripts.Script): if "--" in line: try: args = cmdargs(line) - except Exception: - print(f"Error parsing line {line} as commandline:", file=sys.stderr) - shared.exception() + except Exception as e: + errors.display(e, f'parsing prompts: {line}') args = {"prompt": line} else: args = {"prompt": line} diff --git a/setup.py b/setup.py index cc478ccd8..9a125a7c3 100644 --- a/setup.py +++ b/setup.py @@ -231,11 +231,12 @@ def run_extension_installer(extension_dir): # run installer for each installed and enabled extension and optionally update them def install_extensions(): + settings = {} + if os.path.isfile('config.json'): + with open('config.json', "r", encoding="utf8") as file: + settings = json.load(file) + def list_extensions(dir): - settings = {} - if os.path.isfile('config.json'): - with open('config.json', "r", encoding="utf8") as file: - settings = json.load(file) if settings.get('disable_all_extensions', 'none') != 'none': log.debug(f'Disabled extensions: all') return [] @@ -247,7 +248,8 @@ def install_extensions(): extensions_builtin_dir = os.path.join(os.path.dirname(__file__), 'extensions-builtin') extensions = list_extensions(extensions_builtin_dir) - log.info(f'Built-in extensions: {extensions}') + log.info(f'Extensions disabled: {settings.get("disabled_extensions", [])}') + log.info(f'Extensions built-in: {extensions}') for ext in extensions: if not args.noupdate: update(os.path.join(extensions_builtin_dir, ext)) @@ -256,7 +258,7 @@ def install_extensions(): extensions_dir = os.path.join(os.path.dirname(__file__), 'extensions') extensions = list_extensions(extensions_dir) - log.info(f'Enabled extensions: {extensions}') + log.info(f'Extensions enabled: {extensions}') for ext in extensions: if not args.noupdate: update(os.path.join(extensions_dir, ext))