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

CRAN guardrails #1459

Merged
merged 3 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions R/conda.R
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ conda_create <- function(envname = NULL,
conda = "auto",
python_version = miniconda_python_version())
{
check_forbidden_install("Conda Environments")

# resolve conda binary
conda <- conda_binary(conda)
local_conda_paths(conda)
Expand Down Expand Up @@ -248,6 +250,8 @@ conda_create <- function(envname = NULL,

conda_create_env <- function(envname, environment, conda) {

check_forbidden_install("Conda Environments")

if (!is.null(envname))
envname <- condaenv_resolve(envname)

Expand Down
101 changes: 54 additions & 47 deletions R/config.R
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,8 @@ py_discover_config <- function(required_module = NULL, use_environment = NULL) {
main_process_info <- main_process_python_info()
if (!is.null(main_process_info)) {
python_version <- normalize_python_path(main_process_info$python)$path
config <- python_config(python_version, required_module, forced = "the current process")
return(config)
try(return(python_config(python_version, required_module,
forced = "the current process")))
}

# if PYTHON_SESSION_INITIALIZED is specified then use it without scanning
Expand All @@ -175,8 +175,8 @@ py_discover_config <- function(required_module = NULL, use_environment = NULL) {
py_session_initialized <- py_session_initialized_binary()
if (!is.null(py_session_initialized)) {
python_version <- normalize_python_path(py_session_initialized)$path
config <- python_config(python_version, required_module, forced = "PYTHON_SESSION_INITIALIZED")
return(config)
try(return(python_config(python_version, required_module,
forced = "PYTHON_SESSION_INITIALIZED")))
}

# if RETICULATE_PYTHON is specified then use it without scanning further
Expand All @@ -188,8 +188,8 @@ py_discover_config <- function(required_module = NULL, use_environment = NULL) {
stop("Python specified in RETICULATE_PYTHON (", reticulate_env, ") does not exist")

python_version <- python_version$path
config <- python_config(python_version, required_module, forced = "RETICULATE_PYTHON")
return(config)
try(return(python_config(python_version, required_module,
forced = "RETICULATE_PYTHON")))

}

Expand All @@ -205,8 +205,8 @@ py_discover_config <- function(required_module = NULL, use_environment = NULL) {
stop("Python specified in RETICULATE_PYTHON_ENV (", reticulate_python_env, ") does not exist")
})

config <- python_config(python, required_module, forced = "RETICULATE_PYTHON_ENV")
return(config)
try(return(python_config(python, required_module,
forced = "RETICULATE_PYTHON_ENV")))

}

Expand All @@ -215,17 +215,17 @@ py_discover_config <- function(required_module = NULL, use_environment = NULL) {
required_version <- .globals$required_python_version
if (!is.null(required_version)) {
python_version <- normalize_python_path(required_version)$path
config <- python_config(python_version, required_module, forced = "use_python() function")
return(config)
try(return(python_config(python_version, required_module,
forced = "use_python() function")))
}

# check if we're running in an activated venv
if (is_virtualenv(envpath <- Sys.getenv("VIRTUAL_ENV", NA))) {
# If this check ends up being too strict, we can alternatively do:
# if (python_info(Sys.which("python"))$type == "virtualenv") {
config <- python_config(virtualenv_python(envpath), required_module,
forced = "VIRTUAL_ENV")
return(config)
try(return(python_config(
virtualenv_python(envpath), required_module,
forced = "VIRTUAL_ENV")))
}

# if we're working within a project that contains a pyproject.toml file,
Expand All @@ -244,10 +244,9 @@ py_discover_config <- function(required_module = NULL, use_environment = NULL) {
for (dirpath in c("./venv", "./virtualenv", "./.venv", "./.virtualenv")) {
if (dir.exists(dirpath) && is_virtualenv(dirpath)) {
python <- virtualenv_python(dirpath)
config <- python_config(
try(return(python_config(
python, required_module,
forced = sprintf("'%s' existing in the current working directory", dirpath))
return(config)
forced = sprintf("'%s' existing in the current working directory", dirpath))))
}
}

Expand All @@ -258,10 +257,10 @@ py_discover_config <- function(required_module = NULL, use_environment = NULL) {
next
python <- tryCatch(py_resolve(envname), error = identity)
if (!inherits(python, "error"))
return(python_config(
try(return(python_config(
python, required_module,
forced = sprintf('import("%s")', required_module)
))
)))
}

# check for `use_python(required = FALSE)`. This should rarely be triggered
Expand All @@ -270,9 +269,10 @@ py_discover_config <- function(required_module = NULL, use_environment = NULL) {
# first call of use_*(,required = FALSE) wins
optional_requested_use_pythons <- reticulate_python_versions()
for (python in optional_requested_use_pythons) {
config <- python_config(python, required_module,
forced = "use_python(, required = FALSE)")
return(config)
try(return(python_config(
python, required_module,
forced = "use_python(, required = FALSE)"
)))
}

# look in virtual environments that have a required module derived name,
Expand All @@ -281,10 +281,10 @@ py_discover_config <- function(required_module = NULL, use_environment = NULL) {
envname <- paste0("r-", module)
python <- tryCatch(py_resolve(envname), error = identity)
if (!inherits(python, "error"))
return(python_config(
try(return(python_config(
python, required_module,
forced = sprintf('import("%s")', required_module)
))
)))
}

# if RETICULATE_PYTHON_FALLBACK is specified then use it
Expand All @@ -294,14 +294,14 @@ py_discover_config <- function(required_module = NULL, use_environment = NULL) {
if (!python_version$exists)
stop("Python specified in RETICULATE_PYTHON_FALLBACK (", reticulate_env, ") does not exist")
python_version <- python_version$path
config <- python_config(python_version, required_module, python_version, forced = "RETICULATE_PYTHON_FALLBACK")
return(config)
try(return(python_config(python_version, required_module, python_version,
forced = "RETICULATE_PYTHON_FALLBACK")))
}


## At this point, the user, (and package authors on behalf of the user), has
## expressed no preference for any particular python installation, or the
## preference expressed is for the python environment that don't exist.
## preference expressed is for a python environment that don't exist.
##
## In other words,
## - no use_python(), use_virtualenv(), use_condaenv()
Expand All @@ -311,21 +311,22 @@ py_discover_config <- function(required_module = NULL, use_environment = NULL) {
## - we're not running under an already activated venv (i.e., no VIRTUAL_ENV env var)
## - no configured poetry or pipfile or venv in the current working directory

# Look for a "r-reticulate" venv. if found, use that.
# Look for a "r-reticulate" venv or condaenv. if found, use that.
# If not found, try to get permission to create one.
# This is the default in the absence of any expressed preference by the user.
python <- tryCatch(py_resolve("r-reticulate"), error = identity)
python <- tryCatch(py_resolve("r-reticulate"), error = function(e) {
envpath <- try_create_default_virtualenv(package = "reticulate")
if (!is.null(envpath))
virtualenv_python(envpath)
else
e
})
if (!inherits(python, "error"))
return(python_config(python, required_module))
try(return(python_config(python, required_module)))

# if we couldn't find a "r-reticulate" env, try to create one
# and look for it again
create_default_virtualenv(package = "reticulate")
python <- tryCatch(py_resolve("r-reticulate"), error = identity)
if (!inherits(python, "error"))
return(python_config(python, required_module))

# At this point, user has expressed no preference, and has declined
# to use the create the "r-reticulate" venv.
# At this point, user has expressed no preference, and we do not have user permission
# to create the "r-reticulate" venv

# fall back to using the PATH python, or fail.
# We intentionally do not go on a fishing expedition for every possible python,
Expand All @@ -339,12 +340,14 @@ py_discover_config <- function(required_module = NULL, use_environment = NULL) {
Sys.which("python")
))

if (is_windows())
append(python_versions) <- local({
df <- py_versions_windows()
df$executable_path[df$type == "PythonCore"]
})

windows_registry_python <- character()
if (is_windows()) {
append(python_versions) <- windows_registry_python <-
local({
df <- py_versions_windows()
df$executable_path[df$type == "PythonCore"]
})
}

# filter locations by existence
if (length(python_versions) > 0)
Expand Down Expand Up @@ -374,7 +377,11 @@ py_discover_config <- function(required_module = NULL, use_environment = NULL) {
for (python_version in python_versions) {

# get the config
config <- python_config(python_version, required_module, python_versions)
config <- try(python_config(python_version, required_module, python_versions,
forced = if (python_version %in% windows_registry_python)
"Windows Registry" else "PATH"))
if(inherits(config, "try-error"))
next

# if we have a required module ensure it's satisfied.
# also check architecture (can be an issue on windows)
Expand All @@ -390,9 +397,9 @@ py_discover_config <- function(required_module = NULL, use_environment = NULL) {

# no preferred found, return first with valid config if we have it or NULL
if (length(valid_python_versions) > 0)
return(python_config(valid_python_versions[[1]], required_module, python_versions))
try(return(python_config(valid_python_versions[[1]], required_module, python_versions)))
else if (length(python_versions) > 0)
return(python_config(python_versions[[1]], required_module, python_versions))
try(return(python_config(python_versions[[1]], required_module, python_versions)))
else
return(NULL)
}
Expand Down Expand Up @@ -437,7 +444,7 @@ py_discover_config_fallbacks <- function() {

}

create_default_virtualenv <- function(package = "reticulate", ...) {
try_create_default_virtualenv <- function(package = "reticulate", ...) {

# If the environment already exists, use it
envname <- paste0("r-", package)
Expand Down
3 changes: 3 additions & 0 deletions R/install-python.R
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ install_python <- function(version = "3.9:latest",
list = FALSE,
force = FALSE)
{

check_forbidden_install("Python")

# resolve pyenv path
pyenv <- pyenv_find()
if (!file.exists(pyenv))
Expand Down
2 changes: 2 additions & 0 deletions R/virtualenv.R
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ virtualenv_create <- function(
setuptools_version = getOption("reticulate.virtualenv.setuptools_version", default = NULL),
extra = getOption("reticulate.virtualenv.extra", default = NULL))
{
check_forbidden_install("Python Virtual Environments")

path <- virtualenv_path(envname)
name <- if (is.null(envname)) path else envname

Expand Down