python310Packages.pydeconz: 104 -> 105
[NixPkgs.git] / lib / debug.nix
blobe3ca3352397eccc9e9443c2486de3d6b0d2da868
1 /* Collection of functions useful for debugging
2    broken nix expressions.
4    * `trace`-like functions take two values, print
5      the first to stderr and return the second.
6    * `traceVal`-like functions take one argument
7      which both printed and returned.
8    * `traceSeq`-like functions fully evaluate their
9      traced value before printing (not just to “weak
10      head normal form” like trace does by default).
11    * Functions that end in `-Fn` take an additional
12      function as their first argument, which is applied
13      to the traced value before it is printed.
15 { lib }:
16 let
17   inherit (lib)
18     isInt
19     attrNames
20     isList
21     isAttrs
22     substring
23     addErrorContext
24     attrValues
25     concatLists
26     concatStringsSep
27     const
28     elem
29     generators
30     head
31     id
32     isDerivation
33     isFunction
34     mapAttrs
35     trace;
38 rec {
40   # -- TRACING --
42   /* Conditionally trace the supplied message, based on a predicate.
44      Type: traceIf :: bool -> string -> a -> a
46      Example:
47        traceIf true "hello" 3
48        trace: hello
49        => 3
50   */
51   traceIf =
52     # Predicate to check
53     pred:
54     # Message that should be traced
55     msg:
56     # Value to return
57     x: if pred then trace msg x else x;
59   /* Trace the supplied value after applying a function to it, and
60      return the original value.
62      Type: traceValFn :: (a -> b) -> a -> a
64      Example:
65        traceValFn (v: "mystring ${v}") "foo"
66        trace: mystring foo
67        => "foo"
68   */
69   traceValFn =
70     # Function to apply
71     f:
72     # Value to trace and return
73     x: trace (f x) x;
75   /* Trace the supplied value and return it.
77      Type: traceVal :: a -> a
79      Example:
80        traceVal 42
81        # trace: 42
82        => 42
83   */
84   traceVal = traceValFn id;
86   /* `builtins.trace`, but the value is `builtins.deepSeq`ed first.
88      Type: traceSeq :: a -> b -> b
90      Example:
91        trace { a.b.c = 3; } null
92        trace: { a = <CODE>; }
93        => null
94        traceSeq { a.b.c = 3; } null
95        trace: { a = { b = { c = 3; }; }; }
96        => null
97   */
98   traceSeq =
99     # The value to trace
100     x:
101     # The value to return
102     y: trace (builtins.deepSeq x x) y;
104   /* Like `traceSeq`, but only evaluate down to depth n.
105      This is very useful because lots of `traceSeq` usages
106      lead to an infinite recursion.
108      Example:
109        traceSeqN 2 { a.b.c = 3; } null
110        trace: { a = { b = {…}; }; }
111        => null
112    */
113   traceSeqN = depth: x: y:
114     let snip = v: if      isList  v then noQuotes "[…]" v
115                   else if isAttrs v then noQuotes "{…}" v
116                   else v;
117         noQuotes = str: v: { __pretty = const str; val = v; };
118         modify = n: fn: v: if (n == 0) then fn v
119                       else if isList  v then map (modify (n - 1) fn) v
120                       else if isAttrs v then mapAttrs
121                         (const (modify (n - 1) fn)) v
122                       else v;
123     in trace (generators.toPretty { allowPrettyValues = true; }
124                (modify depth snip x)) y;
126   /* A combination of `traceVal` and `traceSeq` that applies a
127      provided function to the value to be traced after `deepSeq`ing
128      it.
129   */
130   traceValSeqFn =
131     # Function to apply
132     f:
133     # Value to trace
134     v: traceValFn f (builtins.deepSeq v v);
136   /* A combination of `traceVal` and `traceSeq`. */
137   traceValSeq = traceValSeqFn id;
139   /* A combination of `traceVal` and `traceSeqN` that applies a
140   provided function to the value to be traced. */
141   traceValSeqNFn =
142     # Function to apply
143     f:
144     depth:
145     # Value to trace
146     v: traceSeqN depth (f v) v;
148   /* A combination of `traceVal` and `traceSeqN`. */
149   traceValSeqN = traceValSeqNFn id;
151   /* Trace the input and output of a function `f` named `name`,
152   both down to `depth`.
154   This is useful for adding around a function call,
155   to see the before/after of values as they are transformed.
157      Example:
158        traceFnSeqN 2 "id" (x: x) { a.b.c = 3; }
159        trace: { fn = "id"; from = { a.b = {…}; }; to = { a.b = {…}; }; }
160        => { a.b.c = 3; }
161   */
162   traceFnSeqN = depth: name: f: v:
163     let res = f v;
164     in lib.traceSeqN
165         (depth + 1)
166         {
167           fn = name;
168           from = v;
169           to = res;
170         }
171         res;
174   # -- TESTING --
176   /* Evaluate a set of tests.  A test is an attribute set `{expr,
177      expected}`, denoting an expression and its expected result.  The
178      result is a list of failed tests, each represented as `{name,
179      expected, actual}`, denoting the attribute name of the failing
180      test and its expected and actual results.
182      Used for regression testing of the functions in lib; see
183      tests.nix for an example. Only tests having names starting with
184      "test" are run.
186      Add attr { tests = ["testName"]; } to run these tests only.
187   */
188   runTests =
189     # Tests to run
190     tests: concatLists (attrValues (mapAttrs (name: test:
191     let testsToRun = if tests ? tests then tests.tests else [];
192     in if (substring 0 4 name == "test" ||  elem name testsToRun)
193        && ((testsToRun == []) || elem name tests.tests)
194        && (test.expr != test.expected)
196       then [ { inherit name; expected = test.expected; result = test.expr; } ]
197       else [] ) tests));
199   /* Create a test assuming that list elements are `true`.
201      Example:
202        { testX = allTrue [ true ]; }
203   */
204   testAllTrue = expr: { inherit expr; expected = map (x: true) expr; };
207   # -- DEPRECATED --
209   traceShowVal = x: trace (showVal x) x;
210   traceShowValMarked = str: x: trace (str + showVal x) x;
212   attrNamesToStr = a:
213     trace ( "Warning: `attrNamesToStr` is deprecated "
214           + "and will be removed in the next release. "
215           + "Please use more specific concatenation "
216           + "for your uses (`lib.concat(Map)StringsSep`)." )
217     (concatStringsSep "; " (map (x: "${x}=") (attrNames a)));
219   showVal =
220     trace ( "Warning: `showVal` is deprecated "
221           + "and will be removed in the next release, "
222           + "please use `traceSeqN`" )
223     (let
224       modify = v:
225         let pr = f: { __pretty = f; val = v; };
226         in   if isDerivation v then pr
227           (drv: "<δ:${drv.name}:${concatStringsSep ","
228                                  (attrNames drv)}>")
229         else if [] ==   v then pr (const "[]")
230         else if isList  v then pr (l: "[ ${go (head l)}, … ]")
231         else if isAttrs v then pr
232           (a: "{ ${ concatStringsSep ", " (attrNames a)} }")
233         else v;
234       go = x: generators.toPretty
235         { allowPrettyValues = true; }
236         (modify x);
237     in go);
239   traceXMLVal = x:
240     trace ( "Warning: `traceXMLVal` is deprecated "
241           + "and will be removed in the next release. "
242           + "Please use `traceValFn builtins.toXML`." )
243     (trace (builtins.toXML x) x);
244   traceXMLValMarked = str: x:
245     trace ( "Warning: `traceXMLValMarked` is deprecated "
246           + "and will be removed in the next release. "
247           + "Please use `traceValFn (x: str + builtins.toXML x)`." )
248     (trace (str + builtins.toXML x) x);
250   # trace the arguments passed to function and its result
251   # maybe rewrite these functions in a traceCallXml like style. Then one function is enough
252   traceCall  = n: f: a: let t = n2: x: traceShowValMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a));
253   traceCall2 = n: f: a: b: let t = n2: x: traceShowValMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a) (t "arg 2" b));
254   traceCall3 = n: f: a: b: c: let t = n2: x: traceShowValMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a) (t "arg 2" b) (t "arg 3" c));
256   traceValIfNot = c: x:
257     trace ( "Warning: `traceValIfNot` is deprecated "
258           + "and will be removed in the next release. "
259           + "Please use `if/then/else` and `traceValSeq 1`.")
260     (if c x then true else traceSeq (showVal x) false);
263   addErrorContextToAttrs = attrs:
264     trace ( "Warning: `addErrorContextToAttrs` is deprecated "
265           + "and will be removed in the next release. "
266           + "Please use `builtins.addErrorContext` directly." )
267     (mapAttrs (a: v: addErrorContext "while evaluating ${a}" v) attrs);
269   # example: (traceCallXml "myfun" id 3) will output something like
270   # calling myfun arg 1: 3 result: 3
271   # this forces deep evaluation of all arguments and the result!
272   # note: if result doesn't evaluate you'll get no trace at all (FIXME)
273   #       args should be printed in any case
274   traceCallXml = a:
275     trace ( "Warning: `traceCallXml` is deprecated "
276           + "and will be removed in the next release. "
277           + "Please complain if you use the function regularly." )
278     (if !isInt a then
279       traceCallXml 1 "calling ${a}\n"
280     else
281       let nr = a;
282       in (str: expr:
283           if isFunction expr then
284             (arg:
285               traceCallXml (builtins.add 1 nr) "${str}\n arg ${builtins.toString nr} is \n ${builtins.toXML (builtins.seq arg arg)}" (expr arg)
286             )
287           else
288             let r = builtins.seq expr expr;
289             in trace "${str}\n result:\n${builtins.toXML r}" r
290       ));