Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sasquatch: Use SAS, R, and Quarto together #673

Open
11 of 29 tasks
ryanzomorrodi opened this issue Nov 18, 2024 · 29 comments
Open
11 of 29 tasks

sasquatch: Use SAS, R, and Quarto together #673

ryanzomorrodi opened this issue Nov 18, 2024 · 29 comments
Assignees

Comments

@ryanzomorrodi
Copy link

ryanzomorrodi commented Nov 18, 2024

Submitting Author Name: Ryan Zomorrodi
Submitting Author Github Handle: @ryanzomorrodi
Other Package Authors Github handles: (comma separated, delete if none)
Repository: https://github.com/ryanzomorrodi/sasquatch
Version submitted: 0.0.0.9004
Submission type: Standard
Editor: @maurolepore
Reviewers: TBD

Archive: TBD
Version accepted: TBD
Language: en


  • Paste the full DESCRIPTION file inside a code block below:
Package: sasquatch
Title: Use 'SAS', R, and 'quarto' Together
Version: 0.0.0.9004
Authors@R: 
    person("Ryan", "Zomorrodi", , "[email protected]", role = c("aut", "cre", "cph"),
           comment = c(ORCID = "0009-0003-6417-5985"))
Description: Use R and 'SAS' within reproducible multilingual 'quarto' 
    documents. Run 'SAS' code blocks interactively, send data back and forth
    between 'SAS' and R, and render 'SAS' output within quarto documents. 'SAS'
    connections are established through a combination of 'SASPy' and 
    'reticulate'.
License: MIT + file LICENSE
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.2
SystemRequirements: 
    python (>= 3.0.0), 
    SASPy,
    java (>= 7) required for IOM access method
Imports: 
    htmlwidgets,
    knitr,
    reticulate,
    rstudioapi
URL: https://ryanzomorrodi.github.io/sasquatch/, https://github.com/ryanzomorrodi/sasr
Suggests: 
    rmarkdown,
    testthat (>= 3.0.0)
VignetteBuilder: knitr
Config/testthat/edition: 3
BugReports: https://github.com/ryanzomorrodi/sasr/issues

Scope

  • Please indicate which category or categories from our package fit policies this package falls under: (Please check an appropriate box below. If you are unsure, we suggest you make a pre-submission inquiry.):

    • data retrieval
    • data extraction
    • data munging
    • data deposition
    • data validation and testing
    • workflow automation
    • version control
    • citation management and bibliometrics
    • scientific software wrappers
    • field and lab reproducibility tools
    • database software bindings
    • geospatial data
    • text analysis
  • Explain how and why the package falls under these categories (briefly, 1-2 sentences):

sasquatch allows users to create remote SAS sessions, work with them interactively, pass tables between R and SAS, and render quarto documents.

  • Who is the target audience and what are scientific applications of this package?

The target audience would be anyone looking to combine SAS in R within a single workflow. Using quarto, analysts can have one single reproducable report that combines SAS and R (and python potentially).

saquatch works similarly to packages like sasr or configSAS. sasr, while similar to sasquatch, does not include interactive SAS functionality or a knitr engine. On the other hand, configSAS includes a knitr engine, but no interactive SAS functionality. configSAS knitr output also does not include syntax highlighting and nested SAS output interferes with the styles of the rest of the document.

N/A

  • If you made a pre-submission inquiry, please paste the link to the corresponding issue, forum post, or other discussion, or @tag the editor you contacted.

  • Explain reasons for any pkgcheck items which your package is unable to pass.

Locally, pkgcheck is able to pass all tests except coverage. I do not currently have any coverage over the knitr engine because I am struggling to think about how I can test that effectively. My assumption that the automated pkgcheck testing will fail due to the need for python, python package SASPy, java, and a SAS License. (I have set up a CI that has all of these set up within the sasquatch repo).

Technical checks

Confirm each of the following by checking the box.

This package:

Publication options

  • Do you intend for this package to go on CRAN?

  • Do you intend for this package to go on Bioconductor?

  • Do you wish to submit an Applications Article about your package to Methods in Ecology and Evolution? If so:

MEE Options
  • The package is novel and will be of interest to the broad readership of the journal.
  • The manuscript describing the package is no longer than 3000 words.
  • You intend to archive the code for the package in a long-term repository which meets the requirements of the journal (see MEE's Policy on Publishing Code)
  • (Scope: Do consider MEE's Aims and Scope for your manuscript. We make no guarantee that your manuscript will be within MEE scope.)
  • (Although not required, we strongly recommend having a full manuscript prepared when you submit here.)
  • (Please do not submit your package separately to Methods in Ecology and Evolution)

Code of conduct

@ropensci-review-bot
Copy link
Collaborator

Thanks for submitting to rOpenSci, our editors and @ropensci-review-bot will reply soon. Type @ropensci-review-bot help for help.

@ropensci-review-bot
Copy link
Collaborator

🚀

Editor check started

👋

@ropensci-review-bot
Copy link
Collaborator

Checks for sasquatch (v0.0.0.9004)

git hash: 8353d8ac

  • ✔️ Package name is available
  • ✔️ has a 'codemeta.json' file.
  • ✔️ has a 'contributing' file.
  • ✔️ uses 'roxygen2'.
  • ✔️ 'DESCRIPTION' has a URL field.
  • ✔️ 'DESCRIPTION' has a BugReports field.
  • ✔️ Package has at least one HTML vignette
  • ✔️ All functions have examples.
  • ✔️ Package has continuous integration checks.
  • ✖️ Package coverage failed
  • ✖️ R CMD check found 1 error.
  • ✔️ R CMD check found no warnings.

Important: All failing checks above must be addressed prior to proceeding

Package License: MIT + file LICENSE


1. Package Dependencies

Details of Package Dependency Usage (click to open)

The table below tallies all function calls to all packages ('ncalls'), both internal (r-base + recommended, along with the package itself), and external (imported and suggested packages). 'NA' values indicate packages to which no identified calls to R functions could be found. Note that these results are generated by an automated code-tagging system which may not be entirely accurate.

type package ncalls
internal sasquatch 25
internal base 10
internal graphics 1
imports htmlwidgets 1
imports knitr 1
imports reticulate 1
imports rstudioapi 1
suggests rmarkdown NA
suggests testthat NA
linking_to NA NA

Click below for tallies of functions used in each package. Locations of each call within this package may be generated locally by running 's <- pkgstats::pkgstats(<path/to/repo>)', and examining the 'external_calls' table.

sasquatch

read_file (2), rstudio_context (2), sas_engine (2), check_connection (1), newline (1), r_to_sas (1), rstudio_selection (1), rstudio_text_tidy (1), sas_connect (1), sas_disconnect (1), sas_download (1), sas_get_session (1), sas_list (1), sas_remove (1), sas_run_file (1), sas_run_selected (1), sas_run_string (1), sas_to_r (1), sas_upload (1), sas_use_quarto (1), wrap_in_iframe (1), wrap_in_pre (1)

base

list (2), log (2), system.file (2), length (1), options (1), paste (1), paste0 (1)

graphics

text (1)

htmlwidgets

sizingPolicy (1)

knitr

knit_engines$set (1)

reticulate

import (1)

rstudioapi

primary_selection (1)


2. Statistical Properties

This package features some noteworthy statistical properties which may need to be clarified by a handling editor prior to progressing.

Details of statistical properties (click to open)

The package has:

  • code in R (100% in 9 files) and XML (0% in 1 files)
  • 1 authors
  • 1 vignette
  • no internal data file
  • 4 imported packages
  • 14 exported functions (median 7 lines of code)
  • 39 non-exported functions in R (median 6 lines of code)

Statistical properties of package structure as distributional percentiles in relation to all current CRAN packages
The following terminology is used:

  • loc = "Lines of Code"
  • fn = "function"
  • exp/not_exp = exported / not exported

All parameters are explained as tooltips in the locally-rendered HTML version of this report generated by the checks_to_markdown() function

The final measure (fn_call_network_size) is the total number of calls between functions (in R), or more abstract relationships between code objects in other languages. Values are flagged as "noteworthy" when they lie in the upper or lower 5th percentile.

measure value percentile noteworthy
files_R 9 52.3
files_inst 2 99.0
files_vignettes 1 61.9
files_tests 7 81.9
loc_R 238 26.0
loc_inst 30 35.4
loc_vignettes 128 31.6
loc_tests 130 42.6
num_vignettes 1 58.9
n_fns_r 53 56.9
n_fns_r_exported 14 56.1
n_fns_r_not_exported 39 58.6
n_fns_per_file_r 3 50.0
num_params_per_fn 2 8.2
loc_per_fn_r 7 16.7
loc_per_fn_r_exp 8 16.7
loc_per_fn_r_not_exp 6 14.8
rel_whitespace_R 24 36.3
rel_whitespace_inst 97 51.6
rel_whitespace_vignettes 49 43.9
rel_whitespace_tests 40 55.7
doclines_per_fn_exp 20 14.5
doclines_per_fn_not_exp 0 0.0 TRUE
fn_call_network_size 27 51.6

2a. Network visualisation

Click to see the interactive network visualisation of calls between objects in package


3. goodpractice and other checks

Details of goodpractice checks (click to open)

3a. Continuous Integration Badges

R-CMD-check.yaml

GitHub Workflow Results

id name conclusion sha run_number date
11900852065 pages build and deployment success fbd7ec 29 2024-11-18
11900828295 pkgdown.yaml success 8353d8 29 2024-11-18
11900828308 R-CMD-check.yaml success 8353d8 12 2024-11-18
11900828293 test-coverage.yaml success 8353d8 12 2024-11-18

3b. goodpractice results

R CMD check with rcmdcheck

R CMD check generated the following error:

  1. checking tests ...
    Running ‘testthat.R’
    ERROR
    Running the tests in ‘tests/testthat.R’ failed.
    Last 13 lines of output:
    5. └─reticulate (local) python_not_found("Python shared library not found, Python bindings not loaded.")
    ── Error ('test-run.R:18:3'): Run SAS code from file ───────────────────────────
    Error: Python shared library not found, Python bindings not loaded.
    Please create a default virtual environment with reticulate::virtualenv_create('r-reticulate').
    Backtrace:
    1. └─sasquatch::sas_connect() at test-run.R:18:3
    2. └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
    3. └─reticulate:::ensure_python_initialized()
      
    4.   └─reticulate:::initialize_python()
      
    5.     └─reticulate (local) python_not_found("Python shared library not found, Python bindings not loaded.")
      

[ FAIL 12 | WARN 0 | SKIP 0 | PASS 14 ]
Error: Test failures
Execution halted

R CMD check generated the following test_fail:

  1. This file is part of the standard setup for testthat.

It is recommended that you do not modify it.

Where should you do additional test configuration?

Learn more about the roles of various files in:

* https://r-pkgs.org/testing-design.html#sec-tests-files-overview

* https://testthat.r-lib.org/articles/special-files.html

library(testthat)
library(sasquatch)

test_check("sasquatch")
[ FAIL 12 | WARN 0 | SKIP 0 | PASS 14 ]

══ Failed tests ════════════════════════════════════════════════════════════════
── Failure ('test-connection.R:2:3'): test connection ──────────────────────────
Expected sas_connect() to run without any errors.
i Actually got a with text:
Python shared library not found, Python bindings not loaded.
Please create a default virtual environment with reticulate::virtualenv_create('r-reticulate').
── Failure ('test-connection.R:4:3'): test connection ──────────────────────────
Expected sas_connect("oda") to run without any errors.
i Actually got a with text:
Python shared library not found, Python bindings not loaded.
Please create a default virtual environment with reticulate::virtualenv_create('r-reticulate').
── Failure ('test-connection.R:6:3'): test connection ──────────────────────────
Expected sas_get_session() to run without any errors.
i Actually got a with text:
No current SAS session. Use sas_connect() to start one.
── Failure ('test-connection.R:8:3'): test connection ──────────────────────────
Expected sas_disconnect() to run without any errors.
i Actually got a with text:
No current SAS session. Use sas_connect() to start one.
── Error ('test-conversion.R:2:3'): SAS to R data.frame ────────────────────────
Error: Python shared library not found, Python bindings not loaded.
Please create a default virtual environment with reticulate::virtualenv_create('r-reticulate').
Backtrace:

  1. └─sasquatch::sas_connect() at test-conversion.R:2:3
  2. └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. └─reticulate:::ensure_python_initialized()
    
  4.   └─reticulate:::initialize_python()
    
  5.     └─reticulate (local) python_not_found("Python shared library not found, Python bindings not loaded.")
    

── Error ('test-conversion.R:24:3'): R to SAS data.frame ───────────────────────
Error: Python shared library not found, Python bindings not loaded.
Please create a default virtual environment with reticulate::virtualenv_create('r-reticulate').
Backtrace:

  1. └─sasquatch::sas_connect() at test-conversion.R:24:3
  2. └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. └─reticulate:::ensure_python_initialized()
    
  4.   └─reticulate:::initialize_python()
    
  5.     └─reticulate (local) python_not_found("Python shared library not found, Python bindings not loaded.")
    

── Error ('test-io.R:12:3'): uploading files to SAS ────────────────────────────
Error: Python shared library not found, Python bindings not loaded.
Please create a default virtual environment with reticulate::virtualenv_create('r-reticulate').
Backtrace:

  1. └─sasquatch::sas_connect() at test-io.R:12:3
  2. └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. └─reticulate:::ensure_python_initialized()
    
  4.   └─reticulate:::initialize_python()
    
  5.     └─reticulate (local) python_not_found("Python shared library not found, Python bindings not loaded.")
    

── Error ('test-io.R:34:3'): download files from SAS ───────────────────────────
Error: Python shared library not found, Python bindings not loaded.
Please create a default virtual environment with reticulate::virtualenv_create('r-reticulate').
Backtrace:

  1. └─sasquatch::sas_connect() at test-io.R:34:3
  2. └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. └─reticulate:::ensure_python_initialized()
    
  4.   └─reticulate:::initialize_python()
    
  5.     └─reticulate (local) python_not_found("Python shared library not found, Python bindings not loaded.")
    

── Error ('test-io.R:61:3'): removing files from SAS ───────────────────────────
Error: Python shared library not found, Python bindings not loaded.
Please create a default virtual environment with reticulate::virtualenv_create('r-reticulate').
Backtrace:

  1. └─sasquatch::sas_connect() at test-io.R:61:3
  2. └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. └─reticulate:::ensure_python_initialized()
    
  4.   └─reticulate:::initialize_python()
    
  5.     └─reticulate (local) python_not_found("Python shared library not found, Python bindings not loaded.")
    

── Error ('test-io.R:85:3'): listing files ─────────────────────────────────────
Error: Python shared library not found, Python bindings not loaded.
Please create a default virtual environment with reticulate::virtualenv_create('r-reticulate').
Backtrace:

  1. └─sasquatch::sas_connect() at test-io.R:85:3
  2. └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. └─reticulate:::ensure_python_initialized()
    
  4.   └─reticulate:::initialize_python()
    
  5.     └─reticulate (local) python_not_found("Python shared library not found, Python bindings not loaded.")
    

── Error ('test-run.R:4:3'): Run SAS code from string ──────────────────────────
Error: Python shared library not found, Python bindings not loaded.
Please create a default virtual environment with reticulate::virtualenv_create('r-reticulate').
Backtrace:

  1. └─sasquatch::sas_connect() at test-run.R:4:3
  2. └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. └─reticulate:::ensure_python_initialized()
    
  4.   └─reticulate:::initialize_python()
    
  5.     └─reticulate (local) python_not_found("Python shared library not found, Python bindings not loaded.")
    

── Error ('test-run.R:18:3'): Run SAS code from file ───────────────────────────
Error: Python shared library not found, Python bindings not loaded.
Please create a default virtual environment with reticulate::virtualenv_create('r-reticulate').
Backtrace:

  1. └─sasquatch::sas_connect() at test-run.R:18:3
  2. └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. └─reticulate:::ensure_python_initialized()
    
  4.   └─reticulate:::initialize_python()
    
  5.     └─reticulate (local) python_not_found("Python shared library not found, Python bindings not loaded.")
    

[ FAIL 12 | WARN 0 | SKIP 0 | PASS 14 ]
Error: Test failures
Execution halted

R CMD check generated the following check_fail:

  1. rcmdcheck_tests_pass

Test coverage with covr

ERROR: Test Coverage Failed

Cyclocomplexity with cyclocomp

No functions have cyclocomplexity >= 15

Static code analyses with lintr

lintr found no issues with this package!


Package Versions

package version
pkgstats 0.2.0.48
pkgcheck 0.1.2.68


Editor-in-Chief Instructions:

Processing may not proceed until the items marked with ✖️ have been resolved.

mpadge added a commit to ropensci-review-tools/pkgcheck that referenced this issue Nov 19, 2024
@emilyriederer
Copy link

Hi @ryanzomorrodi ! Thanks for your submission.

I see your README.md draws contrast with sasR and configSAS. Could you please also comment how this compares to the "native" sas language engine for R Markdown? Is it largely the interactive capabilities you mention? Or the ability to use SODA versus an on-computer executable?

(Transparently, I'm not a SAS user, so I lack some background context on this space.)

@ryanzomorrodi
Copy link
Author

ryanzomorrodi commented Nov 23, 2024

Hi @emilyriederer ,

I have never used the native sas engine. I saw the mention of it within the RMarkdown cookbook before, but frankly I can't find any other mention of someone using it in the wild. Unfortunately, I don't have access to a computer with a SAS executable installed to test it out.

One package I forgot to mention is SASmarkdown. It also relies on a local SAS executable so I haven't been able to test it out. But, it doesn't have any interactive features, there are also separate engines for output and logs (meaning you have to run your code twice to see the log), and SAS cannot interact with R at all (you need to write everything to a file and read it in the other language). This seems to be what people are currently using instead of the native engine.

One of the advantages in relying on the SASPy Python package is SASPy can connect to both local and remote SAS clients. The configuration is a bit tedious, but at least for SODA, configure_saspy() can do it for you. I was hoping that if others use it, they could share how they set up their configurations and then additional templates can be added.

@emilyriederer
Copy link

Hi @ryanzomorrodi - Thank you for your replied and your patience! I'm seeking out some editors with SAS expertise to help the process here, but will aim to get back to you soon.

@ryanzomorrodi
Copy link
Author

Sounds great! Thank you

@emilyriederer
Copy link

Hi @ryanzomorrodi - I'm happy to share that we think sasquatch is in scope. Given the necessary external dependencies like SAS it may take a bit longer to get our checks run and to identify the right editor, but more to come ASAP.

(Also, what an absolutely perfect name...)

@emilyriederer
Copy link

@ropensci-review-bot check package

@ropensci-review-bot
Copy link
Collaborator

Thanks, about to send the query.

@ropensci-review-bot
Copy link
Collaborator

🚀

Editor check started

👋

@ropensci-review-bot
Copy link
Collaborator

Checks for sasquatch (v0.0.0.9014)

git hash: b506da05

  • ✔️ Package name is available
  • ✔️ has a 'codemeta.json' file.
  • ✔️ has a 'contributing' file.
  • ✔️ uses 'roxygen2'.
  • ✔️ 'DESCRIPTION' has a URL field.
  • ✔️ 'DESCRIPTION' has a BugReports field.
  • ✔️ Package has at least one HTML vignette
  • ✔️ All functions have examples.
  • ✔️ Package has continuous integration checks.
  • ✖️ Package coverage failed
  • ✖️ R CMD check found 1 error.
  • ✔️ R CMD check found no warnings.

Important: All failing checks above must be addressed prior to proceeding

Package License: MIT + file LICENSE


1. Package Dependencies

Details of Package Dependency Usage (click to open)

The table below tallies all function calls to all packages ('ncalls'), both internal (r-base + recommended, along with the package itself), and external (imported and suggested packages). 'NA' values indicate packages to which no identified calls to R functions could be found. Note that these results are generated by an automated code-tagging system which may not be entirely accurate.

type package ncalls
internal base 32
internal sasquatch 32
internal graphics 2
imports reticulate 3
imports knitr 2
imports htmlwidgets 1
imports rstudioapi 1
suggests rmarkdown NA
suggests testthat NA
suggests curl NA
linking_to NA NA

Click below for tallies of functions used in each package. Locations of each call within this package may be generated locally by running 's <- pkgstats::pkgstats(<path/to/repo>)', and examining the 'external_calls' table.

base

list (6), paste0 (5), paste (4), readline (3), log (2), options (2), c (1), length (1), readLines (1), sapply (1), seq_along (1), sub (1), Sys.getenv (1), Sys.which (1), system.file (1), unique (1)

sasquatch

menu (4), get_home_dir (2), list_to_config_dict (2), read_file (2), rstudio_context (2), sas_engine (2), check_connection (1), configure_saspy (1), document (1), install_saspy (1), newline (1), r_to_sas (1), rstudio_selection (1), rstudio_text_tidy (1), sas_connect (1), sas_disconnect (1), sas_download (1), sas_get_session (1), sas_list (1), sas_remove (1), sas_run_file (1), sas_run_selected (1), wrap_in_iframe (1), wrap_in_pre (1)

reticulate

import (1), py_discover_config (1), virtualenv_remove (1)

graphics

text (1), title (1)

knitr

knit_engines$set (1), knit_hooks$set (1)

htmlwidgets

sizingPolicy (1)

rstudioapi

primary_selection (1)


2. Statistical Properties

This package features some noteworthy statistical properties which may need to be clarified by a handling editor prior to progressing.

Details of statistical properties (click to open)

The package has:

  • code in JavaScript (4% in 1 files), R (96% in 10 files) and XML (0% in 1 files)
  • 1 authors
  • 1 vignette
  • no internal data file
  • 4 imported packages
  • 15 exported functions (median 8 lines of code)
  • 47 non-exported functions in R (median 8 lines of code)

Statistical properties of package structure as distributional percentiles in relation to all current CRAN packages
The following terminology is used:

  • loc = "Lines of Code"
  • fn = "function"
  • exp/not_exp = exported / not exported

All parameters are explained as tooltips in the locally-rendered HTML version of this report generated by the checks_to_markdown() function

The final measure (fn_call_network_size) is the total number of calls between functions (in R), or more abstract relationships between code objects in other languages. Values are flagged as "noteworthy" when they lie in the upper or lower 5th percentile.

measure value percentile noteworthy
files_R 10 56.4
files_inst 3 98.9
files_vignettes 0 0.0 TRUE
files_tests 6 79.4
loc_R 371 37.0
loc_inst 47 36.4
loc_tests 133 43.1
num_vignettes 1 58.8
n_fns_r 62 61.9
n_fns_r_exported 15 58.4
n_fns_r_not_exported 47 64.1
n_fns_per_file_r 3 54.3
num_params_per_fn 2 7.0
loc_per_fn_r 8 20.5
loc_per_fn_r_exp 8 16.9
loc_per_fn_r_not_exp 8 22.9
rel_whitespace_R 20 42.3
rel_whitespace_inst 70 53.2
rel_whitespace_tests 45 58.6
doclines_per_fn_exp 19 12.9
doclines_per_fn_not_exp 0 0.0 TRUE
fn_call_network_size 33 56.1

2a. Network visualisation

Click to see the interactive network visualisation of calls between objects in package


3. goodpractice and other checks

Details of goodpractice checks (click to open)

3a. Continuous Integration Badges

R-CMD-check.yaml

GitHub Workflow Results

id name conclusion sha run_number date
11990402536 pages build and deployment success 5afcec 43 2024-11-23
11990389814 pkgdown.yaml success b506da 45 2024-11-23
11990389816 R-CMD-check.yaml failure b506da 28 2024-11-23
11990150794 test-coverage.yaml failure d5bab8 26 2024-11-23

3b. goodpractice results

R CMD check with rcmdcheck

R CMD check generated the following error:

  1. checking tests ...
    Running ‘testthat.R’
    ERROR
    Running the tests in ‘tests/testthat.R’ failed.
    Last 13 lines of output:
    Backtrace:
    1. ├─sasquatch::sas_connect() at test-run.R:22:3
    2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
    3. │ │ ├─base::tryCatch(...)
    4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
    5. │ │ └─base::force(expr)
    6. │ ├─.pkgenv$SASPy$SASsession
    7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
    8. │ └─reticulate::py_get_attr(x, name, TRUE)
    9. └─reticulate (local) <fn>(<python.builtin.module>)

[ FAIL 12 | WARN 0 | SKIP 0 | PASS 13 ]
Error: Test failures
Execution halted

R CMD check generated the following test_fail:

  1. This file is part of the standard setup for testthat.

It is recommended that you do not modify it.

Where should you do additional test configuration?

Learn more about the roles of various files in:

* https://r-pkgs.org/testing-design.html#sec-tests-files-overview

* https://testthat.r-lib.org/articles/special-files.html

library(testthat)
library(sasquatch)

test_check("sasquatch")
List of 22
$ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
$ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
$ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
$ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
$ prefix : chr "/root/.virtualenvs/r-reticulate"
$ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
$ base_exec_prefix : chr "/usr"
$ virtualenv : chr "/root/.virtualenvs/r-reticulate"
$ virtualenv_activate : chr ""
$ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
$ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
$ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
$ version :Classes 'package_version', 'numeric_version' hidden list of 1
..$ : int [1:2] 3 8
$ architecture : chr "64bit"
$ anaconda : logi FALSE
$ conda : chr "False"
$ numpy :List of 2
..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
..$ version:Class 'numeric_version' hidden list of 1
.. ..$ : int [1:3] 1 24 4
$ required_module : chr "saspy"
$ required_module_path: NULL
$ available : logi TRUE
$ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
$ forced : NULL

  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    [ FAIL 12 | WARN 0 | SKIP 0 | PASS 13 ]

══ Failed tests ════════════════════════════════════════════════════════════════
── Failure ('test-connection.R:4:3'): test connection ──────────────────────────
Expected sas_connect() to run without any errors.
i Actually got a with text:
Python module saspy was not found.

Detected Python configuration:

── Failure ('test-connection.R:6:3'): test connection ──────────────────────────
Expected sas_connect("oda") to run without any errors.
i Actually got a with text:
Python module saspy was not found.

Detected Python configuration:

── Failure ('test-connection.R:8:3'): test connection ──────────────────────────
Expected sas_get_session() to run without any errors.
i Actually got a with text:
No current SAS session. Use sas_connect() to start one.
── Failure ('test-connection.R:10:3'): test connection ─────────────────────────
Expected sas_disconnect() to run without any errors.
i Actually got a with text:
No current SAS session. Use sas_connect() to start one.
── Error ('test-conversion.R:4:3'): SAS to R data.frame ────────────────────────
Error: Python module saspy was not found.

Detected Python configuration:

Backtrace:

  1. ├─sasquatch::sas_connect() at test-conversion.R:4:3
  2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. │ │ ├─base::tryCatch(...)
  4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
  5. │ │ └─base::force(expr)
  6. │ ├─.pkgenv$SASPy$SASsession
  7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
  8. │ └─reticulate::py_get_attr(x, name, TRUE)
  9. └─reticulate (local) <fn>(<python.builtin.module>)
    ── Error ('test-conversion.R:28:3'): R to SAS data.frame ───────────────────────
    Error: Python module saspy was not found.

Detected Python configuration:

Backtrace:

  1. ├─sasquatch::sas_connect() at test-conversion.R:28:3
  2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. │ │ ├─base::tryCatch(...)
  4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
  5. │ │ └─base::force(expr)
  6. │ ├─.pkgenv$SASPy$SASsession
  7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
  8. │ └─reticulate::py_get_attr(x, name, TRUE)
  9. └─reticulate (local) <fn>(<python.builtin.module>)
    ── Error ('test-io.R:14:3'): uploading files to SAS ────────────────────────────
    Error: Python module saspy was not found.

Detected Python configuration:

Backtrace:

  1. ├─sasquatch::sas_connect() at test-io.R:14:3
  2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. │ │ ├─base::tryCatch(...)
  4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
  5. │ │ └─base::force(expr)
  6. │ ├─.pkgenv$SASPy$SASsession
  7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
  8. │ └─reticulate::py_get_attr(x, name, TRUE)
  9. └─reticulate (local) <fn>(<python.builtin.module>)
    ── Error ('test-io.R:38:3'): download files from SAS ───────────────────────────
    Error: Python module saspy was not found.

Detected Python configuration:

Backtrace:

  1. ├─sasquatch::sas_connect() at test-io.R:38:3
  2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. │ │ ├─base::tryCatch(...)
  4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
  5. │ │ └─base::force(expr)
  6. │ ├─.pkgenv$SASPy$SASsession
  7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
  8. │ └─reticulate::py_get_attr(x, name, TRUE)
  9. └─reticulate (local) <fn>(<python.builtin.module>)
    ── Error ('test-io.R:67:3'): removing files from SAS ───────────────────────────
    Error: Python module saspy was not found.

Detected Python configuration:

Backtrace:

  1. ├─sasquatch::sas_connect() at test-io.R:67:3
  2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. │ │ ├─base::tryCatch(...)
  4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
  5. │ │ └─base::force(expr)
  6. │ ├─.pkgenv$SASPy$SASsession
  7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
  8. │ └─reticulate::py_get_attr(x, name, TRUE)
  9. └─reticulate (local) <fn>(<python.builtin.module>)
    ── Error ('test-io.R:93:3'): listing files ─────────────────────────────────────
    Error: Python module saspy was not found.

Detected Python configuration:

Backtrace:

  1. ├─sasquatch::sas_connect() at test-io.R:93:3
  2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. │ │ ├─base::tryCatch(...)
  4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
  5. │ │ └─base::force(expr)
  6. │ ├─.pkgenv$SASPy$SASsession
  7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
  8. │ └─reticulate::py_get_attr(x, name, TRUE)
  9. └─reticulate (local) <fn>(<python.builtin.module>)
    ── Error ('test-run.R:6:3'): Run SAS code from string ──────────────────────────
    Error: Python module saspy was not found.

Detected Python configuration:

Backtrace:

  1. ├─sasquatch::sas_connect() at test-run.R:6:3
  2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. │ │ ├─base::tryCatch(...)
  4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
  5. │ │ └─base::force(expr)
  6. │ ├─.pkgenv$SASPy$SASsession
  7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
  8. │ └─reticulate::py_get_attr(x, name, TRUE)
  9. └─reticulate (local) <fn>(<python.builtin.module>)
    ── Error ('test-run.R:22:3'): Run SAS code from file ───────────────────────────
    Error: Python module saspy was not found.

Detected Python configuration:

Backtrace:

  1. ├─sasquatch::sas_connect() at test-run.R:22:3
  2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. │ │ ├─base::tryCatch(...)
  4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
  5. │ │ └─base::force(expr)
  6. │ ├─.pkgenv$SASPy$SASsession
  7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
  8. │ └─reticulate::py_get_attr(x, name, TRUE)
  9. └─reticulate (local) <fn>(<python.builtin.module>)

[ FAIL 12 | WARN 0 | SKIP 0 | PASS 13 ]
Error: Test failures
Execution halted

R CMD check generated the following check_fail:

  1. rcmdcheck_tests_pass

Test coverage with covr

ERROR: Test Coverage Failed

Cyclocomplexity with cyclocomp

The following function have cyclocomplexity >= 15:

function cyclocomplexity
configure_saspy 16

Static code analyses with lintr

lintr found no issues with this package!


Package Versions

package version
pkgstats 0.2.0.48
pkgcheck 0.1.2.77


Editor-in-Chief Instructions:

Processing may not proceed until the items marked with ✖️ have been resolved.

@emilyriederer
Copy link

emilyriederer commented Dec 4, 2024

Incidentally, @ryanzomorrodi -- I see you mention not having a good idea for testing your knitr language engine. Way back in 2019, I PRed the SASS engine to knitr and wrote some test to include with my PR. I don't pretend that this is the best or only way, but I found by using mockr I could override the knitr:::is_html_output function to trick it into producing output I could compare in tests. I've attached in case its at all useful to you.

Other ideas (that I haven't tried) might include snapshot testing.

Again, maybe not the best way, maybe not necessary (the handling editor and reviewer can decide that) but just a few passing thoughts. I also recognize that knitr engines I think are a bit more binary in how and whether they work so may not benefit from testing as much as parts of the code with many corner cases.

test_eng_sxss.txt

@ryanzomorrodi
Copy link
Author

That seems like a very reasonable approach, I will try it out. Thank you

@maurolepore
Copy link
Member

@ropensci-review-bot assign @maurolepore as editor

@ropensci-review-bot
Copy link
Collaborator

Assigned! @maurolepore is now the editor

@maurolepore
Copy link
Member

maurolepore commented Dec 14, 2024

Dear @ryanzomorrodi, thanks so much for your submission. I'm happy to be the handling editor.

Within the next few days I'll be reading this thread in more detail, and going through editor checks.

  • ml01. In the meantime, could you please suggest three reviewers?

It might help you to see the criteria we use to choose reviewers, particularly how we define a conflict of interest.

It's also a good time to do any changes you might have in mind. Once the reviews start it's best to avoid forcing reviewers to work against a moving target.

Very excited your tool as a way to bridge SAS and R 💯 🥇

@ryanzomorrodi
Copy link
Author

Hi @maurolepore

That's great to hear! I reviewed the criteria, but I'm wondering if there is a set list of reviewers to choose from or should I post something on social media. It looks like the book mentions an Airtable directory, but it feels like that is just for editors.

My primary interest is to select reviewers with some level of SAS experience.

@maurolepore
Copy link
Member

@ryanzomorrodi, that's right, that airtable database is restricted to editors.

If nobody from your own network comes to mind, then no need to stretch to social media.

We ask for suggestions only to ensure we understand the kind of skills the author believes to be useful, so thanks for highlighting the usefulness of SAS experience 🙏👍

@ryanzomorrodi
Copy link
Author

Sounds good! I apologize, I'm a relative newcomer to the R open-source world, so I'm not super familiar with the space

@maurolepore
Copy link
Member

No problem at all. One great reason to be here is precisely to expand your networks :-)

@maurolepore
Copy link
Member

@ropensci-review-bot check package

@ropensci-review-bot
Copy link
Collaborator

Thanks, about to send the query.

@ropensci-review-bot
Copy link
Collaborator

🚀

Editor check started

👋

@ropensci-review-bot
Copy link
Collaborator

Checks for sasquatch (v0.0.0.9017)

git hash: aee061da

  • ✔️ Package name is available
  • ✔️ has a 'codemeta.json' file.
  • ✔️ has a 'contributing' file.
  • ✔️ uses 'roxygen2'.
  • ✔️ 'DESCRIPTION' has a URL field.
  • ✔️ 'DESCRIPTION' has a BugReports field.
  • ✔️ Package has at least one HTML vignette
  • ✔️ All functions have examples.
  • ✔️ Package has continuous integration checks.
  • ✖️ Package coverage failed
  • ✖️ R CMD check found 1 error.
  • ✖️ R CMD check found 1 warning.

Important: All failing checks above must be addressed prior to proceeding

Package License: MIT + file LICENSE


1. Package Dependencies

Details of Package Dependency Usage (click to open)

The table below tallies all function calls to all packages ('ncalls'), both internal (r-base + recommended, along with the package itself), and external (imported and suggested packages). 'NA' values indicate packages to which no identified calls to R functions could be found. Note that these results are generated by an automated code-tagging system which may not be entirely accurate.

type package ncalls
internal sasquatch 37
internal base 33
internal graphics 2
imports reticulate 3
imports knitr 2
imports chk 1
imports htmlwidgets 1
imports rstudioapi 1
suggests rmarkdown NA
suggests testthat NA
suggests curl NA
linking_to NA NA

Click below for tallies of functions used in each package. Locations of each call within this package may be generated locally by running 's <- pkgstats::pkgstats(<path/to/repo>)', and examining the 'external_calls' table.

sasquatch

menu (4), get_home_dir (2), list_to_config_dict (2), read_file (2), rstudio_context (2), sas_engine (2), wrap_in_iframe (2), chk_connection (1), chk_has_rownames (1), configure_saspy (1), document (1), execute_safely (1), install_saspy (1), newline (1), r_to_sas (1), rstudio_selection (1), rstudio_text_tidy (1), sas_connect (1), sas_disconnect (1), sas_download (1), sas_get_session (1), sas_list (1), sas_remove (1), sas_run_file (1), sas_run_selected (1), sas_run_string (1), sas_to_r (1), wrap_in_pre (1)

base

list (5), paste0 (5), paste (4), readline (3), log (2), options (2), c (1), call (1), length (1), parent.frame (1), readLines (1), sapply (1), seq_along (1), sub (1), Sys.getenv (1), Sys.which (1), system.file (1), unique (1)

reticulate

import (1), py_discover_config (1), virtualenv_remove (1)

graphics

text (1), title (1)

knitr

knit_engines$set (1), knit_hooks$set (1)

chk

abort_chk (1)

htmlwidgets

sizingPolicy (1)

rstudioapi

primary_selection (1)


2. Statistical Properties

This package features some noteworthy statistical properties which may need to be clarified by a handling editor prior to progressing.

Details of statistical properties (click to open)

The package has:

  • code in JavaScript (4% in 1 files), R (96% in 11 files) and XML (0% in 1 files)
  • 1 authors
  • 1 vignette
  • no internal data file
  • 5 imported packages
  • 15 exported functions (median 10 lines of code)
  • 55 non-exported functions in R (median 8 lines of code)

Statistical properties of package structure as distributional percentiles in relation to all current CRAN packages
The following terminology is used:

  • loc = "Lines of Code"
  • fn = "function"
  • exp/not_exp = exported / not exported

All parameters are explained as tooltips in the locally-rendered HTML version of this report generated by the checks_to_markdown() function

The final measure (fn_call_network_size) is the total number of calls between functions (in R), or more abstract relationships between code objects in other languages. Values are flagged as "noteworthy" when they lie in the upper or lower 5th percentile.

measure value percentile noteworthy
files_R 11 60.2
files_inst 3 98.8
files_vignettes 0 0.0 TRUE
files_tests 6 79.4
loc_R 449 42.4
loc_inst 47 34.9
loc_tests 133 43.0
num_vignettes 1 58.8
n_fns_r 70 65.3
n_fns_r_exported 15 58.4
n_fns_r_not_exported 55 68.5
n_fns_per_file_r 3 55.2
num_params_per_fn 2 7.1
loc_per_fn_r 8 24.5
loc_per_fn_r_exp 10 22.9
loc_per_fn_r_not_exp 8 23.0
rel_whitespace_R 18 44.4
rel_whitespace_inst 70 52.2
rel_whitespace_tests 45 58.5
doclines_per_fn_exp 19 12.9
doclines_per_fn_not_exp 0 0.0 TRUE
fn_call_network_size 44 62.5

2a. Network visualisation

Click to see the interactive network visualisation of calls between objects in package


3. goodpractice and other checks

Details of goodpractice checks (click to open)

3a. Continuous Integration Badges

R-CMD-check.yaml

GitHub Workflow Results

id name conclusion sha run_number date
12408303336 pages build and deployment success dd809c 45 2024-12-19
12408283861 pkgdown.yaml success aee061 47 2024-12-19
12408283864 R-CMD-check.yaml failure aee061 30 2024-12-19
11990150794 test-coverage.yaml failure d5bab8 26 2024-11-23

3b. goodpractice results

R CMD check with rcmdcheck

R CMD check generated the following error:

  1. checking tests ...
    Running ‘testthat.R’
    ERROR
    Running the tests in ‘tests/testthat.R’ failed.
    Last 13 lines of output:
    Backtrace:
    1. ├─sasquatch::sas_connect() at test-run.R:22:3
    2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
    3. │ │ ├─base::tryCatch(...)
    4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
    5. │ │ └─base::force(expr)
    6. │ ├─.pkgenv$SASPy$SASsession
    7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
    8. │ └─reticulate::py_get_attr(x, name, TRUE)
    9. └─reticulate (local) <fn>(<python.builtin.module>)

[ FAIL 12 | WARN 0 | SKIP 0 | PASS 9 ]
Error: Test failures
Execution halted

R CMD check generated the following warning:

  1. checking for code/documentation mismatches ... WARNING
    Codoc mismatches from Rd file 'install_saspy.Rd':
    install_saspy
    Code: function(envname = "r-saspy", extra_packages, restart_session =
    TRUE)
    Docs: function(envname = "r-saspy", extra_packages = NULL,
    restart_session = TRUE)
    Mismatches in argument default values:
    Name: 'extra_packages' Code: Docs: NULL

R CMD check generated the following test_fail:

  1. This file is part of the standard setup for testthat.

It is recommended that you do not modify it.

Where should you do additional test configuration?

Learn more about the roles of various files in:

* https://r-pkgs.org/testing-design.html#sec-tests-files-overview

* https://testthat.r-lib.org/articles/special-files.html

library(testthat)
library(sasquatch)

test_check("sasquatch")
List of 22
$ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
$ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
$ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
$ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
$ prefix : chr "/root/.virtualenvs/r-reticulate"
$ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
$ base_exec_prefix : chr "/usr"
$ virtualenv : chr "/root/.virtualenvs/r-reticulate"
$ virtualenv_activate : chr ""
$ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
$ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
$ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
$ version :Classes 'package_version', 'numeric_version' hidden list of 1
..$ : int [1:2] 3 8
$ architecture : chr "64bit"
$ anaconda : logi FALSE
$ conda : chr "False"
$ numpy :List of 2
..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
..$ version:Class 'numeric_version' hidden list of 1
.. ..$ : int [1:3] 1 24 4
$ required_module : chr "saspy"
$ required_module_path: NULL
$ available : logi TRUE
$ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
$ forced : NULL

  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    List of 22
    $ python : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ libpython : chr "/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so"
    $ pythonhome : chr "/root/.virtualenvs/r-reticulate:/root/.virtualenvs/r-reticulate"
    $ pythonpath : chr "/usr/lib/python38.zip:/usr/lib/python3.8:/usr/lib/python3.8/lib-dynload:/root/.virtualenvs/r-reticulate/lib/pyt"| truncated
    $ prefix : chr "/root/.virtualenvs/r-reticulate"
    $ exec_prefix : chr "/root/.virtualenvs/r-reticulate"
    $ base_exec_prefix : chr "/usr"
    $ virtualenv : chr "/root/.virtualenvs/r-reticulate"
    $ virtualenv_activate : chr ""
    $ executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ base_executable : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ version_string : chr "3.8.10 (default, Nov 7 2024, 13:10:47) [GCC 9.4.0]"
    $ version :Classes 'package_version', 'numeric_version' hidden list of 1
    ..$ : int [1:2] 3 8
    $ architecture : chr "64bit"
    $ anaconda : logi FALSE
    $ conda : chr "False"
    $ numpy :List of 2
    ..$ path : chr "/root/.virtualenvs/r-reticulate/lib/python3.8/site-packages/numpy"
    ..$ version:Class 'numeric_version' hidden list of 1
    .. ..$ : int [1:3] 1 24 4
    $ required_module : chr "saspy"
    $ required_module_path: NULL
    $ available : logi TRUE
    $ python_versions : chr "/root/.virtualenvs/r-reticulate/bin/python"
    $ forced : NULL
  • attr(*, "class")= chr "py_config"
    [ FAIL 12 | WARN 0 | SKIP 0 | PASS 9 ]

══ Failed tests ════════════════════════════════════════════════════════════════
── Failure ('test-connection.R:4:3'): test connection ──────────────────────────
Expected sas_connect() to run without any errors.
i Actually got a with text:
Python module saspy was not found.

Detected Python configuration:

── Failure ('test-connection.R:6:3'): test connection ──────────────────────────
Expected sas_connect("oda") to run without any errors.
i Actually got a with text:
Python module saspy was not found.

Detected Python configuration:

── Failure ('test-connection.R:10:3'): test connection ─────────────────────────
Expected sas_disconnect() to run without any errors.
i Actually got a <chk_error> with text:
No active SAS session. Use sas_connect() to start one.
── Failure ('test-connection.R:14:3'): test connection ─────────────────────────
sas_get_session() did not throw the expected error.
── Error ('test-conversion.R:4:3'): SAS to R data.frame ────────────────────────
Error: Python module saspy was not found.

Detected Python configuration:

Backtrace:

  1. ├─sasquatch::sas_connect() at test-conversion.R:4:3
  2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. │ │ ├─base::tryCatch(...)
  4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
  5. │ │ └─base::force(expr)
  6. │ ├─.pkgenv$SASPy$SASsession
  7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
  8. │ └─reticulate::py_get_attr(x, name, TRUE)
  9. └─reticulate (local) <fn>(<python.builtin.module>)
    ── Error ('test-conversion.R:28:3'): R to SAS data.frame ───────────────────────
    Error: Python module saspy was not found.

Detected Python configuration:

Backtrace:

  1. ├─sasquatch::sas_connect() at test-conversion.R:28:3
  2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. │ │ ├─base::tryCatch(...)
  4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
  5. │ │ └─base::force(expr)
  6. │ ├─.pkgenv$SASPy$SASsession
  7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
  8. │ └─reticulate::py_get_attr(x, name, TRUE)
  9. └─reticulate (local) <fn>(<python.builtin.module>)
    ── Error ('test-io.R:14:3'): uploading files to SAS ────────────────────────────
    Error: Python module saspy was not found.

Detected Python configuration:

Backtrace:

  1. ├─sasquatch::sas_connect() at test-io.R:14:3
  2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. │ │ ├─base::tryCatch(...)
  4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
  5. │ │ └─base::force(expr)
  6. │ ├─.pkgenv$SASPy$SASsession
  7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
  8. │ └─reticulate::py_get_attr(x, name, TRUE)
  9. └─reticulate (local) <fn>(<python.builtin.module>)
    ── Error ('test-io.R:38:3'): download files from SAS ───────────────────────────
    Error: Python module saspy was not found.

Detected Python configuration:

Backtrace:

  1. ├─sasquatch::sas_connect() at test-io.R:38:3
  2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. │ │ ├─base::tryCatch(...)
  4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
  5. │ │ └─base::force(expr)
  6. │ ├─.pkgenv$SASPy$SASsession
  7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
  8. │ └─reticulate::py_get_attr(x, name, TRUE)
  9. └─reticulate (local) <fn>(<python.builtin.module>)
    ── Error ('test-io.R:67:3'): removing files from SAS ───────────────────────────
    Error: Python module saspy was not found.

Detected Python configuration:

Backtrace:

  1. ├─sasquatch::sas_connect() at test-io.R:67:3
  2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. │ │ ├─base::tryCatch(...)
  4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
  5. │ │ └─base::force(expr)
  6. │ ├─.pkgenv$SASPy$SASsession
  7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
  8. │ └─reticulate::py_get_attr(x, name, TRUE)
  9. └─reticulate (local) <fn>(<python.builtin.module>)
    ── Error ('test-io.R:93:3'): listing files ─────────────────────────────────────
    Error: Python module saspy was not found.

Detected Python configuration:

Backtrace:

  1. ├─sasquatch::sas_connect() at test-io.R:93:3
  2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. │ │ ├─base::tryCatch(...)
  4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
  5. │ │ └─base::force(expr)
  6. │ ├─.pkgenv$SASPy$SASsession
  7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
  8. │ └─reticulate::py_get_attr(x, name, TRUE)
  9. └─reticulate (local) <fn>(<python.builtin.module>)
    ── Error ('test-run.R:6:3'): Run SAS code from string ──────────────────────────
    Error: Python module saspy was not found.

Detected Python configuration:

Backtrace:

  1. ├─sasquatch::sas_connect() at test-run.R:6:3
  2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. │ │ ├─base::tryCatch(...)
  4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
  5. │ │ └─base::force(expr)
  6. │ ├─.pkgenv$SASPy$SASsession
  7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
  8. │ └─reticulate::py_get_attr(x, name, TRUE)
  9. └─reticulate (local) <fn>(<python.builtin.module>)
    ── Error ('test-run.R:22:3'): Run SAS code from file ───────────────────────────
    Error: Python module saspy was not found.

Detected Python configuration:

Backtrace:

  1. ├─sasquatch::sas_connect() at test-run.R:22:3
  2. │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
  3. │ │ ├─base::tryCatch(...)
  4. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
  5. │ │ └─base::force(expr)
  6. │ ├─.pkgenv$SASPy$SASsession
  7. │ └─reticulate:::$.python.builtin.module(.pkgenv$SASPy, "SASsession")
  8. │ └─reticulate::py_get_attr(x, name, TRUE)
  9. └─reticulate (local) <fn>(<python.builtin.module>)

[ FAIL 12 | WARN 0 | SKIP 0 | PASS 9 ]
Error: Test failures
Execution halted

R CMD check generated the following check_fails:

  1. rcmdcheck_code_docs_mismatch
  2. rcmdcheck_tests_pass

Test coverage with covr

ERROR: Test Coverage Failed

Cyclocomplexity with cyclocomp

The following function have cyclocomplexity >= 15:

function cyclocomplexity
configure_saspy 16

Static code analyses with lintr

lintr found no issues with this package!


Package Versions

package version
pkgstats 0.2.0.48
pkgcheck 0.1.2.77


Editor-in-Chief Instructions:

Processing may not proceed until the items marked with ✖️ have been resolved.

@maurolepore
Copy link
Member

maurolepore commented Dec 23, 2024

@ryanzomorrodi thanks again for this submission. I had the change to dive deep into sasquatch and made comments that intend to help it go through review as smoothly as possible.

For that it's crucial that reviewers can explore the package as quickly as possible so that they can invest time in the more interesting aspects of sasquatch (rather than, say, debugging installation issues). Also it will help to make an effort to predict what reviewers will expect, based on what's common in the relevant community of practice. Here I can only contribute what relates to general R-package development, and you'll need to work on the more "SAS" related content.

I acknoledge that the comments are many and can feel intimidating. Please know we're all here to help and we're not in a rush. Take your time to process the comments, ask as many questions as you need, make your own research and judgements. With the holidays ahead I expect the process to experience some delays.

No problem if you already know you won't be able to work for a few months. In that case just let me know and we can tag the issue "on hold" so that in the meantime I can take other packages.


Editor checks:

  • Documentation: The package has sufficient documentation available online (README, pkgdown docs) to allow for an assessment of functionality and scope without installing the package. In particular,
    • Is the case for the package well made?
    • Is the reference index page clear (grouped by topic if necessary)?
    • Are vignettes readable, sufficiently detailed and not just perfunctory?
  • Fit: The package meets criteria for fit and overlap.
  • Installation instructions: Are installation instructions clear enough for human users?
  • Tests: If the package has some interactivity / HTTP / plot production etc. are the tests using state-of-the-art tooling?
  • Contributing information: Is the documentation for contribution clear enough e.g. tokens for tests, playgrounds?
  • License: The package has a CRAN or OSI accepted license.
  • Project management: Are the issue and PR trackers in a good shape, e.g. are there outstanding bugs, is it clear when feature requests are meant to be tackled?

Editor comments

To help you track my comments I'll tag them with "ml" and a numbered sequence,
e.g. ml01, ml02, and so on. Comments following bullets are for you to consider
-- you may or may not respond to them. Comments following check-boxes are
requests for some action -- please respond.

I already used ml01 above so the next tag will be ml02.

Documentation
Is the case for the package well made?
  • ml02. Consider strenghening the case for this package. This is particularly important considering the overlap with other packages (see below). The intent is to articulate why a specific group of people, in a specific situation would find
    sasquatch really useful. A useful tool might the a user story. Here's one example based on my relatively shallow understanding (adapt it as necessary):

As someone who uses R and Quarto but lacks a SAS executable I would like to
have sasquatch so I can try SAS for free in a familiar environment.

This narrower focus in not a problem for rOpenSc; Specific applications in
science is generally what we're looking for.

Is the reference index page clear (grouped by topic if necessary)?

Thanks! The index is clearly organized by topic, and with a little more effort
in each help file it will be a pleasure to read:

  • ml03. Please expand the documentation to include more details, particularly
    examples. I'll give you one feedback for only one function here but the same
    applies to most of them. Consider install_saspy():
  • Add a link to the SASPy package.
  • Update the website so that the signature in reflects the source code. The
    website shows exatra_packages = NULL but the source code shows
    extra_packages has no default.
  • Add the type of each argument, e.g.
#' @param extra_packages Character. A vector giving the name of one or more 
#'   packages to install,

If a function is called primarily for its side-effects, it should invisibly
return a useful output. If there’s no obvious output, return the first
argument. This makes it possible to use the function with in a pipeline.

  • Add examples using the arguments. When are they useful? Can you show
    some output? If not, a) show output in a vignette, or b) describe the usage
    of each argument in writing, and point to external documentation to learn more
    (e.g. SASpy).
Are vignettes readable, sufficiently detailed and not just perfunctory?
  • ml04. Please add a vignette to show a more complete usage of the package.
    This is particularly useful if the examples don't show output or don't cover
    all arguments. Consider a "Get started" vignette. You can create it with usethis::use_vignette("sasquatch") and pkgdown should automagically call add a "Get started" hiyper text to the navbar of your pkgdown website. Some of its content might come from what now lives in RADME, and it may point to the other "Setting up" vignette.
Fit: The package meets criteria for fit and overlap.
  • ml05. Please update the documentation to incorporate the points you brought up in this comment. The section Similar packages lacks a mention all the options you discusses in your comment, and maybe needs to go up -- closer to the goal of the package. A clear way to present this information might be similar to this:
  • If you want "a" and have "b", then you may use the native sas engine.
  • If you want "c" and have "d", then you may use the package 1.
  • If you want "e" and have "f", then you may use the package 2.
  • If you want "g" and have "h", then you may use sasquatch.

To do this well you'll need to research the perspective of someone with resources
you lack. Where can you find them?

Here are some ideas:

  1. Remember how you got involved in SAS. Where was that? Who was there?
  2. rOpenSci's discussion forum. SAS is popular
    in Pharma and rOpenSci has several folks who work in Pharma. Maybe you can serch
    on LinkedIn for people following rOpenSci who work in Pharma?
  3. The Pharmaverse: https://pharmaverse.org/. I recently posted in their
    blog and I noticed they are extreemely
    friendly and welcoming.
Installation instructions: Are installation instructions clear enough for human users?
  • ml06. Please refine the installation instructions after watching someone else
    walk through the current version. A real user experience can reveal some
    unexpected ways in which people interpret the same set of instructions. The
    intent is to use effectively the limited time reviewers have. Time they save in
    installation they can invest in more interesting aspects of the package.
  • ml07. Consider that different people with find easy or hard different things.
    Showing many options can be impractical and hard to maintain, but consider
    pointing to external resources for users of the three main platforms: Windows,
    MacOS and Linux. For me it was smooth to install the system requirements on
    Linux from the terminal, but I failed to install via sasquatch:
packageVersion("sasquatch"); sasquatch::install_saspy()
#> [1] '0.0.0.9017'
#> Error in sasquatch::install_saspy(): argument "extra_packages" is missing, with no default

Created on 2024-12-23 with reprex v2.1.1

Tests: If the package has some interactivity / HTTP / plot production etc. are the tests using state-of-the-art tooling?

I see good effort put into tests, and with some guidance I hope they will be an
excellent way to not only ensure sasquatch does what you expect but also they
will facilitate contributions, including the upcoming reviews.

  • ml11. Please ensure to not write files to the developer's system other
    than to the standard temporary directory, e.g. via tempdir(). This is not only
    good etiquette but also it makes cleanup easier. Sometimes a more advanced way
    to cleanup becomes necessary, and you may need on.exit() or friends from the
    withr package. See withr in action at
    https://testthat.r-lib.org/articles/test-fixtures.html.

  • ml12. Ensure unit tests run quickly (e.g. all tests in 1-5 seconds). This
    is critical to allow reviewers to explore and understand sasquatch in the short
    time they have. Exploration requires iteration -- lots of it. Also, the faster
    your tests are, the more often you'll run them, the faster and more
    bugs you'll catch. I acknowledge for sasquatch this is hard because of the
    dependency on a remote server. You may need to see how other packages do it,
    and dive into advanced testing techniques. Some resources that come to mind are
    these:

  • ml13. Whenever possible please replace printed output with something that's
    easier to handle (e.g. message() can be suppressed with
    suppressedMessages()). The clutter in the test output will make it hard for
    reviewers to see what really matters. See
    https://ropensci.org/blog/2024/02/06/verbosity-control-packages/.

Example of the test output I see
==> devtools::test()

ℹ Testing sasquatch
✔ | F W  S  OK | Context
⠏ |          0 | connection   SAS Connection established.
⠋ |          1 | connection   SAS Connection established.
⠙ |          2 | connection   SAS Connection terminated.
✖ | 1        5 | connection [29.6s]
──────────────────────────────
Failure (test-connection.R:16:3): test connection
`sas_get_session()` did not throw the expected error.
──────────────────────────────
⠏ |          0 | conversion   SAS Connection established.
⠋ | 1        0 | conversion   SAS Connection established.
✖ | 2 1      0 | conversion [35.3s]
──────────────────────────────
Error (test-conversion.R:9:3): SAS to R data.frame
Error in `sas_to_r("iris", "sashelp")`: object 'x' not found
Backtrace:
    ▆
 1. └─sasquatch::sas_to_r("iris", "sashelp") at test-conversion.R:9:3
 2.   └─reticulate::py_to_r(x) at sasquatch/R/conversion.R:61:3
 3.     ├─reticulate::is_py_object(x <- py_to_r_cpp(x))
 4.     └─reticulate:::py_to_r_cpp(x)

Warning (test-conversion.R:36:3): R to SAS data.frame
`x` rownames will not be transferred as columns.
Backtrace:
    ▆
 1. └─sasquatch::r_to_sas(iris, "iris") at test-conversion.R:36:3
 2.   └─sasquatch:::chk_has_rownames(x) at sasquatch/R/conversion.R:22:3
 3.     └─chk::wrn(x_name, " rownames will not be transferred as columns") at sasquatch/R/additional_chk.R:14:3

Error (test-conversion.R:37:3): R to SAS data.frame
Error in `sas_to_r("iris")`: object 'x' not found
Backtrace:
    ▆
 1. └─sasquatch::sas_to_r("iris") at test-conversion.R:37:3
 2.   └─reticulate::py_to_r(x) at sasquatch/R/conversion.R:61:3
 3.     ├─reticulate::is_py_object(x <- py_to_r_cpp(x))
 4.     └─reticulate:::py_to_r_cpp(x)
──────────────────────────────
⠙ | 1        1 | io           SAS Connection established.
⠹ | 1        2 | io           SAS Connection terminated.
⠼ | 1        4 | io           SAS Connection established.
SAS Connection terminated.
⠴ | 1        5 | io           SAS Connection established.
⠧ | 1        7 | io           SAS Connection terminated.
⠇ | 1        8 | io           SAS Connection established.
SAS Connection terminated.
⠏ | 1        9 | io           SAS Connection established.
⠙ | 1       11 | io           SAS Connection terminated.
⠹ | 1       12 | io           SAS Connection established.
⠸ | 1       13 | io           SAS Connection terminated.
✖ | 1       13 | io [227.9s]
──────────────────────────────
Failure (test-io.R:17:3): uploading files to SAS
`sas_upload(local_path, sas_path)` did not throw the expected error.
──────────────────────────────
⠋ |          1 | run          SAS Connection established.
⠙ |          2 | run          SAS Connection terminated.
⠹ |          3 | run          SAS Connection established.
⠦ |          7 | run          SAS Connection terminated.
✔ |          7 | run [62.1s]

══ Results ═══════════════════
Duration: 355.0 s

── Failed tests ──────────────
Failure (test-connection.R:16:3): test connection
`sas_get_session()` did not throw the expected error.

Error (test-conversion.R:9:3): SAS to R data.frame
Error in `sas_to_r("iris", "sashelp")`: object 'x' not found
Backtrace:
    ▆
 1. └─sasquatch::sas_to_r("iris", "sashelp") at test-conversion.R:9:3
 2.   └─reticulate::py_to_r(x) at sasquatch/R/conversion.R:61:3
 3.     ├─reticulate::is_py_object(x <- py_to_r_cpp(x))
 4.     └─reticulate:::py_to_r_cpp(x)

Error (test-conversion.R:37:3): R to SAS data.frame
Error in `sas_to_r("iris")`: object 'x' not found
Backtrace:
    ▆
 1. └─sasquatch::sas_to_r("iris") at test-conversion.R:37:3
 2.   └─reticulate::py_to_r(x) at sasquatch/R/conversion.R:61:3
 3.     ├─reticulate::is_py_object(x <- py_to_r_cpp(x))
 4.     └─reticulate:::py_to_r_cpp(x)

Failure (test-io.R:17:3): uploading files to SAS
`sas_upload(local_path, sas_path)` did not throw the expected error.

[ FAIL 4 | WARN 1 | SKIP 0 | PASS 25 ]
Contributing information: Is the documentation for contribution clear enough e.g. tokens for tests, playgrounds?
Additional considerations
  • ml15. Please ensure all URLs are correct. You may use urlchecker::url_check().
urlchecker::url_check()
#> fetching [  0 / 15 ]fetching [  1 / 15 ]fetching [  2 / 15 ]fetching [  3 / 15 ]fetching [  4 / 15 ]fetching [  5 / 15 ]fetching [  6 / 15 ]fetching [  7 / 15 ]fetching [  8 / 15 ]fetching [  9 / 15 ]fetching [ 10 / 15 ]fetching [ 11 / 15 ]fetching [ 12 / 15 ]fetching [ 13 / 15 ]fetching [ 14 / 15 ]                       
#> ! Warning: DESCRIPTION:26:50 Moved
#> URL: https://ryanzomorrodi.github.io/sasquatch/, https://github.com/ryanzomorrodi/sasr
#>                                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#>                                                  https://github.com/ryanzomorrodi/sasquatch
#> ! Warning: DESCRIPTION:33:13 Moved
#> BugReports: https://github.com/ryanzomorrodi/sasr/issues
#>             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#>             https://github.com/ryanzomorrodi/sasquatch
#> ! Warning: README.md:8:17 Moved
#> [![R-CMD-check](https://github.com/ryanzomorrodi/sasr/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/ryanzomorrodi/sasr/actions/workflows/R-CMD-check.yaml)
#>                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#>                 https://github.com/ryanzomorrodi/sasquatch/actions/workflows/R-CMD-check.yaml
#> ! Warning: DESCRIPTION:33:13 Moved
#> BugReports: https://github.com/ryanzomorrodi/sasr/issues
#>             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#>             https://github.com/ryanzomorrodi/sasquatch/issues
#> Warning in readLines(file_path): incomplete final line found on
#> '/home/rstudio/git/sasquatch/vignettes/setting_up.rmd'
#> ✖ Error: vignettes/setting_up.rmd:17:46 404: Not Found
#> `sasquatch` works by utilizing the [`SASPy`](https://sassoftware.github.io/SASPy/) python package, similar to packages like [`sasr`](https://github.com/insightsengineering/sasr) or [configSAS](https://github.com/baselr/configSAS). This means everything we do to connect R and SAS, needs to go through `SASPy`.
#>                                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#> ✖ Error: vignettes/setting_up.rmd:21:120 404: Not Found
#> For more information about setting up non-ODA `SASPy` connections, check out the [`SASPy` configuration documentation](https://sassoftware.github.io/SASPy/configuration.html#).
#>                                                                                                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#> Warning in readLines(file_path): incomplete final line found on
#> '/home/rstudio/git/sasquatch/vignettes/setting_up.rmd'
#> ✖ Error: vignettes/setting_up.rmd:21:120 404: Not Found
#> For more information about setting up non-ODA `SASPy` connections, check out the [`SASPy` configuration documentation](https://sassoftware.github.io/SASPy/configuration.html#).
#>                                                                                                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#> Warning in readLines(file_path): incomplete final line found on
#> '/home/rstudio/git/sasquatch/vignettes/setting_up.rmd'
#> ✖ Error: vignettes/setting_up.rmd:52:88 403: Forbidden
#> Make sure Java is installed on your system. You can download Java from [their website](https://www.java.com/en/download/). Note the Java installation path.
#>                                                                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#> Warning in readLines(file_path): incomplete final line found on
#> '/home/rstudio/git/sasquatch/vignettes/setting_up.rmd'
#> ✖ Error: vignettes/setting_up.rmd:40:98 404: Not Found
#> Make sure Python is installed on your system. You can download from [Python Software Foundation](https://www.python.org/downloads'), or use `install_python()` from reticulate:
#>                                                                                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#> ! Warning: README.md:29:12 Moved
#> Academics](https://www.sas.com/en_us/software/on-demand-for-academics)
#>            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#>            https://www.sas.com/content/sascom/en_us/software/on-demand-for-academics/
  • ml16. Consider showing an example of the round trip from r_to_sas() and
    sas_to_r(). If the round trip is complete, the final result should be
    identical to the starting point.

  • ml17. Consider adding the sas_ prefix to the few functions that lack it, e.g.
    r_to_sas() may become sas_from_r().

  • ml18. Consider running a global search for RStudio and making sure wording won't
    be obsolete when Positron becomes more popular.

@ryanzomorrodi
Copy link
Author

Hi @maurolepore

Sorry for the delay in response. I have tried to address as many of the issues that I can. I will probably need some time to reach out to others to see if there is anyone within the pharmaverse or rOpenSci interested in telling me about their use cases.

ml02

I try to kind of get at the purpose with the following the text:

sasquatch allows you to combine the power of R, SAS, and quarto together to create reproducible multilingual reports. sasquatch can:

  • Run SAS code blocks interactively
  • Send data back and forth between SAS and R
  • Conduct basic file management on a SAS client
  • Render SAS output within quarto documents.

I could expand on it further, but I think the use case for thispackage is summarized within the first line. I see this as useful for someone looking to create multilingual reports where they can connect to nearly any SAS client, pass data back and forth, and have a single reproducable document contain all the code and output.

ml03

I added types to every function parameter, specified analagous functions for sas_file_* functions, and provided additional detail to functions that required additional info like sas_engine().

ml04

I created a Getting started vignette and included a brief breakdown of each function.

ml05

I did add additional info to similar packages section of the README.

I really don't see a reason to use configSAS or sasr over sasquatch because sasquatch encorporates all the same functionality plus more. SASmarkdown on the other hand is easier to set up and does a better job at rendering pdf output, so if you don't care about interactive SAS support or passing data back and forth, I would recommend that. So that's what I tried to articulate.

That being said I think getting outside perspectives is probably a good thing. Hopefully, I can find interested individuals or even get the views of my professors who use SAS.

ml06

This I have yet to do, but plan on doing.

ml07

This error was a very simple mistake in the function code. I have since implemented a lot more testing for many of the functions, so hopefully I caught most of the mistakes like this. I'm not sure if you have any recommendations as to how I can better test the install_saspy() and configure_saspy() functions. Because they require changing system files I think it would be very hard to test them and revert the global state.

I also use linux so maybe watching others on other platforms would help me get a better idea of potential pitfalls.

ml08

I may add this later. I chose pak for the purpose that it explictly lists out all system dependencies needed, which is definitely and added benefit.

ml09

Recording an installation video is definitely something I could do. I first want to see what different platform's installation is like, so I might delay this.

ml10

Done, initially I liked having relevant functions within the same file because they were close together, but even when separating them out they still organized themselves.

ml12

Right now all the tests run in about 2 minutes. I am missing tests for install_saspy() and configure_saspy(), but I think two minutes is reasonable. The main rate limiting step is sas_connect(), which takes a while to set up. Instead of connecting and disconnecting for each function to see if the connection check works within the function, I just set up tests for the connection check function and didn't bother with setting up checks for each individual function.

ml13

There were two uses of cat() which I replaced with message().

ml14

I added instructions that specifies that testing can be done with SAS ODA and that install_saspy() and configure_saspy(template = "oda") should be used to set it up.

I removed the setup file entirely.

ml15

I used urlchecker::url_check() and fixed the issues that popped up, but for some reason https://www.python.org/downloads/ kept giving a 404 within the checker. I left it in though, because I cannot reproduce it within the browser.

ml16

A SAS <-> R was added to the Getting Started vignette. There is no way to make the round trip perfect because of limitations with SAS data types, but I explained any differences.

ml17

Changed r_to_sas() to sas_from_r().

ml18

I use Positron, but I have made most of the docs with RStudio in mind because at least for now that is the more dominant among R users, but I tried to include references to Positron, where functionality diverged.

Thank you for all your thorough comments. This is all helpful insight!

@maurolepore
Copy link
Member

maurolepore commented Dec 31, 2024

@ryanzomorrodi thanks so much for your work!

Based on your comment I ticked lots of boxes. In the next few days I'll try installation, setup, and testing and I'll think about your question:

I'm not sure if you have any recommendations as to how I can better test the install_saspy() and configure_saspy() functions. Because they require changing system files I think it would be very hard to test them and revert the global state.

I'll come back early next year 😄

Happy hollidays!


BTW, re-reading my own comment above I found so many sloppy typos -- I apologize.

@maurolepore
Copy link
Member

maurolepore commented Jan 13, 2025

@ryanzomorrodi, thanks again for the work you put into enhancing the code and sharing it with rOpenSci. I looked at your answers and as I looked at the code I made a number of notes that you might find helpful. At this point most of them should be thought as optional suggestions.

Again, remember the intent is to ensure the reviewers can do their job as quickly as possible, so please try your best to maximize the chance they'll be able to install, configure and test the code without errors, and as quickly as possible. I share some ideas to make tests run faster.

It suggest you take a lil time to digest this feedback and let me know when you feel you're ready to engage reviewers. Then I'll start an active search.

README

  • ml19. Keep the top section focused on the goal

The top section reads:

sasquatch relies on the SASPy Python package and the reticulate R package to interoperate with Python. Check out vignette("configuration") for guidance on SASPy configuration.

Consider reserving this section for the package goal alone, and moving configuration details after installation. These seems to be a natural sequence:

  1. Decide I want this package,
  2. Install it,
  3. Set it up,
  4. Use it.
  • ml20. Add configuration for beginners

Before usage we need configuration. Sending readears to a vignette is fine but making README self-contained seems better because it minimizes the information-hunt before a succesfull first run.

Consider moving here the section "SAS On Demand for Academics", explaining this is a good way to get started for those who lack a license (which seems to be an important audience of this package), then pointing to the vignette for more advanced configuration.

I would focus on helping a) the reviewers, b) then curious R users, c) then anyone else. The users a-b likely lack a SAS license and are rushed to get something running.

  • ml21. Did you mean quarto?

I see: "Now, you should be able to run SAS code blocks in RStudio like any other."
Consider: "Now, you should be able to run SAS code blocks in Quarto like any other."

Also, sasquatch seems to also work with Rmarkdown, right? Does it deserve a mention?

Enhancements

  • ml22. Consider using memoise::memoise() to cache the result of slow function within an R session. (This is suiteble only when you two calls with the same arguments should always return the same result within an R session.) This should save time for reviewers and users.
devtools::load_all()
#> ℹ Loading sasquatch
sas_connect()
#> SAS Connection established.

system.time(sas_list("bad"))
#>    user  system elapsed
#>   0.003   0.001   2.006
# Same time
system.time(sas_list("bad"))
#>    user  system elapsed
#>   0.004   0.000   2.708

# Maybe this specific funciton isn't the best candidate for memoise() but 
# hopefully illustrates the idea
sas_list2 <- memoise::memoise(sas_list)

system.time(sas_list2("bad"))
#>    user  system elapsed
#>   0.005   0.001   3.011
# No time
system.time(sas_list2("bad"))
#>    user  system elapsed
#>   0.013   0.000   0.012
system.time(sas_list2("bad"))
#>    user  system elapsed
#>   0.000   0.000   0.001
system.time(sas_list2("bad"))
#>    user  system elapsed
#>       0       0       0

# https://memoise.r-lib.org/
# https://r-pkgs.org/testing-advanced.html

Created on 2025-01-12 with reprex v2.1.1

  • ml23. Consider modifying sas_connect() to avoid unintentional reconnections and saves time. This may make tests and examples run faster, saving reviewer time.
devtools::load_all()
#> ℹ Loading sasquatch

# Refactor: Rename the implementation
sas_connect_impl <- sas_connect

# Enhance the user-facing function
sas_connect <- function(cfgname, reconnect = FALSE) {
  unconnected <- is.null(sas_get_session())

  if (unconnected || reconnect) {
    sas_connect_impl(cfgname)
  } else {
    message("Already connected.")
    invisible()
  }
}

# The current implementation reconnects, which is slow and likely unnecesary
system.time({
  sas_connect_impl()
  sas_connect_impl()
})
#> SAS Connection established.
#> SAS Connection established.
#>    user  system elapsed
#>   0.140   0.068  34.758

sas_disconnect()
#> SAS Connection terminated.

# The new user-facing function doesn't re-connect
system.time({
  sas_connect()
  sas_connect()
})
#> SAS Connection established.
#> Already connected.
#>    user  system elapsed
#>   0.028   0.013  15.667

sas_disconnect()
#> SAS Connection terminated.

# The user can still request to reconnect on demand
system.time({
  sas_connect()
  sas_connect(reconnect = TRUE)
})
#> SAS Connection established.
#> SAS Connection established.
#>    user  system elapsed
#>   0.043   0.035  35.936

Created on 2025-01-12 with reprex v2.1.1

Reference

  • ml24. Link related funcitons via #' @family or #' @seealso. The goal is make it easier to discover and navigate related topics, like sas_connect() and sas_disconnect(). You may also consider documenting both in the same topic, e.g. by documenting sas_connect() as usual, then using #' @rdname sas_connect over the definition of sas_disconnect(). See https://roxygen2.r-lib.org/articles/reuse.html.

Tests

  • ml25. Answering your question

I'm not sure if you have any recommendations as to how I can better test the install_saspy() and configure_saspy() functions.

  • install_saspy(): I wouldn't worry too much. Based on its body it seems OK to just try it out on a fresh system, ideally in the three main platforms. It seems like a relatively thin wrapper around reticulate funcitons, which I expect to be pretty robust -- guessing from the quality of the developers and the size of the community that would report bugs.

  • configure_saspy(): Yeah, it's hard to test "as is" because code that needs external resources is mixed with code that doesn't. Two approaches that can help:

  1. Use the humble object pattern:

Some program elements are inherently difficult, or even impossible to test. Any logic in these elements is thus prone to bugs and difficult to evolve. To mitigate this problem, move as much as logic as possible out of the hard-to-test element and into other more friendly parts of the code base. By making untestable objects humble 1, we reduce the chances that they harbor evil bugs.

  1. Mock pieces of code that get in the way of your testing the critical logic.

For example, sas_engine() is a fairly large function. A few lines of code really need a conneciton and get in the way of testing the main logic. We may use mocks to turn-off connection-checks, and to verify we've entered a specific if statement.

devtools::load_all()
#> ℹ Loading sasquatch
library(testthat)

# This is what head of sas_engine() looks like. The rest doesn't matter for
# this specific test.
sas_engine <- function (options) {
  # Some checks that force a connection even when we don't need it
  chk_connection()
  options$engine <- "txt"
  options$results <- "asis"

  code <- paste(options$code, collapse = "\n")

  # Some logic we want to test
  if (!isTRUE(getOption('knitr.in.progress'))) {
  r  eturn(sas_run_string(code))
  }

  "... a lot more code"
}

test_that("if the option `knitr.in.progress` is not TRUE returns early", {
  withr::local_options(knitr.in.progress = "not `TRUE`")

  testthat::local_mocked_bindings(
    # Turn off checks, since here we don't need a real connection
    chk_connection = function() NULL,
    # Test that knitr.in.progress forces sas_engine() to return early
    sas_run_string = function(code) "retuning early"
  )

  expect_equal(sas_engine(list(code = "whatever")), "retuning early")
})
#> Test passed 🎉

Created on 2025-01-13 with reprex v2.1.1

More resources:

# Passes: Tests for sas_list() pass because test-connect() first calls sas_connect()
tests/testthat/
- test-connect()
- test-list()

# Fails: Tests for sas_list() fail because they run before test-connect.R calls sas_connect()
tests/testthat/
- test-aaa-list()
- test-connect()

You can avoid this dependency by calling sas_connect() in tests/testthat/test-helper.R. This runs once per test-suite rather than once per test or test file, resulting in a faster test-workflow and braking the dependency on the order of execution of the test-files.

  • ml27. Avoid failures

devtools::test() reports 5 failing tests. That's only a few relative to the much larger number of successful tests, so I wouldn't worry too much about skipping them if it's hard to make them pass in the reviewer's systems.


ℹ Testing sasquatch
✔ | F W  S  OK | Context
✔ |          5 | connect [44.1s]
✔ |          4 | connection-checks [18.0s]
✖ | 1        1 | conversion
───────────────────────────────────────────────────────────────────────────────────────────────────────
Error (test-conversion.R:22:3): back and forth
<python.builtin.TypeError/python.builtin.Exception/python.builtin.BaseException/python.builtin.object/error/condition>
Error in `py_call_impl(callable, call_args$unnamed, call_args$named)`: TypeError: 'list' object cannot be interpreted as an integer
Run `reticulate::py_last_error()` for details.
Backtrace:
▆
1. ├─testthat::expect_equal(sas_from_r(df, "df"), df) at test-conversion.R:22:3
2. │ └─testthat::quasi_label(enquo(object), label, arg = "object")
4. │   └─rlang::eval_bare(expr, quo_get_env(quo))
5. ├─sasquatch::sas_from_r(df, "df")
6. │ ├─reticulate::r_to_py(x) at sasquatch/R/from_r.R:55:3
7. │ └─reticulate:::r_to_py.data.frame(x)
8. │   └─reticulate:::r_to_py_impl(x, convert = convert)
9. ├─reticulate (local) `<fn>`(`<dttm>`, FALSE)
10. └─reticulate:::r_to_py.POSIXt(`<dttm>`, FALSE)
11.   └─datetime$datetime$fromtimestamp(as.double(x), tz = tz)
12.     └─reticulate:::py_call_impl(callable, call_args$unnamed, call_args$named)
───────────────────────────────────────────────────────────────────────────────────────────────────────
✔ |          3 | disconnect [21.2s]
✖ | 1       32 | engine [124.5s]
───────────────────────────────────────────────────────────────────────────────────────────────────────
Error (test-engine.R:193:3): non-html; default
<python.builtin.ModuleNotFoundError/python.builtin.ImportError/python.builtin.Exception/python.builtin.BaseException/python.builtin.object/error/condition>
Error in `py_module_import(module, convert = convert)`: ModuleNotFoundError: No module named 'markdownify'
Run `reticulate::py_last_error()` for details.
Backtrace:
▆
1. └─sasquatch::sas_engine(options) at test-engine.R:193:3
2.   └─reticulate::import("markdownify") at sasquatch/R/engine.R:115:5
3.     └─reticulate:::py_module_import(module, convert = convert)
───────────────────────────────────────────────────────────────────────────────────────────────────────
✔ |          4 | file-copy [37.9s]
✔ |          4 | file-download [49.7s]
✔ |          2 | file-exists [31.7s]
✔ |          3 | file-remove [29.7s]
✔ |          3 | file-upload [20.8s]
✖ | 2        1 | from-r [10.3s]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Error (test-from-r.R:24:3): R data.frame checks
<python.builtin.TypeError/python.builtin.Exception/python.builtin.BaseException/python.builtin.object/error/condition>
Error in `py_call_impl(callable, call_args$unnamed, call_args$named)`: TypeError: 'list' object cannot be interpreted as an integer
Run `reticulate::py_last_error()` for details.
Backtrace:
▆
1. ├─testthat::expect_warning(...) at test-from-r.R:24:3
2. │ └─testthat:::expect_condition_matching(...)
3. │   └─testthat:::quasi_capture(...)
4. │     ├─testthat (local) .capture(...)
5. │     │ └─base::withCallingHandlers(...)
6. │     └─rlang::eval_bare(quo_get_expr(.quo), quo_get_env(.quo))
7. ├─sasquatch::sas_from_r(df, "df")
8. │ ├─reticulate::r_to_py(x) at sasquatch/R/from_r.R:55:3
9. │ └─reticulate:::r_to_py.data.frame(x)
10. │   └─reticulate:::r_to_py_impl(x, convert = convert)
11. ├─reticulate (local) `<fn>`(`<dttm>`, FALSE)
13. └─reticulate:::r_to_py.POSIXt(`<dttm>`, FALSE)
14.   └─datetime$datetime$fromtimestamp(as.double(x), tz = tz)
15.     └─reticulate:::py_call_impl(callable, call_args$unnamed, call_args$named)

Error (test-from-r.R:43:3): Round trip
<python.builtin.TypeError/python.builtin.Exception/python.builtin.BaseException/python.builtin.object/error/condition>
Error in `py_call_impl(callable, call_args$unnamed, call_args$named)`: TypeError: 'list' object cannot be interpreted as an integer
Run `reticulate::py_last_error()` for details.
Backtrace:
▆
1. ├─testthat::expect_equal(sas_from_r(df, "df"), df) at test-from-r.R:43:3
2. │ └─testthat::quasi_label(enquo(object), label, arg = "object")
3. │   └─rlang::eval_bare(expr, quo_get_env(quo))
4. ├─sasquatch::sas_from_r(df, "df")
5. │ ├─reticulate::r_to_py(x) at sasquatch/R/from_r.R:55:3
6. │ └─reticulate:::r_to_py.data.frame(x)
7. │   └─reticulate:::r_to_py_impl(x, convert = convert)
8. ├─reticulate (local) `<fn>`(`<dttm>`, FALSE)
9. └─reticulate:::r_to_py.POSIXt(`<dttm>`, FALSE)
10.   └─datetime$datetime$fromtimestamp(as.double(x), tz = tz)
11.     └─reticulate:::py_call_impl(callable, call_args$unnamed, call_args$named)
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
✔ |          3 | list [6.8s]
✔ |          3 | run-file [14.7s]
✔ |          1 | run-string [5.6s]
✖ | 1        0 | to-r
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Error (test-to-r.R:71:3): SAS to R data.frame
Error in `sas_to_r("airline", "sashelp")`: object 'x' not found
Backtrace:
▆
1. └─sasquatch::sas_to_r("airline", "sashelp") at test-to-r.R:71:3
2.   └─reticulate::py_to_r(x) at sasquatch/R/to_r.R:40:3
3.     ├─reticulate::is_py_object(x <- py_to_r_cpp(x))
4.     └─reticulate:::py_to_r_cpp(x)
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

══ Results ═══════════════════════════════════════════════════════════════════════════════════════════════════════════
Duration: 415.3 s

── Failed tests ──────────────────────────────────────────────────────────────────────────────────────────────────────
Error (test-conversion.R:22:3): back and forth
<python.builtin.TypeError/python.builtin.Exception/python.builtin.BaseException/python.builtin.object/error/condition>
Error in `py_call_impl(callable, call_args$unnamed, call_args$named)`: TypeError: 'list' object cannot be interpreted as an integer
Run `reticulate::py_last_error()` for details.
Backtrace:
▆
1. ├─testthat::expect_equal(sas_from_r(df, "df"), df) at test-conversion.R:22:3
2. │ └─testthat::quasi_label(enquo(object), label, arg = "object")
3. │   └─rlang::eval_bare(expr, quo_get_env(quo))
4. ├─sasquatch::sas_from_r(df, "df")
5. │ ├─reticulate::r_to_py(x) at sasquatch/R/from_r.R:55:3
6. │ └─reticulate:::r_to_py.data.frame(x)
7. │   └─reticulate:::r_to_py_impl(x, convert = convert)
8. ├─reticulate (local) `<fn>`(`<dttm>`, FALSE)
9. └─reticulate:::r_to_py.POSIXt(`<dttm>`, FALSE)
10.   └─datetime$datetime$fromtimestamp(as.double(x), tz = tz)
11.     └─reticulate:::py_call_impl(callable, call_args$unnamed, call_args$named)

Error (test-engine.R:193:3): non-html; default
<python.builtin.ModuleNotFoundError/python.builtin.ImportError/python.builtin.Exception/python.builtin.BaseException/python.builtin.object/error/condition>
Error in `py_module_import(module, convert = convert)`: ModuleNotFoundError: No module named 'markdownify'
Run `reticulate::py_last_error()` for details.
Backtrace:
▆
1. └─sasquatch::sas_engine(options) at test-engine.R:193:3
2.   └─reticulate::import("markdownify") at sasquatch/R/engine.R:115:5
3.     └─reticulate:::py_module_import(module, convert = convert)

Error (test-from-r.R:24:3): R data.frame checks
<python.builtin.TypeError/python.builtin.Exception/python.builtin.BaseException/python.builtin.object/error/condition>
Error in `py_call_impl(callable, call_args$unnamed, call_args$named)`: TypeError: 'list' object cannot be interpreted as an integer
Run `reticulate::py_last_error()` for details.
Backtrace:
▆
1. ├─testthat::expect_warning(...) at test-from-r.R:24:3
2. │ └─testthat:::expect_condition_matching(...)
3. │   └─testthat:::quasi_capture(...)
4. │     ├─testthat (local) .capture(...)
5. │     │ └─base::withCallingHandlers(...)
6. │     └─rlang::eval_bare(quo_get_expr(.quo), quo_get_env(.quo))
7. ├─sasquatch::sas_from_r(df, "df")
8. │ ├─reticulate::r_to_py(x) at sasquatch/R/from_r.R:55:3
9. │ └─reticulate:::r_to_py.data.frame(x)
10. │   └─reticulate:::r_to_py_impl(x, convert = convert)
11. ├─reticulate (local) `<fn>`(`<dttm>`, FALSE)
12. └─reticulate:::r_to_py.POSIXt(`<dttm>`, FALSE)
13.   └─datetime$datetime$fromtimestamp(as.double(x), tz = tz)
14.     └─reticulate:::py_call_impl(callable, call_args$unnamed, call_args$named)

Error (test-from-r.R:43:3): Round trip
<python.builtin.TypeError/python.builtin.Exception/python.builtin.BaseException/python.builtin.object/error/condition>
Error in `py_call_impl(callable, call_args$unnamed, call_args$named)`: TypeError: 'list' object cannot be interpreted as an integer
Run `reticulate::py_last_error()` for details.
Backtrace:
▆
1. ├─testthat::expect_equal(sas_from_r(df, "df"), df) at test-from-r.R:43:3
2. │ └─testthat::quasi_label(enquo(object), label, arg = "object")
3. │   └─rlang::eval_bare(expr, quo_get_env(quo))
4. ├─sasquatch::sas_from_r(df, "df")
5. │ ├─reticulate::r_to_py(x) at sasquatch/R/from_r.R:55:3
6. │ └─reticulate:::r_to_py.data.frame(x)
7. │   └─reticulate:::r_to_py_impl(x, convert = convert)
8. ├─reticulate (local) `<fn>`(`<dttm>`, FALSE)
9. └─reticulate:::r_to_py.POSIXt(`<dttm>`, FALSE)
10.   └─datetime$datetime$fromtimestamp(as.double(x), tz = tz)
11.     └─reticulate:::py_call_impl(callable, call_args$unnamed, call_args$named)

Error (test-to-r.R:71:3): SAS to R data.frame
Error in `sas_to_r("airline", "sashelp")`: object 'x' not found
Backtrace:
▆
1. └─sasquatch::sas_to_r("airline", "sashelp") at test-to-r.R:71:3
2.   └─reticulate::py_to_r(x) at sasquatch/R/to_r.R:40:3
3.     ├─reticulate::is_py_object(x <- py_to_r_cpp(x))
4.     └─reticulate:::py_to_r_cpp(x)

[ FAIL 5 | WARN 0 | SKIP 0 | PASS 69 ]
  • ml28. Consider skipping tests when there's no connection. For example, this failure IS informative but currently the relevant error message is somewhat burried by the less useful backtrace.
devtools::load_all()
#> ℹ Loading sasquatch

# tests/testthat/helper.R
skip_if_unconnected <- function() {
  skip_if(
  is.null(sas_get_session()),
  "No active SAS session. Use sas_connect() to start one."
  )
}


# tests/testthat/test-*.R
testthat::test_that("needs a sas connection", {
  skip_if_unconnected()
  sas_list()
})
#> ── Skip: needs a sas connection ────────────────────────────────────────────────
#> Reason: No active SAS session. Use sas_connect() to start one.



# tests/testthat/test-*.R
# This IS informative but:
# * Doesn't communicate the develper the the connection is a requirement
# * Burries the relevant information with irrelevant clutter from the backtrace
testthat::test_that("needs a sas connection", {
  sas_list()
})
#> ── Error: needs a sas connection ───────────────────────────────────────────────
#> <chk_error/rlang_error/error/condition>
#> Error in `sas_list()`: No active SAS session. Use sas_connect() to start one.
#> Backtrace:
#>     ▆
#>  1. └─sasquatch::sas_list()
#>  2.   └─sasquatch:::chk_connection() at sasquatch/R/list.R:17:3
#>  3.     └─chk::abort_chk("No active SAS session. Use sas_connect() to start one.") at sasquatch/R/connection-checks.R:15:3
#>  4.       └─chk::err(..., n = n, tidy = tidy, class = "chk_error", call = call)
#>  5.         └─rlang::abort(msg, class = class, !!!args[named], call = call)
#> Error:
#> ! Test failed

Created on 2025-01-12 with reprex v2.1.1

  • ml29. If you need to create then destroy a connection multiple times, consider a helper local_sas_connect() to create self-destroying connection.
devtools::load_all()
#> ℹ Loading sasquatch

# See https://testthat.r-lib.org/articles/test-fixtures.html#local-helpers
# tests/testthat/helper.R
local_sas_connect <- function(..., env = parent.frame()) {
  sas_connect()
  withr::defer(suppressMessages(sas_disconnect()), envir = env)
}

# The connection is self-destroying
local({
  local_sas_connect()
  sas_get_session()
})
#> SAS Connection established.
#> Access Method         = IOM
#> SAS Config name       = oda
#> SAS Config file       = /home/rstudio/git/sasquatch/.venv/lib/python3.12/site-packages/saspy/sascfg_personal.py
#> WORK Path             = /saswork/SAS_workF49F00002AC2_odaws01-usw2.oda.sas.com/SAS_work77E600002AC2_odaws01-usw2.oda.sas.com/
#> SAS Version           = 9.04.01M7P08062020
#> SASPy Version         = 5.101.1
#> Teach me SAS          = False
#> Batch                 = False
#> Results               = HTML
#> SAS Session Encoding  = utf-8
#> Python Encoding value = utf-8
#> SAS process Pid value = None
#> SASsession started    = Sun Jan 12 17:24:16 2025

# Gone!
sas_get_session()
#> NULL

Created on 2025-01-12 with reprex v2.1.1

  • ml30. Consider testing errors separately from other types of expected output. This allows you to write a more focused test-title, and more readable test body.

Example:

devtools::load_all()
#> ℹ Loading sasquatch
library(testthat)

test_that("with a list column throws a 'chk_error'", {
with_list_column <- tibble::tibble(x = list(1))
expect_error(sas_from_r(with_list_column, "df"), class = "chk_error")
})
#> Test passed 🎉

Created on 2025-01-13 with reprex v2.1.1

  • ml31. Also consider creating the smallest test-data necessary to trigger the expected error. See the example above. The more focused the test, the easier it is to setup and to read. It's natural to try minimize the number of tests when the setup of each test takes too long to run, but that's a problem that is best avoided with a combination of strategies like the ones mentioned above, including setting up a connection only once in tests/testthat/helper.R, memoise::memoise(), mocks, the humble-object pattern, and a separation between fast unit test (in tests/testthat) from slow tests (e.g. in tests/testthat/slow) which you run occasionally with testthat::test_dir("tests/testthat/slow"). See also https://r-pkgs.org/testing-advanced.html.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants