From 8e9507f7b5ac7487dd1b0d9a48df87626484134d Mon Sep 17 00:00:00 2001 From: lassepe Date: Sun, 8 Aug 2021 17:24:50 +0200 Subject: [PATCH 01/11] Make reexport composable with other import macros --- src/Reexport.jl | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/Reexport.jl b/src/Reexport.jl index 5cde14b..d2929a2 100644 --- a/src/Reexport.jl +++ b/src/Reexport.jl @@ -1,11 +1,20 @@ module Reexport -macro reexport(ex) - isa(ex, Expr) && (ex.head == :module || - ex.head == :using || - (ex.head == :toplevel && - all(e->isa(e, Expr) && e.head == :using, ex.args))) || - error("@reexport: syntax error") +macro reexport(ex::Expr) + esc(reexport(__module__, ex)) +end + +function reexport(m::Module, ex::Expr) + # unpack any macros + ex = macroexpand(m, ex) + # recursively unpack any blocks + if ex.head == :block + return Expr(:block, map(e -> reexport(m, e), ex.args)...) + end + + Meta.isexpr(ex, [:module, :using, :import]) || + Meta.isexpr(ex, :toplevel) && all(e->isa(e, Expr) && e.head == :using, ex.args) || + error("@reexport: syntax error") if ex.head == :module modules = Any[ex.args[2]] @@ -14,15 +23,18 @@ macro reexport(ex) modules = Any[ex.args[end]] elseif ex.head == :using && ex.args[1].head == :(:) symbols = [e.args[end] for e in ex.args[1].args[2:end]] - return esc(Expr(:toplevel, ex, :(eval(Expr(:export, $symbols...))))) + return Expr(:toplevel, ex, :(eval(Expr(:export, $symbols...)))) + elseif ex.head == :import + symbols = Any[e.args[end] for e in ex.args] + return Expr(:toplevel, ex, :(eval(Expr(:export, $symbols...)))) else modules = Any[e.args[end] for e in ex.args] end - esc(Expr(:toplevel, ex, - [:(eval(Expr(:export, filter!(x -> Base.isexported($mod, x), + Expr(:toplevel, ex, + [:(eval(Expr(:export, filter!(x -> Base.isexported($mod, x), names($mod; all=true, imported=true))...))) - for mod in modules]...)) + for mod in modules]...) end export @reexport From 7c6a35f11d06eea7142b4b60e6dc050c89878aae Mon Sep 17 00:00:00 2001 From: lassepe Date: Sun, 8 Aug 2021 17:36:55 +0200 Subject: [PATCH 02/11] Remove any line numbers Expr(:block) --- src/Reexport.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Reexport.jl b/src/Reexport.jl index d2929a2..cd96b7c 100644 --- a/src/Reexport.jl +++ b/src/Reexport.jl @@ -9,6 +9,7 @@ function reexport(m::Module, ex::Expr) ex = macroexpand(m, ex) # recursively unpack any blocks if ex.head == :block + ex = Base.remove_linenums!(ex) return Expr(:block, map(e -> reexport(m, e), ex.args)...) end From d71d84fde610e464f5e3b5dd00dbc4525673c033 Mon Sep 17 00:00:00 2001 From: lassepe Date: Sun, 8 Aug 2021 21:10:43 +0200 Subject: [PATCH 03/11] Add tests --- test/runtests.jl | 119 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/test/runtests.jl b/test/runtests.jl index abfbc4d..1af166f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -92,3 +92,122 @@ module X7 end using .X7 @test Base.isexported(X7, :S7) + +#== Imports ==# + +module X8 + using Reexport + + module InnerX8 + const a = 1 + export a + end + @reexport import .InnerX8.a +end + +module X9 + using Reexport + + module InnerX9_1 + const a = 1 + export a + end + + module InnerX9_2 + const b = 1 + export b + end + + @reexport import .InnerX9_1.a, .InnerX9_2.b +end + +module X10 + using Reexport + + module InnerX10 + const b = 1 + export b + end + @reexport import .InnerX10 +end + + +@testset "import" begin + @testset "Rexported qualified single import" begin + @test Set(names(X8)) == Set([:X8, :a]) + end + + @testset "Rexported qualified multiple import" begin + @test Set(names(X9)) == Set([:X9, :a, :b]) + end + + @testset "Reexported module import" begin + @test Set(names(X10)) == Set([:X10, :InnerX10]) + end +end + +#== block ==# + +module X11 + using Reexport + @reexport begin + using Main.X8 + using Main.X9 + end +end + +module X12 + using Reexport + @reexport begin + import Main.X8 + import Main.X9 + end +end + +module X13 + using Reexport + module InnerX13 + const a = 1 + export a + end + @reexport begin + import Main.X8 + using Main.X9 + using .InnerX13: a + end +end + + +@testset "block" begin + @testset "block of using" begin + @test Set(names(X11)) == union(Set(names(X8)), Set(names(X9)), Set([:X11])) + end + @testset "block of import" begin + @test Set(names(X12)) == Set([:X12, :X8, :X9]) + end + @testset "mixed using and import" begin + @test Set(names(X13)) == union(Set([:X13, :X8, :a]), Set(names(X9))) + end +end + +#== macroexpand ==# + + +module X14 + using Reexport + + macro identity_macro(ex::Expr) + ex + end + + module InnerX14 + const a = 1 + export a + end + + @reexport @identity_macro using .InnerX14: a +end +@testset "macroexpand" begin + @test Set(names(X14)) == Set([:X14, :a]) +end + From fac3b9f2830a27f39198bbae32aa75ca1e6a40e9 Mon Sep 17 00:00:00 2001 From: lassepe Date: Sun, 8 Aug 2021 21:11:31 +0200 Subject: [PATCH 04/11] Clean up --- test/runtests.jl | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 1af166f..a41aef8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -94,7 +94,6 @@ using .X7 @test Base.isexported(X7, :S7) #== Imports ==# - module X8 using Reexport @@ -131,7 +130,6 @@ module X10 @reexport import .InnerX10 end - @testset "import" begin @testset "Rexported qualified single import" begin @test Set(names(X8)) == Set([:X8, :a]) @@ -147,7 +145,6 @@ end end #== block ==# - module X11 using Reexport @reexport begin @@ -177,7 +174,6 @@ module X13 end end - @testset "block" begin @testset "block of using" begin @test Set(names(X11)) == union(Set(names(X8)), Set(names(X9)), Set([:X11])) @@ -191,8 +187,6 @@ end end #== macroexpand ==# - - module X14 using Reexport From fd52f68a5cec2c1b9240c7c511eecaf88ac8fd60 Mon Sep 17 00:00:00 2001 From: lassepe Date: Sat, 21 Aug 2021 22:44:28 +0200 Subject: [PATCH 05/11] Format as requested --- src/Reexport.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Reexport.jl b/src/Reexport.jl index cd96b7c..14807aa 100644 --- a/src/Reexport.jl +++ b/src/Reexport.jl @@ -14,13 +14,13 @@ function reexport(m::Module, ex::Expr) end Meta.isexpr(ex, [:module, :using, :import]) || - Meta.isexpr(ex, :toplevel) && all(e->isa(e, Expr) && e.head == :using, ex.args) || - error("@reexport: syntax error") + Meta.isexpr(ex, :toplevel) && all(e -> isa(e, Expr) && e.head == :using, ex.args) || + error("@reexport: syntax error") if ex.head == :module modules = Any[ex.args[2]] ex = Expr(:toplevel, ex, :(using .$(ex.args[2]))) - elseif ex.head == :using && all(e->isa(e, Symbol), ex.args) + elseif ex.head == :using && all(e -> isa(e, Symbol), ex.args) modules = Any[ex.args[end]] elseif ex.head == :using && ex.args[1].head == :(:) symbols = [e.args[end] for e in ex.args[1].args[2:end]] @@ -34,8 +34,8 @@ function reexport(m::Module, ex::Expr) Expr(:toplevel, ex, [:(eval(Expr(:export, filter!(x -> Base.isexported($mod, x), - names($mod; all=true, imported=true))...))) - for mod in modules]...) + names($mod; all=true, imported=true))...))) + for mod in modules]...) end export @reexport From 68dfd98a80cc8bff50b3cf88c7cd0001bb8a135c Mon Sep 17 00:00:00 2001 From: lassepe Date: Sat, 21 Aug 2021 22:46:11 +0200 Subject: [PATCH 06/11] Don't remove LineNumberNode --- src/Reexport.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Reexport.jl b/src/Reexport.jl index 14807aa..fbbd56e 100644 --- a/src/Reexport.jl +++ b/src/Reexport.jl @@ -4,12 +4,13 @@ macro reexport(ex::Expr) esc(reexport(__module__, ex)) end +reexport(m::Module, l::LineNumberNode) = l + function reexport(m::Module, ex::Expr) # unpack any macros ex = macroexpand(m, ex) # recursively unpack any blocks if ex.head == :block - ex = Base.remove_linenums!(ex) return Expr(:block, map(e -> reexport(m, e), ex.args)...) end From d1fbabd25bed1b29fc1fbf1f90e45b7be5776925 Mon Sep 17 00:00:00 2001 From: lassepe Date: Sun, 22 Aug 2021 00:14:57 +0200 Subject: [PATCH 07/11] Also handle 'import Foo: bar' --- src/Reexport.jl | 12 ++++---- test/runtests.jl | 74 ++++++++++++++++++++++++++++-------------------- 2 files changed, 51 insertions(+), 35 deletions(-) diff --git a/src/Reexport.jl b/src/Reexport.jl index fbbd56e..178e72a 100644 --- a/src/Reexport.jl +++ b/src/Reexport.jl @@ -18,18 +18,20 @@ function reexport(m::Module, ex::Expr) Meta.isexpr(ex, :toplevel) && all(e -> isa(e, Expr) && e.head == :using, ex.args) || error("@reexport: syntax error") - if ex.head == :module + if Meta.isexpr(ex, :module) + # @reexport {using, import} module Foo ... end modules = Any[ex.args[2]] ex = Expr(:toplevel, ex, :(using .$(ex.args[2]))) - elseif ex.head == :using && all(e -> isa(e, Symbol), ex.args) - modules = Any[ex.args[end]] - elseif ex.head == :using && ex.args[1].head == :(:) + elseif Meta.isexpr(ex, [:using, :import]) && ex.args[1].head == :(:) + # @reexport {using, import} Foo: bar, baz symbols = [e.args[end] for e in ex.args[1].args[2:end]] return Expr(:toplevel, ex, :(eval(Expr(:export, $symbols...)))) - elseif ex.head == :import + elseif Meta.isexpr(ex, :import) && all(e -> e.head == :(.), ex.args) + # @reexport import Foo.bar, Baz.qux symbols = Any[e.args[end] for e in ex.args] return Expr(:toplevel, ex, :(eval(Expr(:export, $symbols...)))) else + # @reexport using Foo, Bar, Baz modules = Any[e.args[end] for e in ex.args] end diff --git a/test/runtests.jl b/test/runtests.jl index a41aef8..066a7d5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -94,114 +94,128 @@ using .X7 @test Base.isexported(X7, :S7) #== Imports ==# -module X8 +module X9 using Reexport - module InnerX8 + module InnerX9 const a = 1 export a end - @reexport import .InnerX8.a + @reexport import .InnerX9: a end module X9 using Reexport - module InnerX9_1 + module InnerX9 + const a = 1 + export a + end + @reexport import .InnerX9.a +end + +module X10 + using Reexport + + module InnerX10_1 const a = 1 export a end - module InnerX9_2 + module InnerX10_2 const b = 1 export b end - @reexport import .InnerX9_1.a, .InnerX9_2.b + @reexport import .InnerX10_1.a, .InnerX10_2.b end -module X10 +module X11 using Reexport - module InnerX10 + module InnerX11 const b = 1 export b end - @reexport import .InnerX10 + @reexport import .InnerX11 end @testset "import" begin - @testset "Rexported qualified single import" begin - @test Set(names(X8)) == Set([:X8, :a]) + @testset "Reexported colon-qualified single import" begin + @test Set(names(X9)) == Set([:X9, :a]) + end + + @testset "Reexported dot-qualified single import" begin + @test Set(names(X9)) == Set([:X9, :a]) end - @testset "Rexported qualified multiple import" begin - @test Set(names(X9)) == Set([:X9, :a, :b]) + @testset "Reexported qualified multiple import" begin + @test Set(names(X10)) == Set([:X10, :a, :b]) end @testset "Reexported module import" begin - @test Set(names(X10)) == Set([:X10, :InnerX10]) + @test Set(names(X11)) == Set([:X11, :InnerX11]) end end #== block ==# -module X11 +module X12 using Reexport @reexport begin - using Main.X8 using Main.X9 + using Main.X10 end end -module X12 +module X13 using Reexport @reexport begin - import Main.X8 import Main.X9 + import Main.X10 end end -module X13 +module X14 using Reexport - module InnerX13 + module InnerX14 const a = 1 export a end @reexport begin - import Main.X8 - using Main.X9 - using .InnerX13: a + import Main.X9 + using Main.X10 + using .InnerX14: a end end @testset "block" begin @testset "block of using" begin - @test Set(names(X11)) == union(Set(names(X8)), Set(names(X9)), Set([:X11])) + @test Set(names(X12)) == union(Set(names(X9)), Set(names(X10)), Set([:X12])) end @testset "block of import" begin - @test Set(names(X12)) == Set([:X12, :X8, :X9]) + @test Set(names(X13)) == Set([:X13, :X9, :X10]) end @testset "mixed using and import" begin - @test Set(names(X13)) == union(Set([:X13, :X8, :a]), Set(names(X9))) + @test Set(names(X14)) == union(Set([:X14, :X9, :a]), Set(names(X10))) end end #== macroexpand ==# -module X14 +module X15 using Reexport macro identity_macro(ex::Expr) ex end - module InnerX14 + module InnerX15 const a = 1 export a end - @reexport @identity_macro using .InnerX14: a + @reexport @identity_macro using .InnerX15: a end @testset "macroexpand" begin - @test Set(names(X14)) == Set([:X14, :a]) + @test Set(names(X15)) == Set([:X15, :a]) end From cdf7053cc8ff8d5034014bfa2d10182816740da6 Mon Sep 17 00:00:00 2001 From: lassepe Date: Sun, 22 Aug 2021 00:30:33 +0200 Subject: [PATCH 08/11] Add example to README --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index 32be7e6..46a2adf 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,29 @@ using X # all of Y's exported symbols and Z's x and y also available here ``` +`@reexport import .` or `@reexport import : ` exports `` from `` after importing it. + +```julia +module Y + ... +end + +module Z + ... +end + +module X + using Reexport + @reexport import Y + # Only `Y` itself is available here + @reexport import Z: x, y + # Z's x and y symbols available here +end + +using X +# Y (but not it's exported names) and Z's x and y are available here. +``` + `@reexport module ... end` defines `module ` and also re-exports its symbols: ```julia @@ -45,3 +68,5 @@ end using A # all of B's exported symbols available here ``` + +`@reexport @another_macro ` first expands `@another_macro` on the expression, making `@reexport` with other macros. From 2fa254af13cb34e419c3b7a248a92e4fe17f7db4 Mon Sep 17 00:00:00 2001 From: lassepe Date: Sun, 22 Aug 2021 00:51:25 +0200 Subject: [PATCH 09/11] Mention block syntax in README as well --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 46a2adf..4b916a8 100644 --- a/README.md +++ b/README.md @@ -70,3 +70,5 @@ using A ``` `@reexport @another_macro ` first expands `@another_macro` on the expression, making `@reexport` with other macros. + +`@reexport begin ... end` will apply the reexport macro to every expression in the block. From 40470c5aa95ba934f24ec917255fcdec0c00b292 Mon Sep 17 00:00:00 2001 From: lassepe Date: Sun, 22 Aug 2021 01:02:02 +0200 Subject: [PATCH 10/11] Don't use Meta.isexpr --- src/Reexport.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Reexport.jl b/src/Reexport.jl index 178e72a..ef2694c 100644 --- a/src/Reexport.jl +++ b/src/Reexport.jl @@ -14,19 +14,19 @@ function reexport(m::Module, ex::Expr) return Expr(:block, map(e -> reexport(m, e), ex.args)...) end - Meta.isexpr(ex, [:module, :using, :import]) || - Meta.isexpr(ex, :toplevel) && all(e -> isa(e, Expr) && e.head == :using, ex.args) || + ex.head in (:module, :using, :import) || + ex.head === :toplevel && all(e -> isa(e, Expr) && e.head == :using, ex.args) || error("@reexport: syntax error") - if Meta.isexpr(ex, :module) + if ex.head === :module # @reexport {using, import} module Foo ... end modules = Any[ex.args[2]] ex = Expr(:toplevel, ex, :(using .$(ex.args[2]))) - elseif Meta.isexpr(ex, [:using, :import]) && ex.args[1].head == :(:) + elseif ex.head in (:using, :import) && ex.args[1].head == :(:) # @reexport {using, import} Foo: bar, baz symbols = [e.args[end] for e in ex.args[1].args[2:end]] return Expr(:toplevel, ex, :(eval(Expr(:export, $symbols...)))) - elseif Meta.isexpr(ex, :import) && all(e -> e.head == :(.), ex.args) + elseif ex.head === :import && all(e -> e.head == :(.), ex.args) # @reexport import Foo.bar, Baz.qux symbols = Any[e.args[end] for e in ex.args] return Expr(:toplevel, ex, :(eval(Expr(:export, $symbols...)))) From a3e7e23efea97779107bbb0c6004ac2bfb5159a0 Mon Sep 17 00:00:00 2001 From: lassepe Date: Sun, 22 Aug 2021 01:03:11 +0200 Subject: [PATCH 11/11] Don't shadow test --- test/runtests.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 066a7d5..47349f9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -94,14 +94,14 @@ using .X7 @test Base.isexported(X7, :S7) #== Imports ==# -module X9 +module X8 using Reexport - module InnerX9 + module InnerX8 const a = 1 export a end - @reexport import .InnerX9: a + @reexport import .InnerX8: a end module X9 @@ -142,7 +142,7 @@ end @testset "import" begin @testset "Reexported colon-qualified single import" begin - @test Set(names(X9)) == Set([:X9, :a]) + @test Set(names(X8)) == Set([:X8, :a]) end @testset "Reexported dot-qualified single import" begin