Skip to content

Conversation

cpsievert
Copy link
Collaborator

@cpsievert cpsievert commented Jan 30, 2025

Closes #623
Closes #711

Here's an example to replicate the one on this page https://shiny.posit.co/r/articles/build/client-data/

app.py
# pyright: reportUnknownMemberType=false, reportUnknownVariableType=false
import matplotlib.pyplot as plt
import numpy as np

from shiny.express import input, render, session, ui

with ui.sidebar(open="closed"):
    ui.input_slider("obs", "Number of observations:", min=0, max=1000, value=500)

ui.markdown(
    """
#### `session.clientdata` values

The following methods are available from the `session.clientdata` object and allow you
to reactively read the client data values from the browser.
"""
)


@render.code
def clientdatatext():
    return f"""
    .url_hash()         -> {session.clientdata.url_hash()}
    .url_hash_initial() -> {session.clientdata.url_hash_initial()}
    .url_hostname()     -> {session.clientdata.url_hostname()}
    .url_pathname()     -> {session.clientdata.url_pathname()}
    .url_port()         -> {session.clientdata.url_port()}
    .url_protocol()     -> {session.clientdata.url_protocol()}
    .url_search()       -> {session.clientdata.url_search()}
    .pixelratio()       -> {session.clientdata.pixelratio()}

    .output_height("myplot")       -> {session.clientdata.output_height("myplot")}
    .output_width("myplot")        -> {session.clientdata.output_width("myplot")}
    .output_hidden("myplot")       -> {session.clientdata.output_hidden("myplot")}
    .output_bg_color("myplot")     -> {session.clientdata.output_bg_color("myplot")}
    .output_fg_color("myplot")     -> {session.clientdata.output_fg_color("myplot")}
    .output_accent_color("myplot") -> {session.clientdata.output_accent_color("myplot")}
    .output_font("myplot")         -> {session.clientdata.output_font("myplot")}
    """


@render.plot
def myplot():
    plt.figure()
    plt.hist(np.random.normal(size=input.obs()))  # type: ignore
    plt.title("This is myplot")
Screenshot 2025-01-29 at 6 37 38 PM

TODO

@cpsievert cpsievert changed the title Add clientdata accessor to session Add clientdata accessor to session; allow access to input names via dir() Jan 30, 2025
@cpsievert cpsievert requested a review from gadenbuie January 30, 2025 00:38
@cpsievert cpsievert marked this pull request as ready for review February 10, 2025 17:58
Comment on lines 12 to 14
def clientdatatext():
cdata = session.clientdata
return "\n".join([f"{name} = {cdata[name]()}" for name in reversed(dir(cdata))])
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
def clientdatatext():
cdata = session.clientdata
return "\n".join([f"{name} = {cdata[name]()}" for name in reversed(dir(cdata))])
def client_data_text():
client_data = session.clientdata
return "\n".join([
f"{name} = {client_data[name]()}" for name in reversed(dir(client_data))
])

To avoid CDATA vibes.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We don't get autocomplete for session.clientdata, right? So you can use dir() to find out the methods but you won't get autocomplete for session.clientdata.url_*(). The autocomplete would be helpful but isn't a blocker.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Nope, no autocomplete. And agreed, it'd be useful, but I also don't see a low effort way to faithfully type those values (not to mention them easily getting out of sync when shiny.js changes).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I guess maybe it'd be worth having a ClientData class though so we can at least make them discoverable through the API reference?

class ClientData(Inputs):
     "TODO: docs here"
    pass

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Actually, hold on, maybe there is a sensible thing to do for autocomplete as well...

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah I also don't think all the values need to be accessible in autocomplete, but it'd be useful to have the url_ and other stable data as well

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for the nudge here -- e1e4526 takes a more typing and documentation friendly approach

Comment on lines 11 to 22
page.goto(local_app.url)

text = controller.OutputTextVerbatim(page, "clientdatatext")

# This doesn't cover all the clientdata values since some of them
# are environment-dependent. However, this at least checks that the
# clientdata object is available and that some values are present.
text.expect.to_contain_text("url_protocol = http")
text.expect.to_contain_text("url_pathname = /")
text.expect.to_contain_text(
re.compile("url_hostname = (localhost|127\\.0\\.0\\.1)")
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you test url_search, url_hash_initial and url_hash here? Those are all more valuable and likely to be used than, say, url_protocol.

Copy link
Collaborator Author

@cpsievert cpsievert Feb 10, 2025

Choose a reason for hiding this comment

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

There's a shinycoreci app that already does a good job of covering the front end logic (see comment added in a30b8c8), so I'd rather not redo it

@cpsievert cpsievert changed the title Add clientdata accessor to session; allow access to input names via dir() Add session.clientdata; allow access to input names via dir() Feb 11, 2025
@cpsievert
Copy link
Collaborator Author

@gadenbuie this is ready for another look -- thanks for the suggestions!

@cpsievert cpsievert merged commit c08d91a into main Feb 11, 2025
41 checks passed
@cpsievert cpsievert deleted the session-clientdata branch February 11, 2025 21:29
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Missing .clientdata accessors Expose clientData on session object
2 participants