Question

Hi I need to create a list of all functions from all installed packages with their description from "help()" function. Now I have this code for create list of installed packages:

z=Array[collect(keys(Pkg.installed()))]
p=[z[1],"Base","Core"]

now in cycle I can read all functions from packages. But this error occur:

whos(p[1])
ERROR: no method whos(ASCIIString,)

how to use names in whos() function if they are stored as string?

the second problem I have is similar.

from all these functions I can read description by help() and store it also in array. How to do it?

Was it helpful?

Solution

There are several issues here. First, as you noticed, the whos function does not have a method defined for ASCIIString arguments. Looking at the output of methods(whos) reveals that it requires an argument of type Module.

julia> methods(whos)
# 4 methods for generic function "whos":
whos(m::Module,pattern::Regex) at show.jl:932
whos() at show.jl:939
whos(m::Module) at show.jl:940
whos(pat::Regex) at show.jl:941

julia> typeof(Base)
Module

julia> whos(Base)
⋮

You have a string with the name of a module, like "Base". This is where eval comes in. You must evaluate the module's name (as a symbol) to get the actual Module itself. So, for example, eval(symbol("Base")) will first convert "Base" to the symbol :Base (which is still just a name, but it's now a name that Julia will recognize as an identifier), and then evaluate it to get the real Module Base.

But you can't get a Module (or look inside it) without first importing it. We have to import each module before it's name is defined. Doing so programmatically with each of the names from Pkg.installed() will take a bit of metaprogramming. This, too, is tricky, because the import statement is restricted in where it can appear (must be in a top-level scope) and how it is parsed (must be on its own line, and interpolation within a quote block is not supported). So unfortunately this solution can't be put into a function, for example (there are ways around this: you could manually include each package's top-level file manually, but that requires a bit more pathing logic).

Instead of printing everything out, let's aggregate the results into a nested dictionary, where the top-level dictionary will have the packages as its keys, and the sub-dictionaries will have the exported names of each module as their keys.

julia> d = Dict{Symbol,Any}()
       for pkg in map(symbol, keys(Pkg.installed()))
         eval(Expr(:import, pkg)) # Manually put the import statement into an Expr
         ks = names(eval(pkg))
         vs = zeros(UTF8String,length(ks))
         for (i,k) in enumerate(ks)
           # packages can export undefined names, which would throw an error
           try
             vs[i] = summary(eval(:($pkg.$k)))
           end
         end
         d[pkg] = Dict(ks,vs)
       end

julia> d[:Gadfly][:plot]
"Function"

Since we're iterating through lots of modules, I'm using import instead of using to prevent clashes between the exported names. That means that when referencing each exported name, I need to fully qualify it with the PackageName. That's what eval(:($pkg.$k)) is doing, via interpolation. If a package exports a name but leaves it undefined, accessing it would throw an error. So it must be wrapped in a try block.

You can change the summary command to be whatever you want, but unfortunately packages cannot hook into the help system (yet), so asking for the help text via sprint(help, …) won't give you anything much more informative than summary.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top