Ispublic returns wrong result for types

Even though I have types declared as public, the function Base.ispublic returns false for them.

MWE: Creating a minimal package:

module PubEx

hiworld() = println("Hello World")

struct PubStr
    foo
end

struct ExpStr
    bar
end

public hiworld, PubStr
export ExpStr

end
julia> using PubEx
[ Info: Precompiling PubEx [98793095-e1f6-4496-89aa-0fa6d1a859f3] 

julia> Base.isexported(PubEx, Symbol(ExpStr))
true

julia> Base.ispublic(PubEx, Symbol(PubEx.hiworld))
true

julia> Base.ispublic(PubEx, Symbol(PubEx.PubStr))
false
1 Like

The symbol you should use is just the name within the module, not Symbol(PubEx.PubStr)

julia> Base.ispublic(PubEx, :PubStr)
true

The documentation of ispublic shows the example with Base.ispublic(Mod, :foo), not Base.ispublic(Mod, Symbol(Mod.foo))

3 Likes

Thank you!

OK, the docs are clear about it, but isn’t it inconsistent, that it is different for functions?


I have the following use case, which is probably not unique: I’d like to have separate sections for exported, public, and internal types and functions in Documenter.jl produced documentation. The public keyword provided by Documenter.jl itself is from pre-Julia v1.11 era, corresponds to isexported, and of little use.

For functions, following works e.g. for the “Public” section:

Filter = t -> (! Base.isexported(MyPkg, Symbol(t)) && Base.ispublic(MyPkg, Symbol(t)))

Any one-liner for types?

Inconsistent with what? You always pass the base symbol within the module.

What’s the collection you pass that Filter over? And how did you construct it? It seems like you’re going about this backwards from how I’d do it. I’d use names(MyPkg) for your starting point — those are the symbols you need, and then you can filter them by exported/public/internal and even further refine them by their value with getproperty(MyPkg, :foo).

You’re just getting “lucky” that named functions know about their names and happen to have a Symbol constructor that gives you that name back. But it’s not even necessarily the exported name from the module.

That is what I have in my markdown source:

@autodocs

Modules = [MyPkg]

Order = [:function]

Filter = t -> ! Base.ispublic(MyPkg, Symbol(t))

In your example you already know the name so you should just instantiate the Symbol directly, but the API for getting the definition name from a function or type object is nameof:

julia> module A
         foo() = 0
         struct Bar end
       end
Main.A

julia> nameof.((A.Bar, A.foo))
(:Bar, :foo)

julia> yada1, yada2 = A.Bar, A.foo; nameof.((yada1, yada2))
(:Bar, :foo)

This is what Symbol does, which obviously doesn’t work for ispublic:

julia> Symbol.((A.Bar, A.foo))
(Symbol("Main.A.Bar"), :foo)
1 Like

@Benny - thanks a lot!

So now I’ve changed it to

Filter = t -> ! Base.ispublic(MyPkg, nameof(t))

and it’s working as expected.

Now just out of curiosity:

In the example above these both names are declared as public:

public hiworld, PubStr

Why then Symbol functions differently for functions and types?

julia> using PubEx

julia> Symbol(PubEx.hiworld)
:hiworld

julia> Symbol(PubEx.PubStr)
Symbol("PubEx.PubStr")

They function the same, the printing in the REPL is just different. Prefixing an identifier with : is effectively creating a literal Symbol in the parser.

1 Like

Very literally, the Symbol constructor method is implemented to call string, which calls print. That’s where the method dispatch diverges

julia> print(A.Bar)
Main.A.Bar
julia> print(A.foo)
foo
julia> @which print(stdout, A.Bar)
print(io::IO, x)
     @ Base strings\io.jl:32

julia> @which print(stdout, A.foo)
print(io::IO, f::Function)
     @ Base show.jl:555

nameof is implemented to directly access a cached symbol for the definition name, as part of reflection; Symbol, string, and print do IO.

2 Likes

Symbol() and string() do behave differently for functions and types, printing of non-exported names differs for types and functions · Issue #56790 · JuliaLang/julia · GitHub.

2 Likes