Differences of Core.eval vs. running the code directly in other modules then Main

While writing the documentation of a package I’m currently developing, I did ran into a problem with Documenter.jl which I could track down to the sandboxing mechanism of Documenter in combination with Core.eval.

Without explaining the context here the MWE:

This code works:

function bar()
    Main.Sandbox.Foo()
end

module Sandbox
struct Foo end
Main.bar()
end

But when you call the Main.bar() via Core.eval you get an “Sandbox is not defined message”:

function bar()
    Main.Sandbox.Foo()
end

expr = Meta.parseall("""
struct Foo end
Main.bar()
""")

Core.eval(Module(:Sandbox), expr)

If the code is evaluated in Main, everything works fine again:

function bar()
    Main.Foo()
end

expr = Meta.parseall("""
struct Foo end
Main.bar()
""")

Core.eval(Main, expr)

Can someone explain, where the different behavior comes from?

Thanks

The problem is that Module(:Sandbox) apparently makes a module named Sandbox, but does not bind it to the name Sandbox:

julia> Module(:Baz)
Main.Baz

julia> Baz
ERROR: UndefVarError: Baz not defined

It does work, though, if you do the binding explicitly:

Sandbox = Module(:Sandbox)
Core.eval(Sandbox, expr)

(and using an alias would work as well):

Alias = Module(:Sandbox)
Core.eval(Alias, expr)

Thank you, that is a good hint. But to be honest, I do not understand, why the binding is necessary, and what makes it even worse, why it’s not enough to bind it to a function local variable, as this is happening in Documenter.jl:

function bar()
    Main.Sandbox.Foo()
end

function test()
    expr = Meta.parseall("""
    struct Foo end
    Main.bar()
    """)

    Sandbox = Module(:Sandbox)
    Core.eval(Sandbox, expr)
end

test()

fails again with a "UndefVarError: Sandbox not defined), it is necessary to make the Sandbox binding global.

I have the impression that using an alias does not work. Did you restart the session?

I don’t know the internals, but it seems to me that separating the creation of a module (which needs a fixed name) and the binding to another module’s namespace (possibly with another name) may be necessary to allow things like this:

julia> import Random as RNDM # I might want `Main.Random` to be a different thing

julia> @isdefined Random
false

julia> RNDM.eval(:@__MODULE__) # But this is the "real" name of the module
Random

With Module(:Sandbox) you are creating the module called Sandbox, but still have the possibility of using Main.Sandbox for a different thing, as in the previous example with the module Random.

I don’t know the answer to that, but I had never seen a module defined as a local object of a function - I didn’t even imagine that it could be allowed.

You comment that this is related to Documenter.jl, whose internals I don’t know, so that may be the reason of my failure to understand what you are trying to do. Could this other topic be related to the problem you report?