3

I want to unload package and all its dependancies. But I do not want to unload packages that I have previously loaded with library() or packages that are dependencies of packages that I loaded with library(). How can I do it?

vasili111
  • 6,032
  • 10
  • 50
  • 80
  • Maybe like this? https://stackoverflow.com/questions/6979917/how-to-unload-a-package-without-restarting-r – M.Viking Sep 21 '19 at 23:58
  • @M.Viking I have already looked at it but did not found "But I do not want to unload packages that I have previously loaded with `library()` or packages that are dependencies of packages that I loaded with `library()`." – vasili111 Sep 22 '19 at 00:05

1 Answers1

0

AFAIK, you can't automatically differentiate between packages that are Depends dependencies of other attached packages and the ones you load with library.

EDIT: I've updated my answer to the question linked above, showing a possible alternative to track explicit usages of library. It would be possible to use it to compute attached in the code below instead of using search.

Here's one possibility I found using the remotes package:

transitive_deps <- function(package, acc = character()) {
    direct_deps <- remotes::local_package_deps(system.file(package = package))
    filtered_deps <- setdiff(direct_deps, acc)

    if (length(filtered_deps) == 0L) {
        package
    }
    else {
        acc <- union(acc, filtered_deps)
        for (dep in filtered_deps) { acc <- union(acc, transitive_deps(dep, acc)) }
        union(package, acc)
    }
}

unload_deep <- function(package) {
    # find all transitive dependencies of the package
    deps <- transitive_deps(package) # alternatively: remotes::package_deps(package)$package
    # do not unload base packages
    deps <- setdiff(deps, installed.packages(priority = "base")[, "Package"])
    # do not unload packages explicitly attached with library()
    attached <- search()
    deps <- setdiff(deps, substring(attached, unlist(gregexpr(":", attached)) + 1L)) # remove "package:" prefix
    # first unload provided package
    tryCatch(unloadNamespace(package), error = function(...) {
        package <- paste0("package:", package)
        if (package %in% search()) {
            # at least try to detach it if some other package imports it
            detach(package, character.only = TRUE)
        }
    })

    deps <- setdiff(deps, package)
    # try to unload remaining
    while (length(deps) > 0L) {
        unloaded_one <- FALSE
        for (dep in deps) {
            flag <- try(unloadNamespace(dep), silent = TRUE)
            if (!inherits(flag, "try-error")) {
                unloaded_one <- TRUE
                break
            }
        }

        if (unloaded_one) {
            deps <- setdiff(deps, dep)
        }
        else {
            # could not unload any, must be used by other unrelated packages
            break
        }
    }
    # return
    invisible()
}

You need to pass a character in package. Also, as mentioned in the code comments, you can use remotes::package_deps(package)$package instead of the transitive_deps helper. I think the former already filters base packages, but I believe it checks online each time you use it.

The transitive_deps helper is far from perfect, it just tries to find direct dependencies (which is what remotes::local_package_deps returns) recursively, hopefully avoiding some repetition by using acc to accumulate already seen packages.

The order in which packages are unloaded is important, because you cannot unload a package that is imported by another currently loaded package. Unfortunately the order in deps is not always perfect for that, that's why there's a try in the loop, to skip problematic packages until all dependent packages have been unloaded.

Alexis
  • 4,950
  • 1
  • 18
  • 37