├── .Rbuildignore ├── .git-blame-ignore-revs ├── .github ├── .gitignore ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ ├── R-CMD-check.yaml │ ├── awaiting-response-close.yml │ ├── awaiting-response-remove-label.yml │ ├── pkgdown.yaml │ └── rchk.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── NAMESPACE ├── NEWS.md ├── R ├── RcppExports.R ├── ark-variables-methods.R ├── array.R ├── case.R ├── class.R ├── conda.R ├── config.R ├── conversion.R ├── generator.R ├── help.R ├── import.R ├── imports.R ├── install-python.R ├── install.R ├── knitr-engine.R ├── miniconda.R ├── output.R ├── package.R ├── pickle.R ├── pip.R ├── pipenv.R ├── poetry.R ├── py_func.R ├── py_require.R ├── pyenv.R ├── python-dict.R ├── python-environments.R ├── python-item.R ├── python-packages.R ├── python-tools.R ├── python.R ├── repl.R ├── seed.R ├── source.R ├── testthat-helpers.R ├── thread.R ├── use_python.R ├── utils-format.R ├── utils.R ├── virtualenv.R ├── wrapper.R └── zzz.R ├── README.md ├── _pkgdown.yml ├── cran-comments.md ├── inst ├── config │ └── config.py └── python │ └── rpytools │ ├── .gitignore │ ├── __init__.py │ ├── ark_variables.py │ ├── call.py │ ├── generator.py │ ├── help.py │ ├── ipython.py │ ├── loader.py │ ├── output.py │ ├── run.py │ ├── subprocess.py │ ├── test.py │ └── thread.py ├── man ├── Ops-python-methods.Rd ├── PyClass.Rd ├── array_reshape.Rd ├── as.character.python.builtin.bytes.Rd ├── as.character.python.builtin.str.Rd ├── conda-tools.Rd ├── conda_run2.Rd ├── configure_environment.Rd ├── dict.Rd ├── eng_python.Rd ├── figures │ ├── python_r_object.png │ ├── python_repl.png │ ├── reticulate_completion.png │ ├── reticulated_python.png │ └── rmarkdown_engine_zoomed.png ├── import.Rd ├── install_miniconda.Rd ├── install_python.Rd ├── ipython.Rd ├── is_py_object.Rd ├── iterate.Rd ├── miniconda-params.Rd ├── miniconda_path.Rd ├── miniconda_uninstall.Rd ├── miniconda_update.Rd ├── nameOfClass.python.builtin.type.Rd ├── np_array.Rd ├── py.Rd ├── py_available.Rd ├── py_bool.Rd ├── py_call.Rd ├── py_capture_output.Rd ├── py_config.Rd ├── py_config_error_message.Rd ├── py_del_attr.Rd ├── py_discover_config.Rd ├── py_ellipsis.Rd ├── py_eval.Rd ├── py_exe.Rd ├── py_func.Rd ├── py_function_custom_scaffold.Rd ├── py_function_wrapper.Rd ├── py_get_attr.Rd ├── py_get_item.Rd ├── py_has_attr.Rd ├── py_help.Rd ├── py_help_handler.Rd ├── py_id.Rd ├── py_install.Rd ├── py_is_null_xptr.Rd ├── py_iterator.Rd ├── py_last_error.Rd ├── py_len.Rd ├── py_list_attributes.Rd ├── py_list_packages.Rd ├── py_main_thread_func.Rd ├── py_module_available.Rd ├── py_none.Rd ├── py_register_load_hook.Rd ├── py_require.Rd ├── py_run.Rd ├── py_save_object.Rd ├── py_set_attr.Rd ├── py_set_seed.Rd ├── py_str.Rd ├── py_suppress_warnings.Rd ├── py_to_r_wrapper.Rd ├── py_unicode.Rd ├── py_version.Rd ├── py_versions_windows.Rd ├── r-py-conversion.Rd ├── register_class_filter.Rd ├── register_help_topics.Rd ├── register_module_help_handler.Rd ├── register_suppress_warnings_handler.Rd ├── repl_python.Rd ├── reticulate.Rd ├── source_python.Rd ├── tuple.Rd ├── use_python.Rd ├── uv_run_tool.Rd ├── virtualenv-tools.Rd ├── with-as-operator.Rd └── with.python.builtin.object.Rd ├── reticulate.Rproj ├── src ├── .gitignore ├── RcppExports.cpp ├── common.h ├── event_loop.cpp ├── event_loop.h ├── libpython.cpp ├── libpython.h ├── output.cpp ├── pending_py_calls_notifier.cpp ├── pending_py_calls_notifier.h ├── python.cpp ├── readline.cpp ├── reticulate_types.h ├── signals.cpp ├── signals.h └── tinythread.h ├── tests ├── testthat.R └── testthat │ ├── .gitignore │ ├── _snaps │ ├── py_require.md │ └── python-knitr-engine │ │ ├── knitr-print.md │ │ ├── knitr-print2.md │ │ ├── knitr-warn.md │ │ └── test-chunking.md │ ├── helper-py-require.R │ ├── resources │ ├── .gitignore │ ├── altair-example.Rmd │ ├── eng-reticulate-example.Rmd │ ├── import-test.R │ ├── knitr-print.Rmd │ ├── knitr-warn.Rmd │ ├── matplotlib-example.Rmd │ ├── pandas-example.Rmd │ ├── plotly-example.Rmd │ ├── seaborn-example.Rmd │ ├── test-chunking.Rmd │ ├── test-custom-root-dir.Rmd │ └── venv-activate.R │ ├── script.py │ ├── setup.R │ ├── test-aaa.R │ ├── test-callable-dynamic-dots.R │ ├── test-delay-load.R │ ├── test-examples.R │ ├── test-finalize.R │ ├── test-help-handlers.R │ ├── test-interrupts.R │ ├── test-multiprocessing.R │ ├── test-py_func.R │ ├── test-py_require.R │ ├── test-python-arrays.R │ ├── test-python-base-r-generics.R │ ├── test-python-class.R │ ├── test-python-classes.R │ ├── test-python-closures.R │ ├── test-python-comparisons.R │ ├── test-python-complex.R │ ├── test-python-datetime.R │ ├── test-python-dict.R │ ├── test-python-envs.R │ ├── test-python-exceptions.R │ ├── test-python-factors.R │ ├── test-python-formals.R │ ├── test-python-function.R │ ├── test-python-import-hook.R │ ├── test-python-info.R │ ├── test-python-initialize.R │ ├── test-python-iterators.R │ ├── test-python-knitr-engine.R │ ├── test-python-lists.R │ ├── test-python-modules.R │ ├── test-python-numpy.R │ ├── test-python-objects.R │ ├── test-python-output.R │ ├── test-python-pandas.R │ ├── test-python-pickle.R │ ├── test-python-pipenv.R │ ├── test-python-poetry.R │ ├── test-python-raw.R │ ├── test-python-run.R │ ├── test-python-scipy-sparse-matrix.R │ ├── test-python-scope.R │ ├── test-python-source.R │ ├── test-python-strings.R │ ├── test-python-threads.R │ ├── test-python-vectors.R │ ├── test-python-virtual-environments.R │ ├── test-r-extptr-capsule.R │ ├── test-repl-magics.R │ └── test-subprocess.R ├── tools ├── compile-python.R ├── r-3-5.Dockerfile └── tools-build-site.sh └── vignettes ├── .gitignore ├── arrays.Rmd ├── calling_python.Rmd ├── extra.css ├── images ├── code_completion.png ├── help.png ├── python_chunks.png ├── python_version.png ├── r_from_python.png ├── repl_python.png ├── reticulate_disable.png ├── reticulate_enable.png ├── rmarkdown_engine_zoomed.png ├── rmarkdown_reticulate.png ├── rmarkdown_reticulate_matplotlib.png └── source_python.png ├── package.Rmd ├── python_dependencies.Rmd ├── python_packages.Rmd ├── python_primer.Rmd ├── r_markdown.Rmd ├── rstudio_ide.Rmd └── versions.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | LICENSE 2 | README\.[R]?md 3 | README\.html 4 | \.Renviron$ 5 | \.pyc$ 6 | \.travis\.yml$ 7 | ^.*\.Rproj$ 8 | ^\.Rproj\.user$ 9 | ^\.github$ 10 | ^_pkgdown\.yml$ 11 | ^appveyor\.yml$ 12 | ^derby\.log$ 13 | ^docs$ 14 | ^images$ 15 | ^index\.Rmd$ 16 | ^internal$ 17 | ^issues$ 18 | ^log$ 19 | ^logs$ 20 | ^tools$ 21 | ^pkgdown$ 22 | ^revdep$ 23 | ^tags$ 24 | ^tests/testthat/resources/.*\.html$ 25 | ^tests/testthat/resources/.*_files 26 | ^vignettes/images/code_completion\.png$ 27 | ^vignettes/images/help\.png$ 28 | ^vignettes/images/repl_python\.png$ 29 | ^vignettes/images/rmarkdown_reticulate\.png$ 30 | ^vignettes/images/rmarkdown_reticulate_matplotlib\.png$ 31 | ^vignettes/images/source_python\.png$ 32 | ^vignettes/rstudio_ide\.Rmd$ 33 | ^vignettes/rstudio_ide\.html$ 34 | __pycache__ 35 | ^cran-comments\.md$ 36 | ^CRAN-SUBMISSION$ 37 | ^\.vscode$ 38 | ^Dockerfile$ 39 | ^\.dockerignore$ 40 | ^\.git-blame-ignore-revs$ 41 | ^rchk$ 42 | ^\.cache$ 43 | ^compile_commands\.json$ 44 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Format Python files with Black 2 | fc430b4dbf1854b68de44d927add8a41f64beecc 3 | 4 | # whitespace only commits 5 | e1df4e54c0a4d15cbb01a3af7e8466d29ed64305 6 | 02a0d984171dcacd322e7c071edf3d6b1cefb7b1 7 | 95573e7f3e0a11e4d703c4dbc6c29a409ee7b242 8 | 64acdfa98c29c70917ca7f60f05ad4192272e244 9 | da67e9dff1be9b5c344aa3431eae520b57142bde 10 | 1a62650d952460ce047e06354b84d1a3b3e683de 11 | 12 | 13 | ## Snippet to inspect/filter commits quickly: 14 | # library(tidyverse) 15 | # df <- as_tibble(git2r::repository(".")) 16 | # df |> filter(grepl("white[- ]?space", summary)) 17 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 15 | -------------------------------------------------------------------------------- /.github/workflows/awaiting-response-close.yml: -------------------------------------------------------------------------------- 1 | name: Close 'awaiting response' labeled issues 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: '0 6 * * *' 7 | 8 | jobs: 9 | close-issues-with-no-response: 10 | 11 | runs-on: ubuntu-latest 12 | permissions: 13 | issues: write 14 | pull-requests: write 15 | 16 | steps: 17 | - uses: actions/stale@v8 18 | with: 19 | repo-token: ${{ secrets.GITHUB_TOKEN }} 20 | 21 | stale-issue-label: 'awaiting response' 22 | stale-pr-label: 'awaiting response' 23 | days-before-stale: -1 24 | 25 | days-before-close: 14 26 | close-issue-message: > 27 | Automatically closed because there has not been a response for 14 days. 28 | When you're ready to work on this further, please comment here and the 29 | issue will automatically reopen. 30 | -------------------------------------------------------------------------------- /.github/workflows/awaiting-response-remove-label.yml: -------------------------------------------------------------------------------- 1 | name: Remove 'awaiting response' label 2 | 3 | on: 4 | issue_comment: 5 | types: [created, edited] 6 | 7 | jobs: 8 | remove-awaiting-response-label: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Remove 'awaiting response' label and maybe reopen 12 | if: contains(github.event.issue.labels.*.name, 'awaiting response') 13 | run: | 14 | gh issue edit $ISSUE --remove-label 'awaiting response' 15 | gh issue reopen $ISSUE 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | ISSUE: ${{ github.event.issue.html_url }} 19 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown 13 | 14 | jobs: 15 | pkgdown: 16 | runs-on: ubuntu-latest 17 | env: 18 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 19 | steps: 20 | - uses: actions/checkout@v2 21 | 22 | - uses: r-lib/actions/setup-pandoc@v2 23 | 24 | - uses: r-lib/actions/setup-r@v2 25 | with: 26 | use-public-rspm: true 27 | 28 | - uses: r-lib/actions/setup-r-dependencies@v2 29 | with: 30 | extra-packages: any::pkgdown, local::. 31 | needs: website 32 | 33 | - uses: actions/setup-python@v2 34 | with: 35 | python-version: '3.10' 36 | 37 | - name: Setup r-reticulate env 38 | run: reticulate::virtualenv_create("r-reticulate", Sys.which("python")) 39 | shell: Rscript {0} 40 | 41 | - name: Build site 42 | run: Rscript -e 'pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)' 43 | 44 | - name: Deploy to GitHub pages 🚀 45 | if: github.event_name != 'pull_request' 46 | uses: JamesIves/github-pages-deploy-action@4.1.4 47 | with: 48 | clean: false 49 | branch: gh-pages 50 | folder: docs 51 | -------------------------------------------------------------------------------- /.github/workflows/rchk.yaml: -------------------------------------------------------------------------------- 1 | 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: main 6 | pull_request: 7 | branches: main 8 | 9 | name: rchk 10 | 11 | jobs: 12 | rchk: 13 | runs-on: ubuntu-latest 14 | steps: 15 | 16 | - uses: actions/checkout@v4 17 | 18 | - uses: r-lib/actions/setup-pandoc@v2 19 | - uses: r-lib/actions/setup-r@v2 20 | - uses: r-lib/actions/setup-r-dependencies@v2 21 | - run: R CMD build . 22 | 23 | - run: docker pull kalibera/rchk:latest 24 | - name: run rchk 25 | run: | 26 | pkgtar=$(ls reticulate_*.tar.gz) 27 | mkdir -p rchk/packages 28 | mv $pkgtar rchk/packages/ 29 | cd rchk 30 | docker run -v `pwd`/packages:/rchk/packages kalibera/rchk:latest /rchk/packages/$pkgtar > rchk.log 2>&1 31 | cat rchk.log 32 | 33 | - name: upload rchk log 34 | uses: actions/upload-artifact@v4 35 | with: 36 | name: rchk-log 37 | path: rchk/packages/rchk.log 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Renviron 2 | .Rproj.user 3 | .Rhistory 4 | .RData 5 | .Ruserdata 6 | .DS_Store 7 | docs/ 8 | log/ 9 | src/*.o 10 | src/*.so 11 | src/*.dll 12 | revdep/ 13 | issues/ 14 | README.html 15 | index.html 16 | inst/doc 17 | .vscode/ 18 | rchk 19 | .cache 20 | compile_commands.json 21 | *.Dockerfile 22 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: reticulate 2 | Type: Package 3 | Title: Interface to 'Python' 4 | Version: 1.42.0.9000 5 | Authors@R: c( 6 | person("Tomasz", "Kalinowski", role = c("ctb", "cre"), 7 | email = "tomasz@posit.co"), 8 | person("Kevin", "Ushey", role = c("aut"), 9 | email = "kevin@posit.co"), 10 | person("JJ", "Allaire", role = c("aut"), email = "jj@posit.co"), 11 | person("RStudio", role = c("cph", "fnd")), 12 | person("Yuan", "Tang", role = c("aut", "cph"), 13 | email = "terrytangyuan@gmail.com", 14 | comment = c(ORCID = "0000-0001-5243-233X")), 15 | person("Dirk", "Eddelbuettel", role = c("ctb", "cph"), 16 | email = "edd@debian.org"), 17 | person("Bryan", "Lewis", role = c("ctb", "cph"), 18 | email = "blewis@illposed.net"), 19 | person("Sigrid", "Keydana", role = c("ctb"), 20 | email = "sigrid@posit.co"), 21 | person("Ryan", "Hafen", role = c("ctb", "cph"), 22 | email = "rhafen@gmail.com"), 23 | person("Marcus", "Geelnard", role = c("ctb", "cph"), 24 | comment = "TinyThread library, http://tinythreadpp.bitsnbites.eu/") 25 | ) 26 | Description: Interface to 'Python' modules, classes, and functions. When calling 27 | into 'Python', R data types are automatically converted to their equivalent 'Python' 28 | types. When values are returned from 'Python' to R they are converted back to R 29 | types. Compatible with all versions of 'Python' >= 2.7. 30 | License: Apache License 2.0 31 | URL: https://rstudio.github.io/reticulate/, 32 | https://github.com/rstudio/reticulate 33 | BugReports: https://github.com/rstudio/reticulate/issues 34 | SystemRequirements: Python (>= 2.7.0) 35 | Encoding: UTF-8 36 | Depends: 37 | R (>= 3.5) 38 | Imports: 39 | Matrix, 40 | Rcpp (>= 1.0.7), 41 | RcppTOML, 42 | graphics, 43 | here, 44 | jsonlite, 45 | methods, 46 | png, 47 | rappdirs, 48 | utils, 49 | rlang, 50 | withr 51 | Suggests: 52 | callr, 53 | knitr, 54 | glue, 55 | cli, 56 | rmarkdown, 57 | pillar, 58 | testthat 59 | LinkingTo: Rcpp 60 | RoxygenNote: 7.3.2 61 | Roxygen: list(markdown = TRUE) 62 | VignetteBuilder: knitr 63 | Config/build/compilation-database: true 64 | -------------------------------------------------------------------------------- /R/ark-variables-methods.R: -------------------------------------------------------------------------------- 1 | 2 | # Methods for Populating the Positron/Ark Variables Pane 3 | # These methods primarily delegate to the implementation in the 4 | # Positron python_ipykernel.inspectors python module. 5 | 6 | 7 | ark_positron_variable_display_value.python.builtin.object <- function(x, ..., width = getOption("width")) { 8 | .globals$get_positron_variable_inspector(x)$get_display_value(width)[[1L]] 9 | } 10 | 11 | 12 | ark_positron_variable_display_type.python.builtin.object <- function(x, ..., include_length = TRUE) { 13 | i <- .globals$get_positron_variable_inspector(x) 14 | out <- i$get_display_type() 15 | 16 | if (startsWith(class(x)[1], "python.builtin.")) # display convert value? 17 | out <- paste("python", out) 18 | 19 | out 20 | } 21 | 22 | 23 | ark_positron_variable_kind.python.builtin.object <- function(x, ...) { 24 | i <- .globals$get_positron_variable_inspector(x) 25 | i$get_kind() 26 | } 27 | 28 | 29 | ark_positron_variable_has_children.python.builtin.object <- function(x, ...) { 30 | i <- .globals$get_positron_variable_inspector(x) 31 | i$has_children() 32 | } 33 | 34 | 35 | ark_positron_variable_get_children.python.builtin.object <- function(x, ...) { 36 | # Return an R list of children. The order of children should be 37 | # stable between repeated calls on the same object. 38 | i <- .globals$get_positron_variable_inspector(x) 39 | 40 | get_keys_and_children <- .globals$ark_variable_get_keys_and_children 41 | if (is.null(get_keys_and_children)) { 42 | get_keys_and_children <- .globals$ark_variable_get_keys_and_children <- 43 | import("rpytools.ark_variables", convert = FALSE)$get_keys_and_children 44 | } 45 | 46 | keys_and_children <- iterate(get_keys_and_children(i), simplify = FALSE) 47 | children <- iterate(keys_and_children[[2L]], simplify = FALSE) 48 | names(children) <- as.character(py_to_r(keys_and_children[[1L]])) 49 | 50 | children 51 | } 52 | 53 | 54 | ark_positron_variable_get_child_at.python.builtin.object <- function(x, ..., name, index) { 55 | i <- .globals$get_positron_variable_inspector(x) 56 | get_child <- .globals$ark_variable_get_child 57 | if (is.null(get_child)) { 58 | get_child <- .globals$ark_variable_get_child <- 59 | import("rpytools.ark_variables", convert = FALSE)$get_child 60 | } 61 | 62 | get_child(i, index) 63 | } 64 | 65 | 66 | 67 | ark_positron_variable_display_type.rpytools.ark_variables.ChildrenOverflow <- 68 | function(x, ..., include_length = TRUE) { 69 | "" 70 | } 71 | 72 | ark_positron_variable_kind.rpytools.ark_variables.ChildrenOverflow <- 73 | function(x, ...) { 74 | "empty" # other? collection? map? lazy? 75 | } 76 | 77 | ark_positron_variable_display_value.rpytools.ark_variables.ChildrenOverflow <- 78 | function(x, ..., width = getOption("width")) { 79 | paste(py_to_r(x$n_remaining), "more values") 80 | } 81 | 82 | ark_positron_variable_has_children.rpytools.ark_variables.ChildrenOverflow <- 83 | function(x, ...) { 84 | FALSE 85 | } 86 | -------------------------------------------------------------------------------- /R/case.R: -------------------------------------------------------------------------------- 1 | 2 | case <- function(...) { 3 | 4 | dots <- list(...) 5 | for (dot in dots) { 6 | 7 | if (!inherits(dot, "formula")) 8 | return(dot) 9 | 10 | else if (length(dot) == 2) { 11 | expr <- dot[[2]] 12 | return(eval(expr, envir = environment(dot))) 13 | } 14 | 15 | else { 16 | 17 | cond <- dot[[2]] 18 | expr <- dot[[3]] 19 | if (eval(cond, envir = environment(dot))) 20 | return(eval(expr, envir = environment(dot))) 21 | 22 | } 23 | } 24 | 25 | NULL 26 | 27 | } 28 | -------------------------------------------------------------------------------- /R/class.R: -------------------------------------------------------------------------------- 1 | inject_super <- function(fun) { 2 | # for each function `fun` we need to place a `super` function in its 3 | # search path, and this `super` function must be able to access 4 | # the `self` argument passed to `fun`. 5 | 6 | e <- new.env(parent = environment(fun)) 7 | 8 | e$super <- function() { 9 | bt <- reticulate::import_builtins() 10 | # self is an argument passed to fun 11 | self <- get("self", envir = parent.frame(), inherits = FALSE) 12 | class_ <- get("__class__", envir = e, inherits = FALSE) 13 | bt$super(class_, self) 14 | } 15 | 16 | environment(fun) <- e # so fun can access `super` 17 | 18 | fun 19 | } 20 | 21 | #' Create a python class 22 | #' 23 | #' @param classname Name of the class. The class name is useful for S3 method 24 | #' dispatch. 25 | #' @param defs A named list of class definitions - functions, attributes, etc. 26 | #' @param inherit A list of Python class objects. Usually these objects have 27 | #' the `python.builtin.type` S3 class. 28 | #' 29 | #' @examples 30 | #' \dontrun{ 31 | #' Hi <- PyClass("Hi", list( 32 | #' name = NULL, 33 | #' `__init__` = function(self, name) { 34 | #' self$name <- name 35 | #' NULL 36 | #' }, 37 | #' say_hi = function(self) { 38 | #' paste0("Hi ", self$name) 39 | #' } 40 | #' )) 41 | #' 42 | #' a <- Hi("World") 43 | #' } 44 | #' 45 | #' @export 46 | PyClass <- function(classname, defs = list(), inherit = NULL) { 47 | 48 | builtins <- import_builtins(convert = TRUE) 49 | 50 | if (is_py_object(inherit)) 51 | inherit <- list(inherit) 52 | 53 | bases <- case( 54 | 55 | length(inherit) == 0 ~ tuple(), 56 | is.list(inherit) ~ do.call(tuple, inherit), 57 | is.character(inherit) ~ do.call(tuple, as.list(inherit)), 58 | 59 | ~ stop("unexpected 'inherit' argument") 60 | 61 | ) 62 | 63 | defs <- lapply(defs, function(x) { 64 | 65 | # nothing to be done for non-functions 66 | if (!is.function(x)) 67 | return(x) 68 | 69 | # otherwise, create a new version of the function with 'super' injected 70 | f <- inject_super(x) 71 | 72 | x <- function(...) { 73 | # enable conversion scope for `self` 74 | # the first argument is always `self`.and we don't want to convert it. 75 | args <- list(...) 76 | assign("convert", TRUE, envir = as.environment(args[[1]])) 77 | do.call(f, append(args[1], lapply(args[-1], py_to_r))) 78 | } 79 | 80 | attr(x, "__env__") <- environment(f) 81 | x 82 | 83 | }) 84 | 85 | type <- builtins$type( 86 | classname, 87 | bases, 88 | do.call(reticulate::dict, defs) 89 | ) 90 | 91 | # we add a reference to the type here. so it can be accessed without needing 92 | # to find the type from self. 93 | lapply(defs, function(x) { 94 | 95 | envir <- attr(x, "__env__") 96 | if (!is.environment(envir)) 97 | return() 98 | 99 | envir$`__class__` <- type 100 | 101 | }) 102 | 103 | type 104 | 105 | } 106 | -------------------------------------------------------------------------------- /R/imports.R: -------------------------------------------------------------------------------- 1 | 2 | #' @importFrom utils capture.output download.file tail 3 | #' @importFrom png readPNG writePNG 4 | #' @importFrom jsonlite fromJSON 5 | NULL 6 | -------------------------------------------------------------------------------- /R/output.R: -------------------------------------------------------------------------------- 1 | remap_output_streams <- function() { 2 | # force remapping of output streams (required with Windows + Python 2 3 | # as otherwise output from Python is lost) 4 | force <- is_windows() && !is_python3() 5 | remap <- Sys.getenv("RETICULATE_REMAP_OUTPUT_STREAMS", unset = NA) 6 | if (!is.na(remap)) 7 | force <- identical(remap, "1") 8 | 9 | # if stdout is NULL in Python we want to force remapping too. 10 | if (!force) { 11 | sys <- import("sys") 12 | force <- is.null(sys$stdout) 13 | } 14 | 15 | if (!force) return() 16 | 17 | set_output_streams(tty = interactive() || isatty(stdout())) 18 | } 19 | 20 | set_output_streams <- function(tty) { 21 | stream_context <- output_stream_context(tty) 22 | stream_context$`__enter__`() 23 | } 24 | 25 | set_knitr_python_stdout_hook <- function() { 26 | # if the env var is set to 0, we respect it and never remap. If the env var is 1 27 | # this means that remapping is already set and we don't need the hook anyway. 28 | if (!is.na(Sys.getenv("RETICULATE_REMAP_OUTPUT_STREAMS", unset = NA))) 29 | return() 30 | 31 | # we don't want to to force a knitr load namespace, so if it's already loaded 32 | # we set the knitr hook, otherwise we schedule an onLoad hook. 33 | if (isNamespaceLoaded("knitr")) { 34 | set_knitr_hook() 35 | 36 | # if knitr is already in progress here, this means that python was initialized 37 | # during a chunk execution. We have to force an instant remap as the hook won't 38 | # have a chance to run for that chunk. 39 | # In such cases `context.__enter__` is never called. 40 | if (isTRUE(getOption('knitr.in.progress'))) set_output_streams(tty = FALSE) 41 | } else { 42 | setHook( 43 | packageEvent("knitr", "onLoad"), 44 | function(...) { 45 | set_knitr_hook() 46 | } 47 | ) 48 | } 49 | } 50 | 51 | set_knitr_hook <- function() { 52 | context <- output_stream_context(tty = FALSE) 53 | 54 | knitr::knit_hooks$set(include = function(before, options, envir) { 55 | if (!options$include) return() 56 | if (before) { 57 | context$`__enter__`() 58 | } else { 59 | context$`__exit__`() 60 | } 61 | }) 62 | } 63 | 64 | output_stream_context <- function(tty) { 65 | output <- import("rpytools.output") 66 | output$RemapOutputStreams( 67 | write_stdout, 68 | write_stderr, 69 | tty = tty 70 | ) 71 | } 72 | -------------------------------------------------------------------------------- /R/pickle.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | #' Save and Load Python Objects 4 | #' 5 | #' Save and load Python objects. 6 | #' 7 | #' Python objects are serialized using the `pickle` module -- see 8 | #' for more details. 9 | #' 10 | #' @param object A Python object. 11 | #' 12 | #' @param filename The output file name. Note that the file extension `.pickle` 13 | #' is considered the "standard" extension for serialized Python objects 14 | #' as created by the `pickle` module. 15 | #' 16 | #' @param pickle The "pickle" implementation to use. Defaults to `"pickle`", 17 | #' but other compatible Python "pickle" implementations (e.g. `"cPickle"`) 18 | #' could be used as well. 19 | #' 20 | #' @param ... Optional arguments, to be passed to the `pickle` module's 21 | #' `dump()` and `load()` functions. 22 | #' 23 | #' @param convert Bool. Whether the loaded pickle object should be converted to 24 | #' an R object. 25 | #' 26 | #' @export 27 | py_save_object <- function(object, filename, pickle = "pickle", ...) { 28 | 29 | filename <- normalizePath(filename, winslash = "/", mustWork = FALSE) 30 | 31 | builtins <- import_builtins() 32 | pickle <- import(pickle, convert = TRUE) 33 | 34 | handle <- builtins$open(filename, "wb") 35 | on.exit(handle$close(), add = TRUE) 36 | pickle$dump(object, handle, protocol = pickle$HIGHEST_PROTOCOL, ...) 37 | 38 | } 39 | 40 | #' @rdname py_save_object 41 | #' @export 42 | py_load_object <- function(filename, pickle = "pickle", ..., convert = TRUE) { 43 | 44 | filename <- normalizePath(filename, winslash = "/", mustWork = FALSE) 45 | 46 | builtins <- import_builtins() 47 | pickle <- import(pickle, convert = convert) 48 | 49 | handle <- builtins$open(filename, "rb") 50 | on.exit(handle$close(), add = TRUE) 51 | obj <- py_call(pickle$load, handle, ...) 52 | py_maybe_convert(obj, convert) 53 | } 54 | 55 | -------------------------------------------------------------------------------- /R/pipenv.R: -------------------------------------------------------------------------------- 1 | 2 | pipenv_config <- function(required_module) { 3 | 4 | pipfile <- pipenv_pipfile_path() 5 | if (!file.exists(pipfile)) 6 | return(NULL) 7 | 8 | python <- pipenv_python() 9 | python_config(python, required_module, forced = "Pipfile") 10 | 11 | } 12 | 13 | pipenv_pipfile_path <- function() { 14 | 15 | # check option 16 | pipfile <- getOption("reticulate.pipenv.pipfile") 17 | if (!is.null(pipfile)) 18 | return(pipfile) 19 | 20 | # try default 21 | tryCatch( 22 | here::here("Pipfile"), 23 | error = function(e) "" 24 | ) 25 | 26 | } 27 | 28 | pipenv_python <- function() { 29 | 30 | # validate that pipenv is available on the PATH 31 | if (!nzchar(Sys.which("pipenv"))) 32 | stop("'pipenv' is not available") 33 | 34 | # move to root directory 35 | root <- here::here() 36 | owd <- setwd(root) 37 | on.exit(setwd(owd), add = TRUE) 38 | 39 | # ask pipenv what the environment path is 40 | envpath <- system("pipenv --venv", intern = TRUE) 41 | status <- attr(envpath, "status") %||% 0L 42 | if (status != 0L) { 43 | fmt <- "'pipenv --venv' had status %i" 44 | stopf(fmt, status) 45 | } 46 | 47 | # get path to python 48 | virtualenv_python(envpath) 49 | 50 | } 51 | -------------------------------------------------------------------------------- /R/poetry.R: -------------------------------------------------------------------------------- 1 | 2 | poetry_config <- function(required_module) { 3 | 4 | # check for project file 5 | project <- poetry_project() 6 | projfile <- file.path(project, "pyproject.toml") 7 | if (!file.exists(projfile)) 8 | return(NULL) 9 | 10 | # try to read it 11 | toml <- tryCatch( 12 | RcppTOML::parseTOML(projfile), 13 | error = identity 14 | ) 15 | 16 | if (inherits(toml, "error")) { 17 | warning("This project contains a 'pyproject.toml' file, but it could not be parsed") 18 | warning(toml) 19 | return(NULL) 20 | } 21 | 22 | # check that it has a 'tool.poetry' section 23 | info <- tryCatch(toml[[c("tool", "poetry")]], error = identity) 24 | if (inherits(info, "error")) 25 | return(NULL) 26 | 27 | # validate that 'poetry' is available 28 | poetry <- poetry_exe() 29 | if (!file.exists(poetry)) { 30 | 31 | msg <- heredoc(" 32 | This project appears to use Poetry for Python dependency management. 33 | However, the 'poetry' command line tool is not available. 34 | reticulate will be unable to activate this project. 35 | Please ensure that 'poetry' is available on the PATH. 36 | ") 37 | 38 | warning(msg) 39 | return(NULL) 40 | 41 | } 42 | 43 | python <- poetry_python(project) 44 | python_config(python, required_module, forced = "Poetry") 45 | 46 | } 47 | 48 | poetry_exe <- function() { 49 | 50 | poetry <- getOption("reticulate.poetry.exe") 51 | if (!is.null(poetry)) 52 | return(poetry) 53 | 54 | Sys.which("poetry") 55 | 56 | } 57 | 58 | poetry_project <- function() { 59 | 60 | # check option 61 | project <- getOption("reticulate.poetry.project") 62 | if (!is.null(project)) 63 | return(project) 64 | 65 | # try default 66 | projfile <- tryCatch( 67 | dirname(here::here("pyproject.toml")), 68 | error = function(e) "" 69 | ) 70 | 71 | } 72 | 73 | poetry_python <- function(project) { 74 | 75 | # move to project directory 76 | owd <- setwd(project) 77 | on.exit(setwd(owd), add = TRUE) 78 | 79 | # ask poetry where the virtual environment lives 80 | envpath <- system2("poetry", c("env", "info", "--path"), stdout = TRUE) 81 | 82 | # resolve python from this path 83 | virtualenv_python(envpath) 84 | 85 | } 86 | -------------------------------------------------------------------------------- /R/py_func.R: -------------------------------------------------------------------------------- 1 | get_signature <- function(sigs) { 2 | sig_names <- names(sigs) 3 | signature_strings <- lapply(sig_names, function(k) { 4 | if (identical(sigs[[k]], quote(expr = ))) 5 | # arg without default 6 | k 7 | else { 8 | # arg with default 9 | py_value_str <- ifelse( 10 | is.character(sigs[[k]]), 11 | paste0("'", sigs[[k]], "'"), 12 | as.character(r_to_py(eval(sigs[[k]])))) 13 | paste0(k, "=", py_value_str) 14 | } 15 | }) 16 | paste(signature_strings, collapse = ", ") 17 | } 18 | 19 | #' Wrap an R function in a Python function with the same signature. 20 | #' 21 | #' This function could wrap an R function in a Python function with 22 | #' the same signature. Note that the signature of the R function 23 | #' must not contain esoteric Python-incompatible constructs. 24 | #' 25 | #' @param f An R function 26 | #' @return A Python function that calls the R function `f` with the same signature. 27 | #' @export 28 | py_func <- function(f) { 29 | tryCatch({ 30 | sigs <- formals(f) 31 | if (is.null(sigs)) { 32 | func_signature <- func_pass_args <- "" 33 | } else { 34 | func_signature <- get_signature(sigs) 35 | func_pass_args <- get_signature( 36 | lapply(sigs, function(sig) quote(expr =))) 37 | } 38 | wrap_fn_util <- py_run_string(sprintf(" 39 | def wrap_fn(f): 40 | def fn(%s): 41 | return f(%s) 42 | return fn 43 | ", func_signature, func_pass_args)) 44 | wrap_fn_util$wrap_fn(f) 45 | }, error = function(e) { 46 | stop(paste0("The R function's signature must not contains esoteric ", 47 | "Python-incompatible constructs. Detailed traceback: \n", 48 | e$message)) 49 | }) 50 | } 51 | 52 | -------------------------------------------------------------------------------- /R/python-dict.R: -------------------------------------------------------------------------------- 1 | #' @export 2 | `$.python.builtin.dict` <- function(x, name) { 3 | attr <- py_get_attr(x, name, TRUE) 4 | if(is.null(attr)) 5 | py_dict_get_item(x, name) 6 | else 7 | py_maybe_convert(attr, py_has_convert(x)) 8 | } 9 | 10 | #' @export 11 | `[.python.builtin.dict` <- function(x, name) { 12 | py_dict_get_item(x, name) 13 | } 14 | 15 | #' @export 16 | `[[.python.builtin.dict` <- `[.python.builtin.dict` 17 | 18 | #' @export 19 | `$<-.python.builtin.dict` <- function(x, key, value) { 20 | if(is.null(value)) 21 | py_del_item(x, key) 22 | else 23 | py_dict_set_item(x, key, value) 24 | x 25 | } 26 | 27 | #' @export 28 | `[<-.python.builtin.dict` <- `$<-.python.builtin.dict` 29 | 30 | #' @export 31 | `[[<-.python.builtin.dict` <- `$<-.python.builtin.dict` 32 | 33 | #' @export 34 | length.python.builtin.dict <- function(x) { 35 | if (py_is_null_xptr(x) || !py_available()) 36 | 0L 37 | else 38 | py_dict_length(x) 39 | } 40 | -------------------------------------------------------------------------------- /R/python-environments.R: -------------------------------------------------------------------------------- 1 | 2 | python_environment_resolve <- function(envname = NULL, resolve = identity) { 3 | 4 | # use RETICULATE_PYTHON_ENV as default 5 | envname <- envname %||% Sys.getenv("RETICULATE_PYTHON_ENV", unset = "r-reticulate") 6 | 7 | # treat environment 'names' containing slashes as full paths 8 | if (grepl("[/\\]", envname)) { 9 | envname <- normalizePath(envname, winslash = "/", mustWork = FALSE) 10 | return(envname) 11 | } 12 | 13 | # otherwise, resolve the environment name as necessary 14 | resolve(envname) 15 | 16 | } 17 | -------------------------------------------------------------------------------- /R/seed.R: -------------------------------------------------------------------------------- 1 | 2 | #' Set Python and NumPy random seeds 3 | #' 4 | #' Set various random seeds required to ensure reproducible results. The 5 | #' provided `seed` value will establish a new random seed for Python and NumPy, 6 | #' and will also (by default) disable hash randomization. 7 | #' 8 | #' @param seed A single value, interpreted as an integer 9 | #' @param disable_hash_randomization Disable hash randomization, which is 10 | #' another common source of variable results. See 11 | #' 12 | #' 13 | #' @details This function does not set the R random seed, for that you 14 | #' should call [set.seed()]. 15 | #' 16 | #' @export 17 | py_set_seed <- function(seed, disable_hash_randomization = TRUE) { 18 | 19 | # cast to integer 20 | seed <- as.integer(seed) 21 | 22 | # Ensure reproducibility for certain hash-based operations for Python 3 23 | # References: https://docs.python.org/3/using/cmdline.html#envvar-PYTHONHASHSEED 24 | # https://github.com/fchollet/keras/issues/2280#issuecomment-306959926 25 | if (disable_hash_randomization) { 26 | os <- import("os") 27 | Sys.setenv(PYTHONHASHSEED = "0") 28 | os$environ[["PYTHONHASHSEED"]] <- "0" 29 | } 30 | 31 | # set Python python random seed 32 | random <- import("random") 33 | random$seed(seed) 34 | 35 | # set numpy seed if numpy is available 36 | if (py_numpy_available()) { 37 | np <- import("numpy") 38 | np$random$seed(seed) 39 | } 40 | 41 | invisible(NULL) 42 | } 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /R/source.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | #' Read and evaluate a Python script 4 | #' 5 | #' Evaluate a Python script within the Python main module, then make all public 6 | #' (non-module) objects within the main Python module available within the 7 | #' specified R environment. 8 | #' 9 | #' To prevent assignment of objects into R, pass `NULL` for the `envir` 10 | #' parameter. 11 | #' 12 | #' @inheritParams py_run_file 13 | #' 14 | #' @param envir The environment to assign Python objects into (for example, 15 | #' `parent.frame()` or `globalenv()`). Specify `NULL` to not assign Python 16 | #' objects. 17 | #' 18 | #' @export 19 | #' @importFrom utils download.file 20 | source_python <- function(file, envir = parent.frame(), convert = TRUE) { 21 | 22 | # Download file content from URL to a local tempory file 23 | if (!file.exists(file) && isTRUE(grepl("^https?://", file))) { 24 | tmpfile <- tempfile(fileext = ".py") 25 | utils::download.file(url = file, destfile = tmpfile, quiet = TRUE) 26 | file <- tmpfile 27 | on.exit(unlink(file), add = TRUE) 28 | } 29 | 30 | # source the python script into the main python module 31 | main_dict <- py_run_file(file, local = FALSE, convert = convert) 32 | on.exit(py_flush_output(), add = TRUE) 33 | 34 | # copy objects from the main python module into the specified R environment 35 | if (!is.null(envir)) { 36 | names <- py_dict_get_keys_as_str(main_dict) 37 | names <- names[!startsWith(names, '_')] 38 | names <- names[-match("r", names)] # don't export "R interface object" 39 | Encoding(names) <- "UTF-8" 40 | 41 | for (name in names) { 42 | value <- main_dict[[name]] 43 | if (!inherits(value, "python.builtin.module")) 44 | assign(name, value, envir = envir) 45 | } 46 | } 47 | 48 | # return nothing 49 | invisible(NULL) 50 | } 51 | 52 | -------------------------------------------------------------------------------- /R/utils-format.R: -------------------------------------------------------------------------------- 1 | 2 | sprintf <- function(fmt, ...) { 3 | 4 | dots <- eval(substitute(alist(...))) 5 | if (length(dots) == 0) 6 | return(fmt) 7 | 8 | base::sprintf(fmt, ...) 9 | 10 | } 11 | 12 | stopf <- function(fmt = "", ..., call. = FALSE) { 13 | stop(sprintf(fmt, ...), call. = call.) 14 | } 15 | 16 | warningf <- function(fmt = "", ..., call. = FALSE, immediate. = FALSE) { 17 | warning(sprintf(fmt, ...), call. = call., immediate. = immediate.) 18 | } 19 | 20 | messagef <- function(fmt = "", ..., appendLF = TRUE) { 21 | message(sprintf(fmt, ...), appendLF = appendLF) 22 | } 23 | 24 | printf <- function(fmt = "", ..., file = stdout()) { 25 | if (!is.null(fmt)) 26 | cat(sprintf(fmt, ...), file = file, sep = "") 27 | } 28 | 29 | eprintf <- function(fmt = "", ..., file = stderr()) { 30 | if (!is.null(fmt)) 31 | cat(sprintf(fmt, ...), file = file, sep = "") 32 | } 33 | 34 | writef <- function(fmt = "", ..., con = stdout()) { 35 | if (!is.null(fmt)) 36 | writeLines(sprintf(fmt, ...), con = con) 37 | } 38 | 39 | ewritef <- function(fmt = "", ..., con = stderr()) { 40 | if (!is.null(fmt)) 41 | writeLines(sprintf(fmt, ...), con = con) 42 | } 43 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | 2 | url: https://rstudio.github.io/reticulate 3 | 4 | template: 5 | params: 6 | bootswatch: flatly 7 | 8 | development: 9 | mode: auto 10 | 11 | reference: 12 | 13 | - title: "Python Requirements" 14 | contents: 15 | - py_require 16 | 17 | - title: "Python Execution" 18 | contents: 19 | - import 20 | - source_python 21 | - repl_python 22 | - eng_python 23 | - py_run 24 | - py_eval 25 | - py 26 | 27 | - title: "Python Types" 28 | contents: 29 | - dict 30 | - tuple 31 | - iterate 32 | - py_iterator 33 | - with.python.builtin.object 34 | 35 | - title: "Python Configuration" 36 | contents: 37 | - install_python 38 | - py_config 39 | - py_discover_config 40 | - py_available 41 | - py_module_available 42 | - use_python 43 | - py_exe 44 | - py_version 45 | 46 | - title: "Python Output" 47 | contents: 48 | - py_capture_output 49 | - py_suppress_warnings 50 | 51 | - title: "Arrays" 52 | contents: 53 | - np_array 54 | - array_reshape 55 | 56 | - title: "Persistence" 57 | contents: 58 | - py_save_object 59 | - py_load_object 60 | 61 | - title: "Low-Level Interface" 62 | contents: 63 | - py_has_attr 64 | - py_get_attr 65 | - py_set_attr 66 | - py_del_attr 67 | - py_list_attributes 68 | - py_get_item 69 | - py_set_item 70 | - py_del_item 71 | - py_call 72 | - py_to_r 73 | - r_to_py 74 | - as.character.python.builtin.bytes 75 | - as.character.python.builtin.str 76 | - py_is_null_xptr 77 | - py_id 78 | - py_len 79 | - py_bool 80 | - py_str 81 | - py_unicode 82 | - py_set_seed 83 | - py_last_error 84 | - py_help 85 | - py_func 86 | - py_main_thread_func 87 | - py_ellipsis 88 | - py_none 89 | - PyClass 90 | - py_function_custom_scaffold 91 | - nameOfClass.python.builtin.type 92 | - ==.python.builtin.object 93 | 94 | - title: "Package Installation" 95 | contents: 96 | - py_install 97 | - py_list_packages 98 | - virtualenv-tools 99 | - configure_environment 100 | - conda-tools 101 | - conda_run2 102 | - uv_run_tool 103 | 104 | - title: "Miniconda" 105 | contents: 106 | - install_miniconda 107 | - miniconda_uninstall 108 | - miniconda_path 109 | - miniconda_update 110 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | New features and fixes. See NEWS.md for details. 2 | 3 | ## R CMD check results 4 | 5 | ## revdepcheck results 6 | 7 | We checked 247 reverse dependencies (246 from CRAN + 1 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package. 8 | 9 | * We saw 0 new problems 10 | * We failed to check 1 packages 11 | 12 | Issues with CRAN packages are summarised below. 13 | 14 | ### Failed to check 15 | 16 | * lilikoi (NA) 17 | -------------------------------------------------------------------------------- /inst/config/config.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import sys 3 | import os 4 | 5 | # The 'sysconfig' module is only available with Python 2.7 and newer, but 6 | # an equivalent module in 'distutils' is available for Python 2.6. 7 | if sys.version_info < (2, 7): 8 | from distutils import sysconfig 9 | else: 10 | import sysconfig 11 | 12 | # The 'imp' module is deprecated since Python 3.4, and the use of 13 | # 'importlib' is recommended instead. 14 | if sys.version_info < (3, 4): 15 | import imp 16 | 17 | def module_path(name): 18 | if name in sys.builtin_module_names: 19 | return "[builtin module]" 20 | spec = imp.find_module(name) 21 | return spec[1] 22 | 23 | else: 24 | from importlib import util 25 | 26 | def module_path(name): 27 | if name in sys.builtin_module_names: 28 | return "[builtin module]" 29 | spec = util.find_spec(name) 30 | origin = spec.origin 31 | return os.path.dirname(origin) 32 | 33 | 34 | # Get appropriate path-entry separator for platform 35 | pathsep = ";" if os.name == "nt" else ":" 36 | this_script_dir = os.path.dirname(__file__) 37 | 38 | # Read default configuration values 39 | # fmt: off 40 | config = { 41 | "Architecture" : platform.architecture()[0], 42 | "Version" : str(sys.version).replace("\n", " "), 43 | "VersionNumber" : str(sys.version_info[0]) + "." + str(sys.version_info[1]), 44 | "Prefix" : getattr(sys, "prefix", ""), 45 | "ExecPrefix" : getattr(sys, "exec_prefix", ""), 46 | "BaseExecPrefix" : getattr(sys, "base_exec_prefix", ""), 47 | "PythonPath" : pathsep.join((x or "." for x in sys.path 48 | if x != this_script_dir)), 49 | "LIBPL" : sysconfig.get_config_var("LIBPL"), 50 | "LIBDIR" : sysconfig.get_config_var("LIBDIR"), 51 | "SharedLibrary" : sysconfig.get_config_var("Py_ENABLE_SHARED"), 52 | "Executable" : getattr(sys, "executable", ""), 53 | "BaseExecutable" : getattr(sys, "_base_executable", ""), 54 | } 55 | # fmt: on 56 | 57 | # detect if this is a conda managed python 58 | # https://stackoverflow.com/a/21282816/5128728 59 | if sys.version_info >= (3, 7): 60 | is_conda = os.path.exists(os.path.join(sys.prefix, "conda-meta")) 61 | else: 62 | is_conda = "conda" in sys.version 63 | config["IsConda"] = is_conda 64 | 65 | # Read numpy configuration (if available) 66 | try: 67 | import numpy 68 | 69 | config["NumpyPath"] = str(numpy.__path__[0]) 70 | config["NumpyVersion"] = str(numpy.__version__) 71 | except: 72 | pass 73 | 74 | # Read required module information (if requested) 75 | try: 76 | required_module = os.environ["RETICULATE_REQUIRED_MODULE"] 77 | if required_module is not None and len(required_module) > 0: 78 | config["RequiredModule"] = required_module 79 | config["RequiredModulePath"] = module_path(required_module) 80 | except: 81 | pass 82 | 83 | # Write configuration to stdout 84 | lines = [str(key) + ": " + str(val) for (key, val) in config.items()] 85 | text = "\n".join(lines) 86 | sys.stdout.write(text) 87 | -------------------------------------------------------------------------------- /inst/python/rpytools/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /inst/python/rpytools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/inst/python/rpytools/__init__.py -------------------------------------------------------------------------------- /inst/python/rpytools/ark_variables.py: -------------------------------------------------------------------------------- 1 | from itertools import islice 2 | 3 | MAX_DISPLAY = 100 4 | 5 | 6 | def get_keys_and_children(inspector): 7 | # positron frontend only displays the first 100 items, then the count of the rest. 8 | keys, values = [], [] 9 | keys_iterator = iter(inspector.get_children()) 10 | for key in islice(keys_iterator, MAX_DISPLAY): 11 | keys.append(str(key)) 12 | values.append(inspector.get_child(key)) 13 | 14 | if len(keys) == MAX_DISPLAY: 15 | # check if there are more children 16 | n_children = inspector.get_length() 17 | if n_children == 0: 18 | # no len() method, finish iteratoring over keys_iterator to get the true size 19 | for n_children, _ in enumerate(keys_iterator, MAX_DISPLAY + 1): 20 | pass 21 | n_remaining = n_children - MAX_DISPLAY 22 | if n_remaining > 0: 23 | keys.append("[[...]]") 24 | values.append(ChildrenOverflow(n_remaining)) 25 | 26 | return keys, values 27 | 28 | 29 | def get_child(inspector, index): 30 | key = next(islice(inspector.get_children(), index - 1, None)) 31 | return inspector.get_child(key) 32 | 33 | 34 | class ChildrenOverflow: 35 | def __init__(self, n_remaining): 36 | self.n_remaining = n_remaining 37 | -------------------------------------------------------------------------------- /inst/python/rpytools/call.py: -------------------------------------------------------------------------------- 1 | from rpycall import call_r_function 2 | 3 | 4 | def make_python_function(f, name=None): 5 | def python_function(*args, **kwargs): 6 | return call_r_function(f, *args, **kwargs) 7 | 8 | if name is not None: 9 | python_function.__name__ = name 10 | 11 | return python_function 12 | -------------------------------------------------------------------------------- /inst/python/rpytools/generator.py: -------------------------------------------------------------------------------- 1 | import rpycall 2 | import threading 3 | 4 | import sys 5 | 6 | is_py2 = sys.version[0] == "2" 7 | if is_py2: 8 | import Queue as queue 9 | else: 10 | import queue as queue 11 | 12 | _completed_sentinel = object() 13 | 14 | 15 | class RGenerator(object): 16 | def __init__(self, r_function, prefetch=0): 17 | self.r_function = r_function 18 | self.prefetch = prefetch 19 | self.values_queue = queue.Queue() 20 | self.completed = False 21 | self._pending_tend_queue = False 22 | if prefetch: 23 | self._tend_queue() 24 | 25 | def __iter__(self): 26 | return self 27 | 28 | def next(self): 29 | return self.__next__() 30 | 31 | def __next__(self): 32 | self._tend_queue(1) 33 | val = self._get_or_raise() 34 | if self.prefetch: 35 | self._tend_queue() 36 | return val 37 | 38 | def _tend_queue(self, min_fetch=0): 39 | if self.completed: 40 | return 41 | 42 | ## Prefetch and enqueue generator generated values. 43 | 44 | # If we're not on the main thread, make sure there is a pending call to 45 | # this function from the main thread and return. 46 | if threading.current_thread() is not threading.main_thread(): 47 | if not self._pending_tend_queue: 48 | self._pending_tend_queue = True 49 | rpycall.schedule_python_function_on_main_thread( 50 | self._tend_queue, min_fetch 51 | ) 52 | return 53 | 54 | # We're on the main thread, call the generator and put values on the queue. 55 | self._pending_tend_queue = False 56 | 57 | fetch = max(min_fetch, self.prefetch - self.values_queue.qsize()) 58 | for _ in range(fetch): 59 | try: 60 | val = self.r_function() 61 | except StopIteration: 62 | self.values_queue.put(_completed_sentinel) 63 | self.completed = True 64 | return 65 | 66 | self.values_queue.put(val) 67 | 68 | def _get_or_raise(self): 69 | try: 70 | # only wait/block-thread/yield-to-another-thread if the R generator 71 | # is not exhausted. 72 | val = self.values_queue.get(block=(not self.completed)) 73 | if val is _completed_sentinel: 74 | raise StopIteration() 75 | return val 76 | except queue.Empty: 77 | # only get here if self.completed = True and the queue is empty, 78 | # meaning we've already pulled off _completed_sentinel and 79 | # raised StopIteration 80 | raise StopIteration() 81 | -------------------------------------------------------------------------------- /inst/python/rpytools/ipython.py: -------------------------------------------------------------------------------- 1 | import IPython 2 | from traitlets.config import Config 3 | 4 | 5 | _c = Config() 6 | 7 | _c.InteractiveShell.confirm_exit = False 8 | _c.TerminalIPythonApp.display_banner = False 9 | 10 | # c.InteractiveShell.colors = 'Neutral' 11 | # 'Neutral', 'NoColor', 'LightBG', 'Linux' 12 | 13 | # Only need to register callbacks on first init 14 | # There is probably a better way to not register the same callback multiple times 15 | # doing a straight comparison like `fn in callbacks_list` fails because 16 | # ipython decorates the registered callbacks 17 | _c.InteractiveShellApp.exec_lines = [ 18 | """ 19 | def _reticulate_init(): 20 | import sys 21 | ev = get_ipython().events 22 | if not any(fn.__name__ == 'flush' for fn in ev.callbacks['post_run_cell']): 23 | ev.register("post_run_cell", sys.stdout.flush) 24 | ev.register("post_run_cell", sys.stderr.flush) 25 | 26 | _reticulate_init() 27 | del _reticulate_init 28 | """ 29 | ] 30 | 31 | 32 | def start_ipython(): 33 | IPython.start_ipython(config=_c) 34 | -------------------------------------------------------------------------------- /inst/python/rpytools/run.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | 4 | 5 | def run_file(path): 6 | with open(path, "r") as file: 7 | file_content = file.read() 8 | 9 | # ipykernel patches the loader, so that 10 | # `import __main__` does not produce the "real" __main__, but rather, 11 | # the facade that is the user facing __main__ 12 | # to get the "real" main, do: 13 | # d = sys.modules["__main__"].__dict__ 14 | from __main__ import __dict__ as d 15 | 16 | exec(file_content, d, d) 17 | 18 | 19 | class RunMainScriptContext: 20 | def __init__(self, path, argv=None): 21 | self.path = path 22 | self.argv = tuple(argv) if argv is not None else None 23 | 24 | def __enter__(self): 25 | sys.path.insert(0, os.path.dirname(self.path)) 26 | 27 | if self.argv is not None: 28 | self._orig_sys_argv = sys.argv 29 | sys.argv = [self.path] + list(self.argv) 30 | 31 | def __exit__(self, *_): 32 | # try restore sys.path 33 | try: 34 | sys.path.remove(os.path.dirname(self.path)) 35 | except ValueError: 36 | pass 37 | # try restore sys.argv if we patched it 38 | if self.argv is not None: 39 | # restore sys.argv if it's unmodified from what we set it to. 40 | # otherwise, leave it as-is. 41 | patched_argv = [self.path] + list(self.argv) 42 | if sys.argv == patched_argv: 43 | sys.argv = self._orig_sys_argv 44 | 45 | 46 | def _launch_lsp_server_on_thread(path, args): 47 | # used by Positron reticulate launcher... 48 | # TODO: update Positron to replace usage of this with `run_file_on_thread()` 49 | 50 | return run_file_on_thread(path, args) 51 | 52 | 53 | def set_blank_io_streams(): 54 | import sys 55 | import os 56 | sys.stdout = open(os.devnull, "w") 57 | sys.stderr = open(os.devnull, "w") 58 | 59 | 60 | def run_file_on_thread(path, argv=None, init_globals=None, run_name="__main__"): 61 | # for now, leave sys.argv and sys.path permanently modified. 62 | # Later, revisit if it's desirable/safe to restore after the initial 63 | # lsp event loop startup. 64 | import _thread 65 | from runpy import run_path 66 | 67 | RunMainScriptContext(path, argv).__enter__() 68 | _thread.start_new_thread( 69 | run_path, 70 | (path,), 71 | { 72 | "run_name": run_name, 73 | "init_globals": init_globals, 74 | }, 75 | ) 76 | -------------------------------------------------------------------------------- /inst/python/rpytools/subprocess.py: -------------------------------------------------------------------------------- 1 | # When running on Windows in RStudio, we need to patch subprocess.Popen 2 | # https://github.com/rstudio/reticulate/issues/1448 3 | 4 | 5 | def patch_subprocess_Popen(): 6 | from functools import wraps 7 | import subprocess 8 | 9 | og_Popen__init__ = subprocess.Popen.__init__ 10 | 11 | @wraps(subprocess.Popen.__init__) 12 | def __init__(self, *args, **kwargs): 13 | if kwargs.get("stdin") is None: 14 | kwargs["stdin"] = subprocess.DEVNULL 15 | return og_Popen__init__(self, *args, **kwargs) 16 | 17 | subprocess.Popen.__init__ = __init__ 18 | -------------------------------------------------------------------------------- /inst/python/rpytools/test.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import collections 3 | 4 | import sys 5 | 6 | is_py2 = sys.version[0] == "2" 7 | if is_py2: 8 | import Queue as queue 9 | else: 10 | import queue as queue 11 | 12 | 13 | def isScalar(x): 14 | return not isinstance(x, (list, tuple)) 15 | 16 | 17 | def isList(x): 18 | return isinstance(x, (list)) 19 | 20 | 21 | def asString(x): 22 | return str(x) 23 | 24 | 25 | def makeDict(): 26 | return {"a": 1.0, "c": 3.0, "b": 2.0} 27 | 28 | 29 | def makeTuple(): 30 | return (1.0, 2.0, 3.0) 31 | 32 | 33 | def makeTupleWithOrderedDict(): 34 | return (1.0, collections.OrderedDict({"b": 777, "a": 22})) 35 | 36 | 37 | def makeIterator(x): 38 | return iter(x) 39 | 40 | 41 | def makeGenerator(n): 42 | i = 0 43 | while i < n: 44 | yield i 45 | i += 1 46 | 47 | 48 | def iterateOnThread(iter): 49 | results = [] 50 | 51 | def iteration_worker(): 52 | for i in iter: 53 | results.append(i) 54 | 55 | thread = threading.Thread(target=iteration_worker) 56 | thread.start() 57 | while thread.is_alive(): 58 | thread.join(0.1) 59 | return results 60 | 61 | 62 | def invokeOnThread(f, *args, **kwargs): 63 | result = [] 64 | 65 | def invoke_worker(): 66 | result.append(f(*args, **kwargs)) 67 | 68 | thread = threading.Thread(target=invoke_worker) 69 | thread.start() 70 | while thread.is_alive(): 71 | thread.join(0.1) 72 | return result[0] 73 | 74 | 75 | def reflect(x): 76 | return x 77 | 78 | 79 | def callFunc(f, *args, **kwargs): 80 | return f(*args, **kwargs) 81 | 82 | 83 | def testThrowError(): 84 | throwError() 85 | 86 | 87 | def throwError(): 88 | raise ValueError("A very specific bad thing happened") 89 | 90 | 91 | class PythonClass(object): 92 | 93 | FOO = 1 94 | BAR = 2 95 | 96 | @classmethod 97 | def class_method(cls): 98 | return cls.FOO 99 | 100 | 101 | class PythonCallable(object): 102 | 103 | FOO = 1 104 | BAR = 2 105 | 106 | """ Call a callable 107 | Args: 108 | arg1: First argument. 109 | """ 110 | 111 | def __call__(self, arg1): 112 | return arg1 113 | 114 | 115 | def create_callable(): 116 | return PythonCallable() 117 | 118 | 119 | dict_with_callable = dict(callable=create_callable()) 120 | -------------------------------------------------------------------------------- /inst/python/rpytools/thread.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import queue as queue 3 | from rpycall import call_r_function, schedule_python_function_on_main_thread 4 | 5 | 6 | def safe_call_r_function(f, args, kwargs): 7 | try: 8 | return call_r_function(f, *args, **kwargs), None 9 | except Exception as e: # TODO: should we catch BaseException too? KeyboardInterrupt? 10 | return None, e 11 | 12 | 13 | def safe_call_r_function_on_main_thread(f, *args, **kwargs): 14 | result = queue.Queue() 15 | schedule_python_function_on_main_thread( 16 | lambda: result.put(safe_call_r_function(f, args, kwargs)), 17 | None, 18 | ) 19 | return result.get() 20 | -------------------------------------------------------------------------------- /man/PyClass.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/class.R 3 | \name{PyClass} 4 | \alias{PyClass} 5 | \title{Create a python class} 6 | \usage{ 7 | PyClass(classname, defs = list(), inherit = NULL) 8 | } 9 | \arguments{ 10 | \item{classname}{Name of the class. The class name is useful for S3 method 11 | dispatch.} 12 | 13 | \item{defs}{A named list of class definitions - functions, attributes, etc.} 14 | 15 | \item{inherit}{A list of Python class objects. Usually these objects have 16 | the \code{python.builtin.type} S3 class.} 17 | } 18 | \description{ 19 | Create a python class 20 | } 21 | \examples{ 22 | \dontrun{ 23 | Hi <- PyClass("Hi", list( 24 | name = NULL, 25 | `__init__` = function(self, name) { 26 | self$name <- name 27 | NULL 28 | }, 29 | say_hi = function(self) { 30 | paste0("Hi ", self$name) 31 | } 32 | )) 33 | 34 | a <- Hi("World") 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /man/array_reshape.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/array.R 3 | \name{array_reshape} 4 | \alias{array_reshape} 5 | \title{Reshape an Array} 6 | \usage{ 7 | array_reshape(x, dim, order = c("C", "F")) 8 | } 9 | \arguments{ 10 | \item{x}{An array} 11 | 12 | \item{dim}{The new dimensions to be set on the array.} 13 | 14 | \item{order}{The order in which elements of \code{x} should be read during 15 | the rearrangement. \code{"C"} means elements should be read in row-major 16 | order, with the last index changing fastest; \code{"F"} means elements should 17 | be read in column-major order, with the first index changing fastest.} 18 | } 19 | \description{ 20 | Reshape (reindex) a multi-dimensional array, using row-major (C-style) reshaping 21 | semantics by default. 22 | } 23 | \details{ 24 | This function differs from e.g. \code{dim(x) <- dim} in a very important way: by 25 | default, \code{array_reshape()} will fill the new dimensions in row-major (\code{C}-style) 26 | ordering, while \code{\link[=dim<-]{dim<-()}} will fill new dimensions in column-major 27 | (\code{F}ortran-style) ordering. This is done to be consistent with libraries 28 | like NumPy, Keras, and TensorFlow, which default to this sort of ordering when 29 | reshaping arrays. See the examples for why this difference may be important. 30 | } 31 | \examples{ 32 | \dontrun{ 33 | # let's construct a 2x2 array from a vector of 4 elements 34 | x <- 1:4 35 | 36 | # rearrange will fill the array row-wise 37 | array_reshape(x, c(2, 2)) 38 | # [,1] [,2] 39 | # [1,] 1 2 40 | # [2,] 3 4 41 | # setting the dimensions 'fills' the array col-wise 42 | dim(x) <- c(2, 2) 43 | x 44 | # [,1] [,2] 45 | # [1,] 1 3 46 | # [2,] 2 4 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /man/as.character.python.builtin.bytes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{as.character.python.builtin.bytes} 4 | \alias{as.character.python.builtin.bytes} 5 | \alias{as.raw.python.builtin.bytes} 6 | \title{Convert Python bytes to an R character or raw vector} 7 | \usage{ 8 | \method{as.character}{python.builtin.bytes}( 9 | x, 10 | encoding = "utf-8", 11 | errors = "strict", 12 | nul = stop("Embedded NUL in string."), 13 | ... 14 | ) 15 | 16 | \method{as.raw}{python.builtin.bytes}(x) 17 | } 18 | \arguments{ 19 | \item{x}{object to be coerced or tested.} 20 | 21 | \item{encoding}{Encoding to use for conversion (defaults to utf-8)} 22 | 23 | \item{errors}{Policy for handling conversion errors. Default is 'strict' 24 | which raises an error. Other possible values are 'ignore' and 'replace'.} 25 | 26 | \item{nul}{Action to take if the bytes contain an embedded NUL (\verb{\\x00}). 27 | Python allows embedded \code{NUL}s in strings, while R does not. There are four 28 | options for handling embedded \code{NUL}s: 29 | \enumerate{ 30 | \item Error: This is the default 31 | \item Replace: Supply a replacement string: \code{nul = ""} 32 | \item Remove: Supply an empty string: \code{nul = ""} 33 | \item Split: Supply an R \code{NULL} to indicate that string should be split at embedded \code{NUL} bytes: \code{nul = NULL} 34 | }} 35 | 36 | \item{...}{further arguments passed to or from other methods.} 37 | } 38 | \description{ 39 | Convert Python bytes to an R character or raw vector 40 | } 41 | \examples{ 42 | \dontshow{if (reticulate::py_available()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 43 | # A bytes object with embedded NULs 44 | b <- import_builtins(convert = FALSE)$bytes( 45 | as.raw(c(0x61, 0x20, 0x62, 0x00, 0x63, 0x20, 0x64)) # "a bc d" 46 | ) 47 | 48 | try(as.character(b)) # Error : Embedded NUL in string. 49 | as.character(b, nul = "") # Replace: "a bc d" 50 | as.character(b, nul = "") # Remove: "a bc d" 51 | as.character(b, nul = NULL) # Split: "a b" "c d" 52 | \dontshow{\}) # examplesIf} 53 | } 54 | \seealso{ 55 | \code{\link[=as.character.python.builtin.str]{as.character.python.builtin.str()}} 56 | } 57 | -------------------------------------------------------------------------------- /man/as.character.python.builtin.str.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{as.character.python.builtin.str} 4 | \alias{as.character.python.builtin.str} 5 | \title{Convert a Python string to an R Character Vector} 6 | \usage{ 7 | \method{as.character}{python.builtin.str}(x, nul = stop("Embedded NUL in string."), ...) 8 | } 9 | \arguments{ 10 | \item{x}{A Python string} 11 | 12 | \item{nul}{Action to take if the Python string contains an embedded NUL (\verb{\\x00}). 13 | Python allows embedded \code{NUL}s in strings, while R does not. There are four 14 | options for handling embedded \code{NUL}s: 15 | \enumerate{ 16 | \item Error: This is the default 17 | \item Replace: Supply a replacement string: \code{nul = ""} 18 | \item Remove: Supply an empty string: \code{nul = ""} 19 | \item Split: Supply an R \code{NULL} to indicate that string should be split at embedded \code{NUL} bytes: \code{nul = NULL} 20 | }} 21 | 22 | \item{...}{Unused} 23 | } 24 | \value{ 25 | An R character vector. The returned vector will always of length 1, 26 | unless \code{nul = NULL} was supplied. 27 | } 28 | \description{ 29 | Convert a Python string to an R Character Vector 30 | } 31 | \examples{ 32 | \dontshow{if (reticulate::py_available()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 33 | # Given a Python function that errors when it attempts to return 34 | # a string with an embedded NUL 35 | py_run_string(' 36 | def get_string_w_nul(): 37 | return "a b" + chr(0) + "c d" 38 | ') 39 | get_string_w_nul <- py$get_string_w_nul 40 | 41 | try(get_string_w_nul()) # Error : Embedded NUL in string. 42 | 43 | # To get the string into R, use `r_to_py()` on the function to stop it from 44 | # eagerly converting the Python string to R, and then call `as.character()` with 45 | # a `nul` argument supplied to convert the string to R. 46 | get_string_w_nul <- r_to_py(get_string_w_nul) 47 | get_string_w_nul() # unconverted python string: inherits(x, 'python.builtin.str') 48 | as.character(get_string_w_nul(), nul = "") # Replace: "a bc d" 49 | as.character(get_string_w_nul(), nul = "") # Remove: "a bc d" 50 | as.character(get_string_w_nul(), nul = NULL) # Split: "a b" "c d" 51 | 52 | # cleanup example 53 | rm(get_string_w_nul); py$get_string_w_nul <- NULL 54 | \dontshow{\}) # examplesIf} 55 | } 56 | -------------------------------------------------------------------------------- /man/conda_run2.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/conda.R 3 | \name{conda_run2} 4 | \alias{conda_run2} 5 | \title{Run a command in a conda environment} 6 | \usage{ 7 | conda_run2( 8 | cmd, 9 | args = c(), 10 | conda = "auto", 11 | envname = NULL, 12 | cmd_line = paste(shQuote(cmd), paste(args, collapse = " ")), 13 | intern = FALSE, 14 | echo = !intern 15 | ) 16 | } 17 | \arguments{ 18 | \item{cmd}{The system command to be invoked, as a character string.} 19 | 20 | \item{args}{A character vector of arguments to the command. The arguments should 21 | be quoted e.g. by \code{shQuote()} in case they contain space or other special 22 | characters (a double quote or backslash on Windows, shell-specific special 23 | characters on Unix).} 24 | 25 | \item{conda}{The path to a \code{conda} executable. Use \code{"auto"} to allow 26 | \code{reticulate} to automatically find an appropriate \code{conda} binary. 27 | See \strong{Finding Conda} and \code{\link[=conda_binary]{conda_binary()}} for more details.} 28 | 29 | \item{envname}{The name of, or path to, a conda environment.} 30 | 31 | \item{cmd_line}{The command line to be executed, as a character string. This 32 | is automatically generated from \code{cmd} and \code{args}, but can be provided 33 | directly if needed (if provided, it overrides \code{cmd} and \code{args}).} 34 | 35 | \item{intern}{A logical (not \code{NA}) which indicates whether to capture the 36 | output of the command as an R character vector. If \code{FALSE} (the default), the 37 | return value is the error code (\code{0} for success).} 38 | 39 | \item{echo}{A logical (not \code{NA}) which indicates whether to echo the command to 40 | the console before running it.} 41 | } 42 | \value{ 43 | \code{conda_run2()} runs a command in the desired conda environment. If 44 | \code{intern = TRUE} the output is returned as a character vector; if \code{intern = FALSE} (the 45 | deafult), then the return value is the error code (0 for success). See 46 | \code{\link[=shell]{shell()}} (on windows) or \code{\link[=system2]{system2()}} on macOS or Linux for more details. 47 | } 48 | \description{ 49 | This function runs a command in a chosen conda environment. 50 | } 51 | \details{ 52 | Note that, whilst the syntax is similar to \code{\link[=system2]{system2()}}, the function 53 | dynamically generates a shell script with commands to activate the chosen 54 | conda environent. This avoids issues with quoting, as discussed in this 55 | \href{https://github.com/conda/conda/issues/10972}{GitHub issue}. 56 | } 57 | \seealso{ 58 | \code{\link{conda-tools}} 59 | } 60 | -------------------------------------------------------------------------------- /man/configure_environment.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python-packages.R 3 | \name{configure_environment} 4 | \alias{configure_environment} 5 | \title{Configure a Python Environment} 6 | \usage{ 7 | configure_environment(package = NULL, force = FALSE) 8 | } 9 | \arguments{ 10 | \item{package}{The name of a package to configure. When \code{NULL}, \code{reticulate} 11 | will instead look at all loaded packages and discover their associated 12 | Python requirements.} 13 | 14 | \item{force}{Boolean; force configuration of the Python environment? Note 15 | that \code{configure_environment()} is a no-op within non-interactive \R 16 | sessions. Use this if you require automatic environment configuration, e.g. 17 | when testing a package on a continuous integration service.} 18 | } 19 | \description{ 20 | Configure a Python environment, satisfying the Python dependencies of any 21 | loaded \R packages. 22 | } 23 | \details{ 24 | Normally, this function should only be used by package authors, who want 25 | to ensure that their package dependencies are installed in the active 26 | Python environment. For example: 27 | 28 | \if{html}{\out{
}}\preformatted{.onLoad <- function(libname, pkgname) \{ 29 | reticulate::configure_environment(pkgname) 30 | \} 31 | }\if{html}{\out{
}} 32 | 33 | If the Python session has not yet been initialized, or if the user is not 34 | using the default Miniconda Python installation, no action will be taken. 35 | Otherwise, \code{reticulate} will take this as a signal to install any required 36 | Python dependencies into the user's Python environment. 37 | 38 | If you'd like to disable \code{reticulate}'s auto-configure behavior altogether, 39 | you can set the environment variable: 40 | 41 | \if{html}{\out{
}}\preformatted{RETICULATE_AUTOCONFIGURE = FALSE 42 | }\if{html}{\out{
}} 43 | 44 | e.g. in your \verb{~/.Renviron} or similar. 45 | 46 | Note that, in the case where the Python session has not yet been initialized, 47 | \code{reticulate} will automatically ensure your required Python dependencies 48 | are installed after the Python session is initialized (when appropriate). 49 | } 50 | -------------------------------------------------------------------------------- /man/dict.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{dict} 4 | \alias{dict} 5 | \alias{py_dict} 6 | \title{Create Python dictionary} 7 | \usage{ 8 | dict(..., convert = FALSE) 9 | 10 | py_dict(keys, values, convert = FALSE) 11 | } 12 | \arguments{ 13 | \item{...}{Name/value pairs for dictionary (or a single named list to be 14 | converted to a dictionary).} 15 | 16 | \item{convert}{\code{TRUE} to automatically convert Python objects to their R 17 | equivalent. If you pass \code{FALSE} you can do manual conversion using the 18 | \code{\link[=py_to_r]{py_to_r()}} function.} 19 | 20 | \item{keys}{Keys to dictionary (can be Python objects)} 21 | 22 | \item{values}{Values for dictionary} 23 | } 24 | \value{ 25 | A Python dictionary 26 | } 27 | \description{ 28 | Create a Python dictionary object, including a dictionary whose keys are 29 | other Python objects rather than character vectors. 30 | } 31 | \note{ 32 | The returned dictionary will not automatically convert its elements 33 | from Python to R. You can do manual conversion with the \code{\link[=py_to_r]{py_to_r()}} 34 | function or pass \code{convert = TRUE} to request automatic conversion. 35 | } 36 | -------------------------------------------------------------------------------- /man/figures/python_r_object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/man/figures/python_r_object.png -------------------------------------------------------------------------------- /man/figures/python_repl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/man/figures/python_repl.png -------------------------------------------------------------------------------- /man/figures/reticulate_completion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/man/figures/reticulate_completion.png -------------------------------------------------------------------------------- /man/figures/reticulated_python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/man/figures/reticulated_python.png -------------------------------------------------------------------------------- /man/figures/rmarkdown_engine_zoomed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/man/figures/rmarkdown_engine_zoomed.png -------------------------------------------------------------------------------- /man/install_miniconda.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/miniconda.R 3 | \name{install_miniconda} 4 | \alias{install_miniconda} 5 | \title{Install Miniconda} 6 | \usage{ 7 | install_miniconda(path = miniconda_path(), update = TRUE, force = FALSE) 8 | } 9 | \arguments{ 10 | \item{path}{The location where Miniconda is (or should be) installed. Note 11 | that the Miniconda installer does not support paths containing spaces. See 12 | \link{miniconda_path} for more details on the default path used by \code{reticulate}.} 13 | 14 | \item{update}{Boolean; update to the latest version of Miniconda after 15 | installation?} 16 | 17 | \item{force}{Boolean; force re-installation if Miniconda is already installed 18 | at the requested path?} 19 | } 20 | \description{ 21 | Download the \href{https://docs.conda.io/en/latest/miniconda.html}{Miniconda} 22 | installer, and use it to install Miniconda. 23 | } 24 | \details{ 25 | For arm64 builds of R on macOS, \code{install_miniconda()} will use 26 | binaries from \href{https://github.com/conda-forge/miniforge}{miniforge} instead. 27 | } 28 | \note{ 29 | If you encounter binary incompatibilities between R and Miniconda, a 30 | scripted build and installation of Python from sources can be performed by 31 | \code{\link[=install_python]{install_python()}} 32 | } 33 | \seealso{ 34 | Other miniconda-tools: 35 | \code{\link{miniconda_uninstall}()}, 36 | \code{\link{miniconda_update}()} 37 | } 38 | \concept{miniconda-tools} 39 | -------------------------------------------------------------------------------- /man/install_python.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/install-python.R 3 | \name{install_python} 4 | \alias{install_python} 5 | \title{Install Python} 6 | \usage{ 7 | install_python( 8 | version = "3.11:latest", 9 | list = FALSE, 10 | force = FALSE, 11 | optimized = TRUE 12 | ) 13 | } 14 | \arguments{ 15 | \item{version}{The version of Python to install.} 16 | 17 | \item{list}{Boolean; if set, list the set of available Python versions?} 18 | 19 | \item{force}{Boolean; force re-installation even if the requested version of 20 | Python is already installed?} 21 | 22 | \item{optimized}{Boolean; if \code{TRUE}, installation will take significantly 23 | longer but should result in a faster Python interpreter. Only applicable on 24 | macOS and Linux.} 25 | } 26 | \description{ 27 | Download and install Python, using the 28 | \href{https://github.com/pyenv/pyenv}{pyenv}. and 29 | \href{https://github.com/pyenv-win/pyenv-win}{pyenv-win} projects. 30 | } 31 | \details{ 32 | In general, it is recommended that Python virtual environments are created 33 | using the copies of Python installed by \code{\link[=install_python]{install_python()}}. For example: 34 | 35 | \if{html}{\out{
}}\preformatted{library(reticulate) 36 | version <- "3.9.12" 37 | install_python(version) 38 | virtualenv_create("my-environment", version = version) 39 | use_virtualenv("my-environment") 40 | 41 | # There is also support for a ":latest" suffix to select the latest patch release 42 | install_python("3.9:latest") # install latest patch available at python.org 43 | 44 | # select the latest 3.9.* patch installed locally 45 | virtualenv_create("my-environment", version = "3.9:latest") 46 | }\if{html}{\out{
}} 47 | } 48 | \note{ 49 | On macOS and Linux this will build Python from sources, which may take a few 50 | minutes. Installation will be faster if some build dependencies are 51 | preinstalled. See 52 | \url{https://github.com/pyenv/pyenv/wiki#suggested-build-environment} for example 53 | commands you can run to pre-install system dependencies (requires 54 | administrator privileges). 55 | 56 | For example, on macOS you can pre-run: 57 | 58 | \if{html}{\out{
}}\preformatted{brew install openssl readline sqlite3 xz zlib tcl-tk@8 libb2 59 | }\if{html}{\out{
}} 60 | 61 | If \code{optimized = TRUE}, (the default) Python is build with: 62 | 63 | \if{html}{\out{
}}\preformatted{PYTHON_CONFIGURE_OPTS="--enable-shared --enable-optimizations --with-lto" 64 | PYTHON_CFLAGS="-march=native -mtune=native" 65 | }\if{html}{\out{
}} 66 | 67 | If \code{optimized = FALSE}, Python is built with: 68 | 69 | \if{html}{\out{
}}\preformatted{PYTHON_CONFIGURE_OPTS=--enable-shared 70 | }\if{html}{\out{
}} 71 | 72 | On Windows, prebuilt installers from \url{https://www.python.org} are used. 73 | } 74 | -------------------------------------------------------------------------------- /man/ipython.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/repl.R 3 | \name{ipython} 4 | \alias{ipython} 5 | \title{IPython console} 6 | \usage{ 7 | ipython() 8 | } 9 | \description{ 10 | Launch IPython console app. 11 | } 12 | \details{ 13 | See https://ipython.readthedocs.io/ for features. 14 | } 15 | \keyword{internal} 16 | -------------------------------------------------------------------------------- /man/is_py_object.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/RcppExports.R 3 | \name{is_py_object} 4 | \alias{is_py_object} 5 | \title{Check if x is a Python object} 6 | \usage{ 7 | is_py_object(x) 8 | } 9 | \arguments{ 10 | \item{x}{An \R or Python.} 11 | } 12 | \value{ 13 | \code{TRUE} or \code{FALSE}. 14 | } 15 | \description{ 16 | Checks if \code{x} is a Python object, more efficiently 17 | than \code{inherits(x, "python.builtin.object")}. 18 | } 19 | \keyword{internal} 20 | -------------------------------------------------------------------------------- /man/iterate.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/RcppExports.R, R/python.R 3 | \name{as_iterator} 4 | \alias{as_iterator} 5 | \alias{iterate} 6 | \alias{iter_next} 7 | \title{Traverse a Python iterator or generator} 8 | \usage{ 9 | as_iterator(x) 10 | 11 | iterate(it, f = base::identity, simplify = TRUE) 12 | 13 | iter_next(it, completed = NULL) 14 | } 15 | \arguments{ 16 | \item{x}{Python iterator or iterable} 17 | 18 | \item{it}{Python iterator or generator} 19 | 20 | \item{f}{Function to apply to each item. By default applies the 21 | \code{identity} function which just reflects back the value of the item.} 22 | 23 | \item{simplify}{Should the result be simplified to a vector if possible?} 24 | 25 | \item{completed}{Sentinel value to return from \code{iter_next()} if the iteration 26 | completes (defaults to \code{NULL} but can be any R value you specify).} 27 | } 28 | \value{ 29 | For \code{iterate()}, A list or vector containing the results of calling 30 | \code{f} on each item in \code{x} (invisibly); For \code{iter_next()}, the next 31 | value in the iteration (or the sentinel \code{completed} value if the iteration 32 | is complete). 33 | } 34 | \description{ 35 | Traverse a Python iterator or generator 36 | } 37 | \details{ 38 | Simplification is only attempted all elements are length 1 vectors 39 | of type "character", "complex", "double", "integer", or "logical". 40 | } 41 | -------------------------------------------------------------------------------- /man/miniconda-params.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/miniconda.R 3 | \name{miniconda-params} 4 | \alias{miniconda-params} 5 | \title{miniconda-params} 6 | \arguments{ 7 | \item{path}{The location where Miniconda is (or should be) installed. Note 8 | that the Miniconda installer does not support paths containing spaces. See 9 | \link{miniconda_path} for more details on the default path used by \code{reticulate}.} 10 | } 11 | \description{ 12 | miniconda-params 13 | } 14 | \keyword{internal} 15 | -------------------------------------------------------------------------------- /man/miniconda_path.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/miniconda.R 3 | \name{miniconda_path} 4 | \alias{miniconda_path} 5 | \title{Path to Miniconda} 6 | \usage{ 7 | miniconda_path() 8 | } 9 | \description{ 10 | The path to the Miniconda installation to use. By default, an OS-specific 11 | path is used. If you'd like to instead set your own path, you can set the 12 | \code{RETICULATE_MINICONDA_PATH} environment variable. 13 | } 14 | \concept{miniconda} 15 | -------------------------------------------------------------------------------- /man/miniconda_uninstall.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/miniconda.R 3 | \name{miniconda_uninstall} 4 | \alias{miniconda_uninstall} 5 | \title{Remove Miniconda} 6 | \usage{ 7 | miniconda_uninstall(path = miniconda_path()) 8 | } 9 | \arguments{ 10 | \item{path}{The path in which Miniconda is installed.} 11 | } 12 | \description{ 13 | Uninstall Miniconda. 14 | } 15 | \seealso{ 16 | Other miniconda-tools: 17 | \code{\link{install_miniconda}()}, 18 | \code{\link{miniconda_update}()} 19 | } 20 | \concept{miniconda-tools} 21 | -------------------------------------------------------------------------------- /man/miniconda_update.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/miniconda.R 3 | \name{miniconda_update} 4 | \alias{miniconda_update} 5 | \title{Update Miniconda} 6 | \usage{ 7 | miniconda_update(path = miniconda_path()) 8 | } 9 | \arguments{ 10 | \item{path}{The location where Miniconda is (or should be) installed. Note 11 | that the Miniconda installer does not support paths containing spaces. See 12 | \link{miniconda_path} for more details on the default path used by \code{reticulate}.} 13 | } 14 | \description{ 15 | Update Miniconda to the latest version. 16 | } 17 | \seealso{ 18 | Other miniconda-tools: 19 | \code{\link{install_miniconda}()}, 20 | \code{\link{miniconda_uninstall}()} 21 | } 22 | \concept{miniconda-tools} 23 | -------------------------------------------------------------------------------- /man/nameOfClass.python.builtin.type.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{nameOfClass.python.builtin.type} 4 | \alias{nameOfClass.python.builtin.type} 5 | \title{\code{nameOfClass()} for Python objects} 6 | \usage{ 7 | \method{nameOfClass}{python.builtin.type}(x) 8 | } 9 | \arguments{ 10 | \item{x}{A Python class} 11 | } 12 | \value{ 13 | A scalar string matching the S3 class of objects constructed from the 14 | type. 15 | } 16 | \description{ 17 | This generic enables passing a \code{python.builtin.type} object as the 2nd 18 | argument to \code{base::inherits()}. 19 | } 20 | \examples{ 21 | \dontrun{ 22 | numpy <- import("numpy") 23 | x <- r_to_py(array(1:3)) 24 | inherits(x, numpy$ndarray) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /man/np_array.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/array.R 3 | \name{np_array} 4 | \alias{np_array} 5 | \title{NumPy array} 6 | \usage{ 7 | np_array(data, dtype = NULL, order = "C") 8 | } 9 | \arguments{ 10 | \item{data}{Vector or existing NumPy array providing data for the array} 11 | 12 | \item{dtype}{Numpy data type (e.g. "float32", "float64", etc.)} 13 | 14 | \item{order}{Memory ordering for array. "C" means C order, "F" means Fortran 15 | order.} 16 | } 17 | \value{ 18 | A NumPy array object. 19 | } 20 | \description{ 21 | Create NumPy arrays and convert the data type and in-memory 22 | ordering of existing NumPy arrays. 23 | } 24 | -------------------------------------------------------------------------------- /man/py.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/zzz.R 3 | \docType{data} 4 | \name{py} 5 | \alias{py} 6 | \title{Interact with the Python Main Module} 7 | \format{ 8 | An \R object acting as an interface to the 9 | Python main module. 10 | } 11 | \usage{ 12 | py 13 | } 14 | \description{ 15 | The \code{py} object provides a means for interacting 16 | with the Python main session directly from \R. Python 17 | objects accessed through \code{py} are automatically converted 18 | into \R objects, and can be used with any other \R 19 | functions as needed. 20 | } 21 | \keyword{datasets} 22 | -------------------------------------------------------------------------------- /man/py_available.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/config.R 3 | \name{py_available} 4 | \alias{py_available} 5 | \alias{py_numpy_available} 6 | \title{Check if Python is available on this system} 7 | \usage{ 8 | py_available(initialize = FALSE) 9 | 10 | py_numpy_available(initialize = FALSE) 11 | } 12 | \arguments{ 13 | \item{initialize}{\code{TRUE} to attempt to initialize Python bindings if they 14 | aren't yet available (defaults to \code{FALSE}).} 15 | } 16 | \value{ 17 | Logical indicating whether Python is initialized. 18 | } 19 | \description{ 20 | Check if Python is available on this system 21 | } 22 | \note{ 23 | The \code{py_numpy_available} function is a superset of the 24 | \code{py_available} function (it calls \code{py_available} first before 25 | checking for NumPy). 26 | } 27 | -------------------------------------------------------------------------------- /man/py_bool.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{py_bool} 4 | \alias{py_bool} 5 | \title{Python Truthiness} 6 | \usage{ 7 | py_bool(x) 8 | } 9 | \arguments{ 10 | \item{x, }{A python object.} 11 | } 12 | \value{ 13 | An R scalar logical: \code{TRUE} or \code{FALSE}. If \code{x} is a 14 | null pointer or Python is not initialized, \code{FALSE} is returned. 15 | } 16 | \description{ 17 | Equivalent to \code{bool(x)} in Python, or \verb{not not x}. 18 | } 19 | \details{ 20 | If the Python object defines a \verb{__bool__} method, then that is invoked. 21 | Otherwise, if the object defines a \verb{__len__} method, then \code{TRUE} is 22 | returned if the length is nonzero. If neither \verb{__len__} nor \verb{__bool__} 23 | are defined, then the Python object is considered \code{TRUE}. 24 | } 25 | -------------------------------------------------------------------------------- /man/py_call.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{py_call} 4 | \alias{py_call} 5 | \title{Call a Python callable object} 6 | \usage{ 7 | py_call(x, ...) 8 | } 9 | \arguments{ 10 | \item{...}{Arguments to function (named and/or unnamed)} 11 | } 12 | \value{ 13 | Return value of call as a Python object. 14 | } 15 | \description{ 16 | Call a Python callable object 17 | } 18 | \keyword{internal} 19 | -------------------------------------------------------------------------------- /man/py_capture_output.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{py_capture_output} 4 | \alias{py_capture_output} 5 | \title{Capture and return Python output} 6 | \usage{ 7 | py_capture_output(expr, type = c("stdout", "stderr")) 8 | } 9 | \arguments{ 10 | \item{expr}{Expression to capture stdout for} 11 | 12 | \item{type}{Streams to capture (defaults to both stdout and stderr)} 13 | } 14 | \value{ 15 | Character vector with output 16 | } 17 | \description{ 18 | Capture and return Python output 19 | } 20 | -------------------------------------------------------------------------------- /man/py_config.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/config.R 3 | \name{py_config} 4 | \alias{py_config} 5 | \title{Python configuration} 6 | \usage{ 7 | py_config() 8 | } 9 | \value{ 10 | Information about the version of Python in use, as an \R list with 11 | class \code{"py_config"}. 12 | } 13 | \description{ 14 | Retrieve information about the version of Python currently being used by 15 | \code{reticulate}. 16 | } 17 | \details{ 18 | If Python has not yet been initialized, then calling \code{py_config()} will force 19 | the initialization of Python. See \code{\link[=py_discover_config]{py_discover_config()}} for more details. 20 | } 21 | -------------------------------------------------------------------------------- /man/py_config_error_message.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/config.R 3 | \name{py_config_error_message} 4 | \alias{py_config_error_message} 5 | \title{Build Python configuration error message} 6 | \usage{ 7 | py_config_error_message(prefix) 8 | } 9 | \arguments{ 10 | \item{prefix}{Error message prefix} 11 | } 12 | \description{ 13 | Build Python configuration error message 14 | } 15 | \keyword{internal} 16 | -------------------------------------------------------------------------------- /man/py_del_attr.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/RcppExports.R 3 | \name{py_del_attr} 4 | \alias{py_del_attr} 5 | \title{Delete an attribute of a Python object} 6 | \usage{ 7 | py_del_attr(x, name) 8 | } 9 | \arguments{ 10 | \item{x}{A Python object.} 11 | 12 | \item{name}{The attribute name.} 13 | } 14 | \description{ 15 | Delete an attribute of a Python object 16 | } 17 | -------------------------------------------------------------------------------- /man/py_discover_config.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/config.R 3 | \name{py_discover_config} 4 | \alias{py_discover_config} 5 | \title{Discover the version of Python to use with reticulate.} 6 | \usage{ 7 | py_discover_config(required_module = NULL, use_environment = NULL) 8 | } 9 | \arguments{ 10 | \item{required_module}{A optional module name that will be used to select the 11 | Python environment used.} 12 | 13 | \item{use_environment}{An optional virtual/conda environment name to prefer 14 | in the search.} 15 | } 16 | \value{ 17 | Python configuration object. 18 | } 19 | \description{ 20 | This function enables callers to check which versions of Python will be 21 | discovered on a system as well as which one will be chosen for use with 22 | reticulate. 23 | } 24 | \details{ 25 | The order of discovery is documented in \code{vignette("versions")}, also available online 26 | \href{https://rstudio.github.io/reticulate/articles/versions.html#order-of-discovery}{here} 27 | } 28 | -------------------------------------------------------------------------------- /man/py_ellipsis.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{py_ellipsis} 4 | \alias{py_ellipsis} 5 | \title{The builtin constant Ellipsis} 6 | \usage{ 7 | py_ellipsis() 8 | } 9 | \description{ 10 | The builtin constant Ellipsis 11 | } 12 | -------------------------------------------------------------------------------- /man/py_eval.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{py_eval} 4 | \alias{py_eval} 5 | \title{Evaluate a Python Expression} 6 | \usage{ 7 | py_eval(code, convert = TRUE) 8 | } 9 | \arguments{ 10 | \item{code}{A single Python expression.} 11 | 12 | \item{convert}{Boolean; automatically convert Python objects to R?} 13 | } 14 | \value{ 15 | The result produced by evaluating \code{code}, converted to an \code{R} 16 | object when \code{convert} is set to \code{TRUE}. 17 | } 18 | \description{ 19 | Evaluate a single Python expression, in a way analogous to the Python 20 | \code{eval()} built-in function. 21 | } 22 | \section{Caveats}{ 23 | 24 | 25 | \code{py_eval()} only supports evaluation of 'simple' Python expressions. 26 | Other expressions (e.g. assignments) will fail; e.g. 27 | 28 | \if{html}{\out{
}}\preformatted{> py_eval("x = 1") 29 | Error in py_eval_impl(code, convert) : 30 | SyntaxError: invalid syntax (reticulate_eval, line 1) 31 | }\if{html}{\out{
}} 32 | 33 | and this mirrors what one would see in a regular Python interpreter: 34 | 35 | \if{html}{\out{
}}\preformatted{>>> eval("x = 1") 36 | Traceback (most recent call last): 37 | File "", line 1, in 38 | File "", line 1 39 | x = 1 40 | ^ 41 | SyntaxError: invalid syntax 42 | }\if{html}{\out{
}} 43 | 44 | The \code{\link[=py_run_string]{py_run_string()}} method can be used if the evaluation of arbitrary 45 | Python code is required. 46 | } 47 | 48 | -------------------------------------------------------------------------------- /man/py_exe.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/config.R 3 | \name{py_exe} 4 | \alias{py_exe} 5 | \title{Python executable} 6 | \usage{ 7 | py_exe() 8 | } 9 | \value{ 10 | The path to the Python executable \code{reticulate} has been configured 11 | to use. 12 | } 13 | \description{ 14 | Get the path to the Python executable that \code{reticulate} has been configured 15 | to use. If Python has already been initialized, then \code{reticulate} will 16 | choose the currently-active copy of Python. 17 | } 18 | \details{ 19 | This can occasionally be useful if you'd like to interact with Python (or its 20 | modules) via a subprocess; for example you might choose to install a package 21 | with \code{pip}: 22 | 23 | \if{html}{\out{
}}\preformatted{system2(py_exe(), c("-m", "pip", "install", "numpy")) 24 | }\if{html}{\out{
}} 25 | 26 | and so you can also have greater control over how these modules are invoked. 27 | } 28 | -------------------------------------------------------------------------------- /man/py_func.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/py_func.R 3 | \name{py_func} 4 | \alias{py_func} 5 | \title{Wrap an R function in a Python function with the same signature.} 6 | \usage{ 7 | py_func(f) 8 | } 9 | \arguments{ 10 | \item{f}{An R function} 11 | } 12 | \value{ 13 | A Python function that calls the R function \code{f} with the same signature. 14 | } 15 | \description{ 16 | This function could wrap an R function in a Python function with 17 | the same signature. Note that the signature of the R function 18 | must not contain esoteric Python-incompatible constructs. 19 | } 20 | -------------------------------------------------------------------------------- /man/py_function_custom_scaffold.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/wrapper.R 3 | \name{py_function_custom_scaffold} 4 | \alias{py_function_custom_scaffold} 5 | \title{Custom Scaffolding of R Wrappers for Python Functions} 6 | \usage{ 7 | py_function_custom_scaffold( 8 | python_function, 9 | r_function = NULL, 10 | additional_roxygen_fields = NULL, 11 | process_docs_fn = function(docs) docs, 12 | process_param_fn = function(param, docs) param, 13 | process_param_doc_fn = function(param_doc, docs) param_doc, 14 | postprocess_fn = function() { 15 | }, 16 | file_name = NULL 17 | ) 18 | } 19 | \arguments{ 20 | \item{python_function}{Fully qualified name of Python function or class 21 | constructor (e.g. \code{tf$layers$average_pooling1d})} 22 | 23 | \item{r_function}{Name of R function to generate (defaults to name of Python 24 | function if not specified)} 25 | 26 | \item{additional_roxygen_fields}{A list of additional roxygen fields to write 27 | to the roxygen docs, e.g. \code{list(export = "", rdname = "generated-wrappers")}.} 28 | 29 | \item{process_docs_fn}{A function to process docs obtained from 30 | \code{reticulate::py_function_docs(python_function)}.} 31 | 32 | \item{process_param_fn}{A function to process each parameter needed for 33 | \code{python_funcion} before executing \code{python_funcion.}} 34 | 35 | \item{process_param_doc_fn}{A function to process the roxygen docstring for 36 | each parameter.} 37 | 38 | \item{postprocess_fn}{A function to inject any custom code in the form of a 39 | string before writing the closing curly braces for the generated wrapper 40 | function.} 41 | 42 | \item{file_name}{The file name to write the generated wrapper function to. If 43 | \code{NULL}, the generated wrapper will only be printed out in the console.} 44 | } 45 | \description{ 46 | This function can be used to generate R wrapper for a specified 47 | Python function while allowing to inject custom code for critical parts of 48 | the wrapper generation, such as process the any part of the docs obtained 49 | from \code{\link[=py_function_docs]{py_function_docs()}} and append additional roxygen fields. The result 50 | from execution of \code{python_function} is assigned to a variable called 51 | \code{python_function_result} that can also be processed by \code{postprocess_fn} 52 | before writing the closing curly braces for the generated wrapper function. 53 | } 54 | \examples{ 55 | \dontrun{ 56 | 57 | library(tensorflow) 58 | library(stringr) 59 | 60 | # Example of a `process_param_fn` to cast parameters with default values 61 | # that contains "L" to integers 62 | process_int_param_fn <- function(param, docs) { 63 | # Extract the list of parameters that have integer values as default 64 | int_params <- gsub( 65 | " = [-]?[0-9]+L", 66 | "", 67 | str_extract_all(docs$signature, "[A-z]+ = [-]?[0-9]+L")[[1]]) 68 | # Explicitly cast parameter in the list obtained above to integer 69 | if (param \%in\% int_params) { 70 | param <- paste0("as.integer(", param, ")") 71 | } 72 | param 73 | } 74 | 75 | # Note that since the default value of parameter `k` is `1L`. It is wrapped 76 | # by `as.integer()` to ensure it's casted to integer before sending it to `tf$nn$top_k` 77 | # for execution. We then print out the python function result. 78 | py_function_custom_scaffold( 79 | "tf$nn$top_k", 80 | r_function = "top_k", 81 | process_param_fn = process_int_param_fn, 82 | postprocess_fn = function() { "print(python_function_result)" }) 83 | 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /man/py_function_wrapper.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/wrapper.R 3 | \name{py_function_wrapper} 4 | \alias{py_function_wrapper} 5 | \alias{py_function_docs} 6 | \title{Scaffold R wrappers for Python functions} 7 | \usage{ 8 | py_function_wrapper(python_function, r_prefix = NULL, r_function = NULL) 9 | 10 | py_function_docs(python_function) 11 | } 12 | \arguments{ 13 | \item{python_function}{Fully qualified name of Python function or class 14 | constructor (e.g. \code{tf$layers$average_pooling1d})} 15 | 16 | \item{r_prefix}{Prefix to add to generated R function name} 17 | 18 | \item{r_function}{Name of R function to generate (defaults to name of Python 19 | function if not specified)} 20 | } 21 | \description{ 22 | Scaffold R wrappers for Python functions 23 | } 24 | \note{ 25 | The generated wrapper will often require additional editing (e.g. to 26 | convert Python list literals in the docs to R lists, to massage R numeric 27 | values to Python integers via \code{as.integer} where required, etc.) so is 28 | really intended as an starting point for an R wrapper rather than a wrapper 29 | that can be used without modification. 30 | } 31 | \keyword{internal} 32 | -------------------------------------------------------------------------------- /man/py_get_attr.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/RcppExports.R 3 | \name{py_get_attr} 4 | \alias{py_get_attr} 5 | \title{Get an attribute of a Python object} 6 | \usage{ 7 | py_get_attr(x, name, silent = FALSE) 8 | } 9 | \arguments{ 10 | \item{x}{Python object} 11 | 12 | \item{name}{Attribute name} 13 | 14 | \item{silent}{\code{TRUE} to return \code{NULL} if the attribute 15 | doesn't exist (default is \code{FALSE} which will raise an error)} 16 | } 17 | \value{ 18 | Attribute of Python object 19 | } 20 | \description{ 21 | Get an attribute of a Python object 22 | } 23 | -------------------------------------------------------------------------------- /man/py_get_item.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/RcppExports.R, R/python-item.R 3 | \name{py_get_item} 4 | \alias{py_get_item} 5 | \alias{py_set_item} 6 | \alias{py_del_item} 7 | \alias{[.python.builtin.object} 8 | \alias{[<-.python.builtin.object} 9 | \title{Get/Set/Delete an item from a Python object} 10 | \usage{ 11 | py_get_item(x, key, silent = FALSE) 12 | 13 | py_set_item(x, key, value) 14 | 15 | py_del_item(x, key) 16 | 17 | \method{[}{python.builtin.object}(x, ...) 18 | 19 | \method{[}{python.builtin.object}(x, ...) <- value 20 | } 21 | \arguments{ 22 | \item{x}{A Python object.} 23 | 24 | \item{key, ...}{The key used for item lookup.} 25 | 26 | \item{silent}{Boolean; when \code{TRUE}, attempts to access missing items 27 | will return \code{NULL} rather than throw an error.} 28 | 29 | \item{value}{The item value to set. Assigning \code{value} of \code{NULL} calls 30 | \code{py_del_item()} and is equivalent to the python expression \verb{del x[key]}. To 31 | set an item value of \code{None}, you can call \code{py_set_item()} directly, or call 32 | \code{x[key] <- py_none()}} 33 | } 34 | \value{ 35 | For \code{py_get_item()} and \code{[}, the return value from the 36 | \code{x.__getitem__()} method. For \code{py_set_item()}, \code{py_del_item()} and \verb{[<-}, 37 | the mutate object \code{x} is returned. 38 | } 39 | \description{ 40 | Access an item from a Python object, similar to how \code{x[key]} might be 41 | used in Python code to access an item indexed by \code{key} on an object \code{x}. The 42 | object's \verb{__getitem__()} \verb{__setitem__()} or \verb{__delitem__()} method will be 43 | called. 44 | } 45 | \note{ 46 | The \code{py_get_item()} always returns an unconverted python object, while 47 | \code{[} will automatically attempt to convert the object if \code{x} was created 48 | with \code{convert = TRUE}. 49 | } 50 | \examples{ 51 | \dontrun{ 52 | 53 | ## get/set/del item from Python dict 54 | x <- r_to_py(list(abc = "xyz")) 55 | 56 | #' # R expression | Python expression 57 | # -------------------- | ----------------- 58 | x["abc"] # x["abc"] 59 | x["abc"] <- "123" # x["abc"] = "123" 60 | x["abc"] <- NULL # del x["abc"] 61 | x["abc"] <- py_none() # x["abc"] = None 62 | 63 | ## get item from Python list 64 | x <- r_to_py(list("a", "b", "c")) 65 | x[0] 66 | 67 | ## slice a NumPy array 68 | x <- np_array(array(1:64, c(4, 4, 4))) 69 | 70 | # R expression | Python expression 71 | # ------------ | ----------------- 72 | x[0] # x[0] 73 | x[, 0] # x[:, 0] 74 | x[, , 0] # x[:, :, 0] 75 | 76 | x[NA:2] # x[:2] 77 | x[`:2`] # x[:2] 78 | 79 | x[2:NA] # x[2:] 80 | x[`2:`] # x[2:] 81 | 82 | x[NA:NA:2] # x[::2] 83 | x[`::2`] # x[::2] 84 | 85 | x[1:3:2] # x[1:3:2] 86 | x[`1:3:2`] # x[1:3:2] 87 | 88 | x[.., 1] # x[..., 1] 89 | x[0, .., 1] # x[0, ..., 1] 90 | 91 | } 92 | } 93 | \concept{item-related APIs} 94 | -------------------------------------------------------------------------------- /man/py_has_attr.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/RcppExports.R 3 | \name{py_has_attr} 4 | \alias{py_has_attr} 5 | \title{Check if a Python object has an attribute} 6 | \usage{ 7 | py_has_attr(x, name) 8 | } 9 | \arguments{ 10 | \item{x}{A python object.} 11 | 12 | \item{name}{The attribute to be accessed.} 13 | } 14 | \value{ 15 | \code{TRUE} if the object has the attribute \code{name}, and 16 | \code{FALSE} otherwise. 17 | } 18 | \description{ 19 | Check whether a Python object \code{x} has an attribute 20 | \code{name}. 21 | } 22 | -------------------------------------------------------------------------------- /man/py_help.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/help.R 3 | \name{py_help} 4 | \alias{py_help} 5 | \title{Documentation for Python Objects} 6 | \usage{ 7 | py_help(object) 8 | } 9 | \arguments{ 10 | \item{object}{Object to print documentation for} 11 | } 12 | \description{ 13 | Documentation for Python Objects 14 | } 15 | -------------------------------------------------------------------------------- /man/py_help_handler.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/help.R 3 | \name{py_help_handler} 4 | \alias{py_help_handler} 5 | \title{Provide help for Python objects} 6 | \usage{ 7 | py_help_handler(type = c("completion", "parameter", "url"), topic, source, ...) 8 | } 9 | \description{ 10 | This is an internal method to be used by front-ends which need to provide 11 | help text / information for Python objects in different contexts. 12 | } 13 | \keyword{internal} 14 | -------------------------------------------------------------------------------- /man/py_id.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/RcppExports.R 3 | \name{py_id} 4 | \alias{py_id} 5 | \title{Unique identifer for Python object} 6 | \usage{ 7 | py_id(object) 8 | } 9 | \arguments{ 10 | \item{object}{Python object} 11 | } 12 | \value{ 13 | Unique identifer (as string) or \code{NULL} 14 | } 15 | \description{ 16 | Get a globally unique identifier for a Python object. 17 | } 18 | \note{ 19 | In the current implementation of CPython this is the 20 | memory address of the object. 21 | } 22 | -------------------------------------------------------------------------------- /man/py_install.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/install.R 3 | \name{py_install} 4 | \alias{py_install} 5 | \title{Install Python packages} 6 | \usage{ 7 | py_install( 8 | packages, 9 | envname = NULL, 10 | method = c("auto", "virtualenv", "conda"), 11 | conda = "auto", 12 | python_version = NULL, 13 | pip = FALSE, 14 | ..., 15 | pip_ignore_installed = ignore_installed, 16 | ignore_installed = FALSE 17 | ) 18 | } 19 | \arguments{ 20 | \item{packages}{A vector of Python packages to install.} 21 | 22 | \item{envname}{The name, or full path, of the environment in which Python 23 | packages are to be installed. When \code{NULL} (the default), the active 24 | environment as set by the \code{RETICULATE_PYTHON_ENV} variable will be used; 25 | if that is unset, then the \code{r-reticulate} environment will be used.} 26 | 27 | \item{method}{Installation method. By default, "auto" automatically finds a 28 | method that will work in the local environment. Change the default to force 29 | a specific installation method. Note that the "virtualenv" method is not 30 | available on Windows.} 31 | 32 | \item{conda}{The path to a \code{conda} executable. Use \code{"auto"} to allow 33 | \code{reticulate} to automatically find an appropriate \code{conda} binary. 34 | See \strong{Finding Conda} and \code{\link[=conda_binary]{conda_binary()}} for more details.} 35 | 36 | \item{python_version}{The requested Python version. Ignored when attempting 37 | to install with a Python virtual environment.} 38 | 39 | \item{pip}{Boolean; use \code{pip} for package installation? This is only relevant 40 | when Conda environments are used, as otherwise packages will be installed 41 | from the Conda repositories.} 42 | 43 | \item{...}{Additional arguments passed to \code{\link[=conda_install]{conda_install()}} 44 | or \code{\link[=virtualenv_install]{virtualenv_install()}}.} 45 | 46 | \item{pip_ignore_installed, ignore_installed}{Boolean; whether pip should 47 | ignore previously installed versions of the requested packages. Setting 48 | this to \code{TRUE} causes pip to install the latest versions of all 49 | dependencies into the requested environment. This ensure that no 50 | dependencies are satisfied by a package that exists either in the site 51 | library or was previously installed from a different--potentially 52 | incompatible--distribution channel. (\code{ignore_installed} is an alias for 53 | \code{pip_ignore_installed}, \code{pip_ignore_installed} takes precedence).} 54 | } 55 | \description{ 56 | Install Python packages into a virtual environment or Conda environment. 57 | } 58 | \details{ 59 | On Linux and OS X the "virtualenv" method will be used by default 60 | ("conda" will be used if virtualenv isn't available). On Windows, the 61 | "conda" method is always used. 62 | } 63 | \seealso{ 64 | \code{\link[=conda_install]{conda_install()}}, for installing packages into conda environments. 65 | \code{\link[=virtualenv_install]{virtualenv_install()}}, for installing packages into virtual environments. 66 | } 67 | -------------------------------------------------------------------------------- /man/py_is_null_xptr.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/RcppExports.R 3 | \name{py_is_null_xptr} 4 | \alias{py_is_null_xptr} 5 | \alias{py_validate_xptr} 6 | \title{Check if a Python object is a null externalptr} 7 | \usage{ 8 | py_is_null_xptr(x) 9 | 10 | py_validate_xptr(x) 11 | } 12 | \arguments{ 13 | \item{x}{Python object} 14 | } 15 | \value{ 16 | Logical indicating whether the object is a null externalptr 17 | } 18 | \description{ 19 | Check if a Python object is a null externalptr 20 | } 21 | \details{ 22 | When Python objects are serialized within a persisted R 23 | environment (e.g. .RData file) they are deserialized into null 24 | externalptr objects (since the Python session they were originally 25 | connected to no longer exists). This function allows you to safely 26 | check whether whether a Python object is a null externalptr. 27 | 28 | The \code{py_validate} function is a convenience function which calls 29 | \code{py_is_null_xptr} and throws an error in the case that the xptr 30 | is \code{NULL}. 31 | } 32 | -------------------------------------------------------------------------------- /man/py_iterator.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/generator.R 3 | \name{py_iterator} 4 | \alias{py_iterator} 5 | \title{Create a Python iterator from an R function} 6 | \usage{ 7 | py_iterator(fn, completed = NULL, prefetch = 0L) 8 | } 9 | \arguments{ 10 | \item{fn}{R function with no arguments.} 11 | 12 | \item{completed}{Special sentinel return value which indicates that 13 | iteration is complete (defaults to \code{NULL}).} 14 | 15 | \item{prefetch}{Number items to prefetch. Set this to a positive integer to 16 | avoid a deadlock in situations where the generator values are consumed by 17 | python background threads while the main thread is blocked.} 18 | } 19 | \value{ 20 | Python iterator which calls the R function for each iteration. 21 | } 22 | \description{ 23 | Create a Python iterator from an R function 24 | } 25 | \details{ 26 | Python generators are functions that implement the Python iterator 27 | protocol. In Python, values are returned using the \code{yield} keyword. In R, 28 | values are simply returned from the function. 29 | 30 | In Python, the \code{yield} keyword enables successive iterations to use the state 31 | of previous iterations. In R, this can be done by returning a function that 32 | mutates its enclosing environment via the \verb{<<-} operator. For example: 33 | 34 | \if{html}{\out{
}}\preformatted{sequence_generator <- function(start) \{ 35 | value <- start 36 | function() \{ 37 | value <<- value + 1 38 | value 39 | \} 40 | \} 41 | }\if{html}{\out{
}} 42 | 43 | Then create an iterator using \code{py_iterator()}: 44 | 45 | \if{html}{\out{
}}\preformatted{g <- py_iterator(sequence_generator(10)) 46 | }\if{html}{\out{
}} 47 | } 48 | \section{Ending Iteration}{ 49 | 50 | 51 | In Python, returning from a function without calling \code{yield} indicates the 52 | end of the iteration. In R however, \code{return} is used to yield values, so 53 | the end of iteration is indicated by a special return value (\code{NULL} by 54 | default, however this can be changed using the \code{completed} parameter). For 55 | example: 56 | 57 | \if{html}{\out{
}}\preformatted{sequence_generator <-function(start) \{ 58 | value <- start 59 | function() \{ 60 | value <<- value + 1 61 | if (value < 100) 62 | value 63 | else 64 | NULL 65 | \} 66 | \} 67 | }\if{html}{\out{
}} 68 | } 69 | 70 | \section{Threading}{ 71 | 72 | 73 | Some Python APIs use generators to parallellize operations by calling the 74 | generator on a background thread and then consuming its results on 75 | the foreground thread. The \code{py_iterator()} function creates threadsafe 76 | iterators by ensuring that the R function is always called on the main 77 | thread (to be compatible with R's single-threaded runtime) even if the 78 | generator is run on a background thread. 79 | } 80 | 81 | -------------------------------------------------------------------------------- /man/py_last_error.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{py_clear_last_error} 4 | \alias{py_clear_last_error} 5 | \alias{py_last_error} 6 | \title{Get or (re)set the last Python error encountered.} 7 | \usage{ 8 | py_clear_last_error() 9 | 10 | py_last_error(exception) 11 | } 12 | \arguments{ 13 | \item{exception}{A python exception object. If provided, the provided 14 | exception is set as the last exception.} 15 | } 16 | \value{ 17 | For \code{py_last_error()}, \code{NULL} if no error has yet been encountered. 18 | Otherwise, a named list with entries: 19 | \itemize{ 20 | \item \code{"type"}: R string, name of the exception class. 21 | \item \code{"value"}: R string, formatted exception message. 22 | \item \code{"traceback"}: R character vector, the formatted python traceback, 23 | \item \code{"message"}: The full formatted raised exception, as it would be printed in 24 | Python. Includes the traceback, type, and value. 25 | \item \code{"r_trace"}: A \code{data.frame} with class \code{rlang_trace} and columns: 26 | \itemize{ 27 | \item \code{call}: The R callstack, \code{full_call}, summarized for pretty printing. 28 | \item \code{full_call}: The R callstack. (Output of \code{sys.calls()} at the error callsite). 29 | \item \code{parent}: The parent of each frame in callstack. (Output of \code{sys.parents()} at the error callsite). 30 | \item Additional columns for internals use: \code{namespace}, \code{visible}, \code{scope}. 31 | } 32 | } 33 | 34 | And attribute \code{"exception"}, a \code{'python.builtin.Exception'} object. 35 | 36 | The named list has \code{class} \code{"py_error"}, and has a default \code{print} method 37 | that is the equivalent of \code{cat(py_last_error()$message)}. 38 | } 39 | \description{ 40 | Get or (re)set the last Python error encountered. 41 | } 42 | \examples{ 43 | \dontrun{ 44 | 45 | # see last python exception with R traceback 46 | reticulate::py_last_error() 47 | 48 | # see the full R callstack from the last Python exception 49 | reticulate::py_last_error()$r_trace$full_call 50 | 51 | # run python code that might error, 52 | # without modifying the user-visible python exception 53 | 54 | safe_len <- function(x) { 55 | last_err <- py_last_error() 56 | tryCatch({ 57 | # this might raise a python exception if x has no `__len__` method. 58 | import_builtins()$len(x) 59 | }, error = function(e) { 60 | # py_last_error() was overwritten, is now "no len method for 'object'" 61 | py_last_error(last_err) # restore previous exception 62 | -1L 63 | }) 64 | } 65 | 66 | safe_len(py_eval("object")) 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /man/py_len.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{py_len} 4 | \alias{py_len} 5 | \title{Length of Python object} 6 | \usage{ 7 | py_len(x, default = NULL) 8 | } 9 | \arguments{ 10 | \item{x}{A Python object.} 11 | 12 | \item{default}{The default length value to return, in the case that 13 | the associated Python object has no \verb{__len__} method. When \code{NULL} 14 | (the default), an error is emitted instead.} 15 | } 16 | \value{ 17 | The length of the object, as a numeric value. 18 | } 19 | \description{ 20 | Get the length of a Python object. This is equivalent to calling 21 | the Python builtin \code{len()} function on the object. 22 | } 23 | \details{ 24 | Not all Python objects have a defined length. For objects without a defined 25 | length, calling \code{py_len()} will throw an error. If you'd like to instead 26 | infer a default length in such cases, you can set the \code{default} argument 27 | to e.g. \code{1L}, to treat Python objects without a \verb{__len__} method as having 28 | length one. 29 | } 30 | -------------------------------------------------------------------------------- /man/py_list_attributes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{py_list_attributes} 4 | \alias{py_list_attributes} 5 | \title{List all attributes of a Python object} 6 | \usage{ 7 | py_list_attributes(x) 8 | } 9 | \arguments{ 10 | \item{x}{Python object} 11 | } 12 | \value{ 13 | Character vector of attributes 14 | } 15 | \description{ 16 | List all attributes of a Python object 17 | } 18 | -------------------------------------------------------------------------------- /man/py_list_packages.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/install.R 3 | \name{py_list_packages} 4 | \alias{py_list_packages} 5 | \title{List installed Python packages} 6 | \usage{ 7 | py_list_packages( 8 | envname = NULL, 9 | type = c("auto", "virtualenv", "conda"), 10 | python = NULL 11 | ) 12 | } 13 | \arguments{ 14 | \item{envname}{The name of, or path to, a Python virtual environment. 15 | Ignored when \code{python} is non-\code{NULL}.} 16 | 17 | \item{type}{The virtual environment type. Useful if you have both 18 | virtual environments and Conda environments of the same name on 19 | your system, and you need to disambiguate them.} 20 | 21 | \item{python}{The path to a Python executable.} 22 | } 23 | \value{ 24 | An \R data.frame, with columns: 25 | 26 | \describe{ 27 | \item{\code{package}}{The package name.} 28 | \item{\code{version}}{The package version.} 29 | \item{\code{requirement}}{The package requirement.} 30 | \item{\code{channel}}{(Conda only) The channel associated with this package.} 31 | } 32 | } 33 | \description{ 34 | List the Python packages that are installed in the requested Python 35 | environment. 36 | } 37 | \details{ 38 | When \code{envname} is \code{NULL}, \code{reticulate} will use the "default" version 39 | of Python, as reported by \code{\link[=py_exe]{py_exe()}}. This implies that you 40 | can call \code{py_list_packages()} without arguments in order to list 41 | the installed Python packages in the version of Python currently 42 | used by \code{reticulate}. 43 | } 44 | -------------------------------------------------------------------------------- /man/py_main_thread_func.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/thread.R 3 | \name{py_main_thread_func} 4 | \alias{py_main_thread_func} 5 | \title{\link{Deprecated} Create a Python function that will always be called on the main thread} 6 | \usage{ 7 | py_main_thread_func(f) 8 | } 9 | \arguments{ 10 | \item{f}{An R function with arbitrary arguments} 11 | } 12 | \value{ 13 | A Python function that delegates to the passed R function, which 14 | is guaranteed to always be called on the main thread. 15 | } 16 | \description{ 17 | Beginning with reticulate v1.39.0, every R function is a "main thread func". Usage of \code{py_main_thread_func()} 18 | is no longer necessary. 19 | } 20 | \details{ 21 | This function is helpful when you need to provide a callback to a Python 22 | library which may invoke the callback on a background thread. As R functions 23 | must run on the main thread, wrapping the R function with \code{py_main_thread_func()} 24 | will ensure that R code is only executed on the main thread. 25 | } 26 | \keyword{internal} 27 | -------------------------------------------------------------------------------- /man/py_module_available.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/config.R 3 | \name{py_module_available} 4 | \alias{py_module_available} 5 | \title{Check if a Python module is available on this system.} 6 | \usage{ 7 | py_module_available(module) 8 | } 9 | \arguments{ 10 | \item{module}{The name of the module.} 11 | } 12 | \value{ 13 | \code{TRUE} if the module is available and can be loaded; 14 | \code{FALSE} otherwise. 15 | } 16 | \description{ 17 | Note that this function will also attempt to initialize Python 18 | before checking if the requested module is available. 19 | } 20 | -------------------------------------------------------------------------------- /man/py_none.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{py_none} 4 | \alias{py_none} 5 | \title{The Python None object} 6 | \usage{ 7 | py_none() 8 | } 9 | \description{ 10 | Get a reference to the Python \code{None} object. 11 | } 12 | -------------------------------------------------------------------------------- /man/py_register_load_hook.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{py_register_load_hook} 4 | \alias{py_register_load_hook} 5 | \title{Register a Python module load hook} 6 | \usage{ 7 | py_register_load_hook(module, hook) 8 | } 9 | \arguments{ 10 | \item{module}{String, the name of the Python module.} 11 | 12 | \item{hook}{Function, called with no arguments. If \code{module} is already 13 | loaded, \code{hook()} is called immediately.} 14 | } 15 | \value{ 16 | \code{NULL} invisibly. Called for its side effect. 17 | } 18 | \description{ 19 | Register an R function to be called when a Python module is first loaded in 20 | the current R session. This can be used for tasks such as: 21 | } 22 | \details{ 23 | \itemize{ 24 | \item Delayed registration of S3 methods to accommodate different versions of a Python module. 25 | \item Configuring module-specific logging streams. 26 | } 27 | } 28 | \keyword{internal} 29 | -------------------------------------------------------------------------------- /man/py_run.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{py_run} 4 | \alias{py_run} 5 | \alias{py_run_string} 6 | \alias{py_run_file} 7 | \title{Run Python code} 8 | \usage{ 9 | py_run_string(code, local = FALSE, convert = TRUE) 10 | 11 | py_run_file(file, local = FALSE, convert = TRUE, prepend_path = TRUE) 12 | } 13 | \arguments{ 14 | \item{code}{The Python code to be executed.} 15 | 16 | \item{local}{Boolean; should Python objects be created as part of 17 | a local / private dictionary? If \code{FALSE}, objects will be created within 18 | the scope of the Python main module.} 19 | 20 | \item{convert}{Boolean; should Python objects be automatically converted 21 | to their \R equivalent? If set to \code{FALSE}, you can still manually convert 22 | Python objects to \R via the \code{\link[=py_to_r]{py_to_r()}} function.} 23 | 24 | \item{file}{The Python script to be executed.} 25 | 26 | \item{prepend_path}{Boolean; should the script directory be added to the 27 | Python module search path? The default, \code{TRUE}, matches the behavior of 28 | \verb{python } at the command line.} 29 | } 30 | \value{ 31 | A Python dictionary of objects. When \code{local} is \code{FALSE}, this 32 | dictionary captures the state of the Python main module after running 33 | the provided code. Otherwise, only the variables defined and used are 34 | captured. 35 | } 36 | \description{ 37 | Execute code within the scope of the \code{__main__} Python module. 38 | } 39 | -------------------------------------------------------------------------------- /man/py_save_object.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/pickle.R 3 | \name{py_save_object} 4 | \alias{py_save_object} 5 | \alias{py_load_object} 6 | \title{Save and Load Python Objects} 7 | \usage{ 8 | py_save_object(object, filename, pickle = "pickle", ...) 9 | 10 | py_load_object(filename, pickle = "pickle", ..., convert = TRUE) 11 | } 12 | \arguments{ 13 | \item{object}{A Python object.} 14 | 15 | \item{filename}{The output file name. Note that the file extension \code{.pickle} 16 | is considered the "standard" extension for serialized Python objects 17 | as created by the \code{pickle} module.} 18 | 19 | \item{pickle}{The "pickle" implementation to use. Defaults to \verb{"pickle}", 20 | but other compatible Python "pickle" implementations (e.g. \code{"cPickle"}) 21 | could be used as well.} 22 | 23 | \item{...}{Optional arguments, to be passed to the \code{pickle} module's 24 | \code{dump()} and \code{load()} functions.} 25 | 26 | \item{convert}{Bool. Whether the loaded pickle object should be converted to 27 | an R object.} 28 | } 29 | \description{ 30 | Save and load Python objects. 31 | } 32 | \details{ 33 | Python objects are serialized using the \code{pickle} module -- see 34 | \url{https://docs.python.org/3/library/pickle.html} for more details. 35 | } 36 | -------------------------------------------------------------------------------- /man/py_set_attr.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/RcppExports.R 3 | \name{py_set_attr} 4 | \alias{py_set_attr} 5 | \title{Set an attribute of a Python object} 6 | \usage{ 7 | py_set_attr(x, name, value) 8 | } 9 | \arguments{ 10 | \item{x}{Python object} 11 | 12 | \item{name}{Attribute name} 13 | 14 | \item{value}{Attribute value} 15 | } 16 | \description{ 17 | Set an attribute of a Python object 18 | } 19 | -------------------------------------------------------------------------------- /man/py_set_seed.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/seed.R 3 | \name{py_set_seed} 4 | \alias{py_set_seed} 5 | \title{Set Python and NumPy random seeds} 6 | \usage{ 7 | py_set_seed(seed, disable_hash_randomization = TRUE) 8 | } 9 | \arguments{ 10 | \item{seed}{A single value, interpreted as an integer} 11 | 12 | \item{disable_hash_randomization}{Disable hash randomization, which is 13 | another common source of variable results. See 14 | \url{https://docs.python.org/3/using/cmdline.html#envvar-PYTHONHASHSEED}} 15 | } 16 | \description{ 17 | Set various random seeds required to ensure reproducible results. The 18 | provided \code{seed} value will establish a new random seed for Python and NumPy, 19 | and will also (by default) disable hash randomization. 20 | } 21 | \details{ 22 | This function does not set the R random seed, for that you 23 | should call \code{\link[=set.seed]{set.seed()}}. 24 | } 25 | -------------------------------------------------------------------------------- /man/py_str.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/RcppExports.R, R/python.R 3 | \name{py_repr} 4 | \alias{py_repr} 5 | \alias{py_str} 6 | \title{String representation of a python object.} 7 | \usage{ 8 | py_repr(object) 9 | 10 | py_str(object, ...) 11 | } 12 | \arguments{ 13 | \item{object}{Python object} 14 | 15 | \item{...}{Unused} 16 | } 17 | \value{ 18 | Character vector 19 | } 20 | \description{ 21 | This is equivalent to calling \code{str(object)} or \code{repr(object)} in Python. 22 | } 23 | \details{ 24 | In Python, calling \code{print()} invokes the builtin \code{str()}, while auto-printing 25 | an object at the REPL invokes the builtin \code{repr()}. 26 | 27 | In \R, the default print method for python objects invokes \code{py_repr()}, and 28 | the default \code{format()} and \code{as.character()} methods invoke \code{py_str()}. 29 | 30 | For historical reasons, \code{py_str()} is also an \R S3 method that allows R 31 | authors to customize the the string representation of a Python object from R. 32 | New code is recommended to provide a \code{format()} and/or \code{print()} S3 R method 33 | for python objects instead. 34 | 35 | The default implementation will call \code{PyObject_Str} on the object. 36 | } 37 | \seealso{ 38 | \code{\link[=as.character.python.builtin.str]{as.character.python.builtin.str()}} 39 | \code{\link[=as.character.python.builtin.bytes]{as.character.python.builtin.bytes()}} for handling 40 | \verb{Error : Embedded NUL in string.} if the Python string contains an embedded \code{NUL}. 41 | } 42 | -------------------------------------------------------------------------------- /man/py_suppress_warnings.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{py_suppress_warnings} 4 | \alias{py_suppress_warnings} 5 | \title{Suppress Python warnings for an expression} 6 | \usage{ 7 | py_suppress_warnings(expr) 8 | } 9 | \arguments{ 10 | \item{expr}{Expression to suppress warnings for} 11 | } 12 | \value{ 13 | Result of evaluating expression 14 | } 15 | \description{ 16 | Suppress Python warnings for an expression 17 | } 18 | -------------------------------------------------------------------------------- /man/py_to_r_wrapper.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/conversion.R 3 | \name{py_to_r_wrapper} 4 | \alias{py_to_r_wrapper} 5 | \title{R wrapper for Python objects} 6 | \usage{ 7 | py_to_r_wrapper(x) 8 | } 9 | \arguments{ 10 | \item{x}{Python object} 11 | } 12 | \description{ 13 | S3 method to create a custom R wrapper for a Python object. 14 | The default wrapper is either an R environment or an R function 15 | (for callable python objects). 16 | } 17 | \keyword{internal} 18 | -------------------------------------------------------------------------------- /man/py_unicode.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{py_unicode} 4 | \alias{py_unicode} 5 | \title{Convert to Python Unicode Object} 6 | \usage{ 7 | py_unicode(str) 8 | } 9 | \arguments{ 10 | \item{str}{Single element character vector to convert} 11 | } 12 | \description{ 13 | Convert to Python Unicode Object 14 | } 15 | \details{ 16 | By default R character vectors are converted to Python strings. 17 | In Python 3 these values are unicode objects however in Python 2 18 | they are 8-bit string objects. This function enables you to 19 | obtain a Python unicode object from an R character vector 20 | when running under Python 2 (under Python 3 a standard Python 21 | string object is returned). 22 | } 23 | -------------------------------------------------------------------------------- /man/py_version.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/config.R 3 | \name{py_version} 4 | \alias{py_version} 5 | \title{Python version} 6 | \usage{ 7 | py_version(patch = FALSE) 8 | } 9 | \arguments{ 10 | \item{patch}{boolean, whether to include the patch level in the returned version.} 11 | } 12 | \value{ 13 | The version of Python currently used, or \code{NULL} if Python has 14 | not yet been initialized by \code{reticulate}. 15 | } 16 | \description{ 17 | Get the version of Python currently being used by \code{reticulate}. 18 | } 19 | -------------------------------------------------------------------------------- /man/py_versions_windows.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/config.R 3 | \name{py_versions_windows} 4 | \alias{py_versions_windows} 5 | \title{Discover versions of Python installed on a Windows system} 6 | \usage{ 7 | py_versions_windows() 8 | } 9 | \value{ 10 | Data frame with \code{type}, \code{hive}, \code{install_path}, \code{executable_path}, 11 | and \code{version}. 12 | } 13 | \description{ 14 | Discover versions of Python installed on a Windows system 15 | } 16 | \keyword{internal} 17 | -------------------------------------------------------------------------------- /man/r-py-conversion.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/conversion.R 3 | \name{r-py-conversion} 4 | \alias{r-py-conversion} 5 | \alias{r_to_py} 6 | \alias{py_to_r} 7 | \title{Convert between Python and R objects} 8 | \usage{ 9 | r_to_py(x, convert = FALSE) 10 | 11 | py_to_r(x) 12 | } 13 | \arguments{ 14 | \item{x}{A Python object.} 15 | 16 | \item{convert}{Boolean; should Python objects be automatically converted 17 | to their \R equivalent? If set to \code{FALSE}, you can still manually convert 18 | Python objects to \R via the \code{\link[=py_to_r]{py_to_r()}} function.} 19 | } 20 | \value{ 21 | An \R object, as converted from the Python object. 22 | } 23 | \description{ 24 | Convert between Python and R objects 25 | } 26 | -------------------------------------------------------------------------------- /man/register_class_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{register_class_filter} 4 | \alias{register_class_filter} 5 | \title{Register a filter for class names} 6 | \usage{ 7 | register_class_filter(filter) 8 | } 9 | \arguments{ 10 | \item{filter}{Function which takes a class name and maps it to an alternate 11 | name} 12 | } 13 | \description{ 14 | Register a filter for class names 15 | } 16 | \keyword{internal} 17 | -------------------------------------------------------------------------------- /man/register_help_topics.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/help.R 3 | \name{register_help_topics} 4 | \alias{register_help_topics} 5 | \title{Register help topics} 6 | \usage{ 7 | register_help_topics(type = c("module", "class"), topics) 8 | } 9 | \arguments{ 10 | \item{type}{Type (module or class)} 11 | 12 | \item{topics}{Named list of topics} 13 | } 14 | \description{ 15 | Register a set of help topics for dispatching from F1 help 16 | } 17 | \keyword{internal} 18 | -------------------------------------------------------------------------------- /man/register_module_help_handler.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/help.R 3 | \name{register_module_help_handler} 4 | \alias{register_module_help_handler} 5 | \title{Register a help handler for a root Python module} 6 | \usage{ 7 | register_module_help_handler(module, handler) 8 | } 9 | \arguments{ 10 | \item{module}{Name of a root Python module} 11 | 12 | \item{handler}{Handler function: \verb{function(name, subtopic = NULL)}. The name 13 | will be the fully qualified name of a Python object (module, function, or 14 | class). The \code{subtopic} is optional and is currently used only for methods 15 | within classes.} 16 | } 17 | \description{ 18 | Register a help handler for a root Python module 19 | } 20 | \details{ 21 | The help handler is passed a fully qualified module, class, or 22 | function name (and optional method for classes). It should return a URL for 23 | a help page (or \code{""} if no help page is available). 24 | } 25 | \keyword{internal} 26 | -------------------------------------------------------------------------------- /man/register_suppress_warnings_handler.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{register_suppress_warnings_handler} 4 | \alias{register_suppress_warnings_handler} 5 | \title{Register a handler for calls to py_suppress_warnings} 6 | \usage{ 7 | register_suppress_warnings_handler(handler) 8 | } 9 | \arguments{ 10 | \item{handler}{Handler} 11 | } 12 | \description{ 13 | Register a handler for calls to py_suppress_warnings 14 | } 15 | \details{ 16 | Enables packages to register a pair of functions 17 | to be called to suppress and then re-enable warnings 18 | } 19 | \keyword{internal} 20 | -------------------------------------------------------------------------------- /man/reticulate.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/package.R 3 | \docType{package} 4 | \name{reticulate} 5 | \alias{reticulate} 6 | \alias{reticulate-package} 7 | \title{R Interface to Python} 8 | \description{ 9 | R interface to Python modules, classes, and functions. When calling into 10 | Python R data types are automatically converted to their equivalent Python 11 | types. When values are returned from Python to R they are converted back to R 12 | types. The reticulate package is compatible with all versions of Python >= 3.6. 13 | Integration with NumPy requires NumPy version 1.6 or higher. 14 | } 15 | \seealso{ 16 | Useful links: 17 | \itemize{ 18 | \item \url{https://rstudio.github.io/reticulate/} 19 | \item \url{https://github.com/rstudio/reticulate} 20 | \item Report bugs at \url{https://github.com/rstudio/reticulate/issues} 21 | } 22 | 23 | } 24 | \author{ 25 | \strong{Maintainer}: Tomasz Kalinowski \email{tomasz@posit.co} [contributor] 26 | 27 | Authors: 28 | \itemize{ 29 | \item Kevin Ushey \email{kevin@posit.co} 30 | \item JJ Allaire \email{jj@posit.co} 31 | \item Yuan Tang \email{terrytangyuan@gmail.com} (\href{https://orcid.org/0000-0001-5243-233X}{ORCID}) [copyright holder] 32 | } 33 | 34 | Other contributors: 35 | \itemize{ 36 | \item RStudio [copyright holder, funder] 37 | \item Dirk Eddelbuettel \email{edd@debian.org} [contributor, copyright holder] 38 | \item Bryan Lewis \email{blewis@illposed.net} [contributor, copyright holder] 39 | \item Sigrid Keydana \email{sigrid@posit.co} [contributor] 40 | \item Ryan Hafen \email{rhafen@gmail.com} [contributor, copyright holder] 41 | \item Marcus Geelnard (TinyThread library, http://tinythreadpp.bitsnbites.eu/) [contributor, copyright holder] 42 | } 43 | 44 | } 45 | \keyword{internal} 46 | -------------------------------------------------------------------------------- /man/source_python.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/source.R 3 | \name{source_python} 4 | \alias{source_python} 5 | \title{Read and evaluate a Python script} 6 | \usage{ 7 | source_python(file, envir = parent.frame(), convert = TRUE) 8 | } 9 | \arguments{ 10 | \item{file}{The Python script to be executed.} 11 | 12 | \item{envir}{The environment to assign Python objects into (for example, 13 | \code{parent.frame()} or \code{globalenv()}). Specify \code{NULL} to not assign Python 14 | objects.} 15 | 16 | \item{convert}{Boolean; should Python objects be automatically converted 17 | to their \R equivalent? If set to \code{FALSE}, you can still manually convert 18 | Python objects to \R via the \code{\link[=py_to_r]{py_to_r()}} function.} 19 | } 20 | \description{ 21 | Evaluate a Python script within the Python main module, then make all public 22 | (non-module) objects within the main Python module available within the 23 | specified R environment. 24 | } 25 | \details{ 26 | To prevent assignment of objects into R, pass \code{NULL} for the \code{envir} 27 | parameter. 28 | } 29 | -------------------------------------------------------------------------------- /man/tuple.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{tuple} 4 | \alias{tuple} 5 | \title{Create Python tuple} 6 | \usage{ 7 | tuple(..., convert = FALSE) 8 | } 9 | \arguments{ 10 | \item{...}{Values for tuple (or a single list to be converted to a tuple).} 11 | 12 | \item{convert}{\code{TRUE} to automatically convert Python objects to their R 13 | equivalent. If you pass \code{FALSE} you can do manual conversion using the 14 | \code{\link[=py_to_r]{py_to_r()}} function.} 15 | } 16 | \value{ 17 | A Python tuple 18 | } 19 | \description{ 20 | Create a Python tuple object 21 | } 22 | \note{ 23 | The returned tuple will not automatically convert its elements from 24 | Python to R. You can do manual conversion with the \code{\link[=py_to_r]{py_to_r()}} function or 25 | pass \code{convert = TRUE} to request automatic conversion. 26 | } 27 | -------------------------------------------------------------------------------- /man/with-as-operator.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{with-as-operator} 4 | \alias{with-as-operator} 5 | \alias{\%as\%} 6 | \title{Create local alias for objects in \code{with} statements.} 7 | \usage{ 8 | object \%as\% name 9 | } 10 | \arguments{ 11 | \item{object}{Object to alias} 12 | 13 | \item{name}{Alias name} 14 | } 15 | \description{ 16 | Create local alias for objects in \code{with} statements. 17 | } 18 | \keyword{internal} 19 | -------------------------------------------------------------------------------- /man/with.python.builtin.object.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/python.R 3 | \name{with.python.builtin.object} 4 | \alias{with.python.builtin.object} 5 | \title{Evaluate an expression within a context.} 6 | \usage{ 7 | \method{with}{python.builtin.object}(data, expr, as = NULL, ...) 8 | } 9 | \arguments{ 10 | \item{data}{Context to enter and exit} 11 | 12 | \item{expr}{Expression to evaluate within the context} 13 | 14 | \item{as}{Name of variable to assign context to for the duration of the 15 | expression's evaluation (optional).} 16 | 17 | \item{...}{Unused} 18 | } 19 | \description{ 20 | The \code{with} method for objects of type \code{python.builtin.object} 21 | implements the context manager protocol used by the Python \code{with} 22 | statement. The passed object must implement the 23 | \href{https://docs.python.org/3/reference/datamodel.html#context-managers}{context 24 | manager} (\code{__enter__} and \code{__exit__} methods. 25 | } 26 | -------------------------------------------------------------------------------- /reticulate.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | ProjectId: 67dda317-993d-4309-bfde-903441588c12 3 | 4 | RestoreWorkspace: No 5 | SaveWorkspace: No 6 | AlwaysSaveHistory: Default 7 | 8 | EnableCodeIndexing: Yes 9 | UseSpacesForTab: Yes 10 | NumSpacesForTab: 2 11 | Encoding: UTF-8 12 | 13 | RnwWeave: Sweave 14 | LaTeX: pdfLaTeX 15 | 16 | AutoAppendNewline: Yes 17 | StripTrailingWhitespace: Yes 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageCleanBeforeInstall: No 22 | PackageInstallArgs: --no-multiarch --with-keep.source 23 | PackageRoxygenize: rd,collate,namespace 24 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | Makevars 2 | makevars.win 3 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RETICULATE_COMMON_H 3 | #define RETICULATE_COMMON_H 4 | 5 | #include 6 | 7 | // Debug macros. 8 | 9 | #define DBG(...) 10 | // #define DBG(fmt, ...) Rprintf("[DEBUG] (%s:%d): " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) 11 | 12 | // For importing symbols from R. 13 | // Forget to include __declspec(dllimport) at your own peril. 14 | #ifndef LibExtern 15 | # ifdef _WIN32 16 | # define LibExtern __declspec(dllimport) extern 17 | # else 18 | # define LibExtern extern 19 | # endif 20 | #endif 21 | 22 | // Forward declarations for some R functions that we'd like to avoid 23 | // pulling in all R headers for. 24 | extern "C" { 25 | extern Rboolean R_ToplevelExec(void (*func)(void*), void*); 26 | extern void R_ProcessEvents(); 27 | extern void Rprintf(const char*, ...); 28 | } 29 | 30 | #endif /* RETICULATE_COMMON_H */ 31 | -------------------------------------------------------------------------------- /src/event_loop.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RETICULATE_EVENT_LOOP_H 3 | #define RETICULATE_EVENT_LOOP_H 4 | 5 | namespace reticulate { 6 | namespace event_loop { 7 | 8 | void initialize(); 9 | 10 | void deinitialize(bool wait = false); 11 | 12 | } // namespace event_loop 13 | } // namespace reticulate 14 | 15 | #endif // RETICULATE_EVENT_LOOP_H 16 | -------------------------------------------------------------------------------- /src/output.cpp: -------------------------------------------------------------------------------- 1 | 2 | #define RCPP_NO_MODULES 3 | #define RCPP_NO_SUGAR 4 | #include 5 | using namespace Rcpp; 6 | 7 | #include 8 | 9 | // [[Rcpp::export]] 10 | int write_stdout(std::string text) { 11 | Rprintf("%s", text.c_str()); 12 | return text.length(); 13 | } 14 | 15 | 16 | // [[Rcpp::export]] 17 | int write_stderr(std::string text) { 18 | REprintf("%s", text.c_str()); 19 | return text.length(); 20 | } 21 | -------------------------------------------------------------------------------- /src/pending_py_calls_notifier.h: -------------------------------------------------------------------------------- 1 | #ifndef PENDING_PY_CALLS_NOTIFIER_H 2 | #define PENDING_PY_CALLS_NOTIFIER_H 3 | 4 | #include 5 | 6 | namespace pending_py_calls_notifier { 7 | 8 | // Initialize the notifier with a function that runs pending calls. 9 | void initialize(std::function run_pending_calls); 10 | 11 | // Notify the main thread to run pending calls. 12 | void notify(); 13 | 14 | // Undo initialize 15 | void deinitialize(); 16 | 17 | } // namespace pending_py_calls_notifier 18 | 19 | #endif // PENDING_PY_CALLS_NOTIFIER_H 20 | -------------------------------------------------------------------------------- /src/readline.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include // for strlen, strchr 5 | #define READLINE_BUFFER_SIZE (8192) 6 | extern "C" int R_ReadConsole(const char*, unsigned char*, int, int); 7 | 8 | // [[Rcpp::export]] 9 | SEXP readline(const char* prompt) 10 | { 11 | // read user input (ensure null termination) 12 | char buffer[READLINE_BUFFER_SIZE]; 13 | if (R_ReadConsole(prompt, (unsigned char*) buffer, READLINE_BUFFER_SIZE, 1) == 0) 14 | return R_NilValue; 15 | 16 | buffer[READLINE_BUFFER_SIZE - 1] = '\0'; // Ensure null termination 17 | 18 | // Find the location of the newline character, if any 19 | char* newline_pos = strchr(buffer, '\n'); 20 | if (newline_pos == nullptr) 21 | return R_NilValue; // If no newline found, assume user canceled 22 | 23 | // Determine length up to the newline (excluding the trailing newline) 24 | size_t input_length = newline_pos - buffer; 25 | 26 | SEXP resultSEXP = PROTECT(Rf_allocVector(STRSXP, 1)); 27 | SET_STRING_ELT(resultSEXP, 0, Rf_mkCharLen(buffer, input_length)); 28 | UNPROTECT(1); 29 | return resultSEXP; 30 | } 31 | -------------------------------------------------------------------------------- /src/signals.cpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _WIN32 3 | # include 4 | # include 5 | #else 6 | # include 7 | #endif 8 | 9 | #include "libpython.h" 10 | #include "signals.h" 11 | 12 | // import "common.h" last, so that is included last. 13 | // Otherwise, windows.h defines TRUE and FALSE to values that are not 14 | // Rboolean types, leading to compilation failures. 15 | #include "common.h" 16 | 17 | using namespace reticulate::libpython; 18 | 19 | extern "C" { 20 | 21 | // flag indicating whether R interrupts are pending 22 | #ifndef _WIN32 23 | extern int R_interrupts_pending; 24 | #else 25 | LibExtern int UserBreak; 26 | #endif 27 | 28 | // flag indicating if interrupts are suspended 29 | // note that R doesn't use this on Windows when checking 30 | // for interrupts in R_ProcessEvents 31 | LibExtern Rboolean R_interrupts_suspended; 32 | 33 | } 34 | 35 | namespace reticulate { 36 | namespace signals { 37 | 38 | 39 | bool getInterruptsPending() { 40 | #ifndef _WIN32 41 | return R_interrupts_pending != 0; 42 | #else 43 | return UserBreak != 0; 44 | #endif 45 | } 46 | 47 | void setInterruptsPending(bool value) { 48 | 49 | #ifndef _WIN32 50 | R_interrupts_pending = value ? 1 : 0; 51 | #else 52 | UserBreak = value ? 1 : 0; 53 | #endif 54 | 55 | } 56 | 57 | bool getInterruptsSuspended() { 58 | return R_interrupts_suspended != 0; 59 | } 60 | 61 | void setInterruptsSuspended(bool value) { 62 | R_interrupts_suspended = value ? TRUE : FALSE; 63 | } 64 | 65 | } // end namespace signals 66 | } // end namespace reticulate 67 | 68 | -------------------------------------------------------------------------------- /src/signals.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RETICULATE_SIGNALS_H 3 | #define RETICULATE_SIGNALS_H 4 | 5 | namespace reticulate { 6 | namespace signals { 7 | 8 | 9 | bool getInterruptsPending(); 10 | void setInterruptsPending(bool value); 11 | 12 | bool getInterruptsSuspended(); 13 | void setInterruptsSuspended(bool value); 14 | 15 | 16 | // NOTE: We also manage the interrupts pending flag here since calls to 17 | // R_ProcessEvents (on Windows) will check UserBreak without respecting 18 | // the R_interrupts_suspended flag. 19 | class InterruptsSuspendedScope 20 | { 21 | public: 22 | 23 | InterruptsSuspendedScope() 24 | : pending_(getInterruptsPending()), 25 | suspended_(getInterruptsSuspended()) 26 | { 27 | setInterruptsPending(false); 28 | setInterruptsSuspended(true); 29 | } 30 | 31 | ~InterruptsSuspendedScope() 32 | { 33 | setInterruptsPending(pending_ || getInterruptsPending()); 34 | setInterruptsSuspended(suspended_); 35 | } 36 | 37 | private: 38 | int pending_; 39 | int suspended_; 40 | }; 41 | 42 | } // end namespace signals 43 | } // end namespace reticulate 44 | 45 | #endif /* RETICULATE_SIGNALS_H */ 46 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | 2 | run <- function() { 3 | 4 | on_cran <- !isTRUE(as.logical(Sys.getenv("NOT_CRAN", "false"))) 5 | if (on_cran) { 6 | message("env var 'NOT_CRAN=true' not defined; Skipping tests on CRAN") 7 | return() 8 | } 9 | 10 | if (!requireNamespace("testthat", quietly = TRUE)) { 11 | message("'testthat' package not available; tests cannot be run") 12 | return() 13 | } 14 | 15 | options(error = traceback) 16 | if (requireNamespace("rlang", quietly = TRUE)) 17 | options(error = rlang::trace_back) 18 | 19 | library(testthat) 20 | library(reticulate) 21 | 22 | test_check("reticulate") 23 | 24 | } 25 | 26 | run() 27 | -------------------------------------------------------------------------------- /tests/testthat/.gitignore: -------------------------------------------------------------------------------- 1 | MNIST-data 2 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/python-knitr-engine/knitr-print.md: -------------------------------------------------------------------------------- 1 | bt <- reticulate::import_builtins() 2 | bt$print("Hello world") 3 | 4 | ## Hello world 5 | 6 | ## Print statements should always be captured 7 | 8 | In `jupyter_compat = FALSE`: we should only see the output of both 9 | expressions. 10 | 11 | print(1) 12 | 13 | ## 1 14 | 15 | print(2) 16 | 17 | ## 2 18 | 19 | In `jupyter_compat = TRUE`: we should only see the output of both 20 | expressions. 21 | 22 | print(1) 23 | 24 | ## 1 25 | 26 | print(2) 27 | 28 | ## 2 29 | 30 | ## Only outputs of last expressions are captured in jupyter compat 31 | 32 | In `jupyter_compat = FALSE`: we should only see the output of both 33 | expressions. 34 | 35 | 1 + 0 36 | 37 | ## 1 38 | 39 | 1 + 1 40 | 41 | ## 2 42 | 43 | `jupyter_compat = TRUE`: we should only see the output of the last 44 | expression. 45 | 46 | 1 + 0 47 | 1 + 1 48 | 49 | ## 2 50 | 51 | ## One can disable outputs by using `;` 52 | 53 | In `jupyter_compat = FALSE`: we should only see the print statement 54 | output 55 | 56 | print("hello"); 57 | 58 | ## hello 59 | 60 | 1 + 0; 61 | 1 + 1; 62 | 63 | `jupyter_compat = TRUE`: we should only see the print statement output 64 | 65 | print("hello"); 66 | 67 | ## hello 68 | 69 | 1 + 0; 70 | 1 + 1; 71 | 72 | ## `jupyter_compat` works with interleaved expressions 73 | 74 | print('first_stdout') 75 | 76 | ## first_stdout 77 | 78 | 'first_expression' 79 | print('second_stdout') 80 | 81 | ## second_stdout 82 | 83 | 'final_expression' 84 | 85 | ## 'final_expression' 86 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/python-knitr-engine/knitr-print2.md: -------------------------------------------------------------------------------- 1 | bt <- reticulate::import_builtins() 2 | bt$print("Hello world") 3 | 4 | ## Hello world 5 | 6 | ## Print statements should always be captured 7 | 8 | In `jupyter_compat = FALSE`: we should only see the output of both 9 | expressions. 10 | 11 | print(1) 12 | 13 | ## 1 14 | 15 | print(2) 16 | 17 | ## 2 18 | 19 | In `jupyter_compat = TRUE`: we should only see the output of both 20 | expressions. 21 | 22 | print(1) 23 | 24 | ## 1 25 | 26 | print(2) 27 | 28 | ## 2 29 | 30 | ## Only outputs of last expressions are captured in jupyter compat 31 | 32 | In `jupyter_compat = FALSE`: we should only see the output of both 33 | expressions. 34 | 35 | 1 + 0 36 | 37 | ## 1 38 | 39 | 1 + 1 40 | 41 | ## 2 42 | 43 | `jupyter_compat = TRUE`: we should only see the output of the last 44 | expression. 45 | 46 | 1 + 0 47 | 1 + 1 48 | 49 | ## 2 50 | 51 | ## One can disable outputs by using `;` 52 | 53 | In `jupyter_compat = FALSE`: we should only see the print statement 54 | output 55 | 56 | print("hello"); 57 | 58 | ## hello 59 | 60 | 1 + 0; 61 | 1 + 1; 62 | 63 | `jupyter_compat = TRUE`: we should only see the print statement output 64 | 65 | print("hello"); 66 | 67 | ## hello 68 | 69 | 1 + 0; 70 | 1 + 1; 71 | 72 | ## `jupyter_compat` works with interleaved expressions 73 | 74 | print('first_stdout') 75 | 76 | ## first_stdout 77 | 78 | 'first_expression' 79 | print('second_stdout') 80 | 81 | ## second_stdout 82 | 83 | 'final_expression' 84 | 85 | ## 'final_expression' 86 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/python-knitr-engine/knitr-warn.md: -------------------------------------------------------------------------------- 1 | import warnings 2 | warnings.warn("a warning") 3 | 1 + 1 4 | 5 | ## 2 6 | 7 | ## 2 8 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/python-knitr-engine/test-chunking.md: -------------------------------------------------------------------------------- 1 | library(reticulate) 2 | 3 | prose 4 | 5 | y = [2, 4, 6] 6 | 7 | # Comment about len 8 | print(len(y)) 9 | 10 | ## 3 11 | 12 | # This comment about type should be attached to the code below. 13 | print(type(y)) 14 | 15 | ## 16 | 17 | prose 18 | 19 | # Same Example with Collapsed Chunk 20 | y = [2, 4, 6] 21 | 22 | # Note the output of this command should available immediately below it (like with R). 23 | print(len(y)) 24 | ## 3 25 | 26 | # Comment about type should be attached to the code below (like R). 27 | # The output from the previous command should not be part of this 28 | print(type(y)) 29 | ## 30 | 31 | # Comparative Code for R 32 | x = c(2, 4, 6) 33 | 34 | # Output of length() is given just after the command (as expected) 35 | print(length(x)) 36 | 37 | ## [1] 3 38 | 39 | # Comment about class is attached to the code below (as expected) 40 | print(class(x)) 41 | 42 | ## [1] "numeric" 43 | 44 | # Comparative Code for R 45 | x = c(2, 4, 6) 46 | 47 | # Output of length() is given just after the command (as expected) 48 | print(length(x)) 49 | ## [1] 3 50 | 51 | # Comment about class is attached to the code below (as expected) 52 | print(class(x)) 53 | ## [1] "numeric" 54 | -------------------------------------------------------------------------------- /tests/testthat/helper-py-require.R: -------------------------------------------------------------------------------- 1 | test_py_require_reset <- function() { 2 | .globals$python_requirements <- NULL 3 | } 4 | 5 | r_session <- function(exprs, echo = TRUE, color = FALSE, 6 | attach_namespace = FALSE, 7 | force_managed_python = TRUE) { 8 | withr::local_envvar(c( 9 | "VIRTUAL_ENV" = NA, 10 | "RETICULATE_PYTHON" = if (force_managed_python) "managed" else NA, 11 | "VIRTUAL_ENV_PROMPT" = NA, 12 | "RETICULATE_MINICONDA_ENABLED" = NA, 13 | "RUST_LOG" = NA, 14 | "PYTHONPATH" = NA, 15 | "PYTHONIOENCODING" = "utf-8", 16 | if (isFALSE(color)) c("NO_COLOR" = "1") 17 | )) 18 | exprs <- substitute(exprs) 19 | if (!is.call(exprs)) 20 | stop("exprs must be a call") 21 | 22 | exprs <- if (identical(exprs[[1]], quote(`{`))) 23 | as.list(exprs)[-1] 24 | else 25 | list(exprs) 26 | 27 | exprs <- unlist(c( 28 | if (attach_namespace) 29 | 'attach(asNamespace("reticulate"), name = "namespace:reticulate", warn.conflicts = FALSE)', 30 | if (echo) 31 | "options(echo = TRUE)", 32 | lapply(exprs, deparse) 33 | )) 34 | 35 | writeLines(exprs, file <- tempfile(fileext = ".R")) 36 | on.exit(unlink(file), add = TRUE) 37 | 38 | result <- suppressWarnings(system2( 39 | R.home("bin/R"), 40 | c("--quiet", "--no-save", "--no-restore", "--no-echo", "-f", file), 41 | stdout = TRUE, stderr = TRUE 42 | )) 43 | class(result) <- "r_session_record" 44 | result 45 | } 46 | 47 | print.r_session_record <- function(record, echo = TRUE) { 48 | writeLines(record) 49 | status <- attr(record, "status", TRUE) 50 | cat(sep = "", 51 | "------- session end -------\n", 52 | "success: ", if (is.null(status)) "true" else "false", "\n", 53 | "exit_code: ", status %||% 0L, "\n") 54 | } 55 | registerS3method("print", "r_session_record", print.r_session_record, 56 | envir = environment(print)) 57 | 58 | 59 | py_require_tested_packages <- function() { 60 | py_require(c( 61 | "docutils", "pandas", "scipy", "matplotlib", "ipython", 62 | "tabulate", "plotly", "psutil", "kaleido", "wrapt" 63 | )) 64 | } 65 | 66 | py_require_tested_packages() 67 | 68 | 69 | uninstall_system_uv <- function() { 70 | withr::local_envvar(c("NO_COLOR" = "1")) 71 | cache_dir <- system("uv cache dir", intern = TRUE) %error% NULL 72 | python_dir <- system("uv python dir", intern = TRUE) %error% NULL 73 | tool_dir <- system("uv tool dir", intern = TRUE) %error% NULL 74 | dirs <- c(cache_dir, python_dir, tool_dir) 75 | uv <- Sys.which("uv") 76 | uvx <- Sys.which("uvx") 77 | todelete <- c(dirs, uv, uvx) 78 | todelete <- todelete[nzchar(todelete) & 79 | !is.na(todelete) & file.exists(todelete)] 80 | if (!length(todelete)) { 81 | message("nothing to delete") 82 | return() 83 | } 84 | todelete <- normalizePath(todelete) 85 | msg <- paste0("Delete?:\n", paste0("- ", todelete, collapse = "\n"), "\n") 86 | if (askYesNo(msg)) { 87 | unlink(todelete, recursive = TRUE, force = TRUE) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /tests/testthat/resources/.gitignore: -------------------------------------------------------------------------------- 1 | *.md 2 | *.html 3 | *.pdf 4 | *_files 5 | figure/ 6 | -------------------------------------------------------------------------------- /tests/testthat/resources/altair-example.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Altair Example" 3 | output: 4 | html_document: 5 | df_print: paged 6 | pdf_document: default 7 | --- 8 | 9 | ```{r} 10 | library(reticulate) 11 | 12 | # py_install(c("altair", "vega_datasets", "pandas")) 13 | ``` 14 | 15 | ```{python, altair.fig.width=800, altair.fig.height=300} 16 | import altair as alt 17 | from vega_datasets import data 18 | 19 | source = data.movies.url 20 | 21 | alt.Chart(source).mark_bar().encode( 22 | alt.X("IMDB_Rating:Q", bin=True), 23 | y='count()', 24 | ) 25 | ``` 26 | 27 | ## Compound charts 28 | 29 | ```{python} 30 | import altair as alt 31 | import pandas as pd 32 | 33 | source = pd.DataFrame({ 34 | 'a': ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'], 35 | 'b': [28, 55, 43, 91, 81, 53, 19, 87, 52] 36 | }) 37 | 38 | fig1 = alt.Chart(source).mark_bar().encode( 39 | x='a', 40 | y='b' 41 | ) 42 | fig2 = alt.Chart(source).mark_bar().encode( 43 | x='a', 44 | y='b', 45 | color='b:N' 46 | ) 47 | fig3 = alt.Chart(source).mark_bar().encode( 48 | x='a', 49 | y='b', 50 | color='a:N' 51 | ) 52 | ``` 53 | 54 | ## Compound Chart1 55 | ```{python} 56 | #| label: first 57 | (fig1 | fig2).properties(title="This is the First Chart") 58 | ``` 59 | 60 | ## Compound Chart2 61 | ```{python} 62 | #| label: second 63 | (fig2 | fig1).properties(title="This is the Second Chart") 64 | ``` 65 | 66 | ## Compound Chart3 67 | ```{python} 68 | #| label: third 69 | (fig2 | fig1 | fig3).properties(title="This is the Third Chart") 70 | ``` 71 | -------------------------------------------------------------------------------- /tests/testthat/resources/import-test.R: -------------------------------------------------------------------------------- 1 | 2 | library(reticulate) 3 | options(reticulate.logModuleLoad = TRUE) 4 | py_require("matplotlib") 5 | reticulate::py_run_string("from matplotlib import pyplot as plt") 6 | -------------------------------------------------------------------------------- /tests/testthat/resources/knitr-print.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Preserve output from python print" 3 | output: md_document 4 | --- 5 | 6 | ```{r} 7 | bt <- reticulate::import_builtins() 8 | bt$print("Hello world") 9 | ``` 10 | 11 | ## Print statements should always be captured 12 | 13 | In `jupyter_compat = FALSE`: we should only see the output of both expressions. 14 | 15 | ```{python} 16 | print(1) 17 | print(2) 18 | ``` 19 | 20 | In `jupyter_compat = TRUE`: we should only see the output of both expressions. 21 | 22 | ```{python, jupyter_compat = TRUE} 23 | print(1) 24 | print(2) 25 | ``` 26 | 27 | ## Only outputs of last expressions are captured in jupyter compat 28 | 29 | In `jupyter_compat = FALSE`: we should only see the output of both expressions. 30 | 31 | ```{python} 32 | 1 + 0 33 | 1 + 1 34 | ``` 35 | 36 | `jupyter_compat = TRUE`: we should only see the output of the last expression. 37 | 38 | ```{python, jupyter_compat = TRUE} 39 | 1 + 0 40 | 1 + 1 41 | ``` 42 | 43 | ## One can disable outputs by using `;` 44 | 45 | In `jupyter_compat = FALSE`: we should only see the print statement output 46 | 47 | ```{python} 48 | print("hello"); 49 | 1 + 0; 50 | 1 + 1; 51 | ``` 52 | 53 | `jupyter_compat = TRUE`: we should only see the print statement output 54 | 55 | ```{python, jupyter_compat = TRUE} 56 | print("hello"); 57 | 1 + 0; 58 | 1 + 1; 59 | ``` 60 | 61 | ## `jupyter_compat` works with interleaved expressions 62 | 63 | ```{python, jupyter_compat=TRUE} 64 | print('first_stdout') 65 | 'first_expression' 66 | print('second_stdout') 67 | 'final_expression' 68 | ``` 69 | 70 | 71 | -------------------------------------------------------------------------------- /tests/testthat/resources/knitr-warn.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Warnings" 3 | output: md_document 4 | --- 5 | 6 | ```{python, warning=FALSE} 7 | import warnings 8 | warnings.warn("a warning") 9 | 1 + 1 10 | ``` 11 | 12 | 13 | ```{python, echo=FALSE, warning=FALSE} 14 | import warnings 15 | warnings.warn("a warning") 16 | 1 + 1 17 | ``` 18 | 19 | -------------------------------------------------------------------------------- /tests/testthat/resources/matplotlib-example.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "matplotlib Example" 3 | output: 4 | html_document: 5 | df_print: paged 6 | pdf_document: default 7 | --- 8 | 9 | ```{r} 10 | library(reticulate) 11 | 12 | python <- "~/.virtualenvs/python-3.7.7-venv/bin/python" 13 | if (file.exists(python)) 14 | use_python(python, required = TRUE) 15 | ``` 16 | 17 | ```{python} 18 | import matplotlib.pyplot as plt 19 | import numpy as np 20 | 21 | # Data for plotting 22 | t = np.arange(0.0, 2.0, 0.01) 23 | s = 1 + np.sin(2 * np.pi * t) 24 | 25 | fig, ax = plt.subplots() 26 | ax.plot(t, s) 27 | 28 | output = ax.set(xlabel='time (s)', ylabel='voltage (mV)', 29 | title='About as simple as it gets, folks') 30 | ax.grid() 31 | 32 | plt.show() 33 | ``` 34 | -------------------------------------------------------------------------------- /tests/testthat/resources/pandas-example.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Pandas Example" 3 | output: 4 | html_document: 5 | df_print: paged 6 | pdf_document: default 7 | --- 8 | 9 | ```{r} 10 | library(reticulate) 11 | python <- "~/.virtualenvs/python-3.7.7-venv/bin/python" 12 | if (file.exists(python)) 13 | use_python(python, required = TRUE) 14 | ``` 15 | 16 | ```{python} 17 | import numpy as np 18 | import pandas as pd 19 | 20 | df = pd.DataFrame(np.random.rand(5, 2), columns=["x", "y"]) 21 | 22 | df.plot.scatter(x="x", y="y") 23 | df.plot.scatter(x="x", y="y") 24 | ``` 25 | -------------------------------------------------------------------------------- /tests/testthat/resources/plotly-example.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Plotly Example" 3 | output: 4 | html_document: 5 | df_print: paged 6 | --- 7 | 8 | ```{r} 9 | library(reticulate) 10 | 11 | python <- "~/.virtualenvs/python-3.7.7-venv/bin/python" 12 | if (file.exists(python)) 13 | use_python(python, required = TRUE) 14 | ``` 15 | 16 | ## plotly figures should be rendered on .show() 17 | 18 | ```{python} 19 | import plotly.express as px 20 | fig = px.scatter(x=[0, 1, 2, 3, 4], y=[0, 1, 4, 9, 16]) 21 | fig.show() 22 | ``` 23 | 24 | ## plotly figures should be implicitly rendered on print 25 | 26 | ```{python} 27 | import plotly.express as px 28 | fig = px.scatter(x=[0, 1, 2, 3, 4], y=[0, 1, 4, 9, 16]) 29 | fig 30 | ``` 31 | 32 | -------------------------------------------------------------------------------- /tests/testthat/resources/seaborn-example.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Seaborn Example" 3 | output: 4 | html_document: 5 | df_print: paged 6 | pdf_document: default 7 | --- 8 | 9 | ```{r} 10 | library(reticulate) 11 | python <- "~/.virtualenvs/python-3.7.7-venv/bin/python" 12 | if (file.exists(python)) 13 | use_python(python, required = TRUE) 14 | ``` 15 | 16 | ```{python} 17 | import numpy as np 18 | import pandas as pd 19 | import seaborn as sns 20 | 21 | df = pd.DataFrame(np.random.rand(5, 2), columns=["x", "y"]) 22 | sns.scatterplot(data=df, x="x", y="y") 23 | ``` 24 | -------------------------------------------------------------------------------- /tests/testthat/resources/test-chunking.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Python Multiple Print & Comments" 3 | output: md_document 4 | --- 5 | 6 | ```{r 'setup'} 7 | library(reticulate) 8 | ``` 9 | 10 | prose 11 | 12 | ```{python} 13 | y = [2, 4, 6] 14 | 15 | # Comment about len 16 | print(len(y)) 17 | 18 | # This comment about type should be attached to the code below. 19 | print(type(y)) 20 | 21 | ``` 22 | 23 | prose 24 | 25 | ```{python, collapse=TRUE} 26 | # Same Example with Collapsed Chunk 27 | y = [2, 4, 6] 28 | 29 | # Note the output of this command should available immediately below it (like with R). 30 | print(len(y)) 31 | 32 | # Comment about type should be attached to the code below (like R). 33 | # The output from the previous command should not be part of this 34 | print(type(y)) 35 | ``` 36 | 37 | ```{r} 38 | # Comparative Code for R 39 | x = c(2, 4, 6) 40 | 41 | # Output of length() is given just after the command (as expected) 42 | print(length(x)) 43 | 44 | # Comment about class is attached to the code below (as expected) 45 | print(class(x)) 46 | ``` 47 | 48 | 49 | ```{r, collapse=TRUE} 50 | # Comparative Code for R 51 | x = c(2, 4, 6) 52 | 53 | # Output of length() is given just after the command (as expected) 54 | print(length(x)) 55 | 56 | # Comment about class is attached to the code below (as expected) 57 | print(class(x)) 58 | ``` 59 | -------------------------------------------------------------------------------- /tests/testthat/resources/test-custom-root-dir.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "reticulate" 3 | --- 4 | 5 | Change the knit root directory to the temporary R session. 6 | See https://github.com/rstudio/reticulate/issues/1526 for context. 7 | 8 | ```{r setup} 9 | knitr::opts_knit$set("root.dir" = tempdir()) 10 | ``` 11 | 12 | ```{r confirm-wd} 13 | getwd() 14 | ``` 15 | 16 | ```{r configure-python} 17 | library("reticulate") 18 | matplotlib <- import("matplotlib") 19 | ``` 20 | 21 | ```{r} 22 | reticulate::py_config() 23 | ``` 24 | 25 | The R plot is saved relative to the location of the Rmd file: 26 | 27 | ```{r r-plot} 28 | plot(1:10) 29 | ``` 30 | 31 | The Python plot is also saved relative to the location of the Rmd file (not root.dir): 32 | 33 | ```{python py-plot} 34 | import matplotlib.pyplot as plt 35 | 36 | x = range(1, 10) 37 | plt.plot(x, x) 38 | ``` 39 | 40 | ```{r} 41 | knitr::opts_knit$get("root.dir") 42 | knitr::opts_knit$get("base.dir") 43 | knitr::opts_knit$get("output.dir") 44 | 45 | if (length(Sys.glob(paste0(knitr::opts_knit$get("root.dir"), "/figure*/*")))) 46 | stop("Figures saved in the wrong dir") 47 | 48 | if (!setequal( 49 | basename(Sys.glob(paste0(knitr::opts_knit$get("output.dir"), "/figure*/*"))), 50 | c("r-plot-1.png", "py-plot-1.png"))) 51 | stop("Figures not found in expected output.dir") 52 | 53 | if( file.exists(file.path(knitr::opts_knit$get("root.dir"), "figure", "r-plot-1.png"))) stop("figure saved in wrong place1") 54 | if( file.exists(file.path(knitr::opts_knit$get("root.dir"), "figure", "py-plot-1.png"))) stop("figure saved in wrong place2") 55 | if(!file.exists(file.path(knitr::opts_knit$get("output.dir"), "figure", "r-plot-1.png"))) stop("figure saved in wrong place3") 56 | if(!file.exists(file.path(knitr::opts_knit$get("output.dir"), "figure", "py-plot-1.png"))) stop("figure saved in wrong place4") 57 | ``` 58 | -------------------------------------------------------------------------------- /tests/testthat/resources/venv-activate.R: -------------------------------------------------------------------------------- 1 | 2 | args <- commandArgs(TRUE) 3 | venv <- args[[1]] 4 | 5 | Sys.unsetenv("RETICULATE_PYTHON") 6 | Sys.unsetenv("RETICULATE_PYTHON_ENV") 7 | 8 | reticulate::use_virtualenv(venv, required = TRUE) 9 | sys <- reticulate::import("sys") 10 | writeLines(sys$path) 11 | -------------------------------------------------------------------------------- /tests/testthat/script.py: -------------------------------------------------------------------------------- 1 | value = 42 2 | 3 | 4 | def add(x, y): 5 | return x + y 6 | 7 | 8 | def secret(): 9 | return value 10 | 11 | 12 | def _helper(): 13 | return 42 14 | 15 | 16 | def api(): 17 | return _helper() 18 | -------------------------------------------------------------------------------- /tests/testthat/setup.R: -------------------------------------------------------------------------------- 1 | 2 | ## CRAN reports we create a 3 | # if (!exists("~/.virtualenvs")) 4 | # withr::defer(unlink("~/.virtualenvs", recursive = TRUE), teardown_env()) 5 | 6 | 7 | .oop <- list( 8 | Matrix.warnDeprecatedCoerce = 2L, 9 | warnPartialMatchArgs = TRUE, 10 | warnPartialMatchAttr = TRUE, 11 | warnPartialMatchDollar = TRUE 12 | ) 13 | 14 | if(getRversion() < "3.6") { 15 | .oop$warnPartialMatchArgs <- NULL 16 | .oop$warnPartialMatchAttr <- NULL 17 | .oop$warnPartialMatchDollar <- NULL 18 | } 19 | 20 | .oop <- do.call(options, .oop) 21 | withr::defer(options(.oop), teardown_env()) 22 | -------------------------------------------------------------------------------- /tests/testthat/test-aaa.R: -------------------------------------------------------------------------------- 1 | 2 | # TODO: throw error in py_initialize() if attempting to bind to Python 2, then delete this test helper 3 | 4 | # prefer Python 3 if available 5 | # if (!is_windows() && 6 | # !py_available(initialize = FALSE) && 7 | # is.na(Sys.getenv("RETICULATE_PYTHON", unset = NA))) 8 | # { 9 | # python <- Sys.which("python3") 10 | # if (nzchar(python) && python != "/usr/bin/python3") 11 | # use_python(python, required = TRUE) 12 | # } 13 | -------------------------------------------------------------------------------- /tests/testthat/test-callable-dynamic-dots.R: -------------------------------------------------------------------------------- 1 | context("dynamic-dots") 2 | 3 | test_that("callables support dynamic-dots", { 4 | 5 | fn <- py_eval("lambda *args, **kwargs: (args if args else None, kwargs if kwargs else None)") 6 | 7 | # splicing (unpacking) arguments works correctly 8 | args <- list(1, 2, 3) 9 | kwargs <- list(a = 4, b = 5, c = 6) 10 | expect_identical(fn(!!!args), fn(1, 2, 3)) 11 | expect_identical(fn(!!!kwargs), fn(a = 4, b = 5, c = 6)) 12 | expect_identical(fn(!!!c(args, kwargs)), fn(1, 2, 3, a = 4, b = 5, c = 6)) 13 | expect_identical(fn(!!!c(args, kwargs), x = 7), fn(1, 2, 3, a = 4, b = 5, c = 6, x = 7)) 14 | expect_identical(fn(8, !!!c(args, kwargs), x = 7), fn(8, 1, 2, 3, a = 4, b = 5, c = 6, x = 7)) 15 | expect_identical(fn(8, !!!c(args, kwargs), x = 7), list(list(8, 1, 2, 3), list(a = 4, b = 5, c = 6, x = 7))) 16 | 17 | # injecting names works correctly 18 | nm <- "key" 19 | expect_identical(fn("{nm}" := 42), list(NULL, list(key = 42))) 20 | expect_identical(fn("abc_{nm}" := 42), list(NULL, list(abc_key = 42))) 21 | 22 | # trailing commas are ignored 23 | expect_identical(fn(1, 2, ), fn(1, 2)) 24 | }) 25 | -------------------------------------------------------------------------------- /tests/testthat/test-delay-load.R: -------------------------------------------------------------------------------- 1 | context("delay-load") 2 | 3 | test_that("imported module can be customized via delay_load", { 4 | 5 | # ensure RETICULATE_PYTHON is set for sub-process so that 6 | # the expected version of Python is loaded 7 | config <- py_config() 8 | withr::local_envvar(RETICULATE_PYTHON = config$python) 9 | 10 | # run in a separate process, since we want the attempted module 11 | # load to trigger initialization of Python and so have get_module 12 | # handled specially 13 | result <- callr::r(function() { 14 | 15 | sys <- reticulate::import( 16 | "invalid_module_name", 17 | delay_load = list(get_module = function() { "sys" }) 18 | ) 19 | 20 | stopifnot(isTRUE(reticulate:::py_is_module_proxy(sys))) 21 | 22 | print(sys) 23 | stopifnot(isTRUE(reticulate:::py_is_module_proxy(sys))) 24 | stopifnot(isFALSE(reticulate::py_available())) 25 | 26 | out <- as.character(sys$byteorder) 27 | 28 | stopifnot(isFALSE(reticulate:::py_is_module_proxy(sys))) 29 | stopifnot(isTRUE(reticulate::py_available())) 30 | 31 | out 32 | }) 33 | 34 | # validate expected result 35 | expect_true(result %in% c("little", "big")) 36 | 37 | }) 38 | 39 | 40 | test_that("[[ loads delayed modules", { 41 | 42 | # https://github.com/rstudio/reticulate/issues/1688 43 | config <- py_config() 44 | withr::local_envvar(RETICULATE_PYTHON = config$python) 45 | 46 | result <- callr::r(function() { 47 | time <- reticulate::import('time', delay_load = TRUE) 48 | stopifnot(isFALSE(reticulate::py_available())) 49 | 50 | result <- time[['time']]() 51 | stopifnot(isTRUE(reticulate::py_available())) 52 | result 53 | }) 54 | 55 | # validate expected result 56 | expect_true(typeof(result) %in% c("double", "integer")) 57 | expect_true((result - import("time")$time()) < 10) 58 | 59 | }) 60 | -------------------------------------------------------------------------------- /tests/testthat/test-examples.R: -------------------------------------------------------------------------------- 1 | context("examples") 2 | 3 | # some helpers 4 | run_example <- function(example) { 5 | env <- new.env() 6 | capture.output({ 7 | example_path <- system.file("examples", example, package = "reticulate") 8 | old_wd <- setwd(dirname(example_path)) 9 | on.exit(setwd(old_wd), add = TRUE) 10 | source(basename(example_path), local = env) 11 | }, type = "output") 12 | rm(list = ls(env), envir = env) 13 | gc() 14 | } 15 | 16 | examples <- c() 17 | 18 | for (example in examples) { 19 | test_that(paste(example, "example runs successfully"), { 20 | skip_if_no_python() 21 | expect_error(run_example(example), NA) 22 | }) 23 | } 24 | 25 | -------------------------------------------------------------------------------- /tests/testthat/test-finalize.R: -------------------------------------------------------------------------------- 1 | test_that("py_finalize() works", { 2 | # make sure that on R session termination, Python finalizers are run. 3 | 4 | file <- tempfile() 5 | 6 | callr::r(function(file) { 7 | Sys.setenv("RETICULATE_ENABLE_PYTHON_FINALIZER" = "yes") 8 | library(reticulate) 9 | 10 | py_run_string(sprintf(" 11 | import weakref 12 | 13 | class Foo: 14 | def __init__(self): 15 | weakref.finalize(self, self.on_finalize) 16 | 17 | def on_finalize(self): 18 | with open(r'%s', 'a') as f: 19 | f.write('Foo.finalize ran\\n') 20 | 21 | import atexit 22 | def on_exit(): 23 | with open(r'%s', 'a') as f: 24 | f.write('on_exit finalizer ran\\n') 25 | 26 | atexit.register(on_exit) 27 | obj = Foo() 28 | ", file, file)) 29 | 30 | invisible() 31 | }, list(file)) 32 | 33 | x <- readLines(file) 34 | 35 | # check that the finalizers ran 36 | expect_contains(x, "Foo.finalize ran") 37 | expect_contains(x, "on_exit finalizer ran") 38 | }) 39 | -------------------------------------------------------------------------------- /tests/testthat/test-help-handlers.R: -------------------------------------------------------------------------------- 1 | context("helper handlers") 2 | 3 | test_that("Documentations in Sphinx style can be extracted correctly for help handlers", { 4 | skip_if_no_docutils() 5 | 6 | docs <- " 7 | Initialize the model. 8 | 9 | Parameters 10 | ---------- 11 | a : int, optional 12 | Description for a. 13 | Defaults to 3. 14 | type : str, optional 15 | Type of algorithm (default: 'linear') 16 | 'linear' - linear model 17 | 'nonlinear' - nonlinear model 18 | 19 | Returns 20 | ------- 21 | values : array-like 22 | 23 | Section1 24 | ----------- 25 | Just a placeholder here. 26 | " 27 | 28 | doctree <- sphinx_doctree_from_doc(docs) 29 | expect_setequal(names(doctree$ids), c("parameters", "returns", "section1")) 30 | 31 | arg_descriptions <- arg_descriptions_from_doc_sphinx(docs) 32 | expect_setequal(names(arg_descriptions), c("a", "type")) 33 | expect_match(arg_descriptions[["a"]], "Description for a") 34 | expect_match(arg_descriptions[["type"]], "Type of algorithm") 35 | 36 | result <- help_completion_handler_sphinx(docs) 37 | expect_match(result$description, "Initialize the model") 38 | expect_match(result$returns, "values: array-like") 39 | }) 40 | 41 | test_that("Documentations in Google style can be extracted correctly for help handlers", { 42 | skip_if_no_docutils() 43 | 44 | docs <- " 45 | Initialize the model. 46 | 47 | Args: 48 | a: Description for a. 49 | Defaults to 3. 50 | type: Type of algorithm (default: 'linear') 51 | 'linear' - linear model 52 | 'nonlinear' - nonlinear model 53 | 54 | Returns: 55 | array-like values 56 | 57 | Section1: 58 | Just a placeholder here. 59 | " 60 | 61 | sections <- sections_from_doc(docs) 62 | expect_equal(names(sections), c("Args", "Returns", "Section1")) 63 | 64 | arg_descriptions <- arg_descriptions_from_doc_default(c("a", "type"), docs) 65 | expect_match(arg_descriptions[["a"]], "Description for a") 66 | expect_match(arg_descriptions[["type"]], "Type of algorithm") 67 | 68 | result <- help_completion_handler_default(docs) 69 | expect_match(result$description, "Initialize the model") 70 | expect_match(result$returns, "array-like values") 71 | }) 72 | 73 | -------------------------------------------------------------------------------- /tests/testthat/test-multiprocessing.R: -------------------------------------------------------------------------------- 1 | test_that("multiprocessing works", { 2 | 3 | skip_if_no_python() 4 | 5 | mp <- import("multiprocessing") 6 | queue <- mp$Queue() 7 | 8 | for (i in 1:3) { 9 | 10 | expect_no_error({ 11 | p <- mp$Process(target = queue$put, 12 | args = tuple(i)) 13 | p$start() 14 | p$join() 15 | }) 16 | 17 | expect_equal(p$exitcode, 0L) 18 | 19 | } 20 | 21 | expect_equal(queue$get(), 1L) 22 | expect_equal(queue$get(), 2L) 23 | expect_equal(queue$get(), 3L) 24 | 25 | 26 | }) 27 | -------------------------------------------------------------------------------- /tests/testthat/test-py_func.R: -------------------------------------------------------------------------------- 1 | context("function wrapping") 2 | 3 | test_that("R functions can be wrapped in a Python function with the same signature", { 4 | skip_if_no_python() 5 | 6 | # R function 7 | f1 <- function(a, b = 3) { 8 | a + b 9 | } 10 | 11 | # The same function but re-written in Python 12 | util <- py_run_string(" 13 | def f1(a, b=3): 14 | return a + b 15 | ") 16 | 17 | # signatures should match 18 | inspect <- import("inspect") 19 | expect_equal( 20 | inspect$getfullargspec(py_func(f1)), 21 | inspect$getfullargspec(util$f1)) 22 | 23 | # results should match 24 | expect_equal( 25 | py_func(f1)(1), 26 | util$f1(1)) 27 | expect_equal( 28 | py_func(f1)(1, 2), 29 | util$f1(1, 2)) 30 | expect_equal( 31 | py_func(f1)(a = 1, b = 2), 32 | util$f1(a = 1, b = 2)) 33 | 34 | has_args <- function(f) { 35 | length(inspect$getfullargspec(f)$args) != 0 36 | } 37 | # Some micellaneous test cases which should not fail 38 | expect_true(has_args(py_func(function(a = c(1, 2, 3)) {}))) 39 | expect_true(has_args(py_func(function(a = NULL) {}))) 40 | expect_true(has_args(py_func(function(a = "abc") {}))) 41 | expect_true(has_args(py_func(function(a, b = 3) {}))) 42 | expect_true(has_args(py_func(function(a = list()) {}))) 43 | expect_true(has_args(py_func(function(a = list("a", 1, list(3, "b", NULL))) {}))) 44 | # TODO: test case for py_func(function(x = NA) {}) 45 | # currently blocked by https://github.com/rstudio/reticulate/issues/197 46 | 47 | # Should error out if the R function's signature 48 | # contains esoteric Python-incompatible constructs 49 | expect_error(py_func(function(a = 1, b) {})) 50 | expect_error(py_func(function(a.b) {})) 51 | }) 52 | 53 | test_that("R functions wrapped in py_main_thread_func are called on the main thread", { 54 | skip_if_no_python() 55 | expect_equal(test$invokeOnThread(py_main_thread_func(function(x) x + 1), 41), 42) 56 | }) 57 | -------------------------------------------------------------------------------- /tests/testthat/test-python-classes.R: -------------------------------------------------------------------------------- 1 | context("classes") 2 | 3 | test_that("Python class variables are accessible via $", { 4 | skip_if_no_python() 5 | expect_equal(test$PythonClass$FOO, 1) 6 | }) 7 | 8 | test_that("Python class members can be called via $", { 9 | skip_if_no_python() 10 | expect_equal(test$PythonClass$class_method(), 1) 11 | }) 12 | 13 | 14 | test_that("py_has_method()", { 15 | # in python2, unbound methods are still methods 16 | # in python3, only bound, user-defined, functions are methods 17 | skip_if(py_version() < "3") 18 | Fraction <- import("fractions")$Fraction 19 | 20 | expect_false(py_has_method(Fraction, "conjugate")) 21 | expect_true(py_has_method(Fraction(), "conjugate")) 22 | }) 23 | 24 | 25 | if(getRversion() >= "4.3.0") 26 | test_that("nameOfClass()", { 27 | 28 | numpy <- import("numpy") 29 | x <- r_to_py(array(1:3)) 30 | expect_true(inherits(x, numpy$ndarray)) 31 | 32 | io <- import("io") 33 | builtins <- import_builtins() 34 | 35 | with(builtins$open(tempfile(), "wb") %as% f, { 36 | expect_true(inherits(f, io$BufferedWriter)) 37 | expect_false(f$closed) 38 | }) 39 | expect_true(f$closed) 40 | 41 | }) 42 | -------------------------------------------------------------------------------- /tests/testthat/test-python-closures.R: -------------------------------------------------------------------------------- 1 | context("closures") 2 | 3 | test_that("R functions are converted to Python closures", { 4 | skip_if_no_python() 5 | func <- function(x, y) x - y 6 | pyfunc <- test$reflect(func) 7 | expect_equal(func(10,15), test$callFunc(pyfunc, 10,15)) 8 | }) 9 | 10 | test_that("R functions can accept named arguments from Python", { 11 | skip_if_no_python() 12 | func <- function(x, y) x - y 13 | pyfunc <- test$reflect(func) 14 | expect_equal(func(x = 15, y = 10), test$callFunc(pyfunc, y = 10, x = 15)) 15 | }) 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/testthat/test-python-comparisons.R: -------------------------------------------------------------------------------- 1 | context("comparisons") 2 | 3 | test_that("Python integers can be compared", { 4 | skip_if_no_python() 5 | builtins <- import_builtins(convert = FALSE) 6 | a <- builtins$int(1) 7 | b <- builtins$int(2) 8 | expect_true(py_bool(a < b)) 9 | expect_true(py_bool(b > a)) 10 | expect_true(py_bool(a == a)) 11 | expect_true(py_bool(a != b)) 12 | expect_true(py_bool(a <= 1L)) 13 | expect_true(py_bool(b >= 2L)) 14 | }) 15 | 16 | test_that("Python objects with the same reference are equal", { 17 | skip_if_no_python() 18 | list_fn1 <- import_builtins(convert = FALSE)$list 19 | list_fn2 <- import_builtins(convert = TRUE)$list 20 | 21 | expect_true(py_bool(list_fn1 == list_fn2)) 22 | }) 23 | 24 | -------------------------------------------------------------------------------- /tests/testthat/test-python-complex.R: -------------------------------------------------------------------------------- 1 | context("complex numbers") 2 | 3 | test_that("Complex scalars are converted correctly", { 4 | skip_if_no_python() 5 | z <- complex(real = stats::rnorm(1), imaginary = stats::rnorm(1)) 6 | expect_true(test$isScalar(z)) 7 | expect_equal(z, test$reflect(z)) 8 | }) 9 | 10 | test_that("Complex vectors are converted correctly", { 11 | skip_if_no_python() 12 | z <- complex(real = stats::rnorm(100), imaginary = stats::rnorm(100)) 13 | expect_equal(z, test$reflect(z)) 14 | }) 15 | 16 | 17 | test_that("Conversion from complex matrix to numpy works correctly", { 18 | skip_if_no_numpy() 19 | m <- matrix(1i^ (-6:5), nrow = 4) 20 | expect_equal(m, test$reflect(m)) 21 | }) 22 | 23 | -------------------------------------------------------------------------------- /tests/testthat/test-python-datetime.R: -------------------------------------------------------------------------------- 1 | context("datetime") 2 | 3 | test_that("R dates can be converted to / from Python datetimes", { 4 | skip_if_no_python() 5 | 6 | before <- list(Sys.Date(), as.Date("2019-04-06")) 7 | after <- py_to_r(r_to_py(before)) 8 | 9 | expect_equal(before, after) 10 | }) 11 | 12 | test_that("R times can be converted to / from Python datetimes", { 13 | skip_if_no_numpy() 14 | 15 | before <- Sys.time() 16 | attr(before, "tzone") <- "UTC" 17 | after <- py_to_r(r_to_py(before)) 18 | 19 | expect_equal(as.numeric(before), as.numeric(after)) 20 | }) 21 | 22 | test_that("lists of times are converted", { 23 | skip_if_no_python() 24 | 25 | dates <- replicate(3, Sys.Date(), simplify = FALSE) 26 | expect_equal( 27 | py_to_r(r_to_py(dates)), 28 | dates 29 | ) 30 | 31 | }) 32 | 33 | test_that("R times are converted to NumPy datetime64", { 34 | skip_if_no_numpy() 35 | 36 | np <- import("numpy", convert = TRUE) 37 | 38 | before <- rep(Sys.time(), 3) 39 | converted <- r_to_py(before) 40 | expect_true(np$issubdtype(converted$dtype, np$datetime64)) 41 | 42 | after <- py_to_r(converted) 43 | expect_equal( 44 | as.numeric(as.POSIXct(before)), 45 | as.numeric(as.POSIXct(after)) 46 | ) 47 | 48 | }) 49 | 50 | test_that("R datetimes can be passed to Python functions", { 51 | skip_if_no_python() 52 | py_run_string("def identity(x): return x") 53 | main <- import_main() 54 | date <- Sys.Date() 55 | expect_equal(date, main$identity(Sys.Date())) 56 | }) 57 | 58 | test_that("timezone information is not lost during conversion", { 59 | 60 | skip_if_no_python() 61 | skip_if(py_version() < "3") 62 | 63 | datetime <- import("datetime", convert = FALSE) 64 | if (!py_has_attr(datetime, "timezone")) 65 | skip("datetime.timezone is not available") 66 | 67 | pdt <- datetime$datetime( 68 | year = 2020L, 69 | month = 8L, 70 | day = 24L, 71 | hour = 3L, 72 | minute = 4L, 73 | second = 5L, 74 | tzinfo = datetime$timezone$utc 75 | ) 76 | 77 | rdt <- py_to_r(pdt) 78 | 79 | tzone <- attr(rdt, "tzone", exact = TRUE) 80 | expect_identical(tzone, "UTC") 81 | 82 | expect_identical( 83 | format(rdt, "%Y-%m-%dT%H:%M:%S%z"), 84 | "2020-08-24T03:04:05+0000") 85 | # py_to_r(pdt$isoformat(timespec="seconds"))) 86 | 87 | if (py_version() >= "3.9") { 88 | zoneinfo <- import("zoneinfo") 89 | pdt <- datetime$datetime$now(zoneinfo$ZoneInfo("America/New_York")) 90 | rdt <- py_to_r(pdt) 91 | expect_identical(attr(rdt, "tzone", TRUE), "America/New_York") 92 | expect_identical(format(rdt, "%Y-%m-%dT%H:%M:%S%z"), 93 | py_to_r(pdt$strftime("%Y-%m-%dT%H:%M:%S%z"))) 94 | } 95 | 96 | }) 97 | -------------------------------------------------------------------------------- /tests/testthat/test-python-envs.R: -------------------------------------------------------------------------------- 1 | context("envs") 2 | 3 | test_that("conda utility functions work as expected", { 4 | # TODO: reenable these tests 5 | skip_if_no_test_environments() 6 | 7 | binary <- conda_binary() 8 | expect_type(binary, "character") 9 | expect_length(binary, 1) 10 | 11 | conda_remove('reticulate-testthat') 12 | conda_create('reticulate-testthat') 13 | expect_true('reticulate-testthat' %in% conda_list()$name) 14 | 15 | conda_install('reticulate-testthat', 'Pillow') 16 | conda_remove('reticulate-testthat', 'Pillow') 17 | 18 | conda_remove('reticulate-testthat') 19 | expect_false('reticulate-testthat' %in% conda_list()$name) 20 | 21 | conda_create('reticulate-testthat', forge = TRUE) 22 | expect_true(all(grepl("conda-forge", conda_list_packages("reticulate-testthat")$channel))) 23 | conda_remove('reticulate-testthat') 24 | 25 | conda_create('reticulate-testthat', channel = c("anaconda")) 26 | expect_true(all(grepl("anaconda", conda_list_packages("reticulate-testthat")$channel))) 27 | conda_remove('reticulate-testthat') 28 | 29 | }) 30 | 31 | test_that("virtualenv utility functions work as expected", { 32 | skip_if_no_test_environments() 33 | 34 | expect_error( 35 | virtualenv_remove('reticulate-testthat', confirm = FALSE), 36 | 'Virtual environment \'reticulate-testthat\' does not exist.' 37 | ) 38 | 39 | virtualenv_create('reticulate-testthat') 40 | virtualenv_remove('reticulate-testthat', confirm = FALSE) 41 | 42 | virtualenv_install('reticulate-testthat', 'Pillow') 43 | virtualenv_install('reticulate-testthat', 'Pillow', ignore_installed = TRUE) 44 | 45 | expect_true('reticulate-testthat' %in% virtualenv_list()) 46 | 47 | virtualenv_remove('reticulate-testthat', confirm = FALSE) 48 | 49 | expect_false('reticulate-testthat' %in% virtualenv_list()) 50 | 51 | }) 52 | -------------------------------------------------------------------------------- /tests/testthat/test-python-factors.R: -------------------------------------------------------------------------------- 1 | context("factors") 2 | 3 | test_that("R factors are converted to character", { 4 | skip_if_no_python() 5 | 6 | before <- iris$Species 7 | after <- py_to_r(r_to_py(before)) 8 | expect_equal(as.character(before), after) 9 | }) 10 | 11 | -------------------------------------------------------------------------------- /tests/testthat/test-python-formals.R: -------------------------------------------------------------------------------- 1 | 2 | context("formals") 3 | 4 | fmt_formals <- function(f) { 5 | formatted <- vapply(names(f), function(n) { 6 | v <- as.character(f[[n]]) 7 | if (is.null(f[[n]])) paste(n, '= NULL') 8 | else if (nchar(v) == 0) n 9 | else paste(n, '=', v) 10 | }, character(1)) 11 | sprintf('(%s)', paste(formatted, collapse = ', ')) 12 | } 13 | 14 | expect_formals <- function(given, expected) { 15 | if (is.character(given)) { 16 | sig <- given 17 | object <- py_eval(sprintf('lambda %s: None', given)) 18 | given <- py_get_formals(object) 19 | } else { 20 | sig <- '?' 21 | } 22 | info <- sprintf('(%s)/%s != %s', sig, fmt_formals(given), fmt_formals(expected)) 23 | expect_identical(as.list(given), expected, info) 24 | } 25 | 26 | test_that("Python signatures convert properly", { 27 | 28 | skip_if(py_version() < "3.3") 29 | 30 | expect_formals('a', alist(a = )) 31 | expect_formals('a, b=1', alist(a = , b = 1L)) 32 | expect_formals('a, *, b=1', alist(a = , ... = , b = 1L)) 33 | expect_formals('a, *args, b=1', alist(a = , ... = , b = 1L)) 34 | expect_formals('a, b=1, **kw', alist(a = , b = 1L, ... = )) 35 | expect_formals('a, *args, **kw', alist(a = , ... = )) 36 | expect_formals('a, *args, b=1, **kw', alist(a = , ... = , b = 1L)) 37 | 38 | }) 39 | 40 | test_that("Errors from e.g. builtins are not propagated", { 41 | 42 | skip_if(py_version() < "3.3") 43 | 44 | print <- import_builtins()$print 45 | expect_no_error(py_get_formals(print)) 46 | }) 47 | 48 | test_that("The inspect.Parameter signature converts properly", { 49 | 50 | skip_if(py_version() < "3.3") 51 | 52 | # Parameter.empty usually signifies no default parameter, 53 | # but for args 3 and 4 here, it *is* the default parameter. 54 | inspect <- import("inspect", convert = TRUE) 55 | Parameter <- inspect$Parameter 56 | fmls <- py_get_formals(Parameter) 57 | expect_formals(fmls, alist( 58 | name = , kind = , ... = , 59 | default = , annotation = 60 | )) 61 | 62 | }) 63 | 64 | test_that("Parameters are not matched by prefix", { 65 | 66 | skip_if(py_version() < "3.3") 67 | 68 | f_r <- function(long = NULL, ...) list(long, list(...)) 69 | f_py <- py_eval('lambda long=None, **kw: (long, kw)') 70 | expect_identical(formals(f_r), py_get_formals(f_py)) 71 | 72 | if(getRversion() >= "3.6") { 73 | op <- options(warnPartialMatchArgs = FALSE) 74 | on.exit(options(op)) 75 | } 76 | 77 | # Normal R functions match partially: 78 | expect_identical(f_r(l = 2L), list(2L, list())) 79 | # Python functions behave as expected: 80 | expect_identical(f_py(l = 2L), list(NULL, list(l = 2L))) 81 | 82 | }) 83 | -------------------------------------------------------------------------------- /tests/testthat/test-python-function.R: -------------------------------------------------------------------------------- 1 | context("functions") 2 | 3 | test_that("Python functions are marshalled as function objects", { 4 | skip_if_no_python() 5 | spec <- inspect$getfullargspec(inspect$getclasstree) 6 | expect_equal(spec$args, c("classes", "unique")) 7 | }) 8 | 9 | test_that("Python functions can be called by python", { 10 | skip_if_no_python() 11 | x <- "foo" 12 | expect_equal(test$callFunc(test$asString, x), x) 13 | }) 14 | 15 | test_that("Python callables can be called by R", { 16 | skip_if_no_python() 17 | callable <- test$create_callable() 18 | expect_equal(callable(10), 10) 19 | }) 20 | -------------------------------------------------------------------------------- /tests/testthat/test-python-import-hook.R: -------------------------------------------------------------------------------- 1 | context("imports") 2 | 3 | test_that("The reticulate import hook handles recursive imports", { 4 | 5 | skip_if_no_matplotlib() 6 | 7 | R <- file.path(R.home("bin"), "R") 8 | script <- "resources/import-test.R" 9 | args <- c("--no-save", "--no-restore", "-s", "-f", shQuote(script)) 10 | output <- system2(R, args, stdout = TRUE, stderr = TRUE) 11 | 12 | pattern <- "Loaded module '(.*)'" 13 | modules <- gsub(pattern, "\\1", output) 14 | expect_true("matplotlib.pyplot" %in% modules) 15 | 16 | }) 17 | -------------------------------------------------------------------------------- /tests/testthat/test-python-info.R: -------------------------------------------------------------------------------- 1 | 2 | test_that("python_info() accepts system python", { 3 | 4 | if (file.exists("/usr/bin/python")) { 5 | 6 | info <- python_info("/usr/bin/python") 7 | 8 | expected <- list( 9 | python = "/usr/bin/python", 10 | type = "system", 11 | root = "/usr/bin" 12 | ) 13 | 14 | expect_equal(info, expected) 15 | 16 | } 17 | 18 | if (file.exists("/usr/bin/python3")) { 19 | 20 | info <- python_info("/usr/bin/python3") 21 | 22 | expected <- list( 23 | python = "/usr/bin/python3", 24 | type = "system", 25 | root = "/usr/bin" 26 | ) 27 | 28 | expect_equal(info, expected) 29 | 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /tests/testthat/test-python-initialize.R: -------------------------------------------------------------------------------- 1 | context("py_initialize") 2 | 3 | test_that("sys.executable points to the correct python", { 4 | 5 | 6 | py_exe_stand_alone <- system2(py_exe(), c("-c", shQuote("import sys; print(sys.executable)")), stdout = TRUE) 7 | py_exe_embedded <- import("sys")$executable 8 | 9 | if (is_windows()) 10 | py_exe_embedded <- utils::shortPathName(py_exe_embedded) 11 | 12 | expect_identical(py_exe_stand_alone, py_exe_embedded) 13 | 14 | }) 15 | 16 | 17 | test_that("__main__ initialzed with only 'r'", { 18 | 19 | expect_identical( 20 | callr::r(function() names(reticulate::import_main())), 21 | "r") 22 | }) 23 | -------------------------------------------------------------------------------- /tests/testthat/test-python-lists.R: -------------------------------------------------------------------------------- 1 | context("lists") 2 | 3 | test_that("R named lists become Python dictionaries", { 4 | skip_if_no_python() 5 | l <- list(a = 1, b = 2, c = 3) 6 | reflected <- test$reflect(l) 7 | expect_equal(l$a, reflected$a) 8 | expect_equal(l$b, reflected$b) 9 | expect_equal(l$c, reflected$c) 10 | }) 11 | 12 | test_that("Python dictionaries become R named lists", { 13 | skip_if_no_python() 14 | l <- list(a = 1, b = 2, c = 3) 15 | dict <- test$makeDict() 16 | expect_equal(length(dict), length(l)) 17 | expect_equal(dict$a, l$a) 18 | expect_equal(dict$b, l$b) 19 | expect_equal(dict$c, l$c) 20 | }) 21 | 22 | test_that("R unnamed lists become Python lists", { 23 | skip_if_no_python() 24 | l <- list(1L, 2L, 3L) 25 | expect_equal(test$asString(l), "[1, 2, 3]") 26 | }) 27 | 28 | test_that("Python unnamed tuples become R unnamed lists", { 29 | skip_if_no_python() 30 | l <- list(1, 2, 3) 31 | tuple1 <- test$makeTuple() 32 | expect_equal(tuple1, l) 33 | 34 | expect_equal(length(tuple(l)), length(l)) 35 | }) 36 | 37 | 38 | test_that("length method for Python lists works", { 39 | skip_if_no_python() 40 | py <- import_builtins(convert = FALSE) 41 | l <- py$list() 42 | l$append(1) 43 | l$append(2) 44 | l$append(3) 45 | expect_equal(length(l), 3) 46 | }) 47 | 48 | test_that("tuples are converted recursively just like lists", { 49 | skip_if_no_python() 50 | t <- test$makeTupleWithOrderedDict() 51 | expect_equal(class(t[[2]]), "list") 52 | }) 53 | 54 | -------------------------------------------------------------------------------- /tests/testthat/test-python-modules.R: -------------------------------------------------------------------------------- 1 | context("modules") 2 | 3 | test_that("modules can be imported, printed with 'as'", { 4 | skip_on_cran() 5 | # previously failed when attempting to print the module 6 | # https://github.com/rstudio/reticulate/issues/631 7 | module <- import("time", as = "t") 8 | expect_output(print(module), "Module(time)", fixed = TRUE) 9 | expect_true(inherits(module, "python.builtin.module")) 10 | }) 11 | -------------------------------------------------------------------------------- /tests/testthat/test-python-pickle.R: -------------------------------------------------------------------------------- 1 | context("pickle") 2 | 3 | test_that("Objects can be saved and loaded with pickle", { 4 | skip_if_no_python() 5 | x <- dict() 6 | x$a <- 1 7 | x$b <- 2 8 | py_save_object(x, "x.pickle") 9 | on.exit(unlink("x.pickle"), add = TRUE) 10 | y <- py_load_object("x.pickle") 11 | expect_identical(y, list(a = 1, b = 2)) 12 | expect_true(py_bool(x == y)) 13 | y <- py_load_object("x.pickle", convert = FALSE) 14 | expect_s3_class(y, "python.builtin.dict") 15 | expect_true(py_to_r(x == y)) 16 | }) 17 | 18 | -------------------------------------------------------------------------------- /tests/testthat/test-python-pipenv.R: -------------------------------------------------------------------------------- 1 | context("pipenv") 2 | 3 | test_that("reticulate uses the pipenv-configured version of Python", { 4 | 5 | skip_on_cran() 6 | if (!nzchar(Sys.which("pipenv"))) 7 | skip("pipenv is not installed") 8 | 9 | # use R session directory for tempdir 10 | withr::local_envvar(TMPDIR = tempdir()) 11 | 12 | # move to temporary directory 13 | project <- tempfile("pipenv-") 14 | dir.create(project) 15 | on.exit(unlink(project), add = TRUE) 16 | 17 | owd <- setwd(project) 18 | on.exit(setwd(owd), add = TRUE) 19 | 20 | # initialize a pipenv project 21 | system("pipenv install", ignore.stdout = TRUE, ignore.stderr = TRUE) 22 | 23 | # ask for virtualenv path 24 | expected <- system("pipenv --py", intern = TRUE) 25 | 26 | # try running reticulate in child process 27 | fmt <- "R --vanilla -s -e '%s'" 28 | cmd <- sprintf(fmt, "writeLines(reticulate::py_config()$python)") 29 | actual <- system(cmd, intern = TRUE) 30 | 31 | expect_equal( 32 | normalizePath(expected), 33 | normalizePath(actual) 34 | ) 35 | 36 | }) 37 | -------------------------------------------------------------------------------- /tests/testthat/test-python-poetry.R: -------------------------------------------------------------------------------- 1 | context("poetry") 2 | 3 | test_that("reticulate uses the Poetry-configured version of Python", { 4 | 5 | if (!nzchar(Sys.which("poetry"))) 6 | skip("poetry is not installed") 7 | 8 | # unset RETICULATE_PYTHON in this scope 9 | withr::local_envvar(RETICULATE_PYTHON = NULL) 10 | 11 | # move to temporary directory 12 | project <- tempfile("poetry-") 13 | dir.create(project) 14 | on.exit(unlink(project), add = TRUE) 15 | 16 | owd <- setwd(project) 17 | on.exit(setwd(owd), add = TRUE) 18 | 19 | # initialize project 20 | system("poetry new .", ignore.stdout = TRUE, ignore.stderr = TRUE) 21 | expect_true(file.exists("pyproject.toml")) 22 | 23 | # remove dependency on pytest (poetry solver barfs?) 24 | contents <- readLines("pyproject.toml") 25 | contents <- grep("^pytest", contents, invert = TRUE, value = TRUE) 26 | writeLines(contents, con = "pyproject.toml") 27 | 28 | # try running python (force initialization of virtualenv) 29 | system("poetry run python --version") 30 | 31 | # ask for virtualenv path 32 | envpath <- system("poetry env info --path", intern = TRUE) 33 | expected <- virtualenv_python(envpath) 34 | 35 | # try running reticulate in child process 36 | fmt <- "R -s -e '%s'" 37 | cmd <- sprintf(fmt, "writeLines(reticulate::py_config()$python)") 38 | actual <- system(cmd, intern = TRUE) 39 | 40 | expect_equal( 41 | normalizePath(expected), 42 | normalizePath(actual) 43 | ) 44 | 45 | }) 46 | -------------------------------------------------------------------------------- /tests/testthat/test-python-raw.R: -------------------------------------------------------------------------------- 1 | context("raw") 2 | 3 | raw <- as.raw(c(48:57, 0, 48:57)) 4 | 5 | test_that("Raw vectors are converted to bytearraw", { 6 | skip_if_no_python() 7 | expect_s3_class(r_to_py(raw), "python.builtin.bytearray") 8 | }) 9 | 10 | test_that("Raw vectors with null bytes roundtrip correctly", { 11 | skip_if_no_python() 12 | ba <- r_to_py(raw) 13 | builtins <- import_builtins() 14 | expect_equal(builtins$len(ba), 21) 15 | expect_equal(py_to_r(ba), raw) 16 | }) 17 | 18 | test_that("Raw vector of length zero creates length zero bytearray", { 19 | skip_if_no_python() 20 | builtins <- import_builtins() 21 | ba <- r_to_py(as.raw(c())) 22 | expect_equal(builtins$len(ba), 0) 23 | }) 24 | 25 | test_that("bytearray of length zero creates length zero Raw", { 26 | skip_if_no_python() 27 | builtins <- import_builtins() 28 | expect_equal(builtins$bytearray(), raw()) 29 | }) 30 | 31 | -------------------------------------------------------------------------------- /tests/testthat/test-python-run.R: -------------------------------------------------------------------------------- 1 | context("run") 2 | 3 | test_that("Python code can be run as strings", { 4 | 5 | # runs code in main module 6 | result <- py_run_string("x = 1") 7 | expect_equal(result$x, 1L) 8 | 9 | main <- import_main(convert = TRUE) 10 | expect_equal(main$x, 1L) 11 | 12 | # runs code in local dictionary 13 | result <- py_run_string("x = 42", local = TRUE) 14 | expect_true(result$x == 42L) 15 | expect_true(main$x == 1L) 16 | 17 | }) 18 | 19 | 20 | 21 | test_that("Python files can be run", { 22 | 23 | file <- tempfile(fileext = ".py") 24 | writeLines("file = __file__", file) 25 | 26 | out <- py_run_file(file, local = TRUE) 27 | expect_s3_class(out, "python.builtin.dict") 28 | expect_equal(file, out$file) 29 | expect_false("__name__" %in% names(out)) 30 | 31 | 32 | out <- py_run_file(file, local = FALSE) 33 | expect_s3_class(out, "python.builtin.dict") 34 | expect_identical(get("pyobj", out), 35 | get("pyobj", py_get_attr(import_main(), "__dict__"))) 36 | expect_equal(file, out$file) 37 | expect_false("__name__" %in% names(out)) 38 | 39 | py_run_string("del file") # cleanup after test 40 | }) 41 | -------------------------------------------------------------------------------- /tests/testthat/test-python-scope.R: -------------------------------------------------------------------------------- 1 | 2 | test_that("r helper accesses 'active' environment for tests", { 3 | rA <- 100 4 | pA <- py_eval("r.rA", convert = TRUE) 5 | expect_equal(rA, pA) 6 | }) 7 | -------------------------------------------------------------------------------- /tests/testthat/test-python-source.R: -------------------------------------------------------------------------------- 1 | context("source") 2 | 3 | test_that("Python scripts can be sourced from local file", { 4 | skip_if_no_python() 5 | source_python(test_path('script.py')) 6 | expect_equal(add(2, 4), 6) 7 | }) 8 | 9 | test_that("Python scripts can be sourced from a URL", { 10 | skip_if_no_python() 11 | # skip_if_offline() ## needs {curl} 12 | source_python('https://raw.githubusercontent.com/rstudio/reticulate/main/tests/testthat/script.py') 13 | expect_equal(add(2, 4), 6) 14 | }) 15 | 16 | test_that("source_python assigns into the requested environment", { 17 | skip_if_no_python() 18 | env <- new.env(parent = emptyenv()) 19 | source_python(test_path('script.py'), envir = env) 20 | expect_equal(env$add(2, 4), 6) 21 | }) 22 | 23 | test_that("source_python respects the convert argument", { 24 | skip_if_no_python() 25 | source_python(test_path('script.py'), convert = FALSE) 26 | expect_s3_class(add(2, 4), 'python.builtin.object') 27 | }) 28 | 29 | test_that("python functions can call each other", { 30 | skip_if_no_python() 31 | source_python(test_path('script.py')) 32 | expect_equal(secret(), 42) 33 | expect_equal(api(), 42) 34 | }) 35 | 36 | test_that("source_python() overlays in the main module", { 37 | skip_if_no_python() 38 | source_python(test_path('script.py')) 39 | main <- import_main() 40 | expect_equal(main$value, 42) 41 | }) 42 | -------------------------------------------------------------------------------- /tests/testthat/test-python-strings.R: -------------------------------------------------------------------------------- 1 | context("strings") 2 | 3 | test_that("Unicode strings are handled by py_str", { 4 | skip_if_no_python() 5 | skip_on_cran() 6 | skip_on_os("windows") 7 | main <- py_run_string("x = u'\\xfc'", convert = FALSE) 8 | expect_equal(py_str(main$x), "ü") 9 | }) 10 | 11 | 12 | 13 | test_that("subclassed strings convert", { 14 | skip_if_no_python() 15 | skip_on_cran() 16 | 17 | # https://github.com/rstudio/reticulate/issues/1348 18 | # https://github.com/fastai/fastcore/blob/master/fastcore/basics.py#L1015 19 | PrettyString <- py_run_string( 20 | 'class PrettyString(str): 21 | def __repr__(self): return self', convert = FALSE)$PrettyString 22 | expect_identical(py_repr(PrettyString("abc_xyz")), 23 | "abc_xyz") 24 | }) 25 | 26 | -------------------------------------------------------------------------------- /tests/testthat/test-python-threads.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | test_that("py_allow_threads() can enable/disable background threads", { 4 | 5 | file <- tempfile() 6 | on.exit(unlink(file), add = TRUE) 7 | 8 | write_to_file_from_thread <- py_run_string(" 9 | def write_to_file_from_thread(path, lines): 10 | from time import sleep, localtime, strftime 11 | 12 | def write_to_file(path, lines): 13 | sleep(.1) # don't try to run until we've had a chance to return to the R main thread 14 | with open(path, 'w') as f: 15 | for line in list(lines): 16 | f.write(line + '\\n') 17 | 18 | from _thread import start_new_thread 19 | start_new_thread(write_to_file, (path, lines)) 20 | ", local = TRUE)$write_to_file_from_thread 21 | 22 | reticulate:::py_allow_threads_impl(FALSE) 23 | write_to_file_from_thread(file, letters) 24 | Sys.sleep(.5) 25 | # confirm background thread did not run while R was sleeping 26 | expect_false(file.exists(file)) 27 | # explicitly enter python and release the gil 28 | import("time")$sleep(.3) 29 | # confirm the background thread ran on py_sleep() 30 | expect_identical(readLines(file), letters) 31 | 32 | unlink(file) 33 | 34 | reticulate:::py_allow_threads_impl(TRUE) 35 | write_to_file_from_thread(file, letters) 36 | Sys.sleep(.3) 37 | # confirm that the background thread ran while R was sleeping. 38 | expect_identical(readLines(file), letters) 39 | 40 | }) 41 | 42 | 43 | 44 | test_that("Python calls into R from a background thread are evaluated", { 45 | 46 | x <- 0L 47 | r_func <- function() x <<- x+1 48 | py_file <- withr::local_tempfile(lines = "r_func()", fileext = ".py") 49 | 50 | reticulate:::py_run_file_on_thread( 51 | py_file, 52 | init_globals = list(r_func = r_func) 53 | ) 54 | 55 | # Simulate the main R thread doing non-Python work (e.g., sleeping) 56 | for(i in 1:10) { 57 | Sys.sleep(.01 * i) 58 | if (x != 0L) break 59 | } 60 | 61 | expect_equal(x, 1L) 62 | }) 63 | 64 | 65 | test_that("Errors from background threads calling into main thread are handled", { 66 | 67 | signal_r_error <- function() stop("foo-bar-baz") 68 | 69 | val <- NULL 70 | set_val <- function(v) val <<- v 71 | 72 | py_file <- withr::local_tempfile(lines = " 73 | try: signal_r_error() 74 | except Exception as e: set_val(e.args[0]) 75 | ", fileext = ".py") 76 | 77 | reticulate:::py_run_file_on_thread(py_file, init_globals = list( 78 | signal_r_error = signal_r_error, 79 | set_val = set_val 80 | )) 81 | 82 | # Simulate the main R thread doing non-Python work (e.g., sleeping) 83 | for(i in 1:10) { 84 | Sys.sleep(.01 * i) 85 | if(!is.null(val)) break 86 | } 87 | 88 | expect(!is.null(val), "`thread_err_msg` never created") 89 | expect_equal(val, "foo-bar-baz") 90 | 91 | }) 92 | -------------------------------------------------------------------------------- /tests/testthat/test-python-vectors.R: -------------------------------------------------------------------------------- 1 | context("vectors") 2 | 3 | test_that("Single element vectors are treated as scalars", { 4 | skip_if_no_python() 5 | expect_true(test$isScalar(5)) 6 | expect_true(test$isScalar(5L)) 7 | expect_true(test$isScalar("5")) 8 | expect_true(test$isScalar(TRUE)) 9 | }) 10 | 11 | test_that("Multi-element vectors are treated as lists", { 12 | skip_if_no_python() 13 | expect_true(test$isList(c(5,5))) 14 | expect_true(test$isList(c(5L,5L))) 15 | expect_true(test$isList(c("5", "5"))) 16 | expect_true(test$isList(c(TRUE, TRUE))) 17 | }) 18 | 19 | test_that("The list function forces single-element vectors to be lists", { 20 | skip_if_no_python() 21 | expect_false(test$isScalar(list(5))) 22 | expect_false(test$isScalar(list(5L))) 23 | expect_false(test$isScalar(list("5"))) 24 | expect_false(test$isScalar(list(TRUE))) 25 | }) 26 | 27 | -------------------------------------------------------------------------------- /tests/testthat/test-python-virtual-environments.R: -------------------------------------------------------------------------------- 1 | context("virtual environments") 2 | 3 | test_that("reticulate can bind to virtual environments created with venv", { 4 | 5 | skip_if_no_python() 6 | skip_on_cran() 7 | skip_on_os("windows") 8 | 9 | Sys.unsetenv("RETICULATE_PYTHON") 10 | Sys.unsetenv("RETICULATE_PYTHON_ENV") 11 | 12 | # ensure cacert.pem goes to right folder 13 | withr::local_envvar(TMPDIR = tempdir()) 14 | 15 | # find Python 3 binary for testing 16 | python3 <- Sys.which("python3") 17 | if (!nzchar(python3)) 18 | skip("test requires Python 3 binary with venv module") 19 | 20 | # create a virtual environment in the tempdir 21 | venv <- tempfile("python-3-venv") 22 | status <- tryCatch(system(paste(python3, "-m venv", venv)), error = identity) 23 | if (inherits(status, "error")) 24 | skip("test requires Python 3 binary with venv module") 25 | 26 | on.exit(unlink(venv, recursive = TRUE), add = TRUE) 27 | venv <- normalizePath(venv, mustWork = TRUE) 28 | 29 | # try running reticulate and binding to that virtual environment 30 | R <- file.path(R.home("bin"), "R") 31 | script <- normalizePath("resources/venv-activate.R") 32 | args <- c("--vanilla", "--slave", "-f", shQuote(script), "--args", shQuote(venv)) 33 | output <- system2(R, args, stdout = TRUE) 34 | 35 | # test that we're using the site-packages dir in the venv 36 | site <- grep("site-packages", output, fixed = TRUE, value = TRUE) 37 | expect_true(grepl(venv, site)) 38 | 39 | }) 40 | -------------------------------------------------------------------------------- /tests/testthat/test-r-extptr-capsule.R: -------------------------------------------------------------------------------- 1 | test_that("py_to_r() returns the extptr", { 2 | 3 | # Mock class for testing 4 | Rcpp::sourceCpp(code=' 5 | #include 6 | using namespace Rcpp; 7 | 8 | class AClass { 9 | public: 10 | AClass() { 11 | Rprintf("AClass created"); 12 | } 13 | ~AClass() { 14 | Rprintf("AClass destroyed"); 15 | } 16 | }; 17 | 18 | // [[Rcpp::export]] 19 | SEXP getA() { 20 | AClass* ptr = new AClass; 21 | return Rcpp::XPtr< AClass >(ptr); 22 | } 23 | ', env = environment()) 24 | 25 | expect_output(x <- getA(), "AClass created") 26 | expect_output({ 27 | xpy <- r_to_py(x) 28 | x_ <- py_to_r(xpy) 29 | }, NA) 30 | expect_reference(x, x_) # same memory address 31 | 32 | # test that gc()ing the py capsule doesn't gc() the extptr if there is 33 | # a live R reference to it 34 | expect_output({ rm(x, xpy); for(i in 1:3) gc(full = TRUE) }, NA) 35 | expect_output({ rm(x_) ; for(i in 1:3) gc(full = TRUE) }, "AClass destroyed") 36 | 37 | 38 | # test that gc()ing the R ref to the extptr doesn't actuall gc() the extptr 39 | # object if the py capsule has a live reference to it. 40 | expect_output(x <- getA(), "AClass created") 41 | expect_output({ 42 | xpy <- r_to_py(x) 43 | x_ <- py_to_r(xpy) 44 | }, NA) 45 | expect_reference(x, x_) # same memory address 46 | expect_output({ rm(x, x_); for(i in 1:3) gc(full = TRUE) }, NA) 47 | expect_output({ rm(xpy) ; for(i in 1:3) gc(full = TRUE) }, "AClass destroyed") 48 | 49 | }) 50 | -------------------------------------------------------------------------------- /tests/testthat/test-repl-magics.R: -------------------------------------------------------------------------------- 1 | context("repl_python() magics") 2 | 3 | quiet_repl <- function() { 4 | options("reticulate.repl.quiet" = TRUE) 5 | sink(nullfile()) 6 | } 7 | 8 | if(getRversion() < "3.6") 9 | nullfile <- function() 10 | if (.Platform$OS.type == "windows") "nul:" else "/dev/null" 11 | 12 | unquiet_repl <- function() { 13 | options("reticulate.repl.quiet" = NULL) 14 | sink() 15 | } 16 | 17 | local_quiet_repl <- function(envir = parent.frame()) { 18 | quiet_repl() 19 | withr::defer(unquiet_repl(), envir = envir) 20 | } 21 | 22 | 23 | test_that("%pwd, %cd", { 24 | 25 | owd <- getwd() 26 | local_quiet_repl() 27 | 28 | 29 | expect_output( 30 | repl_python(input = "%pwd"), 31 | paste0(">>> %pwd\n", owd), 32 | fixed = TRUE) 33 | 34 | expect_error( 35 | repl_python(input = "%pwd foo"), "no arguments") 36 | 37 | repl_python(input = c( 38 | "x = %pwd", 39 | "%cd ..", 40 | "y = %pwd", 41 | "%cd -", 42 | "z = %pwd" 43 | )) 44 | 45 | expect_equal(py_eval("x"), owd) 46 | expect_equal(py_eval("y"), dirname(owd)) 47 | expect_equal(py_eval("z"), owd) 48 | 49 | setwd(owd) 50 | 51 | }) 52 | 53 | 54 | 55 | test_that("%env", { 56 | 57 | local_quiet_repl() 58 | 59 | repl_python(input = c( 60 | "x = %env FOOVAR", 61 | "%env FOOVAR baz", 62 | "y = %env FOOVAR", 63 | "%env FOOVAR=foo", 64 | "z = %env FOOVAR" 65 | )) 66 | 67 | expect_equal(py_eval("x"), "") 68 | expect_equal(py_eval("y"), "baz") 69 | expect_equal(py_eval("z"), "foo") 70 | Sys.unsetenv("FOOVAR") 71 | 72 | }) 73 | 74 | test_that("%system, !", { 75 | 76 | local_quiet_repl() 77 | 78 | repl_python(input = "x = !ls") 79 | expect_equal(py_eval("x"), system("ls", intern = TRUE)) 80 | 81 | }) 82 | 83 | 84 | test_that("%pip", { 85 | 86 | skip_if_no_test_environments() 87 | local_quiet_repl() 88 | 89 | env_path <- virtualenv_create("test-pip-repl-magic") 90 | 91 | expect_true(callr::r(function(env_path) { 92 | Sys.unsetenv("RETICULATE_PYTHON") 93 | library(reticulate) 94 | 95 | use_virtualenv(env_path, required = TRUE) 96 | 97 | repl_python(input = "%pip install requests") 98 | import("requests") 99 | TRUE 100 | }, args = list(env_path = env_path))) 101 | 102 | virtualenv_remove(env_path, confirm = FALSE) 103 | # unlink(env_path, recursive = TRUE) 104 | 105 | }) 106 | 107 | 108 | test_that("%conda", { 109 | 110 | skip_if_no_test_environments() 111 | skip_if_no_conda() 112 | local_quiet_repl() 113 | 114 | capture.output({ 115 | python <- conda_create("test-conda-repl-magic") 116 | }) 117 | 118 | expect_true(callr::r(function(python) { 119 | Sys.unsetenv("RETICULATE_PYTHON") 120 | library(reticulate) 121 | 122 | use_condaenv(python, required = TRUE) 123 | 124 | # TODO: pass through interactive response from the user for prompts like: 125 | # Proceed ([y]/n)? 126 | repl_python(input = "%conda install -y rsa") 127 | import("rsa") 128 | TRUE 129 | }, 130 | stdout = tempfile("conda output"), 131 | args = list(python = python))) 132 | 133 | capture.output({ 134 | conda_remove("test-conda-repl-magic") 135 | }) 136 | 137 | # info <- get("get_python_conda_info",asNamespace("reticulate"))(python) 138 | # unlink(info$root, recursive = TRUE) 139 | }) 140 | -------------------------------------------------------------------------------- /tests/testthat/test-subprocess.R: -------------------------------------------------------------------------------- 1 | context("subprocess module") 2 | 3 | test_that("subprocess.Popen works", { 4 | 5 | subprocess <- import("subprocess") 6 | 7 | # needs patching on Windows in the RStudio IDE 8 | # https://github.com/rstudio/reticulate/issues/1448 9 | expect_no_error({ 10 | subprocess$Popen( 11 | c("ls", "."), 12 | shell = FALSE, 13 | stderr = subprocess$PIPE, 14 | stdout = subprocess$PIPE 15 | ) 16 | }) 17 | 18 | }) 19 | 20 | test_that("modules that subclass Popen work", { 21 | 22 | expect_no_error({ 23 | import("asyncio") 24 | }) 25 | 26 | }) 27 | -------------------------------------------------------------------------------- /tools/compile-python.R: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env Rscript 2 | 3 | package_dir <- Sys.getenv("R_PACKAGE_DIR", NA) 4 | if (is.na(package_dir)) 5 | package_dir <- find.package("reticulate") 6 | 7 | tryCatch({ 8 | owd <- setwd(package_dir) 9 | for (stale_pycache in grep("__pycache__$", list.dirs(recursive = TRUE), value = TRUE)) { 10 | message("deleting: ", stale_pycache) 11 | unlink(stale_pycache, recursive = TRUE) 12 | } 13 | 14 | df <- reticulate::virtualenv_starter(all = TRUE) 15 | df <- df[order(df$version, decreasing = TRUE), ] 16 | df$minor <- df$version[, 1:2] 17 | df <- df[!duplicated(df$minor), ] 18 | for (python in df$path) { 19 | reticulate:::system2t(python, "-m compileall config python") 20 | } 21 | }, finally = setwd(owd)) 22 | -------------------------------------------------------------------------------- /tools/r-3-5.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rstudio/r-base:3.5-focal 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | RUN apt-get update \ 5 | && apt-get install -y \ 6 | libpng-dev qpdf \ 7 | python3 libpython3-dev python-is-python3 python3-venv \ 8 | 9 | ## to install devtools: 10 | # apt-get install -y \ 11 | # libssl-dev libfontconfig1-dev libxml2-dev 12 | 13 | RUN rm -rf /var/lib/apt/lists/* 14 | 15 | RUN R -e '{ \ 16 | options(repos = c(CRAN = "https://cloud.r-project.org"), Ncpus = 2L); \ 17 | deps <- unlist(tools::package_dependencies("reticulate", which = "most")); \ 18 | install.packages(c(deps, "remotes", "rcmdcheck", "rmarkdown")); \ 19 | }' 20 | 21 | RUN python -m venv /.virtualenvs/r-reticulate 22 | RUN /.virtualenvs/r-reticulate/bin/python -m pip install --upgrade --no-user 'pip' 'wheel' 'setuptools' 23 | RUN /.virtualenvs/r-reticulate/bin/python -m pip install --upgrade --no-user "numpy" "docutils" "pandas" "scipy" "matplotlib" "ipython" "tabulate" "plotly" "psutil" "kaleido" "wrapt" 24 | ENV RETICULATE_PYTHON=/.virtualenvs/r-reticulate/bin/python 25 | 26 | ADD ./ /reticulate 27 | # RUN R -e 'remotes::install_local("/reticulate", dependencies = TRUE)' 28 | # RUN R -e 'devtools::test("/reticulate")' 29 | RUN R -e '{options(crayon.enabled = TRUE); rcmdcheck::rcmdcheck("/reticulate", args = c("--no-manual", "--as-cran"), error_on = "warning");}' 30 | 31 | # docker build -t reticulate-r-3-5 -f tools/r-3-5.Dockerfile . 32 | # docker run -it reticulate-r-3-5 R -q -e 'rcmdcheck::rcmdcheck("/reticulate", args = c("--no-manual", "--as-cran"))' 33 | 34 | # docker build --pull --rm -f "tools/r-3-5.Dockerfile" -t reticulate-r-3-5 . -------------------------------------------------------------------------------- /tools/tools-build-site.sh: -------------------------------------------------------------------------------- 1 | 2 | # Rebuilds the reticulate pkgdown website, using a particular 3 | # version of reticulate (which should be tagged on release). 4 | set -eux 5 | 6 | : "${TMPDIR:=/tmp}" 7 | 8 | if [ -z "${VERSION}" ]; then 9 | echo "Usage: VERSION= make site" 10 | exit 0 11 | fi 12 | 13 | cd "${TMPDIR}" 14 | 15 | rm -rf reticulate-deploy 16 | mkdir reticulate-deploy 17 | cd reticulate-deploy 18 | 19 | git clone -b "${VERSION}" https://github.com/rstudio/reticulate reticulate 20 | git clone -b gh-pages https://github.com/rstudio/reticulate site 21 | 22 | cd reticulate 23 | R -s -e 'pkgdown::build_site()' 24 | cd .. 25 | 26 | cd site 27 | rm -rf ./* 28 | cd .. 29 | 30 | cp -R reticulate/docs/ site/ 31 | cp -R reticulate/images site/images 32 | 33 | cd site 34 | rm reference/Rplot* 35 | touch .nojekyll 36 | git add -A 37 | git commit -m "Build site for reticulate: ${VERSION}" 38 | git push -u 39 | cd .. 40 | 41 | cd .. 42 | rm -rf reticulate-deploy 43 | 44 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /vignettes/extra.css: -------------------------------------------------------------------------------- 1 | 2 | .page-header { 3 | margin-bottom: 6px; 4 | } 5 | 6 | 7 | h4.date, 8 | h4.author { 9 | display: none; 10 | } 11 | 12 | h2.hasAnchor { 13 | font-weight: 350; 14 | } 15 | 16 | .contents h2 { 17 | padding-top: 80px; 18 | margin-top: -80px; 19 | font-size: 28px; 20 | } 21 | 22 | .ref-index tbody { 23 | margin-bottom: 60px; 24 | } 25 | 26 | pre:not([class]) { 27 | background-color: white; 28 | } 29 | 30 | blockquote { 31 | font-size: inherit; 32 | } 33 | 34 | .examples .page-header { 35 | border-bottom: none; 36 | margin: 0; 37 | padding-bottom: 0; 38 | } 39 | 40 | .examples .sourceCode { 41 | margin-top: 25px; 42 | } 43 | 44 | #sidebar .nav>li>a { 45 | padding-top: 1px; 46 | padding-bottom: 2px; 47 | } 48 | 49 | #installation .sourceCode { 50 | font-size: 13px; 51 | } 52 | 53 | .r-plot { 54 | margin-top: 15px; 55 | margin-bottom: 20px; 56 | border: solid 1px #cccccc; 57 | } 58 | 59 | .screenshot, .illustration { 60 | margin-bottom: 20px; 61 | border: solid 1px #cccccc; 62 | } 63 | 64 | 65 | .source-ref { 66 | margin-bottom: 20px; 67 | } 68 | 69 | .source-ref .caption { 70 | display: none; 71 | } 72 | 73 | .link-table td { 74 | padding-top: 20px !important; 75 | padding-bottom: 210x !important; 76 | } 77 | 78 | .alert-warning { 79 | color: #8a6d3b; 80 | background-color: #fcf8e3; 81 | padding: 15px; 82 | margin-top: 20px; 83 | margin-bottom: 20px; 84 | background-image: linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%); 85 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 86 | background-repeat: repeat-x; 87 | border: 1px solid #f5e79e; 88 | border-radius: 4px; 89 | } 90 | 91 | -------------------------------------------------------------------------------- /vignettes/images/code_completion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/vignettes/images/code_completion.png -------------------------------------------------------------------------------- /vignettes/images/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/vignettes/images/help.png -------------------------------------------------------------------------------- /vignettes/images/python_chunks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/vignettes/images/python_chunks.png -------------------------------------------------------------------------------- /vignettes/images/python_version.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/vignettes/images/python_version.png -------------------------------------------------------------------------------- /vignettes/images/r_from_python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/vignettes/images/r_from_python.png -------------------------------------------------------------------------------- /vignettes/images/repl_python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/vignettes/images/repl_python.png -------------------------------------------------------------------------------- /vignettes/images/reticulate_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/vignettes/images/reticulate_disable.png -------------------------------------------------------------------------------- /vignettes/images/reticulate_enable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/vignettes/images/reticulate_enable.png -------------------------------------------------------------------------------- /vignettes/images/rmarkdown_engine_zoomed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/vignettes/images/rmarkdown_engine_zoomed.png -------------------------------------------------------------------------------- /vignettes/images/rmarkdown_reticulate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/vignettes/images/rmarkdown_reticulate.png -------------------------------------------------------------------------------- /vignettes/images/rmarkdown_reticulate_matplotlib.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/vignettes/images/rmarkdown_reticulate_matplotlib.png -------------------------------------------------------------------------------- /vignettes/images/source_python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/reticulate/32375b8785a4f00290973c98638d5cc0798a2a07/vignettes/images/source_python.png -------------------------------------------------------------------------------- /vignettes/r_markdown.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "R Markdown Python Engine" 3 | output: 4 | rmarkdown::html_vignette 5 | vignette: > 6 | %\VignetteIndexEntry{R Markdown Python Engine} 7 | %\VignetteEngine{knitr::rmarkdown} 8 | %\VignetteEncoding{UTF-8} 9 | --- 10 | 11 | 12 | 13 | ## Overview 14 | 15 | The **reticulate** package includes a Python engine for [R Markdown](https://rmarkdown.rstudio.com/) that enables easy interoperability between Python and R chunks. 16 | 17 | Python chunks behave very similar to R chunks (including graphical output from matplotlib) and the two languages have full access each other's objects. Built in conversion for many Python object types is provided, including [NumPy](https://numpy.org/) arrays and [Pandas](https://pandas.pydata.org/) data frames. 18 | 19 | If you are using knitr version 1.18 or higher, then the reticulate Python engine will be enabled by default whenever reticulate is installed and no further setup is required. If you are running an earlier version of knitr or want to disable the use of the reticulate engine see the [Engine Setup] section below. 20 | 21 | ## Python Version 22 | 23 | By default, reticulate uses the version of Python found on your `PATH` (i.e. `Sys.which("python")`). If you want to use an alternate version you should add one of the `use_python()` family of functions to your R Markdown setup chunk, for example: 24 | 25 | ![](images/python_version.png){.screenshot width="98%"} 26 | 27 | See the article on [Python Version Configuration](versions.html) for additional details on configuring Python versions (including the use of conda or virtualenv environments). 28 | 29 | ## Python Chunks 30 | 31 | Python code chunks work exactly like R code chunks: Python code is executed and any print or graphical (matplotlib) output is included within the document. 32 | 33 | Python chunks all execute within a single Python session so have access to all objects created in previous chunks. Chunk options like `echo`, `include`, etc. all work as expected. 34 | 35 | Here's an R Markdown document that demonstrates this: 36 | 37 | ![](images/python_chunks.png){.screenshot width="98%"} 38 | 39 | [RStudio v1.2](https://posit.co/download/rstudio-desktop/) or greater for reticulate IDE support. 40 | 41 | ## Calling Python from R 42 | 43 | All objects created within Python chunks are available to R using the `py` object exported by the reticulate package. For example, the following code demonstrates reading and filtering a CSV file using Pandas then plotting the resulting data frame using ggplot2: 44 | 45 | ![](images/rmarkdown_engine_zoomed.png){.screenshot width="98%"} 46 | 47 | See the [Calling Python from R](calling_python.html) article for additional details on how to interact with Python types from within R 48 | 49 | ## Calling R from Python 50 | 51 | You can analogously access R objects within Python chunks via the `r` object. For example: 52 | 53 | ![](images/r_from_python.png){.screenshot width="98%"} 54 | 55 | ## Engine Setup 56 | 57 | If you are using a version of knitr prior to 1.18 then add this code to your setup chunk to enable the reticulate Python engine: 58 | 59 | ![](images/reticulate_enable.png){.screenshot width="98%"} 60 | 61 | If you do not wish to use the reticulate Python engine then set the `python.reticulate` chunk option to `FALSE`: 62 | 63 | ![](images/reticulate_disable.png){.screenshot width="98%"} 64 | 65 | --------------------------------------------------------------------------------