| Title: | Checking Tools for Safer R Function Development |
|---|---|
| Description: | Set of functions that check and correct the code of R functions according to the safer-r project recommendations (see <https://github.com/safer-r>): argument value checking, explicit package namespace qualification of the function calls, complete writing of the arguments of the called functions, curly brackets around the bodies of if, else, for, while, repeat and function, TRUE and FALSE written in full, controlled for() loop sequences and standard function() writing of lambda functions. The corrected function is returned or written into a file. Other functions check the R scope, catch messages, check the presence of packages and functions, and write log files. |
| Authors: | Haiding Wang [ctb], Yushi Han [ctb], Mia Legras [ctb], Gael Millot [aut, cre] (ORCID: <https://orcid.org/0000-0002-0591-3509>) |
| Maintainer: | Gael Millot <[email protected]> |
| License: | GPL-3 |
| Version: | 3.0.0 |
| Built: | 2026-10-31 18:51:19 UTC |
| Source: | https://github.com/safer-r/saferdev |
Verify that all the functions used inside a function are written with all their arguments. For instance: base::paste0(letters[1:2], collapse = NULL, recycle0 = FALSE) and not paste0(letters[1:2]).
all_args_here( x, table_export = FALSE, fun_export = FALSE, out_path = ".", df_name = "res.tsv", overwrite = FALSE, safer_check = TRUE, lib_path = NULL, error_text = "" )all_args_here( x, table_export = FALSE, fun_export = FALSE, out_path = ".", df_name = "res.tsv", overwrite = FALSE, safer_check = TRUE, lib_path = NULL, error_text = "" )
x |
Function name, written without quotes and brackets. |
table_export |
Single logical value. Export the result data frame into a |
fun_export |
Single logical value. Export the modified function into a |
out_path |
Single character string indicating the absolute pathway of the folder where to export the data frame or the modified function. |
df_name |
Single character string indicating the name of the exported data frame file. Ignored if |
overwrite |
Single logical value. Overwrite potential files already existing in |
safer_check |
Single logical value. Perform the "safer" checks? If |
lib_path |
Vector of characters specifying the absolute pathways of the directories containing the required packages for the function, if not in the default directories. Useful when R packages are not installed in the default directories because of lack of admin rights. More precisely, |
error_text |
Single character string used to add information in error messages returned by the function, notably if the function is inside other functions, which is practical for debugging. Example: |
More precisely, all_args_here() verifies that all the strings before an opening bracket ( are written with all their arguments. Thus, it cannot check function names written without brackets, like in the FUN argument of some functions, e.g., sapply(1:3, FUN = as.character).
The Perl regex used to detect a function name is: "([a-zA-Z]|\.[a-zA-Z._])[a-zA-Z0-9._]*\s*\(".
Currently, all_args_here() cannot detect functions written between quotes, like "+"() or "rownames<-"(x, "a").
Function names preceded by $ are not considered.
The following R functions are skipped: function, if, for, while, repeat and else.
Empty argument values (see the safer-r rules): 1) a default value that cannot be copied into the call is proposed with an empty value, meaning that the default value is used but cannot be fixed. This is the case when the default value depends on another argument (e.g., envir = as.environment(pos) in utils::getFromNamespace(), proposed as utils::getFromNamespace(x, ns, pos = -1, envir = )), on a variable of the internal environment of the function, or on the frame where it is evaluated (e.g., parent.frame()), because such a default value would be evaluated in the environment of the tested function and not in the one of the called function. 2) An argument with no default value and not written is also proposed with an empty value, because absence of value can mean something (e.g., base::.libPaths(new = , include.site = TRUE) returns the paths of the R libraries, whereas base::.libPaths(new = base::.libPaths(), include.site = TRUE) sets them). In both cases, the behavior of the code is not changed. An argument already written with an empty value (like envir = ) is accepted as is when it has no default value or a default value that cannot be copied, and is not listed in the message above. If its default value can be copied (e.g., safer_check = ), the default value is added (safer_check = TRUE). Of note, only the arguments before ... can be written with a partial name, as in R. It is recommended to test (using testthat) that these default values are unchanged, for instance testthat::expect_identical(object = base::formals(fun = utils::getFromNamespace)$envir, expected = base::quote(expr = as.environment(pos))).
Most of the time, all_args_here() does not check inside comments, but some unexpected writing could dupe all_args_here(). Please, report here if it is the case.
The returned line numbers are indicative, depending on which source is checked. For instance, saferDev::report (compiled) has not the same line numbers as its source file. Notably, compiled functions do not have comments anymore, compared to the same source function sourced into the working environment. In addition, the counting starts at the "<- function" line, i.e., without counting the #' header lines potentially present in source files.
The function works first by replacing in the code: 1) the three consecutive characters '"' or '"' by three spaces, 2) "\"'" and '\'"' by four spaces and 3) escape quotes like \" or \' by two spaces.
Pre-check: the submitted function is first checked by lambda_check(): if some lambda functions are written with the \(x) shorthand, an error is returned asking to run saferDev::lambda_check() first, because the \ writings are not managed by the text-based detection of all_args_here(). As in lambda_check(), the \(x) writing is only detectable in a function carrying its source references (attribute srcref): a compiled or loaded package function prints function(x), so that the pre-check silently passes in that case.
Known limitation: a string literal containing an escaped backslash-quote, like "'\'", desynchronizes the in-quotes detection of all_args_here(), which can return an internal error (see the dev/repro_all_args_here_internal_error_3.R script of the package repository). Please, report here if it is the case.
Line breaks, comments and indentation of the modified function (fun_export argument and returned function): the $STATUS proposals are not pasted in place of the calls. Instead, the missing arguments are inserted into the printed code of the submitted function, at the positions given by the R parser: a missing argument whose value is written without its name (e.g., round(v)) gets its name inserted before this value (round(x = v, digits = 0)), and the other missing arguments are inserted after the last argument of the call. Thus, the line breaks, the indentation, the comments and the strings of the submitted function are kept, including inside calls written on several lines, and nested calls (e.g., paste0(mean(1:2))) are all corrected. The comments and the original indentation require that the submitted function carries its source references (attribute srcref, i.e., options(keep.source = TRUE) when the function was created, the default in interactive sessions). Otherwise (e.g., a function of an installed package, or a function created in an Rscript session), the printed code is the deparsed code, without comments and with a standard indentation, but its line breaks are kept.
Corrections not applied: the call is left unchanged in the modified function and its proposal is listed in the "CORRECTIONS NOT APPLIED" message, with one of the following reasons: 1) the $MISSING_ARGS or $DEF_ARGS column of the call cannot be read; 2) the call cannot be located in the code of the function (e.g., call with an empty argument, like c(1, )); 3) the corrected call does not parse as R code; 4) an argument is written positionally and is also proposed with its name (e.g., sum(x, na.rm) proposed as sum(x, na.rm, na.rm = FALSE)), which would fail at call time; 5) the corrected call does not match the $STATUS proposal (compared whatever the order of the named arguments, the spaces, the line breaks and the comments). Of note, an empty argument value in a proposal (like envir = , see above) is never a reason: such a proposal is applied.
See more examples here.
Warnings:
The following R functions are also skipped (as indicated by "SKIPPED" in the DEF_ARGS column of the returned data frame): as.environment() (see the R bug report 18849 in the R bug tracker).
Some functions, like rownames(), have different arguments depending on whether something is assigned to it (e.g., rownames(x) <- "a") or not. The all_args_here() function always proposes the arguments defined in the help page without assignment, meaning that it cannot detect the assignment. A way to bypass this is to use the "exact" writing of the function. For instance, base::"rownames<-"(x, "a") instead of using base::rownames(x) <- "a", and use getAnywhere("rownames<-") to see the arguments of the function.
The function could not properly work if any comma is present in default argument values. Please, report here if it is the case.
Proposals in the STATUS column are only suggestions, as it is difficult to anticipate all the exceptions with arguments writing.
A message saying whether arguments are missing or everything seems fine, and a data frame indicating the missing arguments (see the column descriptions below).
If table_export argument is FALSE, the data frame is returned as a message. If TRUE, it is exported as df_name in out_path.
If fun_export argument is FALSE, the modified function (an R function object rebuilt from the corrected code, with its comments and indentation preserved if the submitted function carries its source references, see the details section) is returned, or NULL (invisible) if no modification has been applied. If TRUE, the modified function is exported as <FUNCTION_NAME>_safer.R in out_path and NULL is returned (invisible), or nothing is exported if no modification has been applied.
A warning and NULL (invisible) are returned if the submitted function is empty of function, or only made of comments, or only made of function, if, for, while, repeat, else.
Column descriptions:
$LINE_NB: the line number in the function code (starting at the "<- function" line, i.e., without counting the #' header lines).
$FUN_NAME: the function name.
$FUN_ARGS: the written arguments of FUN_NAME. "NOT_CONSIDERED" means that the function is between quotes or after $.
$FUN_POS: the position of the first character of the function name in the $LINE_NB line.
$DEF_ARGS: the defaults arguments of FUN_NAME. "NO_ARGS" means that the function has no arguments. "INTERNAL_FUNCTION" means that the function has been created inside the checked function. "SKIPPED" means that the function is not analyzed for the reason indicated in the "details" section.
$MISSING_ARG_NAMES: the missing argument names in FUN_ARGS.
$MISSING_ARGS: the missing arguments with their values in FUN_ARGS.
$STATUS: either "GOOD", meaning that all the arguments are already written, or a new proposal of arguments writing (arguments written with a partial name, like dig for digits, are written with their full name), or nothing.
An additional message "EVERYTHING SEEMS CLEAN" if the $STATUS column is only made of "" and "GOOD".
An additional message "ARGUMENTS WITH EMPTY VALUE" listing, for each line and function, the arguments proposed in the $STATUS column with an empty value (like envir = ), with their default value, i.e., the potential flaws of the code (see the details section). Arguments already written with an empty value in the code are accepted as is and not listed. Use "= ," or "= )" to seek each case in the code.
An additional message "CORRECTIONS NOT APPLIED" listing the proposals of the $STATUS column that have not been inserted into the modified function, each followed by a REASON: line explaining why. These calls are left unchanged in the modified function and must be corrected manually (see the details section).
Haiding Wang
Yushi Han
# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/all_args_here.html fun1 <- function(x){ base::paste0(x, "a") # collapse and recycle0 arguments not written } fun1_safer <- saferDev::all_args_here(x = fun1, safer_check = FALSE) # arguments added fun1_safer # corrected function # Write the table (res.tsv) and the corrected function (fun1_safer.R) in a new temporary folder: tempo_dir <- base::tempfile(pattern = "dir", tmpdir = base::tempdir(), fileext = "") base::dir.create(path = tempo_dir, showWarnings = TRUE, recursive = FALSE, mode = "0777") saferDev::all_args_here(x = fun1, table_export = TRUE, fun_export = TRUE, out_path = tempo_dir, safer_check = FALSE)# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/all_args_here.html fun1 <- function(x){ base::paste0(x, "a") # collapse and recycle0 arguments not written } fun1_safer <- saferDev::all_args_here(x = fun1, safer_check = FALSE) # arguments added fun1_safer # corrected function # Write the table (res.tsv) and the corrected function (fun1_safer.R) in a new temporary folder: tempo_dir <- base::tempfile(pattern = "dir", tmpdir = base::tempdir(), fileext = "") base::dir.create(path = tempo_dir, showWarnings = TRUE, recursive = FALSE, mode = "0777") saferDev::all_args_here(x = fun1, table_export = TRUE, fun_export = TRUE, out_path = tempo_dir, safer_check = FALSE)
Check expected values of an argument of functions: class, type, mode, length, restricted values panel, kind of numeric values (in addition to the distinction between 'integer' and 'double')? Proportion only? Inf values authorized? negative values authorized? Integers of type 'double'?
arg_check( data, class = NULL, typeof = NULL, mode = NULL, length = NULL, prop = FALSE, options = NULL, double_as_integer_allowed = FALSE, all_options_in_data = FALSE, na_contain = FALSE, neg_values = TRUE, inf_values = TRUE, print = FALSE, data_name = NULL, data_arg = TRUE, safer_check = TRUE, lib_path = NULL, error_text = "" )arg_check( data, class = NULL, typeof = NULL, mode = NULL, length = NULL, prop = FALSE, options = NULL, double_as_integer_allowed = FALSE, all_options_in_data = FALSE, na_contain = FALSE, neg_values = TRUE, inf_values = TRUE, print = FALSE, data_name = NULL, data_arg = TRUE, safer_check = TRUE, lib_path = NULL, error_text = "" )
data |
Object to test. |
class |
Character string or vector of character strings, tested exactly (same values, in the same order) against |
typeof |
Character string or vector of character strings, tested exactly (same values, in the same order) against |
mode |
Character string or vector of character strings, tested exactly (same values, in the same order) against |
length |
Single numeric value indicating the length of the object. Not considered if |
prop |
Single logical value. Are the numeric values between 0 and 1 (proportion)? If |
options |
Vector of character strings or integers indicating all the possible option values for the |
double_as_integer_allowed |
Single logical value. If |
all_options_in_data |
Single logical value. If |
na_contain |
Single logical value. Can the |
neg_values |
Single logical value. Are negative numeric values authorized? Warning: the default setting is |
inf_values |
Single logical value. Are infinite |
print |
Single logical value. Print the message if the |
data_name |
Single character string indicating the name of the object to test. If |
data_arg |
Single logical value. Is the tested object a function argument? If |
safer_check |
Single logical value. Perform the "safer" checks? If |
lib_path |
Vector of characters specifying the absolute pathways of the directories containing the required packages for the function, if not in the default directories. Useful when R packages are not installed in the default directories because of lack of admin rights. More precisely, |
error_text |
Single character string used to add information in error messages returned by the function, notably if the function is inside other functions, which is practical for debugging. Example: |
At least one of the class, type, mode, length, prop or options must be set by the user.
If options == NULL, then at least class or type or mode or length argument must be non-null.
If options is non-null, then class, type and mode must be NULL, and length can be NULL or specified.
If prop is TRUE, then class, type and mode must be NULL, and length can be NULL or specified. The values must be between -1 and 1 with neg_values = TRUE (default), and between 0 and 1 with neg_values = FALSE. Consequently, inf_values must stay TRUE (pointless anyway, since proportions are not infinite).
The function tests what is written in its arguments, even if what is written is incoherent. For instance with arg_check(data = factor(1), class = "factor", mode = "character"), the function tests that data is class "factor" and mode "character", even if no factor object can be class "factor" and mode "character" (factors are class "factor" and mode "numeric"). Thus, arg_check(data = factor(1), class = "factor", mode = "character") will return a problem.
If the tested object is NULL, then the function will always return a checking problem.
The class, typeof and mode arguments are compared to class(data), typeof(data) and mode(data) exactly: the written values must be identical, element per element, in the same order. Since typeof(data) and mode(data) return a single string, passing several values in the typeof or mode arguments always returns a problem. In addition, since class(data) of objects of class "matrix" is c("matrix", "array") since R 4.0.0, class = "matrix" alone always returns a problem for matrices (use class = c("matrix", "array")). Similarly, class = "factor" alone always returns a problem for ordered factors (use class = c("ordered", "factor")), and class = "vector" is no longer a shortcut for the classes "numeric", "integer", "character", "logical", "complex" or "expression": write the exact class instead, or nothing.
Example of code that overcomes the problem of R versions, with a matrix object:
tempo1 <- saferDev::arg_check(data = data, class = "matrix") # before R 4.0.0
tempo2 <- saferDev::arg_check(data = data, class = c("matrix", "array")) # since R 4.0.0
if(tempo1$problem == TRUE & tempo2$problem == TRUE){
print("data ARGUMENT MUST BE A MATRIX")
}
To test several possibilities for the tested object:
tempo1 <- saferDev::arg_check(data = data, mode = "character")
tempo2 <- saferDev::arg_check(data = data, class = "factor")
tempo3 <- saferDev::arg_check(data = data, class = "integer")
if(tempo1$problem == TRUE & tempo2$problem == TRUE & tempo3$problem == TRUE){
print("data ARGUMENT MUST BE A VECTOR OF (1) STRINGS, (2) FACTORS OR (3) INTEGERS")
}
Of note, the length of object of class "environment" is always 0.
A list containing:
problem: logical. Is there any problem detected?
text: message indicating the details of the problem, or the absence of problem.
object_name: value of the data_name argument (i.e., name of the checked object if provided, NULL otherwise).
Haiding Wang
Yushi Han
# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/arg_check.html test <- matrix(1:3) ## Not run: # Example that returns an error (since R 4.0.0, class(matrix(1:3)) is c("matrix", "array")) saferDev::arg_check(data = test, print = TRUE, class = "vector", mode = "numeric") # Example that returns an error as well, because class = "matrix" must match class(data) exactly saferDev::arg_check(data = test, print = TRUE, class = "matrix", mode = "numeric") ## End(Not run) saferDev::arg_check(data = test, print = TRUE, class = c("matrix", "array"), mode = "numeric") saferDev::arg_check( data = test, print = TRUE, class = c("matrix", "array"), mode = "numeric", error_text = " using saferDev::arg_check()" )# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/arg_check.html test <- matrix(1:3) ## Not run: # Example that returns an error (since R 4.0.0, class(matrix(1:3)) is c("matrix", "array")) saferDev::arg_check(data = test, print = TRUE, class = "vector", mode = "numeric") # Example that returns an error as well, because class = "matrix" must match class(data) exactly saferDev::arg_check(data = test, print = TRUE, class = "matrix", mode = "numeric") ## End(Not run) saferDev::arg_check(data = test, print = TRUE, class = c("matrix", "array"), mode = "numeric") saferDev::arg_check( data = test, print = TRUE, class = c("matrix", "array"), mode = "numeric", error_text = " using saferDev::arg_check()" )
Verify that all the functions used inside a function are preceded by a package attribution. For instance: base::mean() and not mean(), or saferDev:::.base_op_check() and not .base_op_check(). Warning: does not check that the package is the good one. Use all_args_here() for that.
colons_check( x, export = FALSE, out_path = ".", safer_check = TRUE, lib_path = NULL, error_text = "" )colons_check( x, export = FALSE, out_path = ".", safer_check = TRUE, lib_path = NULL, error_text = "" )
x |
a function name, written without quotes and brackets. |
export |
Single logical value. Export the modified code into a |
out_path |
Single character string indicating the pathway of the folder where to export the modified code. |
safer_check |
Single logical value. Perform the "safer" checks? If |
lib_path |
Vector of characters specifying the absolute pathways of the directories containing the required packages for the function, if not in the default directories. Useful when R packages are not installed in the default directories because of lack of admin rights. More precisely, |
error_text |
Single character string used to add information in error messages returned by the function, notably if the function is inside other functions, which is practical for debugging. Example: |
The result is used to modify the code inside the tested function like this: <PACKAGE>::<FUNCTION> (or <PACKAGE>:::<FUNCTION> for function names starting by a dot).
Since saferDev v3.0.0, the modified function is returned: the package attributions of the basic R functions, as indicated in the "INSIDE test(), SOME :: OR ::: ARE MISSING BEFORE BASIC FUNCTIONS" section of the returned message, are added to the submitted function code, and the modified function is returned using return(). The package attributions of the other functions, as indicated in the "INSIDE test(), SOME :: OR ::: ARE MISSING BEFORE OTHER FUNCTIONS" section of the returned message, are NOT added automatically, because the correct package cannot be determined reliably: a base::warning() is issued instead (in addition to the printed message). NULL is returned if no basic function required a package attribution.
The modification is only performed on the code lines indicated in the "INSIDE test(), SOME :: OR ::: ARE MISSING BEFORE BASIC FUNCTIONS" section of the message, which is the same analysis as the one performed by colons_check() itself: functions written between quotes or preceded by $ are not modified, and internal functions (created inside the tested function) are not modified either. The analysis is performed on the code with comments removed and multiple spaces collapsed, but the package attributions are added to the code of the submitted function as printed: the returned (and exported) function therefore keeps the comments and the indentation of the submitted function. Warning: comments are only present when the submitted function carries its source references (attribute srcref, e.g., a function defined in the console or in a file loaded with keep.source = TRUE). Otherwise, as for compiled package functions, the code printed by R (without comments and reindented) is used. The modified function is returned with the environment of the submitted function, so that the functions used inside it still resolve the same way. If the modified code does not parse (a rare situation, e.g., a function name split across lines in a way the analyzer could not handle correctly), this is an internal bug: an internal error is returned when export = TRUE, and the submitted function is returned unchanged, with no error, when export = FALSE.
The name of the exported file (when export = TRUE) is the name of the submitted function, as written by the user, with _safer added at the end, e.g., test_safer.R for colons_check(x = test). If the submitted function was written as package::function, like colons_check(x = saferDev::test), the name is test_safer.R (the package name is not part of the file name). The exported file contains an assignment of the modified function: test_safer <- function(...) ..., so that base::source()ing it recreates the modified function. Warning: the tested function must be designated by a function name (like test or package::test) when export = TRUE: an inline definition (like function(x){x}) or a component writing (like FUN_LIST$fun) returns an error, because the name of the exported file could not be determined reliably.
Pre-check: the submitted function is first checked by lambda_check(): if some lambda functions are written with the \(x) shorthand, an error is returned asking to run saferDev::lambda_check() first, because the \ writings are not managed by the text-based detection of colons_check(). As in lambda_check(), the \(x) writing is only detectable in a function carrying its source references (attribute srcref): a compiled or loaded package function prints function(x), so that the pre-check silently passes in that case.
More precisely, colons_check() verifies that all the strings before an opening bracket ( are preceded by ::.
::: are not checked per se, because incorrect writing, like saferDev::.colons_check_message() returns an error when executing the tested function, and because base:::sum() is as ok as base::sum(). In the same manner, more than three colons are not checked because it returns an error when executing the tested function.
Warning: colons_check() cannot check function names written without brackets, like in the FUN argument of some functions, e.g., sapply(1:3, FUN = as.character).
The Perl regex used to detect a function name is: "([a-zA-Z]|\.[a-zA-Z._])[a-zA-Z0-9._]*\s*\(".
Currently, colons_check() cannot detect functions written between quotes, like "+"() or "rownames<-"(x, "a").
Function names preceded by $ are not considered.
The following R functions are skipped: "function", "if", "for", "while", "repeat" and "else".
Most of the time, colons_check() does not check inside comments, but some unexpected writing could dupe colons_check(). Please, report here if it is the case.
The returned line numbers are indicative, depending on which source is checked. For instance, saferDev::report (compiled) has not the same line numbers as its source file (source file). Notably, compiled functions do not have comments anymore, compared to the same source function sourced into the working environment. In addition, the counting starts at the "<- function" line, i.e., without counting the #' header lines potentially present in source files.
Of note, during package creation, the devtools::check() command tells which functions where wrongly attributed to package. Example:
checking dependencies in R code ... WARNING
'::' or ':::' import not declared from: 'sbase'
Missing or unexported objects:
'base::dev.off' 'base::graphics.off' 'base::hcl' 'base::par' 'base::read.table' 'saferGG::report'
A table-like text message indicating the missing :: or ::: or a message saying that everything seems fine.
The message is always printed (see the Details section).
A warning and NULL are returned if the submitted function is empty of function, or only made of comments, or only made of function, if, for, while, repeat, else.
In addition, if the export argument is FALSE (default), the function returns NULL if no basic function required a package attribution, or the modified function if basic functions were modified (see the Details section).
If the export argument is TRUE, the modified code is exported in the .R file specified by the out_path argument, instead of being returned, and a message indicating where the modified code has been exported is printed.
Table-like: column 1, the line number in the function code (starting at the "<- function" line, i.e., without counting the #' header lines); column 2, the function name; column 3, the code preceding the function name.
With missing :: or :::, the message also indicates if internal functions are created inside the checked function code, since these functions cannot have :: or :::.
Haiding Wang
Yushi Han
# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/colons_check.html fun1 <- function(x){ y <- mean(x) # mean() written without base:: base::return(y) } fun1_safer <- saferDev::colons_check(fun1) # base:: added before mean() fun1_safer # corrected function # Write the corrected function in the fun1_safer.R file of a new temporary folder: tempo_dir <- base::tempfile(pattern = "dir", tmpdir = base::tempdir(), fileext = "") base::dir.create(path = tempo_dir, showWarnings = TRUE, recursive = FALSE, mode = "0777") saferDev::colons_check(x = fun1, export = TRUE, out_path = tempo_dir)# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/colons_check.html fun1 <- function(x){ y <- mean(x) # mean() written without base:: base::return(y) } fun1_safer <- saferDev::colons_check(fun1) # base:: added before mean() fun1_safer # corrected function # Write the corrected function in the fun1_safer.R file of a new temporary folder: tempo_dir <- base::tempfile(pattern = "dir", tmpdir = base::tempdir(), fileext = "") base::dir.create(path = tempo_dir, showWarnings = TRUE, recursive = FALSE, mode = "0777") saferDev::colons_check(x = fun1, export = TRUE, out_path = tempo_dir)
Verify that all the bodies of the if, else, for, while, repeat and function are delimited by curly brackets { }. For instance: if(x > 1){ x } and not if(x > 1) x. Return table message of the line with problems and a corrected version of the submitted function.
curly_bracket_check( x, export = FALSE, out_path = ".", safer_check = TRUE, lib_path = NULL, error_text = "" )curly_bracket_check( x, export = FALSE, out_path = ".", safer_check = TRUE, lib_path = NULL, error_text = "" )
x |
A function name, written without quotes and brackets. |
export |
Single logical value. Write the corrected function into a file instead of returning it? If |
out_path |
Single character string indicating the path of the folder where to write the file when |
safer_check |
Single logical value. Perform the "safer" checks? If |
lib_path |
Vector of characters specifying the absolute pathways of the directories containing the required packages for the function, if not in the default directories. Useful when R packages are not installed in the default directories because of lack of admin rights. More precisely, |
error_text |
Single character string used to add information in error messages returned by the function, notably if the function is inside other functions, which is practical for debugging. Example: |
Comments are ignored. In else if chains, the else is not reported: the chained if is checked on its own.
Bodies reduced to .Primitive(...), like in base::`+`, are skipped, because they cannot be enclosed in curly brackets. Bodies made of UseMethod(...), like in base::mean, are reported.
Correction: a { is added before each reported body and a } after it, separated from the body by a space. The rest of the code is unchanged. The corrected code is checked by parsing it. Lines can become longer than 80 characters. The returned function keeps the environment of the submitted function.
The comments and the indentation of the submitted function are kept, in the returned function and in the exported file. Warning: comments are only available when the submitted function carries its source references (attribute srcref). Otherwise (e.g., compiled package functions, or functions created with options(keep.source = FALSE), default of Rscript), the code is the deparsed one: no comments and standard indentation.
Constructs written with backquotes, like `if`(x > 1, x, 0), may not be checked: use the standard writing.
Line numbers are indicative. They start at the <- function line (#' header lines not counted). Compiled functions, like saferDev::colons_check, have no comments: their line numbers differ from their source file.
A table-like message is always printed, with one row per missing { } (column 1: line number; column 2: construct; column 3: code before the body), or a message saying that everything seems fine.
If export = FALSE: the corrected function, or NULL if nothing has to be corrected.
If export = TRUE: NULL, returned invisibly. The corrected function is written in a file (see the export argument).
Haiding Wang
Yushi Han
# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/curly_bracket_check.html fun1 <- function(x){ if(x > 1){ base::return(x) } } saferDev::curly_bracket_check(fun1) # everything fine: NULL returned fun2 <- function(x){ if(x > 1) base::return(x) } fun2_safer <- saferDev::curly_bracket_check(fun2) # missing { } reported fun2_safer # corrected function # Write the corrected function in the fun2_safer.R file of a new temporary folder: tempo_dir <- base::tempfile(pattern = "dir", tmpdir = base::tempdir(), fileext = "") base::dir.create(path = tempo_dir, showWarnings = TRUE, recursive = FALSE, mode = "0777") saferDev::curly_bracket_check(x = fun2, export = TRUE, out_path = tempo_dir)# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/curly_bracket_check.html fun1 <- function(x){ if(x > 1){ base::return(x) } } saferDev::curly_bracket_check(fun1) # everything fine: NULL returned fun2 <- function(x){ if(x > 1) base::return(x) } fun2_safer <- saferDev::curly_bracket_check(fun2) # missing { } reported fun2_safer # corrected function # Write the corrected function in the fun2_safer.R file of a new temporary folder: tempo_dir <- base::tempfile(pattern = "dir", tmpdir = base::tempdir(), fileext = "") base::dir.create(path = tempo_dir, showWarnings = TRUE, recursive = FALSE, mode = "0777") saferDev::curly_bracket_check(x = fun2, export = TRUE, out_path = tempo_dir)
Verify that object names in the environment defined by the pos parameter are identical or not to object names in the above environments (following R Scope). This can be used to verify that names used for objects inside a function or in the working environment do not override names of objects already present in the above R environments, following the R scope.
env_check( pos = 1, name = NULL, safer_check = TRUE, lib_path = NULL, error_text = "" )env_check( pos = 1, name = NULL, safer_check = TRUE, lib_path = NULL, error_text = "" )
pos |
Single non null positive integer indicating the position of the environment checked (argument |
name |
Single character string of the name of the checked environment (used in the output string only, where the name of the ckecked env is replaced by the name value). Can be used when |
safer_check |
Single logical value. Perform the "safer" checks? If |
lib_path |
Vector of characters specifying the absolute pathways of the directories containing the required packages for the function, if not in the default directories. Useful when R packages are not installed in the default directories because of lack of admin rights. More precisely, |
error_text |
Single character string used to add information in error messages returned by the function, notably if the function is inside other functions, which is practical for debugging. Example: |
A character string indicating the object names of the tested environment that match object names in the above environments, following the R scope, or NULL if no match.
Haiding Wang
Yushi Han
# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/env_check.html # Inside a function: the names t.test and mean, created inside fun1(), # also exist in the stats and base environments located above: fun1 <- function(){ t.test <- 0 mean <- 5 saferDev::env_check(pos = 1, name = "fun1") } a <- fun1() base::cat(a) # NULL returned when no name is also present in the environments above: fun2 <- function(){ tempo_name_used_nowhere_else <- 0 saferDev::env_check(pos = 1, name = "fun2") } fun2() # pos = 2 checks the environment one step above the function (here, the calling environment). # See the env_check() article of the website for more examples.# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/env_check.html # Inside a function: the names t.test and mean, created inside fun1(), # also exist in the stats and base environments located above: fun1 <- function(){ t.test <- 0 mean <- 5 saferDev::env_check(pos = 1, name = "fun1") } a <- fun1() base::cat(a) # NULL returned when no name is also present in the environments above: fun2 <- function(){ tempo_name_used_nowhere_else <- 0 saferDev::env_check(pos = 1, name = "fun2") } fun2() # pos = 2 checks the environment one step above the function (here, the calling environment). # See the env_check() article of the website for more examples.
for Loop Number CheckCheck the sequence expression of every for loop of a function. If the number of iterations is not fixed by the writing itself (e.g., 1:length(x)), a checking line is added before the loop, issuing a warning at execution if the sequence is empty, contains NA or infinite values, has a single value, or is decreasing. Return a table-like message of the problematic lines and a corrected version of the submitted function.
for_loop_nb_check( x, export = FALSE, out_path = ".", safer_check = TRUE, lib_path = NULL, error_text = "" )for_loop_nb_check( x, export = FALSE, out_path = ".", safer_check = TRUE, lib_path = NULL, error_text = "" )
x |
A function name, written without quotes and brackets. |
export |
Single logical value. Write the corrected function into a file instead of returning it? If |
out_path |
Single character string indicating the path of the folder where to write the file when |
safer_check |
Single logical value. Perform the "safer" checks? If |
lib_path |
Vector of characters specifying the absolute pathways of the directories containing the required packages for the function, if not in the default directories. Useful when R packages are not installed in the default directories because of lack of admin rights. More precisely, |
error_text |
Single character string used to add information in error messages returned by the function, notably if the function is inside other functions, which is practical for debugging. Example: |
Not reported: hard numbers, i.e., an integer-valued number, possibly negative, or two such numbers joined by : (e.g., 3, 1:3, 1e3, 1L:3L, 0:0, -3:-1, 1:0), and a single seq_len(...) or seq_along(...) call. These writings fully fix the iterations, and their control is left to the writer.
Reported: everything else, e.g., for(i in x), 1:n, 1:length(x), 1:nrow(df), 1:(n - 1), seq_along(x) - 1, 1:2.5, 1:NA or 1:Inf. Comments and strings are ignored.
Why: with x <- character(), for(i in 1:length(x)) runs twice, because 1:length(x) is c(1, 0). Use base::seq_along(along.with = x) instead.
Correction: each reported loop becomes tempo_for_seqN <- <SEQUENCE> ; <CHECKS> ; for(i in tempo_for_seqN)<BODY>, N being the rank of the report. The checks warn if the sequence is empty, contains NA or infinite values, has a single value, has only negative values, or is decreasing (these last two checks only for numeric sequences, i.e., not for factors, dates, characters or complex numbers). The loop thus runs on the checked sequence. Errors of the sequence itself (like 1:NA) are not caught. If the loop is not a standalone statement (e.g., unbraced body of an if, or argument value), the whole is enclosed in curly brackets. The rest of the code is unchanged. The comments and the indentation of the submitted function are kept, in the returned function and in the exported file. Warning: comments are only available when the submitted function carries its source references (attribute srcref). Otherwise (e.g., compiled package functions, or functions created with options(keep.source = FALSE), default of Rscript), the code is the deparsed one: no comments and standard indentation. The returned function is evaluated in a new environment whose parent is the environment of the submitted function: check it before replacing the original function.
Warning: a reported loop is left unchanged, and listed in the message, when:
the name tempo_for_seqN is already used in the function code (including comments and strings);
the loop is inside the sequence expression of another reported loop;
the loop must be enclosed in curly brackets and contains comments;
the loop must be enclosed in curly brackets and ends with another loop that must also be (e.g., for(a in 1:n) for(b in 1:length(x)){...}).
Warning: only the writing is analyzed. In n <- length(x) ; for(i in 1:n), 1:n is reported as a variable, whatever n.
Line numbers are indicative. They start at the <- function line (#' header lines not counted). Compiled functions, like saferDev::colons_check, have no comments: their line numbers differ from their source file.
A table-like message is always printed, with one row per reported loop (column 1: line number; column 2: construct; column 3: code before the body), or a message saying that everything seems fine.
If export = FALSE: the corrected function, or NULL if nothing has been corrected.
If export = TRUE: NULL, returned invisibly. The corrected function is written in a file (see the export argument).
Haiding Wang
Yushi Han
# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/for_loop_nb_check.html fun1 <- function(x){ for(i in base::seq_along(along.with = x)){ base::print(x = i) } } saferDev::for_loop_nb_check(fun1) # everything fine: NULL returned fun2 <- function(x){ for(i in 1:length(x)){ base::print(x = i) } } fun2_safer <- saferDev::for_loop_nb_check(fun2) # 1:length(x) reported fun2_safer # corrected function fun2_safer(x = base::character()) # warning: 1:length(x) is c(1, 0), a decreasing sequence # Write the corrected function in the fun2_safer.R file of a new temporary folder: tempo_dir <- base::tempfile(pattern = "dir", tmpdir = base::tempdir(), fileext = "") base::dir.create(path = tempo_dir, showWarnings = TRUE, recursive = FALSE, mode = "0777") saferDev::for_loop_nb_check(x = fun2, export = TRUE, out_path = tempo_dir)# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/for_loop_nb_check.html fun1 <- function(x){ for(i in base::seq_along(along.with = x)){ base::print(x = i) } } saferDev::for_loop_nb_check(fun1) # everything fine: NULL returned fun2 <- function(x){ for(i in 1:length(x)){ base::print(x = i) } } fun2_safer <- saferDev::for_loop_nb_check(fun2) # 1:length(x) reported fun2_safer # corrected function fun2_safer(x = base::character()) # warning: 1:length(x) is c(1, 0), a decreasing sequence # Write the corrected function in the fun2_safer.R file of a new temporary folder: tempo_dir <- base::tempfile(pattern = "dir", tmpdir = base::tempdir(), fileext = "") base::dir.create(path = tempo_dir, showWarnings = TRUE, recursive = FALSE, mode = "0777") saferDev::for_loop_nb_check(x = fun2, export = TRUE, out_path = tempo_dir)
Evaluate an instruction written between "" and return the first of the error message, or the last of the warning or standard (non error non warning) messages if ever exist.
Using argument print_no = FALSE, return NULL if nothing reported, which is convenient in some cases.
get_message( data, kind = "error", header = TRUE, print_no = FALSE, text = NULL, env = NULL, safer_check = TRUE, lib_path = NULL, error_text = "" )get_message( data, kind = "error", header = TRUE, print_no = FALSE, text = NULL, env = NULL, safer_check = TRUE, lib_path = NULL, error_text = "" )
data |
Single character string of an instruction to evaluate |
kind |
Single character string. Either |
header |
Single logical value. Add a header in the returned message? |
print_no |
Single logical value. Print a message saying that nothing ( |
text |
Single character string added to the header of the output message, even if nothing reported ( |
env |
An object corresponding to an existing environment. Then the |
safer_check |
Single logical value. Perform the "safer" checks? If |
lib_path |
Vector of characters specifying the absolute pathways of the directories containing the required packages for the function, if not in the default directories. Useful when R packages are not installed in the default directories because of lack of admin rights. More precisely, |
error_text |
Single character string used to add information in error messages returned by the function, notably if the function is inside other functions, which is practical for debugging. Example: |
Warnings:
Only the first error or last warning/standard message is returned.
Always use the env argument when get_message() is used inside functions.
The function does not prevent printing, for instance if print() or show() is used inside the instruction tested. To prevent that, use tempo <- utils::capture.output(error <- get_message(data = "arg_check(data = 'a', class = mean, neg_values = FALSE, print = TRUE)")). The return of get_message() is assigned into error and the printed messages are captured by utils::capture.output() and assigned into tempo. See the examples.
The env argument is used as value for the envir argument of the base::eval() function.
The function returns the error, warning, or standard message, as a single character string, depending on what has been selected using the kind argument and if such message exists.
NULL if no selected message returned and the print_no argument is FALSE.
The following message if no selected message returned and the print_no argument is TRUE: "NO (ERROR|WARNING|STANDARD) MESSAGE REPORTED".
The following message if 1) the kind argument is "warning" or "message" while an error message is returned and 2) the print_no argument is TRUE: "NO POTENTIAL (WARNING|STANDARD) MESSAGE BECAUSE OF ERROR MESSAGE REPORTED".
Haiding Wang
Yushi Han
try.
# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/get_message.html # Report error message by default. Here no error to report: saferDev::get_message(data = "wilcox.test(c(1,1,3), c(1, 2, 4), paired = TRUE)") # Warning message to report: saferDev::get_message(data = "wilcox.test(c(1,1,3), c(1, 2, 4), paired = TRUE)", kind = "warning") # No standard message to report: saferDev::get_message(data = "wilcox.test(c(1,1,3), c(1, 2, 4), paired = TRUE)", kind = "message", print_no = TRUE, text = "IN A") # Standard message cannot be caught because wilcox.test() returns an error message: saferDev::get_message(data = "wilcox.test()", kind = "message", print_no = TRUE, text = "IN A") # Text added in warning message to report: saferDev::get_message(data = "wilcox.test()", kind = "error", print_no = TRUE, text = "IN A") # No error message to report with text added: saferDev::get_message(data = "sum(1)", kind = "error", print_no = TRUE, text = "IN A") # No error message to report with text added: saferDev::get_message(data = "message('ahah')", kind = "error", print_no = TRUE, text = "IN A") # Standard message reported with text added: saferDev::get_message(data = "message('ahah')", kind = "message", print_no = TRUE, text = "IN A") # Messages from ggplot2 functions caugth: saferDev::get_message(data = "ggplot2::ggplot(data = data.frame(X = 1:10, stringsAsFactors = TRUE), mapping = ggplot2::aes(x = X)) + ggplot2::geom_histogram()", kind = "message", print_no = TRUE, text = "IN INSTRUCTION 1")# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/get_message.html # Report error message by default. Here no error to report: saferDev::get_message(data = "wilcox.test(c(1,1,3), c(1, 2, 4), paired = TRUE)") # Warning message to report: saferDev::get_message(data = "wilcox.test(c(1,1,3), c(1, 2, 4), paired = TRUE)", kind = "warning") # No standard message to report: saferDev::get_message(data = "wilcox.test(c(1,1,3), c(1, 2, 4), paired = TRUE)", kind = "message", print_no = TRUE, text = "IN A") # Standard message cannot be caught because wilcox.test() returns an error message: saferDev::get_message(data = "wilcox.test()", kind = "message", print_no = TRUE, text = "IN A") # Text added in warning message to report: saferDev::get_message(data = "wilcox.test()", kind = "error", print_no = TRUE, text = "IN A") # No error message to report with text added: saferDev::get_message(data = "sum(1)", kind = "error", print_no = TRUE, text = "IN A") # No error message to report with text added: saferDev::get_message(data = "message('ahah')", kind = "error", print_no = TRUE, text = "IN A") # Standard message reported with text added: saferDev::get_message(data = "message('ahah')", kind = "message", print_no = TRUE, text = "IN A") # Messages from ggplot2 functions caugth: saferDev::get_message(data = "ggplot2::ggplot(data = data.frame(X = 1:10, stringsAsFactors = TRUE), mapping = ggplot2::aes(x = X)) + ggplot2::geom_histogram()", kind = "message", print_no = TRUE, text = "IN INSTRUCTION 1")
Check if required functions are present in installed packages. This controls the potential modifications of package contents.
is_function_here(fun, safer_check = TRUE, lib_path = NULL, error_text = "")is_function_here(fun, safer_check = TRUE, lib_path = NULL, error_text = "")
fun |
Character vector of the names of the required functions, preceded by the name of the package they belong to and a double or triple colon. Example: |
safer_check |
Single logical value. Perform the "safer" checks? If |
lib_path |
Vector of characters specifying the absolute pathways of the directories containing the required packages for the function, if not in the default directories. Useful when R packages are not installed in the default directories because of lack of admin rights. More precisely, |
error_text |
Single character string used to add information in error messages returned by the function, notably if the function is inside other functions, which is practical for debugging. Example: |
An error message if at least one of the checked packages is missing in lib_path, or if at least one of the checked functions is missing in the required package, nothing otherwise.
Haiding Wang
Yushi Han
exists and findFunction
# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/is_function_here.html ## Not run: # Example that returns an error saferDev::is_function_here(fun = "ggplot2::notgood", error_text = " INSIDE P1::F1") saferDev::is_function_here(fun = c("ggplot2::geom_point", "grid::gpar")) saferDev::is_function_here(fun = "c") ## End(Not run) saferDev::is_function_here(fun = "base::c", error_text = " INSIDE P1::F1")# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/is_function_here.html ## Not run: # Example that returns an error saferDev::is_function_here(fun = "ggplot2::notgood", error_text = " INSIDE P1::F1") saferDev::is_function_here(fun = c("ggplot2::geom_point", "grid::gpar")) saferDev::is_function_here(fun = "c") ## End(Not run) saferDev::is_function_here(fun = "base::c", error_text = " INSIDE P1::F1")
Check if required packages are installed locally.
is_package_here( req_package, safer_check = TRUE, lib_path = NULL, error_text = "" )is_package_here( req_package, safer_check = TRUE, lib_path = NULL, error_text = "" )
req_package |
Character vector of package names to check. |
safer_check |
Single logical value. Perform the "safer" checks? If |
lib_path |
Vector of characters specifying the absolute pathways of the directories containing the required packages for the function, if not in the default directories. Useful when R packages are not installed in the default directories because of lack of admin rights. More precisely, |
error_text |
Single character string used to add information in error messages returned by the function, notably if the function is inside other functions, which is practical for debugging. Example: |
An error message if at least one of the checked packages is missing in lib_path, nothing otherwise.
Haiding Wang
Yushi Han
# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/is_package_here.html ## Not run: # Example that returns an error saferDev::is_package_here(req_package = "nopackage", error_text = " INSIDE P1::F1") # Example that returns an error saferDev::is_package_here(req_package = "ggplot2", lib_path = "NOTGOOD") ## End(Not run) saferDev::is_package_here(req_package = "ggplot2")# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/is_package_here.html ## Not run: # Example that returns an error saferDev::is_package_here(req_package = "nopackage", error_text = " INSIDE P1::F1") # Example that returns an error saferDev::is_package_here(req_package = "ggplot2", lib_path = "NOTGOOD") ## End(Not run) saferDev::is_package_here(req_package = "ggplot2")
Verify if lambda functions are written with the shorthand \(x) (like \(x) x * 2). Return table message of the line with problems and a corrected version of the submitted function.
lambda_check( x, export = FALSE, out_path = ".", safer_check = TRUE, lib_path = NULL, error_text = "" )lambda_check( x, export = FALSE, out_path = ".", safer_check = TRUE, lib_path = NULL, error_text = "" )
x |
A function name, written without quotes and brackets. |
export |
Single logical value. Write the corrected function into a file instead of returning it? If |
out_path |
Single character string indicating the path of the folder where to write the file when |
safer_check |
Single logical value. Perform the "safer" checks? If |
lib_path |
Vector of characters specifying the absolute pathways of the directories containing the required packages for the function, if not in the default directories. Useful when R packages are not installed in the default directories because of lack of admin rights. More precisely, |
error_text |
Single character string used to add information in error messages returned by the function, notably if the function is inside other functions, which is practical for debugging. Example: |
Warning: the \(x) writing of a lambda function is only kept in the code of the submitted function when the function carries its source references (attribute srcref, e.g., after a base::parse(keep.source = TRUE) or a function defined in a file loaded with keep.source = TRUE). Otherwise, R prints the lambda as function(x), so that nothing can be reported: run lambda_check() on the source code itself, not on a compiled or on a loaded package function. The base::getOption("keep.source") default is TRUE in interactive sessions and FALSE in Rscript.
Reported: lambdas written \(x), like \(x) x * 2, \(x){ x } or \() base::mean(1:3), wherever they are written in the submitted function (right side of an assignment, argument value of a function call, etc.). In the message table, the SYMBOL column shows the lambda writing with its formals only when they are written on a single line: for a lambda whose formals span several lines, the fallback writing \() is shown (the whole writing is still replaced correctly in the code, because only the \ token is replaced).
Not reported: the function(x) standard writing, and lambda writings of the past (e.g., function(x) x) reported by other functions. Strings, comments and backquoted names (like a backquoted backslash, the deprecated function() writing of R versions before 4.1.0) are ignored: a backslash followed by ( inside a string, a raw string, a comment or a backquoted name is never reported nor modified, whatever the rest of the written text (the detection is token-based, not text-based). A lambda body itself is not modified: run curly_bracket_check() and true_false_check() afterwards to check the corrected function further.
Correction: each reported \ lambda writing is replaced by function (e.g., \(x) x * 2 becomes function(x) x * 2). The rest of the code is unchanged. The corrected code is checked by parsing it. Lines can become longer than 80 characters. The returned function keeps the environment of the submitted function.
The comments and the indentation of the submitted function are kept, in the returned function and in the exported file, which contain the recovered code verbatim, only modified by the \ -> function replacements. Warning: comments are only available when the submitted function carries its source references (attribute srcref). Otherwise (e.g., compiled package functions, or functions created with options(keep.source = FALSE), default of Rscript), the code is the deparsed one: no comments and standard indentation.
Line numbers are indicative. They start at the <- function line (#' header lines not counted). Compiled functions, like saferDev::colons_check, have no comments: their line numbers differ from their source file.
A table-like message is always printed, with one row per reported lambda (column 1: line number; column 2: symbol; column 3: code before the lambda), or a message saying that everything seems fine.
If export = FALSE: the corrected function, or NULL if nothing has to be corrected.
If export = TRUE: NULL, returned invisibly. The corrected function is written in a file (see the export argument).
# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/lambda_check.html # The \(x) writing is only kept in a function carrying its source references, # hence the parse(keep.source = TRUE) writing used here: code1 <- "function(x){ base::sapply(X = 1:3, FUN = function(y){ y * 2 }) }" fun1 <- eval(parse(text = code1, keep.source = TRUE)) saferDev::lambda_check(fun1) # everything fine: NULL returned code2 <- "function(x){ base::sapply(X = 1:3, FUN = \\(y){ y * 2 }) }" fun2 <- eval(parse(text = code2, keep.source = TRUE)) fun2_safer <- saferDev::lambda_check(fun2) # lambda writing reported fun2_safer # corrected function # A backslash followed by ( in a string, a raw string, a comment or a backquoted name # is not a lambda writing (token-based detection): not reported, not modified. code3 <- paste0( "function(x){ y <- \"the text \\\\(x) is inside a string\" ; ", "z <- r\"(raw \\(x))\" ; # a \\(x) in a comment\n base::`\\\\`(x) ; x }" ) fun3 <- eval(parse(text = code3, keep.source = TRUE)) saferDev::lambda_check(fun3) # everything fine: NULL returned # Write the corrected function in the fun2_safer.R file of a new temporary folder: tempo_dir <- base::tempfile(pattern = "dir", tmpdir = base::tempdir(), fileext = "") base::dir.create(path = tempo_dir, showWarnings = TRUE, recursive = FALSE, mode = "0777") saferDev::lambda_check(x = fun2, export = TRUE, out_path = tempo_dir)# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/lambda_check.html # The \(x) writing is only kept in a function carrying its source references, # hence the parse(keep.source = TRUE) writing used here: code1 <- "function(x){ base::sapply(X = 1:3, FUN = function(y){ y * 2 }) }" fun1 <- eval(parse(text = code1, keep.source = TRUE)) saferDev::lambda_check(fun1) # everything fine: NULL returned code2 <- "function(x){ base::sapply(X = 1:3, FUN = \\(y){ y * 2 }) }" fun2 <- eval(parse(text = code2, keep.source = TRUE)) fun2_safer <- saferDev::lambda_check(fun2) # lambda writing reported fun2_safer # corrected function # A backslash followed by ( in a string, a raw string, a comment or a backquoted name # is not a lambda writing (token-based detection): not reported, not modified. code3 <- paste0( "function(x){ y <- \"the text \\\\(x) is inside a string\" ; ", "z <- r\"(raw \\(x))\" ; # a \\(x) in a comment\n base::`\\\\`(x) ; x }" ) fun3 <- eval(parse(text = code3, keep.source = TRUE)) saferDev::lambda_check(fun3) # everything fine: NULL returned # Write the corrected function in the fun2_safer.R file of a new temporary folder: tempo_dir <- base::tempfile(pattern = "dir", tmpdir = base::tempdir(), fileext = "") base::dir.create(path = tempo_dir, showWarnings = TRUE, recursive = FALSE, mode = "0777") saferDev::lambda_check(x = fun2, export = TRUE, out_path = tempo_dir)
Print a character string or a data object into a same output file. Convenient for log file generation.
report( data, output = "log.txt", path, append = FALSE, rownames_kept = FALSE, vector_cat = FALSE, noquote = TRUE, sep = 2, safer_check = TRUE, lib_path = NULL, error_text = "" )report( data, output = "log.txt", path, append = FALSE, rownames_kept = FALSE, vector_cat = FALSE, noquote = TRUE, sep = 2, safer_check = TRUE, lib_path = NULL, error_text = "" )
data |
Object to print in the output file. If |
output |
Single character string. Name of the output file. |
path |
Single character string indicating the path where to write the output file. |
append |
Single logical value. If the output file already exists and |
rownames_kept |
Single logical value. Defines whether row names have to be removed or not in 2D objects. Warning: in 1D tables, names over the values are taken as row names, and are thus removed if |
vector_cat |
Single logical value. If |
noquote |
Single logical value. If |
sep |
Single non null and positive integer representing the number of empty lines added in the output file after the printed data. |
safer_check |
Single logical value. Perform the "safer" checks? If |
lib_path |
Vector of characters specifying the absolute pathways of the directories containing the required packages for the function, if not in the default directories. Useful when R packages are not installed in the default directories because of lack of admin rights. More precisely, |
error_text |
Single character string used to add information in error messages returned by the function, notably if the function is inside other functions, which is practical for debugging. Example: |
Nothing (implementation of an existing file) or a text file.
Haiding Wang
Yushi Han
# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/report.html # Write in the results.txt file of a new temporary folder: tempo_dir <- base::tempfile(pattern = "dir", tmpdir = base::tempdir(), fileext = "") base::dir.create(path = tempo_dir, showWarnings = TRUE, recursive = FALSE, mode = "0777") saferDev::report(data = "THE FOLLOWING VECTOR IS:\n", output = "results.txt", path = tempo_dir, append = TRUE, sep = 1) saferDev::report(data = 1:3, output = "results.txt", path = tempo_dir, append = TRUE, rownames_kept = FALSE, vector_cat = FALSE, noquote = FALSE, sep = 2) saferDev::report(data = "THE FOLLOWING DATA FRAME IS:\n", output = "results.txt", path = tempo_dir, append = TRUE, sep = 1) saferDev::report(data = base::data.frame(A = 1:3, B = letters[1:3]), output = "results.txt", path = tempo_dir, append = TRUE, rownames_kept = FALSE, vector_cat = FALSE, noquote = FALSE, sep = 1) base::cat(base::readLines(con = base::file.path(tempo_dir, "results.txt")), sep = "\n")# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/report.html # Write in the results.txt file of a new temporary folder: tempo_dir <- base::tempfile(pattern = "dir", tmpdir = base::tempdir(), fileext = "") base::dir.create(path = tempo_dir, showWarnings = TRUE, recursive = FALSE, mode = "0777") saferDev::report(data = "THE FOLLOWING VECTOR IS:\n", output = "results.txt", path = tempo_dir, append = TRUE, sep = 1) saferDev::report(data = 1:3, output = "results.txt", path = tempo_dir, append = TRUE, rownames_kept = FALSE, vector_cat = FALSE, noquote = FALSE, sep = 2) saferDev::report(data = "THE FOLLOWING DATA FRAME IS:\n", output = "results.txt", path = tempo_dir, append = TRUE, sep = 1) saferDev::report(data = base::data.frame(A = 1:3, B = letters[1:3]), output = "results.txt", path = tempo_dir, append = TRUE, rownames_kept = FALSE, vector_cat = FALSE, noquote = FALSE, sep = 1) base::cat(base::readLines(con = base::file.path(tempo_dir, "results.txt")), sep = "\n")
Verify that the superassignment operators <<- and ->> are not used in a function. Return an error with a table of the lines with problems if any, since these operators are not allowed in safer-r functions.
super_assign_check(x, safer_check = TRUE, lib_path = NULL, error_text = "")super_assign_check(x, safer_check = TRUE, lib_path = NULL, error_text = "")
x |
A function name, written without quotes and brackets. |
safer_check |
Single logical value. Perform the "safer" checks? If |
lib_path |
Vector of characters specifying the absolute pathways of the directories containing the required packages for the function, if not in the default directories. Useful when R packages are not installed in the default directories because of lack of admin rights. More precisely, |
error_text |
Single character string used to add information in error messages returned by the function, notably if the function is inside other functions, which is practical for debugging. Example: |
Why an error: the superassignment operators <<- and ->> assign in the first environment, among the parent environments of the evaluation environment, where the name already exists, and in the global environment if the name exists nowhere. What is modified thus depends on the environments where the function is defined and called, not on the code of the function itself: it is difficult to guess what the writer had in mind, and nothing can be corrected automatically. The code must be rewritten, for instance by returning the value, or by using base::assign() with an explicit envir argument.
Reported: every <<- and ->> operator written in the submitted function, including inside the functions defined in the submitted function (e.g., a FUN argument of base::lapply() or the handlers of base::tryCatch()).
Not reported: the <-, -> and = assignment operators, and the <<- or ->> writings inside strings, comments or backquoted names (the detection is token-based, not text-based).
Line numbers are indicative. They start at the <- function line (#' header lines not counted). Compiled functions, like saferDev::colons_check, have no comments: their line numbers differ from their source file. A ->> operator is printed <<- in the code of a function without source references (attribute srcref).
If no superassignment operator is detected: a message saying that everything seems fine, and NULL returned invisibly.
Otherwise: an error, with a table-like message made of one row per reported operator (column 1: line number; column 2: symbol; column 3: code of the line).
# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/super_assign_check.html fun1 <- function(x){ y <- x ; base::return(y) } saferDev::super_assign_check(fun1) # everything fine: NULL returned fun2 <- function(x){ y <<- x ; base::return(y) } try(saferDev::super_assign_check(fun2)) # error: <<- reported # A <<- written in a string or in a comment is not an operator: not reported. fun3 <- function(x){ y <- "the <<- writing is just a string" ; x } # a <<- in a comment saferDev::super_assign_check(fun3) # everything fine: NULL returned# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/super_assign_check.html fun1 <- function(x){ y <- x ; base::return(y) } saferDev::super_assign_check(fun1) # everything fine: NULL returned fun2 <- function(x){ y <<- x ; base::return(y) } try(saferDev::super_assign_check(fun2)) # error: <<- reported # A <<- written in a string or in a comment is not an operator: not reported. fun3 <- function(x){ y <- "the <<- writing is just a string" ; x } # a <<- in a comment saferDev::super_assign_check(fun3) # everything fine: NULL returned
Verify that the TRUE and FALSE logical constants are always written in full when used as values (argument values, conditions, comparisons, indexing) and never abbreviated as T or F, which can be overwritten by the user. Return table message of the line with problems and a corrected version of the submitted function.
true_false_check( x, export = FALSE, out_path = ".", safer_check = TRUE, lib_path = NULL, error_text = "" )true_false_check( x, export = FALSE, out_path = ".", safer_check = TRUE, lib_path = NULL, error_text = "" )
x |
A function name, written without quotes and brackets. |
export |
Single logical value. Write the corrected function into a file instead of returning it? If |
out_path |
Single character string indicating the path of the folder where to write the file when |
safer_check |
Single logical value. Perform the "safer" checks? If |
lib_path |
Vector of characters specifying the absolute pathways of the directories containing the required packages for the function, if not in the default directories. Useful when R packages are not installed in the default directories because of lack of admin rights. More precisely, |
error_text |
Single character string used to add information in error messages returned by the function, notably if the function is inside other functions, which is practical for debugging. Example: |
Warning: true_false_check() only checks the T and F symbols used as values: the package association of the function calls is not checked (run saferDev::colons_check() separately for that). Function names written without brackets (e.g., FUN = as.character) or used as function names (e.g., T()) are not reported.
Reported: T or F used as a value, e.g., f(log = T), f(T), if(T), while(T), x == T or x[T].
Not reported: T or F used as a name or assigned, e.g., T <- 1, x <- T, function(T), f(T = 1), df$T, for(T in 1:3), T(), ?T or y ~ T. Strings, comments and backquoted names (`T`) are ignored. If T or F is defined anywhere in the function (argument, loop variable or assignment), none of its uses is reported.
Correction: each reported T is replaced by TRUE and each F by FALSE (base::T also becomes TRUE). The rest of the code is unchanged. The comments and the indentation of the submitted function are kept, in the returned function and in the exported file. Warning: comments are only available when the submitted function carries its source references (attribute srcref). Otherwise (e.g., compiled package functions, or functions created with options(keep.source = FALSE), default of Rscript), the code is the deparsed one: no comments and standard indentation. The returned function is evaluated in a new environment whose parent is the environment of the submitted function: check it before replacing the original function.
Line numbers are indicative. They start at the <- function line (#' header lines not counted). Compiled functions, like saferDev::colons_check, have no comments: their line numbers differ from their source file.
A table-like message is always printed, with one row per reported symbol (column 1: line number; column 2: symbol; column 3: code before the symbol), or a message saying that everything seems fine.
If export = FALSE: the corrected function, or NULL if nothing has to be corrected.
If export = TRUE: NULL, returned invisibly. The corrected function is written in a file (see the export argument).
Haiding Wang
Yushi Han
# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/true_false_check.html fun1 <- function(x){ if(x == TRUE){ base::return(x) } } saferDev::true_false_check(fun1) # everything fine: NULL returned fun2 <- function(x){ if(x == T){ base::return(x) } } fun2_safer <- saferDev::true_false_check(fun2) # T reported fun2_safer # corrected function # Write the corrected function in the fun2_safer.R file of a new temporary folder: tempo_dir <- base::tempfile(pattern = "dir", tmpdir = base::tempdir(), fileext = "") base::dir.create(path = tempo_dir, showWarnings = TRUE, recursive = FALSE, mode = "0777") saferDev::true_false_check(x = fun2, export = TRUE, out_path = tempo_dir)# Warning: these examples may not work well when using the "Run examples" link # because of a particular environment. Please, copy-paste in a local environment. # See also https://safer-r.github.io/saferDev/articles/true_false_check.html fun1 <- function(x){ if(x == TRUE){ base::return(x) } } saferDev::true_false_check(fun1) # everything fine: NULL returned fun2 <- function(x){ if(x == T){ base::return(x) } } fun2_safer <- saferDev::true_false_check(fun2) # T reported fun2_safer # corrected function # Write the corrected function in the fun2_safer.R file of a new temporary folder: tempo_dir <- base::tempfile(pattern = "dir", tmpdir = base::tempdir(), fileext = "") base::dir.create(path = tempo_dir, showWarnings = TRUE, recursive = FALSE, mode = "0777") saferDev::true_false_check(x = fun2, export = TRUE, out_path = tempo_dir)