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

[DO NOT MERGE] Re-worked tooling for Python environments in REMIND #19

Draft
wants to merge 47 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
338c691
Added reticulate as dep
tonnrueter Jul 17, 2024
c1430a6
Verifies availability of Python deps
tonnrueter Jul 17, 2024
ba1f4d9
Determine host system's Python installation
tonnrueter Jul 17, 2024
aa0b40b
Replaced by checkPythonEnv.R
tonnrueter Jul 17, 2024
0048414
Do not update Python env from REMIND
tonnrueter Jul 17, 2024
58d8db5
Renamed for similarity
tonnrueter Jul 17, 2024
500187e
Use action parameter similar to checkDeps.R
tonnrueter Jul 17, 2024
2da6ec3
Reflect changes so far
tonnrueter Jul 17, 2024
d5c4696
Fail if Python does not exist in path
tonnrueter Jul 23, 2024
b937972
Repurposed writePythonVirtualEnvLockFile.R
tonnrueter Jul 23, 2024
5c7af8b
First draft
tonnrueter Jul 23, 2024
a0e482e
Way more complicated to properly dumb conda env to file
tonnrueter Jul 26, 2024
0b827c9
Tools for Python dependency comparison
tonnrueter Jul 29, 2024
bae3f73
Fixed issue in comparePythonVersions
tonnrueter Jul 30, 2024
303de11
Renamed components of createPythonVersion, do type conversions
tonnrueter Jul 30, 2024
dfcf467
Switched to stringr::str_match in extractPythonVersion
tonnrueter Jul 30, 2024
f050127
Adapt comparePythonVersions to new keys
tonnrueter Jul 30, 2024
4616e48
More tooling for checkPythonDeps
tonnrueter Jul 30, 2024
f824c29
Added purrr
tonnrueter Aug 6, 2024
f784bfd
Extended documentation
tonnrueter Aug 6, 2024
086be83
Function checkPythonRequirements
tonnrueter Aug 6, 2024
c14e187
Finished checkPythonDeps
tonnrueter Aug 6, 2024
beeca34
docs fixed
tonnrueter Aug 6, 2024
7d3c79a
Merge branch 'main' into dev/python_env
tonnrueter Aug 6, 2024
9479736
Added stringr, yaml to DESC, fixed docs
tonnrueter Aug 6, 2024
1986880
And more doc string fixes
tonnrueter Aug 6, 2024
8cf5232
Added author
tonnrueter Aug 6, 2024
72256a0
Fixed @export statement
tonnrueter Aug 6, 2024
56938f2
Removed createResultsfolderPythonVirtualEnv.R
tonnrueter Aug 6, 2024
109246e
fixes for make check
tonnrueter Aug 6, 2024
ced212a
Fixed @importFrom yaml
tonnrueter Aug 7, 2024
cc76224
Trying to fix strange roxygen bug
tonnrueter Aug 7, 2024
ff75d35
Make lintr happy
tonnrueter Aug 7, 2024
c56e3a2
Fixed authors
tonnrueter Aug 7, 2024
5b98404
Make linter even happier
tonnrueter Aug 7, 2024
7cacd2a
After lucode2::check
tonnrueter Aug 7, 2024
1bf0625
After make build, new version number is 0.6.0
Aug 7, 2024
8e85c05
After code review pt. 1
tonnrueter Aug 26, 2024
e7670a7
Remove automated installation of Python requirements
tonnrueter Sep 5, 2024
517e1a4
Added "post" to release types
tonnrueter Sep 10, 2024
146f211
Issue warning when checking repo dependencies
tonnrueter Sep 12, 2024
2e49750
Merge branch 'main' into dev/python_env
tonnrueter Sep 12, 2024
16f2fe4
After make build
Sep 12, 2024
1ce1ae7
Added function to read requirement files
tonnrueter Sep 24, 2024
450ae7a
Merge branch 'main' into dev/python_env
tonnrueter Sep 24, 2024
e15dd56
Make warning more readable
tonnrueter Sep 25, 2024
025ad30
After make build
Oct 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .buildlibrary
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ValidationKey: '1053587'
ValidationKey: '1196520'
AutocreateReadme: yes
AcceptedWarnings:
- 'Warning: package ''.*'' was built under R version'
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ repos:
- id: mixed-line-ending

- repo: https://github.com/lorenzwalthert/precommit
rev: 7910e0323d7213f34275a7a562b9ef0fde8ce1b9 # frozen: v0.4.2
rev: bae853d82da476eee0e0a57960ee6b741a3b3fb7 # frozen: v0.4.3
hooks:
- id: parsable-R
- id: deps-in-desc
Expand Down
7 changes: 5 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ cff-version: 1.2.0
message: If you use this software, please cite it using the metadata from this file.
type: software
title: 'piamenv: Package environment support for PIAM'
version: 0.5.3
date-released: '2024-06-05'
version: 0.6.0
date-released: '2024-08-07'
abstract: Enables easier management of package environments, based on renv and Python
venv.
authors:
- family-names: Sauer
given-names: Pascal
email: [email protected]
- family-names: Rüter
given-names: Tonn
email: [email protected]
license: LGPL-3.0
repository-code: https://github.com/pik-piam/piamenv

17 changes: 11 additions & 6 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
Type: Package
Package: piamenv
Title: Package environment support for PIAM
Version: 0.5.3
Date: 2024-06-05
Authors@R:
person("Pascal", "Sauer", , "[email protected]", role = c("aut", "cre"))
Version: 0.6.0
Date: 2024-08-07
Authors@R: c(
person("Pascal", "Sauer", , "[email protected]", role = c("aut", "cre")),
person("Tonn", "Rüter", , "[email protected]", role = c("aut")))
Description: Enables easier management of package environments, based on renv and Python venv.
License: LGPL-3
URL: https://github.com/pik-piam/piamenv
Imports:
desc,
methods,
renv,
withr
reticulate,
stringr,
purrr,
withr,
yaml
Suggests:
covr,
testthat
Encoding: UTF-8
RoxygenNote: 7.3.1
RoxygenNote: 7.3.2
24 changes: 20 additions & 4 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
# Generated by roxygen2: do not edit by hand

export(archivePythonEnv)
export(archiveRenv)
export(checkDeps)
export(createResultsfolderPythonVirtualEnv)
export(checkPythonDeps)
export(checkPythonEnv)
export(checkPythonRequirements)
export(fixDeps)
export(pythonBinPath)
export(restoreRenv)
export(revertDevelopmentVersions)
export(showUpdates)
export(stopIfLoaded)
export(updatePythonVirtualEnv)
export(updateRenv)
export(writePythonVirtualEnvLockFile)
importFrom(purrr,map)
importFrom(purrr,map_chr)
importFrom(purrr,pmap)
importFrom(reticulate,import)
importFrom(reticulate,py_config)
importFrom(reticulate,py_discover_config)
importFrom(reticulate,py_list_packages)
importFrom(reticulate,use_condaenv)
importFrom(reticulate,use_python)
importFrom(reticulate,use_virtualenv)
importFrom(stringr,regex)
importFrom(stringr,str_match)
importFrom(utils,head)
importFrom(utils,strcapture)
importFrom(yaml,as.yaml)
importFrom(yaml,yaml.load)
64 changes: 64 additions & 0 deletions R/archivePythonEnv.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#' writePythonVirtualEnvLockFile
tonnrueter marked this conversation as resolved.
Show resolved Hide resolved
#'
#' Write Python virtual environment lock file specifying package versions currently installed in the given
#' virtual environment. Using the lock file, a new virtual environment can be built containing exactly the same
#' package versions.
#'
#' @param outputDir Path to the directory where the lock file will be written
#' @param pythonConfig as produced by \code{\link{checkPythonEnv}}. Defaults to NULL. If not provided, the
tonnrueter marked this conversation as resolved.
Show resolved Hide resolved
#' RETICULATE_PYTHON environment variable is used
#'
#' @author Mika Pflüger, Tonn Rüter
#' @importFrom reticulate py_config
tonnrueter marked this conversation as resolved.
Show resolved Hide resolved
#' @importFrom yaml as.yaml yaml.load
#' @importFrom utils head
#' @export
archivePythonEnv <- function(outputDir, pythonConfig = reticulate::py_config()) {
if (!dir.exists(outputDir)) {
stop("Provided path '", outputDir, "' does not exist.")
}
dateAndTime <- format(Sys.time(), "%y%m%d_%H%M%S")
tonnrueter marked this conversation as resolved.
Show resolved Hide resolved
# Get the path to the Python executable
# Check if (ana)conda, venv or system Python is used
if (any(as.logical(c(pythonConfig$conda, pythonConfig$anaconda)))) {
envType <- "conda"
logFile <- file.path(outputDir, paste0("log_pyenv_", dateAndTime, "_", envType, ".txt"))
# First run the standard conda command to create an environment file it is yaml formatted, which is nice 'cause
tonnrueter marked this conversation as resolved.
Show resolved Hide resolved
# we possibly need to manipulate it later ..
condaEnv <- yaml.load(system2("conda", args = c("env", "export"), stdout = TRUE))
# .. in case some packages are installed via pip, we need to replace them with the actual pip freeze output
# because the conda env export command does not include information about packages that have been installed
# from particular sources repositories (i.e. it would show a version string like "0.1.4a0" instead of the
# " @ git+https://<repo-url>/<package name>.git@<commit-hash>"). However, this is important for reproducibility and
# a open issue with conda: https://github.com/conda/conda/issues/10320
# Let's start by checking whether there are any pip dependencies in the first place. Conda file contain those in
# a dedicated section called "pip". If there are none, we can skip this step.
pipIdx <- head(which(vapply(condaEnv$dependencies, function(x) "pip" %in% names(x), FUN.VALUE = logical(1))), 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pipIdx <- head(which(vapply(condaEnv$dependencies, function(x) "pip" %in% names(x), FUN.VALUE = logical(1))), 1)
pipIdx <- Find(x = condaEnv$dependencies, f = function(x) "pip" %in% names(x))

This seems quite a bit easier to read, but I didn't test this, please check if it still does what you want.

Copy link
Author

@tonnrueter tonnrueter Aug 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Problem here is that condaEnv$dependencies is a list-of-lists & only the "pip" entry has a name. Imma go with

match(TRUE, grepl("^pip", names(unlist(condaEnv$dependencies))))

but thanks for pointing this out! My solution was super contrived..

# This vector should not contain more than one element. If there'd be more than one element, there's something
# wrong with the conda environment file. Don't fail explicitly here, just disregard other pip dependencies except
# the first one.
if (!is.null(pipIdx)) {
# Run python -m pip [...] here instead of pip [...] cause from within the conda environment, there a chance
# that system pip is used instead of pip that comes with the conda installation
pipDeps <- system2("python", args = c("-m pip", "freeze", "--local"), stdout = TRUE)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pipDeps <- system2("python", args = c("-m pip", "freeze", "--local"), stdout = TRUE)
pipDeps <- system2("python", args = c("-m", "pip", "freeze", "--local"), stdout = TRUE)

Not sure, but I thought for system2 each space should lead to a separate vector entry?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might also apply to later instances of "-m pip"

condaEnv$dependencies[[pipIdx]]$pip <- pipDeps
}
# Dump conda environment + improved pip dependencies to yaml string
envAsString <- as.yaml(condaEnv)
} else {
if (pythonConfig$virtualenv != "") {
envType <- "venv"
# Need to use --local to avoid listing globally installed packages
archiveArgs <- c("-m pip", "freeze", "--local")
} else {
envType <- "system"
archiveArgs <- c("-m pip", "freeze")
}
envAsString <- system2(normalizePath(pythonConfig$python), args = archiveArgs, stdout = TRUE)
}
logFile <- normalizePath(file.path(outputDir, paste0("log_pyenv_", dateAndTime, "_", envType, ".txt")),
mustWork = FALSE
)
writeLines(envAsString, logFile)
return(invisible(TRUE))
}
Loading