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

Form min_width guard #4693

Closed
wants to merge 3 commits into from
Closed

Form min_width guard #4693

wants to merge 3 commits into from

Conversation

akx
Copy link
Contributor

@akx akx commented Jun 27, 2023

Description

This PR ensures no weird algebra is being attempted between integers and Nones for max_width, as was already done for scale, in Form.add_child (ref #4374).

On a similar note, it ensures an user attempting to do Form(min_width=None) will get an error immediately rather than at a later stage.

I don't have a great standalone repro for this, but the symptom is

  File "modules/ui.py", line 311, in create_toprow
    with gr.Row(elem_id=f"{id_part}_tools"):
  File "gradio/blocks.py", line 366, in __exit__
    self.fill_expected_parents()
  File "gradio/blocks.py", line 358, in fill_expected_parents
    pseudo_parent.add_child(child)
  File "gradio/layouts.py", line 334, in add_child
    self.min_width += getattr(child, "min_width", 0)
TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'

@akx akx force-pushed the min-width-guard branch from 896f914 to f0b9e18 Compare June 27, 2023 11:10
@gradio-pr-bot
Copy link
Collaborator

All the demos for this PR have been deployed at https://huggingface.co/spaces/gradio-pr-deploys/pr-4693-all-demos

@abidlabs
Copy link
Member

LGTM, thanks @akx.

@aliabid94 could you also take a quick look? I believe you wrote the related code

@@ -323,15 +323,16 @@ def __init__(self, *, scale: int = 0, min_width: int = 0, **kwargs):
scale: relative width compared to adjacent Columns. For example, if Column A has scale=2, and Column B has scale=1, A will be twice as wide as B.
min_width: minimum pixel width of Column, will wrap if not sufficient screen space to satisfy this value. If a certain scale value results in a column narrower than min_width, the min_width parameter will be respected first.
"""
self.scale = scale
self.min_width = min_width
self.scale = int(scale)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this casting necessary? the constructor expects an int

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type annotation doesn't prevent anyone from passing in something else.

This makes e.g. an inadvertent max_width=None (which would be valid for some other components) fail at construction time instead of when something is attempting to do math with it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should guard against people passing the wrong type in, do we do that anywhere else?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, assert isinstance(min_width, int) would be clearer in intent

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aliabid94 asserts could be compiled out in -O mode. See https://beta.ruff.rs/docs/rules/assert/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pngwn Why not? I think it makes sense here since the overwhelmingly more common types for scale and min_width are

scale: int | None = None,
min_width: int | None = None,

but this component only accepts ints and will break otherwise.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think it's a good idea to check here, even if we don't do it generally.

Test that `Form.add_child` does not crash with a component whose `min_width` is `None`.
"""
r = Row()
f = Form()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems to test the internal implementation, using .parent and .add_child. How about this instead, which is how Blocks are naturally built?

with Row() as r:
  with Form() as f:
    b = Button() 
assert f.min_width == 0 

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aliabid94 This doesn't trigger the original bug (for whichever reason) – I did mention I didn't have a great standalone repro :(

Copy link
Member

@abidlabs abidlabs Jun 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's really strange, can we figure out why the bug is not being triggered and write a test that captures the real usage?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it has to do with the pseudo_parent logic in fill_expected_parents, and truth be told my eyes glazed over trying to figure out what happens there. 😅

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is a reasonable test as it uses an internal unsupported API. We have to reproduce the original problem with our supported API. Was the original bug discovered when using the API in this way as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the original bug simply appears when trying to upgrade the stable-diffusion-webui repo to the latest version of Gradio. It doesn't use the internal APIs (but it does have some special subclassed components).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akx as a general principle, we can't be making changes to the internals of the gradio library based on nonstandard usage. The fix or modification should be downstream library using gradio. If you can find an example where using gradio as intended produces this bug, then we're happy to fix, but in the meantime I think we should close this PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@abidlabs What constitutes "nonstandard usage"? 😁 As I see it, no non-public APIs are being used in the webui.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a simple repro so we can test against regressions without testing implementation details.

@akx akx force-pushed the min-width-guard branch from 1da9a0c to 994f1fb Compare June 27, 2023 20:10
@pngwn
Copy link
Member

pngwn commented Jun 28, 2023

Closing until we have a better repro: #4693 (comment)

@pngwn pngwn closed this Jun 28, 2023
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

Successfully merging this pull request may close these issues.

5 participants