r-lib/clock

calendar columns break `DT::datatable()`

maxheld83 opened this issue · 4 comments

I expected DT::datatable() to use a format method (like as.character()) of a calendar column, and show me that.
But passing a clock calendar column to DT fails.

Manually wrapping as.character() around the offending column works.

data.frame(cals = clock::year_quarter_day(year = 2000:2001, quarter = 2)) |>
  DT::datatable()
#> Error:
#> ! Names must be a character vector.
#> Backtrace:
#>      ▆
#>   1. ├─base::tryCatch(...)
#>   2. │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#>   3. │   ├─base (local) tryCatchOne(...)
#>   4. │   │ └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#>   5. │   └─base (local) tryCatchList(expr, names[-nh], parentenv, handlers[-nh])
#>   6. │     └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#>   7. │       └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#>   8. ├─base::withCallingHandlers(...)
#>   9. ├─base::saveRDS(...)
#>  10. ├─base::do.call(...)
#>  11. ├─base (local) `<fn>`(...)
#>  12. ├─global `<fn>`(input = base::quote("gaudy-geese_reprex.R"))
#>  13. │ └─rmarkdown::render(input, quiet = TRUE, envir = globalenv(), encoding = "UTF-8")
#>  14. │   └─knitr::knit(knit_input, knit_output, envir = envir, quiet = quiet)
#>  15. │     └─knitr:::process_file(text, output)
#>  16. │       ├─base::withCallingHandlers(...)
#>  17. │       ├─knitr:::process_group(group)
#>  18. │       └─knitr:::process_group.block(group)
#>  19. │         └─knitr:::call_block(x)
#>  20. │           └─knitr:::block_exec(params)
#>  21. │             └─knitr:::eng_r(options)
#>  22. │               ├─knitr:::in_input_dir(...)
#>  23. │               │ └─knitr:::in_dir(input_dir(), expr)
#>  24. │               └─knitr (local) evaluate(...)
#>  25. │                 └─evaluate::evaluate(...)
#>  26. │                   └─evaluate:::evaluate_call(...)
#>  27. │                     ├─evaluate (local) handle(...)
#>  28. │                     │ └─base::try(f, silent = TRUE)
#>  29. │                     │   └─base::tryCatch(...)
#>  30. │                     │     └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#>  31. │                     │       └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#>  32. │                     │         └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#>  33. │                     ├─base::withCallingHandlers(...)
#>  34. │                     ├─base::withVisible(value_fun(ev$value, ev$visible))
#>  35. │                     └─knitr (local) value_fun(ev$value, ev$visible)
#>  36. │                       └─knitr (local) fun(x, options = options)
#>  37. │                         ├─base::withVisible(knit_print(x, ...))
#>  38. │                         ├─knitr::knit_print(x, ...)
#>  39. │                         └─htmlwidgets:::knit_print.htmlwidget(x, ...)
#>  40. │                           ├─knitr::knit_print(...)
#>  41. │                           │ └─knitr:::need_screenshot(x, ...)
#>  42. │                           └─htmlwidgets:::toHTML(x, standalone = FALSE, knitrOptions = options)
#>  43. │                             ├─htmltools::tagList(...)
#>  44. │                             │ └─rlang::dots_list(...)
#>  45. │                             └─htmlwidgets:::widget_data(x, x$id)
#>  46. │                               ├─htmlwidgets:::toJSON(createPayload(x))
#>  47. │                               └─htmlwidgets:::createPayload(x)
#>  48. │                                 └─htmlwidgets::JSEvals(x)
#>  49. │                                   ├─names(which(unlist(shouldEval(list)))) %||% list()
#>  50. │                                   ├─base::which(unlist(shouldEval(list)))
#>  51. │                                   ├─base::unlist(shouldEval(list))
#>  52. │                                   └─htmlwidgets:::shouldEval(list)
#>  53. │                                     └─base::lapply(options, shouldEval)
#>  54. │                                       └─htmlwidgets (local) FUN(X[[i]], ...)
#>  55. │                                         └─base::lapply(options, shouldEval)
#>  56. │                                           └─htmlwidgets (local) FUN(X[[i]], ...)
#>  57. │                                             ├─base::`names<-`(`*tmp*`, value = seq_len(n) - 1L)
#>  58. │                                             └─clock:::`names<-.clock_rcrd`(`*tmp*`, value = seq_len(n) - 1L)
#>  59. └─rlang (local) `<fn>`("Names must be a character vector.")
Session info
sessioninfo::session_info()
#> ─ Session info ───────────────────────────────────────────────────────────────
#>  setting  value
#>  version  R version 4.3.0 (2023-04-21)
#>  os       macOS Ventura 13.3.1
#>  system   aarch64, darwin20
#>  ui       X11
#>  language (EN)
#>  collate  en_US.UTF-8
#>  ctype    en_US.UTF-8
#>  tz       Europe/Berlin
#>  date     2023-04-25
#>  pandoc   3.1.2 @ /opt/homebrew/bin/ (via rmarkdown)
#> 
#> ─ Packages ───────────────────────────────────────────────────────────────────
#>  package     * version date (UTC) lib source
#>  bslib         0.4.2   2022-12-16 [1] CRAN (R 4.3.0)
#>  cachem        1.0.7   2023-02-24 [1] CRAN (R 4.3.0)
#>  cli           3.6.1   2023-03-23 [1] CRAN (R 4.3.0)
#>  clock         0.6.1   2022-07-18 [1] CRAN (R 4.3.0)
#>  crosstalk     1.2.0   2021-11-04 [1] CRAN (R 4.3.0)
#>  digest        0.6.31  2022-12-11 [1] CRAN (R 4.3.0)
#>  DT            0.27    2023-01-17 [1] CRAN (R 4.3.0)
#>  ellipsis      0.3.2   2021-04-29 [1] CRAN (R 4.3.0)
#>  evaluate      0.20    2023-01-17 [1] CRAN (R 4.3.0)
#>  fansi         1.0.4   2023-01-22 [1] CRAN (R 4.3.0)
#>  fastmap       1.1.1   2023-02-24 [1] CRAN (R 4.3.0)
#>  fs            1.6.2   2023-04-25 [1] CRAN (R 4.3.0)
#>  glue          1.6.2   2022-02-24 [1] CRAN (R 4.3.0)
#>  htmltools     0.5.5   2023-03-23 [1] CRAN (R 4.3.0)
#>  htmlwidgets   1.6.2   2023-03-17 [1] CRAN (R 4.3.0)
#>  jquerylib     0.1.4   2021-04-26 [1] CRAN (R 4.3.0)
#>  jsonlite      1.8.4   2022-12-06 [1] CRAN (R 4.3.0)
#>  knitr         1.42    2023-01-25 [1] CRAN (R 4.3.0)
#>  lifecycle     1.0.3   2022-10-07 [1] CRAN (R 4.3.0)
#>  magrittr      2.0.3   2022-03-30 [1] CRAN (R 4.3.0)
#>  pillar        1.9.0   2023-03-22 [1] CRAN (R 4.3.0)
#>  purrr         1.0.1   2023-01-10 [1] CRAN (R 4.3.0)
#>  R.cache       0.16.0  2022-07-21 [1] CRAN (R 4.3.0)
#>  R.methodsS3   1.8.2   2022-06-13 [1] CRAN (R 4.3.0)
#>  R.oo          1.25.0  2022-06-12 [1] CRAN (R 4.3.0)
#>  R.utils       2.12.2  2022-11-11 [1] CRAN (R 4.3.0)
#>  R6            2.5.1   2021-08-19 [1] CRAN (R 4.3.0)
#>  reprex        2.0.2   2022-08-17 [1] CRAN (R 4.3.0)
#>  rlang         1.1.0   2023-03-14 [1] CRAN (R 4.3.0)
#>  rmarkdown     2.21    2023-03-26 [1] CRAN (R 4.3.0)
#>  rstudioapi    0.14    2022-08-22 [1] CRAN (R 4.3.0)
#>  sass          0.4.5   2023-01-24 [1] CRAN (R 4.3.0)
#>  sessioninfo   1.2.2   2021-12-06 [1] CRAN (R 4.3.0)
#>  styler        1.9.1   2023-03-04 [1] CRAN (R 4.3.0)
#>  tzdb          0.3.0   2022-03-28 [1] CRAN (R 4.3.0)
#>  utf8          1.2.3   2023-01-31 [1] CRAN (R 4.3.0)
#>  vctrs         0.6.2   2023-04-19 [1] CRAN (R 4.3.0)
#>  withr         2.5.0   2022-03-03 [1] CRAN (R 4.3.0)
#>  xfun          0.39    2023-04-20 [1] CRAN (R 4.3.0)
#>  yaml          2.3.7   2023-01-23 [1] CRAN (R 4.3.0)
#> 
#>  [1] /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/library
#> 
#> ──────────────────────────────────────────────────────────────────────────────

It looks like the problem is coming from htmlwidgets:::shouldEval(), and I don't understand why that function recurses on lists. It eventually gets to the calendar column (which technically is implemented on a list, I would add an is.list() method that returns FALSE but it isn't generic) and tries to set its names to an integer vectors, which I don't allow in clock (names must be a character vector).

I feel like the core of the issue is that it is trying to recurse over the calendar object at all

This is probably a htmlwidgets bug more than a clock bug

Reported upstream

Unfortunately, the error here is a bit of a red herring. While I do have a PR to apply Davis' suggestions in htmlwidgets, once that obstacle is removed, data_table() doesn't coerce the calendar objects to strings. Instead you'll have blank columns and potentially a client-side error when viewing the table.

I'm not sure if DT explicitly performs a format step, or if it does why it's not being applied to the clock_calendar object.
For now, casting to character with as.character() is probably the best thing to do. You might also want to open an issue in DT.