A drop-in replacement of `Base.@assert` that prints out additional information upon failure.
Author MrVPlusOne
14 Stars
Updated Last
10 Months Ago
Started In
February 2022


Run tests

A @smart_assert macro that automatically prints out argument values upon assertion failure, used to replace the standard @assert. Unlike @assert, these smart assertions can also be easily turned off at compile-time.

Note: Most of the functionalities of this package are being integrated into ArgCheck.jl. Considering using that package instead.

Example Usages

A (failed) binary inequality assertion:

julia> let a = 5
           @smart_assert a < 1
ERROR: AssertionError: Condition `a < 1` failed due to:
        `a` evaluates to 5
 [1] top-level scope
   @ REPL[156]:2

Functions with keyword arguments are also supported:

julia> let a = 1.0, rtol=0.1
           @smart_assert isapprox(a, sin(a), atol=0.05; rtol)
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
        `rtol` evaluates to 0.1
 [1] top-level scope
   @ REPL[177]:2

Like @assert, you can also provide an additional message as the second argument:

julia> let a = 5
           @smart_assert a < 1 "This should fail. 2a = $(2 * a)"
ERROR: AssertionError: This should fail. 2a = 10
Caused by Condition `a < 1` failed due to:
        `a` evaluates to 5
 [1] top-level scope
   @ REPL[167]:2

How it works

Under the hood, an expression like @smart_assert f(<ex1>, <ex2>) is expanded by the macro into something like the following (with newly introduced variables renamed by macro hygiene)

    arg1 = <ex1>
    arg2 = <ex2>
    if !(f(arg1, arg2))
        eval_string = Main.join(["\t`$(ex)` evaluates to $(val)" for (ex, val) in<ex1>, <ex2>), (arg1, arg2))], "\n")
        reason_text = "Condition `f(<ex1>, <ex2>)` failed due to:\n" * eval_string

Note that a new local variable (arg1 and arg2) is introduced for each expression to ensure that all expressions are only evaluated once. Thus, when the assertion fails, the original values that caused the assertion condition to fail will be printed out, avoiding re-evaluation.

Supported Syntax

Currently, additional information will be printed out for the following types of expressions:

  • function calls: e.g., a <= b, bool_f(a,k1=...; k2...)

  • type asserts: e.g., Type1 <: Type2

  • comparison expressions: e.g., a == b <= c + d <= e

For other cases, only the original condition expression will be printed but not their argument information.

Turning off the assertions (at compile-time)

Calling SmartAsserts.set_enabled(false) will make all future @smart_assert be compiled into no-ops. Hence, simply call this at the beginning of your module to disable all @smart_asserts in your project. (You might also want to call SmartAsserts.set_enabled(true) at the end of your module to not accidentally turn off others' @smart_asserts).