Using an already-loaded module in 0.7

When doing the following in 0.7:

module Foo end
using Foo

This yields:

ERROR: ArgumentError: Module Foo not found in current path.
Run `Pkg.add("Foo")` to install the Foo package.

I found I could do using Main.Foo instead, but is there a way to avoid this? My usecase is CxxWrap.jl, where modules are created using a function call instead of by regularly loading a package.

That module is defined in Main, which is the module where one executes all the expressions of the REPL, and so on. Just call it by Main.Foo or simply .Foo if you are still in Main.

To check on which module are you use @__module__.

See also: Creating modules in Julia v0.7-alpha - #3 by Roger-luo

using .Foo is unambiguous and will just work in v0.6 and v0.7.

3 Likes

Thanks, missed the other topic about this. So my next question is, is it a good idea to still load modules in Main then? There is now a __toplevel__ module in Base:

# This is used as the current module when loading top-level modules.
# It has the special behavior that modules evaluated in it get added
# to the loaded_modules table instead of getting bindings.
baremodule __toplevel__
using Base
end

But when I use that (i.e. @eval Base.__toplevel__ module Foo end), the module is invisible. Since the default behavior is no longer to load modules into Main, it seems that for CxxWrap I should also avoid doing that.

The normal code-loading logic requires (!) modules to (seem to) be defined by files in LOAD_PATH etc. I suspect that is a mistake, but in any case one can get around this as follows:

# reverse lookup of pkg id 
# (We can't simply use the module as an index because it's not bound)
function findmod(f::Symbol)
    for (u,v) in Base.loaded_modules
        (Symbol(v) == f) && return u
    end
    nothing
end

# substitute for `import Foo`:
Foo = Base.loaded_modules[findmod(:Foo)]

# substitute for `using Foo`:
ccall(:jl_module_using, Cvoid, (Any, Any), MyModule,
      Base.loaded_modules[findmod(:Foo)])

1 Like

Messing around with Base.__toplevel__ or Base.loaded_modules is not recommended and may break at any time in a 1.x release. I don’t really understand the use case for CxxWrap well enough to give advice on a better way to do this. @barche, can you elaborate?

The problematic code is this:

function create_module(name::String, parent::Module)
  return Core.eval(parent, :(module $(Symbol(name)) end))
end

This gets called by wrap_modules(cpp_lib) to create a Julia module for every module declared on the C++ side and compiled into the cpp_lib. The parent module is Main by default, so in Julia 0.7 using the generated modules would require the leading “.”.

I think @Ralph_Smith provided a viable workaround, but on further reflection I think using the following approach is better:

module CppFoo

using CxxWrap

wrap_module(cpp_mod)

end

This approach is already supported as an alternative, but I intend to make it the only way to declare a module. That way no hacking into Julia internals is needed and everything works just like a normal Julia module. The only downside is that it’s a bit more verbose when there are multiple modules in the C++ lib, but in practice it’s often required to add some Julia code to the modules, so manually declaring the module is the only way to go anyway.