Skip to content

Commit

Permalink
Merge pull request #1605 from rstudio/print-callable-signatures
Browse files Browse the repository at this point in the history
Print Python callables with their signature
  • Loading branch information
t-kalinowski authored May 3, 2024
2 parents cdbb523 + 5516bda commit c2a21eb
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 7 deletions.
12 changes: 7 additions & 5 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,22 @@

- Interrupting Python no longer leads to segfaults (#1601, fixed in #1602).

- Print method for Python callables now includes the callable’s signature (#1605)

- `virtualenv_starter()` no longer warns when encountering broken symlinks (#1598).

# reticulate 1.36.1

- Fix issue where `py_to_r()` method for Pandas DataFrames would error
if `py_to_r()` S3 methods were defined for Pandas subtypes,
- Fix issue where `py_to_r()` method for Pandas DataFrames would error
if `py_to_r()` S3 methods were defined for Pandas subtypes,
(as done by {anndata}) (#1591).

- "Python Dependencies" vignette edits (@salim-b, #1586)

- Added an option for extra command-line arguments in
- Added an option for extra command-line arguments in
`conda_create()` and `conda_install()` (#1585).

- Fixed issue where `conda_install()` would ignore user-specified
- Fixed issue where `conda_install()` would ignore user-specified
channels during Python installation (#1594).

# reticulate 1.36.0
Expand Down
64 changes: 62 additions & 2 deletions R/python.R
Original file line number Diff line number Diff line change
@@ -1,11 +1,72 @@

#' @export
print.python.builtin.object <- function(x, ...) {
writeLines(py_repr(x))
formatted <- if (py_is_callable(x))
py_format_callable(x, ...)
else
py_repr(x)

writeLines(formatted)
invisible(x)
}


py_format_callable <- function(x, ...) {

type <- py_to_r(py_get_attr(py_get_attr(x, "__class__"), "__name__"))

name <- py_to_r(py_get_attr(x, "__qualname__", TRUE) %||%
py_get_attr(x, "__name__", TRUE))

module <- py_to_r(py_get_attr(x, "__module__", TRUE))
if (!is.null(module))
name <- paste0(c(module, name), collapse = ".")

inspect <- import("inspect")

get_formatted_signature <- function(x, drop_first = FALSE) {
tryCatch({
sig <- inspect$signature(x)
if (drop_first) {
# i.e., drop first positional arg, most typically: 'self'
#
# We only need to do this if inspect.signature() errored on the the
# callable itself, but succeeded on callable.__init__. This can happen
# for some built-in-C class where methods are slot wrappers.
# E.g., builtin exceptions like 'RuntimeError'.
sig <- inspect$Signature(
parameters = iterate(sig$parameters$values())[-1],
return_annotation = sig$return_annotation
)
}

formatted <- py_str_impl(sig)

# split long signatures across multiple lines, so they're readable
if (py_len(sig$parameters) >= 4L) {
for (formatted_arg in iterate(sig$parameters$values(), py_str_impl))
formatted <- sub(formatted_arg,
paste0("\n ", formatted_arg),
formatted, fixed = TRUE)

formatted <- sub(", /,", ",\n /,", formatted, fixed = TRUE) # positional only separator
formatted <- sub(", *,", ",\n *,", formatted, fixed = TRUE) # kw-only separator
formatted <- sub("\\)($| ->)", "\n)\\1", formatted) # final closing parens )
}
formatted
},
error = function(e) NULL)
}

formatted_sig <- get_formatted_signature(x) %||%
get_formatted_signature(py_get_attr(x, "__init__", TRUE), TRUE) %||%
get_formatted_signature(py_get_attr(x, "__new__", TRUE), TRUE) %||%
"(?)"

sprintf("<%s %s%s>", type, name, formatted_sig)
}


#' @importFrom utils str
#' @export
str.python.builtin.object <- function(object, ...) {
Expand Down Expand Up @@ -1761,4 +1822,3 @@ format_py_exception_traceback_with_clickable_filepaths <- function(etb) {

cli::col_silver(hint)
}

0 comments on commit c2a21eb

Please sign in to comment.