Replacement for keys(Pkg.installed()) with Pkg.dependencies()

I use the current code to pull out installed packages. I want people to just download code for a class, but I’d like to install packages for them. However, Pkg.installed() is deprecated and I cannot figure out how to get the vector of package names from Pkg.dependencies()

# load Pkg package (package manager)
using Pkg

# download package if not installed
if !("DataFrames" in keys(Pkg.installed()))
	println("----- DataFrames does not exist and needs to be installed. Please wait...")
	Pkg.add("DataFrames")
else 
	println("----- DataFrames is downloaded already!")
end

Pkg.dependencies().vals is as far as I got and I’m lost now.

Thanks in advance for any help.

I guess the normal way to go this would be to just give them a Project and Manifest file, and have them to ] activate; instantiate

3 Likes

Okay, thanks. But I would like to just put is at the top of my script. Currently, just re-wrote into a loop such as this and would simply like to plug in a new Pkg.dependencies() here.

# load Pkg package (package manager)
using Pkg

# list packages to install if not installed
my_packages = ["DataFrames", "CategoricalArrays", "GLM", "MixedModels",
"UnicodePlots", "RDatasets", "Statistics", "LinearAlgebra", "Random", 
"Distributions", "Plots", "GLM", "StatsPlots"]

# loop over to install packages
for package in my_packages
	if !(package in keys(Pkg.installed()))
		@info "Installing: " * package
		Pkg.add(package)
	else
		@info "Package " * package * " is installed"
	end
end

Thanks.

One disadvantage of your approach might be, that you can not control much, which version is installed, so to get closer to the Manifest you could also keep a list of versions and add exactly those versions with Pkg.add?

Otherwise you might not be able to ensure that your examples afterwards work with the people you hand this to. The same if the package is installed but a version from 4 years ago.

2 Likes

While I would recommend the Manifest.toml approach mentioned by @nilshg you can get all package names with

[ p.name for p in values(Pkg.dependencies()) ]

but before installing them in the main environment for the users, maybe activate a certain environment to not clutter their main environment? Though then you are even closer to just distributing the Manifest as well.

4 Likes

Okay, then I should look into this approach. I’m not very familiar with the Manifest.toml approach. It’s been awhile since I watched some videos on the package manager and how to handle that. I guess I can distribute that on my GitHub for students and they can close the entire directory. I will try testing a few things out and do some research on Manifest.toml. Thanks for your help!

I’m sorry, one more thing if you can help. I don’t know how to load that package in my loop by using. The string throws off and it’s not a symbol. What do I use? You can see I have it commented out below… Thanks!

# list packages to install if not installed
my_packages = ["LinearAlgebra", "SparseArrays", "Distributions"]

# list current installed packages
cur_packages_installed = [ p.name for p in values(Pkg.dependencies()) ]

# loop over to install packages
for package in my_packages
	if !(package in cur_packages_installed)
		@info "Installing: " * package
		Pkg.add(package)
	else
		@info "Package " * package * " is installed"
		#using Symbol(package)
	end
end

Perhaps @eval in front?

1 Like

Humm, I can’t get that to work. Thanks.

If you really want to do that, you need to write @eval using $(Symbol(package)), but I recommend against doing all this, and using the package manager with a Project.toml instead.

2 Likes

This works perfect! Thanks!

Yes, I will do some reading on Project.toml but I needed a quick fix so I can post these notes and not have people struggle to install packages since they are all new to Julia.

I would also say, take a look at eval, since you want to evaluate the command that you would find in the string `“using Mypackage”), but that makes your code even more complicated.

The Manifest thing is quite easy.

You create a folder o your machine, in that folder you start Julia and do ] activate .

You will see that the blue/package mode format changes a bit and you have an empty Project.toml in that folder. Now (after activating) you add all packages you want.

They will be listed in the Project.toml (and you can even set compass there if you want).

But it also creates the Manifest.toml

Send that to the student/collague. They also create a folder (where they also store their code in later) put the manifest in there, they would activate usually as you do, just…

for the very first time they activate, they have to do ] instantiate. That downloads and precompiles all packages mentioned in the Manifest.

Later if they activate that environment again, they have exactly the same versions of packages again as you specified (set up) the Manifest.

Even better: They also have that if they play around in their general environment with other stuff. Locally in the environment you sent them, they are always back to the package versions you specified (unless they do ] update of course).

I feel these commands are easier and safer than your idea here.

1 Like

Got it, this gives me the steps I was going to look for. I’m sure you guys are right, but it’s still more complex for students on their first time or two looking at Julia code. It will suffice for now but I’ll write instructions and move to this model later in the semester as I do like it can fix the package versions which change rapidly right now. Thanks a lot!

My version in very short would be: Give them a Pluto notebook. Then they just have to add Pluto – And all I wrote is done by Pluto automatically. Some argue that Pluto notebooks load a bit slow for that reason, but well…

1 Like

Great to know, thanks! I’ve tried Pluto once or twice.

If you include a Project.toml and Manifest.toml with your script you could automatically activate and instantiate it using Pkg at the start of the script. There are reasons not to do this also, but I would say this is probably a nicer “automatic” solution where your students have the same steps as in your example.

import Pkg
Pkg.activate(@__DIR__)  # Could also just run the script with --project
Pkg.instantiate()       # If already instantiated this is quick

using PkgA, PkgB, ...
1 Like

Thanks! I should try this and see how it works with a few students.