Skip to content

Instantly share code, notes, and snippets.

@BrianDiggs
Last active July 7, 2023 18:47
Show Gist options
  • Save BrianDiggs/63bc48cf2955e0fd38f0df24fa4e1802 to your computer and use it in GitHub Desktop.
Save BrianDiggs/63bc48cf2955e0fd38f0df24fa4e1802 to your computer and use it in GitHub Desktop.
A script which should find packages which you have loaded which are not used in a script. It needs to be run in a clean session because there is no way to verify that the state of your current session is the same as what the script would create. It assumes that packages are only loaded with either library or require, which is good practice anywa…
# Define the file to test in the line below. That is the only per-run configuration needed.
fileToTest <- "Plot.R"
# Get the parse data for the file
parseData <- getParseData(parse(fileToTest), includeText = TRUE)
# Extract all the function calls and keep a unique list of them.
functionCalls <- unique(parseData[parseData$token == "SYMBOL_FUNCTION_CALL", "text"])
# Look for any calls to `library` or `require` and go two steps up the
# call tree to find the complete call (with arguments).
libraryCalls <- parseData[parseData$token == "SYMBOL_FUNCTION_CALL" & parseData$text %in% c("library", "require"),]
libraryCalls <- parseData[parseData$id %in% libraryCalls$parent,]
libraryCalls <- parseData[parseData$id %in% libraryCalls$parent,]
libraryCalls <- libraryCalls$text
# Execute all the library/require calls to attach them to this session
eval(parse(text = libraryCalls))
# For each function called,
# * Use `getAnywhere` to find out where it is found. That information is in a character
# vector which is the `where` component of the returned list.
# * From that vector of locations, keep only the ones starting with "package:",
# getting rid of those starting with "namespace:".
# * Take the first one of these which should be the first package that the
# function is found in and thus would be the one used.
names(functionCalls) <- functionCalls
matchPkg <- vapply(functionCalls,
FUN = (\(f) grep("^package:", getAnywhere(f)$where, value = TRUE)[1]),
FUN.VALUE = character(1))
# get a list of all packages from the search path, keep only those that are
# actually packages (not .GlobalEnv, Autoloads, etc.), ignore those that are
# automatically attached (base, methods, datasets, utils, grDevices, graphics, stats),
# and then see of those which ones did not show up in the list of packages used
# by the functions.
packages <- search()
packages <- grep("^package:", packages, value = TRUE)
packages <- setdiff(packages, c("package:base", "package:methods", "package:datasets", "package:utils", "package:grDevices", "package:graphics", "package:stats"))
packages <- setdiff(packages, unique(matchPkg))
# Report results
if(length(packages) > 0) {
cat("Unused packages: \n");
print(packages)
} else {
cat("No unused packages found.\n")
}
@BrianDiggs
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment