emacs-ess / ESS

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

iESS R package management processes hang (on inferior I/O?) #1166

Closed kimjetwav closed 2 years ago

kimjetwav commented 2 years ago

Somewhat recently I've noticed that processes initiated by commands in the C-c C-. family (the R package management commands) hang rather than returning as expected. I looked into it briefly and it seemed to be a result of the structure of the string being passed to the R process? ess-elisp-trace-mode output included here:

======================================================================
1 -> (ess-r-package-list-available-packages)
| 2 -> (ess-r-package--list-packages ".ess.rutils.ops <- options(width = 10000);print(available.packages(fields=c(\"Title\"))[, c(\"Title\", \"Version\")]);options(.ess.rutils.ops); rm(.ess.rutils.ops);
")
| | 3 -> (ess-command ".ess.rutils.ops <- options(width = 10000);print(available.packages(fields=c(\"Title\"))[, c(\"Title\", \"Version\")]);options(.ess.rutils.ops); rm(.ess.rutils.ops);
" #<buffer  *ess-rutils-pkgs*>)
| | | 4 -> (ess-command--get-proc nil nil)
| | | | 5 -> (ess-get-process "R")
| | | | | 6 -> (update-ess-process-name-list)
| | | | | 6 <- update-ess-process-name-list: nil
| | | | 5 <- ess-get-process: #<process R>
| | | 4 <- ess-command--get-proc: #<process R>
| | | 4 -> (inferior-ess--output-delimiter)
| | | 4 <- inferior-ess--output-delimiter: "ess-output-delimiter9"
| | | 4 -> (ess--strip-final-newlines ".ess.rutils.ops <- options(width = 10000);print(available.packages(fields=c(\"Title\"))[, c(\"Title\", \"Version\")]);options(.ess.rutils.ops); rm(.ess.rutils.ops);
")
| | | 4 <- ess--strip-final-newlines: ".ess.rutils.ops <- options(width = 10000);print(available.packages(fields=c(\"Title\"))[, c(\"Title\", \"Version\")]);options(.ess.rutils.ops); rm(.ess.rutils.ops);"
| | | 4 -> (ess-r-format-command ".ess.rutils.ops <- options(width = 10000);print(available.packages(fields=c(\"Title\"))[, c(\"Title\", \"Version\")]);options(.ess.rutils.ops); rm(.ess.rutils.ops);" (output-delimiter . "ess-output-delimiter9"))
| | | 4 <- ess-r-format-command: ".ess.command(.ess.rutils.ops <- options(width = 10000);print(available.packages(fields=c(\"Title\"))[, c(\"Title\", \"Version\")]);options(.ess.rutils.ops); rm(.ess.rutils.ops);, 'ess-output-delimiter9')
"
| | | 4 -> (ess-if-verbose-write "(ess-command .ess.rutils.ops <- options(width = 10000);print(available.packages(fields=c(\"Title\"))[, c(\"Title\", \"Version\")]);options(.ess.rutils.ops); rm(.ess.rutils.ops);
 ..)")
| | | 4 <- ess-if-verbose-write: nil
| | | 4 -> (ess--command-make-restore-function #<process R>)
| | | 4 <- ess--command-make-restore-function: #[0 "\302\300\301\"\210\303\300\304\305#\210\303\300\306\305#\210\307\300\310\"\211\205#\0\303\300\311\305#\210\301\300\"\207" [#<process R> inferior-ess-tracebug-output-filter set-process-filter process-put cmd-output-delimiter nil cmd-buffer process-get pending-output pending] 5]
| | | 4 -> (ess-setq-vars-local ((ess-local-process-name . "R") (inferior-ess-primary-prompt . "> ")))
| | | 4 <- ess-setq-vars-local: ((ess-local-process-name . "R") (inferior-ess-primary-prompt . "> "))
| | | 4 -> (inferior-ess-mark-as-busy #<process R>)
| | | 4 <- inferior-ess-mark-as-busy: (busy t sec-prompt nil busy-end\? nil availability-check nil running-async\? nil funargs-pre-cache (("plot" (("graphics") (("x" . #1="") ("y" . "NULL") ("type" . "p") ("xlim" . "NULL") ("ylim" . "NULL") ("log" . #1#) ("main" . "NULL") ("sub" . "NULL") ("xlab" . "NULL") ("ylab" . "NULL") ("ann" . "par(\"ann\")") ("axes" . "TRUE") ("frame.plot" . "axes") ("panel.first" . "NULL") ("panel.last" . "NULL") ("asp" . "NA") ("..." . #1#)) ("x" "y" "..." "ci" "type" "xlab" "ylab" "ylim" "main" "ci.col" "ci.type" "max.mfrow" "ask" "mar" "oma" "mgp" "xpd" "cex.main" "verbose" "scale" "xlim" "log" "sub" "ann" "axes" "frame.plot" "panel.first" "panel.last" "asp" "center" "edge.root" "nodePar" "edgePar" "leaflab" "dLeaf" "xaxt" "yaxt" "horiz" "zero.line" "verticals" "col.01line" "pch" "legend.text" "formula" "data" "subset" "to" "from" "newpage" "vp" "labels" "hang" "freq" "density" "angle" "col" "border" "lty" "add" "predicted.values" "intervals" "separator" "col.predicted" "col.intervals" "col.separator" "lty.predicted" "lty.intervals" "lty.separator" "plot.type" "main2" "par.fit" "grid" "panel" "cex" "dimen" "abbrev" "which" "caption" "sub.caption" "id.n" "labels.id" "cex.id" "qqline" "cook.levels" "add.smooth" "label.pos" "cex.caption" "rows" "levels" "conf" "absVal" "ci.lty" "xval" "do.points" "col.points" "cex.points" "col.hor" "col.vert" "lwd" "set.pars" "range.bars" "col.range" "xy.labels" "xy.lines" "nc" "yax.flip" "mar.multi" "oma.multi"))) ("print" (("base") (("x" . #1#) ("digits" . "NULL") ("quote" . "TRUE") ("na.print" . "NULL") ("print.gap" . "NULL") ("right" . "FALSE") ("max" . "NULL") ("useSource" . "TRUE") ("..." . #1#)) ("x" "..." "digits" "signif.stars" "intercept" "tol" "se" "sort" "verbose" "indent" "style" ".bibstyle" "prefix" "vsep" "minlevel" "quote" "right" "row.names" "max" "na.print" "print.gap" "useSource" "diag" "upper" "justify" "title" "max.levels" "width" "steps" "showEnv" "newpage" "vp" "cutoff" "max.level" "give.attr" "units" "abbrCollate" "print.x" "deparse" "locale" "symbolic.cor" "loadings" "zero.print" "calendar")))) cmd-restore-function #[0 "\302\300\301\"\210\303\300\304\305#\210\303\300\306\305#\210\307\300\310\"\211\205#\0\303\300\311\305#\210\301\300\"\207" [#<process R> inferior-ess-tracebug-output-filter set-process-filter process-put cmd-output-delimiter nil cmd-buffer process-get pending-output pending] 5] cmd-buffer #<buffer  *ess-rutils-pkgs*> cmd-output-delimiter "ess-output-delimiter9" dbg-buffer #<buffer  *ess.dbg.R*> dbg-active nil tracebug t :accum-buffer #<buffer  *R:accum*> suppress-next-output\? nil is-recover nil average-response-time 0.002038481740800955 last-availability-check (24945 45123 53036 301000) pending-output "> " pending nil flush-time nil prev-prompt "> " funargs-cache #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("r" nil)) last-eval (24945 45122 543859 108000))
| | | 4 -> (ess-wait-for-process #<process R> nil nil nil 2305843009213693951)
| | | | 5 -> (inferior-ess-ordinary-filter #<process R> "Error: unexpected ';' in \".ess.command(.ess.rutils.ops <- options(width = 10000);\"
> ")
| | | | | 6 -> (ess--if-verbose-write-process-state #<process R> "Error: unexpected ';' in \".ess.command(.ess.rutils.ops <- options(width = 10000);\"
> " "ordinary-filter")
| | | | | | 7 -> (ess-if-verbose-write "
ordinary-filter:
    --> busy:t busy-end:nil sec-prompt:nil interruptable:nil <--
    --> running-async:nil callback:nil suppress-next-output:nil <--
    --> dbg-active:nil is-recover:nil <--
    --> string:Error: unexpected ';' in \".ess.command(.ess.rutils.ops <- options(width = 10000);\"
>
")
| | | | | | 7 <- ess-if-verbose-write: nil
| | | | | 6 <- ess--if-verbose-write-process-state: nil
| | | | | 6 -> (ess--command-delimited-output-info #<buffer  *ess-rutils-pkgs*> "ess-output-delimiter9")
| | | | | | 7 -> (ess--delimiter-start-re "ess-output-delimiter9")
| | | | | | 7 <- ess--delimiter-start-re: "\\(ess-output-delimiter9-START$\\)"
| | | | | 6 <- ess--command-delimited-output-info: nil
| | | | 5 <- inferior-ess-ordinary-filter: nil
| | | | 5 -> (ess-set-width)
| | | | 5 <- ess-set-width: nil
| | | | 5 -> (ess-debug-minor-mode -1)
| | | | 5 <- ess-debug-minor-mode: nil
| | | | 5 -> (ess-process-live-p)
| | | | 5 <- ess-process-live-p: (run open listen connect stop)
| | | | 5 -> (ess-process-get on-error-action)
| | | | 5 <- ess-process-get: nil
| | | | 5 -> (ess-debug-minor-mode -1)
| | | | 5 <- ess-debug-minor-mode: nil
| | | | 5 -> (ess-process-live-p)
| | | | 5 <- ess-process-live-p: (run open listen connect stop)
| | | | 5 -> (ess-process-get on-error-action)
| | | | 5 <- ess-process-get: nil
| | | | 5 -> (ess-debug-minor-mode -1)
| | | | 5 <- ess-debug-minor-mode: nil
| | | | 5 -> (ess-process-live-p)

... and so on until aborted with C-g.

Found on: Ubuntu 20.04 GNU Emacs 29.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.20, cairo version 1.16.0) (built from commit 64f9fdc812) ESS version [unknown] (built via straight (at commit cfc621c) from commit 569dca1f) R version 4.1.0.

lionel- commented 2 years ago

Thanks for the nice report, should be fixed.

kimjetwav commented 2 years ago

Hey, appreciate the fix! I noticed the issue was still happening with ess-r-package-list-local-packages. Luckily it’s a nearly identical fix to aaa82f24. I tested it under ESS master today, and seems to work just as expected. Here’s the diff for your convenience if you’re interested.

diff --git a/lisp/ess-r-mode.el b/lisp/ess-r-mode.el
index ef086db3..cec47c0c 100644
--- a/lisp/ess-r-mode.el
+++ b/lisp/ess-r-mode.el
@@ -2668,10 +2668,12 @@ from all installed packages, which can be very slow."
 (defun ess-r-package-list-local-packages ()
   "List all packages in all libraries."
   (interactive)
-  (ess-r-package--list-packages (concat ".ess.rutils.ops <- options(width = 10000);"
-                                        "print(installed.packages(fields=c(\"Title\"))[, c(\"Title\", \"Version\")]);"
-                                        "options(.ess.rutils.ops); rm(.ess.rutils.ops);"
-                                        "\n")))
+  (ess-r-package--list-packages
+   "{
+       .ess.rutils.ops <- options(width = 10000)
+       print(installed.packages(fields=c(\"Title\"))[, c(\"Title\", \"Version\")])
+       options(.ess.rutils.ops); rm(.ess.rutils.ops)
+    }\n"))

 (defun ess-r-package--list-packages (cmd)
   "Use CMD to list packages."