emacs-ess / ESS

Emacs Speaks Statistics: ESS
https://ess.r-project.org/
GNU General Public License v3.0
620 stars 162 forks source link

inside `debug()`ging, `str(.)` hangs (just in R inside ESS; not in R) #1173

Open mmaechler opened 2 years ago

mmaechler commented 2 years ago

I hope this is reproducible:

debug(methods)
methods(ls)

then advance line by line, you can even use my favorite ls.str() which does work, however, str(.) itself hangs ... only if all this is inside ESS. Some "transcript":

> methods(ls)
debugging in: methods(ls)
debug:  
    <........... omitted by MM........>
 Browse[2]> 
debug: if (!missing(class) && !is.character(class)) class <- deparse1(substitute(class))
Browse[2]> ls.str()
class : <missing>
envir : <environment: R_GlobalEnv> 
generic.function :  chr "ls"
what :  symbol ls
Browse[2]> 
debug: s3 <- .S3methods(generic.function, class, envir)
Browse[2]> 
debug: s4 <- if (.isMethodsDispatchOn()) methods::.S4methods(generic.function, 
    class)
Warning message:
In .S3methods(generic.function, class, envir) :
  function 'ls' appears not to be S3 generic; found functions that look like S3 methods
Browse[2]> 
debug: methods::.S4methods(generic.function, class)
Browse[2]> 
debug: .MethodsFunction(s3, s4, missing(generic.function))
Browse[2]> ls.str()
class : <missing>
envir : <environment: R_GlobalEnv> 
generic.function :  chr "ls"
s3 :  'MethodsFunction' chr [1:3] "ls.diag" "ls.print" "ls.str"
s4 :  'MethodsFunction' chr(0) 
what :  symbol ls
Browse[2]> str(s4)
  C-c C-c
  <menu-bar> <signals> <stop>
Process R ru at Tue Dec 14 10:03:16 2021
  <menu-bar> <signals> <quit>
Process R verlassen (core dumped) at Tue Dec 14 10:03:22 2021
lionel- commented 2 years ago

This is because eldoc calls methods() on its argument, and then the ess-command gets stuck in the browser. I guess ideally we'd disable browser() for the duration of the command but I can't see any way of doing that.

This seems like this is a wontfix. Once we have solved #1152, we can restore the background command timeout and at least ESS won't be stuck for long in such cases.

In general, you probably want to disable background commands while debugging base R.

lionel- commented 2 years ago

@mmaechler One way would be to introduce a global option to disable browsing in do_browser(). Then it would be easy to set it temporarily in .ess.command().

mmaechler commented 2 years ago

@mmaechler One way would be to introduce a global option to disable browsing in do_browser(). Then it would be easy to set it temporarily in .ess.command().

I see; or just something like withoutBrowser(expr) which would disable browsing only during the evaluation of expr? I'd prefer that quite a bit to a global option with such a radical effect.

lionel- commented 2 years ago

yup that sounds good. Compared to the global option, this would have the advantage that no R code could turn the browser back on.

lionel- commented 2 years ago

@mmaechler Another way we could deal with this is at ESS level by detecting browser prompts in ess-command. When detected, I see two possible recovery mechanisms:

  1. Cancel the ESS command and send a base::return() to R to return to the previous browse level, if any.

  2. Assume the debugged function is still functional and keep sending f until .ess.command() returns.

(1) seems safer but implementing cancellation might be tricky. Cancellation is on the roadmap though. I'd like to detect errors occurring within .ess.command() and handle them at the lisp level (logging the failure in the message buffer and interrupting the calling lisp code).