Exportall?

Is there a way to export ALL the symbols declared in a module? Just to avoid writing them all under export.

4 Likes

No. You typically don’t want to do this.

3 Likes

I am just following this tip:
https://docs.julialang.org/en/stable/manual/workflow-tips/

which I find very useful. However since I am constantly changing the package, it is very inconvenient to track exported variables. So I think this is a use-case for exportall. Why is it so dangerous? I think it could be useful in certain scenarios.

3 Likes

I would prefix the module name before the functions you are using when developing (or do something like const m = MyPackage and use m.f().

For improved workflow, I would look at https://github.com/timholy/Revise.jl

4 Likes

What is the advantage of that, versus exportall? I am just trying to see the downside of having exportall as a feature.

6 Likes

Global namespace is global.

1 Like

Julz has a function for exporting all vars (that were included using the package)

Maybe you can do something like this:

https://github.com/djsegal/Julz.jl/blob/master/src/macros/export_all_files.jl

A practice which I like but I see others employ only rarely is putting export statements immediately following definitions like so

f(x::AbstractFloat) = # do some stuff
function f(x::Complex{<:AbstractFloat})
    # do some other stuff
end
export f

I agree that creating an exportall keyword would be asking for trouble.

4 Likes

Can you explain this code a bit? It seems to be exporting all files, not symbols of a module.

That does seem good for readability while the code is being written, but I wonder if it will become more difficult for other people (or a future you) to understand what the code does. Looking at a list of exports is one of the ways that I typically start to understand a new package.

I think I might try this style on my next project and see how it works…

I haven’t particularly found reading the export lists useful without a good handle on what the functions actually do. Sometimes having argument types is enough for this, but the names alone usually aren’t (for me at least).

Granted, it does make it easy to lose track of which functions are being exported.

2 Likes

Yea, I felt like exportall was a little dodgy (so I scoped it on files for Julz).

I believe the following code does exactly what you want

// at least it did in v0.5

https://github.com/djsegal/julz_legacy/blob/master/app/templates/config/export_all_except.jl

1 Like

The following simple snippet seems to work:

module Foo
        foo(x) = x+1
        bar(x) = x-1
 
        # exportall:
        for n in names(current_module(), true)
            if Base.isidentifier(n) && n ∉ (Symbol(current_module()), :eval)
                @eval export $n
            end
        end
end
14 Likes

@stevengj Sometimes I have a module spread over multiple files. Is there a way to specialize this to only export symbols defined in the current file?

Here’s a version of the export-all code chunk from @stevengj that is updated for Julia v1.0. (Well, for v1.4 at least. I haven’t tested on v1.0).

# export all
for n in names(@__MODULE__; all=true)
    if Base.isidentifier(n) && n ∉ (Symbol(@__MODULE__), :eval, :include)
        @eval export $n
    end
end

When I’m developing a new package, I find it pretty convenient to just export all the functions in the package. Later when the code stabilizes I can choose which functions to export, but initially I’m usually the only user of my package and it’s easier just to export everything.

Here’s the new code in action:

julia> module Foo
           x = 1
           y = 2
       
           for n in names(@__MODULE__; all=true)
               if Base.isidentifier(n) && n ∉ (Symbol(@__MODULE__), :eval, :include)
                   @eval export $n
               end
           end
       end
Main.Foo

julia> using .Foo

julia> x
1

julia> y
2
7 Likes

@CameronBieganek Thanks for posting. Adding export all to my toolbox.

1 Like

I wrote my own convenient macros for this, one exports all names and one follows all sub-modules recursively. Only tested under 1.7.1

Usage:

module MyModule
    # code here
end
@make_public MyModule
# or, if you want to follow sub-modules recursively:
@make_public_rec MyModule

Code:

#
# Copyright 2021 Clemens Cords
# Created on 26.12.2021 by clem (mail@clemens-cords.com)
#

"""
export all non-temporary, non-imported values (values not having a '#' at the
start of it's symbol when listed via Base.names) in a module

Example:
    module MyModule

        module SubModule
            _sub_variable
        end

        _variable
    end

    @make_public MyModule

    # exports:
    #   MyModule
    #   MyModule.SubModule
    #   MyModule._variable

See also: @make_public_rec
"""
macro make_public(module_name::Symbol)

    eval(Meta.parse("export " * string(module_name)))

    as_module = eval(module_name)
    @assert as_module isa Module

    for name in names(as_module; all = true)
        if (string(name)[1] != '#')
            #println("export " * string(name))
            as_module.eval(Meta.parse("export " * string(name)))
        end
    end

    return nothing
end
export make_public

"""
export all non-temporary, non-imported values (values not having a '#' at the
start of it's symbol when listed via Base.names) in a module and all
such values in any submodule, recursively

Example:
    module MyModule

        module SubModule
            _sub_variable
        end

        _variable
    end

    @make_public_rec MyModule

    # exports:
    #   MyModule
    #   MyModule.SubModule
    #   MyModule.SubModule._sub_variable
    #   MyModule._variable

See also: @make_public
"""
macro make_public_rec(module_name::Symbol)

    function make_public_aux(child_name::Symbol, super::Module) ::Nothing

        if (string(child_name)[1] != '#')
            #println("export " * string(child_name))
            super.eval(Meta.parse("export " * string(child_name)))
        end

        child = super.eval(child_name)
        if (child isa Module && child != super)
            for name in names(child; all = true)
                make_public_aux(name, child)
            end
        end

        return nothing
    end

    origin = eval(module_name)
    @assert origin isa Module

    for name in names(origin; all = true)
        make_public_aux(name, origin)
    end

    return nothing
end
export make_public_rec

It will be part of jlwrap which I am the author of and can be used freely with attribution under GNU license

1 Like

I think that a good in-between would be the ability to write something like

export function myfn()
    # ...
end

This way you would be explicit about exported function, but you don’t have to review the code every so often to export whatever you wrote: you just put export in front of every function you write and you’re sure is going to be exported.

Attaching export to method definitions is not a good idea, I think. Export cares about functions, not methods (i.e. you cannot export a subset of methods of a given function; it’s all or none).

3 Likes

Makes sense