From 99fe52c62e9742a8e65faf257360cec2937b397f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Cs=C3=A1rdi?= Date: Tue, 7 Jan 2025 17:22:48 +0100 Subject: [PATCH] Scan deps: support IPython notebooks (#422) --- R/scan-deps.R | 28 +++++++++ tests/testthat/_snaps/scan-deps.md | 13 +++++ tests/testthat/fixtures/scan/notebook.ipynb | 63 +++++++++++++++++++++ tests/testthat/test-scan-deps.R | 8 +++ 4 files changed, 112 insertions(+) create mode 100644 tests/testthat/fixtures/scan/notebook.ipynb diff --git a/R/scan-deps.R b/R/scan-deps.R index 5183910e..94f63ef7 100644 --- a/R/scan-deps.R +++ b/R/scan-deps.R @@ -281,6 +281,7 @@ scan_path_deps_do <- function(code, path) { ".rmarkdown" = , ".rmd" = scan_path_deps_do_rmd(code, path), ".rnw" = scan_path_deps_do_rnw(code, path), + ".ipynb" = scan_path_deps_do_ipynb(code, path), "DESCRIPTION" = scan_path_deps_do_dsc(code, path), "NAMESPACE" = scan_path_deps_do_namespace(code, path), "_bookdown.yml" = scan_path_deps_do_bookdown(code, path), @@ -1231,3 +1232,30 @@ scan_path_deps_do_rnw_ranges <- function(code) { c(bmx, emx) }) } + +# ------------------------------------------------------------------------- + +scan_path_deps_do_ipynb <- function(code, path) { + ipynb <- jsonlite::fromJSON(code, simplifyVector = FALSE) + if (!identical(ipynb$metadata$kernelspec$language, "R")) { + return(NULL) + } + ir <- if (identical(ipynb$metadata$kernelspec$name, "ir")) { + scan_deps_df( + path = path, + package = "IRkernel", + code = NA_character_ + ) + } + + deps <- lapply(ipynb$cells, function(cell) { + if (identical(cell$cell_type, "code")) { + c1 <- paste(unlist(cell$source), collapse = "") + scan_path_deps_do_r(c1, path) + } + }) + + adeps <- drop_nulls(c(list(ir), deps)) + + do.call("rbind", adeps) +} diff --git a/tests/testthat/_snaps/scan-deps.md b/tests/testthat/_snaps/scan-deps.md index 5e257852..1ae1c6b4 100644 --- a/tests/testthat/_snaps/scan-deps.md +++ b/tests/testthat/_snaps/scan-deps.md @@ -499,3 +499,16 @@ 5 ignore-test.Rnw good good * prod library(good) 1 1 1 6 ignore-test.Rnw good good * prod library(good) 1 1 1 +# IPython notebook + + Code + scan_path_deps_do(readLines(path), basename(path)) + Output + # A data frame: 4 x 9 + path ref package version type code start_row start_column start_byte + + 1 notebook.ipynb IRkernel IRkernel * prod 1 1 1 + 2 notebook.ipynb MASS MASS * prod library(MASS) 1 1 1 + 3 notebook.ipynb stats stats * prod stats::setNames 1 1 1 + 4 notebook.ipynb cli cli * prod cli::cli_text 2 1 19 + diff --git a/tests/testthat/fixtures/scan/notebook.ipynb b/tests/testthat/fixtures/scan/notebook.ipynb new file mode 100644 index 00000000..8b95cf5f --- /dev/null +++ b/tests/testthat/fixtures/scan/notebook.ipynb @@ -0,0 +1,63 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Some markdown text" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "vscode": { + "languageId": "r" + } + }, + "outputs": [], + "source": [ + "library(MASS)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "vscode": { + "languageId": "r" + } + }, + "outputs": [], + "source": [ + "stats::setNames()\n", + "cli::cli_text()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "R", + "language": "R", + "name": "ir" + }, + "language_info": { + "codemirror_mode": "r", + "file_extension": ".r", + "mimetype": "text/x-r-source", + "name": "R", + "pygments_lexer": "r", + "version": "4.0.3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/tests/testthat/test-scan-deps.R b/tests/testthat/test-scan-deps.R index 04c09bba..4275b236 100644 --- a/tests/testthat/test-scan-deps.R +++ b/tests/testthat/test-scan-deps.R @@ -545,3 +545,11 @@ test_that("Ignored chunks in .Rnw file", { ) }) }) + +test_that("IPython notebook", { + local_reproducible_output(width = 500) + path <- test_path("fixtures/scan/notebook.ipynb") + expect_snapshot({ + scan_path_deps_do(readLines(path), basename(path)) + }) +})