If you set the env variable in sourceCpp, Rcpp correctly makes the R functions and modules available in the specified environment. However, the R snippet in the /*** R block is not executed in this same environment. This causes unexpected errors.
test.rcpp
#include <Rcpp.h>
using namespace Rcpp ;
// [[Rcpp::export]]
int answer(){
return 42 ;
}
/*** R
# call answer and check you get the right result
x <- answer()
x
*/
Next, in R:
library(Rcpp)
testEnv <- new.env()
sourceCpp("test.cpp", env = testEnv)
ls(testEnv)
Expected output:
> library(Rcpp)
> testEnv <- new.env()
> sourceCpp("test.cpp", env = testEnv)
> # call answer and check you get the right result
> x <- answer()
> x
[1] 42
> ls(testEnv)
[1] "answer" "x"
Actual output:
> library(Rcpp)
> testEnv <- new.env()
> sourceCpp("test.cpp", env = testEnv)
> # call answer and check you get the right result
> x <- answer()
Error in answer() : could not find function "answer"
> ls(testEnv)
[1] "answer"
I work at DataCamp, where we're building a course on Rcpp together with Romain Francois. Our R execution backend heavily relies on the concept of environments: we run the solution code in a solution environment and the student code in a student environment, so that we can check the equality of objects. Because of the above, it is currently very hard for Rcpp exercise to do this. For even more context, you can check out this issue.
I think that passing local = env to the source() call here fixes this, as done here. Before doing a PR, I wanted to know whether the current behavior is intended and what the authors' take on it is.
cc @sumedh10
@jjallaire Thoughts? Potential solution could work.
Not sure how many people pass an arg there _and_ have a local R snippet...
Seems like this is an oversight. @filipsch Could you give us a PR for this?
I have opened a PR, but honestly have no clue on how to include a unit test for this. The structure of the Rcpp package is too unfamiliar. Let's continue the discussion there.
@filipsch I am seeing side effects. Ubuntu 17.10; g++ 7.2; R 3.4.4.
On the console:
* checking tests ...
Running ‘doRUnit.R’ [142s/109s]
[143s/109s] ERROR
Running the tests in ‘tests/doRUnit.R’ failed.
Last 13 lines of output:
13: try(eval(expr, envir = parent.frame()), silent = silent)
14: inherits(try(eval(expr, envir = parent.frame()), silent = silent), "try-error")
15: checkException(Rcpp::sourceCpp(file.path(path, "embeddedR2.cpp"), env = newEnv2), " not available in other env")
16: func()
17: system.time(func(), gcFirst = RUnitEnv$.gcBeforeTest)
18: doTryCatch(return(expr), name, parentenv, handler)
19: tryCatchOne(expr, names, parentenv, handlers[[1L]])
20: tryCatchList(expr, classes, parentenv, handlers)
21: tryCatch(expr, error = function(e) { call <- conditionCall(e) if (!is.null(call)) { if (identical(call[[1L]], quote(doTryCatch))) call <- sys.call(-4L) dcall <- deparse(call)[1L] prefix <- paste("Error in", dcall, ": ") LONG <- 75L msg <- conditionMessage(e) sm <- strsplit(msg, "\n")[[1L]] w <- 14L + nchar(dcall, type = "w") + nchar(sm[1L], type = "w") if (is.na(w)) w <- 14L + nchar(dcall, type = "b") + nchar(sm[1L], type = "b") if (w > LONG) prefix <- paste0(prefix, "\n ") } else prefix <- "Error : " msg <- paste0(prefix, conditionMessage(e), "\n") .Internal(seterrmessage(msg[1L])) if (!silent && identical(getOption("show.error.messages"), TRUE)) { cat(msg, file = outFile) .Internal(printDeferredWarnings()) } invisible(structure(msg, class = "try-error", condition = e))})
22: try(system.time(func(), gcFirst = RUnitEnv$.gcBeforeTest))
23: .executeTestCase(funcName, envir = sandbox, setUpFunc = .setUp, tearDownFunc = .tearDown)
24: .sourceTestFile(testFile, testSuite$testFuncRegexp)
25: runTestSuite(testSuite)
An irrecoverable exception occurred. R is aborting now ...
Segmentation fault (core dumped)
* checking for unstated dependencies in vignettes ... OK
* checking package vignettes in ‘inst/doc’ ... OK
* checking re-building of vignette outputs ... [70s/66s] OK
* checking PDF version of manual ... OK
* DONE
Status: 1 ERROR, 3 NOTEs
In the log file:
Executing test function test.embeddedR ...
> x <- foo()
> x
[1] 42
> x <- foo()
*** caught segfault ***
address 0x55660000000c, cause 'memory not mapped'
Traceback:
1: .External(list(name = "InternalFunction_invoke", address = <pointer: 0x556629108820>, dll = list(name = "Rcpp", path = "/home/edd/git/rcpp/Rcpp.Rcheck/Rcpp/libs/Rcpp.so", dynamicLookup = TRUE, handle = <pointer: 0x556629164270>, info = <pointer: 0x556627cd3930>), numParameters = -1L), <pointer: 0x556627ed7bd0>, ...)
2: foo()
3: eval(ei, envir)
4: eval(ei, envir)
5: withVisible(eval(ei, envir))
6: source(file = srcConn, local = env, echo = TRUE)
7: Rcpp::sourceCpp(file.path(path, "embeddedR2.cpp"), env = newEnv2)
8: eval(expr, envir = parent.frame())
9: doTryCatch(return(expr), name, parentenv, handler)
10: tryCatchOne(expr, names, parentenv, handlers[[1L]])
11: tryCatchList(expr, classes, parentenv, handlers)
12: tryCatch(expr, error = function(e) { call <- conditionCall(e) if (!is.null(call)) { if (identical(call[[1L]], quote(doTryCatch))) call <- sys.call(-4L) dcall <- deparse(call)[1L] prefix <- paste("Error in", dcall, ": ") LONG <- 75L msg <- conditionMessage(e) sm <- strsplit(msg, "\n")[[1L]] w <- 14L + nchar(dcall, type = "w") + nchar(sm[1L], type = "w") if (is.na(w)) w <- 14L + nchar(dcall, type = "b") + nchar(sm[1L], type = "b") if (w > LONG) prefix <- paste0(prefix, "\n ") } else prefix <- "Error : " msg <- paste0(prefix, conditionMessage(e), "\n") .Internal(seterrmessage(msg[1L])) if (!silent && identical(getOption("show.error.messages"), TRUE)) { cat(msg, file = outFile) .Internal(printDeferredWarnings()) } invisible(structure(msg, class = "try-error", condition = e))})
13: try(eval(expr, envir = parent.frame()), silent = silent)
14: inherits(try(eval(expr, envir = parent.frame()), silent = silent), "try-error")
15: checkException(Rcpp::sourceCpp(file.path(path, "embeddedR2.cpp"), env = newEnv2), " not available in other env")
16: func()
17: system.time(func(), gcFirst = RUnitEnv$.gcBeforeTest)
18: doTryCatch(return(expr), name, parentenv, handler)
19: tryCatchOne(expr, names, parentenv, handlers[[1L]])
20: tryCatchList(expr, classes, parentenv, handlers)
21: tryCatch(expr, error = function(e) { call <- conditionCall(e) if (!is.null(call)) { if (identical(call[[1L]], quote(doTryCatch))) call <- sys.call(-4L) dcall <- deparse(call)[1L] prefix <- paste("Error in", dcall, ": ") LONG <- 75L msg <- conditionMessage(e) sm <- strsplit(msg, "\n")[[1L]] w <- 14L + nchar(dcall, type = "w") + nchar(sm[1L], type = "w") if (is.na(w)) w <- 14L + nchar(dcall, type = "b") + nchar(sm[1L], type = "b") if (w > LONG) prefix <- paste0(prefix, "\n ") } else prefix <- "Error : " msg <- paste0(prefix, conditionMessage(e), "\n") .Internal(seterrmessage(msg[1L])) if (!silent && identical(getOption("show.error.messages"), TRUE)) { cat(msg, file = outFile) .Internal(printDeferredWarnings()) } invisible(structure(msg, class = "try-error", condition = e))})
22: try(system.time(func(), gcFirst = RUnitEnv$.gcBeforeTest))
23: .executeTestCase(funcName, envir = sandbox, setUpFunc = .setUp, tearDownFunc = .tearDown)
24: .sourceTestFile(testFile, testSuite$testFuncRegexp)
25: runTestSuite(testSuite)
An irrecoverable exception occurred. R is aborting now ...
Any idea? Do you have a similar setup?
Same result on macOS R 3.5.0:
* checking tests ...
Running ‘doRUnit.R’/Library/Frameworks/R.framework/Resources/bin/BATCH: line 60: 22999 Segmentation fault: 11 ${R_HOME}/bin/R -f ${in} ${opts} ${R_BATCH_OPTIONS} > ${out} 2>&1
ERROR
Running the tests in ‘tests/doRUnit.R’ failed.
Last 13 lines of output:
*** caught segfault ***
address 0x20, cause 'memory not mapped'
Traceback:
1: sourceCpp(file.path(pathToRcppTests, "cpp", file))
2: setUpFunc()
3: doTryCatch(return(expr), name, parentenv, handler)
4: tryCatchOne(expr, names, parentenv, handlers[[1L]])
5: tryCatchList(expr, classes, parentenv, handlers)
6: tryCatch(expr, error = function(e) { call <- conditionCall(e) if (!is.null(call)) { if (identical(call[[1L]], quote(doTryCatch))) call <- sys.call(-4L) dcall <- deparse(call)[1L] prefix <- paste("Error in", dcall, ": ") LONG <- 75L sm <- strsplit(conditionMessage(e), "\n")[[1L]] w <- 14L + nchar(dcall, type = "w") + nchar(sm[1L], type = "w") if (is.na(w)) w <- 14L + nchar(dcall, type = "b") + nchar(sm[1L], type = "b") if (w > LONG) prefix <- paste0(prefix, "\n ") } else prefix <- "Error : " msg <- paste0(prefix, conditionMessage(e), "\n") .Internal(seterrmessage(msg[1L])) if (!silent && isTRUE(getOption("show.error.messages"))) { cat(msg, file = outFile) .Internal(printDeferredWarnings()) } invisible(structure(msg, class = "try-error", condition = e))})
7: try(setUpFunc())
8: .executeTestCase(funcName, envir = sandbox, setUpFunc = .setUp, tearDownFunc = .tearDown)
9: .sourceTestFile(testFile, testSuite$testFuncRegexp)
10: runTestSuite(testSuite)
An irrecoverable exception occurred. R is aborting now ...
I also get:
* checking re-building of vignette outputs ...Warning in file.copy(pkgdir, vd2, recursive = TRUE) : too deep nesting
Warning in file.copy(pkgdir, vd2, recursive = TRUE) : too deep nesting
Not sure where that is coming from...
@eddelbuettel cc @coatless I will look into this asap, hope before the end of today.
@filipsch Thanks, appreciate it.
@lionel- hinted that maybe it is the (larger, overall) test setup (as well as possibly something with your tests, we don't know). He also left one idea there somewhere as I recall (distinct envs to run in).
We have had on occassion weird behaviour come from test setups so thst we ended up not running certain tests regularly. It would be a shame. At the end of the day your code is not that large; maybe we find another to trigger it.
First check, I guess, would be for you to see if R CMD check ... blows up as it did for @coatless and myself.
For reference, conversation with @lionel- happened here: https://github.com/RcppCore/Rcpp/issues/807#issuecomment-395981114. I'll try to reproduce and resolve this.
I cannot reproduce. Trying on MacOS, R 3.5.0, I instead get another error message:
$ ./run.sh run_tests
Building with: R CMD build --no-build-vignettes --no-manual
* checking for file ‘./DESCRIPTION’ ... OK
* preparing ‘Rcpp’:
* checking DESCRIPTION meta-information ... OK
* cleaning src
* running ‘cleanup’
* installing the package to process help pages
* cleaning src
* running ‘cleanup’
* checking for LF line-endings in source and make files and shell scripts
* checking for empty or unneeded directories
* building ‘Rcpp_0.12.17.2.tar.gz’
Testing with: R CMD check "Rcpp_0.12.17.2.tar.gz" --no-build-vignettes --no-manual --as-cran --install-args=--install-tests
(CRAN incoming checks are off)
* using log directory ‘/Users/filip/workspace/Rcpp/Rcpp.Rcheck’
* using R version 3.5.0 (2018-04-23)
* using platform: x86_64-apple-darwin17.5.0 (64-bit)
* using session charset: UTF-8
* using options ‘--no-manual --no-build-vignettes --as-cran’
... <left-out>
* checking for unstated dependencies in ‘tests’ ... OK
* checking tests ...
Running ‘doRUnit.R’ [176s/177s]
ERROR
Running the tests in ‘tests/doRUnit.R’ failed.
Last 13 lines of output:
test.wrap.unordered.map.string.string: (3 checks) ... OK (0 seconds)
test.wrap.vector.Foo: (1 checks) ... OK (0 seconds)
---------------------------
Test file: /Users/filip/workspace/Rcpp/Rcpp.Rcheck/Rcpp/unitTests/runit.wstring.R
test.CharacterVector_wstring: (1 checks) ... OK (0 seconds)
test.wrap_vector_wstring: (1 checks) ... OK (0 seconds)
test.wstring_param: (1 checks) ... OK (0 seconds)
test.wstring_return: (1 checks) ... OK (0 seconds)
>
> ## Return success or failure to R CMD CHECK
> if (getErrors(tests)$nFail > 0) {
+ stop("TEST FAILED!")
+ }
Error: TEST FAILED!
Execution halted
* checking for unstated dependencies in vignettes ... OK
... <left-out>
Status: 1 ERROR, 2 WARNINGs, 3 NOTEs
See
‘/Users/filip/workspace/Rcpp/Rcpp.Rcheck/00check.log’
for details.
$ gcc --version
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 9.1.0 (clang-902.0.39.2)
Target: x86_64-apple-darwin17.5.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
In the log file:
test.DateVector.operator.SEXP: (1 checks) ... OK (0 seconds)
test.DateVector.wrap: (1 checks) ... OK (0 seconds)
test.Datetime.ctor.diffs: (3 checks) ... OK (0 seconds)
test.Datetime.ctor.notFinite: (3 checks) ... OK (0 seconds)
test.Datetime.formating: FAILURE !! (check number 1)
Error in checkEquals(Datetime_format(d, "%Y-%m-%d %H:%M:%S"), format(d, :
1 string mismatch
Datetime.formating.default
test.Datetime.fromString: (1 checks) ... OK (0 seconds)
test.Datetime.get.functions: (1 checks) ... OK (0 seconds)
test.Datetime.operators: (1 checks) ... OK (0 seconds)
test.Datetime.wrap: (1 checks) ... OK (0 seconds)
I can try a PR that implements @lionel- 's suggestion of using new.env(parent = baseenv()) in the test when creating the envs and seeing if that passes on your machines?
Hm, runit.wstring.R -- could part of the difference be locale related ?
I managed to reproduce the side effects. Switching to new.env(parent = baseenv()) as lionel suggested fixed it. PR has been made.