Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Forge and/or Gradio) Error Afflicting Many Extensions #1605

Open
altoiddealer opened this issue Aug 30, 2024 · 7 comments
Open

(Forge and/or Gradio) Error Afflicting Many Extensions #1605

altoiddealer opened this issue Aug 30, 2024 · 7 comments

Comments

@altoiddealer
Copy link
Contributor

altoiddealer commented Aug 30, 2024

Gradio 4 directly caused many extensions to have issues, due to code changes, deprecations, etc.

Not this error. This error is from Forge, and it affects many extensions.

The error, which I'll refer to later as "Inherited Component bug":

      File "C:\stable-diffusion-webui-forge\venv\lib\site-packages\gradio\components\base.py", line 118, in get_component_class_id
        module_path = sys.modules[module_name].__file__
    KeyError: 'extension-script-name.py'

Traceback Sample
*** Error calling: C:\stable-diffusion-webui-forge\extensions\--sd-webui-ar-plus\scripts\sd-webui-ar.py/ui
    Traceback (most recent call last):
      File "C:\stable-diffusion-webui-forge\modules\scripts.py", line 545, in wrap_call
        return func(*args, **kwargs)
      File "C:\stable-diffusion-webui-forge\extensions\--sd-webui-ar-plus\scripts\sd-webui-ar.py", line 261, in ui
        ar_btns = [
      File "C:\stable-diffusion-webui-forge\extensions\--sd-webui-ar-plus\scripts\sd-webui-ar.py", line 262, in <listcomp>
        ARButton(ar=ar, value=label)
      File "C:\stable-diffusion-webui-forge\extensions\--sd-webui-ar-plus\scripts\sd-webui-ar.py", line 35, in __init__
        super().__init__(**kwargs)
      File "C:\stable-diffusion-webui-forge\modules\ui_components.py", line 23, in __init__
        super().__init__(*args, elem_classes=["tool", *elem_classes], value=value, **kwargs)
      File "C:\stable-diffusion-webui-forge\modules\gradio_extensions.py", line 126, in __repaired_init__
        original(self, *args, **fixed_kwargs)
      File "C:\stable-diffusion-webui-forge\venv\lib\site-packages\gradio\component_meta.py", line 163, in wrapper
        return fn(self, **kwargs)
      File "C:\stable-diffusion-webui-forge\venv\lib\site-packages\gradio\components\button.py", line 61, in __init__
        super().__init__(
      File "C:\stable-diffusion-webui-forge\modules\gradio_extensions.py", line 35, in IOComponent_init
        res = original_IOComponent_init(self, *args, **kwargs)
      File "C:\stable-diffusion-webui-forge\venv\lib\site-packages\gradio\component_meta.py", line 163, in wrapper
        return fn(self, **kwargs)
      File "C:\stable-diffusion-webui-forge\venv\lib\site-packages\gradio\components\base.py", line 224, in __init__
        self.component_class_id = self.__class__.get_component_class_id()
      File "C:\stable-diffusion-webui-forge\venv\lib\site-packages\gradio\components\base.py", line 118, in get_component_class_id
        module_path = sys.modules[module_name].__file__
    KeyError: 'sd-webui-ar.py'

What I believe Forge needs to Fix this Forge error:

  • I may be wrong, the error may lie elsewhere.
  • I believe IOComponent_init needs to be updated, or another function working with it.

Reproduction:

Inherit from ToolButton() (or, presumably, any component imported from modules.ui_components).

from modules.ui_components import ToolButton

class CustomToolButton(ToolButton):
    def __init__(self, value='button', **kwargs):
        super().__init__(value, **kwargs)
        self.value = value

    def some_method(self):
        pass

button = CustomToolButton(value='test')

How extension developers can resolve the issue:

Note: developers are typically subclassing UI components so they can define class methods.

Therefore, this solution can be extremely painful/confusing depending on the application.

Solution: Do not subclass any UI components.

from modules.ui_components import ToolButton

button = ToolButton(value='test')

What's happening:

Refer to the Traceback Sample in the accordion above.

Strange Initialization for subclassed components:

      File "C:\stable-diffusion-webui-forge\modules\ui_components.py", line 23, in __init__
        super().__init__(*args, elem_classes=["tool", *elem_classes], value=value, **kwargs)

Important: At this step, all WebUIs (A1111, Forge, Reforge, etc) experience "Inherited Component bug"

  • "Among other things", subclassed component's have .__module__ attribute value: "extension-script-name"
  • Components which are not subclassed have .__module__ attribute value: "modules.ui_components"

WebUI Repairs Strange Initialization:

      File "C:\stable-diffusion-webui-forge\modules\gradio_extensions.py", line 126, in __repaired_init__
        original(self, *args, **fixed_kwargs)

At this step, all other WebUIs except Forge successfully repair the Strange Initialization that occurs.

  • "Among other things", .__module__ attribute value: "extension-script-name" will be updated to "modules.ui_components"

Hack that fixes the error, but not "Among other things":

"Among other things" (not resolved by this) includes the following:

  • Button may initialize as disabled for no apparent reason
  • "value" can be bugged resulting in a blank button
  • These buttons seem to completely ignore any custom .js code defined by the script
  • There could be much more strange behavior for different components defined like this

This will make the script load in Forge:

CustomToolButton.__module__ = "modules.ui_components"

Example:

from modules.ui_components import ToolButton

class CustomToolButton(ToolButton):
    def __init__(self, value='button', **kwargs):
        super().__init__(value, **kwargs)
        self.value = value

    def some_method(self):
        pass

CustomToolButton.__module__ = "modules.ui_components"

button = CustomToolButton(value='test')

What is the specific problem?!?!?!

It has something to do with the patching steps when it tries "__repaired_init__"

      File "C:\stable-diffusion-webui-forge\venv\lib\site-packages\gradio\component_meta.py", line 163, in wrapper
        return fn(self, **kwargs)
      File "C:\stable-diffusion-webui-forge\venv\lib\site-packages\gradio\components\button.py", line 61, in __init__
        super().__init__(
      File "C:\stable-diffusion-webui-forge\modules\gradio_extensions.py", line 35, in IOComponent_init
        res = original_IOComponent_init(self, *args, **kwargs)
      File "C:\stable-diffusion-webui-forge\venv\lib\site-packages\gradio\component_meta.py", line 163, in wrapper
        return fn(self, **kwargs)

This in particular looks very suspicious to me:

Screenshot 2024-08-30 094449
  • There are many differences between IOComponent and Component
  • IOComponent_init() is identical between Forge and A1111
@wcole3
Copy link
Contributor

wcole3 commented Aug 30, 2024

I appreciate the discussion you had here, helped to fix some other extensions.

For this issue, have you tried debug stepping through the repair loop in gradio_extensions? Or looked at the @wraps decorators in the extended subclasses? I wonder if the Button and FormComponent have differetn inits in Gradio.

@wcole3
Copy link
Contributor

wcole3 commented Aug 30, 2024

The above is probably not good advice; the root cause seems to be that subclassed gradio components from extensions that are not modules, and thus they aren't in sys's module dict. They will then fail in gradio.components.base.get_component_class_id:

@classmethod
    def get_component_class_id(cls) -> str:
        module_name = cls.__module__
        module_path = sys.modules[module_name].__file__
        module_hash = hashlib.md5(f"{cls.__name__}_{module_path}".encode()).hexdigest()
        return module_hash

Is it possible that the extension loading is different in Forge compared to other webui's?

@altoiddealer
Copy link
Contributor Author

I want to add a quick thing I meant to preface my OP with:

When I see people on Reddit / discord saying the reasons they do not use Forge, the main reason is "My extensions don't work".

Here is one major bug that will not be easily resolved by all extension developers, if I am correct that this is indeed on Forge.


I've suffered traumatically in attempting / assisting with debugging this particular issue, and it's brought me some relief to just spill out everything that I learned in the process, in hopes someone who really knows what they are doing can solve this.

I'm really not willing to dive back in, test prints, etc.

I had put debug prints all over the place, tested over and over again for hours... in my script, in the modules, etc.

  • Before the repair stage, Forge / A1111 print statements are the same
  • In base.py when Forge errors, A1111 has it resolved - it successfully fixes that component.

@wcole3
Copy link
Contributor

wcole3 commented Aug 30, 2024

Thanks for transferring some of your trauma on to me 😅

You were correct that something was wrong, though it is also a problem in auto1111, they just won't start encountering it until they update to Gradio 4 (or some other code explicitly checks sys.modules). See PR above and let me know if it fixes the issue you were seeing.

@altoiddealer
Copy link
Contributor Author

altoiddealer commented Aug 30, 2024

Thanks for transferring some of your trauma on to me 😅

You were correct that something was wrong, though it is also a problem in auto1111, they just won't start encountering it until they update to Gradio 4 (or some other code explicitly checks sys.modules). See PR above and let me know if it fixes the issue you were seeing.

You're very good! I applied your PR, then used git checkout to roll back my extension.

Your PR resolved the __module__ error, but it does not resolve the other Strange Initialization stuff.

  • Is ignoring the .js code targetting it
  • Some labels not setting. If I change the way of setting them, those buttons will initialize as disabled. I could not figure out why this is. LEv145 had the same issue when trying to adjust their subclassed ToolButton() components.

But your PR is already a huge step in the right direction, as the extension loaded up without error!

Screenshot 2024-08-30 194132

Comparing the gradio code, base.py between Forge (Gradio 4) and A1111 (Gradio 3), I see there is a drastically different way for initializing component bases.

Screenshot 2024-08-30 193739

@altoiddealer altoiddealer changed the title Forge (not Gradio!) Error Afflicting Many Extensions (Forge and/or Gradio) Error Afflicting Many Extensions Aug 30, 2024
@wcole3
Copy link
Contributor

wcole3 commented Sep 2, 2024

While we think about the PR, can you describe some tests I can try in this extension? The buttons seemed to load up fine for me:
image

It seems like I might be on a different branch?

@altoiddealer
Copy link
Contributor Author

altoiddealer commented Sep 2, 2024

While we think about the PR, can you describe some tests I can try in this extension? The buttons seemed to load up fine for me: image

It seems like I might be on a different branch?

The bug affects any extension that subclasses a UI component. My extension was updated to simply not subclass the ToolButtons. I used git checkout to roll back to a version before I made that change, and the PR did fix the error.

Lev145 really solved the problem for us here and it’s actually a complicated solution for our application. Instead of making custom button classes, it tuples functions with each button instance

EDIT Okay I see you were actually looking at their repo. They fixed it as I described

EDIT2 this is the commit before I resolved the issue

you can also check out Segment Anything main branch, they also have the bug - I sent them a PR that replaces the subclassed button with a direct instance

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants