Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@SrGesus
Copy link
Contributor

@SrGesus SrGesus commented Mar 12, 2025

Fixes #8761

Describe your changes

When an element is created with dg._block() the _form_data will be propagated from its parent, but when an element is created with a dg._enqueue it will not. This makes it so the current code to find a form will not be able to find the right form, so the easiest way is just making _enqueue propagate a form. ( current code to find the form: )

if this_dg._form_data is not None:
return this_dg._form_data
if this_dg == this_dg._main_dg:
# We were created via an `st.foo` call.
# Walk up the dg_stack to see if we're nested inside a `with st.form` statement.
for dg in reversed(context_dg_stack.get()):
if dg._form_data is not None:
return dg._form_data
else:
# We were created via an `dg.foo` call.
# Take a look at our parent's form data to see if we're nested inside a form.
parent = this_dg._parent
if parent is not None and parent._form_data is not None:
return parent._form_data
return None

But this makes it so the element after being created has the proper _form_data, so the above code could just be

return this_dg._active_dg._form_data

I have also an alternative suggestion that i think might be much cleaner but i wanted a second opinion since it would mean deleting 2 tests. But i find that these tests are highly problematic, let's look:

def test_is_in_form_true_when_dg_stack_has_form(self):
form_dg = DeltaGenerator()
form_dg._form_data = FormData("form_id")
dg = DeltaGenerator()
context_dg_stack.set((form_dg, dg))
assert is_in_form(dg) is True
def test_is_in_form_false_when_dg_stack_has_no_form(self):
form_dg = DeltaGenerator()
dg = DeltaGenerator()
context_dg_stack.set((form_dg, dg))
assert is_in_form(dg) is False

They are expecting that, if when _current_form is called there is a form in the context stack then it should mean there is a form in the element being created, it has the same assumption in the _current_form function which iterates the current context until it finds a _form_data (it does this when the dg is parentless, so st or st.sidebar etc). However this is wrong and i'll give an example:

no_form = st.empty()
with st.form("form"):
  st.write("hi")
  with no_form:
    st.form_submit_button("OK")

In here (run from origin/develop) the form_submit_button is erroneously thought to be in a form since the _current_form function checks the closest context, sees no form, goes to the next and sees a form, but the context does not reflect the actual structure, it should iterate the parents!
I know realistically no one writes an interface like this, but what happens when you run this piece of code is you get a submit button, outside of any form, and a form without a submit button inside it, instead of an exception.

So the cleaner alternative would be:

  • Neither _block nor _enqueue propagate _form_data to every element
  • Test test_is_in_form_true_when_dg_stack_has_form and test_is_in_form_false_when_dg_stack_has_no_form is deleted
  • The _current_form function actually iterates the parents of the active_dg until it hits the form dg which has a _form_data
    Something like
dg = this_dg._active_dg

while dg is not None:
    if dg._form_data is not None:
        return dg._form_data
    dg = dg._parent

Which one works best?

GitHub Issue Link (if applicable)

Fixes #8761
@Asaurus1 @kmcgrady I'm tagging you two because i saw you were interested in the issue

Testing Plan

I have added 2 new unit tests that reflect the desired behavior in the mentioned issue (these tests fail on the current origin/develop branch)


Contribution License Agreement

By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.

@SrGesus SrGesus changed the title [Fix] Being unable to determine form [Fix] Being unable to determine the form a current element is in Mar 13, 2025
@jrieke jrieke added the change:bugfix PR contains bug fix implementation label Mar 14, 2025
@lukasmasuch lukasmasuch added the impact:users PR changes affect end users label Mar 15, 2025
@SrGesus
Copy link
Contributor Author

SrGesus commented Mar 19, 2025

Is anyone able to take a look?

@sfc-gh-kjavadyan
Copy link
Contributor

Hey, @SrGesus, thank you very much for opening this PR ❤️ !

I just want to sync on the plan for working on this;
If I understood correctly, your current change solves the problem.
I am leaning toward merging this solution for now, and then, if you are interested, you can work on the more elegant solution as a separate PR.

For the second opinion on the alternative suggestion, I think @raethlein could help since he recently refactored DeltaGenerator, so he should have the most up-to-date context for it!

@sfc-gh-kjavadyan sfc-gh-kjavadyan added the security-assessment-completed Security assessment has been completed for PR label Mar 21, 2025

# Elements inherit their parent form ids.
# NOTE: Form ids aren't set in dg constructor.
output_dg._form_data = FormData(current_form_id(dg))
Copy link
Contributor

@sfc-gh-kjavadyan sfc-gh-kjavadyan Mar 21, 2025

Choose a reason for hiding this comment

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

optional: it would be possible to accept _form_data as an argument for dg construction and pass it at output_dg creation on line 485, right?

@kajarenc kajarenc merged commit faab7ca into streamlit:develop Mar 21, 2025
37 of 40 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:bugfix PR contains bug fix implementation impact:users PR changes affect end users security-assessment-completed Security assessment has been completed for PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cannot create form submit button in empty in form from outside form with with.

5 participants