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

after turning off tracebug, in debugging, ESS continues to send <newline> to R. #973

Open
mmaechler opened this issue Dec 23, 2019 · 24 comments · May be fixed by #1134
Open

after turning off tracebug, in debugging, ESS continues to send <newline> to R. #973

mmaechler opened this issue Dec 23, 2019 · 24 comments · May be fixed by #1134
Milestone

Comments

@mmaechler
Copy link
Member

mmaechler commented Dec 23, 2019

Because of the source(*, echo=TRUE) breaking bug, I've turned off tracebug completely in ESS for the time being. Now, e.g.,

debug(ts)
ts(1:100, start = as.Date("2019-12-12"), frequency=12)

(which reveals a bug in current R-devel from one my own recent commits !),
then in the debugger, typing 'n', occasionally inspecting variables, or typing ls.str(),
I several times have ESS sending <newline> or <enter> to R "behind my back"; here's part of a transcript (using R 3.6.2 where ts() does not show any bug, of course):

debugging in: ts(1:100, start = as.Date("2019-12-12"), frequency = 12)
debug: {
    if (is.data.frame(data)) 
        data <- data.matrix(data)
    if (is.matrix(data)) {
        nseries <- ncol(data)
        ndata <- nrow(data)
        dimnames(data) <- list(NULL, names)
    }
    else {
        nseries <- 1
        ndata <- length(data)
    }
    if (ndata == 0) 
        stop("'ts' object must have one or more observations")
    if (missing(frequency)) 
        frequency <- 1/deltat
    else if (missing(deltat)) 
        deltat <- 1/frequency
    if (frequency > 1 && abs(frequency - round(frequency)) < 
        ts.eps) 
        frequency <- round(frequency)
    if (length(start) > 1L) {
        start <- start[1L] + (start[2L] - 1)/frequency
    }
    if (length(end) > 1L) {
        end <- end[1L] + (end[2L] - 1)/frequency
    }
    if (missing(end)) 
        end <- start + (ndata - 1)/frequency
    else if (missing(start)) 
        start <- end - (ndata - 1)/frequency
    if (start > end) 
        stop("'start' cannot be after 'end'")
    nobs <- floor((end - start) * frequency + 1.01)
    if (nobs != ndata) 
        data <- if (NCOL(data) == 1) {
            if (ndata < nobs) 
                rep_len(data, nobs)
            else if (ndata > nobs) 
                data[1L:nobs]
        }
        else {
            if (ndata < nobs) 
                data[rep_len(1L:ndata, nobs), ]
            else if (ndata > nobs) 
                data[1L:nobs, ]
        }
    attr(data, "tsp") <- c(start, end, frequency)
    if (!is.null(class) && class[[1]] != "none") 
        attr(data, "class") <- class
    data
}
Browse[2]> n
debug: if (is.data.frame(data)) data <- data.matrix(data)
Browse[2]> n
debug: if (is.matrix(data)) {
    nseries <- ncol(data)
    ndata <- nrow(data)
    dimnames(data) <- list(NULL, names)
} else {
    nseries <- 1
    ndata <- length(data)
}
Browse[2]> ls.str()
class : <missing>
data :  int [1:100] 1 2 3 4 5 6 7 8 9 10 ...
deltat :  num 1
end :  num(0) 
frequency :  num 12
names : <missing>
start :  Date[1:1], format: "2019-12-12"
ts.eps :  num 1e-05
Browse[2]> debug: nseries <- 1
Browse[2]> n
debug: ndata <- length(data)
Browse[2]> n
debug: if (ndata == 0) stop("'ts' object must have one or more observations")
Browse[2]> debug: if (missing(frequency)) frequency <- 1/deltat else if (missing(deltat)) deltat <- 1/frequency
Browse[2]> missing(start)
[1] FALSE
Browse[2]> debug: if (missing(deltat)) deltat <- 1/frequency
Browse[2]> 

note in the last 6-7 lines above, that two of them have debug: <.. R code ..> and these were caused by ESS entirely, without me any typing at all; so there were line advances that I did not cause by typing Enter (aka "return" aka "newline") or n at all.

@mmaechler
Copy link
Member Author

can anybody reproduce, please? NB I have the following in my ~/.emacs initialization before loading ess :

;;MM_2019-12-13: ess-tracebug 
;;MM_2019-12-13: invalidates source(*, echo=TRUE) and confuses me more than it helps
(setq ess-use-tracebug nil); default is 't'

the behaviour super frustrating to such an extent that I sometimes need to start an R session outside of ESS (which I then do in a *shell* buffer, so I can at least use comint-style navigation) just so I can debug properly.. and yes, as an R Core member I do need to carefully debug frequently..

@jabranham
Copy link
Contributor

Yes, I can reproduce. Haven't had time to investigate yet. If you run into this you could always temporarily enable tracebug (C-c C-t T in the inferior buffer).

@vspinu
Copy link
Member

vspinu commented Feb 23, 2020

I cannot reproduce unfortunately. With or without tracebug. When I press RET repeatedly stepping happens one by one as expected.

@jabranham
Copy link
Contributor

jabranham commented Feb 24, 2020 via email

@mmaechler
Copy link
Member Author

mmaechler commented Feb 25, 2020

exactly! For me, it only happens once in a while (it seems), not every time... but it's a "killer":
If I debug (traditionally, not with tracebug ESS support), I want to use ls.str() {I had added that to R for debugging !}, inspect variables, run some small pieces of code, etc etc.. and then the debugger advances too far, e.g., causing the error I want to debug.. and I'm out of the debugging session. It's really a very awful experience. And remember: I'm debugging sometimes very basic R functions {some of which are called by your tracebug "craft"}, so I do need to be able to run the debugger as in an R console, and not as in a tracebug-smartified-I-know-better-what-you-want-than-yourself R console...

@vspinu
Copy link
Member

vspinu commented Feb 26, 2020

I am pretty sure that this is not caused by tracebug in your case. Once you disable it with M-x ess-tracebug there is no way any of the process related ess "goodies" should get in your way, simply because all of them happen in inferior-ess-tracebug-output-filter.

You can check it at any time with M-: (process-filter (ess-get-process)) RET. Without tracebug it's our plain inferior-ess-output-filter.

@mmaechler
Copy link
Member Author

mmaechler commented Apr 8, 2020

There is even more going badly wrong in R's debug() / debugonce() mode, when
ess-tracebug is turned off.
As we've seen there are good reasons notably for low-level developers like me to turn off tracebug. But I'm coming more and more to the conclusion that turning it off is even worse than turning it on. This is really really not tolerable: The consequence is that I have NO WAY to have R inside ESS behave like "real R". At least in Rstudio, I think I have been able to completely turn off all the hand-holding I-help-you-debugging-because-I-know-better-than-you-what-you-want.

Here's the 2nd severe problem with turned off ess-tracebug:

  1. with ess-tracebug activated .. here everything is as it should be:
## ess-tracebug  activated :
> debugonce(paste); paste("a", character(), "b")
debugging in: paste("a", character(), "b")
debug: .Internal(paste(list(...), sep, collapse, zero.length))
Browse[2]> dput(list(...))
list("a", character(0), "b")
Browse[2]> c
exiting from: paste("a", character(), "b")
[1] "a  b"
> 
  1. Now, with ess-tracebug mode disabled (that's the message after toggling it via M-x ess-tracebug):
## ess-tracebug disabled :
> debugonce(paste); paste("a", character(), "b")
debugging in: paste("a", character(), "b")
debug: .Internal(paste(list(...), sep, collapse, zero.length))
Browse[2]> dput(list(...))
Error in dput(list(...)) : '...' used in an incorrect context
>

@mmaechler
Copy link
Member Author

(BTW: This bug seems to happen only for functions where body() does not start with "{" ... (that's not an excuse, but may help diagnosing)

@vspinu
Copy link
Member

vspinu commented Apr 13, 2020

I cannot reproduce the second problem without tracebug. It really doesn't seem related to ESS at all.

@mmaechler
Copy link
Member Author

This is still bugging me very regularly. And it has been reproducible by @jabranham ...
I do have ess-eval-visibly set to nil (where we have nowait as default IIRC).

@lionel-

This comment has been minimized.

@mmaechler

This comment has been minimized.

@lionel-

This comment has been minimized.

@mmaechler

This comment has been minimized.

@lionel-

This comment has been minimized.

@lionel-

This comment has been minimized.

@lionel- lionel- added this to the 20.09 milestone Aug 22, 2020
@lionel-
Copy link
Member

lionel- commented Aug 22, 2020

@mmaechler I cannot reproduce with tracebug and visibility switched off. Can you reproduce it with a vanilla Emacs? If not, could you try to pinpoint which part of your config makes it reproducible please?

@lionel-
Copy link
Member

lionel- commented Dec 1, 2020

These newlines are possibly due to background tasks calling inferior-ess-available-p. This routine checks whether a process has been marked busy (i.e. there's no final prompt) and also sends a new line to double check.

@lionel-
Copy link
Member

lionel- commented Feb 5, 2021

@mmaechler Can you still reproduce this? If not we can probably close this.

@mmaechler
Copy link
Member Author

It became so extremely painful that I reverted to keep tracebug active by default and live with the drawback..
Ok, so I'll try now again to see the effect (after updating and make ing ESS ((yes, I still alway work with the *.elc ..))

@lionel-
Copy link
Member

lionel- commented Mar 26, 2021

I can reproduce! I must have forgotten to turn off tracebug the last time.

The problem is that inferior-ess-available-p sends a newline to the process to double-check that it is not busy: https://github.com/emacs-ess/ESS/blob/bd87ffb1/lisp/ess-inf.el#L373-L375. This predicate is called by ess-get-next-available-process which itself is called by eldoc or completion routines. When you type n in the REPL, ESS looks for completions which triggers this newline check.
Why doesn't this happen with tracebug? Because it is special-cased here: https://github.com/emacs-ess/ESS/blob/bd87ffb1/lisp/ess-inf.el#L369.

Now this newline check is normally not a problem because we have our prompts set to > . When the prompt is prefixed however, inferior-ess--set-status explicitly lets the newlines go through: https://github.com/emacs-ess/ESS/blob/bd87ffb1/lisp/ess-inf.el#L398. This means that users who have set their prompts to R> or similar must have a lot of intempestive newlines causing a mess in the output.

This check was introduced in 1ff5b8c6.

Some thoughts:

  • It is a bit unclear to me how safe this newline check. Regarding active sinks I think we're good because they don't get the prompts. However I'm concerned about the case where a newline doesn't pong back. In this case it is assumed that when the process becomes available again we can delete the last prompt caused by the newline. This doesn't seem to take into account the possibility of new input being sent in the meantime. Also it seems that the whole output batch is discarded instead of just the trailing prompt, which means there is a risk of discarding relevant output.

  • Is it really necessary to treat user-defined prompts differently here? It seems we could just as safely handle arbitrary prompts.

  • Since we want to support user-defined prompts, I think we should remove the tracebug special-casing so that we (the developers with a bare > prompt) get confronted to this code path whenever we are in the debugger. As a plus we'd get contextual help in the debugger.

I suggest trying this patch:

@@ -366,8 +366,7 @@ defined. If no project directory has been found, use
   (when-let ((proc (or proc (and ess-local-process-name
                                  (get-process ess-local-process-name)))))
     (unless (process-get proc 'busy)
-      (or (ess-debug-active-p proc) ; don't send empty lines in debugger
-          (when-let ((last-check (process-get proc 'last-availability-check)))
+      (or (when-let ((last-check (process-get proc 'last-availability-check)))
             (time-less-p (process-get proc 'last-eval) last-check))
           (progn
             ;; Send an empty string and waiting a bit to make sure we are not busy.
@@ -395,7 +394,7 @@ Return non-nil if the process is in a ready (not busy) state."
     ;; When "\n" inserted from inferior-ess-available-p, delete the prompt.
-    (when (and ready
-               (process-get proc 'availability-check)
-               (string-match-p (concat "^" inferior-ess-primary-prompt "\\'") string))
+    (when (and ready (process-get proc 'availability-check))
       (process-put proc 'suppress-next-output? t))
     (process-put proc 'availability-check nil)
     (when ready

What do you think @vspinu @mmaechler?

@mmaechler
Copy link
Member Author

Well, I'm happy you can finally reproduce! Your patch suggestion looks very good to me (without trying it) for principal reasons (you give yourself): Less special casing i.e. more coherent behavior.... and the prompt detection business needs to become even more fail proof .. I also agree on that (says the one who's not really contributing currently apart from bug reporting... )

@lionel-
Copy link
Member

lionel- commented Mar 27, 2021

Here is a reprex that we should add as unit test:

# (1) Disable the newline check
# (2) Trigger eldoc or company after evaluating this to cause a hang
{
    cat(paste0(
        "Next line looks like a prompt\n",
        getOption("prompt")
    ))
    Sys.sleep(10)
}

IIUC this is the reason that we have this newline check. It is a protection measure against what we could call "fake prompt hangs".

lionel- added a commit to lionel-/ESS that referenced this issue Mar 31, 2021
@lionel- lionel- linked a pull request Mar 31, 2021 that will close this issue
@jkroes
Copy link

jkroes commented Jul 26, 2022

I'm still seeing a lot of newlines inserted into the inferior buffer at random times when I'm not evaluating anything. Not sure whether this issue was considered resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants