Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

env argument not used to actually source the R code #851

Closed
filipsch opened this Issue May 24, 2018 · 11 comments

Comments

Projects
None yet
4 participants
@filipsch
Copy link
Contributor

filipsch commented May 24, 2018

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.

Reproducible example

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"

Context

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.

Potential solution

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

@eddelbuettel

This comment has been minimized.

Copy link
Member

eddelbuettel commented May 24, 2018

@jjallaire Thoughts? Potential solution could work.

Not sure how many people pass an arg there and have a local R snippet...

@jjallaire

This comment has been minimized.

Copy link
Member

jjallaire commented May 24, 2018

Seems like this is an oversight. @filipsch Could you give us a PR for this?

@filipsch

This comment has been minimized.

Copy link
Contributor Author

filipsch commented May 25, 2018

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.

@eddelbuettel

This comment has been minimized.

Copy link
Member

eddelbuettel commented Jun 9, 2018

@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?

@coatless

This comment has been minimized.

Copy link
Contributor

coatless commented Jun 9, 2018

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...

@filipsch

This comment has been minimized.

Copy link
Contributor Author

filipsch commented Jun 10, 2018

@eddelbuettel cc @coatless I will look into this asap, hope before the end of today.

@eddelbuettel

This comment has been minimized.

Copy link
Member

eddelbuettel commented Jun 10, 2018

@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.

@filipsch

This comment has been minimized.

Copy link
Contributor Author

filipsch commented Jun 11, 2018

For reference, conversation with @lionel- happened here: #807 (comment). I'll try to reproduce and resolve this.

@filipsch

This comment has been minimized.

Copy link
Contributor Author

filipsch commented Jun 11, 2018

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?

@eddelbuettel

This comment has been minimized.

Copy link
Member

eddelbuettel commented Jun 11, 2018

Hm, runit.wstring.R -- could part of the difference be locale related ?

@filipsch

This comment has been minimized.

Copy link
Contributor Author

filipsch commented Jun 13, 2018

I managed to reproduce the side effects. Switching to new.env(parent = baseenv()) as lionel suggested fixed it. PR has been made.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.