Skip to content

Instantly share code, notes, and snippets.

@pkoppstein
Created August 26, 2023 20:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pkoppstein/9b0f49a213a5b8083bab094ede06652d to your computer and use it in GitHub Desktop.
Save pkoppstein/9b0f49a213a5b8083bab094ede06652d to your computer and use it in GitHub Desktop.
# Version 2023.08.25
# This jq module defines filters for assertion checking.
# The module can be included or imported in the usual way, e.g.
# by including a line similar to the following at the top of your program file:
# include "assert" {search: "."};
# By changing "keys_unsorted" to "keys", this module can also be used with gojq,
# the Go implementation of jq.
# Introduction
# The assertion filters defined here are designed so that assertion
# checking can be turned on or off. Since the run-time cost of having
# assertions is negligibly small when assertion checking is turned
# off, assertions intended for development or testing can be left in
# place, possibly even in production code. In addition, one can
# choose whether assertion violations should cause execution to
# terminate or to be reported using jq's `debug` or `stderr` filters.
# Highlights:
# * The input to every assertion filter is always passed through without alteration.
# * Assertion-checking is controlled by the jq global variable $assert and/or the environment
# variable $JQ_ASSERT as explained below; these variables also determine
# how assertion violations are reported.
# * Pinpointing the occurrence of an assertion violation by using $__loc__ is supported,
# provided your implementation of jq includes this filter.
# Details
# 1) If both $assert and the environment variable $JQ_ASSERT are set, then the value of
# $assert takes precedence.
# 2) If neither $assert nor $JQ_ASSERT is set, or if $assert is the JSON value `false`,
# then no assertion checking takes place.
# 3) If $assert is "debug" or "stderr" or any other value except the JSON value `false`,
# then violations are reported, respectively, using `debug`, `stderr` or `error`.
# 4) If $assert is unset, then the value of $JQ_ASSERT determines how violations are reported
# in the same way as described in (3) above.
# The assertion-checking filters defined here are as follows:
#
# assert(predicate)
# assert(predicate; msg)
# where:
# - msg determines the message that is printed as part of an assertion violation warning;
# it would typically be specified as a string, or the $__loc__ object if your jq supports it.
# - if msg is $__loc__, then the message will contain the file and line number defined by $__loc__,
# giving the location of the assertion;
# - if msg is a JSON object other than $__loc__, it will be used to construct a message
# showing the object's keys and values.
# Toy examples:
# With --arg assert stderr
# 101 | assert(. == false; $__loc__)
# yields:
# assertion violation @ <top-level>:90 => false
# 101
# With --arg assert true
# 101 | 'assert(. == false; "Checking whether \(.) == false")'
# yields
# jq: error (at <unknown>): assertion violation @ Checking whether 101 == false => false
# With --arg assert true
# 101 | assert(. == false; $__loc__)
# yields:
# jq: error (at <unknown>): assertion violation @ <top-level>:90 => false
# With --arg assert debug
# 101 | assert(. == false; $__loc__)
# ["DEBUG:","assertion violation @ <top-level>:90 => false"]
# 101
# With --arg assert debug
# 101 | assert(. == false; $__loc__ + {input: .})
# ["DEBUG:","assertion violation @ file:<top-level>; line:106; input:101 => false"]
# 101
#####################################################################################
# module {name: "assert"};
# Is assertion-checking turned on?
def __assert__:
if $ARGS.named.assert == false then false
else ($ARGS.named | has("assert")) or env.JQ_ASSERT
end;
# Handle an assertion violation
def __assertion_violation__:
if $ARGS.named | has("assert")
then $ARGS.named.assert as $x
| if $x == "debug" then debug
elif $x == "stderr" then (tostring+"\n") | stderr
elif $x == false then .
else error
end
else env.JQ_ASSERT as $x
| if $x == "debug" then debug
elif $x == "stderr" then (tostring +"\n")|stderr
elif $x then error
else .
end
end;
def assert(exp; $msg):
def m: $msg
| if type == "object"
then if keys == ["file", "line"] then [.[]] | join(":")
else [keys_unsorted[] as $k | "\($k):\(.[$k])"] | join("; ")
end
else tostring
end;
if __assert__ then
(exp as $e | if $e then . else . as $in | "assertion violation @ \(m) => \($e)" | __assertion_violation__ | $in end)
else . end;
def assert(exp):
if __assert__ then
(exp as $e | if $e then . else . as $in | "assertion violation: \($e)" | __assertion_violation__ | $in end)
else . end;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment