tests/testthat/test-python-envs.R

context("envs")

python_is_free_threaded_via_vv <- function(python) {
  output <- tryCatch(
    system2(python, c("-E", "-VV"), stdout = TRUE, stderr = TRUE),
    error = identity
  )
  if (inherits(output, "error"))
    return(FALSE)

  status <- attr(output, "status") %||% 0L
  if (status != 0L || !length(output))
    return(FALSE)

  any(grepl("free-thread(?:ed|ing) build", output, ignore.case = TRUE))
}

test_that("conda utility functions work as expected", {
  # TODO: reenable these tests
  skip_if_no_test_environments()

  binary <- conda_binary()
  expect_type(binary, "character")
  expect_length(binary, 1)

  conda_remove('reticulate-testthat')
  conda_create('reticulate-testthat')
  expect_true('reticulate-testthat' %in% conda_list()$name)

  conda_install('reticulate-testthat', 'Pillow')
  conda_remove('reticulate-testthat', 'Pillow')

  conda_remove('reticulate-testthat')
  expect_false('reticulate-testthat' %in% conda_list()$name)

  conda_create('reticulate-testthat', forge = TRUE)
  expect_true(all(grepl("conda-forge", conda_list_packages("reticulate-testthat")$channel)))
  conda_remove('reticulate-testthat')

  conda_create('reticulate-testthat', channel = c("anaconda"))
  expect_true(all(grepl("anaconda", conda_list_packages("reticulate-testthat")$channel)))
  conda_remove('reticulate-testthat')

})

test_that("virtualenv utility functions work as expected", {
  skip_if_no_test_environments()

  expect_error(
    virtualenv_remove('reticulate-testthat', confirm = FALSE),
    'Virtual environment \'reticulate-testthat\' does not exist.'
  )

  virtualenv_create('reticulate-testthat')
  virtualenv_remove('reticulate-testthat', confirm = FALSE)

  virtualenv_install('reticulate-testthat', 'Pillow')
  virtualenv_install('reticulate-testthat', 'Pillow', ignore_installed = TRUE)

  expect_true('reticulate-testthat' %in% virtualenv_list())

  virtualenv_remove('reticulate-testthat', confirm = FALSE)

  expect_false('reticulate-testthat' %in% virtualenv_list())

})

test_that("py_list_packages() works for virtualenv-created environments when uv is installed", {
  skip_if_no_test_environments()
  skip_if(is.null(uv_binary(bootstrap_install = FALSE)), "uv not installed")

  envname <- tempfile("reticulate-py-list-packages-venv")
  on.exit(virtualenv_remove(envname, confirm = FALSE), add = TRUE)

  virtualenv_create(envname)
  virtualenv_install(envname, "numpy")

  pkgs <- py_list_packages(python = virtualenv_python(envname))

  expect_true(all(c("numpy", "pip") %in% pkgs$package))
})

test_that("Python version checker support a 'x.x.*' pattern", {
  check <- as_version_constraint_checkers("==3.12.*")
  expect_false(check[[1]]("3.9"))
  expect_true(check[[1]]("3.12"))
})

test_that("Python version checker returns expected error message", {
  check <- as_version_constraint_checkers("==3.12.-")
  expect_error(check[[1]]("3.12"), "Version `==3.12.-` is not valid.")
})

test_that("Python version checker does not support pattern 'x.*.x'", {
  check <- as_version_constraint_checkers(">=3.*.11")
  expect_false(check[[1]]("3.12"))
  expect_false(check[[1]]("4.1"))
})

test_that("python_is_free_threaded matches interpreter config", {
  skip_if_no_python()

  python <- Sys.which("python3")
  if (!nzchar(python))
    python <- Sys.which("python")
  if (!nzchar(python))
    skip("No python interpreter available for testing")

  expect_identical(
    python_is_free_threaded(python),
    python_is_free_threaded_via_vv(python)
  )
})

test_that("virtualenv_starter excludes free-threaded Python builds", {
  skip_if_no_python()

  starters <- virtualenv_starter(all = TRUE)
  if (!nrow(starters))
    skip("No virtualenv starters found")

  is_free_threaded <- vapply(
    starters$path,
    python_is_free_threaded_via_vv,
    logical(1),
    USE.NAMES = FALSE
  )
  expect_false(any(is_free_threaded))
})

test_that("virtualenv_starter rejects explicitly requested free-threaded Python", {
  candidates <- c(
    Sys.glob("~/.pyenv/versions/*t*/bin/python*"),
    Sys.glob("~/.pyenv/versions/*t*/python*.exe")
  )
  candidates <- candidates[grep("^python[0-9.]*(\\.exe)?$", basename(candidates))]
  candidates <- candidates[utils::file_test("-x", candidates)]
  candidates <- candidates[utils::file_test("-f", candidates)]
  candidates <- unique(normalizePath(candidates, winslash = "/", mustWork = FALSE))
  candidates <- candidates[vapply(
    candidates, python_is_free_threaded_via_vv, logical(1), USE.NAMES = FALSE
  )]

  if (!length(candidates))
    skip("No free-threaded Python installations found")

  expect_null(virtualenv_starter(candidates[[1L]]))
})

Try the reticulate package in your browser

Any scripts or data that you put into this service are public.

reticulate documentation built on April 9, 2026, 5:08 p.m.