Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Creating knit_print.foo method does not work in R 3.5 #1580

Closed
wch opened this issue Jul 27, 2018 · 8 comments
Closed

Creating knit_print.foo method does not work in R 3.5 #1580

wch opened this issue Jul 27, 2018 · 8 comments
Milestone

Comments

@wch
Copy link

wch commented Jul 27, 2018

Example:

---
output: html_document
---

```{r}
knit_print.data.frame <- function(x, ...) {
  cat("HELLO!")
}

pressure
```

In R 3.4.4 (on rstudio.cloud), I get this:

image

In R 3.5.0 (on rstudio.cloud and on my local mac), I get:

image

I believe it must be due to a change in how S3 dispatch works.

A workaround is to use registerS3method.

Here's an example that prints some more information and uses registerS3method.

---
output: html_document
---

```{r}
library(knitr)
knit_print.data.frame <- function(x, ...) {
  cat("HELLO!")
}

registerS3method("knit_print", "data.frame", function(x, ...) {
    cat("HELLO 2!")
})

methods("knit_print")
getS3method("knit_print", "data.frame")
pressure
```

R 3.4.4:

image

R 3.5.0:
image

@yihui
Copy link
Owner

yihui commented Aug 2, 2018

Thanks for the report! I was aware of it when R 3.5.0 was released, but haven't had a chance to investigate it. I think you are correct that the S3 dispatch mechanism has changed somehow.

@kevinushey
Copy link

We had to make a number of changes in RStudio to accommodate this:

rstudio/rstudio#2762

Not sure if it's helpful here, but our (very gross) workaround was to patch the S3 methods table directly:

https://github.com/rstudio/rstudio/pull/2762/files#diff-70c773ec78a4d2867262228e1bbaf1c7R377

@wch
Copy link
Author

wch commented Aug 6, 2018

Yuck, that's unfortunate that there's no way to easily add a temporary S3 method now. EDIT: I mean, unless you have access to the correct environment.

For reference, here's the NEWS item that Jonathan referenced in the issue that Kevin linked to (https://github.com/wch/r-source/blob/tags/R-3-5-0/doc/NEWS.Rd#L312):

S3 method lookup now searches the namespace registry after the top level environment of the calling environment.

I think the problem is that the knitr namespace is a top-level environment (see ?topenv) and so R stops there in its search for S3 methods.

@skranz
Copy link

skranz commented Nov 15, 2018

Just for info: I encountered a variant of this problem. My custom knit_print.data.frame function works if no package is loaded that also implements that function. Yet, if I load the package rmarkdown, which implements a knit_print.data.frame function, my custom function does no longer work, however.

Below is an example code. It can also be tested https://rstudio.cloud/project/137636

# Restart R session if you load / unload rmarkdown

# Custom knit_print.data.frame only works if rmarkdown is not loaded
# library(rmarkdown) 
library(knitr)

knit_print.data.frame = function (x, options = NULL, ...) {
  asis_output("Custom Data Frame Printer!")
}

txt = "
```{r}
data.frame(x=1,y=2)
```"
html = knit2html(text=txt,fragment.only=TRUE)
html

The work around with registerS3method seems to work, however. Maybe one could update the vignette https://cran.r-project.org/web/packages/knitr/vignettes/knit_print.html and note this workaround?

@yihui
Copy link
Owner

yihui commented Nov 16, 2018

Yes, I should definitely update the vignette, and I need some helping hands...

@skranz
Copy link

skranz commented Nov 16, 2018

I don't know whether that counts as a helping hand. But until some other resolution is found, you could e.g. just change lines 40-50 in https://github.com/yihui/knitr/blob/master/vignettes/knit_print.Rmd by the following new lines. Note that if rmarkdown is loaded, I needed to register additionally for the class grouped_df, even though grouped tibbles also have the class data.frame.


S3 generic functions are extensible in the sense that we can define custom methods for them. A method `knit_print.foo()` will be applied to the object that has the class `foo`.

Here is quick example of how we can print data frames as tables:

```{r}
library(knitr)
# define a method for objects of the class data.frame
knit_print.data.frame = function(x, ...) {
  res = paste(c('', '', kable(x)), collapse = '\n')
  asis_output(res)
}
# Also call registerS3method to make sure that knitr finds the custom method 
registerS3method("knit_print", "data.frame", knit_print.data.frame)
registerS3method("knit_print", "grouped_df", knit_print.data.frame)
```
The calls to `registerS3methods` are a workaround for some problems due to recent changes in the S3 dispatch mechanism. Without those calls, knitr may not always find your custom method, depending on which other packages you have loaded. See the discussion in this Github issue: https://github.com/yihui/knitr/issues/1580

@yihui yihui added this to the v1.21 milestone Dec 10, 2018
@yihui yihui closed this as completed in 194c0af Dec 10, 2018
@yihui
Copy link
Owner

yihui commented Dec 10, 2018

Thanks @skranz! That was helpful. I just clarified in the vignette that registerS3method() would be necessary for methods defined in code chunks, and pointed package authors to rstudio/htmltools#108 if they don't want to import knitr but keep it as a Suggests dependency.

@github-actions
Copy link

This old thread has been automatically locked. If you think you have found something related to this, please open a new issue by following the issue guide (https://yihui.org/issue/), and link to this old issue if necessary.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 10, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants