Skip to content

Widgets with identical IDs raise an Exception#425

Merged
tconkling merged 7 commits intostreamlit:developfrom
tconkling:tim/SameWidgetIDError
Oct 16, 2019
Merged

Widgets with identical IDs raise an Exception#425
tconkling merged 7 commits intostreamlit:developfrom
tconkling:tim/SameWidgetIDError

Conversation

@tconkling
Copy link
Copy Markdown
Contributor

@tconkling tconkling commented Oct 16, 2019

  • When two widgets with the same ID are encountered in a report run, we throw a DuplicateWidgetID exception. The exception tells the user that they can use the new optional "key" parameter that each widget now takes in order to fix the issue.
  • (The error is slightly different if the user has manually supplied an identical key to multiple widgets.)
  • To track widget keys, ReportContext has a new widget_ids_this_run set. Each time a widget has an ID assigned, we add that ID to the set. If the ID was already in the set, the exception is thrown. (Because it's possible to access this set from multiple threads, access to it is wrapped in a threading.Lock).
  • ScriptRunner clears the widget_ids_this_run right before running its script.
  • User-supplied widget keys are "namespaced" by widget type (if you supply a custom widget key, the actual widget id becomes "{widget_type}-{user-key}"). So this allows two widgets with different types to share the same user key. Easy to change if this is undesirable!

Fixes #40

@tconkling tconkling requested a review from a team October 16, 2019 01:00
Copy link
Copy Markdown
Contributor

@monchier monchier left a comment

Choose a reason for hiding this comment

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

It would be nice to have this be some "base" property of a Widget, so that we don't have to go and test each individual widget.

Not sure this is feasible or what is a good strategy. Maybe could go in one of these decorators we have in DeltaGenerator.py?
Looks good to me otherwise.

Comment on lines +175 to +176
added = ctx.widget_ids_this_run.add(widget_id)
if not added:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It is not straightforward that added is None means that the widget id is duplicated. Maybe raise the exception inside add(?

Copy link
Copy Markdown
Contributor Author

@tconkling tconkling Oct 16, 2019

Choose a reason for hiding this comment

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

I (softly!) disagree - I have add() returning a bool (not a None) indicating whether the item was successfully added, which mirrors the API in other languages (e.g. https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.add?view=netframework-4.8).

I'm hesitant to add an Exception to indicate this case; it feels like it violates the "Never use exception for flow-control" principle. The item existing in the set already is not (IMO) an exceptional case - the exceptional case is when the user has defined a duplicate key, but this is the concern of the DeltaGenerator function, not the set it uses to detect this.

That said, I don't disagree so strongly that I wouldn't back down! Does anyone else in @streamlit/eng have an opinion here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think I come to agree with you. I missed it is returning a bool.



class _WidgetIDSet(object):
"""Stores a set of widget IDs. Safe to mutate from multiple threads."""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I have been told to use

"""Stores a set of widget IDs. Safe to mutate from multiple threads.
"""

See the newline.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

My understanding is that this rule only applies when the docstring is multiline. For singleline docstrings, the closing triple-quote goes on the same line, per https://www.python.org/dev/peps/pep-0257/#one-line-docstrings

Comment on lines +217 to +226
"button": lambda key=None: st.button("", key=key),
"checkbox": lambda key=None: st.checkbox("", key=key),
"multiselect": lambda key=None: st.multiselect("", options=[1, 2], key=key),
"radio": lambda key=None: st.radio("", options=[1, 2], key=key),
"selectbox": lambda key=None: st.selectbox("", options=[1, 2], key=key),
"slider": lambda key=None: st.slider("", key=key),
"text_area": lambda key=None: st.text_area("", key=key),
"text_input": lambda key=None: st.text_input("", key=key),
"time_input": lambda key=None: st.time_input("", key=key),
"date_input": lambda key=None: st.date_input("", key=key),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How are we going to remember to add new widgets here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure there's a good mechanism for this, other than vigilance? That is, anyone adding a new widget should be following the conventions established by all the other widgets.

We don't currently annotate any of the widget creation functions (and I believe @tvst is hoping to move away from our more deeply-nested decorator abuse within DeltaGenerator?). But: open to suggestions!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah... I see abuse in the decorator pattern. I feel having all this logic in DeltaGenerator is a bit overwhelming... Maybe better class structure? I am hesitant to propose a "base" class/"derived" class mechanism or an equivalent composition pattern, but it may work here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll open a ticket to start thinking about how this might be improved!

@tconkling tconkling merged commit 8b2481b into streamlit:develop Oct 16, 2019
@tconkling tconkling deleted the tim/SameWidgetIDError branch October 16, 2019 21:13
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.

Widgets with identical "ids" should raise exception

2 participants