lib/debug: document module & functions, prune imports
This commit is contained in:
parent
e49f40e1ca
commit
5012c49fc0
120
lib/debug.nix
120
lib/debug.nix
|
@ -1,28 +1,67 @@
|
||||||
|
/* Collection of functions useful for debugging
|
||||||
|
broken nix expressions.
|
||||||
|
|
||||||
|
* `trace`-like functions take two values, print
|
||||||
|
the first to stderr and return the second.
|
||||||
|
* `traceVal`-like functions take one argument
|
||||||
|
which both printed and returned.
|
||||||
|
* `traceSeq`-like functions fully evaluate their
|
||||||
|
traced value before printing (not just to “weak
|
||||||
|
head normal form” like trace does by default).
|
||||||
|
* Functions that end in `-Fn` take an additional
|
||||||
|
function as their first argument, which is applied
|
||||||
|
to the traced value before it is printed.
|
||||||
|
*/
|
||||||
{ lib }:
|
{ lib }:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
inherit (builtins) trace isAttrs isList isInt
|
||||||
inherit (builtins) trace attrNamesToStr isAttrs isList isInt
|
head substring attrNames;
|
||||||
isString isBool head substring attrNames;
|
inherit (lib) id elem isFunction;
|
||||||
|
|
||||||
inherit (lib) all id mapAttrsFlatten elem isFunction;
|
|
||||||
|
|
||||||
in
|
in
|
||||||
|
|
||||||
rec {
|
rec {
|
||||||
|
|
||||||
traceIf = p: msg: x: if p then trace msg x else x;
|
# -- TRACING --
|
||||||
|
|
||||||
|
/* Trace msg, but only if pred is true.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
traceIf true "hello" 3
|
||||||
|
trace: hello
|
||||||
|
=> 3
|
||||||
|
*/
|
||||||
|
traceIf = pred: msg: x: if pred then trace msg x else x;
|
||||||
|
|
||||||
|
/* Trace the value and also return it.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
traceValFn (v: "mystring ${v}") "foo"
|
||||||
|
trace: mystring foo
|
||||||
|
=> "foo"
|
||||||
|
*/
|
||||||
traceValFn = f: x: trace (f x) x;
|
traceValFn = f: x: trace (f x) x;
|
||||||
traceVal = traceValFn id;
|
traceVal = traceValFn id;
|
||||||
# strict trace functions (traced structure is fully evaluated and printed)
|
|
||||||
|
|
||||||
/* `builtins.trace`, but the value is `builtins.deepSeq`ed first. */
|
/* `builtins.trace`, but the value is `builtins.deepSeq`ed first.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
trace { a.b.c = 3; } null
|
||||||
|
trace: { a = <CODE>; }
|
||||||
|
=> null
|
||||||
|
traceSeq { a.b.c = 3; } null
|
||||||
|
trace: { a = { b = { c = 3; }; }; }
|
||||||
|
=> null
|
||||||
|
*/
|
||||||
traceSeq = x: y: trace (builtins.deepSeq x x) y;
|
traceSeq = x: y: trace (builtins.deepSeq x x) y;
|
||||||
|
|
||||||
/* Like `traceSeq`, but only down to depth n.
|
/* Like `traceSeq`, but only evaluate down to depth n.
|
||||||
* This is very useful because lots of `traceSeq` usages
|
This is very useful because lots of `traceSeq` usages
|
||||||
* lead to an infinite recursion.
|
lead to an infinite recursion.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
traceSeqN 2 { a.b.c = 3; } null
|
||||||
|
trace: { a = { b = {…}; }; }
|
||||||
|
=> null
|
||||||
*/
|
*/
|
||||||
traceSeqN = depth: x: y: with lib;
|
traceSeqN = depth: x: y: with lib;
|
||||||
let snip = v: if isList v then noQuotes "[…]" v
|
let snip = v: if isList v then noQuotes "[…]" v
|
||||||
|
@ -37,15 +76,42 @@ rec {
|
||||||
in trace (generators.toPretty { allowPrettyValues = true; }
|
in trace (generators.toPretty { allowPrettyValues = true; }
|
||||||
(modify depth snip x)) y;
|
(modify depth snip x)) y;
|
||||||
|
|
||||||
/* `traceSeq`, but the same value is traced and returned */
|
/* A combination of `traceVal` and `traceSeq` */
|
||||||
traceValSeqFn = f: v: traceVal f (builtins.deepSeq v v);
|
traceValSeqFn = f: v: traceVal f (builtins.deepSeq v v);
|
||||||
traceValSeq = traceValSeqFn id;
|
traceValSeq = traceValSeqFn id;
|
||||||
/* `traceValSeq` but with fixed depth */
|
|
||||||
|
/* A combination of `traceVal` and `traceSeqN`. */
|
||||||
traceValSeqNFn = f: depth: v: traceSeqN depth (f v) v;
|
traceValSeqNFn = f: depth: v: traceSeqN depth (f v) v;
|
||||||
traceValSeqN = traceValSeqNFn id;
|
traceValSeqN = traceValSeqNFn id;
|
||||||
|
|
||||||
|
|
||||||
# this can help debug your code as well - designed to not produce thousands of lines
|
# -- TESTING --
|
||||||
|
|
||||||
|
/* Evaluate a set of tests. A test is an attribute set {expr,
|
||||||
|
expected}, denoting an expression and its expected result. The
|
||||||
|
result is a list of failed tests, each represented as {name,
|
||||||
|
expected, actual}, denoting the attribute name of the failing
|
||||||
|
test and its expected and actual results. Used for regression
|
||||||
|
testing of the functions in lib; see tests.nix for an example.
|
||||||
|
Only tests having names starting with "test" are run.
|
||||||
|
Add attr { tests = ["testName"]; } to run these test only
|
||||||
|
*/
|
||||||
|
runTests = tests: lib.concatLists (lib.attrValues (lib.mapAttrs (name: test:
|
||||||
|
let testsToRun = if tests ? tests then tests.tests else [];
|
||||||
|
in if (substring 0 4 name == "test" || elem name testsToRun)
|
||||||
|
&& ((testsToRun == []) || elem name tests.tests)
|
||||||
|
&& (test.expr != test.expected)
|
||||||
|
|
||||||
|
then [ { inherit name; expected = test.expected; result = test.expr; } ]
|
||||||
|
else [] ) tests));
|
||||||
|
|
||||||
|
# create a test assuming that list elements are true
|
||||||
|
# usage: { testX = allTrue [ true ]; }
|
||||||
|
testAllTrue = expr: { inherit expr; expected = map (x: true) expr; };
|
||||||
|
|
||||||
|
|
||||||
|
# -- DEPRECATED --
|
||||||
|
|
||||||
traceShowVal = x: trace (showVal x) x;
|
traceShowVal = x: trace (showVal x) x;
|
||||||
traceShowValMarked = str: x: trace (str + showVal x) x;
|
traceShowValMarked = str: x: trace (str + showVal x) x;
|
||||||
|
|
||||||
|
@ -100,28 +166,6 @@ rec {
|
||||||
+ "and will be removed in the next release." )
|
+ "and will be removed in the next release." )
|
||||||
(lib.mapAttrs (a: v: lib.addErrorContext "while evaluating ${a}" v) attrs);
|
(lib.mapAttrs (a: v: lib.addErrorContext "while evaluating ${a}" v) attrs);
|
||||||
|
|
||||||
/* Evaluate a set of tests. A test is an attribute set {expr,
|
|
||||||
expected}, denoting an expression and its expected result. The
|
|
||||||
result is a list of failed tests, each represented as {name,
|
|
||||||
expected, actual}, denoting the attribute name of the failing
|
|
||||||
test and its expected and actual results. Used for regression
|
|
||||||
testing of the functions in lib; see tests.nix for an example.
|
|
||||||
Only tests having names starting with "test" are run.
|
|
||||||
Add attr { tests = ["testName"]; } to run these test only
|
|
||||||
*/
|
|
||||||
runTests = tests: lib.concatLists (lib.attrValues (lib.mapAttrs (name: test:
|
|
||||||
let testsToRun = if tests ? tests then tests.tests else [];
|
|
||||||
in if (substring 0 4 name == "test" || elem name testsToRun)
|
|
||||||
&& ((testsToRun == []) || elem name tests.tests)
|
|
||||||
&& (test.expr != test.expected)
|
|
||||||
|
|
||||||
then [ { inherit name; expected = test.expected; result = test.expr; } ]
|
|
||||||
else [] ) tests));
|
|
||||||
|
|
||||||
# create a test assuming that list elements are true
|
|
||||||
# usage: { testX = allTrue [ true ]; }
|
|
||||||
testAllTrue = expr: { inherit expr; expected = map (x: true) expr; };
|
|
||||||
|
|
||||||
# example: (traceCallXml "myfun" id 3) will output something like
|
# example: (traceCallXml "myfun" id 3) will output something like
|
||||||
# calling myfun arg 1: 3 result: 3
|
# calling myfun arg 1: 3 result: 3
|
||||||
# this forces deep evaluation of all arguments and the result!
|
# this forces deep evaluation of all arguments and the result!
|
||||||
|
|
Loading…
Reference in New Issue