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