I'm developing a package in RStudio with usethis, trying to make use of best practices. Previously, I had run usethis::use_tidy_eval(). Now, I'm using data.table, and set this up by running usethis::use_data_table(). I get a warning,
Warning message:
replacing previous import ‘data.table:::=’ by ‘rlang:::=’ when loading ‘breakdown’
because the NAMESPACE contains the the two lines:
importFrom(rlang,":=")
importFrom(data.table,":=")
It turns out I no longer need usethis::use_tidy_eval(), so I'd like to revert it and in doing so get rid of the warning.
How can I undo whatever usethis helper functions do? Must I edit the NAMESPACE myself? How do I know what else was modified by usethis::use_tidy_eval()? What about undoing usethis::use_pipe()?
Unless you made a Git commit before and after running that code, there's probably not an extremely easy way. The two options I'd consider would be:
Read the source code of the function. This can require some hopping around to find definitions of helper functions, but use_tidy_eval looks like it:
adds roxygen to Suggests in DESCRIPTION
adds rlang to Imports in DESCRIPTION
adds the template R file tidy-eval.R
asks you to run document() which is what actually updates the NAMESPACE. You can find the lines added by looking for the importFrom roxygen tags in the template file.
To undo this, you should just be able to delete all of the above. However, you need to be a bit careful - e.g. if you import functions from rlang outside of tidy-eval.R, removing it from DESCRIPTION might prevent installation. Hopefully any such issues would be revealed by devtools::check() if they do happen.
The other option would be to get an older version of your package, run use_tidy_eval() and document() and then compare the changes. That will be more comprehensive and might catch things I missed above, but the same caveats about not being able to necessarily just reverse everything still apply.
Same strategy for use_pipe().
Sidenote: there are probably ways to adequately qualify different uses of := so that both can coexist in your package, in case that would be preferable.
Related
I'm trying to run a testthat script using GitHub Actions.
I would like to test a functionality of my function that allows it to be combined with (many) external packages. Now I want to test these external packages for the R CMD Check but I don't want to load the external packages generally (i.e. putting them into the Description) - after all, most people will not use these external packages.
Any ideas how to just include an external package in the testing files but not in the DESCRIPTION?
Thanks!
I think you describe a very standard use of Suggests.
I see two related but separable issues:
You want to test something using CI, in this case GHA. That is fine. Because you control the execution of the code, you could move your code from the test runner to, say, inst/examples and call it explicitly. That way the standard check of 'is the package using undeclared code' passes as inst/examples is not checked
You want to not force other people to have to load these packages. That is fine too, and we have Suggests: for this! Read Section 1.1 of Writing R Extensions about all the detailed semantics. If your package invokes other packages via tests, the every R CMD check touches this (and the external packages) so they must be declared. But you already know that only "some" people will want to use this "some of the time": that is precisely what Suggests: does, and you bracket the use with if (requireNamespace(pkgHere, quietly=TRUE)).
You can go either way, or even combine both. But you cannot call packages from tests and not declare them.
Following your recommendations (or trying to do it, at least), I have tried some options, but the problem remains, so there must be something I am missing.
I have included a more complete code
setwd("C:/naapp")
#' #import utils
#' #import devtools
I have tried with and without using suppressForeignCheck
if(getRversion() >= "2.15.1"){
utils::globalVariables(c("eleven"))
utils::suppressForeignCheck(c("eleven"))
}
myFunctionSum <- function(X){print(X+eleven)}
myFunctionMul <- function(X){print(X*eleven)}
myFunction11 <- function(X){
assign("eleven",11,envir=environment(myFunctionMul))
}
maybe I should use a particular environment?
package.skeleton(name = "myPack11", list=ls(),
path = "C:/naapp", force = TRUE,
code_files = character())
I remove the "man" directory from the directory myPack11,
otherwise I would get an error because the help files are empty.
I add the imports utils, and devtools to the descrption
Then I run check
devtools::check("myPack11")
And I still get this note
#checking R code for possible problems ... NOTE
#myFunctionMul: no visible binding for global variable 'eleven'
#myFunctionSum: no visible binding for global variable 'eleven'
#Undefined global functions or variables:eleven
I have tried also to make an enviroment, combining Tomas Kalibera's suggetion and an example I found in the Internet.
myEnvir <- new.env()
myEnvir$eleven <- 11
etc
In this case, I get the same note, but with "myEnvir", instead of "eleven"
First version of the question
I trying to use "globalVariables" from the package utils. I am building an interface in R and I am planning to submit to CRAN. This is my first time, so, sorry if the question is very basic.I have read the help and I have tried to find examples, but I still don't know how to use it.
I have made a little silly example to ilustrate my question, which is:
Where do I have to place this line exactly?:
if(getRversion() >= "2.15.1"){utils::globalVariables("eleven")}
My example has three functions. myFunction11 creates the global variable "eleven" and the other two functions manipulate it. In my real code, I cannot use arguments in the functions that are called by means of a button. Consider that this is just a silly example to learn how to use globalVariables (to avoid binding notes).
myFunction11 <- function(){
assign("eleven",11,envir=environment(myFunctionSum))
}
myFunctionSum <- function(X){
print(X+eleven)
}
myFunctionMul <- function(X){
print(X*eleven)
}
Thank you in advance
I thought that the file globals.R would be automatically generated when using globalsVariables. The problem was that I needed to create the package skeleton, then create the file globals.R, add it to the R directory in the package and check the package.
So, I needed to place this in a different file:
#' #import utils
utils::globalVariables(c("eleven"))
and save it
The documentation clearly says:
## In the same source file (to remind you that you did it) add:
if(getRversion() >= "2.15.1") utils::globalVariables(c(".obj1", "obj2"))
so put it in the same source file as your functions. It can go in any of your R source files, but the comment above recommends you put it close to your code. Looking at a bunch of github packages reveals another common pattern is to have a globals.R function with it in, but this is probably a bad idea. If you later remove the global from your package but neglect to update globals.R you could mask a problem. Putting it right close to the functions that use it will hopefully remind you when you edit those functions.
Make sure you put it outside any function definitions in the file, or it won't get seen.
You cannot modify bindings in a package namespace once the package is loaded (and namespace sealed, and bindings locked). The check tool helps you to spot violations of this restriction, so you find out about the problem when checking the package rather than while running it. globalVariables is just a call to silence check when looking for these violations, which is undesirable in almost all cases. If you really need mutable state in a package, you can create a new environment (using new.env) and bind it to an (unexported) "global" variable in your namespace. This binding will be locked, but this is ok, because in R you can change an environment in place (add/remove elements, effectively modifying the elements).
The best situation is however when you can keep all mutable state in user objects (passed in as arguments into functions, and their modified versions returned as output values of functions).
I am quite new to R but it seems, this question is closely related to the following post 1, 2, 3 and a bit different topic 4. Unfortunately, I have not enough reputation to comment right there. My problem is that after going through all the suggestions there, the code still does not work:
I included "Depends" in the description file
I tried the second method including a change of NAMESPACE (Not reproducable)
I created a example package here containing a very small part of the code which showed a bit different error ("J" not found in routes[J(lat1, lng1, lat2, lng2), .I, roll = "nearest", by = .EACHI] instead of 'lat1' not found in routes[order(lat1, lng1, lat2, lng2, time)])
I tested all scripts using the console and R-scripts. There, the code ran without problems.
Thank you very much for your support!
Edit: #Roland
You are right. Roxygen overwrites the namespace. You have to include #' #import data.table to the function. Do you understand, why only inserting Depends: data.table in the DESCRIPTION file does not work? This might be a useful hint in the documentation or did I miss it?
It was missleading that changing to routes <- routes[order("lat1", "lng1", "lat2", "lng2", "time")] helped at least a bit as this line was suddenly no problem any more. Is it correct, that in this case data.frame order is used? I will see how far I get now. I will let you know the final result...
Answering your questions (after edit).
Quoting R exts manual:
Almost always packages mentioned in ‘Depends’ should also be imported from in the NAMESPACE file: this ensures that any needed parts of those packages are available when some other package imports the current package.
So you still should have import in NAMESPACE despite the fact if you depends or import data.table.
The order call doesn't seems to be what you expect, try the following:
order("lat1", "lng1", "lat2", "lng2", "time")
library(data.table)
data.table(a=2:1,b=1:2)[order("a","b")]
In case of issues I recommend to start debugging by writing unit test for your expected results. The most basic way to put unit tests in package is just plain R script in tests directory having stopifnot(...) call. Be aware you need to library/require your package at the start of the script.
This is more in addition to the answers above: I found this to be really useful...
From the docs [Hadley-description](http://r-pkgs.had.co.nz/description.html und)
Imports packages listed here must be present for your package to
work. In fact, any time your package is installed, those packages
will, if not already present, be installed on your computer
(devtools::load_all() also checks that the packages are installed).
Adding a package dependency here ensures that it’ll be installed.
However, it does not mean that it will be attached along with your
package (i.e., library(x)). The best practice is to explicitly refer
to external functions using the syntax package::function(). This
makes it very easy to identify which functions live outside of your
package. This is especially useful when you read your code in the
future.
If you use a lot of functions from other packages this is rather
verbose. There’s also a minor performance penalty associated with
:: (on the order of 5$\mu$s, so it will only matter if you call the
function millions of times).
From the docs Hadley-namespace
NAMESPACE also controls which external functions can be used by your
package without having to use ::. It’s confusing that both
DESCRIPTION (through the Imports field) and NAMESPACE (through import
directives) seem to be involved in imports. This is just an
unfortunate choice of names. The Imports field really has nothing to
do with functions imported into the namespace: it just makes sure the
package is installed when your package is. It doesn’t make functions
available. You need to import functions in exactly the same way
regardless of whether or not the package is attached.
... this is what I recommend: list the package in DESCRIPTION so that it’s
installed, then always refer to it explicitly with pkg::fun().
Unless there is a strong reason not to, it’s better to be explicit.
It’s a little more work to write, but a lot easier to read when you
come back to the code in the future. The converse is not true. Every
package mentioned in NAMESPACE must also be present in the Imports or
Depends fields.
I am receiving an error [could not find function "str_trim"] when I run check() on an R package I am developing. I have since added two things:
1) In DESCRIPTION
Imports: stringr
2) In NAMESPACE
importFrom(stringr,str_trim)
However, when I then run install() and document(), then the line in NAMESPACE is removed. Then, when I again run check(), I receive the original error.
Why is this line being removed? Should I try a different approach, and if so, what kind of approach? Thank you!
It seems you are doing package development "the Hadley way".
Hadley wants you to use roxygen (i.e. the roxygen2 package but that should be automatic if you use his 'devtools').
Then, the roxygen "magic" is used to auto-write your NAMESPACE file... and so also destroys things you've put there.
You must add #importFrom .... statements to your R/*.R files if you want to use roxygen.
I agree with many things Hadley advocates; the wholesale use of 'roxygen' is not among them however.
I want good, carefully maintained help files with \link{}s, \eqn{}, etc etc
-> I edit my man/*.Rd files and I manually build NAMESPACE (so it ends up looking well organized, I can also add comments there, and I can even use
if(getRVersion() >= "3.2.0") { ...... } in the NAMESPACE file, something that's not easily possible with roxygen.
I've hit an issue trying to import a package (namely, 'robfilter') inside one of my own packages. One of its methods that I am trying to use, adore.filter, is failing at this line:
data(critvals)
With error 'data set 'critvals' not found'.
The function works fine if I load the library via require(robfilter). However, this means that in order to use my custom package which calls adore.filter, I will have to load my own package, and then load robfilter. Not a huge problem but slightly annoying.
I'm not sure if the problem is that there is an extra step I need to do in order to make critvals visible within my package, or if perhaps there is something the package author needed to do (and hasn't done) to add critvals to its package namespace; there is no sign of 'critvals' in the robfilter NAMESPACE file. I haven't encountered this issue before and don't really understand how the use of data() inside a package is supposed to work.
There are two solutions as far as I know:
Either ask the robfilter Maintainer to put the data needed by robfiler in the internal data file of robfilter. (R/sysdata.rda)
Or make your package Depends on robfilter
So it works if you put robfilter in the depends section of your description file. But in my case (both are my packages), I was trying to avoid the Depends solution as it loads the imported package and also any other package will need to depend ont its imported package... See my question is quite a duplicate of yours but not in the same context.