Skip to content

Commit

Permalink
Merge pull request #1754 from rstudio/export-py_register_load_hook
Browse files Browse the repository at this point in the history
Export `py_register_load_hook()`
  • Loading branch information
t-kalinowski authored Feb 28, 2025
2 parents 1b67aec + 2def327 commit e1371db
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 14 deletions.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ export(py_main_thread_func)
export(py_module_available)
export(py_none)
export(py_numpy_available)
export(py_register_load_hook)
export(py_repr)
export(py_require)
export(py_run_file)
Expand Down
8 changes: 6 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
# reticulate (development version)

- Reticulate-managed `uv` can now resolve system-installed Pythons,
supporting platforms where pre-built binaries are unavailable, such as
- Reticulate-managed `uv` can now resolve system-installed Pythons,
supporting platforms where pre-built binaries are unavailable, such as
musl-based Alpine Linux (#1751, #1752).

- `uv_run_tool()` gains an `exclude_newer` argument (#1748).

- `py_register_load_hook()` is now exported to enable usage
described in the "Using reticulate in an R package vignette" (#1754).
https://rstudio.github.io/reticulate/articles/package.html

- Internal changes to support R-devel (4.5) (#1747).

- Internal fixes to prevent reticulate-managed `uv` from writing outside
Expand Down
15 changes: 11 additions & 4 deletions R/py_require.R
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ py_reqs_get <- function(x = NULL) {
# uv ---------------------------------------------------------------------------

uv_binary <- function(bootstrap_install = TRUE) {
required_version <- numeric_version("0.6.1")
required_version <- numeric_version("0.6.3")
is_usable_uv <- function(uv) {
if (is.null(uv) || is.na(uv) || uv == "" || !file.exists(uv)) {
return(FALSE)
Expand Down Expand Up @@ -781,12 +781,19 @@ uv_run_tool <- function(tool,
UV_PYTHON_INSTALL_DIR = reticulate_data_dir("uv", "python")
)
))
python <- resolve_python_version(constraints = python_version, uv = uv)

python <- .globals$cached_uv_run_tool_python_version[[python_version %||% "default"]]
if (is.null(python)) {
.globals$cached_uv_run_tool_python_version[[python_version %||% "default"]] <-
python <-
resolve_python_version(constraints = python_version, uv = uv)
}

system2(uv, c(
"tool",
"run",
"--isolated",
if (length(python)) c("--python", python),
"--python", python,
if (length(exclude_newer)) c("--exclude-newer", exclude_newer),
if (length(from)) c("--from", maybe_shQuote(from)),
if (length(with)) c(rbind("--with", maybe_shQuote(with))),
Expand Down Expand Up @@ -835,7 +842,7 @@ uv_python_list <- function(uv = uv_binary()) {
),
stdout = TRUE
)

x <- paste0(x, collapse = "")
x <- jsonlite::parse_json(x, simplifyVector = TRUE)

x <- x[is.na(x$symlink) , ] # ignore local filesystem symlinks
Expand Down
24 changes: 21 additions & 3 deletions R/python.R
Original file line number Diff line number Diff line change
Expand Up @@ -1431,7 +1431,7 @@ py_resolve_module_proxy <- function(proxy) {

# fixup the proxy. Note, the proxy may have already been fixed up,
# if `import(module)` triggered hooks to run registered via
# (unexported) py_register_load_hook()
# py_register_load_hook()
py_module_proxy_import(proxy)


Expand Down Expand Up @@ -1585,11 +1585,29 @@ py_module_loaded <- function(module) {
module %in% modules
}


#' Register a Python module load hook
#'
#' Register an R function to be called when a Python module is first loaded in
#' the current R session. This can be used for tasks such as:
#'
#' - Delayed registration of S3 methods to accommodate different versions of a Python module.
#' - Configuring module-specific logging streams.
#'
#' @param module String, the name of the Python module.
#' @param hook Function, called with no arguments. If `module` is already
#' loaded, `hook()` is called immediately.
#'
#' @return `NULL` invisibly. Called for its side effect.
#' @export
#' @keywords internal
py_register_load_hook <- function(module, hook) {

# if the module is already loaded, just run the hook
if (py_module_loaded(module))
return(hook())
if (py_module_loaded(module)) {
hook()
return(invisible())
}

# otherwise, register the hook to be run on next load
name <- paste("reticulate", module, "load", sep = "::")
Expand Down
1 change: 0 additions & 1 deletion _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,3 @@ reference:
- miniconda_uninstall
- miniconda_path
- miniconda_update

28 changes: 28 additions & 0 deletions man/py_register_load_hook.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 8 additions & 3 deletions tests/testthat/helper-py-require.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ r_session <- function(exprs, echo = TRUE, color = FALSE,
force_managed_python = TRUE) {
withr::local_envvar(c(
"VIRTUAL_ENV" = NA,
"RETICULATE_PYTHON" = if (force_managed_python) "managed" else NA
"RETICULATE_PYTHON" = if (force_managed_python) "managed" else NA,
"VIRTUAL_ENV_PROMPT" = NA,
"RETICULATE_MINICONDA_ENABLED" = NA,
"RUST_LOG" = NA,
"PYTHONPATH" = NA,
"PYTHONIOENCODING" = "utf-8",
if (isFALSE(color)) c("NO_COLOR" = "1")
))
exprs <- substitute(exprs)
if (!is.call(exprs))
Expand All @@ -32,8 +38,7 @@ r_session <- function(exprs, echo = TRUE, color = FALSE,
result <- suppressWarnings(system2(
R.home("bin/R"),
c("--quiet", "--no-save", "--no-restore", "--no-echo", "-f", file),
stdout = TRUE, stderr = TRUE,
env = c(character(), if (isFALSE(color)) "NO_COLOR=1")
stdout = TRUE, stderr = TRUE
))
class(result) <- "r_session_record"
result
Expand Down
2 changes: 1 addition & 1 deletion vignettes/package.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ py_to_r.markitdown.DocumentConverterResult <- function(x) {
.onLoad <- function(libname, pkgname) {
reticulate::py_require("markitdown")

reticulate:::py_register_load_hook("markitdown", function() {
reticulate::py_register_load_hook("markitdown", function() {
markitdown <- reticulate::import("markitdown")
registerS3method(
"py_to_r",
Expand Down

0 comments on commit e1371db

Please sign in to comment.