Skip to content

Commit

Permalink
🚸 Improve the user experience for setting up Python & reticulate (#129)
Browse files Browse the repository at this point in the history
* ✨ Add install_lamindb() function

Installs lamindb into a new or existing environment

* ✨ Add .onLoad() function

Sets the default Python environment if reticulate is not already
connected

* ➕ Move reticulate to Imports

* ✨ Add lamin_connect() function

* 📝 Add setup vignette

Adjust instructions in other documentation

* ✨ Add lamin_login() function

* 👷 Use R setup functions for CI

* 📝 Roxygenise

* 🚨 Fix lint

* 💚 Fix missing shell in CI

* 🐛 Fix lamin_login function name

* 🐛 Remove argument check in lamin_login()

* 📝 Fix \dontru{} in example

* ➕ Install s3fs on CI

* 📝 Adjust setup vignette title

* Update R-CMD-check.yaml

* 💚 Install laminr in check action

* 💚 Use pak to install tiledbsoma on GHA

* 💚 Use reticulate to install Python 3.12 on macOS

* 💚 Correctly install Python 3.12 on macOS not Linux

* 🐛 Fix logic in lamin_login()

* 🐛 Adjust settings directory path for Windows

* 📝 Update CHANGELOG

* 📝 Remove login with user from README

* 📝 Update development vignette

* 📝 Fix version in CHANGELOG

---------

Co-authored-by: Robrecht Cannoodt <[email protected]>
  • Loading branch information
lazappi and rcannood authored Dec 20, 2024
1 parent f5bd317 commit a555bf2
Show file tree
Hide file tree
Showing 16 changed files with 443 additions and 46 deletions.
40 changes: 22 additions & 18 deletions .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,9 @@ jobs:
fail-fast: false
matrix:
config:
# note: we use python 3.12 on mac os x so we can install scipy 1.13 from a wheel
- { os: macos-latest, r: "release", python: "3.12" }
- { os: macos-latest, r: "release", python: "3.x" }
- { os: windows-latest, r: "release", python: "3.x" }
- {
os: ubuntu-latest,
r: "devel",
http-user-agent: "release",
python: "3.x",
}
- { os: ubuntu-latest, r: "devel", http-user-agent: "release", python: "3.x" }
- { os: ubuntu-latest, r: "release", python: "3.x" }
- { os: ubuntu-latest, r: "oldrel-1", python: "3.9" }

Expand All @@ -52,7 +46,7 @@ jobs:

# manually installing openblas as a workaround for issue
# https://github.com/laminlabs/laminr/issues/57
- name: Install OpenBLAS
- name: Install OpenBLAS on macOS
if: runner.os == 'macOS'
run: |
brew install openblas
Expand All @@ -62,36 +56,46 @@ jobs:
- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: any::rcmdcheck
extra-packages: any::rcmdcheck, local::.
needs: check

- name: Install {tiledbsoma}
if: runner.os == 'Linux'
run: |
options(repos = c("https://chanzuckerberg.r-universe.dev", getOption("repos")))
install.packages("tiledbsoma")
pak::pkg_install("tiledbsoma")
shell: Rscript {0}

- name: Install lamindb
- name: Install Python 3.12 on macOS
# We use python 3.12 on mac os x so we can install scipy 1.13 from a wheel
if: runner.os == 'macOS'
run: |
pip install 'lamindb[aws]>=0.77.2'
reticulate::install_python(version = "3.12")
shell: Rscript {0}

# Make sure IPython is installed --
# Workaround for laminlabs/laminhub-public#29
pip install ipython
- name: Setup Python environment
run: |
laminr::install_lamindb(extra_packages = c("s3fs"))
shell: Rscript {0}

- name: Log in to Lamin
run: |
lamin login
reticulate::use_virtualenv("r-lamindb")
laminr::lamin_login()
shell: Rscript {0}

- name: Set lamindata as default instance
run: |
lamin connect laminlabs/lamindata
reticulate::use_virtualenv("r-lamindb")
laminr::lamin_connect("laminlabs/lamindata")
shell: Rscript {0}

- name: Check whether we can import lamindb and connect to the default instance
run: |
reticulate::use_virtualenv("r-lamindb")
reticulate::py_config()
reticulate::import("lamindb")
laminr::connect()
shell: Rscript {0}

- name: Check
Expand Down
21 changes: 11 additions & 10 deletions .github/workflows/pkgdown.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,31 +49,32 @@ jobs:
run: |
if (!requireNamespace("tiledbsoma", quietly = TRUE)) {
options(repos = c("https://chanzuckerberg.r-universe.dev", getOption("repos")))
install.packages("tiledbsoma")
pak::pkg_install("tiledbsoma")
} else {
message("Package 'tiledbsoma' already installed")
}
shell: Rscript {0}

- name: Install lamindb
- name: Setup Python environment
run: |
# install bionty and wetlab to avoid warnings about missing dependencies
pip install 'lamindb[aws,bionty,wetlab]>=0.77.2'
# Make sure IPython is installed --
# Workaround for laminlabs/laminhub-public#29
pip install ipython
laminr::install_lamindb(extra_packages = c("s3fs"))
shell: Rscript {0}

- name: Log in to Lamin
run: |
lamin login
reticulate::use_virtualenv("r-lamindb")
laminr::lamin_login()
shell: Rscript {0}

- name: Set lamindata as default instance
run: |
lamin connect laminlabs/lamindata
reticulate::use_virtualenv("r-lamindb")
laminr::lamin_connect("laminlabs/lamindata")
shell: Rscript {0}

- name: Check whether we can import lamindb and connect to the default instance
run: |
reticulate::use_virtualenv("r-lamindb")
reticulate::py_config()
reticulate::import("lamindb")
laminr::connect()
Expand Down
16 changes: 13 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
# laminr v0.3.1

## NEW FUNCTIONALITY

- Add a `install_lamindb()` function to help with setting up the default Python environment
- Add `lamin_login()` and `lamin_connect()` functions to allow access to CLI functionality from R

## DOCUMENTATION

- Add a set up vignette and update other documentation with instructions for how to set up a Python environment

# laminr v0.3.0

This release contains mostly UX improvements:

* Support for interacting with private LaminDB instances
* Support for interacting with TileDB-SOMA / CELLxGENE Census
* Improved UX for tracking and finishing runs
- Support for interacting with private LaminDB instances
- Support for interacting with TileDB-SOMA / CELLxGENE Census
- Improved UX for tracking and finishing runs

## NEW FUNCTIONALITY

Expand Down
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Imports:
purrr,
R.utils,
R6,
reticulate,
rlang,
tibble
Suggests:
Expand All @@ -31,15 +32,14 @@ Suggests:
nanoparquet,
quarto,
readr,
reticulate,
rstudioapi,
rsvg,
s3 (>= 1.1.0),
Seurat,
testthat (>= 3.0.0),
withr,
yaml
VignetteBuilder:
VignetteBuilder:
quarto
Config/testthat/edition: 3
Encoding: UTF-8
Expand Down
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Generated by roxygen2: do not edit by hand

export(connect)
export(install_lamindb)
export(lamin_connect)
export(lamin_login)
importFrom(R6,R6Class)
importFrom(cli,cli_abort)
importFrom(cli,cli_inform)
Expand Down
77 changes: 77 additions & 0 deletions R/connect.R
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,80 @@ connect <- function(slug = NULL) {

InstanceSettings$new(content)
}

#' Set the default LaminDB instance
#'
#' Set the default LaminDB instance by calling `lamin connect` on the command
#' line
#'
#' @param slug Slug giving the instance to connect to ("<owner>/<name>")
#'
#' @export
#'
#' @examples
#' \dontrun{
#' lamin_connect("laminlabs/cellxgene")
#' }
lamin_connect <- function(slug) {
current_default <- getOption("LAMINR_DEFAULT_INSTANCE")
if (!is.null(current_default)) {
cli::cli_abort(c(
"There is already a default instance connected ({.field {current_default}})",
"x" = "{.code lamin connect} will not be run"
))
}

# Set the default environment if not set
reticulate::use_virtualenv("r-lamindb", required = FALSE)
if (!reticulate::py_available()) {
# Force reticulate to connect to Python
py_config <- reticulate::py_config() # nolint object_usage_linter
}

system2("lamin", paste("connect", slug))
}

#' Login to LaminDB
#'
#' Login as a LaminDB user
#'
#' @param user Handle for the user to login as
#' @param api_key API key for a user
#'
#' @details
#' Setting `user` will run `lamin login <user>`. Setting `api_key` will set the
#' `LAMIN_API_KEY` environment variable tempoarily with `withr::with_envvar()`
#' and run `lamin login`. If neither `user` or `api_key` are set `lamin login`
#' will be run if `LAMIN_API_KEY` is set.
#'
#' @export
lamin_login <- function(user = NULL, api_key = NULL) {
current_default <- getOption("LAMINR_DEFAULT_INSTANCE")
if (!is.null(current_default)) {
cli::cli_abort(c(
"There is already a default instance connected ({.field {current_default}})",
"x" = "{.code lamin login} will not be run"
))
}

# Set the default environment if not set
reticulate::use_virtualenv("r-lamindb", required = FALSE)
if (!reticulate::py_available()) {
# Force reticulate to connect to Python
py_config <- reticulate::py_config() # nolint object_usage_linter
}

if (!is.null(user)) {
system2("lamin", paste("login", user))
} else if (!is.null(api_key)) {
withr::with_envvar(c("LAMIN_API_KEY" = api_key), {
system2("lamin", "login")
})
} else {
if (Sys.getenv("LAMIN_API_KEY") == "") {
cli::cli_abort("{.arg LAMIN_API_KEY} is not set")
}

system2("lamin", "login")
}
}
44 changes: 44 additions & 0 deletions R/install.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#' Install LaminDB
#'
#' Create a Python environment containing **lamindb** or install **lamindb**
#' into an existing environment.
#'
#' @param ... Additional arguments passed to `reticulate::py_install()`
#' @param envname String giving the name of the environment to install packages
#' into
#' @param extra_packages A vector giving the names of additional Python packages
#' to install
#' @param new_env Whether to remove any existing `virtualenv` with the same name
#' before creating a new one with the requested packages
#'
#' @return The result of `reticulate::py_install()`
#' @export
#'
#' @details
#' See `vignette("setup", package = "laminr")` for further details on setting up
#' a Python environment
#'
#' @examples
#' \dontrun{
#' install_lamindb()
#'
#' # Add additional packages to the environment
#' install_lamindb(extra_packages = c("bionty", "wetlab"))
#'
#' # Install into a different environment
#' install_lamindb(envvname = "your-env")
#' }
install_lamindb <- function(..., envname = "r-lamindb", extra_packages = NULL,
new_env = identical(envname, "r-lamindb")) {

if (new_env && reticulate::virtualenv_exists(envname)) {
reticulate::virtualenv_remove(envname)
}

packages <- unique(c(
"lamindb",
"ipython",
extra_packages
))
reticulate::py_install(packages = packages, envname = envname, ...)
}
7 changes: 6 additions & 1 deletion R/settings_store.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
if (settings_dir != "") {
file.path(settings_dir, ".lamin")
} else {
file.path(Sys.getenv("HOME"), ".lamin")
if (.Platform$OS.type == "windows") {
home_dir <- paste0(Sys.getenv("HOMEDRIVE"), Sys.getenv("HOMEPATH"))
} else {
home_dir <- Sys.getenv("HOME")
}
file.path(home_dir, ".lamin")
}
}

Expand Down
3 changes: 3 additions & 0 deletions R/zzz.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.onLoad <- function(libname, pkgname) {
reticulate::use_virtualenv("r-lamindb", required = FALSE)
}
30 changes: 24 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,6 @@ Get started with **{laminr}** by installing the package from CRAN:
install.packages("laminr")
```

You will also need to install the `lamindb` Python package:

```bash
pip install 'lamindb[aws]>=0.77.2'
```

### Additional packages

Some functionality requires additional packages. To install all of these use:
Expand All @@ -51,6 +45,30 @@ This will also install these package for the following tasks:

If you choose not to install all packages now you will be prompted to do so whenever one is required.

## Setting up

Before loading **{laminr}** for the first time you should:

1. Set up a Python environment

```r
laminr::install_lamindb()
```

2. Log in

```r
laminr::lamin_login(api_key = "your_api_key")
```

3. Set a default instance

```r
laminr::lamin_connect("<owner>/<name>")
```

See the [setup vignette](https://laminr.lamin.ai/articles/setup.html) for more information (`vignette("setup", package = "laminr")`).

## Getting started

The best way to get started with **{laminr}** is to explore the package vignettes (available at [laminr.lamin.ai](https://laminr.lamin.ai)):
Expand Down
Loading

0 comments on commit a555bf2

Please sign in to comment.