Skip to content

Commit

Permalink
add markers to check blocks (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
jw3126 authored Feb 12, 2020
1 parent 644b932 commit 5fce7b4
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 14 deletions.
3 changes: 1 addition & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
name = "ArgCheck"
uuid = "dce04be8-c92d-5529-be00-80e4d2c0e197"
license = "MIT"
version = "1.0.1"
version = "1.1"

[deps]
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[compat]
julia = "≥ 1.0.0"
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,14 @@ You can also customize the error:
@argcheck det(A) < 0 DomainError
@argcheck false MyCustomError(my, args...)
```

### Performance
`@argcheck code` is as fast as `@assert` or a hand written `if`. That being said it is possible to erase argchecks, much like one can erase bounds checking using `@inbounds`. This is implemented in [OptionalArgChecks.jl](https://github.com/simeonschaub/OptionalArgChecks.jl):

```julia
using OptionalArgChecks # this also reexports ArgCheck.jl for convenience

f(x) = @argcheck x > 0

@skipargcheck f(-1)
```
1 change: 0 additions & 1 deletion src/ArgCheck.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
__precompile__()
module ArgCheck

using Base.Meta
Expand Down
17 changes: 16 additions & 1 deletion src/checks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@ struct CheckError <: Exception
msg::String
end

const MARKER = :argcheck

const MARKER_BEGIN_CHECK = Expr(:meta, :begin_optional, :argcheck)
const MARKER_END_CHECK = Expr(:meta, :end_optional, :argcheck)

function mark_check(code)
# Mark a code block as check, for usage with OptionalArgChecks.jl
Expr(:block,
MARKER_BEGIN_CHECK,
code,
MARKER_END_CHECK,
)
end

Base.showerror(io::IO, err::CheckError) = print(io, "CheckError: $(err.msg)")

abstract type AbstractCheckFlavor end
Expand Down Expand Up @@ -87,7 +101,8 @@ function check(ex, checkflavor, options...)
FallbackFlavor()
end
checker = Checker(ex, checkflavor, codeflavor, options)
check(checker, codeflavor)
inner = check(checker, codeflavor)
mark_check(inner)
end

function is_simple_call(ex)
Expand Down
33 changes: 28 additions & 5 deletions test/checks.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
module TestChecks
using Test
using ArgCheck
using ArgCheck: pretty_string
using Random: randstring

macro catch_exception_object(code)
quote
Expand All @@ -25,7 +29,7 @@ end

@test_throws ArgumentError @argcheck 1 2 == 2
@argcheck 1 == 1 1 < 2 > 1.2
@test_throws DimensionMismatch @argcheck 1 < 2 ==3 DimensionMismatch
@test_throws DimensionMismatch @argcheck 1 < 2 ==3 DimensionMismatch
end

@testset "@argcheck" begin
Expand All @@ -35,11 +39,11 @@ end
x = 1
@test_throws ArgumentError (@argcheck x > 1)
@argcheck x>0 # does not throw

n =2; m=3
@test_throws DimensionMismatch (@argcheck n==m DimensionMismatch)
@argcheck n==n DimensionMismatch

denominator = 0
@test_throws DivideError (@argcheck denominator != 0 DivideError())
@argcheck 1 !=0 DivideError()
Expand Down Expand Up @@ -206,14 +210,33 @@ end

@testset "pretty_string" begin
@test pretty_string("asd") == "\"asd\""

data = rand(10000:99999, 1000)
str = pretty_string(data)
@test length(str) < 1000
@test occursin(string(last(data)), str)
@test occursin(string(first(data)),str)
@test !occursin("\n",str)

data = randn()
@test parse(Float64,pretty_string(data)) === data
end

@testset "marker" begin
for ex in [
:(@argcheck some_expr),
:(@check some_expr),
:(@check A < b),
:(@check A < b MyError),
]
ex = macroexpand(TestChecks, ex)
@test Meta.isexpr(ex, :block)
@test first(ex.args) == ArgCheck.MARKER_BEGIN_CHECK
@test last(ex.args) == ArgCheck.MARKER_END_CHECK
end
@test ArgCheck.MARKER_BEGIN_CHECK != ArgCheck.MARKER_END_CHECK
@test Meta.isexpr(ArgCheck.MARKER_BEGIN_CHECK, :meta)
@test Meta.isexpr(ArgCheck.MARKER_END_CHECK, :meta)
end

end#module
3 changes: 3 additions & 0 deletions test/perf.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
module Perf
# compare performance with plain assertion
using BenchmarkTools
using ArgCheck
Expand Down Expand Up @@ -35,3 +36,5 @@ for (f_argcheck, f_assert, arg) in benchmarks
println(f_assert)
@btime ($f_assert)($arg)
end

end#module
5 changes: 0 additions & 5 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,2 @@
using ArgCheck

using Test
using Random

include("checks.jl")
include("perf.jl")

2 comments on commit 5fce7b4

@jw3126
Copy link
Owner Author

@jw3126 jw3126 commented on 5fce7b4 Feb 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register()

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/9353

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if Julia TagBot is installed, or can be done manually through the github interface, or via:

git tag -a v1.1.0 -m "<description of version>" 5fce7b4e11a4357c6005b4649516781ea2cbe27c
git push origin v1.1.0

Please sign in to comment.