39

Is there any way to stop an R program without error?

For example I have a big source, defining several functions and after it there are some calls to the functions. It happens that I edit some function, and want the function definitions to be updated in R environment, but they are not actually called.

I defined a variable justUpdate and when it is TRUE want to stop the program just after function definitions.

ReadInput <- function(...) ...
Analyze <- function(...) ...
WriteOutput <- function(...) ...

if (justUpdate)
    stop()
# main body
x <- ReadInput()
y <- Analyze(x)
WriteOutput(y)

I have called stop() function, but the problem is that it prints an error message.

ctrl+c is another option, but I want to stop the source in specific line.

The problem with q() or quit() is that it terminates R session, but I would like to have the R session still open.

As @JoshuaUlrich proposed browser() can be another option, but still not perfect, because the source terminates in a new environment (i.e. the R prompt will change to Browser[1]> rather than >). Still we can press Q to quit it, but I am looking for the straightforward way.

Another option is to use if (! justUpdate) { main body } but it's clearing the problem, not solving it.

Is there any better option?

Mogsdad
  • 44,709
  • 21
  • 151
  • 275
Ali
  • 9,440
  • 12
  • 62
  • 92
  • Could you elaborate on what you are trying to do, and why? This can help us help you. – Paul Hiemstra Jan 22 '13 at 23:04
  • @joran I don't want to shut down all error messages – Ali Jan 22 '13 at 23:13
  • 4
    Either 1) put your function definitions in a separate file (or better yet, a package) from your program and source it after you've updated the function(s); or 2) use `if(!justUpdate) { ... }`. I recommend 1). What you currently want to do is a confusing way to organize your code. – Joshua Ulrich Jan 22 '13 at 23:19
  • @JoshuaUlrich Thanks, maybe this example does not fit the question the best, but is there not anyway to do exactly what stop() function does without error? – Ali Jan 22 '13 at 23:25
  • @joran `try(stop(""),silent = TRUE)` does not stop – Ali Jan 23 '13 at 00:23
  • 1
    Yes to @Joshua's comment above and suggestion 1). Some programming languages call it a *library*, others a *module*, others a *package*, but it is the same idea everywhere: it should only contain functions. The code to be run should only be in your main script. That's programming 101. Anything else you try is just dumb IMHO. – flodel Jan 23 '13 at 01:08
  • 1
    @Ali yes, it's called `return`, but it doesn't work at the top-level. If you want to stop without error, you'll need to wrap everything in a function. – hadley Jan 24 '13 at 13:48
  • If the script is a `knitr` document being rendered, `knitr::knit_exit()` can be used. See here: https://stackoverflow.com/q/33705662/1072349 – ajerneck Oct 10 '18 at 14:04

12 Answers12

28

I found a rather neat solution here. The trick is to turn off all error messages just before calling stop(). The function on.exit() is used to make sure that error messages are turned on again afterwards. The function looks like this:

stop_quietly <- function() {
  opt <- options(show.error.messages = FALSE)
  on.exit(options(opt))
  stop()
}

The first line turns off error messages and stores the old setting to the variable opt. After this line, any error that occurs will not output a message and therfore, also stop() will not cause any message to be printed.

According to the R help,

on.exit records the expression given as its argument as needing to be executed when the current function exits.

The current function is stop_quietly() and it exits when stop() is called. So the last thing that the program does is call options(opt) which will set show.error.messages to the value it had, before stop_quietly() was called (presumably, but not necessarily, TRUE).

Stibu
  • 15,166
  • 6
  • 57
  • 71
15

There is a nice solution in a mailing list here that defines a stopQuietly function that basically hides the error shown from the stop function:

stopQuietly <- function(...) {
  blankMsg <- sprintf("\r%s\r", paste(rep(" ", getOption("width")-1L), collapse=" "));
  stop(simpleError(blankMsg));
} # stopQuietly()

> stopQuietly()
Vangelis Tasoulas
  • 3,109
  • 3
  • 23
  • 36
12

I have a similar problem and, based on @VangelisTasoulas answer, I got a simple solution.
Inside functions, I have to check if DB is updated. If it is not, stop the execution.

r=readline(prompt="Is DB updated?(y/n)")
Is DB updated?(y/n)n
if(r != 'y') stop('\r Update DB')
Update DB

Just putting \r in the beginning of the message, overwrite Error: in the message.

xm1
  • 1,663
  • 1
  • 17
  • 28
7

You're looking for the function browser.

Joshua Ulrich
  • 173,410
  • 32
  • 338
  • 418
  • 2
    Thanks, `browser` is a good choice but not perfect. When I put it in the source code, the program stops successfully from the line I wanted, however the prompt is changed to "Browser[1]>" rather than ">" which means in new environment. I want simply the same environment my source was finished normally – Ali Jan 22 '13 at 22:59
  • Just type `Q` at that prompt to close the browser prompt and land back in the main prompt... – Paul Hiemstra Jan 22 '13 at 23:02
  • @PaulHiemstra Thanks I updated the question, have you any more convenient idea? – Ali Jan 22 '13 at 23:03
  • 2
    Are you looking for `break`? – Paul Hiemstra Jan 22 '13 at 23:05
5

You can use the following solution to stop an R program without error:

if (justUpdate)
    return(cat(".. Your Message .. "))
Grant Miller
  • 27,532
  • 16
  • 147
  • 165
  • 2
    Welcome to SO! This may answer the question, but a short explanation and context can help everyone understand your solution. – ggorlen Aug 24 '18 at 20:21
1

Just return something at the line you want to quit the function:

f <- function(x, dry=F) { 
   message("hi1")
   if (dry) return(x)
   message("hi2")
   x <- 2*x 
}
y1 <- f(2) # = 4 hi1 hi2
y2 <- f(2, dry=T) # = 2 hi1
Chris
  • 83
  • 7
0

In addition to answer from Stibu on Mar 22 '17 at 7:29, if you want to write a message as a part of stop(), this message is not written.

I perceive strange that following two lines have to be used meaning on.exit(options(options(show....))) doesn't work.

opt <- options(show.error.messages = F)
on.exit(options(opt))
0

I had forgotten the answer to this and needed to look it up and landed here... You posted the hint to the answer in your question...

ctrl+c is another option, but I want to stop the source in specific line.

Signal an error, warning, or message

rlang::inform("Updated Only")
rlang::interrupt()
Thell
  • 5,883
  • 31
  • 55
0

I've found it good to write a script and run it with source(). In the script, a write exit statements as a special class of error that a tryCatch() can pick up and send back as just a message:

exit <- function(..., .cl = NULL) {
  # Use to capture acceptable stop
  cond <- structure(
    list(.makeMessage(...), .cl),
    class = c("exitError", "error", "condition"),
    names = c("message", "call")
  )

  stop(cond)
}

foo <- function() {
  exit("quit here")
  1
}

tryCatch(
  # rather than foo(), you might use source(filename)
  foo(),
  exitError = function(e) message(e$message)
)
#> quit here

Created on 2022-01-24 by the reprex package (v2.0.1)

Jordan
  • 169
  • 1
  • 6
  • It would be helpful to add some sample data so that users can see how those functions work in action. – coip Jan 27 '22 at 17:29
  • @coip, the `foo()` functions as the sample "data" (or in this case a small function to represent a separate script). It contains an `exit()` call at an arbitrary location. – Jordan Jan 28 '22 at 18:46
0

You can use with_options() in the withr package to temporarily disable error messages and then you can call stop() directly.

Here is an example:

weird_math <- function(x, y, z) {
  
  if (x > z) {
    withr::with_options(
      list(show.error.messages = FALSE),
      {
        print("You can execute other code here if you want")
        stop()
      }
    )
  }
  
  # only runs if x <= z
  x + y ^ z
  
}

weird_math(1, 2, 3)
[1] 9

weird_math(3, 2, 1)
[1] "You can execute other code here if you want"
Harrison Jones
  • 2,256
  • 5
  • 27
  • 34
-1

why not just use an if () {} else {}? It's only a couple of characters...

f1 <- function(){}
f2 <- function(){}

if (justUpdate) {
} else {
# main body 
}

or even

f1 <- function(){}
f2 <- function(){}

if (!justUpdate) {
# main body 
}
user1697590
  • 143
  • 3
  • 7
-1

The below code work for me stopped without error messages.

opt <- options(show.error.messages = FALSE)
on.exit(options(opt))
break