diff --git a/DESCRIPTION b/DESCRIPTION index 72fa79b2e..08eae06d1 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,7 @@ Package: polars Title: Lightning-Fast 'DataFrame' Library Version: 0.14.1.9000 Depends: R (>= 4.2) -Imports: utils, codetools +Imports: utils, codetools, methods Authors@R: c(person("Ritchie", "Vink", , "ritchie46@gmail.com", role = c("aut")), person("Soren", "Welling", , "sorhawell@gmail.com", role = c("aut", "cre")), diff --git a/NAMESPACE b/NAMESPACE index 56b81cfc9..49c04156d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -263,6 +263,7 @@ export(polars_envvars) export(polars_info) export(polars_options) export(polars_options_reset) +importFrom(methods,new) importFrom(stats,median) importFrom(stats,na.omit) importFrom(utils,.DollarNames) diff --git a/NEWS.md b/NEWS.md index f364703cd..22babf2de 100644 --- a/NEWS.md +++ b/NEWS.md @@ -49,6 +49,12 @@ - Converts `clock_time_point` and `clock_zoned_time` objects from the `{clock}` package to Polars datetime type (#861). +### Bug fixes + +- R no longer crashes when calling an invalid Polars object that points + to a null pointer (#874). This was occurring, such as when a Polars object + was saved in an RDS file and loaded from another session. + ## Polars R Package 0.14.1 ### Breaking changes diff --git a/R/after-wrappers.R b/R/after-wrappers.R index 0b632991e..9d4f18544 100644 --- a/R/after-wrappers.R +++ b/R/after-wrappers.R @@ -142,10 +142,11 @@ extendr_method_to_pure_functions = function(env, class_name = NULL) { macro_add_syntax_check_to_class = function(Class_name) { tokens = paste0( "`$.", Class_name, "` <- function (self, name) {\n", - " verify_method_call(", Class_name, ",name)\n", + " verify_not_null_pointer(self, 'in `$.", Class_name, "`')\n", + " verify_method_call(", Class_name, ", name)\n", " func <- ", Class_name, "[[name]]\n", " environment(func) <- environment()\n", - " if(inherits(func,'property')) {\n", + " if(inherits(func, 'property')) {\n", " func()\n", " } else {\n", " func\n", @@ -329,8 +330,6 @@ pl_mem_address = function(robj) { #' - Code completion is facilitated by `.DollarNames.ClassName`-s3method see e.g. 'R/dataframe__frame.R' #' - Implementation of property-methods as DataFrame_columns() and syntax checking is an extension to `$.ClassName` #' See function macro_add_syntax_check_to_class(). -#' -#' @importFrom utils .DollarNames #' @return not applicable #' @examples #' # all a polars object is only made of: diff --git a/R/pkg-knitr.R b/R/pkg-knitr.R index 8eaa6e9c7..c2db90f76 100644 --- a/R/pkg-knitr.R +++ b/R/pkg-knitr.R @@ -41,7 +41,6 @@ knit_print.RPolarsDataFrame = function(x, ...) { #' @examples #' to_html_table(mtcars, 3, 3) #' @noRd -#' @importFrom utils getFromNamespace to_html_table = function(x, max_cols = 75, max_rows = 40) { if (!requireNamespace("knitr", quietly = TRUE)) { stop("Please install the `knitr` package to use `to_html_table`.") diff --git a/R/polars-package.R b/R/polars-package.R index 4ecd80114..8740259e2 100644 --- a/R/polars-package.R +++ b/R/polars-package.R @@ -2,7 +2,9 @@ "_PACKAGE" ## usethis namespace: start -#' @importFrom utils globalVariables head tail download.file capture.output str +#' @importFrom stats na.omit median +#' @importFrom utils .DollarNames globalVariables getFromNamespace head tail download.file capture.output str +#' @importFrom methods new ## usethis namespace: end NULL diff --git a/R/s3_methods.R b/R/s3_methods.R index 49e95175f..8a2b8dd04 100644 --- a/R/s3_methods.R +++ b/R/s3_methods.R @@ -341,16 +341,13 @@ mean.RPolarsSeries = function(x, ...) x$mean() #' #' @export #' @rdname S3_median -#' @importFrom stats median median.RPolarsDataFrame = function(x, ...) x$median() #' @export -#' @importFrom stats median #' @rdname S3_median median.RPolarsLazyFrame = function(x, ...) x$median() #' @export -#' @importFrom stats median #' @rdname S3_median median.RPolarsSeries = function(x, ...) x$median() @@ -494,8 +491,6 @@ c.RPolarsSeries = \(x, ...) { #' @param object A [DataFrame][DataFrame_class] or [LazyFrame][LazyFrame_class] #' @param subset Character vector of column names to drop missing values from. #' @param ... Not used. -#' -#' @importFrom stats na.omit #' @export #' @rdname S3_na.omit #' @examples diff --git a/R/utils.R b/R/utils.R index fbf8c87f7..9fec4b827 100644 --- a/R/utils.R +++ b/R/utils.R @@ -49,7 +49,34 @@ check_no_missing_args = function( } +# https://stackoverflow.com/a/27350487/3297472 +is_null_external_pointer = function(pointer) { + a = attributes(pointer) + attributes(pointer) = NULL + out = identical(pointer, new("externalptr")) + attributes(pointer) = a + out +} + +verify_not_null_pointer = function(pointer, context = NULL) { + valid = FALSE + tryCatch( + { + valid = !is_null_external_pointer(pointer) + }, + error = function(c) {} + ) + + if (!valid) { + Err_plain( + "This Polars object is not valid. Execute `rm()` to remove the object or restart the R session." + ) |> + unwrap(context = context) + } + + invisible(NULL) +} #' Verify user selected method/attribute exists diff --git a/tests/testthat/test-after-wrappers.R b/tests/testthat/test-after-wrappers.R index 8c9ce761c..95eafb888 100644 --- a/tests/testthat/test-after-wrappers.R +++ b/tests/testthat/test-after-wrappers.R @@ -32,3 +32,10 @@ patrick::with_parameters_test_that("public and private methods of each class", }, .cases = make_class_cases() ) + + +test_that("check the polars object is valid", { + raw = as_polars_df(mtcars) |> + serialize(connection = NULL) + expect_error(print(unserialize(raw)), "restart the R session") +})