at 24.11-pre 7.7 kB view raw
1/** 2 Collection of functions useful for debugging 3 broken nix expressions. 4 5 * `trace`-like functions take two values, print 6 the first to stderr and return the second. 7 * `traceVal`-like functions take one argument 8 which both printed and returned. 9 * `traceSeq`-like functions fully evaluate their 10 traced value before printing (not just to weak 11 head normal form like trace does by default). 12 * Functions that end in `-Fn` take an additional 13 function as their first argument, which is applied 14 to the traced value before it is printed. 15*/ 16{ lib }: 17let 18 inherit (lib) 19 isList 20 isAttrs 21 substring 22 attrValues 23 concatLists 24 const 25 elem 26 generators 27 id 28 mapAttrs 29 trace; 30in 31 32rec { 33 34 # -- TRACING -- 35 36 /** 37 Conditionally trace the supplied message, based on a predicate. 38 39 40 # Inputs 41 42 `pred` 43 44 : Predicate to check 45 46 `msg` 47 48 : Message that should be traced 49 50 `x` 51 52 : Value to return 53 54 # Type 55 56 ``` 57 traceIf :: bool -> string -> a -> a 58 ``` 59 60 # Examples 61 :::{.example} 62 ## `lib.debug.traceIf` usage example 63 64 ```nix 65 traceIf true "hello" 3 66 trace: hello 67 => 3 68 ``` 69 70 ::: 71 */ 72 traceIf = 73 pred: 74 msg: 75 x: if pred then trace msg x else x; 76 77 /** 78 Trace the supplied value after applying a function to it, and 79 return the original value. 80 81 82 # Inputs 83 84 `f` 85 86 : Function to apply 87 88 `x` 89 90 : Value to trace and return 91 92 # Type 93 94 ``` 95 traceValFn :: (a -> b) -> a -> a 96 ``` 97 98 # Examples 99 :::{.example} 100 ## `lib.debug.traceValFn` usage example 101 102 ```nix 103 traceValFn (v: "mystring ${v}") "foo" 104 trace: mystring foo 105 => "foo" 106 ``` 107 108 ::: 109 */ 110 traceValFn = 111 f: 112 x: trace (f x) x; 113 114 /** 115 Trace the supplied value and return it. 116 117 # Inputs 118 119 `x` 120 121 : Value to trace and return 122 123 # Type 124 125 ``` 126 traceVal :: a -> a 127 ``` 128 129 # Examples 130 :::{.example} 131 ## `lib.debug.traceVal` usage example 132 133 ```nix 134 traceVal 42 135 # trace: 42 136 => 42 137 ``` 138 139 ::: 140 */ 141 traceVal = traceValFn id; 142 143 /** 144 `builtins.trace`, but the value is `builtins.deepSeq`ed first. 145 146 147 # Inputs 148 149 `x` 150 151 : The value to trace 152 153 `y` 154 155 : The value to return 156 157 # Type 158 159 ``` 160 traceSeq :: a -> b -> b 161 ``` 162 163 # Examples 164 :::{.example} 165 ## `lib.debug.traceSeq` usage example 166 167 ```nix 168 trace { a.b.c = 3; } null 169 trace: { a = <CODE>; } 170 => null 171 traceSeq { a.b.c = 3; } null 172 trace: { a = { b = { c = 3; }; }; } 173 => null 174 ``` 175 176 ::: 177 */ 178 traceSeq = 179 x: 180 y: trace (builtins.deepSeq x x) y; 181 182 /** 183 Like `traceSeq`, but only evaluate down to depth n. 184 This is very useful because lots of `traceSeq` usages 185 lead to an infinite recursion. 186 187 188 # Inputs 189 190 `depth` 191 192 : 1\. Function argument 193 194 `x` 195 196 : 2\. Function argument 197 198 `y` 199 200 : 3\. Function argument 201 202 # Type 203 204 ``` 205 traceSeqN :: Int -> a -> b -> b 206 ``` 207 208 # Examples 209 :::{.example} 210 ## `lib.debug.traceSeqN` usage example 211 212 ```nix 213 traceSeqN 2 { a.b.c = 3; } null 214 trace: { a = { b = {}; }; } 215 => null 216 ``` 217 218 ::: 219 */ 220 traceSeqN = depth: x: y: 221 let snip = v: if isList v then noQuotes "[]" v 222 else if isAttrs v then noQuotes "{}" v 223 else v; 224 noQuotes = str: v: { __pretty = const str; val = v; }; 225 modify = n: fn: v: if (n == 0) then fn v 226 else if isList v then map (modify (n - 1) fn) v 227 else if isAttrs v then mapAttrs 228 (const (modify (n - 1) fn)) v 229 else v; 230 in trace (generators.toPretty { allowPrettyValues = true; } 231 (modify depth snip x)) y; 232 233 /** 234 A combination of `traceVal` and `traceSeq` that applies a 235 provided function to the value to be traced after `deepSeq`ing 236 it. 237 238 239 # Inputs 240 241 `f` 242 243 : Function to apply 244 245 `v` 246 247 : Value to trace 248 */ 249 traceValSeqFn = 250 f: 251 v: traceValFn f (builtins.deepSeq v v); 252 253 /** 254 A combination of `traceVal` and `traceSeq`. 255 256 # Inputs 257 258 `v` 259 260 : Value to trace 261 262 */ 263 traceValSeq = traceValSeqFn id; 264 265 /** 266 A combination of `traceVal` and `traceSeqN` that applies a 267 provided function to the value to be traced. 268 269 270 # Inputs 271 272 `f` 273 274 : Function to apply 275 276 `depth` 277 278 : 2\. Function argument 279 280 `v` 281 282 : Value to trace 283 */ 284 traceValSeqNFn = 285 f: 286 depth: 287 v: traceSeqN depth (f v) v; 288 289 /** 290 A combination of `traceVal` and `traceSeqN`. 291 292 # Inputs 293 294 `depth` 295 296 : 1\. Function argument 297 298 `v` 299 300 : Value to trace 301 */ 302 traceValSeqN = traceValSeqNFn id; 303 304 /** 305 Trace the input and output of a function `f` named `name`, 306 both down to `depth`. 307 308 This is useful for adding around a function call, 309 to see the before/after of values as they are transformed. 310 311 312 # Inputs 313 314 `depth` 315 316 : 1\. Function argument 317 318 `name` 319 320 : 2\. Function argument 321 322 `f` 323 324 : 3\. Function argument 325 326 `v` 327 328 : 4\. Function argument 329 330 331 # Examples 332 :::{.example} 333 ## `lib.debug.traceFnSeqN` usage example 334 335 ```nix 336 traceFnSeqN 2 "id" (x: x) { a.b.c = 3; } 337 trace: { fn = "id"; from = { a.b = {}; }; to = { a.b = {}; }; } 338 => { a.b.c = 3; } 339 ``` 340 341 ::: 342 */ 343 traceFnSeqN = depth: name: f: v: 344 let res = f v; 345 in lib.traceSeqN 346 (depth + 1) 347 { 348 fn = name; 349 from = v; 350 to = res; 351 } 352 res; 353 354 355 # -- TESTING -- 356 357 /** 358 Evaluates a set of tests. 359 360 A test is an attribute set `{expr, expected}`, 361 denoting an expression and its expected result. 362 363 The result is a `list` of __failed tests__, each represented as 364 `{name, expected, result}`, 365 366 - expected 367 - What was passed as `expected` 368 - result 369 - The actual `result` of the test 370 371 Used for regression testing of the functions in lib; see 372 tests.nix for more examples. 373 374 Important: Only attributes that start with `test` are executed. 375 376 - If you want to run only a subset of the tests add the attribute `tests = ["testName"];` 377 378 379 # Inputs 380 381 `tests` 382 383 : Tests to run 384 385 # Type 386 387 ``` 388 runTests :: { 389 tests = [ String ]; 390 ${testName} :: { 391 expr :: a; 392 expected :: a; 393 }; 394 } 395 -> 396 [ 397 { 398 name :: String; 399 expected :: a; 400 result :: a; 401 } 402 ] 403 ``` 404 405 # Examples 406 :::{.example} 407 ## `lib.debug.runTests` usage example 408 409 ```nix 410 runTests { 411 testAndOk = { 412 expr = lib.and true false; 413 expected = false; 414 }; 415 testAndFail = { 416 expr = lib.and true false; 417 expected = true; 418 }; 419 } 420 -> 421 [ 422 { 423 name = "testAndFail"; 424 expected = true; 425 result = false; 426 } 427 ] 428 ``` 429 430 ::: 431 */ 432 runTests = 433 tests: concatLists (attrValues (mapAttrs (name: test: 434 let testsToRun = if tests ? tests then tests.tests else []; 435 in if (substring 0 4 name == "test" || elem name testsToRun) 436 && ((testsToRun == []) || elem name tests.tests) 437 && (test.expr != test.expected) 438 439 then [ { inherit name; expected = test.expected; result = test.expr; } ] 440 else [] ) tests)); 441 442 /** 443 Create a test assuming that list elements are `true`. 444 445 446 # Inputs 447 448 `expr` 449 450 : 1\. Function argument 451 452 453 # Examples 454 :::{.example} 455 ## `lib.debug.testAllTrue` usage example 456 457 ```nix 458 { testX = allTrue [ true ]; } 459 ``` 460 461 ::: 462 */ 463 testAllTrue = expr: { inherit expr; expected = map (x: true) expr; }; 464}