Names that are not exported from a package can also be imported into another project

I’m encountered with a fairly strange circumstance where the names that are not exported from a package can also be imported into another project environment.

Detail: There is a package named MyPkg that is just in development. In this package, function f has been exported explicitly with export f, while function g hasn’t. I added this package in another project environment with command dev path/to/MyPkg for the purpose of package test. After using MyPkg in the new environment I found I can invoke not only function f but also g.

Is this reasonable?

Thanks in advance!

That is as expected. Symbols from modules can be brought into scope whether exported or not.

That is not expected. If g is not exported, then using MyPkg shouldn’t make the name available. You can still import MyPkg: g to request the internal name explicitly though.

That’s what I meant. Thanks for the clarification.

In that case, I need to check what went wrong. :thinking:

Thanks!

I found that the issue lies in my use of Reexport. Once I use the @reexport using SomePkg command, the aforementioned issue occurs. Is this considered a bug?

We use Reexport.jl too, and don’t see this issue.

For instance,

module Y
export g
    g(x) = x + 1
end

module X
export h
    using Reexport
    @reexport using JuMP
    @reexport using ..Y
    add_variable(model) = @variable(model)
    f(x) = x + 2
    h(x) = x + 3
end

using ..X
g(1) # Available 
f(1) # Not available 
h(1) # Available 
add_variable # Available. Why?

What is strange in that example? X directly exports h and reexports g from Y and add_variable from JuMP. It doesn’t export f.

It all looks consistent with your observations at the end.

module Y
export g
    g(x) = x + 1
end

module X
export h
    using Reexport
    @reexport using JuMP
    @reexport using ..Y
    add_variable(model) = @variable(model)
    function add_variable2(model)
        return @variable(model)
    end
    f(x) = x + 2
    h(x) = x + 3
end

using ..X
g(1) # Available 
f(1) # Not available
h(1) # Available 
add_variable # Available. Why?
add_variable2 # Not available. Why?

Maybe what I feel strange lies in I don’t understand the difference between the definitions of function add_variable and add_variable2.

The difference is that JuMP exports add_variable but not add_variable2 and you do @reexport using JuMP.

Why does JuMP export add_variable? Isn’t it a function name like add_variable2 that is defined in module X?

Because add_variable is part of the JuMP API? You probably shouldn’t be reexporting JuMP if you don’t know what it contains.

Ah, I see! It had been troubling me for a while. Thank you for your patient help and valuable advice! :handshake: :handshake: :handshake:

But, sorry. There is still a phenomenon that I feel strange.

module X
export h
    using Reexport
    @reexport using JuMP
    add_variable(x) = x + 1
    f(x) = x + 2
    h(x) = x + 3
end

using ..X
f(1) # Not available
h(1) # Available 
add_variable # Available. 
add_variable(1) # Available. Why?

Since add_variable is part of the JuMP API, it’s not surprising that it can be exported. However, I am now wondering why the new methods defined for add_variable in module X can also be exported, and it seems like it overwrite the original add_variable function in JuMP. :thinking:

The reexport macro expands to, among other things,

export add_variable

I.e. module X exports the symbol add_variable, whatever it happens to be bound to in the module. In this case it is first imported from JuMP with using, then you shadow it with your own definition.

Thank you, for me it finally makes sense, although I still feels a bit strange. :handshake:

By the way,

module Y
export f, g
    f(x) = x + 1
    g(x) = x + 2
end

module X
export h
    using Reexport
    @reexport using ..Y: f
    h(x) = f(x) + 3
    # Place-1: let `f` available here.
end

using ..X
# Place-2: let `g` available and `f` not available here.

If I want to let f available at Place-1 and let g available and f not available at Place-2, how should I code?

If I understand correctly, you want to use Y.f as f only from within X, and you want to use X.g as g outside of f. If so, then just don’t re-export. Reexport is a convenience for exporting everything. If that’s not want you want, then explicitly export what you do want.

From GitHub - simonster/Reexport.jl: Julia macro for re-exporting one module from another (with emphasis added):

Maybe you have a module X that depends on module Y and you want using X to pull in all of the symbols from Y .

Thanks!