From dae1624bf5a5aaede6149271f7ca9a1f256c80ae Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 25 Jun 2025 12:04:58 -0500 Subject: [PATCH 1/2] Pin kaleido to 0.2.1; get CI passing again --- .github/workflows/R-CMD-check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 66e69dd636..d6fee47ed7 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -72,7 +72,7 @@ jobs: - name: Install kaleido if: matrix.config.visual_tests == true run: | - Rscript -e 'library(reticulate); use_python(Sys.which("python")); py_install(c("kaleido", "plotly"))' + Rscript -e 'library(reticulate); use_python(Sys.which("python")); py_install(c("kaleido==0.2.1", "plotly"))' # Run test() before R CMD check since, for some reason, rcmdcheck::rcmdcheck() skips vdiffr tests - name: Run Tests From 7e558f59a042b77eec21e3f274c708ab966a6c8c Mon Sep 17 00:00:00 2001 From: Carson Date: Mon, 30 Jun 2025 17:42:03 -0500 Subject: [PATCH 2/2] First pass at kaleido 1.0 support --- .gitignore | 1 + R/kaleido.R | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 72c3ccb16a..107f4231a2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ Rapp.history *.RData *.Rproj.user *.DS_Store +.venv node_modules/ build_site.R revdep_email.R diff --git a/R/kaleido.R b/R/kaleido.R index 2ed9cdb678..f0667dfd91 100644 --- a/R/kaleido.R +++ b/R/kaleido.R @@ -70,7 +70,7 @@ kaleido <- function(...) { call_env <- rlang::caller_env() - if (!reticulate::py_available()) { + if (!reticulate::py_available(TRUE)) { rlang::abort(c("`{reticulate}` wasn't able to find a Python environment.", i = "If you have an existing Python installation, use `reticulate::use_python()` to inform `{reticulate}` of it.", i = "To have `{reticulate}` install Python for you, `reticulate::install_python()`." @@ -97,6 +97,67 @@ kaleido <- function(...) { } ) + res <- if (is.null(tryNULL(kaleido$scopes))) { + newKaleidoScope(kaleido) + } else { + legacyKaleidoScope(kaleido) + } + + class(res) <- "kaleidoScope" + res +} + +newKaleidoScope <- function(kaleido) { + list( + scopes = NULL, + transform = function(p, file, ..., width = NULL, height = NULL, scale = NULL) { + # Perform JSON conversion exactly how the R package would do it + fig <- to_JSON( + plotly_build(p)$x[c("data", "layout", "config")] + ) + + # Write to JSON file + tmp_json <- tempfile(fileext = ".json") + on.exit(unlink(tmp_json)) + writeLines(fig, tmp_json) + + # Import it as a fig (dict) + load_json <- sprintf( + "import json; fig = json.load(open('%s'))", + tmp_json + ) + reticulate::py_run_string(load_json) + + # TODO: Pass plotlyMainBundlePath() (and mathjax?) + # to page level options + #reticulate::py_run_string( + # "import kaleido; kaleido.PageGenerator()" + #) + + # Gather figure-level options + opts <- list( + format = tools::file_ext(file), + width = reticulate::r_to_py(width), + height = reticulate::r_to_py(height), + scale = reticulate::r_to_py(scale) + ) + + # TODO: how to bring in Mapbox token? + # https://github.com/plotly/Kaleido/issues/348 + #mapbox <- Sys.getenv("MAPBOX_TOKEN", NA) + #if (!is.na(mapbox)) { + # opts$mapboxAccessToken <- mapbox + #} + + # Write the figure to a file using kaleido + kaleido$write_fig_sync(reticulate::py$fig, file, opts = opts) + }, + shutdown = function() {} + ) +} + + +legacyKaleidoScope <- function(kaleido) { py <- reticulate::py scope_name <- paste0("scope_", new_id()) py[[scope_name]] <- kaleido$scopes$plotly$PlotlyScope( @@ -151,7 +212,6 @@ kaleido <- function(...) { reticulate::py_run_string(paste("del", scope_name)) }) - class(res) <- "kaleidoScope" res }