Nothing
#' Python configuration
#'
#' Retrieve information about the version of Python currently being used by
#' `reticulate`.
#'
#' If Python has not yet been initialized, then calling `py_config()` will force
#' the initialization of Python. See [py_discover_config()] for more details.
#'
#' @return Information about the version of Python in use, as an \R list with
#' class `"py_config"`.
#'
#' @export
py_config <- function() {
ensure_python_initialized()
.globals$py_config
}
#' Python version
#'
#' Get the version of Python currently being used by `reticulate`.
#'
#' @return The version of Python currently used, or `NULL` if Python has
#' not yet been initialized by `reticulate`.
#'
#' @export
py_version <- function() {
if (!py_available(initialize = FALSE))
return(NULL)
config <- py_config()
numeric_version(config$version)
}
#' Python executable
#'
#' Get the path to the Python executable that `reticulate` has been configured
#' to use. If Python has already been initialized, then `reticulate` will
#' choose the currently-active copy of Python.
#'
#' This can occasionally be useful if you'd like to interact with Python (or its
#' modules) via a subprocess; for example you might choose to install a package
#' with `pip`:
#'
#' ```
#' system2(py_exe(), c("-m", "pip", "install", "numpy"))
#' ```
#'
#' and so you can also have greater control over how these modules are invoked.
#'
#' @return The path to the Python executable `reticulate` has been configured
#' to use.
#'
#' @export
py_exe <- function() {
# if python has already been initialized, use that
if (!is.null(.globals$py_config))
return(.globals$py_config$python)
# otherwise, guess what version of python we'd use
py_discover_config()$python
}
#' Build Python configuration error message
#'
#' @param prefix Error message prefix
#'
#' @keywords internal
#' @export
py_config_error_message <- function(prefix) {
message <- prefix
config <- py_config()
if (!is.null(config)) {
message <- paste0(message, "\n\nDetected Python configuration:\n\n",
str(config), "\n")
}
message
}
#' Check if Python is available on this system
#'
#' @param initialize `TRUE` to attempt to initialize Python bindings if they
#' aren't yet available (defaults to `FALSE`).
#'
#' @return Logical indicating whether Python is initialized.
#'
#' @note The `py_numpy_available` function is a superset of the
#' `py_available` function (it calls `py_available` first before
#' checking for NumPy).
#'
#' @export
py_available <- function(initialize = FALSE) {
if (is_python_initialized())
return(.globals$py_config$available)
if (!initialize)
return(FALSE)
tryCatch({
ensure_python_initialized()
.globals$py_config$available
}, error = function(e) FALSE)
}
#' @rdname py_available
#' @export
py_numpy_available <- function(initialize = FALSE) {
if (!py_available(initialize = initialize))
return(FALSE)
py_numpy_available_impl()
}
#' Check if a Python module is available on this system.
#'
#' Note that this function will also attempt to initialize Python
#' before checking if the requested module is available.
#'
#' @param module The name of the module.
#'
#' @return `TRUE` if the module is available and can be loaded;
#' `FALSE` otherwise.
#'
#' @export
py_module_available <- function(module) {
tryCatch({ import(module); TRUE }, error = clear_error_handler(FALSE))
}
#' Discover the version of Python to use with reticulate.
#'
#' This function enables callers to check which versions of Python will be
#' discovered on a system as well as which one will be chosen for use with
#' reticulate.
#'
#' The order of discovery is documented in `vignette("versions")`, also available online
#' [here](https://rstudio.github.io/reticulate/articles/versions.html#order-of-discovery)
#'
#' @param required_module A optional module name that will be used to select the
#' Python environment used.
#'
#' @param use_environment An optional virtual/conda environment name to prefer
#' in the search.
#'
#' @return Python configuration object.
#'
#' @export
py_discover_config <- function(required_module = NULL, use_environment = NULL) {
if (is.null(required_module) && length(.globals$delay_load_imports$module))
required_module <- .globals$delay_load_imports$module[[1L]]
if (!is.null(required_module))
required_module <- strsplit(required_module, ".", fixed = TRUE)[[1L]][[1L]]
# check if python symbols can already be found in the current process
main_process_info <- main_process_python_info()
if (!is.null(main_process_info)) {
python_version <- normalize_python_path(main_process_info$python)$path
try(return(python_config(python_version, required_module,
forced = "the current process")))
}
# if PYTHON_SESSION_INITIALIZED is specified then use it without scanning
# further (this is a "hard" requirement because an embedding process may
# set this to indicate that the python interpreter is already loaded)
py_session_initialized <- py_session_initialized_binary()
if (!is.null(py_session_initialized)) {
python_version <- normalize_python_path(py_session_initialized)$path
try(return(python_config(python_version, required_module,
forced = "PYTHON_SESSION_INITIALIZED")))
}
# if RETICULATE_PYTHON is specified then use it without scanning further
reticulate_env <- Sys.getenv("RETICULATE_PYTHON", unset = NA)
if (!is.na(reticulate_env)) {
python_version <- normalize_python_path(reticulate_env)
if (!python_version$exists)
stop("Python specified in RETICULATE_PYTHON (", reticulate_env, ") does not exist")
python_version <- python_version$path
try(return(python_config(python_version, required_module,
forced = "RETICULATE_PYTHON")))
}
# if RETICULATE_PYTHON_ENV is specified then use that
# can be a bare envname or a path
reticulate_python_env <- Sys.getenv("RETICULATE_PYTHON_ENV", unset = NA)
if (!is.na(reticulate_python_env)) {
# resolve the path to the environment directory
tryCatch({
python <- py_resolve(reticulate_python_env)
}, error = function(e) {
stop("Python specified in RETICULATE_PYTHON_ENV (", reticulate_python_env, ") does not exist")
})
try(return(python_config(python, required_module,
forced = "RETICULATE_PYTHON_ENV")))
}
# look for a required python version
# (e.g. use_python("/usr/bin/python", required = TRUE))
required_version <- .globals$required_python_version
if (!is.null(required_version)) {
python_version <- normalize_python_path(required_version)$path
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") {
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,
# then use the copy of Python associated with the poetry environment
config <- tryCatch(poetry_config(required_module), error = identity)
if (!inherits(config, "error") && !is.null(config))
return(config)
# if we're working within a project that contains a Pipfile, then
# use the copy of Python associated with that pipenv
config <- tryCatch(pipenv_config(required_module), error = identity)
if (!inherits(config, "error") && !is.null(config))
return(config)
# if the current directory contains a venv, use it:
for (dirpath in c("./venv", "./virtualenv", "./.venv", "./.virtualenv")) {
if (dir.exists(dirpath) && is_virtualenv(dirpath)) {
python <- virtualenv_python(dirpath)
try(return(python_config(
python, required_module,
forced = sprintf("'%s' existing in the current working directory", dirpath))))
}
}
# look for any environment names supplied in a call like:
# import("bar", delayed = list(environment = "r-barlyr"))
for (envname in c(use_environment, .globals$delay_load_imports$environment)) {
if (is.na(envname))
next
python <- tryCatch(py_resolve(envname), error = identity)
if (!inherits(python, "error"))
try(return(python_config(
python, required_module,
forced = sprintf('import("%s")', required_module)
)))
}
# check for `use_python(required = FALSE)`. This should rarely be triggered
# any more by users, since the default value for `required` changed from FALSE to TRUE.
# excepting if the use_*() call is within a `.onLoad()` call of a package.
# first call of use_*(,required = FALSE) wins
optional_requested_use_pythons <- reticulate_python_versions()
for (python in optional_requested_use_pythons) {
try(return(python_config(
python, required_module,
forced = "use_python(, required = FALSE)"
)))
}
# look in virtual environments that have a required module derived name,
# e.g., given a call to import("bar"), look for an environment named "r-bar"
for (module in c(required_module, .globals$delay_load_imports$module)) {
envname <- paste0("r-", module)
python <- tryCatch(py_resolve(envname), error = identity)
if (!inherits(python, "error"))
try(return(python_config(
python, required_module,
forced = sprintf('import("%s")', required_module)
)))
}
# if RETICULATE_PYTHON_FALLBACK is specified then use it
reticulate_env <- Sys.getenv("RETICULATE_PYTHON_FALLBACK", unset = NA)
if (!is.na(reticulate_env)) {
python_version <- normalize_python_path(reticulate_env)
if (!python_version$exists)
stop("Python specified in RETICULATE_PYTHON_FALLBACK (", reticulate_env, ") does not exist")
python_version <- python_version$path
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 a python environment that don't exist.
##
## In other words,
## - no use_python(), use_virtualenv(), use_condaenv()
## - no RETICULATE_PYTHON, RETICULATE_PYTHON_ENV, or RETICULATE_PYTHON_FALLBACK env vars
## - no existing venv in the current working directory named: venv .venv virtualenv or .virtualenv
## - no env named 'r-bar' if there was a call like `import('foo', delay_load = list(environment = "r-bar"))`
## - no env named 'r-foo' if there was a call like `import('foo')`
## - 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 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 = function(e) {
envpath <- try_create_default_virtualenv(package = "reticulate")
if (!is.null(envpath))
virtualenv_python(envpath)
else
e
})
if (!inherits(python, "error"))
try(return(python_config(python, required_module)))
# 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,
# for two reasons:
# - the default workflow should be to use venvs
# - which python is found should be predictable.
# create a list of possible python versions to bind to
python_versions <- unique(c(
Sys.which("python3"),
Sys.which("python")
))
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)
python_versions <- python_versions[file.exists(python_versions)]
if (is_windows()) {
# remove 'fake' / inaccessible python executables
# https://github.com/rstudio/reticulate/issues/534
info <- suppressWarnings(file.info(python_versions))
size <- ifelse(is.na(info$size), 0, info$size)
python_versions <- python_versions[size != 0]
# remove msys2 / cygwin python executables.
# path translation going to and from msys2 currently not implemented.
# E.g.: "C:\foo\bar" -> "/c/foo/bar" and "/foo/bar" -> "C:\rtools43\foo\bar"
# https://github.com/rstudio/reticulate/issues/1325
python_sys_platforms <- vapply(
python_versions, system2, "",
args = c("-c", shQuote("import sys; print(sys.platform)")),
stdout = TRUE)
python_versions <- python_versions[python_sys_platforms != "cygwin"]
}
# scan until we find a version of python that meets our qualifying conditions
valid_python_versions <- c()
for (python_version in python_versions) {
# get the config
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)
has_python_gte_36 <- as.numeric_version(config$version) >= "3.6"
has_compatible_arch <- !is_incompatible_arch(config)
has_preferred_numpy <- !is.null(config$numpy) && config$numpy$version >= "1.6"
if (has_compatible_arch && has_preferred_numpy)
append(valid_python_versions) <- python_version
has_required_module <- is.null(config$required_module) || !is.null(config$required_module_path)
if (has_python_gte_36 && has_compatible_arch && has_preferred_numpy && has_required_module)
return(config)
}
# no preferred found, return first with valid config if we have it or NULL
if (length(valid_python_versions) > 0)
try(return(python_config(valid_python_versions[[1]], required_module, python_versions)))
else if (length(python_versions) > 0)
try(return(python_config(python_versions[[1]], required_module, python_versions)))
else
return(NULL)
}
py_discover_config_fallbacks <- function() {
# prefer conda python if available
conda <- find_conda()[[1L]]
if (!is.null(conda) && file.exists(conda)) {
pythons <- tryCatch(
conda_python(envname = "base", conda = conda, all = TRUE),
error = identity
)
if (is.character(pythons))
return(pythons)
}
# on Windows, try looking in the registry
if (is_windows())
return(py_versions_windows()$executable_path)
# otherwise, just search some default locations
prefixes <- c(
"/opt/local/python",
"/opt/python",
"/usr/local",
"/usr"
)
suffixes <- c("bin/python3", "bin/python")
grid <- expand.grid(
prefix = prefixes,
suffix = suffixes,
KEEP.OUT.ATTRS = FALSE,
stringsAsFactors = FALSE
)
paste(grid$prefix, grid$suffix, sep = "/")
}
try_create_default_virtualenv <- function(package = "reticulate", ...) {
# If the environment already exists, use it
envname <- paste0("r-", package)
if (virtualenv_exists(envname))
return(virtualenv_python(envname))
if (!isTRUE(getOption("reticulate.python.initializing")))
return(NULL)
# if we're in a recursive call, return NULL (we've already asked.)
# py_discover_config() -> try_create_default_virtualenv() ->
# virtualenv_create() -> virtualenv_starter() -> py_exe() ->
# py_discover_config() -> try_create_default_virtualenv()
for(cl in sys.calls()[-length(sys.calls())])
if (identical(cl[[1L]], quote(try_create_default_virtualenv)))
return(NULL)
permission <- tolower(Sys.getenv("RETICULATE_AUTOCREATE_PACKAGE_VENV", ""))
if (permission %in% c("false", "0", "no"))
return(NULL)
if (permission == "") {
if (is_interactive()) {
permission <- utils::askYesNo(sprintf(
"Would you like to create a default Python environment for the %s package?",
package))
if (!isTRUE(permission))
return(NULL)
permission <- "true"
}
}
if (!permission %in% c("true", "yes", "1"))
return(NULL)
virtualenv_create(
envname = envname,
...
)
}
#' Discover versions of Python installed on a Windows system
#'
#' @return Data frame with `type`, `hive`, `install_path`, `executable_path`,
#' and `version`.
#'
#' @keywords internal
#' @export
py_versions_windows <- function() {
rbind(
read_python_versions_from_registry("HCU", key = "PythonCore"),
read_python_versions_from_registry("HLM", key = "PythonCore"),
windows_registry_anaconda_versions()
)
}
python_virtualenv_versions <- function() {
home <- virtualenv_root()
bins <- python_environments(home)
data.frame(
name = basename(dirname(dirname(bins))),
python = bins,
stringsAsFactors = FALSE
)
}
python_conda_versions <- function() {
if (is_windows()) {
# list all conda environments
conda_envs <- data.frame(name = character(),
python = character(),
stringsAsFactors = FALSE)
registry_versions <- py_versions_windows()
anaconda_registry_versions <- subset(registry_versions, registry_versions$type == "Anaconda")
for (conda in file.path(anaconda_registry_versions$install_path, "Scripts", "conda.exe")) {
conda_envs <- rbind(conda_envs, conda_list(conda = conda))
}
conda_envs
} else {
env_dirs <- c("~/anaconda/envs",
"~/anaconda2/envs",
"~/anaconda3/envs",
"~/anaconda4/envs",
"~/miniconda/envs",
"~/miniconda2/envs",
"~/miniconda3/envs",
"~/miniconda4/envs",
"/anaconda/envs",
"/anaconda2/envs",
"/anaconda3/envs",
"/anaconda4/envs",
"/miniconda/envs",
"/miniconda2/envs",
"/miniconda3/envs",
"/miniconda4/envs",
"~/opt/anaconda/envs",
"~/opt/anaconda2/envs",
"~/opt/anaconda3/envs",
"~/opt/anaconda4/envs",
"~")
python_env_binaries <- python_environments(env_dirs)
data.frame(name = basename(dirname(dirname(python_env_binaries))),
python = python_env_binaries,
stringsAsFactors = FALSE)
}
}
python_environments <- function(env_dirs, required_module = NULL) {
# filter env_dirs by existence
env_dirs <- env_dirs[utils::file_test("-d", env_dirs)]
# envs to return
envs <- character()
# python bin differs by platform
python_bin <- ifelse(is_windows(), "python.exe", "bin/python")
for (env_dir in env_dirs) {
# filter by required module if requested
if (!is.null(required_module)) {
module_envs <- c(paste0("r-", required_module), required_module)
envs <- c(envs, path.expand(sprintf("%s/%s/%s", env_dir, module_envs, python_bin)))
# otherwise return all
} else {
envs <- c(envs, path.expand(sprintf("%s/%s",
list.dirs(env_dir, recursive = FALSE),
python_bin)))
}
}
# filter by existence
if (length(envs) > 0)
envs[file.exists(envs)]
else
envs
}
python_munge_path <- function(python) {
# add the python bin dir to the PATH (so that any execution of python from
# within the interpreter, from a system call, or from within a terminal
# hosted within the front end will use the same version of python.
#
# we do this up-front in python_config as otherwise attempts to discover
# and load numpy can fail, especially on Windows
# https://github.com/rstudio/reticulate/issues/367
python_home <- dirname(python)
python_dirs <- c(normalizePath(python_home))
# fix rpath for anaconda libmkl
if (is_osx()) {
libmkl <- file.path(python_home, "../lib/libmkl_intel_thread.dylib")
if (file.exists(libmkl)) {
libmkl <- normalizePath(libmkl)
args <- c("-add_rpath", shQuote(dirname(libmkl)), libmkl)
system2("install_name_tool", args, stdout = FALSE, stderr = FALSE)
}
}
if (is_conda_python(python)) {
conda_info <- get_python_conda_info(python)
new_path <- conda_run2(
"python",
c("-c", shQuote("import os; print(os.environ['PATH'])")),
conda = conda_info$conda,
envname = conda_info$root,
intern = TRUE
)
# maybe discard unsilenceable warnings, see issue #1303
new_path <- new_path[length(new_path)]
old_path <- Sys.getenv("PATH")
Sys.setenv("PATH" = new_path)
return(old_path)
}
if (is_windows()) {
# include the Scripts path, as well
python_scripts <- file.path(python_home, "Scripts")
if (file.exists(python_scripts))
python_dirs <- c(python_dirs, normalizePath(python_scripts))
# we saw some crashes occurring when Python modules attempted to load
# dynamic libraries at runtime; e.g.
#
# Intel MKL FATAL ERROR: Cannot load mkl_intel_thread.dll
#
# we work around this by putting the associated binary directory
# on the PATH so it can be successfully resolved
python_library_bin <- file.path(python_home, "Library/bin")
if (file.exists(python_library_bin))
python_dirs <- c(python_dirs, normalizePath(python_library_bin))
}
path_prepend(python_dirs)
}
python_config_impl <- function(python) {
if(!file.exists(python)) {
# Test if `python` is broken symlink, which can happen with a venv if the
# venv starter is moved/removed
msg <- paste0("Error running ", shQuote(python), ": No such file.")
info <- python_info(python)
if (info$type == "virtualenv") {
msg <- paste0(sep = "", c(msg, "\n",
"The Python installation used to create the virtualenv has been moved or removed",
if(is.null(info$starter)) "." else ":\n ", shQuote(info$starter)
))
}
stop(msg)
}
script <- system.file("config/config.py", package = "reticulate")
config <- tryCatch(system2(
command = python,
args = shQuote(script),
stdout = TRUE,
stderr = FALSE
), error = function(e) {
e$message <- paste(e$message, shQuote(python))
stop(e)
})
# check for error
status <- attr(config, "status")
if (!is.null(status)) {
errmsg <- attr(config, "errmsg")
stop("Error ", status, " occurred running ", python, ": ", errmsg)
}
# on macOS, if we see some variables referencing /Applications/Xcode.app
# but that doesn't actually exist, then redirect to default CLT
if (is_osx()) {
clt <- "/Library/Developer/CommandLineTools"
xcode <- "/Applications/Xcode.app/Contents/Developer"
if (file.exists(clt))
config <- gsub(xcode, clt, config, fixed = TRUE)
}
# return config
config
}
python_config <- function(python,
required_module = NULL,
python_versions = python,
forced = NULL)
{
# normalize and remove duplicates
python <- canonical_path(python)
python_versions <- canonical_path(python_versions)
python_versions <- unique(python_versions)
# update and restore PATH when done
oldpath <- python_munge_path(python)
on.exit(Sys.setenv(PATH = oldpath), add = TRUE)
# set LD_LIBRARY_PATH on Linux as well, just to make sure Python libraries
# can be resolved if necessary (also need to guard against users who munge
# LD_LIBRARY_PATH in a way that breaks dynamic lookup of Python libraries)
if (is_linux()) {
libpath <- file.path(dirname(dirname(python)), "lib")
if (file.exists(libpath)) {
oldlibpath <- Sys.getenv("LD_LIBRARY_PATH", unset = NA)
newlibpath <- paste(libpath, oldlibpath, sep = ":")
Sys.setenv(LD_LIBRARY_PATH = newlibpath)
on.exit({
if (is.na(oldlibpath))
Sys.unsetenv("LD_LIBRARY_PATH")
else
Sys.setenv(LD_LIBRARY_PATH = oldlibpath)
}, add = TRUE)
}
}
# collect configuration information
if (!is.null(required_module)) {
Sys.setenv(RETICULATE_REQUIRED_MODULE = required_module)
on.exit(Sys.unsetenv("RETICULATE_REQUIRED_MODULE"), add = TRUE)
}
# execute config script
config <- python_config_impl(python)
# read output as dcf
config_connection <- textConnection(config)
on.exit(close(config_connection), add = TRUE)
config <- read.dcf(config_connection, all = TRUE)
# get the full textual version and the numeric version, check for anaconda
version_string <- config$Version
version <- config$VersionNumber
anaconda <- grepl("anaconda|continuum", version_string, ignore.case = TRUE)
architecture <- config$Architecture
# determine the location of libpython
# see also: https://github.com/JuliaPy/PyCall.jl/blob/master/deps/build.jl
main_process_info <- main_process_python_info()
if (!is.null(main_process_info)) {
# either we have the main process libpython, or NA in case of PIE executable
libpython <- main_process_info$libpython
} else if (is_windows()) {
# construct DLL name
dll <- sprintf("python%s.dll", gsub(".", "", version, fixed = TRUE))
# default to just using dll as libpython path (implies lookup on PATH)
libpython <- dll
# search for python DLL in one of the declared prefixes
roots <- c(
dirname(python),
config$Prefix,
config$ExecPrefix,
config$BaseExecPrefix
)
for (root in roots) {
candidate <- file.path(root, dll)
if (file.exists(candidate)) {
libpython <- canonical_path(candidate)
break
}
}
} else {
# default to NULL
libpython <- NULL
# check multiple library directories
# (necessary for virtualenvs that don't copy over the shared library)
libsrcs <- c("LIBPL", "LIBDIR", "Prefix", "ExecPrefix", "BaseExecPrefix")
for (libsrc in libsrcs) {
# skip null entries in config
src <- config[[libsrc]]
if (is.null(src))
next
# get appropriate libpython extension for platform
ext <- switch(
Sys.info()[["sysname"]],
Darwin = ".dylib",
Windows = ".dll",
".so"
)
# try to resolve libpython in this location
pattern <- sprintf("^libpython%sd?m?%s", version, ext)
candidates <- list.files(src, pattern = pattern, full.names = TRUE)
if (length(candidates)) {
libpython <- candidates
break
}
}
}
# determine PYTHONHOME
pythonhome <- NULL
if (!is.null(config$Prefix)) {
pythonhome <- canonical_path(config$Prefix)
if (!is_windows()) {
exec_prefix <- canonical_path(config$ExecPrefix)
pythonhome <- paste(pythonhome, exec_prefix, sep = ":")
}
}
as_numeric_version <- function(version) {
version <- clean_version(version)
numeric_version(version)
}
# check for numpy
numpy <- NULL
if (!is.null(config$NumpyPath)) {
numpy <- list(
path = canonical_path(config$NumpyPath),
version = as_numeric_version(config$NumpyVersion)
)
}
# check to see if this is a Python virtualenv
root <- dirname(dirname(python))
virtualenv <- if (is_virtualenv(root))
root
else
""
# check for virtualenv activate script
activate_this <- file.path(dirname(python), "activate_this.py")
if (file.exists(activate_this))
virtualenv_activate <- activate_this
else
virtualenv_activate <- ""
# check for required module
required_module_path <- config$RequiredModulePath
# return config info
info <- list(
python = python,
libpython = libpython[1],
pythonhome = pythonhome,
pythonpath = config$PythonPath,
prefix = config$Prefix,
exec_prefix = config$ExecPrefix,
base_exec_prefix = config$BaseExecPrefix,
virtualenv = virtualenv,
virtualenv_activate = virtualenv_activate,
executable = config$Executable, # sys.executable
base_executable = config$BaseExecutable, # sys._base_executable; exe for venv starter
version_string = version_string,
version = version,
architecture = architecture,
anaconda = anaconda,
conda = config$IsConda,
numpy = numpy,
required_module = required_module,
required_module_path = required_module_path,
available = FALSE,
python_versions = python_versions,
forced = forced
)
class(info) <- "py_config"
info
}
#' @export
str.py_config <- function(object, ...) {
NextMethod()
}
#' @export
format.py_config <- function(x, ...) {
out <- ""
out <- paste0(out, "python: ", x$python, "\n")
out <- paste0(out, "libpython: ", ifelse(is.null(x$libpython), "[NOT FOUND]", x$libpython), ifelse(is_windows() || is.null(x$libpython) || is.na(x$libpython) || file.exists(x$libpython), "", "[NOT FOUND]"), "\n")
out <- paste0(out, "pythonhome: ", ifelse(is.null(x$pythonhome), "[NOT FOUND]", x$pythonhome), "\n")
if (nzchar(x$virtualenv_activate))
out <- paste0(out, "virtualenv: ", x$virtualenv_activate, "\n")
out <- paste0(out, "version: ", x$version_string, "\n")
if (is_windows())
out <- paste0(out, "Architecture: ", x$architecture, "\n")
if (!is.null(x$numpy)) {
out <- paste0(out, "numpy: ", x$numpy$path, "\n")
out <- paste0(out, "numpy_version: ", as.character(x$numpy$version), "\n")
} else {
out <- paste0(out, "numpy: [NOT FOUND]\n")
}
if (!is.null(x$required_module)) {
out <- paste0(out, sprintf("%-16s", paste0(x$required_module, ":")))
if (!is.null(x$required_module_path))
out <- paste0(out, x$required_module_path, "\n")
else
out <- paste0(out, "[NOT FOUND]\n")
}
if (!is.null(x$forced)) {
out <- paste0(out, "\nNOTE: Python version was forced by ", x$forced, "\n")
}
if (length(x$python_versions) > 1) {
out <- paste0(out, "\npython versions found: \n")
python_versions <- paste0(" ", x$python_versions, collapse = "\n")
out <- paste0(out, python_versions, sep = "\n")
}
out
}
#' @export
print.py_config <- function(x, ...) {
cat(format(x))
}
is_windows <- function() {
identical(.Platform$OS.type, "windows")
}
is_unix <- function() {
identical(.Platform$OS.type, "unix")
}
is_osx <- function() {
Sys.info()["sysname"] == "Darwin"
}
is_macos <- is_osx
is_linux <- function() {
identical(tolower(Sys.info()[["sysname"]]), "linux")
}
is_ubuntu <- function() {
# check /etc/lsb-release
if (is_unix() && file.exists("/etc/lsb-release")) {
lsbRelease <- readLines("/etc/lsb-release")
any(grepl("Ubuntu", lsbRelease))
} else {
FALSE
}
}
is_fedora <- function() {
if (is_unix() && file.exists("/etc/os-release")) {
os_info <- readLines("/etc/os-release")
any(grepl("Fedora", os_info))
} else {
FALSE
}
}
is_rstudio <- function() {
exists("RStudio.Version", envir = globalenv())
}
is_rstudio_desktop <- function() {
if (!exists("RStudio.Version", envir = globalenv()))
return(FALSE)
RStudio.Version <- get("RStudio.Version", envir = globalenv())
version <- RStudio.Version()
identical(version$mode, "desktop")
}
is_positron <- function() {
exists(".ps.ark.version", envir = globalenv())
}
clean_version <- function(version) {
gsub("\\.$", "", gsub("[A-Za-z_+].*$", "", version))
}
reticulate_python_versions <- function() {
# python versions to return
python_versions <- c()
# get versions specified via use_* functions
reticulate_python_options <- .globals$use_python_versions
# determine python versions to return
if (length(reticulate_python_options) > 0) {
for (i in 1:length(reticulate_python_options)) {
python <- normalize_python_path(reticulate_python_options[[i]])
if (python$exists)
python_versions <- c(python_versions, python$path)
}
}
# return them
python_versions
}
normalize_python_path <- function(python) {
# normalize trailing slash and expand
python <- gsub("[\\/]+$", "", python)
python <- path.expand(python)
# check for existence
if (!utils::file_test("-d", python) &&
!utils::file_test("-f", python)) {
list(
path = python,
exists = FALSE
)
} else {
# append binary if it's a directory
if (utils::file_test("-d", python))
python <- file.path(python, "python")
# append .exe if necessary on windows
# accept .bat for pyenv-win shim
if (is_windows() && (!grepl("^.*\\.(exe|bat)$", tolower(python))))
python <- paste0(python, ".exe")
# return
list(
path = python,
exists = TRUE
)
}
}
windows_registry_anaconda_versions <- function() {
rbind(read_python_versions_from_registry("HCU", key = "ContinuumAnalytics", type = "Anaconda"),
read_python_versions_from_registry("HLM", key = "ContinuumAnalytics", type = "Anaconda"))
}
read_python_versions_from_registry <- function(hive, key, type=key) {
python_core_key <- tryCatch(utils::readRegistry(
key = paste0("SOFTWARE\\Python\\", key), hive = hive, maxdepth = 3),
error = function(e) NULL)
types <- c()
hives <- c()
install_paths <- c()
executable_paths <- c()
versions <- c()
archs <- c()
if (length(python_core_key) > 0) {
for (version in names(python_core_key)) {
version_key <- python_core_key[[version]]
if (is.list(version_key) && !is.null(version_key$InstallPath)) {
version_dir <- version_key$InstallPath$`(Default)`
if (!is.null(version_dir) && utils::file_test("-d", version_dir)) {
# determine install_path and executable_path
install_path <- version_dir
executable_path <- file.path(install_path, "python.exe")
# proceed if it exists
if (file.exists(executable_path)) {
# determine version and arch
if (type == "Anaconda") {
matches <- regexec("^Anaconda.*(32|64).*$", version)
matches <- regmatches(version, matches)[[1]]
if (length(matches) == 2) {
version <- version_key$SysVersion
arch <- matches[[2]]
} else {
warning("Unexpected format for Anaconda version: ", version,
"\n(Please install a more recent version of Anaconda)")
arch <- NA
}
} else { # type == "PythonCore"
matches <- regexec("^(\\d+)\\.(\\d+)(?:-(32|64))?$", version)
matches <- regmatches(version, matches)[[1]]
if (length(matches) == 4) {
version <- paste(matches[[2]], matches[[3]], sep = ".")
arch <- matches[[4]]
if (!nzchar(arch)) {
if (numeric_version(version) >= "3.0")
arch <- "64"
else {
python_arch <- python_arch(executable_path)
arch <- gsub("bit", "", python_arch, fixed = TRUE)
}
}
} else {
warning("Unexpected format for PythonCore version: ", version)
arch <- NA
}
}
if (!is.na(arch)) {
# convert to R arch
if (arch == "32")
arch <- "i386"
else if (arch == "64")
arch <- "x64"
# append to vectors
types <- c(types, type)
hives <- c(hives, hive)
install_paths <- c(install_paths, utils::shortPathName(install_path))
executable_paths <- c(executable_paths, utils::shortPathName(executable_path))
versions <- c(versions, version)
archs <- c(archs, arch)
}
}
}
}
}
}
data.frame(
type = types,
hive = hives,
install_path = install_paths,
executable_path = executable_paths,
version = versions,
arch = archs,
stringsAsFactors = FALSE
)
}
# get the architecture from a python binary
python_arch <- function(python) {
# run command
result <- system2(python, stdout = TRUE, args = c("-c", shQuote(
"import sys; import platform; sys.stdout.write(platform.architecture()[0])")))
# check for error
error_status <- attr(result, "status")
if (!is.null(error_status))
stop("Error ", error_status, " occurred while checking for python architecture", call. = FALSE)
# return arch
result
}
# convert R arch to python arch
current_python_arch <- function() {
if (.Platform$r_arch == "i386")
"32bit"
else if (.Platform$r_arch == "x64")
"64bit"
else
"Unknown"
}
# check for compatible architecture
is_incompatible_arch <- function(config) {
if (is_windows()) {
!identical(current_python_arch(),config$architecture)
} else {
FALSE
}
}
py_session_initialized_binary <- function() {
# binary to return
python_binary <- NULL
# check environment variable
py_session <- Sys.getenv("PYTHON_SESSION_INITIALIZED", unset = NA)
if (!is.na(py_session)) {
py_session <- strsplit(py_session, ":", fixed = TRUE)[[1]]
py_session <- strsplit(py_session, "=", fixed = TRUE)
keys <- character()
py_session <- lapply(py_session, function(x) {
keys <<- c(keys, x[[1]])
x[[2]]
})
if (all(c("current_pid", "sys.executable") %in% keys)) {
names(py_session) <- keys
# verify it's from the current process
if (identical(as.character(Sys.getpid()), py_session$current_pid)) {
python_binary <- py_session$sys.executable
}
} else {
warning("PYTHON_SESSION_INITIALIZED does not include current_pid and sys.executable",
call. = FALSE)
}
}
# return
python_binary
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.