TRUE <- "Not TRUE anymore"
Error in TRUE <- "Not TRUE anymore": invalid (do_set) left-hand side to assignment
print(TRUE)
[1] TRUE
T
/F
Instead of TRUE
/FALSE
You are writing the abbreviated forms of TRUE
and FALSE
, T
and F
or you are using T
or F
as names.
Change the abbreviation to the full words or use a different variable name.
In R, T
and F
are variables being set to TRUE
and FALSE
, respectively. However, those variables can be redefined by the user since these are not reserved words as TRUE
and FALSE
are (see the R Language Definition for more details). This can result in unexpected code behavior as users of your package might have variables named T
/F
.
The first example shows that you cannot overwrite TRUE
:
TRUE <- "Not TRUE anymore"
Error in TRUE <- "Not TRUE anymore": invalid (do_set) left-hand side to assignment
print(TRUE)
[1] TRUE
This throws an error as TRUE
is a reserved word and the value of TRUE
does not change.
T
(and F
) on the other hand can be set to a different value:
<- "Not TRUE anymore"
T
print(T)
[1] "Not TRUE anymore"
To avoid any unexpected behaviors and inconsistencies, CRAN reviewers will ask you to write the reserved words, TRUE
and FALSE
instead of their abbreviated forms. For the same reason, T
or F
should not be used as variable names in your code, examples, tests or vignettes.
In your functions, you are setting a the random seed to a specific number which cannot be changed.
Remove the code that sets the seed. If you want to set it specifically in your functions, users should be able to set no seed if they want.
Changing the seed is not necessarily forbidden. However, users should be able to control which seed to use. Therefore, avoid code that changes the seed without user consent (e.g.: set.seed(123)
). A good solution for allowing to set a seed is to add an argument seed
to your function and set it conditionally.
function(... , seed = NULL){
if(!is.null(seed)){
set.seed(seed)
}
#the rest of your function can be written here.
}
This allows users to avoid setting a seed if they change the argument to NULL
. Ideally, the argument is already set to NULL
per default.
In your examples, vignettes, demos and tests setting a seed is not only allowed but also recommended to ensure reproducible results.
print()
/cat()
You are using functions, like print()
or cat()
, to print unsuppressable output to the console.
Change print()
/cat()
to message()
, warning()
, stop()
, or wrap them in if(verbose){}
.
Information messages, like loop counts or status updates, can clutter the console. While some users prefer this display, others appreciate less information on their console. The use of printing functions for console output is not forbidden on CRAN. However, this output must be suppressable by users.
Printing in special functions like print, summary, interactive functions or methods for generic functions is accepted.
To allow users to suppress the console output CRAN recommends two different ways:
cat()
/print()
with other generics
This allows to use functions like suppressMessages()
to avoid unwanted output.
verbose
, other names are acceptedThis example code shows the use of a verbose
argument to allow users to suppress printing
<- function(..., verbose = TRUE){
foo # your code
if(verbose){
print("Whatever you want to say!")
}# your code
}
Functions can print per default, like the example above, as long as the printing can be turned off (here, by setting verbose = FALSE
).
print()
and cat()
are not the only functions which can write output onto the console. The issue described in the recipe, also applies to the use of other printing function like writeLines()
. If you are using loggers to document your functions’ process, make sure that users can set their log level such that not messages are displayed.
You are changing the par()
, options()
or setwd()
in your functions, examples, demos or vignettes without resetting them.
Reset the changed options, in your functions by using on.exit()
or in examples, demos and vignettes with an additional line of code after the example.
Ideally, the user’s options are not changed at all. If they really have to be altered, restoring the previous values of user options is mandatory for CRAN packages. The reason for this rule is this line stated in the CRAN Repository Policy:
The code and examples provided in a package should never do anything which might be regarded as malicious or anti-social.
Resetting options is therefore mainly a Quality-of-Life feature for users of your package.
There are different ways of resetting for functions, and examples, demos or vignettes which are recommended by CRAN.
Changing par()
, options()
or setwd()
all invisibly return the previous values and therefore these can be stored in variables using the assignment operator <-
and later be restored by calling the variable name as argument in the respective function.
For functions:
When changing options inside one of your package functions, you can use on.exit()
for restoring.
<- function(x){
foo
# par():
<- par(mfrow = c(2,2))
oldpar on.exit(par(oldpar))
# options():
<- options(digits = 3)
oldop on.exit(options(oldop))
# setwd():
<- setwd(".")
oldwd on.exit(setwd(oldwd))
# your code which requires a changed option
}
This will reset the par()
, options()
and setwd()
. The use of on.exit()
makes it possible to restore options before exiting a function even if the function breaks. Therefore it needs to be called immediately after the option change within a function. For more information, call ?on.exit()
in your console.
For demos, examples and vignettes: Since no function is exited when changing options in examples, on.exit()
cannot be used. CRAN recommends the following way for restoring options:
<- par(mfrow = c(2,2))
oldpar
# your code which requires a changed option
par(oldpar)
Here the code will only reset the options if the example runs without breaking. Therefore, try to keep the code between setting and resetting as concise as possible. Restoring the options()
and setwd()
can be done using the same principle as for par()
shown above.
If you need to change more than one option in the same function, example, vignette or demo, you can use oldpar <- par(no.readonly = TRUE)
or oldop <- options()
to reset all parameters at once. Saving the entire Note, that for par()
the no.readonly
argument must be set to TRUE
or else warnings will be produced.
The issue described in the recipe, also applies to the use of other function which change some parameters persistently, like Sys.setLanguage
.
You are writing, either by default or within your examples, vignettes, or tests, to the user’s home filespace, including the package directory and the current working directory (getwd()
).
Omit any default path in your functions and write to tempdir()
in examples, vignettes or tests.
The CRAN Repository Policy states that:
The code and examples provided in a package should never do anything which might be regarded as malicious or anti-social.
and gives as example:
Packages should not write in the user’s home filespace (including clipboards), nor anywhere else on the file system apart from the R session’s temporary directory (or during installation in the location pointed to by TMPDIR: and such usage should be cleaned up). Installing into the system’s R installation (e.g., scripts to its bin directory) is not allowed.
Packages can write per default to tempdir()
, or any other temporary/cache directory, or have no default path at all, as long as the default is not the user’s home filespace.
Some of your code that runs during R CMD check
creates files in the temporary directory specified by the environment variable TMPDIR
and doesn’t remove them afterwards. As a result, R CMD check
gives your package a NOTE complaining about the “detritus in the temp directory”.
If you use tempfile()
/tempdir()
as the default destination for functions called in your examples or tests, unlink()
the resulting files as part of the test. In the \examples{}
section of your help files, clean-up actions can be wrapped in \dontshow{}
for aesthetic reasons. If you launch a browser, ‘Calibre’, ‘Python’ code or other software, and it creates temporary files, find out where they are and remove them once the code is done with them.
The code responsible for the detritus may include your own tests and examples creating files under tempdir()
, child processes, or ‘Python’ code launched using ‘reticulate’. For example, R packages that use the ‘tensorflow’ ‘Python’ package find and remove temporary files created by its auto-gradient feature.
A package that uses the ‘testthat’ test suite will benefit from the self-cleaning functions withr::local_tempfile()
and withr::local_tempdir()
to work with temporary files. Even without ‘testthat’, the ‘withr’ package has no external strong dependencies and can be used independently.
.GlobalEnv
You are writing per default to the global environment, .GlobalEnv
, the user’s workspace.
Omit any default writing to the global environment.
The .GlobalEnv
is the main workspace of users. It can also be accessed by globalenv()
. Writing to the global environment is forbidden for CRAN packages.
Sometimes package maintainers use the operator <<-
. This operator not only evaluates the expression in the environment it is called in, checks parent environments for an existing definition of the variable. If such a variable is found then its value is redefined, otherwise assignment takes place in the .GlobalEnv
. To avoid writing to the global environment, the variable must be defined in a parent environment.
<- function(){
foo
# defines the variable in the foo()-function environment
<- NULL
var
<- function(){
foo1
# redefines var but only in the foo() environment as it is the parent of foo1()
<<- "redefined"
var
}
# calls the foo1() function to redefine var
foo1()
return(var)
}
Part of the .GlobalEnv
is the .Random.seed
which should not be changed at all.
Excepted from this rule are ‘shiny’ packages which build interactive web apps. They sometimes need to modify the .GlobalEnv
.
Calls of rm(list = ls())
to remove variables of the current environment, should not be used in examples, vignettes or demos. In functions rm(list = ls())
can be used, as the active environment is then the function environment instead of the global environment.
installed.packages()
You are calling installed.packages()
to check if a package is installed.
Instead of using installed.packages()
, use requireNamespace("pkg")
or require("pkg")
to find out if packages are available.
installed.packages()
outputs a matrix with details of all packages which are installed in the specified libraries. This can be used to check if a specific package is installed. As mentioned in the help file of the installed.packages()
function, it can be very slow under certain circumstances.
This needs to read several files per installed package, which will be slow on Windows and on some network-mounted file systems. It will be slow when thousands of packages are installed, …
Therefore, do not use installed.packages()
in CRAN packages.
The help file also lists different solutions and the respective, alternative functions.
… so do not use it to find out if a named package is installed (use
find.package
orsystem.file
) nor to find out if a package is usable (callrequireNamespace
orrequire
and check the return value) nor to find details of a small number of packages (usepackageDescription
).
Ideally, use requireNamespace("pkg")
or require("pkg")
, both return FALSE
if a package isn’t available, and throw an error conditionally. For more details on package installations in your code see this recipe.
options(warn = -1)
You are setting options(warn = -1)
.
Consider using suppressWarnings()
instead of options(warn = -1)
if you absolutely need to suppress warnings.
CRAN doesn’t allow negative warn
options. This setting will turn off all warning messages. Even if the settings are correctly restored, as explained in the Change of Options recipe, the submission will be rejected.
CRAN recommends using suppressWarnings()
, which disables warnings only for the specific expression it’s applied to, rather than globally.
You are installing software or packages in your functions, examples, tests or vignettes.
Create special functions for the purpose of installing software and don’t install it in examples, tests, or vignettes.
Packages should usually not be installed within functions, especially since dependencies should already be listed in the DESCRIPTION. For external software this is typically the same. However, if the purpose of your package is to connect to specific APIs or provides easier installation for some programs, installing software or packages is allowed on CRAN.
To ensure shorter check times, those functions should not be called within tests, vignettes or examples. The name of function which install software should ideally indicate that, for example:
devtools::install_github()
reticulate::install_python()
The corresponding help file should also state that software will be installed.
Even if it is made clear that installations will happen, the function shouldn’t write to the user’s home file space, as mentioned in this recipe.
You are using more than 2 CPU cores in your examples, vignettes, tests or during installation.
Make sure that you set the maximum number of cores used to 2 in examples, vignettes or tests. During installation, many modern build systems use the -j
flag.
CRAN checks run on big, multi-core computers, running many R CMD check
processes in parallel. Every process is allowed to use a maximum of 2 CPU cores to exercise parallel code in examples and unit tests. When using more than 2 cores, your package will get a NOTE from CRAN checks, saying that it took more CPU time than elapsed time.
Automatic tests will generate a NOTE if more than 2 cores are used in examples, vignettes, tests, or during installation. It’s best to provide an option for setting the number of cores in any function, with the default ideally set to fewer than 2 cores.
Something that ran as part of the installation process might have started more than two child processes (or threads) at the same time. The solution for this is specific to the build system your package is using. Many modern build systems however, can use the -j
flag which originated from GNU Make
(for more information, see their manual).