Skip to content

Suggestions for an "official" place to store permissions/options for a package?

4 messages · Jonathan Greenberg, Reijo Sund, Milan Bouchet-Valat +1 more

#
R-developers:

Duncan Murdoch suggested I move a post I started on r-help over here,
since it is more at the developer level.  Here is my
question/challenge -- to my knowledge, there is not currently an
official way to store a *package*'s options to a standardized location
on a user's computer.  Given that OS-level programs have standard
preference locations, I was hoping to first assess:

- Does R, in fact, have a place for a *package* to store preferences
(a file-based "setOptions") that would be persistent across sessions.

One suggestion that was given was to perhaps write these options to
the .RProfile, but this strikes me as potentially dangerous -- a
poorly written function to write to a user's .RProfile could corrupt
it.

If the answer to the initial question is "no" (there is not an
official location/approach for a package to store its own files), I'd
like to suggest we open this for a wider discussion amongst the
developers, to perhaps come up with a general (not package-by-package)
solution for a package to store options (and perhaps other files) in a
standard location.

The particular application that has brought me to asking about this is
that I'm writing a package that has external calls to command-line
programs that may not be properly registered with a user's environment
(think: Windows, in particular, although I've found issues with a Mac
as well), so the first step of these R - wrapper functions is to
search a user's local machine for the binaries.  If it finds it, it
stores the path to this binary as an option.  Since this is a
brute-force search, this can take some time and, ideally, this
information would be preserved across sessions, without the function
having to re-search their drive every time they start R fresh.

--j
#
CRAN Repository policy gives some guidance:
"Packages should not write in the users? home filespace, nor anywhere else on the file system apart from the R session?s temporary directory (or during installation in the location pointed to by TMPDIR: and such usage should be cleaned up). Installing into the system?s R installation (e.g., scripts to its bin directory) is not allowed. Limited exceptions may be allowed in interactive sessions if the package obtains confirmation from the user."

Of the existing packages in CRAN, at least Rcmdr allows to use config files.

If there is a need for storing package's options for longer time, it may not be a good idea to write package's directories (or to system's R installation) as updates of the package (or R) would then erase the config file. It is also obvious that storing of options is sensible only for directories for which the user has write access. In this sense, a (subdirectory in) user's home directory is probably the best place to store package's config files unless the user has provided other information in function call or using environmental variables.

Below is an extract of code from my package muste that also uses config files. I'm not claiming that it would provide any general or even good solution, but at least it gives a concrete example that hopefully stimulates discussion on this topic..

Best wishes,
Reijo Sund


- - -


# Create environment for package's global variables 
.muste <- new.env(hash=TRUE, parent=emptyenv())

muste <- function(config="<empty>") 
    {

# Package directory
    .muste$mustepath <- system.file(package="muste")

# Check write access to package directory
    if(file.access(.muste$mustepath,mode=2)==-1) .muste$writeaccess <-FALSE
    else .muste$writeaccess <- TRUE
  
# Start path  
    .muste$startdir <- getwd()
  
# Path to actual R directory 
    .muste$Rhome <- normalizePath(R.home())
  
# Path to home directory (see R documentation for more information)
    .muste$homedir <- normalizePath("~/")

# Path to temp directory with guaranteed write access
    .muste$Rtempdir <- tempdir()
  
# System, OS and R info  
    .muste$sysname<-unlist(Sys.info()["sysname"])[[1]]
    .muste$OS.type <- .Platform$OS.type
    .muste$r_arch <- .Platform$r_arch
  
# Path to R binary  
    if (.muste$sysname=="Windows")
      {
      .muste$Rbin <-  paste(file.path(R.home("bin"),"Rgui --sdi"))  
      }
    else .muste$Rbin <- paste(file.path(R.home("bin"),"R"))  
  
# Location of config file
    if (config=="<empty>") 
        {
        .muste$apufile <- Sys.getenv("MUSTEAPU") # Read path from environmental variable
        if (nchar(.muste$apufile)==0) # If file is not given, use defaults
            {
            if (.muste$sysname=="Windows") .muste$apufile <- paste(.muste$homedir,'\\.muste\\muste.apu',sep="")
            else .muste$apufile <- paste(.muste$homedir,'/.muste/muste.apu',sep="")
            }
        }
    else .muste$apufile <- config  # Path to file given as a parameter

# Check if given setup file exists
    if(!file.exists(.muste$apufile)) .muste.setup()

}

.muste.setup <- function()
    {
# Ask about creation of dir(s)/file(s)    
    viesti <- paste("Configuration file was not found!\nIs it OK to create\n",.muste$apufile,"?",sep=" ")
    response <- "no"
    if (interactive())
        {
        require(tcltk)
        response <- tclvalue(tkmessageBox(message=viesti, icon="question", type="yesno", default="no", title=""))
        }
    if (response == "no") return()

# Directory part of the path to config file	
    .muste$apufiledir <- dirname(.muste$apufile) 
    
# Create directory for config file
    dir.create(.muste$apufiledir,showWarnings=FALSE)
	
# Check write access to given config file directory		
    if(file.access(.muste$apufiledir,mode=2)==-1) stop("No write access!")     
    
# Create and initialize config file
    file.create(.muste$apufile)
    cat("/ Configuration file",file=.muste$apufile,sep="\n",append=TRUE)
    }
#
Le vendredi 18 octobre 2013 ? 19:32 -0500, Jonathan Greenberg a ?crit :
I support this request. This would be useful for Rcmdr and Rcmdr
plug-ins to save GUI preferences in a cleaner fashion than writing
to .Rprofile. A major use case for R base itself would be to store the
default CRAN mirror once for all instead of asking the user every time
(s)he installs a package.

Any mechanism will do, from a simple .ini style key-value file to
serializing and restoring an arbitrary (but usually very simple) R
object. .Rprofile is really not designed to store preferences. A
per-package way of saving settings would mean that a package has no
chance of messing with the user's global settings when it is not loaded,
reducing the risk of breakage.


Regards
#
On 13-10-18 8:32 PM, Jonathan Greenberg wrote:
I think you misread that suggestion.  I think it was a suggestion that 
the user should write that information into their own .RProfile.

What I'd suggest you do is have two functions, saveFooOptions and 
loadFooOptions in your package Foo. (The exact names aren't important, 
but don't use generic names like saveOptions and loadOptions; other 
packages may want to follow your model.)  Instruct the user to set the 
options in a session, then call saveFooOptions to save them.  Tell them 
how to put loadFooOptions into their .RProfile file if they want them 
loaded automatically at startup.

Currently the only files that R writes automatically are .RData and 
.RHistory. It only writes those after confirmation from the user, and 
they are not written to a standard location, so users can maintain 
multiple copies of them.  Packages should be no more intrusive.

On Windows, the R GUI maintains a collection of preferences, but they 
are never written automatically.  The loadRconsole() function can load 
them from an arbitrary file; they are saved from a dialog in the GUI. 
There's a default set in the R install directory, but often users don't 
have write permission there.  You could do the same, having a default 
set of options in your package installation, but you shouldn't assume 
the user has rights to change it.
That sounds like a case where a user might choose to save the locations. 
  But you shouldn't do it without asking, and you should allow the user 
to decide where the file goes.

Duncan Murdoch