aboutsummaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/api/api.py22
-rw-r--r--modules/call_queue.py5
-rw-r--r--modules/cmd_args.py1
-rw-r--r--modules/esrgan_model.py11
-rw-r--r--modules/esrgan_model_arch.py16
-rw-r--r--modules/extra_networks_hypernet.py3
-rw-r--r--modules/generation_parameters_copypaste.py10
-rw-r--r--modules/hashes.py4
-rw-r--r--modules/images.py9
-rw-r--r--modules/img2img.py3
-rw-r--r--modules/interrogate.py4
-rw-r--r--modules/mac_specific.py9
-rw-r--r--modules/modelloader.py30
-rw-r--r--modules/models/diffusion/ddpm_edit.py4
-rw-r--r--modules/models/diffusion/uni_pc/uni_pc.py4
-rw-r--r--modules/ngrok.py4
-rw-r--r--modules/paths.py2
-rw-r--r--modules/processing.py24
-rw-r--r--modules/progress.py3
-rw-r--r--modules/realesrgan_model.py8
-rw-r--r--modules/safe.py6
-rw-r--r--modules/scripts.py5
-rw-r--r--modules/sd_hijack_clip_old.py3
-rw-r--r--modules/sd_hijack_optimizations.py3
-rw-r--r--modules/sd_hijack_unet.py2
-rw-r--r--modules/sd_models.py58
-rw-r--r--modules/sd_models_config.py2
-rw-r--r--modules/sd_samplers_kdiffusion.py2
-rw-r--r--modules/sd_vae.py2
-rw-r--r--modules/shared.py68
-rw-r--r--modules/styles.py2
-rw-r--r--modules/textual_inversion/autocrop.py6
-rw-r--r--modules/textual_inversion/dataset.py2
-rw-r--r--modules/textual_inversion/preprocess.py6
-rw-r--r--modules/textual_inversion/textual_inversion.py12
-rw-r--r--modules/ui.py84
-rw-r--r--modules/ui_extensions.py3
-rw-r--r--modules/ui_extra_networks.py73
-rw-r--r--modules/ui_tempdir.py2
39 files changed, 337 insertions, 180 deletions
diff --git a/modules/api/api.py b/modules/api/api.py
index cdbdce32..9bb95dfd 100644
--- a/modules/api/api.py
+++ b/modules/api/api.py
@@ -570,20 +570,20 @@ class Api:
filename = create_embedding(**args) # create empty embedding
sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings() # reload embeddings so new one can be immediately used
shared.state.end()
- return CreateResponse(info = "create embedding filename: {filename}".format(filename = filename))
+ return CreateResponse(info=f"create embedding filename: {filename}")
except AssertionError as e:
shared.state.end()
- return TrainResponse(info = "create embedding error: {error}".format(error = e))
+ return TrainResponse(info=f"create embedding error: {e}")
def create_hypernetwork(self, args: dict):
try:
shared.state.begin()
filename = create_hypernetwork(**args) # create empty embedding
shared.state.end()
- return CreateResponse(info = "create hypernetwork filename: {filename}".format(filename = filename))
+ return CreateResponse(info=f"create hypernetwork filename: {filename}")
except AssertionError as e:
shared.state.end()
- return TrainResponse(info = "create hypernetwork error: {error}".format(error = e))
+ return TrainResponse(info=f"create hypernetwork error: {e}")
def preprocess(self, args: dict):
try:
@@ -593,13 +593,13 @@ class Api:
return PreprocessResponse(info = 'preprocess complete')
except KeyError as e:
shared.state.end()
- return PreprocessResponse(info = "preprocess error: invalid token: {error}".format(error = e))
+ return PreprocessResponse(info=f"preprocess error: invalid token: {e}")
except AssertionError as e:
shared.state.end()
- return PreprocessResponse(info = "preprocess error: {error}".format(error = e))
+ return PreprocessResponse(info=f"preprocess error: {e}")
except FileNotFoundError as e:
shared.state.end()
- return PreprocessResponse(info = 'preprocess error: {error}'.format(error = e))
+ return PreprocessResponse(info=f'preprocess error: {e}')
def train_embedding(self, args: dict):
try:
@@ -617,10 +617,10 @@ class Api:
if not apply_optimizations:
sd_hijack.apply_optimizations()
shared.state.end()
- return TrainResponse(info = "train embedding complete: filename: {filename} error: {error}".format(filename = filename, error = error))
+ return TrainResponse(info=f"train embedding complete: filename: {filename} error: {error}")
except AssertionError as msg:
shared.state.end()
- return TrainResponse(info = "train embedding error: {msg}".format(msg = msg))
+ return TrainResponse(info=f"train embedding error: {msg}")
def train_hypernetwork(self, args: dict):
try:
@@ -641,10 +641,10 @@ class Api:
if not apply_optimizations:
sd_hijack.apply_optimizations()
shared.state.end()
- return TrainResponse(info="train embedding complete: filename: {filename} error: {error}".format(filename=filename, error=error))
+ return TrainResponse(info=f"train embedding complete: filename: {filename} error: {error}")
except AssertionError as msg:
shared.state.end()
- return TrainResponse(info="train embedding error: {error}".format(error=error))
+ return TrainResponse(info=f"train embedding error: {error}")
def get_memory(self):
try:
diff --git a/modules/call_queue.py b/modules/call_queue.py
index 1829f3a6..447bb764 100644
--- a/modules/call_queue.py
+++ b/modules/call_queue.py
@@ -60,7 +60,7 @@ def wrap_gradio_call(func, extra_outputs=None, add_stats=False):
max_debug_str_len = 131072 # (1024*1024)/8
print("Error completing request", file=sys.stderr)
- argStr = f"Arguments: {str(args)} {str(kwargs)}"
+ argStr = f"Arguments: {args} {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)
@@ -73,7 +73,8 @@ def wrap_gradio_call(func, extra_outputs=None, add_stats=False):
if extra_outputs_array is None:
extra_outputs_array = [None, '']
- res = extra_outputs_array + [f"<div class='error'>{html.escape(type(e).__name__+': '+str(e))}</div>"]
+ error_message = f'{type(e).__name__}: {e}'
+ res = extra_outputs_array + [f"<div class='error'>{html.escape(error_message)}</div>"]
shared.state.skipped = False
shared.state.interrupted = False
diff --git a/modules/cmd_args.py b/modules/cmd_args.py
index f47c21bb..d906a571 100644
--- a/modules/cmd_args.py
+++ b/modules/cmd_args.py
@@ -102,3 +102,4 @@ parser.add_argument("--no-gradio-queue", action='store_true', help="Disables gra
parser.add_argument("--skip-version-check", action='store_true', help="Do not check versions of torch and xformers")
parser.add_argument("--no-hashing", action='store_true', help="disable sha256 hashing of checkpoints to help loading performance", default=False)
parser.add_argument("--no-download-sd-model", action='store_true', help="don't download SD1.5 model even if no model is found in --ckpt-dir", default=False)
+parser.add_argument('--subpath', type=str, help='customize the subpath for gradio, use with reverse proxy') \ No newline at end of file
diff --git a/modules/esrgan_model.py b/modules/esrgan_model.py
index 9a9c38f1..f4369257 100644
--- a/modules/esrgan_model.py
+++ b/modules/esrgan_model.py
@@ -156,13 +156,16 @@ class UpscalerESRGAN(Upscaler):
def load_model(self, path: str):
if "http" in path:
- filename = load_file_from_url(url=self.model_url, model_dir=self.model_path,
- file_name="%s.pth" % self.model_name,
- progress=True)
+ filename = load_file_from_url(
+ url=self.model_url,
+ model_dir=self.model_path,
+ file_name=f"{self.model_name}.pth",
+ progress=True,
+ )
else:
filename = path
if not os.path.exists(filename) or filename is None:
- print("Unable to load %s from %s" % (self.model_path, filename))
+ print(f"Unable to load {self.model_path} from {filename}")
return None
state_dict = torch.load(filename, map_location='cpu' if devices.device_esrgan.type == 'mps' else None)
diff --git a/modules/esrgan_model_arch.py b/modules/esrgan_model_arch.py
index 1b52b0f5..6071fea7 100644
--- a/modules/esrgan_model_arch.py
+++ b/modules/esrgan_model_arch.py
@@ -38,7 +38,7 @@ class RRDBNet(nn.Module):
elif upsample_mode == 'pixelshuffle':
upsample_block = pixelshuffle_block
else:
- raise NotImplementedError('upsample mode [{:s}] is not found'.format(upsample_mode))
+ raise NotImplementedError(f'upsample mode [{upsample_mode}] is not found')
if upscale == 3:
upsampler = upsample_block(nf, nf, 3, act_type=act_type, convtype=convtype)
else:
@@ -261,10 +261,10 @@ class Upsample(nn.Module):
def extra_repr(self):
if self.scale_factor is not None:
- info = 'scale_factor=' + str(self.scale_factor)
+ info = f'scale_factor={self.scale_factor}'
else:
- info = 'size=' + str(self.size)
- info += ', mode=' + self.mode
+ info = f'size={self.size}'
+ info += f', mode={self.mode}'
return info
@@ -350,7 +350,7 @@ def act(act_type, inplace=True, neg_slope=0.2, n_prelu=1, beta=1.0):
elif act_type == 'sigmoid': # [0, 1] range output
layer = nn.Sigmoid()
else:
- raise NotImplementedError('activation layer [{:s}] is not found'.format(act_type))
+ raise NotImplementedError(f'activation layer [{act_type}] is not found')
return layer
@@ -372,7 +372,7 @@ def norm(norm_type, nc):
elif norm_type == 'none':
def norm_layer(x): return Identity()
else:
- raise NotImplementedError('normalization layer [{:s}] is not found'.format(norm_type))
+ raise NotImplementedError(f'normalization layer [{norm_type}] is not found')
return layer
@@ -388,7 +388,7 @@ def pad(pad_type, padding):
elif pad_type == 'zero':
layer = nn.ZeroPad2d(padding)
else:
- raise NotImplementedError('padding layer [{:s}] is not implemented'.format(pad_type))
+ raise NotImplementedError(f'padding layer [{pad_type}] is not implemented')
return layer
@@ -432,7 +432,7 @@ def conv_block(in_nc, out_nc, kernel_size, stride=1, dilation=1, groups=1, bias=
pad_type='zero', norm_type=None, act_type='relu', mode='CNA', convtype='Conv2D',
spectral_norm=False):
""" Conv layer with padding, normalization, activation """
- assert mode in ['CNA', 'NAC', 'CNAC'], 'Wrong conv mode [{:s}]'.format(mode)
+ assert mode in ['CNA', 'NAC', 'CNAC'], f'Wrong conv mode [{mode}]'
padding = get_valid_padding(kernel_size, dilation)
p = pad(pad_type, padding) if pad_type and pad_type != 'zero' else None
padding = padding if pad_type == 'zero' else 0
diff --git a/modules/extra_networks_hypernet.py b/modules/extra_networks_hypernet.py
index 33d100dd..04f27c9f 100644
--- a/modules/extra_networks_hypernet.py
+++ b/modules/extra_networks_hypernet.py
@@ -10,7 +10,8 @@ class ExtraNetworkHypernet(extra_networks.ExtraNetwork):
additional = shared.opts.sd_hypernetwork
if additional != "None" and additional in shared.hypernetworks and len([x for x in params_list if x.items[0] == additional]) == 0:
- p.all_prompts = [x + f"<hypernet:{additional}:{shared.opts.extra_networks_default_multiplier}>" for x in p.all_prompts]
+ hypernet_prompt_text = f"<hypernet:{additional}:{shared.opts.extra_networks_default_multiplier}>"
+ p.all_prompts = [f"{prompt}{hypernet_prompt_text}" for prompt in p.all_prompts]
params_list.append(extra_networks.ExtraNetworkParams(items=[additional, shared.opts.extra_networks_default_multiplier]))
names = []
diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py
index 99f1a0d3..fe8b18b2 100644
--- a/modules/generation_parameters_copypaste.py
+++ b/modules/generation_parameters_copypaste.py
@@ -59,6 +59,7 @@ def image_from_url_text(filedata):
is_in_right_dir = ui_tempdir.check_tmp_file(shared.demo, filename)
assert is_in_right_dir, 'trying to open image file outside of allowed directories'
+ filename = filename.rsplit('?', 1)[0]
return Image.open(filename)
if type(filedata) == list:
@@ -129,6 +130,7 @@ def connect_paste_params_buttons():
_js=jsfunc,
inputs=[binding.source_image_component],
outputs=[destination_image_component, destination_width_component, destination_height_component] if destination_width_component else [destination_image_component],
+ show_progress=False,
)
if binding.source_text_component is not None and fields is not None:
@@ -140,6 +142,7 @@ def connect_paste_params_buttons():
fn=lambda *x: x,
inputs=[field for field, name in paste_fields[binding.source_tabname]["fields"] if name in paste_field_names],
outputs=[field for field, name in fields if name in paste_field_names],
+ show_progress=False,
)
binding.paste_button.click(
@@ -147,6 +150,7 @@ def connect_paste_params_buttons():
_js=f"switch_to_{binding.tabname}",
inputs=None,
outputs=None,
+ show_progress=False,
)
@@ -265,8 +269,8 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model
v = v[1:-1] if v[0] == '"' and v[-1] == '"' else v
m = re_imagesize.match(v)
if m is not None:
- res[k+"-1"] = m.group(1)
- res[k+"-2"] = m.group(2)
+ res[f"{k}-1"] = m.group(1)
+ res[f"{k}-2"] = m.group(2)
else:
res[k] = v
@@ -409,12 +413,14 @@ def connect_paste(button, paste_fields, input_comp, override_settings_component,
fn=paste_func,
inputs=[input_comp],
outputs=[x[0] for x in paste_fields],
+ show_progress=False,
)
button.click(
fn=None,
_js=f"recalculate_prompts_{tabname}",
inputs=[],
outputs=[],
+ show_progress=False,
)
diff --git a/modules/hashes.py b/modules/hashes.py
index 83272a07..032120f4 100644
--- a/modules/hashes.py
+++ b/modules/hashes.py
@@ -13,7 +13,7 @@ cache_data = None
def dump_cache():
- with filelock.FileLock(cache_filename+".lock"):
+ with filelock.FileLock(f"{cache_filename}.lock"):
with open(cache_filename, "w", encoding="utf8") as file:
json.dump(cache_data, file, indent=4)
@@ -22,7 +22,7 @@ def cache(subsection):
global cache_data
if cache_data is None:
- with filelock.FileLock(cache_filename+".lock"):
+ with filelock.FileLock(f"{cache_filename}.lock"):
if not os.path.isfile(cache_filename):
cache_data = {}
else:
diff --git a/modules/images.py b/modules/images.py
index fd173829..a41965ab 100644
--- a/modules/images.py
+++ b/modules/images.py
@@ -357,6 +357,7 @@ class FilenameGenerator:
'generation_number': lambda self: NOTHING_AND_SKIP_PREVIOUS_TEXT if self.p.n_iter == 1 and self.p.batch_size == 1 else self.p.iteration * self.p.batch_size + self.p.batch_index + 1,
'hasprompt': lambda self, *args: self.hasprompt(*args), # accepts formats:[hasprompt<prompt1|default><prompt2>..]
'clip_skip': lambda self: opts.data["CLIP_stop_at_last_layers"],
+ 'denoising': lambda self: self.p.denoising_strength if self.p and self.p.denoising_strength else NOTHING_AND_SKIP_PREVIOUS_TEXT,
}
default_time_format = '%Y%m%d%H%M%S'
@@ -466,7 +467,7 @@ def get_next_sequence_number(path, basename):
"""
result = -1
if basename != '':
- basename = basename + "-"
+ basename = f"{basename}-"
prefix_length = len(basename)
for p in os.listdir(path):
@@ -535,7 +536,7 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i
add_number = opts.save_images_add_number or file_decoration == ''
if file_decoration != "" and add_number:
- file_decoration = "-" + file_decoration
+ file_decoration = f"-{file_decoration}"
file_decoration = namegen.apply(file_decoration) + suffix
@@ -565,7 +566,7 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i
def _atomically_save_image(image_to_save, filename_without_extension, extension):
# save image with .tmp extension to avoid race condition when another process detects new image in the directory
- temp_file_path = filename_without_extension + ".tmp"
+ temp_file_path = f"{filename_without_extension}.tmp"
image_format = Image.registered_extensions()[extension]
if extension.lower() == '.png':
@@ -625,7 +626,7 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i
if opts.save_txt and info is not None:
txt_fullfn = f"{fullfn_without_extension}.txt"
with open(txt_fullfn, "w", encoding="utf8") as file:
- file.write(info + "\n")
+ file.write(f"{info}\n")
else:
txt_fullfn = None
diff --git a/modules/img2img.py b/modules/img2img.py
index 56c846d6..9fc3a698 100644
--- a/modules/img2img.py
+++ b/modules/img2img.py
@@ -48,7 +48,8 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args):
try:
img = Image.open(image)
- except UnidentifiedImageError:
+ except UnidentifiedImageError as e:
+ print(e)
continue
# Use the EXIF orientation of photos taken by smartphones.
img = ImageOps.exif_transpose(img)
diff --git a/modules/interrogate.py b/modules/interrogate.py
index e1665708..9f7d657f 100644
--- a/modules/interrogate.py
+++ b/modules/interrogate.py
@@ -28,7 +28,7 @@ def category_types():
def download_default_clip_interrogate_categories(content_dir):
print("Downloading CLIP categories...")
- tmpdir = content_dir + "_tmp"
+ tmpdir = f"{content_dir}_tmp"
category_types = ["artists", "flavors", "mediums", "movements"]
try:
@@ -214,7 +214,7 @@ class InterrogateModels:
if shared.opts.interrogate_return_ranks:
res += f", ({match}:{score/100:.3f})"
else:
- res += ", " + match
+ res += f", {match}"
except Exception:
print("Error interrogating", file=sys.stderr)
diff --git a/modules/mac_specific.py b/modules/mac_specific.py
index 6fe8dea0..40ce2101 100644
--- a/modules/mac_specific.py
+++ b/modules/mac_specific.py
@@ -54,6 +54,11 @@ if has_mps:
CondFunc('torch.cumsum', cumsum_fix_func, None)
CondFunc('torch.Tensor.cumsum', cumsum_fix_func, None)
CondFunc('torch.narrow', lambda orig_func, *args, **kwargs: orig_func(*args, **kwargs).clone(), None)
- if version.parse(torch.__version__) == version.parse("2.0"):
+
# MPS workaround for https://github.com/pytorch/pytorch/issues/96113
- CondFunc('torch.nn.functional.layer_norm', lambda orig_func, x, normalized_shape, weight, bias, eps, **kwargs: orig_func(x.float(), normalized_shape, weight.float() if weight is not None else None, bias.float() if bias is not None else bias, eps).to(x.dtype), lambda *args, **kwargs: len(args) == 6)
+ CondFunc('torch.nn.functional.layer_norm', lambda orig_func, x, normalized_shape, weight, bias, eps, **kwargs: orig_func(x.float(), normalized_shape, weight.float() if weight is not None else None, bias.float() if bias is not None else bias, eps).to(x.dtype), lambda _, input, *args, **kwargs: len(args) == 4 and input.device.type == 'mps')
+
+ # MPS workaround for https://github.com/pytorch/pytorch/issues/92311
+ if platform.processor() == 'i386':
+ for funcName in ['torch.argmax', 'torch.Tensor.argmax']:
+ CondFunc(funcName, lambda _, input, *args, **kwargs: torch.max(input.float() if input.dtype == torch.int64 else input, *args, **kwargs)[1], lambda _, input, *args, **kwargs: input.device.type == 'mps') \ No newline at end of file
diff --git a/modules/modelloader.py b/modules/modelloader.py
index 522affc6..cb85ac4f 100644
--- a/modules/modelloader.py
+++ b/modules/modelloader.py
@@ -22,9 +22,6 @@ def load_models(model_path: str, model_url: str = None, command_path: str = None
"""
output = []
- if ext_filter is None:
- ext_filter = []
-
try:
places = []
@@ -39,22 +36,14 @@ def load_models(model_path: str, model_url: str = None, command_path: str = None
places.append(model_path)
for place in places:
- if os.path.exists(place):
- for file in glob.iglob(place + '**/**', recursive=True):
- full_path = file
- if os.path.isdir(full_path):
- continue
- if os.path.islink(full_path) and not os.path.exists(full_path):
- print(f"Skipping broken symlink: {full_path}")
- continue
- if ext_blacklist is not None and any([full_path.endswith(x) for x in ext_blacklist]):
- continue
- if len(ext_filter) != 0:
- model_name, extension = os.path.splitext(file)
- if extension not in ext_filter:
- continue
- if file not in output:
- output.append(full_path)
+ for full_path in shared.walk_files(place, allowed_extensions=ext_filter):
+ if os.path.islink(full_path) and not os.path.exists(full_path):
+ print(f"Skipping broken symlink: {full_path}")
+ continue
+ if ext_blacklist is not None and any([full_path.endswith(x) for x in ext_blacklist]):
+ continue
+ if full_path not in output:
+ output.append(full_path)
if model_url is not None and len(output) == 0:
if download_name is not None:
@@ -133,12 +122,9 @@ forbidden_upscaler_classes = set()
def list_builtin_upscalers():
- load_upscalers()
-
builtin_upscaler_classes.clear()
builtin_upscaler_classes.extend(Upscaler.__subclasses__())
-
def forbid_loaded_nonbuiltin_upscalers():
for cls in Upscaler.__subclasses__():
if cls not in builtin_upscaler_classes:
diff --git a/modules/models/diffusion/ddpm_edit.py b/modules/models/diffusion/ddpm_edit.py
index f3d49c44..f880bc3c 100644
--- a/modules/models/diffusion/ddpm_edit.py
+++ b/modules/models/diffusion/ddpm_edit.py
@@ -223,7 +223,7 @@ class DDPM(pl.LightningModule):
for k in keys:
for ik in ignore_keys:
if k.startswith(ik):
- print("Deleting key {} from state_dict.".format(k))
+ print(f"Deleting key {k} from state_dict.")
del sd[k]
missing, unexpected = self.load_state_dict(sd, strict=False) if not only_model else self.model.load_state_dict(
sd, strict=False)
@@ -386,7 +386,7 @@ class DDPM(pl.LightningModule):
_, loss_dict_no_ema = self.shared_step(batch)
with self.ema_scope():
_, loss_dict_ema = self.shared_step(batch)
- loss_dict_ema = {key + '_ema': loss_dict_ema[key] for key in loss_dict_ema}
+ loss_dict_ema = {f"{key}_ema": loss_dict_ema[key] for key in loss_dict_ema}
self.log_dict(loss_dict_no_ema, prog_bar=False, logger=True, on_step=False, on_epoch=True)
self.log_dict(loss_dict_ema, prog_bar=False, logger=True, on_step=False, on_epoch=True)
diff --git a/modules/models/diffusion/uni_pc/uni_pc.py b/modules/models/diffusion/uni_pc/uni_pc.py
index eb5f4e76..11b330bc 100644
--- a/modules/models/diffusion/uni_pc/uni_pc.py
+++ b/modules/models/diffusion/uni_pc/uni_pc.py
@@ -94,7 +94,7 @@ class NoiseScheduleVP:
"""
if schedule not in ['discrete', 'linear', 'cosine']:
- raise ValueError("Unsupported noise schedule {}. The schedule needs to be 'discrete' or 'linear' or 'cosine'".format(schedule))
+ raise ValueError(f"Unsupported noise schedule {schedule}. The schedule needs to be 'discrete' or 'linear' or 'cosine'")
self.schedule = schedule
if schedule == 'discrete':
@@ -469,7 +469,7 @@ class UniPC:
t = torch.linspace(t_T**(1. / t_order), t_0**(1. / t_order), N + 1).pow(t_order).to(device)
return t
else:
- raise ValueError("Unsupported skip_type {}, need to be 'logSNR' or 'time_uniform' or 'time_quadratic'".format(skip_type))
+ raise ValueError(f"Unsupported skip_type {skip_type}, need to be 'logSNR' or 'time_uniform' or 'time_quadratic'")
def get_orders_and_timesteps_for_singlestep_solver(self, steps, order, skip_type, t_T, t_0, device):
"""
diff --git a/modules/ngrok.py b/modules/ngrok.py
index 1ad7989b..7a7b4b26 100644
--- a/modules/ngrok.py
+++ b/modules/ngrok.py
@@ -7,8 +7,8 @@ def connect(token, port, region):
else:
if ':' in token:
# token = authtoken:username:password
- account = token.split(':')[1] + ':' + token.split(':')[-1]
- token = token.split(':')[0]
+ token, username, password = token.split(':', 2)
+ account = f"{username}:{password}"
config = conf.PyngrokConfig(
auth_token=token, region=region
diff --git a/modules/paths.py b/modules/paths.py
index 0e1e00e7..acf1894b 100644
--- a/modules/paths.py
+++ b/modules/paths.py
@@ -16,7 +16,7 @@ for possible_sd_path in possible_sd_paths:
sd_path = os.path.abspath(possible_sd_path)
break
-assert sd_path is not None, "Couldn't find Stable Diffusion in any of: " + str(possible_sd_paths)
+assert sd_path is not None, f"Couldn't find Stable Diffusion in any of: {possible_sd_paths}"
path_dirs = [
(sd_path, 'ldm', 'Stable Diffusion', []),
diff --git a/modules/processing.py b/modules/processing.py
index e8808beb..1a76e552 100644
--- a/modules/processing.py
+++ b/modules/processing.py
@@ -458,6 +458,16 @@ def fix_seed(p):
p.subseed = get_fixed_seed(p.subseed)
+def program_version():
+ import launch
+
+ res = launch.git_tag()
+ if res == "<none>":
+ res = None
+
+ return res
+
+
def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments=None, iteration=0, position_in_batch=0):
index = position_in_batch + iteration * p.batch_size
@@ -483,13 +493,14 @@ def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments=None, iter
"Init image hash": getattr(p, 'init_img_hash', None),
"RNG": opts.randn_source if opts.randn_source != "GPU" else None,
"NGMS": None if p.s_min_uncond == 0 else p.s_min_uncond,
+ "Version": program_version() if opts.add_version_to_infotext else None,
}
generation_params.update(p.extra_generation_params)
generation_params_text = ", ".join([k if k == v else f'{k}: {generation_parameters_copypaste.quote(v)}' for k, v in generation_params.items() if v is not None])
- negative_prompt_text = "\nNegative prompt: " + p.all_negative_prompts[index] if p.all_negative_prompts[index] else ""
+ negative_prompt_text = f"\nNegative prompt: {p.all_negative_prompts[index]}" if p.all_negative_prompts[index] else ""
return f"{all_prompts[index]}{negative_prompt_text}\n{generation_params_text}".strip()
@@ -769,7 +780,16 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
devices.torch_gc()
- res = Processed(p, output_images, p.all_seeds[0], infotext(), comments="".join(["\n\n" + x for x in comments]), subseed=p.all_subseeds[0], index_of_first_image=index_of_first_image, infotexts=infotexts)
+ res = Processed(
+ p,
+ images_list=output_images,
+ seed=p.all_seeds[0],
+ info=infotext(),
+ comments="".join(f"\n\n{comment}" for comment in comments),
+ subseed=p.all_subseeds[0],
+ index_of_first_image=index_of_first_image,
+ infotexts=infotexts,
+ )
if p.scripts is not None:
p.scripts.postprocess(p, res)
diff --git a/modules/progress.py b/modules/progress.py
index 5655346b..948e6f00 100644
--- a/modules/progress.py
+++ b/modules/progress.py
@@ -96,7 +96,8 @@ def progressapi(req: ProgressRequest):
if image is not None:
buffered = io.BytesIO()
image.save(buffered, format="png")
- live_preview = 'data:image/png;base64,' + base64.b64encode(buffered.getvalue()).decode("ascii")
+ base64_image = base64.b64encode(buffered.getvalue()).decode('ascii')
+ live_preview = f"data:image/png;base64,{base64_image}"
id_live_preview = shared.state.id_live_preview
else:
live_preview = None
diff --git a/modules/realesrgan_model.py b/modules/realesrgan_model.py
index d6079433..efd7fca5 100644
--- a/modules/realesrgan_model.py
+++ b/modules/realesrgan_model.py
@@ -28,9 +28,9 @@ class UpscalerRealESRGAN(Upscaler):
for scaler in scalers:
if scaler.local_data_path.startswith("http"):
filename = modelloader.friendly_name(scaler.local_data_path)
- local = next(iter([local_model for local_model in local_model_paths if local_model.endswith(filename + '.pth')]), None)
- if local:
- scaler.local_data_path = local
+ local_model_candidates = [local_model for local_model in local_model_paths if local_model.endswith(f"{filename}.pth")]
+ if local_model_candidates:
+ scaler.local_data_path = local_model_candidates[0]
if scaler.name in opts.realesrgan_enabled_models:
self.scalers.append(scaler)
@@ -47,7 +47,7 @@ class UpscalerRealESRGAN(Upscaler):
info = self.load_model(path)
if not os.path.exists(info.local_data_path):
- print("Unable to load RealESRGAN model: %s" % info.name)
+ print(f"Unable to load RealESRGAN model: {info.name}")
return img
upsampler = RealESRGANer(
diff --git a/modules/safe.py b/modules/safe.py
index dadf319c..e6c2f2c0 100644
--- a/modules/safe.py
+++ b/modules/safe.py
@@ -24,7 +24,11 @@ class RestrictedUnpickler(pickle.Unpickler):
def persistent_load(self, saved_id):
assert saved_id[0] == 'storage'
- return TypedStorage(_internal=True)
+
+ try:
+ return TypedStorage(_internal=True)
+ except TypeError:
+ return TypedStorage() # PyTorch before 2.0 does not have the _internal argument
def find_class(self, module, name):
if self.extra_handler is not None:
diff --git a/modules/scripts.py b/modules/scripts.py
index 4d0bbd66..d945b89f 100644
--- a/modules/scripts.py
+++ b/modules/scripts.py
@@ -163,7 +163,8 @@ class Script:
"""helper function to generate id for a HTML element, constructs final id out of script name, tab and user-supplied item_id"""
need_tabname = self.show(True) == self.show(False)
- tabname = ('img2img' if self.is_img2img else 'txt2txt') + "_" if need_tabname else ""
+ tabkind = 'img2img' if self.is_img2img else 'txt2txt'
+ tabname = f"{tabkind}_" if need_tabname else ""
title = re.sub(r'[^a-z_0-9]', '', re.sub(r'\s', '_', self.title().lower()))
return f'script_{tabname}{title}_{item_id}'
@@ -526,7 +527,7 @@ def add_classes_to_gradio_component(comp):
this adds gradio-* to the component for css styling (ie gradio-button to gr.Button), as well as some others
"""
- comp.elem_classes = ["gradio-" + comp.get_block_name(), *(comp.elem_classes or [])]
+ comp.elem_classes = [f"gradio-{comp.get_block_name()}", *(comp.elem_classes or [])]
if getattr(comp, 'multiselect', False):
comp.elem_classes.append('multiselect')
diff --git a/modules/sd_hijack_clip_old.py b/modules/sd_hijack_clip_old.py
index 6d9fbbe6..a3476e95 100644
--- a/modules/sd_hijack_clip_old.py
+++ b/modules/sd_hijack_clip_old.py
@@ -75,7 +75,8 @@ def forward_old(self: sd_hijack_clip.FrozenCLIPEmbedderWithCustomWordsBase, text
self.hijack.comments += hijack_comments
if len(used_custom_terms) > 0:
- self.hijack.comments.append("Used embeddings: " + ", ".join([f'{word} [{checksum}]' for word, checksum in used_custom_terms]))
+ embedding_names = ", ".join(f"{word} [{checksum}]" for word, checksum in used_custom_terms)
+ self.hijack.comments.append(f"Used embeddings: {embedding_names}")
self.hijack.fixes = hijack_fixes
return self.process_tokens(remade_batch_tokens, batch_multipliers)
diff --git a/modules/sd_hijack_optimizations.py b/modules/sd_hijack_optimizations.py
index 372555ff..f10865cd 100644
--- a/modules/sd_hijack_optimizations.py
+++ b/modules/sd_hijack_optimizations.py
@@ -256,6 +256,9 @@ def sub_quad_attention_forward(self, x, context=None, mask=None):
k = k.unflatten(-1, (h, -1)).transpose(1,2).flatten(end_dim=1)
v = v.unflatten(-1, (h, -1)).transpose(1,2).flatten(end_dim=1)
+ if q.device.type == 'mps':
+ q, k, v = q.contiguous(), k.contiguous(), v.contiguous()
+
dtype = q.dtype
if shared.opts.upcast_attn:
q, k = q.float(), k.float()
diff --git a/modules/sd_hijack_unet.py b/modules/sd_hijack_unet.py
index 15858263..ca1daf45 100644
--- a/modules/sd_hijack_unet.py
+++ b/modules/sd_hijack_unet.py
@@ -18,7 +18,7 @@ class TorchHijackForUnet:
if hasattr(torch, item):
return getattr(torch, item)
- raise AttributeError("'{}' object has no attribute '{}'".format(type(self).__name__, item))
+ raise AttributeError(f"'{type(self).__name__}' object has no attribute '{item}'")
def cat(self, tensors, *args, **kwargs):
if len(tensors) == 2:
diff --git a/modules/sd_models.py b/modules/sd_models.py
index 4f7613a1..36f643e1 100644
--- a/modules/sd_models.py
+++ b/modules/sd_models.py
@@ -2,6 +2,8 @@ import collections
import os.path
import sys
import gc
+import threading
+
import torch
import re
import safetensors.torch
@@ -45,7 +47,7 @@ class CheckpointInfo:
self.model_name = os.path.splitext(name.replace("/", "_").replace("\\", "_"))[0]
self.hash = model_hash(filename)
- self.sha256 = hashes.sha256_from_cache(self.filename, "checkpoint/" + name)
+ self.sha256 = hashes.sha256_from_cache(self.filename, f"checkpoint/{name}")
self.shorthash = self.sha256[0:10] if self.sha256 else None
self.title = name if self.shorthash is None else f'{name} [{self.shorthash}]'
@@ -67,7 +69,7 @@ class CheckpointInfo:
checkpoint_alisases[id] = self
def calculate_shorthash(self):
- self.sha256 = hashes.sha256(self.filename, "checkpoint/" + self.name)
+ self.sha256 = hashes.sha256(self.filename, f"checkpoint/{self.name}")
if self.sha256 is None:
return
@@ -404,13 +406,39 @@ def repair_config(sd_config):
sd1_clip_weight = 'cond_stage_model.transformer.text_model.embeddings.token_embedding.weight'
sd2_clip_weight = 'cond_stage_model.model.transformer.resblocks.0.attn.in_proj_weight'
-def load_model(checkpoint_info=None, already_loaded_state_dict=None, time_taken_to_load_state_dict=None):
+
+class SdModelData:
+ def __init__(self):
+ self.sd_model = None
+ self.lock = threading.Lock()
+
+ def get_sd_model(self):
+ if self.sd_model is None:
+ with self.lock:
+ try:
+ load_model()
+ except Exception as e:
+ errors.display(e, "loading stable diffusion model")
+ print("", file=sys.stderr)
+ print("Stable diffusion model failed to load", file=sys.stderr)
+ self.sd_model = None
+
+ return self.sd_model
+
+ def set_sd_model(self, v):
+ self.sd_model = v
+
+
+model_data = SdModelData()
+
+
+def load_model(checkpoint_info=None, already_loaded_state_dict=None):
from modules import lowvram, sd_hijack
checkpoint_info = checkpoint_info or select_checkpoint()
- if shared.sd_model:
- sd_hijack.model_hijack.undo_hijack(shared.sd_model)
- shared.sd_model = None
+ if model_data.sd_model:
+ sd_hijack.model_hijack.undo_hijack(model_data.sd_model)
+ model_data.sd_model = None
gc.collect()
devices.torch_gc()
@@ -464,7 +492,7 @@ def load_model(checkpoint_info=None, already_loaded_state_dict=None, time_taken_
timer.record("hijack")
sd_model.eval()
- shared.sd_model = sd_model
+ model_data.sd_model = sd_model
sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings(force_reload=True) # Reload embeddings after model load as they may or may not fit the model
@@ -484,7 +512,7 @@ def reload_model_weights(sd_model=None, info=None):
checkpoint_info = info or select_checkpoint()
if not sd_model:
- sd_model = shared.sd_model
+ sd_model = model_data.sd_model
if sd_model is None: # previous model load failed
current_checkpoint_info = None
@@ -512,7 +540,7 @@ def reload_model_weights(sd_model=None, info=None):
del sd_model
checkpoints_loaded.clear()
load_model(checkpoint_info, already_loaded_state_dict=state_dict)
- return shared.sd_model
+ return model_data.sd_model
try:
load_model_weights(sd_model, checkpoint_info, state_dict, timer)
@@ -535,17 +563,15 @@ def reload_model_weights(sd_model=None, info=None):
return sd_model
+
def unload_model_weights(sd_model=None, info=None):
from modules import lowvram, devices, sd_hijack
timer = Timer()
- if shared.sd_model:
-
- # shared.sd_model.cond_stage_model.to(devices.cpu)
- # shared.sd_model.first_stage_model.to(devices.cpu)
- shared.sd_model.to(devices.cpu)
- sd_hijack.model_hijack.undo_hijack(shared.sd_model)
- shared.sd_model = None
+ if model_data.sd_model:
+ model_data.sd_model.to(devices.cpu)
+ sd_hijack.model_hijack.undo_hijack(model_data.sd_model)
+ model_data.sd_model = None
sd_model = None
gc.collect()
devices.torch_gc()
diff --git a/modules/sd_models_config.py b/modules/sd_models_config.py
index 9398f528..7a79925a 100644
--- a/modules/sd_models_config.py
+++ b/modules/sd_models_config.py
@@ -111,7 +111,7 @@ def find_checkpoint_config_near_filename(info):
if info is None:
return None
- config = os.path.splitext(info.filename)[0] + ".yaml"
+ config = f"{os.path.splitext(info.filename)[0]}.yaml"
if os.path.exists(config):
return config
diff --git a/modules/sd_samplers_kdiffusion.py b/modules/sd_samplers_kdiffusion.py
index eb98e599..0fc9f456 100644
--- a/modules/sd_samplers_kdiffusion.py
+++ b/modules/sd_samplers_kdiffusion.py
@@ -198,7 +198,7 @@ class TorchHijack:
if hasattr(torch, item):
return getattr(torch, item)
- raise AttributeError("'{}' object has no attribute '{}'".format(type(self).__name__, item))
+ raise AttributeError(f"'{type(self).__name__}' object has no attribute '{item}'")
def randn_like(self, x):
if self.sampler_noises:
diff --git a/modules/sd_vae.py b/modules/sd_vae.py
index 9b00f76e..521e485a 100644
--- a/modules/sd_vae.py
+++ b/modules/sd_vae.py
@@ -89,7 +89,7 @@ def refresh_vae_list():
def find_vae_near_checkpoint(checkpoint_file):
checkpoint_path = os.path.splitext(checkpoint_file)[0]
- for vae_location in [checkpoint_path + ".vae.pt", checkpoint_path + ".vae.ckpt", checkpoint_path + ".vae.safetensors"]:
+ for vae_location in [f"{checkpoint_path}.vae.pt", f"{checkpoint_path}.vae.ckpt", f"{checkpoint_path}.vae.safetensors"]:
if os.path.isfile(vae_location):
return vae_location
diff --git a/modules/shared.py b/modules/shared.py
index 6a2b3c2b..090707ca 100644
--- a/modules/shared.py
+++ b/modules/shared.py
@@ -16,6 +16,7 @@ import modules.styles
import modules.devices as devices
from modules import localization, script_loading, errors, ui_components, shared_items, cmd_args
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
+from ldm.models.diffusion.ddpm import LatentDiffusion
demo = None
@@ -391,21 +392,20 @@ options_templates.update(options_section(('ui', "User interface"), {
"return_mask": OptionInfo(False, "For inpainting, include the greyscale mask in results for web"),
"return_mask_composite": OptionInfo(False, "For inpainting, include masked composite in results for web"),
"do_not_show_images": OptionInfo(False, "Do not show any images in results for web"),
- "add_model_hash_to_info": OptionInfo(True, "Add model hash to generation information"),
- "add_model_name_to_info": OptionInfo(True, "Add model name to generation information"),
- "disable_weights_auto_swap": OptionInfo(True, "When reading generation parameters from text into UI (from PNG info or pasted text), do not change the selected model/checkpoint."),
"send_seed": OptionInfo(True, "Send seed when sending prompt or image to other interface"),
"send_size": OptionInfo(True, "Send size when sending prompt or image to another interface"),
"font": OptionInfo("", "Font for image grids that have text"),
"js_modal_lightbox": OptionInfo(True, "Enable full page image viewer"),
"js_modal_lightbox_initially_zoomed": OptionInfo(True, "Show images zoomed in by default in full page image viewer"),
+ "js_modal_lightbox_gamepad": OptionInfo(True, "Navigate image viewer with gamepad"),
+ "js_modal_lightbox_gamepad_repeat": OptionInfo(250, "Gamepad repeat period, in milliseconds"),
"show_progress_in_title": OptionInfo(True, "Show generation progress in window title."),
"samplers_in_dropdown": OptionInfo(True, "Use dropdown for sampler selection instead of radio group"),
"dimensions_and_batch_together": OptionInfo(True, "Show Width/Height and Batch sliders in same row"),
"keyedit_precision_attention": OptionInfo(0.1, "Ctrl+up/down precision when editing (attention:1.1)", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}),
"keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing <extra networks:0.9>", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}),
- "keyedit_delimiters": OptionInfo(".,\/!?%^*;:{}=`~()", "Ctrl+up/down word delimiters"),
- "quicksettings": OptionInfo("sd_model_checkpoint", "Quicksettings list"),
+ "keyedit_delimiters": OptionInfo(".,\\/!?%^*;:{}=`~()", "Ctrl+up/down word delimiters"),
+ "quicksettings_list": OptionInfo(["sd_model_checkpoint"], "Quicksettings list", ui_components.DropdownMulti, lambda: {"choices": list(opts.data_labels.keys())}),
"hidden_tabs": OptionInfo([], "Hidden UI tabs (requires restart)", ui_components.DropdownMulti, lambda: {"choices": [x for x in tab_names]}),
"ui_reorder": OptionInfo(", ".join(ui_reorder_categories), "txt2img/img2img UI item order"),
"ui_extra_networks_tab_reorder": OptionInfo("", "Extra networks tab order"),
@@ -413,6 +413,13 @@ options_templates.update(options_section(('ui', "User interface"), {
"gradio_theme": OptionInfo("Default", "Gradio theme (requires restart)", ui_components.DropdownEditable, lambda: {"choices": ["Default"] + gradio_hf_hub_themes})
}))
+options_templates.update(options_section(('infotext', "Infotext"), {
+ "add_model_hash_to_info": OptionInfo(True, "Add model hash to generation information"),
+ "add_model_name_to_info": OptionInfo(True, "Add model name to generation information"),
+ "add_version_to_infotext": OptionInfo(True, "Add program version to generation information"),
+ "disable_weights_auto_swap": OptionInfo(True, "When reading generation parameters from text into UI (from PNG info or pasted text), do not change the selected model/checkpoint."),
+}))
+
options_templates.update(options_section(('ui', "Live previews"), {
"show_progressbar": OptionInfo(True, "Show progressbar"),
"live_previews_enable": OptionInfo(True, "Show live previews of the created image"),
@@ -542,6 +549,10 @@ class Options:
with open(filename, "r", encoding="utf8") as file:
self.data = json.load(file)
+ # 1.1.1 quicksettings list migration
+ if self.data.get('quicksettings') is not None and self.data.get('quicksettings_list') is None:
+ self.data['quicksettings_list'] = [i.strip() for i in self.data.get('quicksettings').split(',')]
+
bad_settings = 0
for k, v in self.data.items():
info = self.data_labels.get(k, None)
@@ -600,13 +611,37 @@ class Options:
return value
-
opts = Options()
if os.path.exists(config_filename):
opts.load(config_filename)
+
+class Shared(sys.modules[__name__].__class__):
+ """
+ this class is here to provide sd_model field as a property, so that it can be created and loaded on demand rather than
+ at program startup.
+ """
+
+ sd_model_val = None
+
+ @property
+ def sd_model(self):
+ import modules.sd_models
+
+ return modules.sd_models.model_data.get_sd_model()
+
+ @sd_model.setter
+ def sd_model(self, value):
+ import modules.sd_models
+
+ modules.sd_models.model_data.set_sd_model(value)
+
+
+sd_model: LatentDiffusion = None # this var is here just for IDE's type checking; it cannot be accessed because the class field above will be accessed instead
+sys.modules[__name__].__class__ = Shared
+
settings_components = None
-"""assinged from ui.py, a mapping on setting anmes to gradio components repsponsible for those settings"""
+"""assinged from ui.py, a mapping on setting names to gradio components repsponsible for those settings"""
latent_upscale_default_mode = "Latent"
latent_upscale_modes = {
@@ -620,8 +655,6 @@ latent_upscale_modes = {
sd_upscalers = []
-sd_model = None
-
clip_model = None
progress_print_out = sys.stdout
@@ -701,3 +734,20 @@ def html(filename):
return file.read()
return ""
+
+
+def walk_files(path, allowed_extensions=None):
+ if not os.path.exists(path):
+ return
+
+ if allowed_extensions is not None:
+ allowed_extensions = set(allowed_extensions)
+
+ for root, dirs, files in os.walk(path):
+ for filename in files:
+ if allowed_extensions is not None:
+ _, ext = os.path.splitext(filename)
+ if ext not in allowed_extensions:
+ continue
+
+ yield os.path.join(root, filename)
diff --git a/modules/styles.py b/modules/styles.py
index 9ed85991..11642075 100644
--- a/modules/styles.py
+++ b/modules/styles.py
@@ -74,7 +74,7 @@ class StyleDatabase:
def save_styles(self, path: str) -> None:
# Always keep a backup file around
if os.path.exists(path):
- shutil.copy(path, path + ".bak")
+ shutil.copy(path, f"{path}.bak")
fd = os.open(path, os.O_RDWR|os.O_CREAT)
with os.fdopen(fd, "w", encoding="utf-8-sig", newline='') as file:
diff --git a/modules/textual_inversion/autocrop.py b/modules/textual_inversion/autocrop.py
index 68e1103c..ba1bdcd4 100644
--- a/modules/textual_inversion/autocrop.py
+++ b/modules/textual_inversion/autocrop.py
@@ -111,7 +111,7 @@ def focal_point(im, settings):
if corner_centroid is not None:
color = BLUE
box = corner_centroid.bounding(max_size * corner_centroid.weight)
- d.text((box[0], box[1]-15), "Edge: %.02f" % corner_centroid.weight, fill=color)
+ d.text((box[0], box[1]-15), f"Edge: {corner_centroid.weight:.02f}", fill=color)
d.ellipse(box, outline=color)
if len(corner_points) > 1:
for f in corner_points:
@@ -119,7 +119,7 @@ def focal_point(im, settings):
if entropy_centroid is not None:
color = "#ff0"
box = entropy_centroid.bounding(max_size * entropy_centroid.weight)
- d.text((box[0], box[1]-15), "Entropy: %.02f" % entropy_centroid.weight, fill=color)
+ d.text((box[0], box[1]-15), f"Entropy: {entropy_centroid.weight:.02f}", fill=color)
d.ellipse(box, outline=color)
if len(entropy_points) > 1:
for f in entropy_points:
@@ -127,7 +127,7 @@ def focal_point(im, settings):
if face_centroid is not None:
color = RED
box = face_centroid.bounding(max_size * face_centroid.weight)
- d.text((box[0], box[1]-15), "Face: %.02f" % face_centroid.weight, fill=color)
+ d.text((box[0], box[1]-15), f"Face: {face_centroid.weight:.02f}", fill=color)
d.ellipse(box, outline=color)
if len(face_points) > 1:
for f in face_points:
diff --git a/modules/textual_inversion/dataset.py b/modules/textual_inversion/dataset.py
index af9fbcf2..41610e03 100644
--- a/modules/textual_inversion/dataset.py
+++ b/modules/textual_inversion/dataset.py
@@ -72,7 +72,7 @@ class PersonalizedBase(Dataset):
except Exception:
continue
- text_filename = os.path.splitext(path)[0] + ".txt"
+ text_filename = f"{os.path.splitext(path)[0]}.txt"
filename = os.path.basename(path)
if os.path.exists(text_filename):
diff --git a/modules/textual_inversion/preprocess.py b/modules/textual_inversion/preprocess.py
index 4a29151d..da0bcb26 100644
--- a/modules/textual_inversion/preprocess.py
+++ b/modules/textual_inversion/preprocess.py
@@ -63,9 +63,9 @@ def save_pic_with_caption(image, index, params: PreprocessParams, existing_capti
image.save(os.path.join(params.dstdir, f"{basename}.png"))
if params.preprocess_txt_action == 'prepend' and existing_caption:
- caption = existing_caption + ' ' + caption
+ caption = f"{existing_caption} {caption}"
elif params.preprocess_txt_action == 'append' and existing_caption:
- caption = caption + ' ' + existing_caption
+ caption = f"{caption} {existing_caption}"
elif params.preprocess_txt_action == 'copy' and existing_caption:
caption = existing_caption
@@ -174,7 +174,7 @@ def preprocess_work(process_src, process_dst, process_width, process_height, pre
params.src = filename
existing_caption = None
- existing_caption_filename = os.path.splitext(filename)[0] + '.txt'
+ existing_caption_filename = f"{os.path.splitext(filename)[0]}.txt"
if os.path.exists(existing_caption_filename):
with open(existing_caption_filename, 'r', encoding="utf8") as file:
existing_caption = file.read()
diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py
index 379df243..4368eb63 100644
--- a/modules/textual_inversion/textual_inversion.py
+++ b/modules/textual_inversion/textual_inversion.py
@@ -69,7 +69,7 @@ class Embedding:
'hash': self.checksum(),
'optimizer_state_dict': self.optimizer_state_dict,
}
- torch.save(optimizer_saved_dict, filename + '.optim')
+ torch.save(optimizer_saved_dict, f"{filename}.optim")
def checksum(self):
if self.cached_checksum is not None:
@@ -437,8 +437,8 @@ def train_embedding(id_task, embedding_name, learn_rate, batch_size, gradient_st
optimizer = torch.optim.AdamW([embedding.vec], lr=scheduler.learn_rate, weight_decay=0.0)
if shared.opts.save_optimizer_state:
optimizer_state_dict = None
- if os.path.exists(filename + '.optim'):
- optimizer_saved_dict = torch.load(filename + '.optim', map_location='cpu')
+ if os.path.exists(f"{filename}.optim"):
+ optimizer_saved_dict = torch.load(f"{filename}.optim", map_location='cpu')
if embedding.checksum() == optimizer_saved_dict.get('hash', None):
optimizer_state_dict = optimizer_saved_dict.get('optimizer_state_dict', None)
@@ -599,7 +599,7 @@ def train_embedding(id_task, embedding_name, learn_rate, batch_size, gradient_st
data = torch.load(last_saved_file)
info.add_text("sd-ti-embedding", embedding_to_b64(data))
- title = "<{}>".format(data.get('name', '???'))
+ title = f"<{data.get('name', '???')}>"
try:
vectorSize = list(data['string_to_param'].values())[0].shape[0]
@@ -608,8 +608,8 @@ def train_embedding(id_task, embedding_name, learn_rate, batch_size, gradient_st
checkpoint = sd_models.select_checkpoint()
footer_left = checkpoint.model_name
- footer_mid = '[{}]'.format(checkpoint.shorthash)
- footer_right = '{}v {}s'.format(vectorSize, steps_done)
+ footer_mid = f'[{checkpoint.shorthash}]'
+ footer_right = f'{vectorSize}v {steps_done}s'
captioned_image = caption_image_overlay(image, title, footer_left, footer_mid, footer_right)
captioned_image = insert_image_data_embed(captioned_image, data)
diff --git a/modules/ui.py b/modules/ui.py
index 7b45f131..d02f6e82 100644
--- a/modules/ui.py
+++ b/modules/ui.py
@@ -101,7 +101,7 @@ def visit(x, func, path=""):
for c in x.children:
visit(c, func, path)
elif x.label is not None:
- func(path + "/" + str(x.label), x)
+ func(f"{path}/{x.label}", x)
def add_style(name: str, prompt: str, negative_prompt: str):
@@ -166,7 +166,7 @@ def process_interrogate(interrogation_function, mode, ii_input_dir, ii_output_di
img = Image.open(image)
filename = os.path.basename(image)
left, _ = os.path.splitext(filename)
- print(interrogation_function(img), file=open(os.path.join(ii_output_dir, left + ".txt"), 'a'))
+ print(interrogation_function(img), file=open(os.path.join(ii_output_dir, f"{left}.txt"), 'a'))
return [gr.update(), None]
@@ -182,29 +182,29 @@ def interrogate_deepbooru(image):
def create_seed_inputs(target_interface):
- with FormRow(elem_id=target_interface + '_seed_row', variant="compact"):
- seed = (gr.Textbox if cmd_opts.use_textbox_seed else gr.Number)(label='Seed', value=-1, elem_id=target_interface + '_seed')
+ with FormRow(elem_id=f"{target_interface}_seed_row", variant="compact"):
+ seed = (gr.Textbox if cmd_opts.use_textbox_seed else gr.Number)(label='Seed', value=-1, elem_id=f"{target_interface}_seed")
seed.style(container=False)
- random_seed = ToolButton(random_symbol, elem_id=target_interface + '_random_seed', label='Random seed')
- reuse_seed = ToolButton(reuse_symbol, elem_id=target_interface + '_reuse_seed', label='Reuse seed')
+ random_seed = ToolButton(random_symbol, elem_id=f"{target_interface}_random_seed", label='Random seed')
+ reuse_seed = ToolButton(reuse_symbol, elem_id=f"{target_interface}_reuse_seed", label='Reuse seed')
- seed_checkbox = gr.Checkbox(label='Extra', elem_id=target_interface + '_subseed_show', value=False)
+ seed_checkbox = gr.Checkbox(label='Extra', elem_id=f"{target_interface}_subseed_show", value=False)
# Components to show/hide based on the 'Extra' checkbox
seed_extras = []
- with FormRow(visible=False, elem_id=target_interface + '_subseed_row') as seed_extra_row_1:
+ with FormRow(visible=False, elem_id=f"{target_interface}_subseed_row") as seed_extra_row_1:
seed_extras.append(seed_extra_row_1)
- subseed = gr.Number(label='Variation seed', value=-1, elem_id=target_interface + '_subseed')
+ subseed = gr.Number(label='Variation seed', value=-1, elem_id=f"{target_interface}_subseed")
subseed.style(container=False)
- random_subseed = ToolButton(random_symbol, elem_id=target_interface + '_random_subseed')
- reuse_subseed = ToolButton(reuse_symbol, elem_id=target_interface + '_reuse_subseed')
- subseed_strength = gr.Slider(label='Variation strength', value=0.0, minimum=0, maximum=1, step=0.01, elem_id=target_interface + '_subseed_strength')
+ random_subseed = ToolButton(random_symbol, elem_id=f"{target_interface}_random_subseed")
+ reuse_subseed = ToolButton(reuse_symbol, elem_id=f"{target_interface}_reuse_subseed")
+ subseed_strength = gr.Slider(label='Variation strength', value=0.0, minimum=0, maximum=1, step=0.01, elem_id=f"{target_interface}_subseed_strength")
with FormRow(visible=False) as seed_extra_row_2:
seed_extras.append(seed_extra_row_2)
- seed_resize_from_w = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize seed from width", value=0, elem_id=target_interface + '_seed_resize_from_w')
- seed_resize_from_h = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize seed from height", value=0, elem_id=target_interface + '_seed_resize_from_h')
+ seed_resize_from_w = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize seed from width", value=0, elem_id=f"{target_interface}_seed_resize_from_w")
+ seed_resize_from_h = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize seed from height", value=0, elem_id=f"{target_interface}_seed_resize_from_h")
random_seed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[seed])
random_subseed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[subseed])
@@ -765,7 +765,7 @@ def create_ui():
)
button.click(
fn=lambda: None,
- _js="switch_to_"+name.replace(" ", "_"),
+ _js=f"switch_to_{name.replace(' ', '_')}",
inputs=[],
outputs=[],
)
@@ -828,7 +828,7 @@ def create_ui():
with FormGroup():
with FormRow():
cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=7.0, elem_id="img2img_cfg_scale")
- image_cfg_scale = gr.Slider(minimum=0, maximum=3.0, step=0.05, label='Image CFG Scale', value=1.5, elem_id="img2img_image_cfg_scale", visible=shared.sd_model and shared.sd_model.cond_stage_key == "edit")
+ image_cfg_scale = gr.Slider(minimum=0, maximum=3.0, step=0.05, label='Image CFG Scale', value=1.5, elem_id="img2img_image_cfg_scale", visible=False)
denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.75, elem_id="img2img_denoising_strength")
elif category == "seed":
@@ -1462,18 +1462,18 @@ def create_ui():
elif t == bool:
comp = gr.Checkbox
else:
- raise Exception(f'bad options item type: {str(t)} for key {key}')
+ raise Exception(f'bad options item type: {t} for key {key}')
- elem_id = "setting_"+key
+ elem_id = f"setting_{key}"
if info.refresh is not None:
if is_quicksettings:
res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
- create_refresh_button(res, info.refresh, info.component_args, "refresh_" + key)
+ create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}")
else:
with FormRow():
res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
- create_refresh_button(res, info.refresh, info.component_args, "refresh_" + key)
+ create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}")
else:
res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
@@ -1525,7 +1525,7 @@ def create_ui():
result = gr.HTML(elem_id="settings_result")
- quicksettings_names = [x.strip() for x in opts.quicksettings.split(",")]
+ quicksettings_names = opts.quicksettings_list
quicksettings_names = {x: i for i, x in enumerate(quicksettings_names) if x != 'quicksettings'}
quicksettings_list = []
@@ -1545,7 +1545,7 @@ def create_ui():
current_tab.__exit__()
gr.Group()
- current_tab = gr.TabItem(elem_id="settings_{}".format(elem_id), label=text)
+ current_tab = gr.TabItem(elem_id=f"settings_{elem_id}", label=text)
current_tab.__enter__()
current_row = gr.Column(variant='compact')
current_row.__enter__()
@@ -1566,7 +1566,7 @@ def create_ui():
current_row.__exit__()
current_tab.__exit__()
- with gr.TabItem("Actions", id="actions"):
+ with gr.TabItem("Actions", id="actions", elem_id="settings_tab_actions"):
request_notifications = gr.Button(value='Request browser notifications', elem_id="request_notifications")
download_localization = gr.Button(value='Download localization template', elem_id="download_localization")
reload_script_bodies = gr.Button(value='Reload custom script bodies (No ui updates, No restart)', variant='secondary', elem_id="settings_reload_script_bodies")
@@ -1574,7 +1574,7 @@ def create_ui():
unload_sd_model = gr.Button(value='Unload SD checkpoint to free VRAM', elem_id="sett_unload_sd_model")
reload_sd_model = gr.Button(value='Reload the last SD checkpoint back into VRAM', elem_id="sett_reload_sd_model")
- with gr.TabItem("Licenses", id="licenses"):
+ with gr.TabItem("Licenses", id="licenses", elem_id="settings_tab_licenses"):
gr.HTML(shared.html("licenses.html"), elem_id="licenses")
gr.Button(value="Show all pages", elem_id="settings_show_all_pages")
@@ -1664,7 +1664,7 @@ def create_ui():
for interface, label, ifid in interfaces:
if label in shared.opts.hidden_tabs:
continue
- with gr.TabItem(label, id=ifid, elem_id='tab_' + ifid):
+ with gr.TabItem(label, id=ifid, elem_id=f"tab_{ifid}"):
interface.render()
if os.path.exists(os.path.join(script_path, "notification.mp3")):
@@ -1693,11 +1693,9 @@ def create_ui():
show_progress=info.refresh is not None,
)
- text_settings.change(
- fn=lambda: gr.update(visible=shared.sd_model and shared.sd_model.cond_stage_key == "edit"),
- inputs=[],
- outputs=[image_cfg_scale],
- )
+ update_image_cfg_scale_visibility = lambda: gr.update(visible=shared.sd_model and shared.sd_model.cond_stage_key == "edit")
+ text_settings.change(fn=update_image_cfg_scale_visibility, inputs=[], outputs=[image_cfg_scale])
+ demo.load(fn=update_image_cfg_scale_visibility, inputs=[], outputs=[image_cfg_scale])
button_set_checkpoint = gr.Button('Change checkpoint', elem_id='change_checkpoint', visible=False)
button_set_checkpoint.click(
@@ -1773,10 +1771,10 @@ def create_ui():
def loadsave(path, x):
def apply_field(obj, field, condition=None, init_field=None):
- key = path + "/" + field
+ key = f"{path}/{field}"
if getattr(obj, 'custom_script_source', None) is not None:
- key = 'customscript/' + obj.custom_script_source + '/' + key
+ key = f"customscript/{obj.custom_script_source}/{key}"
if getattr(obj, 'do_not_save_to_config', False):
return
@@ -1925,7 +1923,7 @@ def versions_html():
python_version = ".".join([str(x) for x in sys.version_info[0:3]])
commit = launch.commit_hash()
- short_commit = commit[0:8]
+ tag = launch.git_tag()
if shared.xformers_available:
import xformers
@@ -1934,6 +1932,8 @@ def versions_html():
xformers_version = "N/A"
return f"""
+version: <a href="https://github.com/AUTOMATIC1111/stable-diffusion-webui/commit/{commit}">{tag}</a>
+ • 
python: <span title="{sys.version}">{python_version}</span>
 • 
torch: {getattr(torch, '__long_version__',torch.__version__)}
@@ -1942,7 +1942,21 @@ xformers: {xformers_version}
 • 
gradio: {gr.__version__}
 • 
-commit: <a href="https://github.com/AUTOMATIC1111/stable-diffusion-webui/commit/{commit}">{short_commit}</a>
- • 
checkpoint: <a id="sd_checkpoint_hash">N/A</a>
"""
+
+
+def setup_ui_api(app):
+ from pydantic import BaseModel, Field
+ from typing import List
+
+ class QuicksettingsHint(BaseModel):
+ name: str = Field(title="Name of the quicksettings field")
+ label: str = Field(title="Label of the quicksettings field")
+
+ def quicksettings_hint():
+ return [QuicksettingsHint(name=k, label=v.label) for k, v in opts.data_labels.items()]
+
+ app.add_api_route("/internal/quicksettings-hint", quicksettings_hint, methods=["GET"], response_model=List[QuicksettingsHint])
+
+ app.add_api_route("/internal/ping", lambda: {}, methods=["GET"])
diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py
index 99ac8756..d9faf85a 100644
--- a/modules/ui_extensions.py
+++ b/modules/ui_extensions.py
@@ -61,7 +61,8 @@ def save_config_state(name):
if not name:
name = "Config"
current_config_state["name"] = name
- filename = os.path.join(config_states_dir, datetime.now().strftime("%Y_%m_%d-%H_%M_%S") + "_" + name + ".json")
+ timestamp = datetime.now().strftime('%Y_%m_%d-%H_%M_%S')
+ filename = os.path.join(config_states_dir, f"{timestamp}_{name}.json")
print(f"Saving backup of webui/extension state to {filename}.")
with open(filename, "w", encoding="utf-8") as f:
json.dump(current_config_state, f)
diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py
index aa2f5d1b..8c3dea56 100644
--- a/modules/ui_extra_networks.py
+++ b/modules/ui_extra_networks.py
@@ -69,7 +69,9 @@ class ExtraNetworksPage:
pass
def link_preview(self, filename):
- return "./sd_extra_networks/thumb?filename=" + urllib.parse.quote(filename.replace('\\', '/')) + "&mtime=" + str(os.path.getmtime(filename))
+ quoted_filename = urllib.parse.quote(filename.replace('\\', '/'))
+ mtime = os.path.getmtime(filename)
+ return f"./sd_extra_networks/thumb?filename={quoted_filename}&mtime={mtime}"
def search_terms_from_path(self, filename, possible_directories=None):
abspath = os.path.abspath(filename)
@@ -89,19 +91,22 @@ class ExtraNetworksPage:
subdirs = {}
for parentdir in [os.path.abspath(x) for x in self.allowed_directories_for_previews()]:
- for x in glob.glob(os.path.join(parentdir, '**/*'), recursive=True):
- if not os.path.isdir(x):
- continue
+ for root, dirs, files in os.walk(parentdir):
+ for dirname in dirs:
+ x = os.path.join(root, dirname)
- subdir = os.path.abspath(x)[len(parentdir):].replace("\\", "/")
- while subdir.startswith("/"):
- subdir = subdir[1:]
+ if not os.path.isdir(x):
+ continue
- is_empty = len(os.listdir(x)) == 0
- if not is_empty and not subdir.endswith("/"):
- subdir = subdir + "/"
+ subdir = os.path.abspath(x)[len(parentdir):].replace("\\", "/")
+ while subdir.startswith("/"):
+ subdir = subdir[1:]
- subdirs[subdir] = 1
+ is_empty = len(os.listdir(x)) == 0
+ if not is_empty and not subdir.endswith("/"):
+ subdir = subdir + "/"
+
+ subdirs[subdir] = 1
if subdirs:
subdirs = {"": 1, **subdirs}
@@ -157,8 +162,20 @@ class ExtraNetworksPage:
if metadata:
metadata_button = f"<div class='metadata-button' title='Show metadata' onclick='extraNetworksRequestMetadata(event, {json.dumps(self.name)}, {json.dumps(item['name'])})'></div>"
+ local_path = ""
+ filename = item.get("filename", "")
+ for reldir in self.allowed_directories_for_previews():
+ absdir = os.path.abspath(reldir)
+
+ if filename.startswith(absdir):
+ local_path = filename[len(absdir):]
+
+ # if this is true, the item must not be show in the default view, and must instead only be
+ # shown when searching for it
+ serach_only = "/." in local_path or "\\." in local_path
+
args = {
- "style": f"'{height}{width}{background_image}'",
+ "style": f"'display: none; {height}{width}{background_image}'",
"prompt": item.get("prompt", None),
"tabname": json.dumps(tabname),
"local_preview": json.dumps(item["local_preview"]),
@@ -168,6 +185,7 @@ class ExtraNetworksPage:
"save_card_preview": '"' + html.escape(f"""return saveCardPreview(event, {json.dumps(tabname)}, {json.dumps(item["local_preview"])})""") + '"',
"search_term": item.get("search_term", ""),
"metadata_button": metadata_button,
+ "serach_only": " search_only" if serach_only else "",
}
return self.card_page.format(**args)
@@ -209,6 +227,11 @@ def intialize():
class ExtraNetworksUi:
def __init__(self):
self.pages = None
+ """gradio HTML components related to extra networks' pages"""
+
+ self.page_contents = None
+ """HTML content of the above; empty initially, filled when extra pages have to be shown"""
+
self.stored_extra_pages = None
self.button_save_preview = None
@@ -236,17 +259,22 @@ def pages_in_preferred_order(pages):
def create_ui(container, button, tabname):
ui = ExtraNetworksUi()
ui.pages = []
+ ui.pages_contents = []
ui.stored_extra_pages = pages_in_preferred_order(extra_pages.copy())
ui.tabname = tabname
with gr.Tabs(elem_id=tabname+"_extra_tabs") as tabs:
for page in ui.stored_extra_pages:
- with gr.Tab(page.title, id=page.title.lower().replace(" ", "_")):
+ page_id = page.title.lower().replace(" ", "_")
- page_elem = gr.HTML(page.create_html(ui.tabname))
+ with gr.Tab(page.title, id=page_id):
+ elem_id = f"{tabname}_{page_id}_cards_html"
+ page_elem = gr.HTML('', elem_id=elem_id)
ui.pages.append(page_elem)
- filter = gr.Textbox('', show_label=False, elem_id=tabname+"_extra_search", placeholder="Search...", visible=False)
+ page_elem.change(fn=lambda: None, _js='function(){applyExtraNetworkFilter(' + json.dumps(tabname) + '); return []}', inputs=[], outputs=[])
+
+ gr.Textbox('', show_label=False, elem_id=tabname+"_extra_search", placeholder="Search...", visible=False)
button_refresh = gr.Button('Refresh', elem_id=tabname+"_extra_refresh")
ui.button_save_preview = gr.Button('Save preview', elem_id=tabname+"_save_preview", visible=False)
@@ -254,19 +282,22 @@ def create_ui(container, button, tabname):
def toggle_visibility(is_visible):
is_visible = not is_visible
- return is_visible, gr.update(visible=is_visible), gr.update(variant=("secondary-down" if is_visible else "secondary"))
+
+ if is_visible and not ui.pages_contents:
+ refresh()
+
+ return is_visible, gr.update(visible=is_visible), gr.update(variant=("secondary-down" if is_visible else "secondary")), *ui.pages_contents
state_visible = gr.State(value=False)
- button.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container, button])
+ button.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container, button, *ui.pages])
def refresh():
- res = []
-
for pg in ui.stored_extra_pages:
pg.refresh()
- res.append(pg.create_html(ui.tabname))
- return res
+ ui.pages_contents = [pg.create_html(ui.tabname) for pg in ui.stored_extra_pages]
+
+ return ui.pages_contents
button_refresh.click(fn=refresh, inputs=[], outputs=ui.pages)
diff --git a/modules/ui_tempdir.py b/modules/ui_tempdir.py
index 21945235..46fa9cb0 100644
--- a/modules/ui_tempdir.py
+++ b/modules/ui_tempdir.py
@@ -36,7 +36,7 @@ def save_pil_to_file(pil_image, dir=None):
if already_saved_as and os.path.isfile(already_saved_as):
register_tmp_file(shared.demo, already_saved_as)
- file_obj = Savedfile(already_saved_as)
+ file_obj = Savedfile(f'{already_saved_as}?{os.path.getmtime(already_saved_as)}')
return file_obj
if shared.opts.temp_dir != "":