diff --git a/installer.py b/installer.py index 2e13c515f..0b3a9de6b 100644 --- a/installer.py +++ b/installer.py @@ -705,7 +705,7 @@ def extensions_preload(force = False): from modules.paths_internal import extensions_builtin_dir, extensions_dir extension_folders = [extensions_builtin_dir] if args.safe else [extensions_builtin_dir, extensions_dir] for ext_dir in extension_folders: - preload_extensions(ext_dir, parser, args.debug) + preload_extensions(ext_dir, parser) except: log.error('Error running extension preloading') if args.profile: diff --git a/modules/deepbooru_model.py b/modules/deepbooru_model.py index c2c77cd25..33e24396d 100644 --- a/modules/deepbooru_model.py +++ b/modules/deepbooru_model.py @@ -671,7 +671,7 @@ class DeepDanbooruModel(nn.Module): t_771 = torch.sigmoid(t_770) return t_771 - def load_state_dict(self, state_dict, **kwargs): + def load_state_dict(self, state_dict, **kwargs): # pylint: disable=arguments-differ,unused-argument self.tags = state_dict.get('tags', []) super(DeepDanbooruModel, self).load_state_dict({k: v for k, v in state_dict.items() if k != 'tags'}) diff --git a/modules/esrgan_model.py b/modules/esrgan_model.py index f2565ca47..b685711ef 100644 --- a/modules/esrgan_model.py +++ b/modules/esrgan_model.py @@ -17,7 +17,7 @@ def mod2normal(state_dict): if 'conv_first.weight' in state_dict: crt_net = {} items = [] - for k, v in state_dict.items(): + for k, _v in state_dict.items(): items.append(k) crt_net['model.0.weight'] = state_dict['conv_first.weight'] @@ -53,7 +53,7 @@ def resrgan2normal(state_dict, nb=23): re8x = 0 crt_net = {} items = [] - for k, v in state_dict.items(): + for k, _v in state_dict.items(): items.append(k) crt_net['model.0.weight'] = state_dict['conv_first.weight'] @@ -186,7 +186,7 @@ class UpscalerESRGAN(Upscaler): elif "conv_first.weight" in state_dict: state_dict = mod2normal(state_dict) elif "model.0.weight" not in state_dict: - raise Exception("The file is not a recognized ESRGAN model.") + raise TypeError("The file is not a recognized ESRGAN model.") in_nc, out_nc, nf, nb, plus, mscale = infer_params(state_dict) diff --git a/modules/gfpgan_model.py b/modules/gfpgan_model.py index 9f332a730..728df70bf 100644 --- a/modules/gfpgan_model.py +++ b/modules/gfpgan_model.py @@ -13,9 +13,8 @@ loaded_gfpgan_model = None def gfpgann(): import facexlib - import gfpgan - global loaded_gfpgan_model - global model_path + import gfpgan # pylint: disable=unused-import + global loaded_gfpgan_model # pylint: disable=global-statement if loaded_gfpgan_model is not None: loaded_gfpgan_model.gfpgan.to(devices.device_gfpgan) return loaded_gfpgan_model @@ -54,7 +53,7 @@ def gfpgan_fix_faces(np_image): send_model_to(model, devices.device_gfpgan) np_image_bgr = np_image[:, :, ::-1] - cropped_faces, restored_faces, gfpgan_output_bgr = model.enhance(np_image_bgr, has_aligned=False, only_center_face=False, paste_back=True) + _cropped_faces, _restored_faces, gfpgan_output_bgr = model.enhance(np_image_bgr, has_aligned=False, only_center_face=False, paste_back=True) np_image = gfpgan_output_bgr[:, :, ::-1] model.face_helper.clean_all() @@ -69,7 +68,6 @@ gfpgan_constructor = None def setup_model(dirname): - global model_path if not os.path.exists(model_path): os.makedirs(model_path) @@ -77,9 +75,9 @@ def setup_model(dirname): import gfpgan import facexlib - global user_path - global have_gfpgan - global gfpgan_constructor + global user_path # pylint: disable=global-statement + global have_gfpgan # pylint: disable=global-statement + global gfpgan_constructor # pylint: disable=global-statement load_file_from_url_orig = gfpgan.utils.load_file_from_url facex_load_file_from_url_orig = facexlib.detection.load_file_from_url diff --git a/modules/localization.py b/modules/localization.py index 5b58f9e8c..d18d5137b 100644 --- a/modules/localization.py +++ b/modules/localization.py @@ -1,5 +1,4 @@ import json -import os import sys import modules.errors as errors @@ -7,9 +6,8 @@ import modules.errors as errors localizations = {} -def list_localizations(dirname): +def list_localizations(dirname): # pylint: disable=unused-argument localizations.clear() - return localizations """ for file in os.listdir(dirname): fn, ext = os.path.splitext(file) @@ -23,6 +21,8 @@ def list_localizations(dirname): fn, ext = os.path.splitext(file.filename) localizations[fn] = file.path """ + return localizations + def localization_js(current_localization_name): fn = localizations.get(current_localization_name, None) diff --git a/modules/lowvram.py b/modules/lowvram.py index e254cc131..cb684acd2 100644 --- a/modules/lowvram.py +++ b/modules/lowvram.py @@ -6,7 +6,7 @@ cpu = torch.device("cpu") def send_everything_to_cpu(): - global module_in_gpu + global module_in_gpu # pylint: disable=global-statement if module_in_gpu is not None: module_in_gpu.to(cpu) @@ -22,7 +22,7 @@ def setup_for_low_vram(sd_model, use_medvram): we add this as forward_pre_hook to a lot of modules and this way all but one of them will be in CPU """ - global module_in_gpu + global module_in_gpu # pylint: disable=global-statement module = parents.get(module, module) diff --git a/modules/masking.py b/modules/masking.py index a5c4d2da5..484650530 100644 --- a/modules/masking.py +++ b/modules/masking.py @@ -4,7 +4,7 @@ from PIL import Image, ImageFilter, ImageOps def get_crop_region(mask, pad=0): """finds a rectangular region that contains all masked ares in an image. Returns (x1, y1, x2, y2) coordinates of the rectangle. For example, if a user has painted the top-right part of a 512x512 image", the result may be (256, 0, 512, 256)""" - + h, w = mask.shape crop_left = 0 @@ -96,4 +96,3 @@ def fill(image, mask): image_mod.alpha_composite(blurred) return image_mod.convert("RGB") - diff --git a/modules/processing.py b/modules/processing.py index f46eb65fe..4c3cca1db 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -220,7 +220,7 @@ class StableDiffusionProcessing: source_image = devices.cond_cast_float(source_image) # HACK: Using introspection as the Depth2Image model doesn't appear to uniquely # identify itself with a field common to all models. The conditioning_key is also hybrid. - if opts.sd_backend == 'Diffusers': # TODO: img2img_image_conditioning + if opts.sd_backend == 'Diffusers': # TODO: Diffusers img2img_image_conditioning return latent_image.new_zeros(latent_image.shape[0], 5, 1, 1) if isinstance(self.sd_model, LatentDepth2ImageDiffusion): return self.depth2img_image_conditioning(source_image) @@ -649,7 +649,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: devices.torch_gc() if p.scripts is not None: p.scripts.postprocess_batch(p, x_samples_ddim, batch_number=n) - else: # TODO Diffusers + else: # TODO Diffusers main processing generator = [torch.Generator(device="cpu").manual_seed(s) for s in seeds] if shared.sd_model.scheduler.name != p.sampler_name: sampler = sd_samplers.all_samplers_map.get(p.sampler_name, None) diff --git a/modules/prompt_parser.py b/modules/prompt_parser.py index e2647a6f0..1474e69d3 100644 --- a/modules/prompt_parser.py +++ b/modules/prompt_parser.py @@ -13,7 +13,7 @@ from typing import List import lark import torch from compel import Compel -from modules.shared import log, opts +from modules.shared import opts # a prompt like this: "fantasy landscape with a [mountain:lake:0.25] and [an oak:a christmas tree:0.75][ in foreground::0.6][ in background:0.25] [shoddy:masterful:0.5]" # will be represented with prompt_schedule like this (assuming steps=100): diff --git a/modules/script_loading.py b/modules/script_loading.py index 6827515fc..b28f6b65d 100644 --- a/modules/script_loading.py +++ b/modules/script_loading.py @@ -6,7 +6,7 @@ import modules.errors as errors preloaded = [] -def load_module(path, detailed=False): +def load_module(path): module_spec = importlib.util.spec_from_file_location(os.path.basename(path), path) module = importlib.util.module_from_spec(module_spec) try: @@ -17,7 +17,7 @@ def load_module(path, detailed=False): -def preload_extensions(extensions_dir, parser, detailed=False): +def preload_extensions(extensions_dir, parser): if not os.path.isdir(extensions_dir): return for dirname in sorted(os.listdir(extensions_dir)): @@ -28,7 +28,7 @@ def preload_extensions(extensions_dir, parser, detailed=False): if not os.path.isfile(preload_script): continue try: - module = load_module(preload_script, detailed) + module = load_module(preload_script) if hasattr(module, 'preload'): module.preload(parser) except Exception as e: diff --git a/modules/scripts_postprocessing.py b/modules/scripts_postprocessing.py index 64563ab62..7baa4c738 100644 --- a/modules/scripts_postprocessing.py +++ b/modules/scripts_postprocessing.py @@ -31,23 +31,19 @@ class ScriptPostprocessing: The return value should be a dictionary that maps parameter names to components used in processing. Values of those components will be passed to process() function. """ - - pass + pass # pylint: disable=unnecessary-pass def process(self, pp: PostprocessedImage, **args): """ This function is called to postprocess the image. args contains a dictionary with all values returned by components from ui() """ - - pass + pass # pylint: disable=unnecessary-pass def image_changed(self): pass - - def wrap_call(func, filename, funcname, *args, default=None, **kwargs): try: res = func(*args, **kwargs) @@ -66,7 +62,7 @@ class ScriptPostprocessingRunner: def initialize_scripts(self, scripts_data): self.scripts = [] - for script_class, path, basedir, script_module in scripts_data: + for script_class, path, _basedir, _script_module in scripts_data: script: ScriptPostprocessing = script_class() script.filename = path @@ -124,7 +120,7 @@ class ScriptPostprocessingRunner: script_args = args[script.args_from:script.args_to] process_args = {} - for (name, component), value in zip(script.controls.items(), script_args): + for (name, _component), value in zip(script.controls.items(), script_args): process_args[name] = value script.process(pp, **process_args) diff --git a/modules/sd_disable_initialization.py b/modules/sd_disable_initialization.py index c4a09d15d..c30525c30 100644 --- a/modules/sd_disable_initialization.py +++ b/modules/sd_disable_initialization.py @@ -35,10 +35,10 @@ class DisableInitialization: return original def __enter__(self): - def do_nothing(*args, **kwargs): + def do_nothing(*args, **kwargs): # pylint: disable=unused-argument pass - def create_model_and_transforms_without_pretrained(*args, pretrained=None, **kwargs): + def create_model_and_transforms_without_pretrained(*args, pretrained=None, **kwargs): # pylint: disable=unused-argument return self.create_model_and_transforms(*args, pretrained=None, **kwargs) def CLIPTextModel_from_pretrained(pretrained_model_name_or_path, *model_args, **kwargs): @@ -61,16 +61,16 @@ class DisableInitialization: if res is None: res = original(url, *args, local_files_only=False, **kwargs) return res - except Exception as e: + except Exception: return original(url, *args, local_files_only=False, **kwargs) - def transformers_utils_hub_get_from_cache(url, *args, local_files_only=False, **kwargs): + def transformers_utils_hub_get_from_cache(url, *args, local_files_only=False, **kwargs): # pylint: disable=unused-argument return transformers_utils_hub_get_file_from_cache(self.transformers_utils_hub_get_from_cache, url, *args, **kwargs) - def transformers_tokenization_utils_base_cached_file(url, *args, local_files_only=False, **kwargs): + def transformers_tokenization_utils_base_cached_file(url, *args, local_files_only=False, **kwargs): # pylint: disable=unused-argument return transformers_utils_hub_get_file_from_cache(self.transformers_tokenization_utils_base_cached_file, url, *args, **kwargs) - def transformers_configuration_utils_cached_file(url, *args, local_files_only=False, **kwargs): + def transformers_configuration_utils_cached_file(url, *args, local_files_only=False, **kwargs): # pylint: disable=unused-argument return transformers_utils_hub_get_file_from_cache(self.transformers_configuration_utils_cached_file, url, *args, **kwargs) self.replace(torch.nn.init, 'kaiming_uniform_', do_nothing) @@ -78,16 +78,15 @@ class DisableInitialization: self.replace(torch.nn.init, '_no_grad_uniform_', do_nothing) if self.disable_clip: - self.create_model_and_transforms = self.replace(open_clip, 'create_model_and_transforms', create_model_and_transforms_without_pretrained) - self.CLIPTextModel_from_pretrained = self.replace(ldm.modules.encoders.modules.CLIPTextModel, 'from_pretrained', CLIPTextModel_from_pretrained) - self.transformers_modeling_utils_load_pretrained_model = self.replace(transformers.modeling_utils.PreTrainedModel, '_load_pretrained_model', transformers_modeling_utils_load_pretrained_model) - self.transformers_tokenization_utils_base_cached_file = self.replace(transformers.tokenization_utils_base, 'cached_file', transformers_tokenization_utils_base_cached_file) - self.transformers_configuration_utils_cached_file = self.replace(transformers.configuration_utils, 'cached_file', transformers_configuration_utils_cached_file) - self.transformers_utils_hub_get_from_cache = self.replace(transformers.utils.hub, 'get_from_cache', transformers_utils_hub_get_from_cache) + self.create_model_and_transforms = self.replace(open_clip, 'create_model_and_transforms', create_model_and_transforms_without_pretrained) # pylint: disable=attribute-defined-outside-init + self.CLIPTextModel_from_pretrained = self.replace(ldm.modules.encoders.modules.CLIPTextModel, 'from_pretrained', CLIPTextModel_from_pretrained) # pylint: disable=attribute-defined-outside-init + self.transformers_modeling_utils_load_pretrained_model = self.replace(transformers.modeling_utils.PreTrainedModel, '_load_pretrained_model', transformers_modeling_utils_load_pretrained_model) # pylint: disable=attribute-defined-outside-init + self.transformers_tokenization_utils_base_cached_file = self.replace(transformers.tokenization_utils_base, 'cached_file', transformers_tokenization_utils_base_cached_file) # pylint: disable=attribute-defined-outside-init + self.transformers_configuration_utils_cached_file = self.replace(transformers.configuration_utils, 'cached_file', transformers_configuration_utils_cached_file) # pylint: disable=attribute-defined-outside-init + self.transformers_utils_hub_get_from_cache = self.replace(transformers.utils.hub, 'get_from_cache', transformers_utils_hub_get_from_cache) # pylint: disable=attribute-defined-outside-init def __exit__(self, exc_type, exc_val, exc_tb): for obj, field, original in self.replaced: setattr(obj, field, original) self.replaced.clear() - diff --git a/modules/sd_hijack_checkpoint.py b/modules/sd_hijack_checkpoint.py index 2604d969f..6146c19a6 100644 --- a/modules/sd_hijack_checkpoint.py +++ b/modules/sd_hijack_checkpoint.py @@ -5,15 +5,15 @@ import ldm.modules.diffusionmodules.openaimodel def BasicTransformerBlock_forward(self, x, context=None): - return checkpoint(self._forward, x, context) + return checkpoint(self._forward, x, context) # pylint: disable=protected-access def AttentionBlock_forward(self, x): - return checkpoint(self._forward, x) + return checkpoint(self._forward, x) # pylint: disable=protected-access def ResBlock_forward(self, x, emb): - return checkpoint(self._forward, x, emb) + return checkpoint(self._forward, x, emb) # pylint: disable=protected-access stored = [] @@ -43,4 +43,3 @@ def remove(): ldm.modules.diffusionmodules.openaimodel.AttentionBlock.forward = stored[2] stored.clear() - diff --git a/modules/sd_hijack_clip_old.py b/modules/sd_hijack_clip_old.py index a3476e956..21af997e9 100644 --- a/modules/sd_hijack_clip_old.py +++ b/modules/sd_hijack_clip_old.py @@ -70,7 +70,7 @@ def process_text_old(self: sd_hijack_clip.FrozenCLIPEmbedderWithCustomWordsBase, def forward_old(self: sd_hijack_clip.FrozenCLIPEmbedderWithCustomWordsBase, texts): - batch_multipliers, remade_batch_tokens, used_custom_terms, hijack_comments, hijack_fixes, token_count = process_text_old(self, texts) + batch_multipliers, remade_batch_tokens, used_custom_terms, hijack_comments, hijack_fixes, _token_count = process_text_old(self, texts) self.hijack.comments += hijack_comments diff --git a/modules/sd_hijack_inpainting.py b/modules/sd_hijack_inpainting.py index 4b23c132d..02691b705 100644 --- a/modules/sd_hijack_inpainting.py +++ b/modules/sd_hijack_inpainting.py @@ -4,12 +4,11 @@ import ldm.models.diffusion.ddpm import ldm.models.diffusion.ddim import ldm.models.diffusion.plms -from ldm.models.diffusion.ddpm import LatentDiffusion -from ldm.models.diffusion.plms import PLMSSampler -from ldm.models.diffusion.ddim import DDIMSampler, noise_like +from ldm.models.diffusion.ddpm import LatentDiffusion # pylint: disable=unused-import +from ldm.models.diffusion.plms import PLMSSampler # pylint: disable=unused-import +from ldm.models.diffusion.ddim import DDIMSampler, noise_like # pylint: disable=unused-import from ldm.models.diffusion.sampling_util import norm_thresholding - @torch.no_grad() def p_sample_plms(self, x, c, t, index, repeat_noise=False, use_original_steps=False, quantize_denoised=False, temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None, @@ -63,7 +62,6 @@ def p_sample_plms(self, x, c, t, index, repeat_noise=False, use_original_steps=F if quantize_denoised: pred_x0, _, *_ = self.model.first_stage_model.quantize(pred_x0) if dynamic_threshold is not None: - from ldm.models.diffusion.sampling_util import norm_thresholding pred_x0 = norm_thresholding(pred_x0, dynamic_threshold) # direction pointing to x_t dir_xt = (1. - a_prev - sigma_t**2).sqrt() * e_t diff --git a/modules/sd_hijack_optimizations.py b/modules/sd_hijack_optimizations.py index e8c8ce763..ef02ccc35 100644 --- a/modules/sd_hijack_optimizations.py +++ b/modules/sd_hijack_optimizations.py @@ -51,7 +51,7 @@ def get_available_vram(): # see https://github.com/basujindal/stable-diffusion/pull/117 for discussion -def split_cross_attention_forward_v1(self, x, context=None, mask=None): +def split_cross_attention_forward_v1(self, x, context=None, mask=None): # pylint: disable=unused-argument h = self.heads q_in = self.to_q(x) @@ -90,7 +90,7 @@ def split_cross_attention_forward_v1(self, x, context=None, mask=None): # taken from https://github.com/Doggettx/stable-diffusion and modified -def split_cross_attention_forward(self, x, context=None, mask=None): +def split_cross_attention_forward(self, x, context=None, mask=None): # pylint: disable=unused-argument h = self.heads q_in = self.to_q(x) context = default(context, x) @@ -231,7 +231,7 @@ def einsum_op(q, k, v): # Tested on i7 with 8MB L3 cache. return einsum_op_tensor_mem(q, k, v, 32) -def split_cross_attention_forward_invokeAI(self, x, context=None, mask=None): +def split_cross_attention_forward_invokeAI(self, x, context=None, mask=None): # pylint: disable=unused-argument h = self.heads q = self.to_q(x) @@ -315,7 +315,7 @@ def sub_quad_attention(q, k, v, q_chunk_size=1024, kv_chunk_size=None, kv_chunk_ if chunk_threshold_bytes is not None and qk_matmul_size_bytes <= chunk_threshold_bytes: # the big matmul fits into our memory limit; do everything in 1 chunk, # i.e. send it down the unchunked fast-path - query_chunk_size = q_tokens + query_chunk_size = q_tokens # pylint: disable=unused-variable kv_chunk_size = k_tokens with devices.without_autocast(disable=q.dtype == v.dtype): @@ -336,7 +336,7 @@ def get_xformers_flash_attention_op(q, k, v): try: flash_attention_op = xformers.ops.MemoryEfficientAttentionFlashAttentionOp - fw, bw = flash_attention_op + fw, _bw = flash_attention_op if fw.supports(xformers.ops.fmha.Inputs(query=q, key=k, value=v, attn_bias=None)): return flash_attention_op except Exception as e: @@ -345,7 +345,7 @@ def get_xformers_flash_attention_op(q, k, v): return None -def xformers_attention_forward(self, x, context=None, mask=None): +def xformers_attention_forward(self, x, context=None, mask=None): # pylint: disable=unused-argument h = self.heads q_in = self.to_q(x) context = default(context, x) @@ -481,7 +481,7 @@ def xformers_attnblock_forward(self, x): q = self.q(h_) k = self.k(h_) v = self.v(h_) - b, c, h, w = q.shape + b, c, h, w = q.shape # pylint: disable=unused-variable q, k, v = map(lambda t: rearrange(t, 'b c h w -> b (h w) c'), (q, k, v)) dtype = q.dtype if shared.opts.upcast_attn: @@ -503,7 +503,7 @@ def sdp_attnblock_forward(self, x): q = self.q(h_) k = self.k(h_) v = self.v(h_) - b, c, h, w = q.shape + b, c, h, w = q.shape # pylint: disable=unused-variable q, k, v = map(lambda t: rearrange(t, 'b c h w -> b (h w) c'), (q, k, v)) dtype = q.dtype if shared.opts.upcast_attn: @@ -531,7 +531,7 @@ def sub_quad_attnblock_forward(self, x): q = self.q(h_) k = self.k(h_) v = self.v(h_) - b, c, h, w = q.shape + b, c, h, w = q.shape # pylint: disable=unused-variable q, k, v = map(lambda t: rearrange(t, 'b c h w -> b (h w) c'), (q, k, v)) q = q.contiguous() k = k.contiguous() diff --git a/modules/sd_hijack_unet.py b/modules/sd_hijack_unet.py index 252e8e5fc..c7fee64b5 100644 --- a/modules/sd_hijack_unet.py +++ b/modules/sd_hijack_unet.py @@ -47,25 +47,25 @@ def apply_model(orig_func, self, x_noisy, t, cond, **kwargs): class GELUHijack(torch.nn.GELU, torch.nn.Module): - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs): # pylint: disable=super-init-not-called torch.nn.GELU.__init__(self, *args, **kwargs) - def forward(self, x): + def forward(self, input): # pylint: disable=redefined-builtin if devices.unet_needs_upcast: - return torch.nn.GELU.forward(self.float(), x.float()).to(devices.dtype_unet) + return torch.nn.GELU.forward(self.float(), input.float()).to(devices.dtype_unet) else: - return torch.nn.GELU.forward(self, x) + return torch.nn.GELU.forward(self, input) ddpm_edit_hijack = None def hijack_ddpm_edit(): - global ddpm_edit_hijack + global ddpm_edit_hijack # pylint: disable=global-statement if not ddpm_edit_hijack: CondFunc('modules.models.diffusion.ddpm_edit.LatentDiffusion.decode_first_stage', first_stage_sub, first_stage_cond) CondFunc('modules.models.diffusion.ddpm_edit.LatentDiffusion.encode_first_stage', first_stage_sub, first_stage_cond) ddpm_edit_hijack = CondFunc('modules.models.diffusion.ddpm_edit.LatentDiffusion.apply_model', apply_model, unet_needs_upcast) -unet_needs_upcast = lambda *args, **kwargs: devices.unet_needs_upcast +unet_needs_upcast = lambda *args, **kwargs: devices.unet_needs_upcast # pylint: disable=unnecessary-lambda-assignment CondFunc('ldm.models.diffusion.ddpm.LatentDiffusion.apply_model', apply_model, unet_needs_upcast) CondFunc('ldm.modules.diffusionmodules.openaimodel.timestep_embedding', lambda orig_func, timesteps, *args, **kwargs: orig_func(timesteps, *args, **kwargs).to(torch.float32 if timesteps.dtype == torch.int64 else devices.dtype_unet), unet_needs_upcast) if version.parse(torch.__version__) <= version.parse("1.13.2") or torch.cuda.is_available() or shared.cmd_opts.use_ipex: @@ -73,8 +73,8 @@ if version.parse(torch.__version__) <= version.parse("1.13.2") or torch.cuda.is_ CondFunc('ldm.modules.attention.GEGLU.forward', lambda orig_func, self, x: orig_func(self.float(), x.float()).to(devices.dtype_unet), unet_needs_upcast) CondFunc('open_clip.transformer.ResidualAttentionBlock.__init__', lambda orig_func, *args, **kwargs: kwargs.update({'act_layer': GELUHijack}) and False or orig_func(*args, **kwargs), lambda _, *args, **kwargs: kwargs.get('act_layer') is None or kwargs['act_layer'] == torch.nn.GELU) -first_stage_cond = lambda _, self, *args, **kwargs: devices.unet_needs_upcast and self.model.diffusion_model.dtype == torch.float16 -first_stage_sub = lambda orig_func, self, x, **kwargs: orig_func(self, x.to(devices.dtype_vae), **kwargs) +first_stage_cond = lambda _, self, *args, **kwargs: devices.unet_needs_upcast and self.model.diffusion_model.dtype == torch.float16 # pylint: disable=unnecessary-lambda-assignment +first_stage_sub = lambda orig_func, self, x, **kwargs: orig_func(self, x.to(devices.dtype_vae), **kwargs) # pylint: disable=unnecessary-lambda-assignment CondFunc('ldm.models.diffusion.ddpm.LatentDiffusion.decode_first_stage', first_stage_sub, first_stage_cond) CondFunc('ldm.models.diffusion.ddpm.LatentDiffusion.encode_first_stage', first_stage_sub, first_stage_cond) CondFunc('ldm.models.diffusion.ddpm.LatentDiffusion.get_first_stage_encoding', lambda orig_func, *args, **kwargs: orig_func(*args, **kwargs).float(), first_stage_cond) diff --git a/modules/sd_models.py b/modules/sd_models.py index 80626abde..e33465be0 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -28,7 +28,7 @@ checkpoints_loaded = collections.OrderedDict() skip_next_load = False -class CheckpointInfo: # TODO Diffusers +class CheckpointInfo: def __init__(self, filename): name = '' self.name = None @@ -48,7 +48,6 @@ class CheckpointInfo: # TODO Diffusers self.hash = model_hash(self.filename) self.sha256 = hashes.sha256_from_cache(self.filename, f"checkpoint/{name}") else: # TODO Diffusers - # sd_model.unet.config._name_or_path.split("/")[-2] repo = [r for r in modelloader.diffuser_repos if filename == r['filename']] if len(repo) == 0: shared.log.error(f'Cannot find diffuser model: {filename}') @@ -540,7 +539,7 @@ def reload_model_weights(sd_model=None, info=None): sd_model.to(devices.cpu) if shared.opts.model_reuse_dict and sd_model is not None: shared.log.info('Reusing previous model dictionary') - sd_hijack.model_hijack.undo_hijack(sd_model) # TODO double undo hijack + sd_hijack.model_hijack.undo_hijack(sd_model) else: unload_model_weights() sd_model = None diff --git a/modules/sd_samplers_compvis.py b/modules/sd_samplers_compvis.py index 0fb411111..5f5544479 100644 --- a/modules/sd_samplers_compvis.py +++ b/modules/sd_samplers_compvis.py @@ -40,7 +40,7 @@ class VanillaStableDiffusionSampler: self.conditioning_key = sd_model.model.conditioning_key - def number_of_needed_noises(self, p): + def number_of_needed_noises(self, p): # pylint: disable=unused-argument return 0 def launch_sampling(self, steps, func): @@ -128,7 +128,7 @@ class VanillaStableDiffusionSampler: self.update_step(res[1]) return x, ts, cond, uncond, res - def unipc_after_update(self, x, model_x): + def unipc_after_update(self, x, model_x): # pylint: disable=unused-argument self.update_step(x) def initialize(self, p): diff --git a/modules/sd_samplers_kdiffusion.py b/modules/sd_samplers_kdiffusion.py index 0928b8ee1..8622c0b9c 100644 --- a/modules/sd_samplers_kdiffusion.py +++ b/modules/sd_samplers_kdiffusion.py @@ -97,10 +97,10 @@ class CFGDenoiser(torch.nn.Module): if shared.sd_model.model.conditioning_key == "crossattn-adm": image_uncond = torch.zeros_like(image_cond) - make_condition_dict = lambda c_crossattn, c_adm: {"c_crossattn": c_crossattn, "c_adm": c_adm} + make_condition_dict = lambda c_crossattn, c_adm: {"c_crossattn": c_crossattn, "c_adm": c_adm} # pylint: disable=unnecessary-lambda-assignment else: image_uncond = image_cond - make_condition_dict = lambda c_crossattn, c_concat: {"c_crossattn": c_crossattn, "c_concat": [c_concat]} + make_condition_dict = lambda c_crossattn, c_concat: {"c_crossattn": c_crossattn, "c_concat": [c_concat]} # pylint: disable=unnecessary-lambda-assignment if not is_edit_model: x_in = torch.cat([torch.stack([x[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [x]) diff --git a/modules/sd_vae_approx.py b/modules/sd_vae_approx.py index e2f004683..20e337255 100644 --- a/modules/sd_vae_approx.py +++ b/modules/sd_vae_approx.py @@ -32,7 +32,7 @@ class VAEApprox(nn.Module): def model(): - global sd_vae_approx_model + global sd_vae_approx_model # pylint: disable=global-statement if sd_vae_approx_model is None: model_path = os.path.join(paths.models_path, "VAE-approx", "model.pt") diff --git a/modules/sub_quadratic_attention.py b/modules/sub_quadratic_attention.py index 87c18a38d..bab11d411 100644 --- a/modules/sub_quadratic_attention.py +++ b/modules/sub_quadratic_attention.py @@ -19,7 +19,7 @@ from torch.utils.checkpoint import checkpoint def narrow_trunc( - input: Tensor, + input: Tensor, # pylint: disable=redefined-builtin dim: int, start: int, length: int @@ -79,8 +79,8 @@ def _query_chunk_attention( summarize_chunk: SummarizeChunk, kv_chunk_size: int, ) -> Tensor: - batch_x_heads, k_tokens, k_channels_per_head = key.shape - _, _, v_channels_per_head = value.shape + _batch_x_heads, k_tokens, _k_channels_per_head = key.shape + _, _, _v_channels_per_head = value.shape def chunk_scanner(chunk_idx: int) -> AttnChunk: key_chunk = narrow_trunc( @@ -113,7 +113,6 @@ def _query_chunk_attention( return all_values / all_weights -# TODO: refactor CrossAttention#get_attention_scores to share code with this def _get_attention_scores_no_kv_chunking( query: Tensor, key: Tensor, @@ -164,7 +163,7 @@ def efficient_dot_product_attention( Returns: Output of shape `[batch * num_heads, query_tokens, channels_per_head]`. """ - batch_x_heads, q_tokens, q_channels_per_head = query.shape + _batch_x_heads, q_tokens, q_channels_per_head = query.shape _, k_tokens, _ = key.shape scale = q_channels_per_head ** -0.5 @@ -202,7 +201,7 @@ def efficient_dot_product_attention( value=value, ) - # TODO: maybe we should use torch.empty_like(query) to allocate storage in-advance, + # maybe we should use torch.empty_like(query) to allocate storage in-advance, # and pass slices to be mutated, instead of torch.cat()ing the returned slices res = torch.cat([ compute_query_chunk_attn( diff --git a/modules/ui.py b/modules/ui.py index 1b2c6c836..0e79e983d 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -1324,7 +1324,7 @@ def create_ui(): reload_sd_model = gr.Button(value='Reload checkpoint', variant='primary', elem_id="sett_reload_sd_model") # reload_script_bodies = gr.Button(value='Reload scripts', variant='primary', elem_id="settings_reload_script_bodies") with gr.Row(): - _settings_search = gr.Text(label="Search", elem_id="settings_search") # TODO settings search + _settings_search = gr.Text(label="Search", elem_id="settings_search") result = gr.HTML(elem_id="settings_result") quicksettings_names = opts.quicksettings_list diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py index 9be1c3400..dc3298fe8 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -1,445 +1,445 @@ -import json -import os.path -import shutil -import errno -import html -from datetime import datetime -import git -import gradio as gr -from modules import extensions, shared, paths, errors -from modules.call_queue import wrap_gradio_gpu_call - - -extensions_index = "https://vladmandic.github.io/sd-data/pages/extensions.json" -hide_tags = ["localization"] -extensions_list = [] -sort_ordering = { - "default": (True, lambda x: x.get('sort_default', '')), - "user extensions": (True, lambda x: x.get('sort_user', '')), - "update avilable": (True, lambda x: x.get('sort_update', '')), - "updated date": (True, lambda x: x.get('updated', '2000-01-01T00:00')), - "created date": (True, lambda x: x.get('created', '2000-01-01T00:00')), - "name": (False, lambda x: x.get('name', '').lower()), - "enabled": (False, lambda x: x.get('sort_enabled', '').lower()), - "size": (True, lambda x: x.get('size', 0)), - "stars": (True, lambda x: x.get('stars', 0)), - "commits": (True, lambda x: x.get('commits', 0)), - "issues": (True, lambda x: x.get('issues', 0)), -} - - -def update_extension_list(): - global extensions_list # pylint: disable=global-statement - try: - with open(os.path.join(paths.script_path, "html", "extensions.json"), "r", encoding="utf-8") as f: - extensions_list = json.loads(f.read()) - shared.log.debug(f'Extensions list loaded: {os.path.join(paths.script_path, "html", "extensions.json")}') - except: - shared.log.debug(f'Extensions list failed to load: {os.path.join(paths.script_path, "html", "extensions.json")}') - found = [] - for ext in extensions.extensions: - ext.read_info_from_repo() - for ext in extensions_list: - installed = [extension for extension in extensions.extensions - if extension.git_name == ext['name'] - or extension.name == ext['name'] - or (extension.remote or '').startswith(ext['url'].replace('.git', ''))] - if len(installed) > 0: - found.append(installed[0]) - not_matched = [extension for extension in extensions.extensions if extension not in found] - for ext in not_matched: - entry = { - "name": ext.name or "", - "description": ext.description or "", - "url": ext.remote or "", - "tags": [], - "stars": 0, - "issues": 0, - "commits": 0, - "size": 0, - "long": ext.git_name or ext.name or "", - "added": ext.ctime, - "created": ext.ctime, - "updated": ext.mtime, - } - extensions_list.append(entry) - - -def check_access(): - assert not shared.cmd_opts.disable_extension_access, "extension access disabled because of command line flags" - - -def apply_and_restart(disable_list, update_list, disable_all): - check_access() - shared.log.debug(f'Extensions apply: disable={disable_list} update={update_list}') - disabled = json.loads(disable_list) - assert type(disabled) == list, f"wrong disable_list data for apply_and_restart: {disable_list}" - update = json.loads(update_list) - assert type(update) == list, f"wrong update_list data for apply_and_restart: {update_list}" - update = set(update) - for ext in extensions.extensions: - if ext.name not in update: - continue - try: - ext.fetch_and_reset_hard() - 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 - shared.opts.save(shared.config_filename) - shared.restart_server(restart=True) - - -def check_updates(_id_task, disable_list, search_text, sort_column): - check_access() - disabled = json.loads(disable_list) - assert type(disabled) == list, f"wrong disable_list data for apply_and_restart: {disable_list}" - exts = [ext for ext in extensions.extensions if ext.remote is not None and ext.name not in disabled] - shared.log.info(f'Extensions update check: update={len(exts)} disabled={len(disable_list)}') - shared.state.job_count = len(exts) - for ext in exts: - shared.state.textinfo = ext.name - try: - ext.check_updates() - if ext.can_update: - ext.fetch_and_reset_hard() - ext.read_info_from_repo() - commit_date = ext.commit_date or 1577836800 - shared.log.info(f'Extensions updated: {ext.name} {ext.commit_hash[:8]} {datetime.utcfromtimestamp(commit_date)}') - else: - commit_date = ext.commit_date or 1577836800 - shared.log.debug(f'Extensions no update available: {ext.name} {ext.commit_hash[:8]} {datetime.utcfromtimestamp(commit_date)}') - except FileNotFoundError as e: - if 'FETCH_HEAD' not in str(e): - raise - except Exception: - errors.display(e, f'extensions check update: {ext.name}') - shared.state.nextjob() - return refresh_extensions_list_from_data(search_text, sort_column), "Extension update complete | Restart required" - - -def make_commit_link(commit_hash, remote, text=None): - if text is None: - text = commit_hash[:8] - if remote.startswith("https://github.com/"): - href = os.path.join(remote, "commit", commit_hash) - return f'{text}' - else: - return text - - -def normalize_git_url(url): - if url is None: - return "" - url = url.replace(".git", "") - return url - - -def install_extension_from_url(dirname, url, branch_name, search_text, sort_column): - check_access() - assert url, 'No URL specified' - if dirname is None or dirname == "": - *parts, last_part = url.split('/') # pylint: disable=unused-variable - last_part = normalize_git_url(last_part) - dirname = last_part - target_dir = os.path.join(extensions.extensions_dir, dirname) - shared.log.info(f'Installing extension: {url} into {target_dir}') - assert not os.path.exists(target_dir), f'Extension directory already exists: {target_dir}' - normalized_url = normalize_git_url(url) - assert len([x for x in extensions.extensions if normalize_git_url(x.remote) == normalized_url]) == 0, 'Extension with this URL is already installed' - tmpdir = os.path.join(paths.data_path, "tmp", dirname) - try: - shutil.rmtree(tmpdir, True) - if not branch_name: - # if no branch is specified, use the default branch - with git.Repo.clone_from(url, tmpdir) as repo: - repo.remote().fetch() - for submodule in repo.submodules: - submodule.update() - else: - with git.Repo.clone_from(url, tmpdir, branch=branch_name) as repo: - repo.remote().fetch() - for submodule in repo.submodules: - submodule.update() - try: - os.rename(tmpdir, target_dir) - except OSError as err: - if err.errno == errno.EXDEV: - shutil.move(tmpdir, target_dir) - else: - raise err - from launch import run_extension_installer - run_extension_installer(target_dir) - extensions.list_extensions() - return [refresh_extensions_list_from_data(search_text, sort_column), html.escape(f"Extension installed: {target_dir} | Restart required")] - finally: - shutil.rmtree(tmpdir, True) - - -def install_extension(extension_to_install, search_text, sort_column): - shared.log.info(f'Extension install: {extension_to_install}') - code, message = install_extension_from_url(None, extension_to_install, None, search_text, sort_column) - return code, message - - -def uninstall_extension(extension_path, search_text, sort_column): - def errorRemoveReadonly(func, path, exc): - import stat - excvalue = exc[1] - shared.log.debug(f'Exception during cleanup: {func} {path} {excvalue.strerror}') - if func in (os.rmdir, os.remove, os.unlink) and excvalue.errno == errno.EACCES: - shared.log.debug(f'Retrying cleanup: {path}') - os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) - func(path) - - ext = [extension for extension in extensions.extensions if os.path.abspath(extension.path) == os.path.abspath(extension_path)] - if len(ext) > 0 and os.path.isdir(extension_path): - found = ext[0] - try: - shutil.rmtree(found.path, ignore_errors=False, onerror=errorRemoveReadonly) - except Exception as e: - shared.log.warning(f'Extension uninstall failed: {found.path} {e}') - extensions.extensions = [extension for extension in extensions.extensions if os.path.abspath(found.path) != os.path.abspath(extension_path)] - update_extension_list() - code = refresh_extensions_list_from_data(search_text, sort_column) - shared.log.info(f'Extension uninstalled: {found.path}') - return code, f"Extension uninstalled: {found.path} | Restart required" - else: - shared.log.warning(f'Extension uninstall cannot find extension: {extension_path}') - code = refresh_extensions_list_from_data(search_text, sort_column) - return code, f"Extension uninstalled failed: {extension_path}" - - -def update_extension(extension_path, search_text, sort_column): - exts = [extension for extension in extensions.extensions if os.path.abspath(extension.path) == os.path.abspath(extension_path)] - shared.state.job_count = len(exts) - for ext in exts: - shared.log.debug(f'Extensions update start: {ext.name} {ext.commit_hash} {ext.commit_date}') - shared.state.textinfo = ext.name - try: - ext.check_updates() - if ext.can_update: - ext.fetch_and_reset_hard() - ext.read_info_from_repo() - commit_date = ext.commit_date or 1577836800 - shared.log.info(f'Extensions updated: {ext.name} {ext.commit_hash[:8]} {datetime.utcfromtimestamp(commit_date)}') - else: - commit_date = ext.commit_date or 1577836800 - shared.log.info(f'Extensions no update available: {ext.name} {ext.commit_hash[:8]} {datetime.utcfromtimestamp(commit_date)}') - except FileNotFoundError as e: - if 'FETCH_HEAD' not in str(e): - raise - except Exception as e: - shared.log.error(f'Extensions update failed: {ext.name}') - errors.display(e, f'extensions check update: {ext.name}') - shared.log.debug(f'Extensions update finish: {ext.name} {ext.commit_hash} {ext.commit_date}') - shared.state.nextjob() - return refresh_extensions_list_from_data(search_text, sort_column), f"Extension updated | {extension_path} | Restart required" - - -def refresh_extensions_list(search_text, sort_column): - global extensions_list # pylint: disable=global-statement - import urllib.request - try: - with urllib.request.urlopen(extensions_index) as response: - text = response.read() - extensions_list = json.loads(text) - with open(os.path.join(paths.script_path, "html", "extensions.json"), "w", encoding="utf-8") as outfile: - json_object = json.dumps(extensions_list, indent=2) - outfile.write(json_object) - shared.log.debug(f'Updated extensions list: {len(extensions_list)} {extensions_index} {outfile}') - except Exception as e: - shared.log.warning(f'Updated extensions list failed: {extensions_index} {e}') - update_extension_list() - code = refresh_extensions_list_from_data(search_text, sort_column) - return code, f'Extensions | {len(extensions.extensions)} registered | {len(extensions_list)} available' - - -def search_extensions(search_text, sort_column): - code = refresh_extensions_list_from_data(search_text, sort_column) - return code, f'Search | {search_text} | {sort_column}' - - -def refresh_extensions_list_from_data(search_text, sort_column): - shared.log.debug(f'Extensions manager: refresh list search="{search_text}" sort="{sort_column}"') - code = """ - - - - - - - - - - - - - - - - - - - - """ - for ext in extensions_list: - extension = [extension for extension in extensions.extensions if extension.git_name == ext['name'] or extension.name == ext['name']] - if len(extension) > 0: - extension[0].read_info_from_repo() - ext['installed'] = len(extension) > 0 - ext['commit_date'] = extension[0].commit_date if len(extension) > 0 else 1577836800 - ext['is_builtin'] = extension[0].is_builtin if len(extension) > 0 else False - ext['version'] = extension[0].version if len(extension) > 0 else '' - ext['enabled'] = extension[0].enabled if len(extension) > 0 else '' - ext['remote'] = extension[0].remote if len(extension) > 0 else None - ext['path'] = extension[0].path if len(extension) > 0 else '' - ext['sort_default'] = f"{'1' if ext['is_builtin'] else '0'}{'1' if ext['installed'] else '0'}{ext.get('updated', '2000-01-01T00:00')}" - sort_reverse, sort_function = sort_ordering[sort_column] - - def dt(x: str): - val = ext.get(x, None) - if val is not None: - return datetime.fromisoformat(val[:-1]).strftime('%a %b%d %Y %H:%M') - else: - return "N/A" - - for ext in sorted(extensions_list, key=sort_function, reverse=sort_reverse): - name = ext.get("name", "unknown") - added = dt('added') - created = dt('created') - pushed = dt('pushed') - updated = dt('updated') - url = ext.get('url', None) - size = ext.get('size', 0) - stars = ext.get('stars', 0) - issues = ext.get('issues', 0) - commits = ext.get('commits', 0) - description = ext.get("description", "") - installed = ext.get("installed", False) - enabled = ext.get("enabled", False) - path = ext.get("path", "") - remote = ext.get("remote", None) - commit_date = ext.get("commit_date", 1577836800) or 1577836800 - update_available = (remote is not None) & (installed) & (datetime.utcfromtimestamp(commit_date + 60 * 60) < datetime.fromisoformat(ext.get('updated', '2000-01-01T00:00:00.000Z')[:-1])) - ext['sort_user'] = f"{'0' if ext['is_builtin'] else '1'}{'1' if ext['installed'] else '0'}{ext.get('name', '')}" - ext['sort_enabled'] = f"{'0' if ext['enabled'] else '1'}{'1' if ext['is_builtin'] else '0'}{'1' if ext['installed'] else '0'}{ext.get('updated', '2000-01-01T00:00')}" - ext['sort_update'] = f"{'1' if update_available else '0'}{'1' if ext['installed'] else '0'}{ext.get('updated', '2000-01-01T00:00')}" - tags = ext.get("tags", []) - tags_string = ' '.join(tags) - tags = tags + ["installed"] if installed else tags - if len([x for x in tags if x in hide_tags]) > 0: - continue - if search_text and search_text.strip(): - if search_text.lower() not in html.escape(name).lower() and search_text.lower() not in html.escape(description).lower() and search_text.lower() not in html.escape(tags_string).lower(): - continue - version_code = '' - type_code = '' - install_code = '' - enabled_code = '' - if installed: - type_code = f"""
{"SYSTEM" if ext['is_builtin'] else 'USER'}
""" - version_code = f"""
{ext['version']}
""" - enabled_code = f"""""" - masked_path = html.escape(path.replace('\\', '/')) - if not ext['is_builtin']: - install_code = f"""""" - if update_available: - install_code += f"""""" - else: - install_code = f"""""" - tags_text = ", ".join([f"{x}" for x in tags]) - code += f""" - - {enabled_code} - - - - - - """ - code += "
EnabledExtensionDescriptionTypeCurrent version
{html.escape(name)}
{tags_text}
{html.escape(description)} -

Created {html.escape(created)} | Added {html.escape(added)} | Pushed {html.escape(pushed)} | Updated {html.escape(updated)}

-

Stars {html.escape(str(stars))} | Size {html.escape(str(size))} | Commits {html.escape(str(commits))} | Issues {html.escape(str(issues))}

-
{type_code}{version_code}{install_code}
" - return code - - -def create_ui(): - import modules.ui - with gr.Blocks(analytics_enabled=False) as ui: - extensions_disable_all = gr.Radio(label="Disable all extensions", choices=["none", "user", "all"], value=shared.opts.disable_all_extensions, elem_id="extensions_disable_all", visible=False) - extensions_disabled_list = gr.Text(elem_id="extensions_disabled_list", visible=False).style(container=False) - extensions_update_list = gr.Text(elem_id="extensions_update_list", visible=False).style(container=False) - with gr.Tabs(elem_id="tabs_extensions"): - with gr.TabItem("Manage Extensions", id="manage"): - with gr.Row(elem_id="extensions_installed_top"): - extension_to_install = gr.Text(elem_id="extension_to_install", visible=False) - install_extension_button = gr.Button(elem_id="install_extension_button", visible=False) - uninstall_extension_button = gr.Button(elem_id="uninstall_extension_button", visible=False) - update_extension_button = gr.Button(elem_id="update_extension_button", visible=False) - with gr.Column(scale=4): - search_text = gr.Text(label="Search") - info = gr.HTML('Note: After any operation such as install/uninstall or enable/disable, please restart the server') - with gr.Column(scale=1): - sort_column = gr.Dropdown(value="default", label="Sort by", choices=list(sort_ordering.keys()), multiselect=False) - with gr.Column(scale=1): - refresh_extensions_button = gr.Button(value="Refresh extension list", variant="primary") - check = gr.Button(value="Update installed extensions", variant="primary") - apply = gr.Button(value="Apply changes & restart server", variant="primary") - update_extension_list() - extensions_table = gr.HTML(refresh_extensions_list_from_data(search_text.value, sort_column.value)) - check.click( - fn=wrap_gradio_gpu_call(check_updates, extra_outputs=[gr.update()]), - _js="extensions_check", - inputs=[info, extensions_disabled_list, search_text, sort_column], - outputs=[extensions_table, info], - ) - apply.click( - fn=apply_and_restart, - _js="extensions_apply", - inputs=[extensions_disabled_list, extensions_update_list, extensions_disable_all], - outputs=[], - ) - refresh_extensions_button.click( - fn=modules.ui.wrap_gradio_call(refresh_extensions_list, extra_outputs=[gr.update(), gr.update()]), - inputs=[search_text, sort_column], - outputs=[extensions_table, info], - ) - install_extension_button.click( - fn=modules.ui.wrap_gradio_call(install_extension, extra_outputs=[gr.update(), gr.update(), gr.update()]), - inputs=[extension_to_install, search_text, sort_column], - outputs=[extensions_table, info], - ) - uninstall_extension_button.click( - fn=modules.ui.wrap_gradio_call(uninstall_extension, extra_outputs=[gr.update(), gr.update(), gr.update()]), - inputs=[extension_to_install, search_text, sort_column], - outputs=[extensions_table, info], - ) - update_extension_button.click( - fn=modules.ui.wrap_gradio_call(update_extension, extra_outputs=[gr.update(), gr.update(), gr.update()]), - inputs=[extension_to_install, search_text, sort_column], - outputs=[extensions_table, info], - ) - search_text.change( - fn=modules.ui.wrap_gradio_call(search_extensions, extra_outputs=[gr.update(), gr.update()]), - inputs=[search_text, sort_column], - outputs=[extensions_table, info], - ) - sort_column.change( - fn=modules.ui.wrap_gradio_call(search_extensions, extra_outputs=[gr.update(), gr.update()]), - inputs=[search_text, sort_column], - outputs=[extensions_table, info], - ) - with gr.TabItem("Manual install", id="install_from_url"): - install_url = gr.Text(label="URL for extension's git repository") - install_branch = gr.Text(label="Specific branch name", placeholder="Leave empty for default main branch") - install_dirname = gr.Text(label="Local directory name", placeholder="Leave empty for auto") - install_button = gr.Button(value="Install", variant="primary") - info = gr.HTML(elem_id="extension_info") - install_button.click( - fn=modules.ui.wrap_gradio_call(install_extension_from_url, extra_outputs=[gr.update()]), - inputs=[install_dirname, install_url, install_branch, search_text, sort_column], - outputs=[extensions_table, info], - ) - return ui +import json +import os.path +import shutil +import errno +import html +from datetime import datetime +import git +import gradio as gr +from modules import extensions, shared, paths, errors +from modules.call_queue import wrap_gradio_gpu_call + + +extensions_index = "https://vladmandic.github.io/sd-data/pages/extensions.json" +hide_tags = ["localization"] +extensions_list = [] +sort_ordering = { + "default": (True, lambda x: x.get('sort_default', '')), + "user extensions": (True, lambda x: x.get('sort_user', '')), + "update avilable": (True, lambda x: x.get('sort_update', '')), + "updated date": (True, lambda x: x.get('updated', '2000-01-01T00:00')), + "created date": (True, lambda x: x.get('created', '2000-01-01T00:00')), + "name": (False, lambda x: x.get('name', '').lower()), + "enabled": (False, lambda x: x.get('sort_enabled', '').lower()), + "size": (True, lambda x: x.get('size', 0)), + "stars": (True, lambda x: x.get('stars', 0)), + "commits": (True, lambda x: x.get('commits', 0)), + "issues": (True, lambda x: x.get('issues', 0)), +} + + +def update_extension_list(): + global extensions_list # pylint: disable=global-statement + try: + with open(os.path.join(paths.script_path, "html", "extensions.json"), "r", encoding="utf-8") as f: + extensions_list = json.loads(f.read()) + shared.log.debug(f'Extensions list loaded: {os.path.join(paths.script_path, "html", "extensions.json")}') + except: + shared.log.debug(f'Extensions list failed to load: {os.path.join(paths.script_path, "html", "extensions.json")}') + found = [] + for ext in extensions.extensions: + ext.read_info_from_repo() + for ext in extensions_list: + installed = [extension for extension in extensions.extensions + if extension.git_name == ext['name'] + or extension.name == ext['name'] + or (extension.remote or '').startswith(ext['url'].replace('.git', ''))] + if len(installed) > 0: + found.append(installed[0]) + not_matched = [extension for extension in extensions.extensions if extension not in found] + for ext in not_matched: + entry = { + "name": ext.name or "", + "description": ext.description or "", + "url": ext.remote or "", + "tags": [], + "stars": 0, + "issues": 0, + "commits": 0, + "size": 0, + "long": ext.git_name or ext.name or "", + "added": ext.ctime, + "created": ext.ctime, + "updated": ext.mtime, + } + extensions_list.append(entry) + + +def check_access(): + assert not shared.cmd_opts.disable_extension_access, "extension access disabled because of command line flags" + + +def apply_and_restart(disable_list, update_list, disable_all): + check_access() + shared.log.debug(f'Extensions apply: disable={disable_list} update={update_list}') + disabled = json.loads(disable_list) + assert type(disabled) == list, f"wrong disable_list data for apply_and_restart: {disable_list}" + update = json.loads(update_list) + assert type(update) == list, f"wrong update_list data for apply_and_restart: {update_list}" + update = set(update) + for ext in extensions.extensions: + if ext.name not in update: + continue + try: + ext.fetch_and_reset_hard() + 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 + shared.opts.save(shared.config_filename) + shared.restart_server(restart=True) + + +def check_updates(_id_task, disable_list, search_text, sort_column): + check_access() + disabled = json.loads(disable_list) + assert type(disabled) == list, f"wrong disable_list data for apply_and_restart: {disable_list}" + exts = [ext for ext in extensions.extensions if ext.remote is not None and ext.name not in disabled] + shared.log.info(f'Extensions update check: update={len(exts)} disabled={len(disable_list)}') + shared.state.job_count = len(exts) + for ext in exts: + shared.state.textinfo = ext.name + try: + ext.check_updates() + if ext.can_update: + ext.fetch_and_reset_hard() + ext.read_info_from_repo() + commit_date = ext.commit_date or 1577836800 + shared.log.info(f'Extensions updated: {ext.name} {ext.commit_hash[:8]} {datetime.utcfromtimestamp(commit_date)}') + else: + commit_date = ext.commit_date or 1577836800 + shared.log.debug(f'Extensions no update available: {ext.name} {ext.commit_hash[:8]} {datetime.utcfromtimestamp(commit_date)}') + except FileNotFoundError as e: + if 'FETCH_HEAD' not in str(e): + raise + except Exception: + errors.display(e, f'extensions check update: {ext.name}') + shared.state.nextjob() + return refresh_extensions_list_from_data(search_text, sort_column), "Extension update complete | Restart required" + + +def make_commit_link(commit_hash, remote, text=None): + if text is None: + text = commit_hash[:8] + if remote.startswith("https://github.com/"): + href = os.path.join(remote, "commit", commit_hash) + return f'{text}' + else: + return text + + +def normalize_git_url(url): + if url is None: + return "" + url = url.replace(".git", "") + return url + + +def install_extension_from_url(dirname, url, branch_name, search_text, sort_column): + check_access() + assert url, 'No URL specified' + if dirname is None or dirname == "": + *parts, last_part = url.split('/') # pylint: disable=unused-variable + last_part = normalize_git_url(last_part) + dirname = last_part + target_dir = os.path.join(extensions.extensions_dir, dirname) + shared.log.info(f'Installing extension: {url} into {target_dir}') + assert not os.path.exists(target_dir), f'Extension directory already exists: {target_dir}' + normalized_url = normalize_git_url(url) + assert len([x for x in extensions.extensions if normalize_git_url(x.remote) == normalized_url]) == 0, 'Extension with this URL is already installed' + tmpdir = os.path.join(paths.data_path, "tmp", dirname) + try: + shutil.rmtree(tmpdir, True) + if not branch_name: + # if no branch is specified, use the default branch + with git.Repo.clone_from(url, tmpdir) as repo: + repo.remote().fetch() + for submodule in repo.submodules: + submodule.update() + else: + with git.Repo.clone_from(url, tmpdir, branch=branch_name) as repo: + repo.remote().fetch() + for submodule in repo.submodules: + submodule.update() + try: + os.rename(tmpdir, target_dir) + except OSError as err: + if err.errno == errno.EXDEV: + shutil.move(tmpdir, target_dir) + else: + raise err + from launch import run_extension_installer + run_extension_installer(target_dir) + extensions.list_extensions() + return [refresh_extensions_list_from_data(search_text, sort_column), html.escape(f"Extension installed: {target_dir} | Restart required")] + finally: + shutil.rmtree(tmpdir, True) + + +def install_extension(extension_to_install, search_text, sort_column): + shared.log.info(f'Extension install: {extension_to_install}') + code, message = install_extension_from_url(None, extension_to_install, None, search_text, sort_column) + return code, message + + +def uninstall_extension(extension_path, search_text, sort_column): + def errorRemoveReadonly(func, path, exc): + import stat + excvalue = exc[1] + shared.log.debug(f'Exception during cleanup: {func} {path} {excvalue.strerror}') + if func in (os.rmdir, os.remove, os.unlink) and excvalue.errno == errno.EACCES: + shared.log.debug(f'Retrying cleanup: {path}') + os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + func(path) + + ext = [extension for extension in extensions.extensions if os.path.abspath(extension.path) == os.path.abspath(extension_path)] + if len(ext) > 0 and os.path.isdir(extension_path): + found = ext[0] + try: + shutil.rmtree(found.path, ignore_errors=False, onerror=errorRemoveReadonly) + except Exception as e: + shared.log.warning(f'Extension uninstall failed: {found.path} {e}') + extensions.extensions = [extension for extension in extensions.extensions if os.path.abspath(found.path) != os.path.abspath(extension_path)] + update_extension_list() + code = refresh_extensions_list_from_data(search_text, sort_column) + shared.log.info(f'Extension uninstalled: {found.path}') + return code, f"Extension uninstalled: {found.path} | Restart required" + else: + shared.log.warning(f'Extension uninstall cannot find extension: {extension_path}') + code = refresh_extensions_list_from_data(search_text, sort_column) + return code, f"Extension uninstalled failed: {extension_path}" + + +def update_extension(extension_path, search_text, sort_column): + exts = [extension for extension in extensions.extensions if os.path.abspath(extension.path) == os.path.abspath(extension_path)] + shared.state.job_count = len(exts) + for ext in exts: + shared.log.debug(f'Extensions update start: {ext.name} {ext.commit_hash} {ext.commit_date}') + shared.state.textinfo = ext.name + try: + ext.check_updates() + if ext.can_update: + ext.fetch_and_reset_hard() + ext.read_info_from_repo() + commit_date = ext.commit_date or 1577836800 + shared.log.info(f'Extensions updated: {ext.name} {ext.commit_hash[:8]} {datetime.utcfromtimestamp(commit_date)}') + else: + commit_date = ext.commit_date or 1577836800 + shared.log.info(f'Extensions no update available: {ext.name} {ext.commit_hash[:8]} {datetime.utcfromtimestamp(commit_date)}') + except FileNotFoundError as e: + if 'FETCH_HEAD' not in str(e): + raise + except Exception as e: + shared.log.error(f'Extensions update failed: {ext.name}') + errors.display(e, f'extensions check update: {ext.name}') + shared.log.debug(f'Extensions update finish: {ext.name} {ext.commit_hash} {ext.commit_date}') + shared.state.nextjob() + return refresh_extensions_list_from_data(search_text, sort_column), f"Extension updated | {extension_path} | Restart required" + + +def refresh_extensions_list(search_text, sort_column): + global extensions_list # pylint: disable=global-statement + import urllib.request + try: + with urllib.request.urlopen(extensions_index) as response: + text = response.read() + extensions_list = json.loads(text) + with open(os.path.join(paths.script_path, "html", "extensions.json"), "w", encoding="utf-8") as outfile: + json_object = json.dumps(extensions_list, indent=2) + outfile.write(json_object) + shared.log.debug(f'Updated extensions list: {len(extensions_list)} {extensions_index} {outfile}') + except Exception as e: + shared.log.warning(f'Updated extensions list failed: {extensions_index} {e}') + update_extension_list() + code = refresh_extensions_list_from_data(search_text, sort_column) + return code, f'Extensions | {len(extensions.extensions)} registered | {len(extensions_list)} available' + + +def search_extensions(search_text, sort_column): + code = refresh_extensions_list_from_data(search_text, sort_column) + return code, f'Search | {search_text} | {sort_column}' + + +def refresh_extensions_list_from_data(search_text, sort_column): + shared.log.debug(f'Extensions manager: refresh list search="{search_text}" sort="{sort_column}"') + code = """ + + + + + + + + + + + + + + + + + + + + """ + for ext in extensions_list: + extension = [extension for extension in extensions.extensions if extension.git_name == ext['name'] or extension.name == ext['name']] + if len(extension) > 0: + extension[0].read_info_from_repo() + ext['installed'] = len(extension) > 0 + ext['commit_date'] = extension[0].commit_date if len(extension) > 0 else 1577836800 + ext['is_builtin'] = extension[0].is_builtin if len(extension) > 0 else False + ext['version'] = extension[0].version if len(extension) > 0 else '' + ext['enabled'] = extension[0].enabled if len(extension) > 0 else '' + ext['remote'] = extension[0].remote if len(extension) > 0 else None + ext['path'] = extension[0].path if len(extension) > 0 else '' + ext['sort_default'] = f"{'1' if ext['is_builtin'] else '0'}{'1' if ext['installed'] else '0'}{ext.get('updated', '2000-01-01T00:00')}" + sort_reverse, sort_function = sort_ordering[sort_column] + + def dt(x: str): + val = ext.get(x, None) + if val is not None: + return datetime.fromisoformat(val[:-1]).strftime('%a %b%d %Y %H:%M') + else: + return "N/A" + + for ext in sorted(extensions_list, key=sort_function, reverse=sort_reverse): + name = ext.get("name", "unknown") + added = dt('added') + created = dt('created') + pushed = dt('pushed') + updated = dt('updated') + url = ext.get('url', None) + size = ext.get('size', 0) + stars = ext.get('stars', 0) + issues = ext.get('issues', 0) + commits = ext.get('commits', 0) + description = ext.get("description", "") + installed = ext.get("installed", False) + enabled = ext.get("enabled", False) + path = ext.get("path", "") + remote = ext.get("remote", None) + commit_date = ext.get("commit_date", 1577836800) or 1577836800 + update_available = (remote is not None) & (installed) & (datetime.utcfromtimestamp(commit_date + 60 * 60) < datetime.fromisoformat(ext.get('updated', '2000-01-01T00:00:00.000Z')[:-1])) + ext['sort_user'] = f"{'0' if ext['is_builtin'] else '1'}{'1' if ext['installed'] else '0'}{ext.get('name', '')}" + ext['sort_enabled'] = f"{'0' if ext['enabled'] else '1'}{'1' if ext['is_builtin'] else '0'}{'1' if ext['installed'] else '0'}{ext.get('updated', '2000-01-01T00:00')}" + ext['sort_update'] = f"{'1' if update_available else '0'}{'1' if ext['installed'] else '0'}{ext.get('updated', '2000-01-01T00:00')}" + tags = ext.get("tags", []) + tags_string = ' '.join(tags) + tags = tags + ["installed"] if installed else tags + if len([x for x in tags if x in hide_tags]) > 0: + continue + if search_text and search_text.strip(): + if search_text.lower() not in html.escape(name).lower() and search_text.lower() not in html.escape(description).lower() and search_text.lower() not in html.escape(tags_string).lower(): + continue + version_code = '' + type_code = '' + install_code = '' + enabled_code = '' + if installed: + type_code = f"""
{"SYSTEM" if ext['is_builtin'] else 'USER'}
""" + version_code = f"""
{ext['version']}
""" + enabled_code = f"""""" + masked_path = html.escape(path.replace('\\', '/')) + if not ext['is_builtin']: + install_code = f"""""" + if update_available: + install_code += f"""""" + else: + install_code = f"""""" + tags_text = ", ".join([f"{x}" for x in tags]) + code += f""" + + {enabled_code} + + + + + + """ + code += "
EnabledExtensionDescriptionTypeCurrent version
{html.escape(name)}
{tags_text}
{html.escape(description)} +

Created {html.escape(created)} | Added {html.escape(added)} | Pushed {html.escape(pushed)} | Updated {html.escape(updated)}

+

Stars {html.escape(str(stars))} | Size {html.escape(str(size))} | Commits {html.escape(str(commits))} | Issues {html.escape(str(issues))}

+
{type_code}{version_code}{install_code}
" + return code + + +def create_ui(): + import modules.ui + with gr.Blocks(analytics_enabled=False) as ui: + extensions_disable_all = gr.Radio(label="Disable all extensions", choices=["none", "user", "all"], value=shared.opts.disable_all_extensions, elem_id="extensions_disable_all", visible=False) + extensions_disabled_list = gr.Text(elem_id="extensions_disabled_list", visible=False).style(container=False) + extensions_update_list = gr.Text(elem_id="extensions_update_list", visible=False).style(container=False) + with gr.Tabs(elem_id="tabs_extensions"): + with gr.TabItem("Manage Extensions", id="manage"): + with gr.Row(elem_id="extensions_installed_top"): + extension_to_install = gr.Text(elem_id="extension_to_install", visible=False) + install_extension_button = gr.Button(elem_id="install_extension_button", visible=False) + uninstall_extension_button = gr.Button(elem_id="uninstall_extension_button", visible=False) + update_extension_button = gr.Button(elem_id="update_extension_button", visible=False) + with gr.Column(scale=4): + search_text = gr.Text(label="Search") + info = gr.HTML('Note: After any operation such as install/uninstall or enable/disable, please restart the server') + with gr.Column(scale=1): + sort_column = gr.Dropdown(value="default", label="Sort by", choices=list(sort_ordering.keys()), multiselect=False) + with gr.Column(scale=1): + refresh_extensions_button = gr.Button(value="Refresh extension list", variant="primary") + check = gr.Button(value="Update installed extensions", variant="primary") + apply = gr.Button(value="Apply changes & restart server", variant="primary") + update_extension_list() + extensions_table = gr.HTML(refresh_extensions_list_from_data(search_text.value, sort_column.value)) + check.click( + fn=wrap_gradio_gpu_call(check_updates, extra_outputs=[gr.update()]), + _js="extensions_check", + inputs=[info, extensions_disabled_list, search_text, sort_column], + outputs=[extensions_table, info], + ) + apply.click( + fn=apply_and_restart, + _js="extensions_apply", + inputs=[extensions_disabled_list, extensions_update_list, extensions_disable_all], + outputs=[], + ) + refresh_extensions_button.click( + fn=modules.ui.wrap_gradio_call(refresh_extensions_list, extra_outputs=[gr.update(), gr.update()]), + inputs=[search_text, sort_column], + outputs=[extensions_table, info], + ) + install_extension_button.click( + fn=modules.ui.wrap_gradio_call(install_extension, extra_outputs=[gr.update(), gr.update(), gr.update()]), + inputs=[extension_to_install, search_text, sort_column], + outputs=[extensions_table, info], + ) + uninstall_extension_button.click( + fn=modules.ui.wrap_gradio_call(uninstall_extension, extra_outputs=[gr.update(), gr.update(), gr.update()]), + inputs=[extension_to_install, search_text, sort_column], + outputs=[extensions_table, info], + ) + update_extension_button.click( + fn=modules.ui.wrap_gradio_call(update_extension, extra_outputs=[gr.update(), gr.update(), gr.update()]), + inputs=[extension_to_install, search_text, sort_column], + outputs=[extensions_table, info], + ) + search_text.change( + fn=modules.ui.wrap_gradio_call(search_extensions, extra_outputs=[gr.update(), gr.update()]), + inputs=[search_text, sort_column], + outputs=[extensions_table, info], + ) + sort_column.change( + fn=modules.ui.wrap_gradio_call(search_extensions, extra_outputs=[gr.update(), gr.update()]), + inputs=[search_text, sort_column], + outputs=[extensions_table, info], + ) + with gr.TabItem("Manual install", id="install_from_url"): + install_url = gr.Text(label="URL for extension's git repository") + install_branch = gr.Text(label="Specific branch name", placeholder="Leave empty for default main branch") + install_dirname = gr.Text(label="Local directory name", placeholder="Leave empty for auto") + install_button = gr.Button(value="Install", variant="primary") + info = gr.HTML(elem_id="extension_info") + install_button.click( + fn=modules.ui.wrap_gradio_call(install_extension_from_url, extra_outputs=[gr.update()]), + inputs=[install_dirname, install_url, install_branch, search_text, sort_column], + outputs=[extensions_table, info], + ) + return ui diff --git a/modules/ui_extra_networks_hypernets.py b/modules/ui_extra_networks_hypernets.py index 545898486..d29863212 100644 --- a/modules/ui_extra_networks_hypernets.py +++ b/modules/ui_extra_networks_hypernets.py @@ -12,7 +12,7 @@ class ExtraNetworksPageHypernetworks(ui_extra_networks.ExtraNetworksPage): def list_items(self): for name, path in shared.hypernetworks.items(): - path, ext = os.path.splitext(path) + path, _ext = os.path.splitext(path) yield { "name": name, "filename": path,