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

Provide support for hot reloading in Dash for R #127

Merged
merged 59 commits into from
Nov 1, 2019
Merged

Conversation

rpkyle
Copy link
Contributor

@rpkyle rpkyle commented Sep 18, 2019

This PR proposes to support frontend and backend hot reloading in Dash for R, in a manner analogous to that currently provided in Dash for Python (plotly/dash#66, plotly/dash#362, plotly/dash-renderer#73).

The primary modifications required include

  • Identifying the local path to the running Dash app, and storing this within the Dash object
  • Tracking file changes, both within the specified assets folder, but also within the app directory root itself
  • Generating a reload hash and a route to supply it on demand to the frontend
  • Formatting a JSON response to the frontend, including a modified file manifest
  • Handle partial reloading for CSS modifications only as in Dash for Python
  • Providing a custom Fiery logger which silences routing and request traffic on demand using the dev_tools_silence_routes_logging parameter, as in Dash for Python
  • Providing support for restarting the Fiery server when hard_reload is set (e.g. if non-CSS assets or the app itself is modified), and running source to reload the app code

✨ It also proposes a new (logical) viewer parameter, which can be used to load Dash apps within RStudio for a more pleasant development experience. This feature requires specifying 127.0.0.1 or localhost for the host parameter; this is a restriction imposed by RStudio's API.

https://www.rdocumentation.org/packages/rstudioapi/versions/0.10/topics/viewer

Additional work is required:

  • To format and return a JSON response on file modification, and to ensure that reloading for modifications to CSS only behaves as in Dash for Python (soft reload, style should change without losing state)
  • I'll also need to consider implementation of dev_tools_hot_reload_watch_interval; this is used to control how frequently walk_assets_directory is invoked.
  • 🚨 We'll need a minimal test suite to verify that this feature behaves as desired.
  • Add in relevant R help content for (internal, package-level) documentation
  • Update CHANGELOG.md for dev branch

Closes #125.

@alexcjohnson @Stalkcomrade

@rpkyle rpkyle added enhancement feature request Community request for a package enhancement labels Sep 18, 2019
@rpkyle rpkyle added this to the Dash v1.4.0 milestone Sep 18, 2019
@rpkyle rpkyle self-assigned this Sep 18, 2019
@rpkyle rpkyle added the parity Modifications to improve parity across Dash implementations label Sep 22, 2019
@Marc-Andre-Rivet Marc-Andre-Rivet removed this from the Dash v1.4.0 milestone Oct 8, 2019
@rpkyle
Copy link
Contributor Author

rpkyle commented Oct 30, 2019

  • Test a hard-reload scenario - same as above but the callback output should reset. I guess that would mean putting the app itself into a file and editing that file similarly to the css edit...

fixed in 9979346

@rpkyle
Copy link
Contributor Author

rpkyle commented Oct 30, 2019

  • Update CHANGELOG.md for dev branch

fixed in da30cb2

CHANGELOG.md Outdated Show resolved Hide resolved
@rpkyle
Copy link
Contributor Author

rpkyle commented Oct 30, 2019

  • Add in relevant R help content for (internal, package-level) documentation

fixed in 79a4996

@rpkyle
Copy link
Contributor Author

rpkyle commented Oct 30, 2019

@alexcjohnson I think this one is ready to go 🚒 . I also added in documentation for clientsideFunction, which was previously missing.

@rpkyle rpkyle force-pushed the 125-hot-reloading branch 2 times, most recently from cb02497 to df4c737 Compare October 31, 2019 07:47
R/dash.R Outdated
router$add_route(route, "dashR-endpoints")
server$attach(router)

Copy link
Collaborator

Choose a reason for hiding this comment

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

πŸ™„

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed in aa1ad07

R/dash.R Outdated
self$server$port <- getServerParam(as.integer(port), "numeric", 8050)

dev_tools_ui <- getServerParam(dev_tools_ui, "logical", NULL)
dev_tools_props_check <- getServerParam(dev_tools_props_check, "logical", NULL)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you need an as.logical here (and all the "logical" items), for the getenv case? In fact, perhaps getServerParam could be written to do this coercion, at least if you start with a string?

This looks like a good solution to me - one kind of funny thing is users can provide all the run_server args as strings - app$run_server(port="1234") as well as app$run_server(port=1234) - but a nice side effect is we won't accept run_server args that don't end up with the correct type.

BTW it would be cleaner to set debug first, then use that as the default for some of these other args - rather than needing the confusing is.null(x) && debug || isTRUE(x) clauses.

Copy link
Contributor Author

@rpkyle rpkyle Nov 1, 2019

Choose a reason for hiding this comment

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

Good point. I'll make that change, you're right that the handling for logicals-as-strings is missing this critical detail.

As for the confusing clauses, following up on our offline conversation, will use debug as their default since it is always TRUE or FALSE.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed in 6131677

R/dash.R Outdated
if (is.null(dev_tools_ui) && debug || isTRUE(dev_tools_ui)) {
self$config$ui <- TRUE
getServerParam <- function(value, type, default) {
if (!is.null(value) && toupper(value) %in% c("TRUE", "FALSE"))
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
if (!is.null(value) && toupper(value) %in% c("TRUE", "FALSE"))
if (type == "logical" && !is.null(value) && toupper(value) %in% c("TRUE", "FALSE"))

Maybe also add a clause

if (type == "numeric")
    value <- as.numeric(value)

for the hot reload intervals; as.integer probably has to stay where it is since that doesn't appear to map onto a type...

Copy link
Contributor Author

@rpkyle rpkyle Nov 1, 2019

Choose a reason for hiding this comment

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

You can verify that an integer is of mode numeric and storage mode integer, typeof is probably a better approach but mode and storage.mode are other options:

> foo <- 1L
> mode(foo)
[1] "numeric"
> storage.mode(foo)
[1] "integer"

The col_info function I wrote a while back attempts to present a summary with all the type/mode information I generally want for vectors and data.frames:

https://github.com/rpkyle/cscmisc/blob/master/R/col_info.R

e.g.

> cscmisc::col_info(foo)
       mode   class storage.mode is.factor
foo numeric integer      integer     FALSE

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed in ba7c015

@rpkyle
Copy link
Contributor Author

rpkyle commented Nov 1, 2019

@alexcjohnson As a follow-up to our offline conversation, I've tried to make handling of (what ought to be) numeric dev tools parameters a bit more robust in 2e8b90c.

Copy link
Collaborator

@alexcjohnson alexcjohnson left a comment

Choose a reason for hiding this comment

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

πŸ”₯ πŸ’ƒ πŸ”₯

@rpkyle rpkyle merged commit c947c73 into dev Nov 1, 2019
@rpkyle rpkyle deleted the 125-hot-reloading branch November 1, 2019 23:17
@rpkyle rpkyle mentioned this pull request Jan 3, 2020
rpkyle added a commit that referenced this pull request Jan 4, 2020
* Provide support for no_update in Dash for R (#111)

* Use dev_tools_prune_errors instead of pruned_errors (#113)

* Better handling for user-defined error conditions in debug mode (#116)

* Provide support for multiple outputs (#119)

* Provide support for hot reloading in Dash for R (#127)

* Implement support for clientside callbacks in Dash for R (#130)

* Add line number context to stack traces when srcrefs are available (#133)

* Update dash-renderer to 1.2.2 and fix dev tools UI display of stack traces (#137)

* Support for meta tags in Dash for R (#142)

* Fixes for hot reloading interval handling and refreshing apps within viewer pane (#148)

* Support for asynchronous loading/compression in Dash for R (#157)

* Support returning asset URLs via public method within Dash class (#160)

* Minor fix for get_asset_url + docs, add url_base_pathname (#161)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dash-type-enhancement feature request Community request for a package enhancement parity Modifications to improve parity across Dash implementations
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants