askpass icon indicating copy to clipboard operation
askpass copied to clipboard

optionalize use of stty

Open r2evans opened this issue 8 months ago • 0 comments

TL;DR:

Please make the call of stty echo optional, perhaps based on a getOption(..).

I use ssh/tmux to get to a headless system where I run R within emacs/ESS. For some things, I find it convenient to use the keyring package to store some credentials/secrets, and its default behavior in this scenario is to use its backend_file. When I need to access a secret, the first time I call keyring::key_get(...) it internally calls askpass::askpass(..). This correctly retrieves my typed-in password for the file-store.

However, the default action of askpass() is to close out by calling system('stty echo') which is mildly disruptive in emacs/ESS. (This does the same thing in my local, gui-based emacs, but since it's not headless, I'm able to use gnome's secret service which is not file-based so it does not prompt me for a password.)

This is how it looks. If you are not familiar with emacs/ESS, it normally does not echo every command, instead this is how it "should" look:

> paste("hello", "world")
[1] "hello world"
> pi*2
[1] 6.283185

If I use askpass::askpass (which, again, is used by keyring::key_get(..)), from then on it duplicates all text:

> askpass::askpass("hello")
hello 
🔑 somesecret
 OK
[1] "somesecret"
> paste("hello", "world")
paste("hello", "world")
[1] "hello world"
> pi*2
pi*2
[1] 6.283185

Resolving it is easy enough:

> system("stty -echo")
system("stty -echo")
> pi*2
[1] 6.283185

But it would be incredibly helpful if I didn't have to do that step.. While the use of system('stty echo') is likely good in most situations, it is not right for emacs/ess, we do not need the stty to echo all commands.

Since it is likely that askpass::askpass(.) is more often called from within another package, I don't know that "just" a formal argument would suffice; for it to be usable in my situations, it would need to be in an envvar or as an option. For instance, perhaps

readline_silent <- function(prompt, icon = "\U0001f511 ", restore_stty = getOption("askpass_restore_stty", TRUE)) {
  if(is_unix() && isatty(stdin())){
    if(system('stty -echo') == 0){
      if (!isFALSE(restore_stty)) on.exit(system('stty echo'))
    }
  }
  cat(prompt, "\n")
  out <- base::readline(icon)
  cat(" OK\n")
  out
}

My use of !isFALSE is purely intended to safely default to the current behavior, resilient to most "oopses". I'm not dead-set on that technique, just the notion of using an R option to better inform that step.

Thoughts?

r2evans avatar Jun 24 '24 05:06 r2evans