software-review
software-review copied to clipboard
`sasquatch`: Use SAS, R, and Quarto together
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: @thisisnic, @tataphani
Due date for @tataphani: 2025-06-20Archive: 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
- [x] 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).
- Are there other R packages that accomplish the same thing? If so, how does yours differ or meet our criteria for best-in-category?
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.
- (If applicable) Does your package comply with our guidance around Ethics, Data Privacy and Human Subjects Research?
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
pkgcheckitems 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.
- [x] I have read the rOpenSci packaging guide.
- [x] I have read the author guide and I expect to maintain this package for at least 2 years or to find a replacement.
This package:
- [x] does not violate the Terms of Service of any service it interacts with.
- [x] has a CRAN and OSI accepted license.
- [x] contains a README with instructions for installing the development version.
- [x] includes documentation with examples for all functions, created with roxygen2.
- [ ] contains a vignette with examples of its essential functions and uses. (this would need to be rendered locally)
- [x] has a test suite.
- [x] has continuous integration, including reporting of test coverage.
Publication options
-
[x] 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
- [x] I agree to abide by rOpenSci's Code of Conduct during the review process and in maintaining my package should it be accepted.
Thanks for submitting to rOpenSci, our editors and @ropensci-review-bot will reply soon. Type @ropensci-review-bot help for help.
:rocket:
Editor check started
:wave:
Checks for sasquatch (v0.0.0.9004)
git hash: 8353d8ac
- :heavy_check_mark: Package name is available
- :heavy_check_mark: has a 'codemeta.json' file.
- :heavy_check_mark: has a 'contributing' file.
- :heavy_check_mark: uses 'roxygen2'.
- :heavy_check_mark: 'DESCRIPTION' has a URL field.
- :heavy_check_mark: 'DESCRIPTION' has a BugReports field.
- :heavy_check_mark: Package has at least one HTML vignette
- :heavy_check_mark: All functions have examples.
- :heavy_check_mark: Package has continuous integration checks.
- :heavy_multiplication_x: Package coverage failed
- :heavy_multiplication_x: R CMD check found 1 error.
- :heavy_check_mark: 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
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:
- 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: ▆- └─sasquatch::sas_connect() at test-run.R:18:3
- └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
-
└─reticulate:::ensure_python_initialized() -
└─reticulate:::initialize_python() -
└─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:
-
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 <simpleError> 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 <simpleError> 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 <simpleError> 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 <simpleError> 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:
▆
- └─sasquatch::sas_connect() at test-conversion.R:2:3
- └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
-
└─reticulate:::ensure_python_initialized() -
└─reticulate:::initialize_python() -
└─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:
▆
- └─sasquatch::sas_connect() at test-conversion.R:24:3
- └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
-
└─reticulate:::ensure_python_initialized() -
└─reticulate:::initialize_python() -
└─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:
▆
- └─sasquatch::sas_connect() at test-io.R:12:3
- └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
-
└─reticulate:::ensure_python_initialized() -
└─reticulate:::initialize_python() -
└─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:
▆
- └─sasquatch::sas_connect() at test-io.R:34:3
- └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
-
└─reticulate:::ensure_python_initialized() -
└─reticulate:::initialize_python() -
└─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:
▆
- └─sasquatch::sas_connect() at test-io.R:61:3
- └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
-
└─reticulate:::ensure_python_initialized() -
└─reticulate:::initialize_python() -
└─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:
▆
- └─sasquatch::sas_connect() at test-io.R:85:3
- └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
-
└─reticulate:::ensure_python_initialized() -
└─reticulate:::initialize_python() -
└─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:
▆
- └─sasquatch::sas_connect() at test-run.R:4:3
- └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
-
└─reticulate:::ensure_python_initialized() -
└─reticulate:::initialize_python() -
└─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:
▆
- └─sasquatch::sas_connect() at test-run.R:18:3
- └─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
-
└─reticulate:::ensure_python_initialized() -
└─reticulate:::initialize_python() -
└─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:
- 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 :heavy_multiplication_x: have been resolved.
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.)
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.
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.
Sounds great! Thank you
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...)
@ropensci-review-bot check package
Thanks, about to send the query.
:rocket:
Editor check started
:wave:
Checks for sasquatch (v0.0.0.9014)
git hash: b506da05
- :heavy_check_mark: Package name is available
- :heavy_check_mark: has a 'codemeta.json' file.
- :heavy_check_mark: has a 'contributing' file.
- :heavy_check_mark: uses 'roxygen2'.
- :heavy_check_mark: 'DESCRIPTION' has a URL field.
- :heavy_check_mark: 'DESCRIPTION' has a BugReports field.
- :heavy_check_mark: Package has at least one HTML vignette
- :heavy_check_mark: All functions have examples.
- :heavy_check_mark: Package has continuous integration checks.
- :heavy_multiplication_x: Package coverage failed
- :heavy_multiplication_x: R CMD check found 1 error.
- :heavy_check_mark: 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
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:
- checking tests ...
Running ‘testthat.R’
ERROR
Running the tests in ‘tests/testthat.R’ failed.
Last 13 lines of output:
Backtrace:
▆
- ├─sasquatch::sas_connect() at test-run.R:22:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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:
-
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 <simpleError> 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 <simpleError> 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 <simpleError> 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 <simpleError> 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: ▆
- ├─sasquatch::sas_connect() at test-conversion.R:4:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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: ▆
- ├─sasquatch::sas_connect() at test-conversion.R:28:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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: ▆
- ├─sasquatch::sas_connect() at test-io.R:14:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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: ▆
- ├─sasquatch::sas_connect() at test-io.R:38:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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: ▆
- ├─sasquatch::sas_connect() at test-io.R:67:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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: ▆
- ├─sasquatch::sas_connect() at test-io.R:93:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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: ▆
- ├─sasquatch::sas_connect() at test-run.R:6:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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: ▆
- ├─sasquatch::sas_connect() at test-run.R:22:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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:
- 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 :heavy_multiplication_x: have been resolved.
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.
That seems like a very reasonable approach, I will try it out. Thank you
@ropensci-review-bot assign @maurolepore as editor
Assigned! @maurolepore is now the editor
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 :100: :1st_place_medal:
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.
@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 🙏👍
Sounds good! I apologize, I'm a relative newcomer to the R open-source world, so I'm not super familiar with the space
No problem at all. One great reason to be here is precisely to expand your networks :-)
@ropensci-review-bot check package
Thanks, about to send the query.
:rocket:
Editor check started
:wave:
Checks for sasquatch (v0.0.0.9017)
git hash: aee061da
- :heavy_check_mark: Package name is available
- :heavy_check_mark: has a 'codemeta.json' file.
- :heavy_check_mark: has a 'contributing' file.
- :heavy_check_mark: uses 'roxygen2'.
- :heavy_check_mark: 'DESCRIPTION' has a URL field.
- :heavy_check_mark: 'DESCRIPTION' has a BugReports field.
- :heavy_check_mark: Package has at least one HTML vignette
- :heavy_check_mark: All functions have examples.
- :heavy_check_mark: Package has continuous integration checks.
- :heavy_multiplication_x: Package coverage failed
- :heavy_multiplication_x: R CMD check found 1 error.
- :heavy_multiplication_x: 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
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:
- checking tests ...
Running ‘testthat.R’
ERROR
Running the tests in ‘tests/testthat.R’ failed.
Last 13 lines of output:
Backtrace:
▆
- ├─sasquatch::sas_connect() at test-run.R:22:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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:
- 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:
-
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 <simpleError> 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 <simpleError> 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: ▆
- ├─sasquatch::sas_connect() at test-conversion.R:4:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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: ▆
- ├─sasquatch::sas_connect() at test-conversion.R:28:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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: ▆
- ├─sasquatch::sas_connect() at test-io.R:14:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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: ▆
- ├─sasquatch::sas_connect() at test-io.R:38:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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: ▆
- ├─sasquatch::sas_connect() at test-io.R:67:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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: ▆
- ├─sasquatch::sas_connect() at test-io.R:93:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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: ▆
- ├─sasquatch::sas_connect() at test-run.R:6:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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: ▆
- ├─sasquatch::sas_connect() at test-run.R:22:3
- │ ├─reticulate::py_capture_output(.pkgenv$session <- .pkgenv$SASPy$SASsession())
- │ │ ├─base::tryCatch(...)
- │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
- │ │ └─base::force(expr)
- │ ├─.pkgenv$SASPy$SASsession
- │ └─reticulate:::
$.python.builtin.module(.pkgenv$SASPy, "SASsession") - │ └─reticulate::py_get_attr(x, name, TRUE)
- └─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:
- rcmdcheck_code_docs_mismatch
- 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 :heavy_multiplication_x: have been resolved.
@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:
- [x] 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,
- [x] Is the case for the package well made?
- [x] Is the reference index page clear (grouped by topic if necessary)?
- [x] Are vignettes readable, sufficiently detailed and not just perfunctory?
- [x] Fit: The package meets criteria for fit and overlap.
- [x] 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?
- [x] License: The package has a CRAN or OSI accepted license.
- [x] 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:
- [x] 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 = NULLbut the source code showsextra_packageshas 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,
- Consider returning something other than
NULL. See https://design.tidyverse.org/out-invisible.html#whats-the-pattern.
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?
- [x] 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.
- [x] 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
sasengine. - 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:
- Remember how you got involved in SAS. Where was that? Who was there?
- 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?
- 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?
- [x] 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
- ml08. Consider mentioning that pak installs system dependencies automatically.
- ml09. Consider recording a video to support installation and setup.
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.
- ml10. Consider structuring R/ and tests/ such that each exported function f has an R/f.R and a tests/testthat/test-f.R. This structure became common so it will likely help reviewers understant each unit of behaviour in relative isolation from the rest of the system. A good reference is https://r-pkgs.org/testing-basics.html, and I also like https://mastering-shiny.org/scaling-testing.html#testing-functions.
-
[ ] 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 needon.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:
- https://r-pkgs.org/testing-advanced.html
- https://books.ropensci.org/http-testing/
- https://testthat.r-lib.org/reference/index.html#mocking. You
-
[x] ml13. Whenever possible please replace printed output with something that's easier to handle (e.g.
message()can be suppressed withsuppressedMessages()). 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?
- [x] ml14. Please add instructions for reviewers and other contributors to
successfullly run tests. What seems like test setup points to a specific,
private
sas_userthat will unlikely work for reviewers. See https://devguide.ropensci.org/maintenance_collaboration.html. Also note that testthat enforces a different name-convention for heper files versus setup files. See https://testthat.r-lib.org/articles/special-files.html#setup-files.
Additional considerations
- [x] 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
#> [](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()andsas_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 becomesas_from_r(). -
ml18. Consider running a global search for RStudio and making sure wording won't be obsolete when Positron becomes more popular.
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!
@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()andconfigure_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 :smile:
Happy hollidays!
BTW, re-reading my own comment above I found so many sloppy typos -- I apologize.
@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:
- Decide I want this package,
- Install it,
- Set it up,
- 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
#' @familyor#' @seealso. The goal is make it easier to discover and navigate related topics, likesas_connect()andsas_disconnect(). You may also consider documenting both in the same topic, e.g. by documentingsas_connect()as usual, then using#' @rdname sas_connectover the definition ofsas_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()andconfigure_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:
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.
- 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:
-
https://r-pkgs.org/testing-advanced.html
-
https://books.ropensci.org/http-testing/
-
https://webfakes.r-lib.org/
-
ml26. Avoid dependencies between tests
# 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.
Dear @ryanzomorrodi FYI my EiC rotation starts today and I'm reviewing all open issues. I see I reached out about 3 weeks ago, but got no response. Are you still planning to continue?
If you can't continue right now you can choose to put the submission on hold. This is totally fine as we understand things can change -- so no pressure.
https://devguide.ropensci.org/softwarereview_policies.html#policiesreviewprocess
- The author can choose to have their submission put on hold (editor applies the holding label). The holding status will be revisited every 3 months, and after one year the issue will be closed.
- If the author hasn’t requested a holding label, but is simply not responding, we should close the issue within one month after the last contact intent. This intent will include a comment tagging the author, but also an email using the email address listed in the DESCRIPTION of the package which is one of the rare cases where the editor will try to contact the author by email.