-
Notifications
You must be signed in to change notification settings - Fork 100
Consider renaming shiny.express.layout
#854
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
Comments
I also like I also see a lot of value in being able to mix Express and core UI elements, so I see the closeness as useful to be able to switch between implementations if you realize you need one or the other. from shiny import ui
from shiny.express import layout as ux
with ux.div():
"something in a div"
ui.div("something also in a div") |
My sense of what's important for express is the following:
It seems that mixing Classic containers and Express containers is a bit of an edge case in that world, and so we shouldn't optimize for that use if it undermines the other goals. I really don't like the ui/ux because it's so easy to mess up when copying and pasting, and it'll make our documentation very confusing. I would have trouble distinguishing these two blocks of code, so a new learner would probably have more trouble.
I think if we want to bring the two interfaces together then it's probably better to have
|
As it currently stands, you can't really make anything useful without mixing the two, due in large part because it doesn't make a lot of sense to have "terminal" components (e.g.,
I can see the value in this when it comes to the coupling/decoupling of ui and server logic, but at least under the current implementation, there is an intentional and useful connection when it comes to UI components. If I where to teach the difference today, it would be "use |
Oh sorry I was unclear, I meant mixing container functions from |
I agree with @gshotwell's opinion that
Let's suppose we went with from shiny import ui
from shiny.express import xui
with xui.sidebar():
ui.input_action_button("go", ui.tags.b("Go!"))
ui.div("Hello")
with xui.div(style = "color: red;"):
ui.span("World!") Here are some of the good/bad/notable things:
Now, here's another possibility that is similar to @gshotwell's proposal: from shiny.express import ui
with ui.sidebar():
ui.input_action_button("go", ui.tags.b("Go!"))
ui.div("Hello", style = "color: blue;")
with ui.div(style = "color: red;"):
ui.span("World!") In this case, there are three categories of functions, all in
Good/bad/notable things about this:
|
My sense of Streamlit and Gradio users is that they pretty much exclusively use context management for layout. Here's an example of a fairly complicated and well regarded Gradio app. My guess is that these users would probably not think to create a container using the functional form unless we specifically told them to do so so if we're optimizing for these folks then the downside is probably worth the simplicity of the single input. I think if we exclusively use context management in our express examples and functional form in our prime examples then most users won't confuse the two. We can include some extended documentation which specifies which functions can be used in both forms for users who are interested in that. |
I have a couple questions, mostly out of curiosity and for my own edification:
|
Yes. An advantage of this is that everything is in the same module, and all functions from that module are usable in Express apps. In contrast, if there are two separate imports for The drawback of doing it this way is that it's not obvious which functions are context managers and which are not. That said, shinylive and pyright/VS Code will flag invalid uses in either direction.
There are some functions that must be used as context managers. There are others that can't be used as context managers. And there's a third category: functions that can be used either way. This category includes the It would be nice if all functions that could be used as context managers could also be used as normal functions, but when I tried to make this work earlier, I ran into some roadblocks that seemed insurmoutable. It seems like it's worth taking another look at this, though.
The general heuristic is that nestable components use
After #905, where results aren't automatically put in a # Use a context manager as a regular function
shiny.express.ui.value_box("title", "Value") Will throw an error like this:
On other side: with shiny.ui.nav_spacer():
"Content" Results in this error:
|
Thanks for the detailed explanation, @wch! Is it technically possible for us to improve those error messages in the future? If we're able, it'd be nice to give clear, actionable advice about how to fix the "right function, wrong context" problem. As a relative newcomer approaching Shiny Express, I have to say I've found it very helpful that where the function lives gives me a strong hint about how to use the function. I'd personally prefer to learn "functions from |
On one hand, I'm sympathetic to the idea of having a clear namespace distinction between context managers components and regular components. On the other hand, it's nice to have a single import, so the user doesn't have to think about which module to use each time they use one of the functions -- everything they'd want to use is in one place. To help make it clear whether a function is a context manager or regular function, it would be good for the docstrings of context managers to clearly indicate that at the top. I think we can improve the error messages when a context manager is used as a regular function. I don't think that we can do that for the reverse situation, where a regular function is used as a context manager (well, we could add |
I'm worried we're trading a small convenience -- a single import -- for many small inconveniences while writing and developing Shiny Express apps. If we separate context manager components and functional components, then users may need to import both modules if they want to use a mix of both components. Managing imports seems to be a common and normal activity for anyone developing in Python. So in this scenario we'd need to teach people:
If
From an educational perspective, the first scenario is a much easier teaching task. It's a simpler mental model and it's all information you can provide up front when teaching Shiny Express. On the other hand, the second scenario is educationally complex. Someone trying Shiny Express for the first time will definitely end up using a component in the wrong form and will run into the error scenarios without having the full context to debug them. We could alleviate that friction with more details up front, but this will make Shiny Express feel more complicated to learn, so we'd have to strike a balance in our docs. Above you mentioned there are some edge cases where |
I understand your concern here but I actually don't think it's going to be that confusing in practice. Our goal would be that the documentation for every context manager function would start with "Context manager" so you can immediately see it when the autocomplete/help popup appears. And for things that just take arguments (like And also I think in practice the functions that we have fall pretty clearly into "container-ish" or "widget-ish". The only one that gives me pause is The big exception to the above is HTML tags, because they will all support both. I don't have an intuition yet whether that will muddy the waters--I was thinking of not even explaining that there are two paradigms at work here, just show examples. with ui.div(class="border border-default"):
ui.h3("This is a title")
ui.p("This is a paragraph")
with ui.p():
"This is a much more complicated paragraph"
@render.display
def foo():
... I feel like I'd just look at that and be like, "OK, sure."
I have to admit it's news to me that you can't call a context manager directly and just have it tagify. I feel like it'd be trivial to have |
I think that would help a lot, as does the fact that that tags will support both forms. As long as nothing bad will happen when users pick the wrong form (except when that causes a truly show-stopping error), then my concerns would be alleviated quite a lot. |
I think most of the users who are coming from Streamlit or Gradio will have a pretty strong bias for context managers for layout components. So the thing I would worry most about is the case where someone tries to use something as a context manager and it fails because that's the target user's most likely pattern. My view is that we should try to use context managers exclusively in our Express documentation, even if they're not really the only way to lay those apps out. That way the user can have a fairly simple rule of "in Express I should use context managers for layout" even if that's more of a stylistic thing than a coding thing. |
Uh oh!
There was an error while loading. Please reload this page.
The name
layout
seems to be over-optimizing forwith layout.sidebar()
. It gets awkward with things likelayout.layout_*()
,layout.page_*()
, etc.I think I like
ux
as an alternative. Here are some pros/cons I can think of:Pros
ui
, which helps with the mental model of "useux
as an alternative ofui
when you want to use@render.*
decorators as children"layout
related.Cons
ui
(hurts readability), but the fact thatux
things will always startwith ux
will provide a clear enough distinction.The text was updated successfully, but these errors were encountered: