Hi all, I just published SmartAsserts to the general registry. It provides a convenient macro @smart_assert that automatically prints out argument values upon assertion failure (without re-evaluating the original expression) and can be used as a drop-in replacement for @assert. Unlike @assert , these smart assertions can also be easily turned off at compile-time. Hope you will find it useful!
An assertion failure example using @smart_assert:
julia> let a = 1.0, rtol=0.1
@smart_assert isapprox(a, sin(a), atol=0.05; rtol)
end
ERROR: AssertionError: Condition `isapprox(a, sin(a), atol = 0.05; rtol)` failed due to:
`a` evaluates to 1.0
`sin(a)` evaluates to 0.8414709848078965
`0.05` evaluates to 0.05
`rtol` evaluates to 0.1
Stacktrace:
[1] top-level scope
@ REPL[177]:2
One feature I’d like to have is to disable assertions in release mode, like in C. I know there are tricks to enable this, but it would be a nice built-in feature.
One easy way to implement this would be via an enabled::Ref{Bool} that is checked at run-time and skips the tests if set to false. However, there would still be a small amount of overhead due to this run-time check even if it’s set to false. Would this be sufficient for your common use cases?
Alternatively, to truly get rid of any run-time cost, I can add an enabled keyword argument to the macro, but the user will need to make sure it’s set to a compile-time constant.
Another solution that’s more similar to what you described would be using a constant ENABLE_ASSERTS=false defined inside module M to control whether to disenable all smart_asserts inside M. This way, the package author can easily control whether to turn off all their smart_asserts by setting the value of this constant inside their module (e.g., via environment variables using const ENABLE_ASSERTS = ENV["ENABLE_ASSERTS"]).
I think under the hood this one still relies on run-time level checks. The code generated by @debug contains the line if std_level >= _min_enabled_level[] ....
Wow, I did not know it exists! Thanks for the info! Looks like ArgCheck’s @check does something very similar to my @smart_assert! Maybe I should merge my package into that one instead? Most of my code is very similar to theirs anyway…
I noticed that in the result produced by @check, the stack trace seems to point to ArgCheck’s source location instead of the assertion’s. Also, the values of keyword arguments are printed out before the positional args. Would you consider these as issues that are worthwhile to fix? If so, I’d be happy to make a pull request (I have addressed these in @smart_assert and I think they should be easy to fix in ArgCheck as well).