├── .Rbuildignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── R ├── env.R └── zzz.R ├── README.Rmd ├── README.md ├── inst └── helloworld.py ├── man └── hello.Rd └── pyfns.Rproj /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^README\.Rmd$ 4 | ^LICENSE\.md$ 5 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: pyfns 2 | Type: Package 3 | Title: What the Package Does (Title Case) 4 | Version: 0.1.0 5 | Author: Who wrote it 6 | Maintainer: The package maintainer 7 | Description: More about what it does (maybe more than one line) 8 | Use four spaces when indenting paragraphs within the Description. 9 | License: MIT + file LICENSE 10 | Encoding: UTF-8 11 | LazyData: true 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2023 2 | COPYRIGHT HOLDER: pyfns authors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2023 pyfns authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | exportPattern("^[[:alpha:]]+") 2 | -------------------------------------------------------------------------------- /R/env.R: -------------------------------------------------------------------------------- 1 | pyfn_env <- rlang::env() 2 | 3 | #'@export 4 | hello_world <- function() { 5 | pyfn_env$hello_world() 6 | } 7 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | .onLoad <- function(libname, pkgname){ 2 | reticulate::source_python( 3 | system.file("helloworld.py", package = "pyfns"), 4 | envir = pyfn_env 5 | ) 6 | } 7 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r, include = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "man/figures/README-", 12 | out.width = "100%" 13 | ) 14 | ``` 15 | 16 | # pyfns 17 | 18 | This package serves to illustrate a minimal approach to exporting Python 19 | functions in R packages. 20 | 21 | The process is _fairly_ simple. 22 | 23 | - We create an environment inside of our package 24 | - On package start-up we source python scripts using `reticulate::source_python()` into the new environment 25 | - We create R wrapper functions that call the reticulated function. 26 | 27 | Example usage: 28 | 29 | ```{r} 30 | pyfns::hello_world() 31 | ``` 32 | 33 | ## Storing Python Scripts 34 | 35 | Store python scripts inside of `inst/`. These files can be read using 36 | `system.file()`. In this example `inst/helloworld.py` contains 37 | 38 | ```py 39 | def hello_world(): 40 | return "Helloooo world" 41 | ``` 42 | 43 | ## Creating an environment 44 | 45 | Before we can source python scripts, we must create an environment to soure them into. This is done in `R/env.R` like so 46 | 47 | ```r 48 | pyfn_env <- rlang::env() 49 | ``` 50 | 51 | ## Sourcing scripts 52 | 53 | Scripts are sourced in `R/zzz.R` in which there is an `.onLoad()` function call. This gets called only once when the package is loaded. 54 | 55 | ```r 56 | .onLoad <- function(libname, pkgname){ 57 | reticulate::source_python( 58 | system.file("helloworld.py", package = "pyfns"), 59 | envir = pyfn_env 60 | ) 61 | } 62 | ``` 63 | 64 | In this chunk we use `reticulate::source_python()` to bring the python function into scope. The function needs a path to the python script that we want to source. This is where `system.file()` comes into play. It can access files stored in `inst`. _Note that it does not include `inst`_. And most importantly we set `envir = pyfn_env` which is the environment we created in `R/env.R` 65 | 66 | ## Wrapper functions 67 | 68 | Since the functions are being sourced into `pyfn_env` they can be called from the environment directly. In `R/env.R`, the R function `hello_world()` is just calling the `hello_world()` python function from the `pyfn_env`. If there were arguments we can pass them in using `...` in the outer function or recreating the same function arguments. 69 | 70 | ```r 71 | #'@export 72 | hello_world <- function() { 73 | pyfn_env$hello_world() 74 | } 75 | ``` 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # pyfns 5 | 6 | This package serves to illustrate a minimal approach to exporting Python 7 | functions in R packages. 8 | 9 | The process is *fairly* simple. 10 | 11 | - We create an environment inside of our package 12 | - On package start-up we source python scripts using 13 | `reticulate::source_python()` into the new environment 14 | - We create R wrapper functions that call the reticulated function. 15 | 16 | Example usage: 17 | 18 | ``` r 19 | pyfns::hello_world() 20 | #> [1] "Helloooo world" 21 | ``` 22 | 23 | ## Storing Python Scripts 24 | 25 | Store python scripts inside of `inst/`. These files can be read using 26 | `system.file()`. In this example `inst/helloworld.py` contains 27 | 28 | ``` py 29 | def hello_world(): 30 | return "Helloooo world" 31 | ``` 32 | 33 | ## Creating an environment 34 | 35 | Before we can source python scripts, we must create an environment to 36 | soure them into. This is done in `R/env.R` like so 37 | 38 | ``` r 39 | pyfn_env <- rlang::env() 40 | ``` 41 | 42 | ## Sourcing scripts 43 | 44 | Scripts are sourced in `R/zzz.R` in which there is an `.onLoad()` 45 | function call. This gets called only once when the package is loaded. 46 | 47 | ``` r 48 | .onLoad <- function(libname, pkgname){ 49 | reticulate::source_python( 50 | system.file("helloworld.py", package = "pyfns"), 51 | envir = pyfn_env 52 | ) 53 | } 54 | ``` 55 | 56 | In this chunk we use `reticulate::source_python()` to bring the python 57 | function into scope. The function needs a path to the python script that 58 | we want to source. This is where `system.file()` comes into play. It can 59 | access files stored in `inst`. *Note that it does not include `inst`*. 60 | And most importantly we set `envir = pyfn_env` which is the environment 61 | we created in `R/env.R` 62 | 63 | ## Wrapper functions 64 | 65 | Since the functions are being sourced into `pyfn_env` they can be called 66 | from the environment directly. In `R/env.R`, the R function 67 | `hello_world()` is just calling the `hello_world()` python function from 68 | the `pyfn_env`. If there were arguments we can pass them in using `...` 69 | in the outer function or recreating the same function arguments. 70 | 71 | ``` r 72 | #'@export 73 | hello_world <- function() { 74 | pyfn_env$hello_world() 75 | } 76 | ``` 77 | -------------------------------------------------------------------------------- /inst/helloworld.py: -------------------------------------------------------------------------------- 1 | def hello_world(): 2 | return "Helloooo world" 3 | -------------------------------------------------------------------------------- /man/hello.Rd: -------------------------------------------------------------------------------- 1 | \name{hello} 2 | \alias{hello} 3 | \title{Hello, World!} 4 | \usage{ 5 | hello() 6 | } 7 | \description{ 8 | Prints 'Hello, world!'. 9 | } 10 | \examples{ 11 | hello() 12 | } 13 | -------------------------------------------------------------------------------- /pyfns.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | --------------------------------------------------------------------------------