lib/asserts: use throw to display message for assertMsg

`assert` has the annoying property that it dumps a lot of code at the
user without the built in capability to display a nicer message. We have
worked around this using `assertMsg` which would *additionally* display
a nice message. We can do even better: By using `throw` we can make
evaluation fail before assert draws its conclusions and prevent it from
displaying the code making up the assert condition, so we get the nicer
message of `throw` and the syntactical convenience of `assert`.

Before:

    nix-repl> python.override { reproducibleBuild = true; stripBytecode = false; }
    trace: Deterministic builds require stripping bytecode.
    error: assertion (((lib).assertMsg  (reproducibleBuild -> stripBytecode))  "Deterministic builds require stripping bytecode.") failed at /home/lukas/src/nix/nixpkgs/pkgs/development/interpreters/python/cpython/2.7/default.nix:45:1

After:

    nix-repl> python.override { reproducibleBuild = true; stripBytecode = false; }
    error: Deterministic builds require stripping bytecode.
This commit is contained in:
sternenseemann 2022-01-17 19:12:54 +01:00 committed by sterni
parent 6733a4dffa
commit 48965506a1
3 changed files with 24 additions and 12 deletions

View File

@ -2,35 +2,33 @@
rec {
/* Print a trace message if pred is false.
/* Throw if pred is false, else return pred.
Intended to be used to augment asserts with helpful error messages.
Example:
assertMsg false "nope"
=> false
stderr> trace: nope
stderr> error: nope
assert (assertMsg ("foo" == "bar") "foo is not bar, silly"); ""
stderr> trace: foo is not bar, silly
stderr> assert failed at
assert assertMsg ("foo" == "bar") "foo is not bar, silly"; ""
stderr> error: foo is not bar, silly
Type:
assertMsg :: Bool -> String -> Bool
*/
# TODO(Profpatsch): add tests that check stderr
assertMsg = pred: msg:
if pred
then true
else builtins.trace msg false;
pred || builtins.throw msg;
/* Specialized `assertMsg` for checking if val is one of the elements
of a list. Useful for checking enums.
Example:
let sslLibrary = "libressl"
let sslLibrary = "libressl";
in assertOneOf "sslLibrary" sslLibrary [ "openssl" "bearssl" ]
=> false
stderr> trace: sslLibrary must be one of "openssl", "bearssl", but is: "libressl"
stderr> error: sslLibrary must be one of [
stderr> "openssl"
stderr> "bearssl"
stderr> ], but is: "libressl"
Type:
assertOneOf :: String -> ComparableVal -> List ComparableVal -> Bool

View File

@ -408,6 +408,18 @@
configuration.
</para>
</listitem>
<listitem>
<para>
<literal>lib.assertMsg</literal> and
<literal>lib.assertOneOf</literal> no longer return
<literal>false</literal> if the passed condition is
<literal>false</literal>, <literal>throw</literal>ing the
given error message instead (which makes the resulting error
message less cluttered). This will not impact the behaviour of
code using these functions as intended, namely as top-level
wrapper for <literal>assert</literal> conditions.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="sec-release-22.05-notable-changes">

View File

@ -132,6 +132,8 @@ In addition to numerous new and upgraded packages, this release has the followin
[settings-style](https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md)
configuration.
- `lib.assertMsg` and `lib.assertOneOf` no longer return `false` if the passed condition is `false`, `throw`ing the given error message instead (which makes the resulting error message less cluttered). This will not impact the behaviour of code using these functions as intended, namely as top-level wrapper for `assert` conditions.
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
## Other Notable Changes {#sec-release-22.05-notable-changes}