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

Allow passing on_change_callback for CustomComponents #8633

Merged
merged 6 commits into from May 22, 2024

Conversation

raethlein
Copy link
Collaborator

@raethlein raethlein commented May 8, 2024

Describe your changes

In the past, some custom components have used a patch to register an on_change callback. Recently, we have done some refactoring that broke this workaround. This PR is a suggestion to extend our official API to make the patch redundant.

Note that we only want to pass the on_change_callback and not the args and kwargs.
The register_widget function today uses args and kwargs as keywords to pass to the on_change callback. Besides the unfortunate naming - these are special keywords meant for functions themselves and not for pass-through arguments - we are thinking about deprecating them entirely, since you can wrap the callback easily to pass the arguments.

The way you can use it then looks like the following:

from functools import partial

import streamlit.components.v1 as components

_custom_component = components.declare_component("example", path=build_dir)

callback = partial(lambda x: f"On Change called with {x}", "some-value-for-x")

value = _custom_component("some-arg", other_arg="some-other-arg", on_change=callback)
Without the partial-function
import streamlit.components.v1 as components

_custom_component = components.declare_component("example", path=build_dir)

def create_callback(x):
  return lambda: f"On Change called with {x}"
callback = create_callback("some-value-for-x")

value = _custom_component("some-arg", other_arg="some-other-arg", on_change=callback)

GitHub Issue Link (if applicable)

Closes #3977
Related to victoryhb/streamlit-option-menu#70

Testing Plan

  • Explanation of why no additional tests are needed
  • Unit Tests (JS and/or Python)
    • A new unit test is added to make sure the on_change callback is called when the value changes during a ScriptRun
  • E2E Tests
    • prepare on_change callback test in the option_menu function
  • Any manual testing needed?

Contribution License Agreement

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

@@ -215,7 +215,7 @@ def user_key_from_widget_id(widget_id: str) -> str | None:
"None" as a key, but we can't avoid this kind of problem while storing the
string representation of the no-user-key sentinel as part of the widget id.
"""
user_key = widget_id.split("-", maxsplit=2)[-1]
user_key: str | None = widget_id.split("-", maxsplit=2)[-1]
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this is an unrelated drive-by change since the linter complained about wrong typing

Comment on lines +81 to +83
# declare_component needs a script_run_ctx to be set
self.script_run_ctx = MagicMock(spec=ScriptRunContext)
add_script_run_ctx(threading.current_thread(), self.script_run_ctx)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This has to be added if you want to run the tests here standalone like pytest lib/tests/streamlit/components_test.py. In the CI it does not fail due to pure luck as apparently the order of execution of the tests ensures that a runtime exists 🫠

@raethlein raethlein marked this pull request as ready for review May 17, 2024 13:28
@raethlein raethlein requested review from vdonato and LukasMasuch and removed request for LukasMasuch and vdonato May 17, 2024 13:28
@whitphx
Copy link
Contributor

whitphx commented May 18, 2024

Hi, this PR looks great,
and as @raethlein suggested in #3977 (comment), I would like to test this patch in my use case.
Is it possible to get a wheel file including this somehow?

@raethlein
Copy link
Collaborator Author

@whitphx great, thanks for trying it out! Each PR generates a wheel file in the CI pipeline, here is the one for this 😊: https://core-previews.s3-us-west-2.amazonaws.com/pr-8633/streamlit-1.34.0-py2.py3-none-any.whl

@whitphx
Copy link
Contributor

whitphx commented May 20, 2024

Thanks!
I tested it and confirmed my use case was totally covered :D

@@ -102,6 +112,7 @@ def create_instance(
*args,
default: Any = None,
key: str | None = None,
on_change_handler: WidgetCallback | None,
Copy link
Collaborator Author

@raethlein raethlein May 21, 2024

Choose a reason for hiding this comment

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

@sfc-gh-jcarroll @jrieke Lukas' point makes sense to me. Could you have a quick look and confirm this, please?

Copy link
Collaborator

@LukasMasuch LukasMasuch left a comment

Choose a reason for hiding this comment

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

LGTM 👍 But I think we probably want to call this on_change and not on_change_handler to better align with APIs of other commands.

@raethlein raethlein force-pushed the custom-components-callback branch 2 times, most recently from 025ef65 to 6a86336 Compare May 22, 2024 10:41
Copy link
Collaborator

@LukasMasuch LukasMasuch left a comment

Choose a reason for hiding this comment

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

LGTM 👍 Another way to test it could be to update our component-template tests and update a component to use this callback.

@raethlein raethlein merged commit ad3dc4e into develop May 22, 2024
35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

A callback on a custom component
4 participants