Skip to content

Commit

Permalink
Merge pull request #118 from bodkan/delayed-py-env
Browse files Browse the repository at this point in the history
Do not automatically activate slendr Python virtual environment upon loading the package
  • Loading branch information
bodkan committed Jan 16, 2023
2 parents dd34bd2 + e65e778 commit 13133ed
Show file tree
Hide file tree
Showing 200 changed files with 18,000 additions and 10,605 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ Imports:
ijtiff,
shinyWidgets,
shiny,
rgdal,
ape
Suggests:
testthat (>= 3.0.0),
knitr,
rmarkdown,
admixr,
units,
rgdal,
magick,
cowplot,
forcats,
Expand Down
12 changes: 6 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
.PHONY: build vignettes docs
.PHONY: build docs website

version := $(shell less DESCRIPTION | grep 'Version' | sed 's/Version: \(.*\)$$/\1/')
pkg := build/slendr_$(version).tar.gz
logo := man/figures/logo.png

docs:
rm -rf docs/reference
R -e 'devtools::install(upgrade = "never")'
R -e 'devtools::document()'
R -e 'pkgdown::build_reference()'
R -e 'pkgdown::build_reference_index()'
Expand All @@ -17,13 +17,13 @@ docs:
#git restore docs/reference/world.html
#git restore docs/reference/expand_range-1.png

website: $(logo)
rm -rf docs/
website: $(logo) README.md
R -e 'devtools::install(upgrade = "never")'
R -e 'knitr::knit("README.Rmd", output = "README.md")'
R -e 'devtools::document()'
R -e 'pkgdown::build_reference()'
R -e 'pkgdown::build_reference_index()'
R -e 'pkgdown::build_news()'
R -e 'pkgdown::build_site()'
git restore docs/CNAME
# discard useless updates of temporary paths, random seed values, etc.
#git restore docs/pkgdown.yml
#git restore docs/reference/join.html
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export(distance)
export(expand_range)
export(explore_model)
export(gene_flow)
export(init_env)
export(join)
export(move)
export(msprime)
Expand Down
12 changes: 12 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# slendr (development version)

- **<u>Minor breaking change!</u> Python environments of _slendr_ are no longer automatically activated upon calling `library(slendr)`! Using the coalescent _msprime_ back end and _slendr_'s tree-sequence functions now requires making an explicit call to a new function `init_env()` after `library(slendr)` is executed.** (PR [#102](https://github.com/bodkan/slendr/pull/118))

Motivation for the change: A small proportion of users have been experiencing issues with broken conda environments and various other issues with Python virtual environments in general. It's hard to guess how frequent this has been, but experience from workshops and courses suggests perhaps 1 in 20 of users experiencing Python issues which hindered their ability to use _slendr_ .(Fun fact: the first user-submitted GitHub issue upon releasing the first version of the _slendr_ R package was... a Python virtual environment issue).

Explanation: Activating Python environments automatically upon calling `library(slendr)` has been a popular feature because it hid away most of the complexities of the R-Python interface that powers _slendr_'s tree-sequence functionality. This was particularly convenient for many _slendr_ users, particularly those who have no experience with Python at all.

Unfortunately, in cases where a Python virtual environments with tskit/msprime/pyslim on a user's system ended up corrupted (or if anything else at the Python level got broken), the automatic Python environment activation performed by the `library(slendr)` call failed and _slendr_ was not even loaded. Sadly, this completely pulled the rug from under _slendr_ and there was nothing that could be done about it from its perspective (the issue happened at a low-level layer of embedded-Python before _slendr_ could've been loaded into R). Solving these issues was not difficult for experienced users, but many _slendr_ users have no experience with Python at all, they have never used conda, they don't understand the concept of "Python virtual environments" or how the R-Python interface works. And nor should they! After all, _slendr_ is an R package.

Splitting the Python virtual environment activation step into its own `init_env()` function means that `library(slendr)` now always succeeds (regardless of potential underlying Python issues on a user's sytem), making it much easier to diagnose and fix Python problems from R once the package is loaded.

So, to recap: `library(slendr)` no longer activates _slendr_'s isolated Python virtual environment. In order to simulate tree sequences and analyse them using its interface to _tskit_, it is necessary to call `init_env()`. This function performs the same Python-activation steps that `library(slendr)` used to call automagically in earlier _slendr_ versions. No other change to your scripts is necessary.

- When a named list is provided as a `sample_sets =` argument to a oneway statistic function, the names are used in a `set` column of the resulting data frame even if only single samples were used. ([#2a6781](https://github.com/bodkan/slendr/commit/2a6781))

- It is now possible to label groups of samples in _slendr_'s _tskit_ interface functions which should make data frames with statistics results more readable. As an example, running `ts_f3(ts, A = c("p1_1", "p1_2", "p1_3"), B = c("p2_1", "p2_3"), C = c("p3_1", "p3_2", "p3_"))` resulted in a following data-frame output:
Expand Down
6 changes: 6 additions & 0 deletions R/compilation.R
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ setting `direction = 'backward'.`", call. = FALSE)
#' @examples
#' \dontshow{check_dependencies(python = TRUE) # make sure dependencies are present
#' }
#' init_env()
#'
#' # load an example model with an already simulated tree sequence
#' path <- system.file("extdata/models/introgression", package = "slendr")
#' model <- read_model(path)
Expand Down Expand Up @@ -347,6 +349,8 @@ read_model <- function(path) {
#' @examples
#' \dontshow{check_dependencies(python = TRUE, slim = TRUE) # make sure dependencies are present
#' }
#' init_env()
#'
#' # load an example model
#' model <- read_model(path = system.file("extdata/models/introgression", package = "slendr"))
#'
Expand Down Expand Up @@ -557,6 +561,8 @@ slim <- function(
#' @examples
#' \dontshow{check_dependencies(python = TRUE) # make sure dependencies are present
#' }
#' init_env()
#'
#' # load an example model
#' model <- read_model(path = system.file("extdata/models/introgression", package = "slendr"))
#'
Expand Down
81 changes: 57 additions & 24 deletions R/interface.R
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,8 @@ area <- function(x) {
#' @examples
#' \dontshow{check_dependencies(python = TRUE) # make sure dependencies are present
#' }
#' init_env()
#'
#' # load an example model with an already simulated tree sequence
#' path <- system.file("extdata/models/introgression", package = "slendr")
#' model <- read_model(path)
Expand Down Expand Up @@ -1238,6 +1240,44 @@ schedule_sampling <- function(model, times, ..., locations = NULL, strict = FALS
schedule
}

#' Activate slendr's own dedicated Python environment
#'
#' This function attempts to activate a dedicated slendr Miniconda Python
#' environment previously set up via \code{setup_env}.
#'
#' @param quiet Should informative messages be printed to the console? Default
#' is \code{FALSE}.
#'
#' @return No return value, called for side effects
#'
#' @export
init_env <- function(quiet = FALSE) {
if (!is_slendr_env_present())
stop("Could not activate slendr's Python environment because it is not\npresent ",
"on your system ('", PYTHON_ENV, "').\n\n",
"To set up a dedicated Python environment you first need to run setup_env().", call. = FALSE)
else {
reticulate::use_condaenv(PYTHON_ENV, required = TRUE)
if (!reticulate::py_module_available("msprime") ||
!reticulate::py_module_available("tskit") ||
!reticulate::py_module_available("pyslim")) {
stop("Python environment ", PYTHON_ENV, " has been found but it",
" does not appear to have msprime, tskit and pyslim modules all",
" installed. Perhaps the environment got corrupted somehow?",
" Running `clear_env()` and `setup_env()` to reset the slendr's Python",
" environment is recommended.", call. = FALSE)
} else {
# pylib <<- reticulate::import_from_path(
# "pylib",
# path = system.file("python", package = "slendr"),
# delay_load = TRUE
# )
if (!quiet)
message("The interface to all required Python modules has been activated.")
}
}
}

#' Setup a dedicated Python virtual environment for slendr
#'
#' This function will automatically download a Python miniconda distribution
Expand All @@ -1260,41 +1300,29 @@ schedule_sampling <- function(model, times, ..., locations = NULL, strict = FALS
#' @export
setup_env <- function(quiet = FALSE, agree = FALSE, pip = NULL) {
if (is_slendr_env_present()) {
reticulate::use_condaenv(PYTHON_ENV, required = TRUE)
if (!reticulate::py_module_available("msprime") ||
!reticulate::py_module_available("tskit") ||
!reticulate::py_module_available("pyslim")) {
stop("Python environment ", PYTHON_ENV, " has been found but it",
" does not appear to have msprime, tskit and pyslim modules all",
" installed. Perhaps the environment got corrupted somehow?",
" Running `clear_env()` and `setup_env()` to reset the slendr's Python",
" environment is recommended.")
} else if (!quiet)
message("The interface to all required Python modules has been activated.")
message("A required slendr Python environment is already present. You can activate\n",
"it by calling init_env().")
} else {
if (agree)
answer <- 2
else
answer <- utils::menu(
c("No", "Yes"),
title = paste0(
"No pre-configured Python environment for slendr has been found.\n\n",
"Do you wish to install a completely isolated Miniconda Python distribution\n",
"just for slendr and create an isolated environment with all required Python\n",
"modules automatically?\n",
"\nNo need to worry, everything will be installed into a completely\n",
"separate location into an isolated environment in an R library directory.\n",
"This won't affect your other Python installations at all, whatever those\n",
"might be (standard Python installations or conda setups). You can always\n",
"wipe out the automatically created environment by running `clear_env()`.\n\n",
"This function will install a completely isolated Miniconda Python distribution\n",
"just for slendr and create an environment with all required Python modules.\n",
"\nEverything will be installed into a completely separate location into an\n",
"isolated environment in an R library directory. This won't affect your other\n",
"Python installations at all. You can always wipe out the automatically created\n",
"environment by running clear_env().\n\n",
"Do you wish to proceed with the automated Python environment setup?")
)
if (answer == 2) {
message("============================================================")
message("=======================================================================")
message("Installing slendr's Python environment. Please wait until")
message("the installation procedure finishes. Do NOT interrupt the")
message("process while the installation is still running.")
message("============================================================\n")
message("======================================================================\n")
Sys.sleep(10)

if (!dir.exists(reticulate::miniconda_path()))
Expand All @@ -1318,9 +1346,13 @@ setup_env <- function(quiet = FALSE, agree = FALSE, pip = NULL) {

reticulate::conda_install(envname = PYTHON_ENV, packages = deps, pip = pip)

if (!quiet)
if (!quiet) {
message("======================================================================")
message("Python environment for slendr has been successfuly created, and ",
"the R\ninterface to msprime, tskit, and pyslim modules has been activated.")
"the R\ninterface to msprime, tskit, and pyslim modules has been activated.\n")
message("In future sessions, activate this environment by calling init_env().")
message("=======================================================================")
}
} else
warning("Your Python environment is not set up correctly which means that the tree\n",
"sequence functionality of slendr will not work.", call. = FALSE)
Expand Down Expand Up @@ -1371,6 +1403,7 @@ clear_env <- function(force = FALSE) {
#' @examples
#' \dontshow{check_dependencies(python = TRUE) # make sure dependencies are present
#' }
#' init_env()
#' check_env()
#' @export
check_env <- function(verbose = TRUE) {
Expand Down
Loading

0 comments on commit 13133ed

Please sign in to comment.