How to View the Source Code For a Function

How can I view the source code for a function?

UseMethod("t") is telling you that t() is a (S3) generic function that has methods for different object classes.

The S3 method dispatch system

For S3 classes, you can use the methods function to list the methods for a particular generic function or class.

> methods(t)
[1] t.data.frame t.default t.ts*

Non-visible functions are asterisked
> methods(class="ts")
[1] aggregate.ts as.data.frame.ts cbind.ts* cycle.ts*
[5] diffinv.ts* diff.ts kernapply.ts* lines.ts
[9] monthplot.ts* na.omit.ts* Ops.ts* plot.ts
[13] print.ts time.ts* [<-.ts* [.ts*
[17] t.ts* window<-.ts* window.ts*

Non-visible functions are asterisked

"Non-visible functions are asterisked" means the function is not exported from its package's namespace. You can still view its source code via the ::: function (i.e. stats:::t.ts), or by using getAnywhere(). getAnywhere() is useful because you don't have to know which package the function came from.

> getAnywhere(t.ts)
A single object matching ‘t.ts’ was found
It was found in the following places
registered S3 method for t from namespace stats
namespace:stats
with value

function (x)
{
cl <- oldClass(x)
other <- !(cl %in% c("ts", "mts"))
class(x) <- if (any(other))
cl[other]
attr(x, "tsp") <- NULL
t(x)
}
<bytecode: 0x294e410>
<environment: namespace:stats>

The S4 method dispatch system

The S4 system is a newer method dispatch system and is an alternative to the S3 system. Here is an example of an S4 function:

> library(Matrix)
Loading required package: lattice
> chol2inv
standardGeneric for "chol2inv" defined from package "base"

function (x, ...)
standardGeneric("chol2inv")
<bytecode: 0x000000000eafd790>
<environment: 0x000000000eb06f10>
Methods may be defined for arguments: x
Use showMethods("chol2inv") for currently available ones.

The output already offers a lot of information. standardGeneric is an indicator of an S4 function. The method to see defined S4 methods is offered helpfully:

> showMethods(chol2inv)
Function: chol2inv (package base)
x="ANY"
x="CHMfactor"
x="denseMatrix"
x="diagonalMatrix"
x="dtrMatrix"
x="sparseMatrix"

getMethod can be used to see the source code of one of the methods:

> getMethod("chol2inv", "diagonalMatrix")
Method Definition:

function (x, ...)
{
chk.s(...)
tcrossprod(solve(x))
}
<bytecode: 0x000000000ea2cc70>
<environment: namespace:Matrix>

Signatures:
x
target "diagonalMatrix"
defined "diagonalMatrix"

There are also methods with more complex signatures for each method, for example

require(raster)
showMethods(extract)
Function: extract (package raster)
x="Raster", y="data.frame"
x="Raster", y="Extent"
x="Raster", y="matrix"
x="Raster", y="SpatialLines"
x="Raster", y="SpatialPoints"
x="Raster", y="SpatialPolygons"
x="Raster", y="vector"

To see the source code for one of these methods the entire signature must be supplied, e.g.

getMethod("extract" , signature = c( x = "Raster" , y = "SpatialPolygons") )

It will not suffice to supply the partial signature

getMethod("extract",signature="SpatialPolygons")
#Error in getMethod("extract", signature = "SpatialPolygons") :
# No method found for function "extract" and signature SpatialPolygons

Functions that call unexported functions

In the case of ts.union, .cbindts and .makeNamesTs are unexported functions from the stats namespace. You can view the source code of unexported functions by using the ::: operator or getAnywhere.

> stats:::.makeNamesTs
function (...)
{
l <- as.list(substitute(list(...)))[-1L]
nm <- names(l)
fixup <- if (is.null(nm))
seq_along(l)
else nm == ""
dep <- sapply(l[fixup], function(x) deparse(x)[1L])
if (is.null(nm))
return(dep)
if (any(fixup))
nm[fixup] <- dep
nm
}
<bytecode: 0x38140d0>
<environment: namespace:stats>

Functions that call compiled code

Note that "compiled" does not refer to byte-compiled R code as created by the compiler package. The <bytecode: 0x294e410> line in the above output indicates that the function is byte-compiled, and you can still view the source from the R command line.

Functions that call .C, .Call, .Fortran, .External, .Internal, or .Primitive are calling entry points in compiled code, so you will have to look at sources of the compiled code if you want to fully understand the function. This GitHub mirror of the R source code is a decent place to start. The function pryr::show_c_source can be a useful tool as it will take you directly to a GitHub page for .Internal and .Primitive calls. Packages may use .C, .Call, .Fortran, and .External; but not .Internal or .Primitive, because these are used to call functions built into the R interpreter.

Calls to some of the above functions may use an object instead of a character string to reference the compiled function. In those cases, the object is of class "NativeSymbolInfo", "RegisteredNativeSymbol", or "NativeSymbol"; and printing the object yields useful information. For example, optim calls .External2(C_optimhess, res$par, fn1, gr1, con) (note that's C_optimhess, not "C_optimhess"). optim is in the stats package, so you can type stats:::C_optimhess to see information about the compiled function being called.

Compiled code in a package

If you want to view compiled code in a package, you will need to download/unpack the package source. The installed binaries are not sufficient. A package's source code is available from the same CRAN (or CRAN compatible) repository that the package was originally installed from. The download.packages() function can get the package source for you.

download.packages(pkgs = "Matrix", 
destdir = ".",
type = "source")

This will download the source version of the Matrix package and save the corresponding .tar.gz file in the current directory. Source code for compiled functions can be found in the src directory of the uncompressed and untared file. The uncompressing and untaring step can be done outside of R, or from within R using the untar() function. It is possible to combine the download and expansion step into a single call (note that only one package at a time can be downloaded and unpacked in this way):

untar(download.packages(pkgs = "Matrix",
destdir = ".",
type = "source")[,2])

Alternatively, if the package development is hosted publicly (e.g. via GitHub, R-Forge, or RForge.net), you can probably browse the source code online.

Compiled code in a base package

Certain packages are considered "base" packages. These packages ship with R and their version is locked to the version of R. Examples include base, compiler, stats, and utils. As such, they are not available as separate downloadable packages on CRAN as described above. Rather, they are part of the R source tree in individual package directories under /src/library/. How to access the R source is described in the next section.

Compiled code built into the R interpreter

If you want to view the code built-in to the R interpreter, you will need to download/unpack the R sources; or you can view the sources online via the R Subversion repository or Winston Chang's github mirror.

Uwe Ligges's R news article (PDF) (p. 43) is a good general reference of how to view the source code for .Internal and .Primitive functions. The basic steps are to first look for the function name in src/main/names.c and then search for the "C-entry" name in the files in src/main/*.

Why can't I see the source code of a function within a function in R?

It may be a function that is defined in the package but not exported. For example:

> usethis::use_template
function (template, save_as = template, data = list(), ignore = FALSE,
open = FALSE, package = "usethis")
{
template_contents <- render_template(template, data, package = package)
new <- write_over(proj_path(save_as), template_contents)
if (ignore) {
use_build_ignore(save_as)
}
if (open && new) {
edit_file(proj_path(save_as))
}
invisible(new)
}
<bytecode: 0x00000168a175c3b8>
<environment: namespace:usethis>

You can see a call to the function render_template(). However, if you try to call that function directly:

> usethis::render_template
Error: 'render_template' is not an exported object from 'namespace:usethis'

It doesn't work! To understand why, you can look at the source code. You should see that before the definition of use_template(), there is a big block of special comments that will become the documentation. However, render_template() is defined just below, without any comment or documentation. This is because use_template() is made available to the package user, whereas render_template() is meant for internal use only.

If you really want to see the code of that function, you can use a triple colon:

> usethis:::render_template
function (template, data = list(), package = "usethis")
{
template_path <- find_template(template, package = package)
strsplit(whisker::whisker.render(read_utf8(template_path),
data), "\n")[[1]]
}
<bytecode: 0x00000168a33d7b98>
<environment: namespace:usethis>

This is practical to find the source code of a function, but you shouldn't use it to call the function: there is usually a reason for it to be hidden.

How to view the source code of an R function

Since it is a function within the vars package, you can look at the source code the same way that you did for irf.varest.

library(vars)
vars:::.irf

Output

function (x, impulse, response, y.names, n.ahead, ortho, cumulative) 
{
if ((class(x) == "varest") || (class(x) == "vec2var")) {
if (ortho) {
irf <- Psi(x, nstep = n.ahead)
}
else {
irf <- Phi(x, nstep = n.ahead)
}
}
else if ((class(x) == "svarest") || (class(x) == "svecest")) {
irf <- Phi(x, nstep = n.ahead)
}
dimnames(irf) <- list(y.names, y.names, NULL)
idx <- length(impulse)
irs <- list()
for (i in 1:idx) {
irs[[i]] <- matrix(t(irf[response, impulse[i], 1:(n.ahead +
1)]), nrow = n.ahead + 1)
colnames(irs[[i]]) <- response
if (cumulative) {
if (length(response) > 1)
irs[[i]] <- apply(irs[[i]], 2, cumsum)
if (length(response) == 1) {
tmp <- matrix(cumsum(irs[[i]]))
colnames(tmp) <- response
irs[[i]] <- tmp
}
}
}
names(irs) <- impulse
result <- irs
return(result)
}
<bytecode: 0x7ff5bbb40138>
<environment: namespace:vars>

show source code for function in R

You have to ask using the corresponding method used by the function. Try this:

princomp # this is what you did without having a good enough answer
methods(princomp) # Next step, ask for the method: 'princomp.default'
getAnywhere('princomp.default') # this will show you the code

The code you are looking for is:

function (x, cor = FALSE, scores = TRUE, covmat = NULL, subset = rep(TRUE, 
nrow(as.matrix(x))), ...)
{
cl <- match.call()
cl[[1L]] <- as.name("princomp")
if (!missing(x) && !missing(covmat))
warning("both 'x' and 'covmat' were supplied: 'x' will be ignored")
z <- if (!missing(x))
as.matrix(x)[subset, , drop = FALSE]
if (is.list(covmat)) {
if (any(is.na(match(c("cov", "n.obs"), names(covmat)))))
stop("'covmat' is not a valid covariance list")
cv <- covmat$cov
n.obs <- covmat$n.obs
cen <- covmat$center
}
else if (is.matrix(covmat)) {
cv <- covmat
n.obs <- NA
cen <- NULL
}
else if (is.null(covmat)) {
dn <- dim(z)
if (dn[1L] < dn[2L])
stop("'princomp' can only be used with more units than variables")
covmat <- cov.wt(z)
n.obs <- covmat$n.obs
cv <- covmat$cov * (1 - 1/n.obs)
cen <- covmat$center
}
else stop("'covmat' is of unknown type")
if (!is.numeric(cv))
stop("PCA applies only to numerical variables")
if (cor) {
sds <- sqrt(diag(cv))
if (any(sds == 0))
stop("cannot use cor=TRUE with a constant variable")
cv <- cv/(sds %o% sds)
}
edc <- eigen(cv, symmetric = TRUE)
ev <- edc$values
if (any(neg <- ev < 0)) {
if (any(ev[neg] < -9 * .Machine$double.eps * ev[1L]))
stop("covariance matrix is not non-negative definite")
else ev[neg] <- 0
}
cn <- paste("Comp.", 1L:ncol(cv), sep = "")
names(ev) <- cn
dimnames(edc$vectors) <- if (missing(x))
list(dimnames(cv)[[2L]], cn)
else list(dimnames(x)[[2L]], cn)
sdev <- sqrt(ev)
sc <- if (cor)
sds
else rep(1, ncol(cv))
names(sc) <- colnames(cv)
scr <- if (scores && !missing(x) && !is.null(cen))
scale(z, center = cen, scale = sc) %*% edc$vectors
if (is.null(cen))
cen <- rep(NA_real_, nrow(cv))
edc <- list(sdev = sdev, loadings = structure(edc$vectors,
class = "loadings"), center = cen, scale = sc, n.obs = n.obs,
scores = scr, call = cl)
class(edc) <- "princomp"
edc
}
<environment: namespace:stats>

I think this what you were asking for.

How to find out if (the source code of) a function contains a call to a method from a specific module?

You can replace the random module with a mock object, providing custom attribute access and hence intercepting function calls. Whenever one of the functions tries to import (from) random it will actually access the mock object. The mock object can also be designed as a context manager, handing back the original random module after the test.

import sys


class Mock:
import random
random = random

def __enter__(self):
sys.modules['random'] = self
self.method_called = False
return self

def __exit__(self, *args):
sys.modules['random'] = self.random

def __getattr__(self, name):
def mock(*args, **kwargs):
self.method_called = True
return getattr(self.random, name)
return mock


def uses_module(func):
with Mock() as m:
func()
return m.method_called

Variable module name

A more flexible way, specifying the module's name, is achieved by:

import importlib
import sys


class Mock:
def __init__(self, name):
self.name = name
self.module = importlib.import_module(name)

def __enter__(self):
sys.modules[self.name] = self
self.method_called = False
return self

def __exit__(self, *args):
sys.modules[self.name] = self.module

def __getattr__(self, name):
def mock(*args, **kwargs):
self.method_called = True
return getattr(self.module, name)
return mock


def uses_module(func):
with Mock('random') as m:
func()
return m.method_called

Finding the source code for built-in Python functions?

Since Python is open source you can read the source code.

To find out what file a particular module or function is implemented in you can usually print the __file__ attribute. Alternatively, you may use the inspect module, see the section Retrieving Source Code in the documentation of inspect.

For built-in classes and methods this is not so straightforward since inspect.getfile and inspect.getsource will return a type error stating that the object is built-in. However, many of the built-in types can be found in the Objects sub-directory of the Python source trunk. For example, see here for the implementation of the enumerate class or here for the implementation of the list type.



Related Topics



Leave a reply



Submit