Get the original version of a package function, even if reassigned

Suppose I replace a package function, for example knitr:::sub_ext . (Note: I am particularly interested in where this is an internal function, i.e. only ::: , not :: , but the same answer may work for both).

 library(knitr) my.sub_ext <- function (x, ext) { return("I'm in your package stealing your functions D:") } # replace knitr:::sub_ext with my.sub_ext knitr <- asNamespace('knitr') unlockBinding('sub_ext', knitr) assign('sub_ext', my.sub_ext, knitr) lockBinding('sub_ext', knitr) 

Question: is there a way to get the original knitr:::sub_ext after I have done this? Preferably without reloading the package?


(I know that some people want to know why I would like to do this, that's all. No reading is required for the question). I fixed some functions in such packages (not really sub_ext function ...):

 original.sub_ext <- knitr:::sub_ext new.sub_ext <- function (x, ext) { # some extra code that does something first, eg x <- do.something.with(x) # now call the original knitr:::sub_ext original.sub_ext(x, ext) } # now set knitr:::sub_ext to new.sub_ext like before. 

I agree that this is not a good idea at all (in most cases, these are quick fixes until the changes turn into CRAN, or they are โ€œfeature requestsโ€ that will never be approved, as they are case-specific).

The problem with the above is that I accidentally execute it twice (for example, at the top of the script, which I run twice without restarting R in between), the second time original.sub_ext is actually the previous new.sub_ext as opposed to the real one knitr:::sub_ext , so I get infinite recursion.

Since sub_ext is an internal function (I would not call it directly, but functions from knitr like knit all called it internally), I cannot hope to change all the functions that call sub_ext , call new.sub_ext manually, therefore, the approach of replacing the definition in package namespace.

+6
source share
1 answer

When you execute assign('sub_ext', my.sub_ext, knitr) , you irrevocably overwrite the value previously associated with sub_ext with the value my.sub_ext . If you first ran the original value, it is not difficult to reset when you are done:

 library(knitr) knitr <- asNamespace("knitr") ## Store the original value of sub_ext .sub_ext <- get("sub_ext", envir = knitr) ## Overwrite it with your own function my.sub_ext <- function (x, ext) "I'm in your package stealing your functions D:" assignInNamespace('sub_ext', my.sub_ext, knitr) knitr:::sub_ext("eg.csv", "pdf") # [1] "I'm in your package stealing your functions D:" ## Reset when you're done assignInNamespace('sub_ext', .sub_ext, knitr) knitr:::sub_ext("eg.csv", "pdf") # [1] "eg.pdf" 

Alternatively , if you simply add lines of code to what you already have, you can add this code with trace() . What's nice about trace() is that when you are done, you can use untrace() to return the body of the function to its original form:

 trace(what = "mean.default", tracer = quote({ a <- 1 b <- 2 x <- x*(a+b) }), at = 1) mean(1:2) # Tracing mean.default(1:2) step 1 # [1] 4.5 untrace("mean.default") # Untracing function "mean.default" in package "base" mean(1:2) # [1] 1.5 

Note that if the function you are tracing is in the namespace, you want to use the trace() where argument, passing it the name of some other (exported) function that shares the traceability function namespace. Thus, to trace the outstanding function in the knitr namespace, you can set where=knit

+4
source

Source: https://habr.com/ru/post/944674/


All Articles