Tool to tell if a package has unused dependencies or using unused packages

When developing large projects, I sometimes 1) lose track of what packages I ]added but am not using any longer, and 2) what packages I’m using but not actually making use of.

Is there a tool to detect these kind of situations? Or how do you deal with that? Right now I’m just manually looking in the code to see what I need and what I don’t and I try (with Revise) to see if things fail when I remove stuff.

2 Likes

First case (check packages that project Proj is actually using) should be easy:

using Proj
using TOML
deps = TOML.parsefile("Project.toml")["deps"]
for d in deps
    if !isdefined(Proj, Symbol(d))
    println("$d is not being used")
end

This is assuming the simple case in which Proj is only a module with the same name. Otherwise, you should navigate through the different modules of the project.

For the second case (check packages that are used/imported, but not really “used”), I have no idea.

2 Likes

That should do it, although it should be possible to accomplish purely syntactically. If import X or using X doesn’t appear anywhere in the package code then X is not used. Parsing fully correctly is hard, of course, but Julia’s own parser and CSTParser both do it already.

We should add that to the VS Code linter.

8 Likes

Thanks all, I’ll use that for the first part and wait for some linter magic for the second.

Start removing packages one by one, run all of your code and see when things break :laughing:

But seriously, thank you for asking this. I’m currently in a similar position, and have been trying to figure this out (the above method could take up to 16 hours per test, and I’ve got 6 or 7 packages I’m not sure of…)

2 Likes

Would the linter address the second issue (using X but nothing from X is ever called)?

If not, is there currently a way of addressing this?

Thank you.

3 Likes

I don’t think a linter should warn about the latter, because you might only have that call there to run the init function in the package, or the package might add a method to a function say in Base. In both cases that using statement does something, even though it might appear unnecessary.

1 Like

Well, it cold warn about it, and let the user choose. I’d wager that in most cases it’s just a unintended using

1 Like

Just tried this now, had to fix a few things, hopefully this helps:

using Proj
using Pkg.TOML
deps = TOML.parsefile("Project.toml")["deps"]
remove = String[]
for d in keys(deps)
    if !isdefined(Proj, Symbol(d))
        push!(remove, d)
    end
end
println("rm ", join(remove, " "))
2 Likes

Sorry to resurrect this old topic but I noted a corner case:

Imagine that in MyPkg.jl I do a

using Printf: @printf

then

julia> isdefined(MyPkg,:Printf)
false

and the remove would incorrectly (at least to me) suggest to remove Printf. Any way to programmatically check for unused deps?

2 Likes

FYI, I added the check for unused dependencies in Aqua.jl (Auto QUality Assurance for Julia packages). Either Aqua.test_all(YourPackage; stale_deps = true) or Aqua.test_stale_deps(YourPackage) should work.

4 Likes