Modify package function
Asked Answered
D

3

37

This is my first time trying this, so apologies if I get the terminology wrong. There's a package (snapCGH on bioconductor) that I'm using. I call one function, plotSegmentedGenome, which in turn calls genomePlot. Both functions are within the snapCGH namespace:

> environment(plotSegmentedGenome)
<environment: namespace:snapCGH>
> environment(genomePlot)
<environment: namespace:snapCGH>

I want to modify genomePlot. My first attempt consisted of simply running

> genomePlot

So I could get the code and create a new function from that. Two minor things I'd like to change. It labels the x-axis in mb whereas Id like it bp (so multiplying the labels but no the values plotted by 1000000). Secondly, it labels the x-axis additionally with the chromosome in a jarring red. Id like to remove that label totally. Saved this attempt as genomePlot.R and sourced it.

If I then run plotSegmentedGenome then nothing changes. So I presume it's still using the genomePlot function within its namespace. If I create my own copy of plotSegmentedGenome in the global env, then I get an error "Error: object 'chrominfo.Mb' not found". This is one of the arguments for plotSegmentedGenome which I suppose is created in the env.

I hope this makes sense and that there's a solution that isnt embarrisingly easy :)

ps: I read this, http://www.r-bloggers.com/environments-in-r/, which was interesting but not quite detailed enough to let me figure out how to do fix this. If only I could write

snapCGH$genomePlot <- customGenomePlot
or 
snapCGH:::genomePlot <- customGenomePlot

Update: Based on Redirect/intercept function calls within a package function

I tried

 library(proto)
  plotSegmentedGenome <- with(proto(environment( plotSegmentedGenome),  plotSegmentedGenome = snapCGH:: plotSegmentedGenome, genomePlot = genomePlot), my_genomePlot)

But I still received the error

 Error in plotSegmentedGenome(SegInfo.Hom.runDNAcopy, array = array, chrom.to.plot = 19,  : 
   object 'chrominfo.Mb' not found
  > 

It is calling my version of the function at least though, as it prints the message("its alive!") I stuck in my_genomePlot

Deragon answered 20/6, 2014 at 16:22 Comment(2)
reassignInPackage in package R.utils is worth a lookPileus
Also mockery::stub(snapCGH:::genomePlot, 'customGenomePlot') should work.Donnenfeld
F
44

I finally found a solution that should work in all situations!

environment(customGenomePlot) <- asNamespace('snapCGH')
assignInNamespace("genomePlot", customGenomePlot, ns = "snapCGH")

The call to environment() assures that the function will be able to call other hidden functions from the package.

The call to assignInNamespace() assures that other functions from the package will call your updated version of the function.

It is possible that in certain situation, you need only one of these, but in general you need both. I struggled to find this general solution, found many other which are not working in some cases, like this (need opposite order), or this (misses the second part), or this (throws the error "cannot add bindings to a locked environment").

Fluctuant answered 4/10, 2019 at 15:2 Comment(0)
A
28

Do you want to edit the function permanently? or just temporarily?

If you want a permanent change then you should get a source version of the package, modify the source code, then install the package from your changed source. Also consider contributing the changes back to the author/maintainer.

If you want a temporary change then try the command:

trace(genomePlot, edit=TRUE)

This will open an editor (you can also specify which editor if you don't want to use the default) with the source code and let you edit it. When you save and exit from the editor it will save your edited version in place of the original taking care of environments/namespaces/etc.

This change will only last for the current R session or until you call the untrace function.

Allegedly answered 20/6, 2014 at 17:0 Comment(0)
S
4

Try something like this:

library(snapCGH)
my.genomePlot <- function (...) 
{

 ## your custom code goes here
  message("in my genomePlot")
}

unlockBinding("genomePlot", as.environment("package:snapCGH"))
assign("genomePlot ", my.genomePlot , as.environment("package:snapCGH"))
lockBinding("genomePlot ", as.environment("package:snapCGH"))    
Standfast answered 20/6, 2014 at 16:51 Comment(1)
I tried this, but the assign() throws an error "cannot add bindings to a locked environment". I finally found another solution!Fluctuant

© 2022 - 2024 — McMap. All rights reserved.