1

Setup

Inspired from this question

I have some tools in a package called consoleR.

Inside this package, a setwd funciton is masking base::setwd in order to display the current directory in the prompt like this:

## file consoleR/R/setwd.R
#' @export
setwd <- function(...) {
  base::setwd(...)
  current_dir <- basename(getwd())
  options(prompt = paste(current_dir, " > "))
}

I load that package every session in my .Rprofile:

## file ~/.Rprofile
if(interactive()) {
  library(consoleR, warn.conflicts = FALSE)
  setwd(getwd()) ## initializing the prompt
}

Issue

When I start R from a nix-terminal everything works fine, but when I start RStudio I got an error I cannot explain:

R is a collaborative project with many contributors.
Type 'contributors()' for more information and
'citation()' on how to cite R or R packages in publications.

Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.

Error in base::setwd(...) : character argument expected
project_dir > 

Effort

Then I use traceback() and I get:

project_dir > traceback()
5: base::setwd(...) at setwd.R#10
4: setwd(owd)
3: .rs.onAvailablePackagesStale(reposString)
2: .rs.availablePackages()
1: .rs.rpc.discover_package_dependencies("353B2D06", ".R")
rstudio-available-packages-3178bc4c5ea9a2  >

Note that the prompt is showing another directory than the current one BEFORE and AFTER the call to traceback.

The current dir is in fact my $HOME dir.


What could be going on?

pietrodito
  • 1,783
  • 15
  • 24
  • I would suggest that you don't mask `setwd` and do this instead: `trace(base::setwd, exit = quote(options(prompt = paste(basename(getwd()), " > "))), print = FALSE)` – Roland Jul 28 '23 at 08:27
  • @Roland Your implementation is of course better but, generally, why do you “strongly suggest” not to mask base functions? This *should* work, and generally *does*, unless you do something wrong (which you can still do with `trace()`), or some other code has glaring bugs. – Konrad Rudolph Jul 28 '23 at 08:30
  • 1
    Some comments on your code: (1) Calling `library` or similar in `.Rprofile` is asking for trouble. Instead, I recommend you modify `options('defaultPackages')`. (2) Since your package always needs to initialise the prompt, I suggest moving that code into the package’s `.onLoad` hook. And instead of calling `setwd(getwd())` there, just set the prompt directly. (3) Your package *overwrites* the user-defined prompt, which is usually unwanted. Instead, save the existing user prompt and *prefix* it with the WD. Or, even better, allow the user to supply a `sprintf`-like pattern and insert the WD. – Konrad Rudolph Jul 28 '23 at 08:35
  • @KonradRudolph I don't "generally" advise against masking base functions (although I usually hesitate doing that). I have already retracted the "strongly". – Roland Jul 28 '23 at 08:35
  • I was not aware of `trace` or `defaultPackages` or `.onLoad`! You made my day... – pietrodito Jul 28 '23 at 08:35
  • @Roland To clarify, my use of “generally” was intended as synonymous to “apart from that”; it wasn’t meant to suggest that you “generally” advise against doing X. – Konrad Rudolph Jul 28 '23 at 08:36
  • @KonradRudolph Can you explain why a call to the `library` function in `.Rprofile` can cause problems even when used with `if(interactive)`? – pietrodito Jul 29 '23 at 10:43
  • @pietrodito Because it changes the order in which packages are loaded and attached. In particular, calling `library()` inside `.Rprofile` attaches that package *before* some of the core R packages, and this can lead to issues when packages redefine names that are also exported by core R packages. In your case this won’t happen (I guess: your `setwd` overwrites `base::setwd`, and the ‘base’ package is always attached first) so it’s not as important, but it still changes the order of `search()`, which may confuse other (poorly written) packages. – Konrad Rudolph Aug 02 '23 at 07:08

1 Answers1

2

The regular setwd return a character string or NULL is the working directory is not available. Your setwd doesn't as the last statement is the call to options which returns a list. So the following will probably resolve this:

## file consoleR/R/setwd.R
#' @export
setwd <- function(...) {
  res <- base::setwd(...)
  current_dir <- basename(getwd())
  options(prompt = paste(current_dir, " > "))
  res
}

Edit: The return values of setwd and options are usually used to set the directory back afterwards:

function(...) {
  olddir <- setwd(newdir)
  on.exit(setwd(olddir))
  # do stuff
}

The setwd called in your example will then get a list which explains the error message.

Jan van der Laan
  • 8,005
  • 1
  • 20
  • 35