From de9aa0a4a495f3519d0227725967d32e93f964c5 Mon Sep 17 00:00:00 2001 From: MurielleDelmotte Date: Thu, 1 Aug 2024 13:17:36 +0000 Subject: [PATCH 1/4] feat: add att_from_examples() - Get all packages called in examples from R files Issue #103 --- DESCRIPTION | 2 +- NAMESPACE | 1 + R/add_from_examples.R | 34 +++++++++++++++++++++++++ man/att_from_examples.Rd | 23 +++++++++++++++++ tests/testthat/test-att_from_examples.R | 24 +++++++++++++++++ 5 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 R/add_from_examples.R create mode 100644 man/att_from_examples.Rd create mode 100644 tests/testthat/test-att_from_examples.R diff --git a/DESCRIPTION b/DESCRIPTION index da5f301..29aafc8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -31,7 +31,7 @@ Config/Needs/website: ThinkR-open/thinkrtemplate Config/testthat/edition: 3 Encoding: UTF-8 Roxygen: list(markdown = TRUE) -RoxygenNote: 7.3.0 +RoxygenNote: 7.3.1 Language: en-US Depends: R (>= 3.4) diff --git a/NAMESPACE b/NAMESPACE index 93eb18b..e5a41fe 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,6 +3,7 @@ export("%>%") export(att_amend_desc) export(att_from_description) +export(att_from_examples) export(att_from_namespace) export(att_from_qmd) export(att_from_qmds) diff --git a/R/add_from_examples.R b/R/add_from_examples.R new file mode 100644 index 0000000..32e9b74 --- /dev/null +++ b/R/add_from_examples.R @@ -0,0 +1,34 @@ +#' Get all packages called in examples from R files +#' +#' @param dir.r path to directory with R scripts. +#' +#' @return Character vector of packages called with library or require. +#' +#' @examples +#' dummypackage <- system.file("dummypackage",package = "attachment") +#' +#' # browseURL(dummypackage) +#' att_from_examples(dir.r = file.path(dummypackage,"R")) + +#' @export +att_from_examples <- function(dir.r = "R") { + rfiles <- list.files(dir.r, full.names = TRUE) + + roxy_file <- tempfile("roxy.examples", fileext = ".R") + + all_examples <- unlist(lapply(rfiles, function(the_file) { + file_roxytags <- roxygen2::parse_file(the_file) + res <- unlist( + lapply(file_roxytags, + function(x) roxygen2::block_get_tag_value(block = x, tag = "examples")) + ) + res + })) + # Clean \dontrun and \donttest, and replace with '{' on next line + all_examples_clean <- + gsub(pattern = "\\\\dontrun\\s*\\{|\\\\donttest\\s*\\{", replacement = "#ICI\n{", x = all_examples) + cat(all_examples_clean, file = roxy_file, sep = "\n") + all_deps_examples <- attachment::att_from_rscript(roxy_file) + file.remove(roxy_file) + return(all_deps_examples) +} diff --git a/man/att_from_examples.Rd b/man/att_from_examples.Rd new file mode 100644 index 0000000..473c463 --- /dev/null +++ b/man/att_from_examples.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/add_from_examples.R +\name{att_from_examples} +\alias{att_from_examples} +\title{Get all packages called in examples from R files} +\usage{ +att_from_examples(dir.r = "R") +} +\arguments{ +\item{dir.r}{path to directory with R scripts.} +} +\value{ +Character vector of packages called with library or require. +} +\description{ +Get all packages called in examples from R files +} +\examples{ +dummypackage <- system.file("dummypackage",package = "attachment") + +# browseURL(dummypackage) +att_from_examples(dir.r = file.path(dummypackage,"R")) +} diff --git a/tests/testthat/test-att_from_examples.R b/tests/testthat/test-att_from_examples.R new file mode 100644 index 0000000..4242b27 --- /dev/null +++ b/tests/testthat/test-att_from_examples.R @@ -0,0 +1,24 @@ +test_that("att_from_examples works", { + tmpdir <- tempfile("suggestexamples") + + dir.create(file.path(tmpdir, "R"), recursive = TRUE) + + r_file <- file.path(tmpdir, "R", "fun_manual.R") + file.create(r_file) + + writeLines( + text = "#' @importFrom magrittr %>% +#' @examples +#' library(magrittr) +#' fakepkg::fun() +#' @export +my_length <- function(x) { + x %>% length() +}", + con = r_file + ) + + expect_equal(att_from_examples(dir.r = file.path(tmpdir, "R")), + c("magrittr", "fakepkg")) + +}) From 85297a26035be79554d1548c7a17632d10b59995 Mon Sep 17 00:00:00 2001 From: MurielleDelmotte Date: Thu, 1 Aug 2024 13:23:18 +0000 Subject: [PATCH 2/4] feat: Improve att_amend_desc with Suggest from examples - Used att_from_examples - Complete dummypackage with an example - Update test with the new dummypackage Issue #103 --- R/att_to_description.R | 12 ++- inst/dummypackage/R/my_mean.R | 3 + man/att_amend_desc.Rd | 2 +- man/attachment-package.Rd | 4 +- tests/testthat/test-amend-description.R | 106 ++++++++++++++++++++++-- 5 files changed, 117 insertions(+), 10 deletions(-) diff --git a/R/att_to_description.R b/R/att_to_description.R index 1bc5131..3c3804d 100644 --- a/R/att_to_description.R +++ b/R/att_to_description.R @@ -1,7 +1,7 @@ #' Amend DESCRIPTION with dependencies read from package code parsing #' #' Amend package DESCRIPTION file with the list of dependencies extracted from -#' R, tests, vignettes files. +#' R, examples, tests, vignettes files. #' att_to_desc_from_pkg() is an alias of att_amend_desc(), #' for the correspondence with [att_to_desc_from_is()]. #' @@ -19,6 +19,7 @@ #' @inheritParams att_from_namespace #' @inheritParams att_to_desc_from_is #' @inheritParams att_from_rmds +#' @inheritParams att_from_examples #' #' @importFrom desc description #' @@ -88,6 +89,8 @@ att_amend_desc <- function(path = ".", on.exit(setwd(old)) } + + path <- normalizePath(path) # decide whether to use or update config file ---- @@ -196,6 +199,13 @@ att_amend_desc <- function(path = ".", # Suggests ---- suggests <- NULL + + # Get suggests in examples and remove if already in imports + if (dir.r != "") { + ex <- att_from_examples(dir.r = dir.r) + suggests <- c(suggests, ex[!ex %in% imports]) + } + # Get suggests in vignettes and remove if already in imports if (!grepl("^$|^\\s+$$", dir.v)) { vg <- att_from_rmds(dir.v, inside_rmd = inside_rmd) diff --git a/inst/dummypackage/R/my_mean.R b/inst/dummypackage/R/my_mean.R index 1f2d3ae..df18239 100644 --- a/inst/dummypackage/R/my_mean.R +++ b/inst/dummypackage/R/my_mean.R @@ -4,6 +4,9 @@ #' #' @export #' @importFrom magrittr %>% +#' @examples +#' # example code +#' library(utils) my_mean <- function(x){ x <- x %>% stats::na.omit() 1+1 diff --git a/man/att_amend_desc.Rd b/man/att_amend_desc.Rd index b39f39f..6ddc9e6 100644 --- a/man/att_amend_desc.Rd +++ b/man/att_amend_desc.Rd @@ -83,7 +83,7 @@ Update DESCRIPTION file. } \description{ Amend package DESCRIPTION file with the list of dependencies extracted from -R, tests, vignettes files. +R, examples, tests, vignettes files. att_to_desc_from_pkg() is an alias of att_amend_desc(), for the correspondence with \code{\link[=att_to_desc_from_is]{att_to_desc_from_is()}}. } diff --git a/man/attachment-package.Rd b/man/attachment-package.Rd index f2a2f28..5c11039 100644 --- a/man/attachment-package.Rd +++ b/man/attachment-package.Rd @@ -20,12 +20,12 @@ Useful links: } \author{ -\strong{Maintainer}: Murielle Delmotte \email{murielle@thinkr.fr} (\href{https://orcid.org/0000-0002-1339-2424}{ORCID}) +\strong{Maintainer}: Sébastien Rochette \email{sebastien@thinkr.fr} (\href{https://orcid.org/0000-0002-1565-9313}{ORCID}) Authors: \itemize{ - \item Sébastien Rochette \email{sebastien@thinkr.fr} (\href{https://orcid.org/0000-0002-1565-9313}{ORCID}) \item Vincent Guyader \email{vincent@thinkr.fr} (\href{https://orcid.org/0000-0003-0671-9270}{ORCID}) (previous maintainer) + \item Murielle Delmotte \email{murielle@thinkr.fr} (\href{https://orcid.org/0000-0002-1339-2424}{ORCID}) \item Swann Floc'hlay \email{swann@thinkr.fr} (\href{https://orcid.org/0000-0003-1477-830X}{ORCID}) } diff --git a/tests/testthat/test-amend-description.R b/tests/testthat/test-amend-description.R index 3ce263e..413408b 100644 --- a/tests/testthat/test-amend-description.R +++ b/tests/testthat/test-amend-description.R @@ -40,9 +40,10 @@ test_that("att_amend_desc updates description", { expect_equal(desc_file[w.depends + 6], " glue,") expect_equal(desc_file[w.depends + 7], " knitr,") expect_equal(desc_file[w.depends + 8], " rmarkdown,") - expect_equal(desc_file[w.depends + 9], " testthat") - expect_equal(desc_file[w.depends + 10], "LinkingTo:" ) - expect_equal(desc_file[w.depends + 11], " Rcpp") + expect_equal(desc_file[w.depends + 9], " testthat,") + expect_equal(desc_file[w.depends + 10], " utils") + expect_equal(desc_file[w.depends + 11], "LinkingTo:" ) + expect_equal(desc_file[w.depends + 12], " Rcpp") # base does not appear expect_false(all(grepl("base", desc_file))) # utils is removed @@ -485,12 +486,105 @@ library(ggplot3) expect_equal(desc_file[w.depends + 7], " glue,") expect_equal(desc_file[w.depends + 8], " knitr,") expect_equal(desc_file[w.depends + 9], " rmarkdown,") - expect_equal(desc_file[w.depends + 10], " testthat") - expect_equal(desc_file[w.depends + 11], "LinkingTo:" ) - expect_equal(desc_file[w.depends + 12], " Rcpp") + expect_equal(desc_file[w.depends + 10], " testthat,") + expect_equal(desc_file[w.depends + 11], " utils") + expect_equal(desc_file[w.depends + 12], "LinkingTo:" ) + expect_equal(desc_file[w.depends + 13], " Rcpp") # Clean after unlink(dummypackage, recursive = TRUE) }) + +# Test update desc when packages in examples ---- +test_that("if a package is used in an example, it is added to Suggests packages", { + # Copy package in a temporary directory + tmpdir <- tempfile("dummysuggestexamples") + dir.create(tmpdir) + file.copy(system.file("dummypackage",package = "attachment"), tmpdir, recursive = TRUE) + dummypackage <- file.path(tmpdir, "dummypackage") + + r_file <- file.path(dummypackage, "R", "fun_manual.R") + file.create(r_file) + #> [1] TRUE + writeLines( + text = "#' @importFrom magrittr %>% +#' @examples +#' library(pkgfake) +#' fakepkg::fun() +#' @export +my_length <- function(x) { + x %>% length() +}", + con = r_file + ) + + attachment::att_amend_desc(path = dummypackage, check_if_suggests_is_installed = FALSE) + + + desc_file <- readLines(file.path(dummypackage, "DESCRIPTION")) + + w.depends <- grep("Depends:", desc_file) + expect_length(w.depends, 1) + expect_equal(desc_file[w.depends + 5], "Suggests: ") + expect_equal(desc_file[w.depends + 6], " fakepkg,") + expect_equal(desc_file[w.depends + 7], " glue,") + expect_equal(desc_file[w.depends + 8], " knitr,") + expect_equal(desc_file[w.depends + 9], " pkgfake,") + expect_equal(desc_file[w.depends + 10], " rmarkdown,") + expect_equal(desc_file[w.depends + 11], " testthat,") + expect_equal(desc_file[w.depends + 12], " utils") + # Clean after + unlink(dummypackage, recursive = TRUE) + +}) + + +test_that("if a package is used in an example has already been added to IMPORTS, it is not added to the suggests, it is added to Suggests packages", { + # Copy package in a temporary directory + tmpdir <- tempfile("dummysuggestexamples") + dir.create(tmpdir) + file.copy(system.file("dummypackage",package = "attachment"), tmpdir, recursive = TRUE) + dummypackage <- file.path(tmpdir, "dummypackage") + + r_file <- file.path(dummypackage, "R", "fun_manual.R") + file.create(r_file) + #> [1] TRUE + writeLines( + text = "#' @importFrom magrittr %>% +#' @examples +#' library(magrittr) +#' fakepkg::fun() +#' @export +my_length <- function(x) { + x %>% length() +}", + con = r_file + ) + + attachment::att_amend_desc(path = dummypackage, check_if_suggests_is_installed = FALSE) + + + desc_file <- readLines(file.path(dummypackage, "DESCRIPTION")) + + w.depends <- grep("Depends:", desc_file) + expect_length(w.depends, 1) + expect_equal(desc_file[w.depends + 1], " R (>= 3.5.0)") + expect_equal(desc_file[w.depends + 2], "Imports: ") + expect_equal(desc_file[w.depends + 3], " magrittr,") + expect_equal(desc_file[w.depends + 4], " stats") + expect_equal(desc_file[w.depends + 5], "Suggests: ") + expect_equal(desc_file[w.depends + 6], " fakepkg,") + expect_equal(desc_file[w.depends + 7], " glue,") + expect_equal(desc_file[w.depends + 8], " knitr,") + expect_equal(desc_file[w.depends + 9], " rmarkdown,") + expect_equal(desc_file[w.depends + 10], " testthat,") + expect_equal(desc_file[w.depends + 11], " utils") + expect_equal(desc_file[w.depends + 12], "LinkingTo:" ) + expect_equal(desc_file[w.depends + 13], " Rcpp") + + # Clean after + unlink(dummypackage, recursive = TRUE) + +}) From 76990ffd8f70f6c46e2d4cc33bd86a0dbe89cdb9 Mon Sep 17 00:00:00 2001 From: MurielleDelmotte Date: Thu, 1 Aug 2024 13:24:19 +0000 Subject: [PATCH 3/4] Doc : complete doc with att_from_examples - How to use att_from_examples Issue #103 --- vignettes/a-fill-pkg-description.Rmd | 3 ++- vignettes/b-bookdown-and-scripts.Rmd | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/vignettes/a-fill-pkg-description.Rmd b/vignettes/a-fill-pkg-description.Rmd index 21b33d5..3bf26a7 100644 --- a/vignettes/a-fill-pkg-description.Rmd +++ b/vignettes/a-fill-pkg-description.Rmd @@ -221,13 +221,14 @@ for (i in to_install) { ## Other possibilities -Of course, you can also use {attachment} out of a package to list all package dependencies of R scripts using `att_from_rscripts()` or Rmd files using `att_from_rmds()`. +Of course, you can also use {attachment} out of a package to list all package dependencies of R scripts using `att_from_rscripts()`, Rmd files using `att_from_rmds()` or examples from R scripts using `att_from_examples()`. ```{r, eval=TRUE} dummypackage <- system.file("dummypackage", package = "attachment") att_from_rscripts(path = file.path(dummypackage, "R")) att_from_rmds(path = file.path(dummypackage, "vignettes"), inside_rmd = TRUE) +att_from_examples(dir.r = file.path(dummypackage, "R")) ``` diff --git a/vignettes/b-bookdown-and-scripts.Rmd b/vignettes/b-bookdown-and-scripts.Rmd index 5d75433..a7e2779 100644 --- a/vignettes/b-bookdown-and-scripts.Rmd +++ b/vignettes/b-bookdown-and-scripts.Rmd @@ -106,6 +106,12 @@ This reads all files in directories of R scripts (default to `R` directory of a att_from_rscripts() ``` +Called in examples from R scripts: + +```{r, eval=FALSE} +att_from_examples() +``` + ## Get all packages called in your Rmd If you have vignette, you may want to list extra libraries, not listed in your "Depends" list. This function applies to any Rmd file, of course. From 2c6270c1c40c176a88111108886ba79bcc903686 Mon Sep 17 00:00:00 2001 From: MurielleDelmotte Date: Thu, 1 Aug 2024 14:11:24 +0000 Subject: [PATCH 4/4] chore : bump dev version 0.4.2.9000 - complete NEWS with minor changes Issue #103 --- DESCRIPTION | 2 +- NEWS.md | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 29aafc8..df46657 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: attachment Title: Deal with Dependencies -Version: 0.4.2 +Version: 0.4.2.9000 Authors@R: c( person("Sébastien", "Rochette", , "sebastien@thinkr.fr", role = c("cre", "aut"), comment = c(ORCID = "0000-0002-1565-9313")), diff --git a/NEWS.md b/NEWS.md index d399da2..ec33ddd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,14 @@ +# attachment (development version) + +## New features + +- Add `att_from_examples()` to get all packages called in examples from R files +- `att_amend_desc` amend package DESCRIPTION file (Suggests) with the list of dependencies extracted from examples in R files. + +## Patch + +- Adding an example using a suggest package to the dummypackage + # attachment 0.4.2 ## New features @@ -182,7 +193,7 @@ Minor * New function `install_from_description` to install all missing packages listed in the description file * Add an hex by @statnmap ! * Allow for absence of vignette folder in `att_to_description` -* Add `create_dependencies_file` to create a file listing all packages dependencies to install before your package +* Add `create_dependencies_file` to create a file listing all packages dependencies to dinstall before your package * Allow for `pkg::fun` calls in R scripts with `att_from_functions` * Add option to run `devtools::document()` before `att_from_description`