AUTOMATIC1111 / stable-diffusion-webui

Stable Diffusion web UI
GNU Affero General Public License v3.0
135.8k stars 25.92k forks source link

[Bug]: on_ui_settings callback gets called after "ui" method in scripts #11210

Open nochnoe opened 1 year ago

nochnoe commented 1 year ago

Is there an existing issue for this?

What happened?

When I'm trying to access shared.opts in ui method of a script, the script fails with the following error:

AttributeError: 'Options' object has no attribute 'my_configuration_option_name'

The problem goes away when I use "Reload UI" button in the footer. The included repro shows, that on cold start ui gets called before registered on_ui_settings callback of a script. Is this a normal behavior?

Steps to reproduce the problem

Run sd-webui with this script:

from modules import scripts, script_callbacks

class Script(scripts.Script):
  def title(self):
    return "Bug repro"

  def show(self, _is_img2img):
    return scripts.AlwaysVisible

  def ui(self, _is_img2img):
    print("Hello from ui")

    return []

def on_ui_settings():
  print("Hello from on_ui_settings")

script_callbacks.on_ui_settings(on_ui_settings)

Terminal shows this:

Hello from ui
Hello from ui
Hello from on_ui_settings

What should have happened?

Callback should run before ui

Commit where the problem happens

baf6946e06249c5af9851c60171692c44ef633e0

What Python version are you running on ?

Python 3.10.x

What platforms do you use to access the UI ?

Windows

What device are you running WebUI on?

Nvidia GPUs (RTX 20 above)

What browsers do you use to access the UI ?

Mozilla Firefox

Command Line Arguments

--xformers

List of extensions

a1111-sd-webui-tagcomplete https://github.com/DominikDoom/a1111-sd-webui-tagcomplete.git main f4218a71 Fri Jun 9 14:15:27 2023 unknown sd-webui-controlnet https://github.com/Mikubill/sd-webui-controlnet.git main c488ef2c Sun Jun 11 20:01:30 2023 unknown sd-webui-gelbooru-prompt https://github.com/antis0007/sd-webui-gelbooru-prompt.git main 4dbea1df Fri Apr 7 18:47:44 2023 unknown stable-diffusion-webui-images-browser https://github.com/AlUlkesh/stable-diffusion-webui-images-browser.git main b2f6e4cb Thu Jun 8 08:11:43 2023 unknown

+my own script

Console logs

Error calling: C:\Users\me\Documents\sd-new\extensions\my-extension\scripts\my-script.py/ui
Traceback (most recent call last):
  File "C:\Users\me\Documents\sd-new\modules\scripts.py", line 283, in wrap_call
    res = func(*args, **kwargs)
  File "C:\Users\me\Documents\sd-new\extensions\my-extension\scripts\my-script.py", line 215, in ui
    value=opts.my_configuration_option_name,
  File "C:\Users\me\Documents\sd-new\modules\shared.py", line 581, in __getattr__
    return super(Options, self).__getattribute__(item)
AttributeError: 'Options' object has no attribute 'my_configuration_option_name'

Additional information

No response

AlUlkesh commented 1 year ago

I'm not sure I see the connection here. on_ui_settings is called before the settings in the UI are populated. That does not mean opts. isn't populated yet.

Can you show some of your code that throws the AttributeError?

nochnoe commented 1 year ago

I'm not sure I see the connection here. on_ui_settings is called before the settings in the UI are populated. That does not mean opts. isn't populated yet.

Can you show some of your code that throws the AttributeError?

Here's the short repro script with the error:

import gradio as gr

from modules import scripts, script_callbacks, shared

class Script(scripts.Script):
  def title(self):
    return "Bug repro"

  def show(self, _is_img2img):
    return scripts.AlwaysVisible

  def ui(self, _is_img2img):
    with gr.Group():
      with gr.Accordion("Repro", open=True):
        gr.Dropdown(choices=["a", "b"], value=shared.opts.repro_test_option)

def on_ui_settings():
  section = ("repro-script", "Bug repro")

  shared.opts.add_option(
    "repro_test_option",
    shared.OptionInfo("a", "Label", gr.Dropdown, lambda: {"choices": ["a", "b"]}, section=section)
  )

script_callbacks.on_ui_settings(on_ui_settings)
AlUlkesh commented 1 year ago

You're right, if the setting doesn't exist yet, this seem to be an issue.

Of course you can workaround it by doing something like

gr.Dropdown(choices=["a", "b"], value=shared.opts.data.get("repro_test_option", "a")

but having to put in the default value twice is not pretty.

nochnoe commented 1 year ago

There is not only an issue with having to put the default twice, but if user already changed this option, looks like it also would fail to read it, before add_option is executed.

I've figured out that "Defaults" tab exists in the settings, which works for my usecase, but it's very clunky to use, and I can imagine some more usecases (like conditionally changing visibility of fields, depending on current setting value), which may benefit from the fix of this issue.

AlUlkesh commented 1 year ago

There is not only an issue with having to put the default twice, but if user already changed this option, looks like it also would fail to read it, before add_option is executed.

No, that should work, config.json is read and opts filled much earlier than the processing for the ui settings.

nochnoe commented 1 year ago

Yep, looks like it, just tested with the repro script. So only an issue with defaults then

pk5ls20 commented 11 months ago

I encountered the same problem while writing a plugin... I hope it can be fixed