How to load packages in a for loop?

I have a Julia project that is growing in number of files. To break it into smaller and easier-to-manage files, I want to have a .jl where I keep a list of all packages required by the project.

Then, I will use a for loop to iterate over the list. For each package in the list, an if statement will check if the package is installed. If true, then just load the file with using. If false, then install the package and load it.

Here, I’m assuming the project environment has been activated. Here’s the code I thought it was going to work.

# This file is called required_packages.jl.  
#I will include this package into a main.jl file using the include() command
# include('path/to/required_packages.jl')

const required_packages = [
    "DataFrames",
    "Dates",
    "Distributions",
]

# Install package if necessary. Load all packages.
for pkg in required_packages
    if pkg ∉ keys(Pkg.installed())
        Pkg.add(pkg)
    end
    using $pkg
end

The error message I get is: LoadError: TypeError: in using, expected Symbol, got a value of type Expr . I did try using the function Symbol() instead of $, but it didn’t work either. All help will be appreciated.

You can do this with eval, e.g.

julia> pkg = :Test
:Test

julia> @eval using $pkg

julia> Test
Test

but it’s unclear what problem this solves that is not better solved with some combination of Project.toml, Manifest.toml, Pkg.activate, and Pkg.instantiate.

1 Like

Hey, @GunnarFarneback, thanks for your answer. What is the combination of Project.toml , Manifest.toml , Pkg.activate , and Pkg.instantiate you would use to solve this problem? For me, using the for loop to check if a package is installed and to load package is the “shortest” and “cleanest” answer.

To be honest I don’t understand the use case but generally speaking I would activate an environment with a saved Project.toml rather than repeatedly Pkg.adding packages. If the purpose is just to initialize a number of new environments it seems easier to copy the Project.toml to each environment. These tools can’t help with a using loop but at this point I would just write out the usings.

2 Likes

The use case is for a project that I will have to share with people. By doing what I wrote in the code, I hope to make it easier for people to have all the packages the code requires to their environment. If the package is already installed, then all it does is to load the package.

When you write

I would activate an environment with a saved Project.toml rather than repeatedly Pkg.add ing packages.

Is there a way to read the packages from the Project.toml and automatically install the packages and load them?

If you want to share a project with other people, you should be working in the environment of the project, i.e. activate it, and Pkg.add all packages it needs. Then you commit both the Project.toml and Manifest.toml together with the code.

When other people want to use it, they activate the project and run Pkg.instantiate. This ensures not only that they have all the same packages you had, but also the exact same versions.

If your code is designed to be run from scripts, you can start them off with

using Pkg
Pkg.activate(@__DIR__)
Pkg.instantiate

for a robustly reproducible workflow, at least if they are using the same Julia version as you.

3 Likes

May be?
import Pkg; Pkg.activate("."); Pkg.instantiate();

and look at this:
https://pkgdocs.julialang.org/v1/api/#Pkg.instantiate

Oh, wow! Thanks for this. I’ll certainly look into it.

Hey, @andrey2185, thanks for this.

1 Like