From 6183923f9c3e5ae54ba32515bdb200ef9160b8d0 Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Sun, 23 Feb 2020 21:07:53 +0100 Subject: [PATCH] support non symbolic labels --- src/OptionalArgChecks.jl | 34 ++++++---- test/runtests.jl | 130 +++++++++++++++++++++++++++------------ 2 files changed, 112 insertions(+), 52 deletions(-) diff --git a/src/OptionalArgChecks.jl b/src/OptionalArgChecks.jl index 3d232f7..247ee03 100644 --- a/src/OptionalArgChecks.jl +++ b/src/OptionalArgChecks.jl @@ -6,9 +6,22 @@ using MacroTools: postwalk export @mark, @skip, @unsafe_skipargcheck # reexport @argcheck and @check -using ArgCheck: @argcheck, @check, LABEL_ARGCHECK +using ArgCheck: @argcheck, @check, LABEL_ARGCHECK, ArgCheck +export LABEL_ARGCHECK export @argcheck, @check +function eval_label(label) + try + return Main.eval(label) + catch err + msg = """Cannot evaluate $label + Labels are evaluated globally in the `Main` module. + """ + @error msg + rethrow(err) + end +end + """ @mark label ex @@ -17,7 +30,7 @@ Marks `ex` as an optional argument check, so when a function is called via ```jldoctest julia> function half(x::Integer) - @mark check_even iseven(x) || throw(DomainError(x, "x has to be an even number")) + @mark :check_even iseven(x) || throw(DomainError(x, "x has to be an even number")) return x รท 2 end half (generic function with 1 method) @@ -30,12 +43,12 @@ ERROR: DomainError with 3: x has to be an even number [...] -julia> @skip check_even half(3) +julia> @skip :check_even half(3) 1 ``` """ macro mark(label, ex) - label isa Symbol || error("label has to be a Symbol") + label = eval_label(label) return Expr( :block, Expr(:meta, :begin_optional, label), @@ -94,17 +107,10 @@ end end function _skip(l, ex, recursive=true) - labels::Vector{Symbol} = if l isa Symbol - [l] - elseif Meta.isexpr(l, :vect) - labels = l.args - else - error("label has to be a name or array of names") - end - return _skip(labels, ex, recursive) + return _skip([l], ex, recursive) end -function _skip(labels::Vector, ex, recursive) +function _skip(labels::AbstractVector, ex, recursive) ex = postwalk(ex) do x if Meta.isexpr(x, :call) pushfirst!(x.args, Expr( @@ -129,10 +135,12 @@ For every function call in `ex`, expressions marked with label `label` (or any o macro skip end macro skip(l, ex) + l = eval_label(l) return _skip(l, ex, true) end macro skip(l, ex, r) + l = eval_label(l) r = parse_recursive(r) return _skip(l, ex, r) end diff --git a/test/runtests.jl b/test/runtests.jl index 7096273..bfce1c4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,9 +1,60 @@ using Test using OptionalArgChecks +module A + struct MyLabel end +end +using .A: MyLabel +const alias_AMyLabel = A.MyLabel() + +module B + struct MyLabel end +end +struct MyMainLabel end +myone = 1 + +@testset "non symbolic labels" begin + function f1() + @mark 1 begin + return :executed + end + return :skipped + end + function fMyMainLabel() + @mark MyMainLabel() begin + return :executed + end + return :skipped + end + function fAMyLabel() + @mark A.MyLabel() begin + return :executed + end + return :skipped + end + @test @skip(1, f1()) == :skipped + @test @skip(1.0, f1()) == :skipped + @test @skip(myone,f1()) == :skipped + @test @skip(2-1,f1()) == :skipped + @test @skip(1:3,f1()) == :skipped + @test @skip(2:3,f1()) == :executed + @test @skip(2,f1()) == :executed + @test @skip(MyMainLabel(),f1()) == :executed + @test @skip(:one,f1()) == :executed + @test @skip(A.MyLabel(),f1()) == :executed + + @test @skip(A.MyLabel(), fAMyLabel()) == :skipped + @test @skip(MyLabel(), fAMyLabel()) == :skipped + @test @skip(alias_AMyLabel, fAMyLabel()) == :skipped + @test @skip([A.MyLabel(), 1], fAMyLabel()) == :skipped + @test @skip(B.MyLabel(), fAMyLabel()) == :executed + @test @skip(:MyLabel, fAMyLabel()) == :executed + @test @skip(1, fAMyLabel()) == :executed +end + @testset "argcheck" begin function f(x) - @mark argcheck x<2 && if x == 1 + @mark :argcheck x<2 && if x == 1 return "foo" else error("Test") @@ -12,21 +63,21 @@ using OptionalArgChecks end for x in 0:2 - @test @skip argcheck f(x) == x + 3 + @test @skip :argcheck f(x) == x + 3 end g(x) = @argcheck x > 0 @test_throws ArgumentError g(-1) @test @unsafe_skipargcheck(g(-1)) === nothing - @test_throws ArgumentError @skip(argcheck, g(-1)) === nothing + @test_throws ArgumentError @skip(:argcheck, g(-1)) === nothing @test g(1) === nothing g(x) = @check x > 0 @test_throws Exception g(-1) @test @unsafe_skipargcheck(g(-1)) === nothing - @test_throws Exception @skip(argcheck, g(-1)) === nothing + @test_throws Exception @skip(:argcheck, g(-1)) === nothing @test g(1) === nothing outer(x) = inner(x) @@ -44,50 +95,51 @@ end @testset "mark skip" begin function simple() - @mark return1 begin + @mark :return1 begin return 1 end return 2 end @test simple() === 1 - @test @skip(does_not_exist, simple()) === 1 - @test @skip(return1, simple()) === 2 + @test @skip(:does_not_exist, simple()) === 1 + @test @skip(:return1, simple()) === 2 - @test_throws LoadError eval(:(@skip(2 + 3, simple()))) + # @test_throws LoadError eval(:(@skip(2 + 3, simple()))) + @skip(2 + 3, simple()) function indirect() simple() end @test indirect() === 1 - @test @skip(does_not_exist, indirect()) === 1 - @test @skip(return1, indirect()) === 2 + @test @skip(:does_not_exist, indirect()) === 1 + @test @skip(:return1, indirect()) === 2 function complex() ret = Int[] push!(ret,1) - @mark two begin + @mark :two begin push!(ret,2) end push!(ret, 3) - @mark four begin + @mark :four begin push!(ret, 4) end ret end @test complex() == 1:4 - @test @skip(does_not_exist, complex()) == 1:4 - @test @skip(two, complex()) == [1,3,4] - @test @skip(four, complex()) == [1,2,3] - @test @skip([two, four], complex()) == [1,3] + @test @skip(:does_not_exist, complex()) == 1:4 + @test @skip(:two, complex()) == [1,3,4] + @test @skip(:four, complex()) == [1,2,3] + @test @skip([:two, :four], complex()) == [1,3] function nested() ret = Int[] - @mark nested begin + @mark :nested begin push!(ret, 1) - @mark nested begin + @mark :nested begin push!(ret,2) end push!(ret,3) @@ -97,27 +149,27 @@ end end @test nested() == 1:4 - @test @skip(nested, nested()) == [4] + @test @skip(:nested, nested()) == [4] function complex_nested() ret = Char[] push!(ret,'a') - @mark bcdg begin + @mark :bcdg begin push!(ret,'b') - @mark cdh begin + @mark :cdh begin push!(ret,'c') - @mark bcdg begin + @mark :bcdg begin push!(ret,'d') end end end push!(ret,'e') - @mark fgh begin + @mark :fgh begin push!(ret,'f') - @mark bcdg begin + @mark :bcdg begin push!(ret,'g') end - @mark cdh begin + @mark :cdh begin push!(ret,'h') end end @@ -125,19 +177,19 @@ end end @test @skip([], complex_nested()) == 'a':'h' - @test @skip([bcdg], complex_nested()) == collect("aefh") - @test @skip([cdh], complex_nested()) == collect("abefg") - @test @skip([fgh], complex_nested()) == 'a':'e' - @test @skip([bcdg, cdh], complex_nested()) == collect("aef") - @test @skip([fgh, cdh], complex_nested()) == collect("abe") - @test @skip([bcdg, cdh, fgh], complex_nested()) == collect("ae") + @test @skip([:bcdg], complex_nested()) == collect("aefh") + @test @skip([:cdh], complex_nested()) == collect("abefg") + @test @skip([:fgh], complex_nested()) == 'a':'e' + @test @skip([:bcdg, :cdh], complex_nested()) == collect("aef") + @test @skip([:fgh, :cdh], complex_nested()) == collect("abe") + @test @skip([:bcdg, :cdh, :fgh], complex_nested()) == collect("ae") end @testset "recursion through function calls" begin function inner() ret = Int[] push!(ret, 2) - @mark three begin + @mark :three begin push!(ret, 3) end ret @@ -148,7 +200,7 @@ end push!(ret, 1) x = inner() append!(ret, x) - @mark four begin + @mark :four begin push!(ret, 4) end push!(ret, 5) @@ -156,12 +208,12 @@ end end @test outer() == 1:5 - @test @skip(four, outer(), recursive=true) == [1,2,3,5] - @test @skip(four, outer(), recursive=false) == [1,2,3,5] - @test @skip(three, outer(), recursive=true) == [1,2,4,5] - @test @skip(three, outer(), recursive=false) == 1:5 - @test @skip([three, four], outer()) == [1,2,5] - @test @skip([three, four], outer(), recursive=false) == [1,2,3,5] + @test @skip(:four, outer(), recursive=true) == [1,2,3,5] + @test @skip(:four, outer(), recursive=false) == [1,2,3,5] + @test @skip(:three, outer(), recursive=true) == [1,2,4,5] + @test @skip(:three, outer(), recursive=false) == 1:5 + @test @skip([:three, :four], outer()) == [1,2,5] + @test @skip([:three, :four], outer(), recursive=false) == [1,2,3,5] end using Documenter