summary refs log tree commit diff
diff options
context:
space:
mode:
authorMaximilian Bosch <maximilian@mbosch.me>2021-08-26 00:28:49 +0200
committerMaximilian Bosch <maximilian@mbosch.me>2021-08-26 00:28:49 +0200
commit5773ae93f75cfd504d7971c1b26955fa50a00744 (patch)
treed5f19ba068e4efee4f711e1dad969cf3b3c61470
parentb6d3c9f821b704fbfb68d20a76520fa9e160df36 (diff)
downloadnixpkgs-5773ae93f75cfd504d7971c1b26955fa50a00744.tar
nixpkgs-5773ae93f75cfd504d7971c1b26955fa50a00744.tar.gz
nixpkgs-5773ae93f75cfd504d7971c1b26955fa50a00744.tar.bz2
nixpkgs-5773ae93f75cfd504d7971c1b26955fa50a00744.tar.lz
nixpkgs-5773ae93f75cfd504d7971c1b26955fa50a00744.tar.xz
nixpkgs-5773ae93f75cfd504d7971c1b26955fa50a00744.tar.zst
nixpkgs-5773ae93f75cfd504d7971c1b26955fa50a00744.zip
lib/generators: move limit detection into `withRecursion`
As suggested in #131205.

Now it's possible to pretty-print a value with `lib.generators` like
this:

    with lib.generators;
    toPretty { }
      (withRecursion { depthLimit = 10; } /* arbitrarily complex value */)

Also, this can be used for any other pretty-printer now if needed.
-rw-r--r--lib/generators.nix45
-rw-r--r--lib/options.nix4
-rw-r--r--lib/tests/misc.nix7
3 files changed, 37 insertions, 19 deletions
diff --git a/lib/generators.nix b/lib/generators.nix
index b1639b677f6..9271b9746ae 100644
--- a/lib/generators.nix
+++ b/lib/generators.nix
@@ -195,6 +195,30 @@ rec {
     */
   toYAML = {}@args: toJSON args;
 
+  withRecursion =
+    args@{
+      /* If this option is not null, `toPretty` will stop evaluating at a certain depth */
+      depthLimit
+      /* If this option is true, an error will be thrown, if a certain given depth is exceeded */
+    , throwOnDepthLimit ? true
+    }:
+      assert builtins.isInt depthLimit;
+      let
+        transform = depth:
+          if depthLimit != null && depth > depthLimit then
+            if throwOnDepthLimit
+              then throw "Exceeded maximum eval-depth limit of ${toString depthLimit} while trying to pretty-print with `generators.withRecursion'!"
+              else const "<unevaluated>"
+          else id;
+        mapAny = with builtins; depth: v:
+          let
+            evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
+          in
+            if isAttrs v then mapAttrs (const evalNext) v
+            else if isList v then map evalNext v
+            else transform (depth + 1) v;
+      in
+        mapAny 0;
 
   /* Pretty print a value, akin to `builtins.trace`.
     * Should probably be a builtin as well.
@@ -205,23 +229,14 @@ rec {
        (This means fn is type Val -> String.) */
     allowPrettyValues ? false,
     /* If this option is true, the output is indented with newlines for attribute sets and lists */
-    multiline ? true,
-    /* If this option is not null, `toPretty` will stop evaluating at a certain depth */
-    depthLimit ? null,
-    /* If this option is true, an error will be thrown, if a certain given depth is exceeded */
-    throwOnDepthLimit ? false
+    multiline ? true
   }@args:
-    assert depthLimit != null -> builtins.isInt depthLimit;
-    assert throwOnDepthLimit -> depthLimit != null;
     let
-    go = depth: indent: v: with builtins;
+    go = indent: v: with builtins;
     let     isPath   = v: typeOf v == "path";
             introSpace = if multiline then "\n${indent}  " else " ";
             outroSpace = if multiline then "\n${indent}" else " ";
-    in if depthLimit != null && depth > depthLimit then
-      if throwOnDepthLimit then throw "Exceeded maximum eval-depth limit of ${toString depthLimit} while trying to pretty-print with `generators.toPretty'!"
-      else "<unevaluated>"
-    else if   isInt      v then toString v
+    in if   isInt      v then toString v
     else if isFloat    v then "~${toString v}"
     else if isString   v then
       let
@@ -243,7 +258,7 @@ rec {
     else if isList     v then
       if v == [] then "[ ]"
       else "[" + introSpace
-        + libStr.concatMapStringsSep introSpace (go (depth + 1) (indent + "  ")) v
+        + libStr.concatMapStringsSep introSpace (go (indent + "  ")) v
         + outroSpace + "]"
     else if isFunction v then
       let fna = lib.functionArgs v;
@@ -262,10 +277,10 @@ rec {
       else "{" + introSpace
           + libStr.concatStringsSep introSpace (libAttr.mapAttrsToList
               (name: value:
-                "${libStr.escapeNixIdentifier name} = ${go (depth + 1) (indent + "  ") value};") v)
+                "${libStr.escapeNixIdentifier name} = ${go (indent + "  ") value};") v)
         + outroSpace + "}"
     else abort "generators.toPretty: should never happen (v = ${v})";
-  in go 0 "";
+  in go "";
 
   # PLIST handling
   toPlist = {}: v: let
diff --git a/lib/options.nix b/lib/options.nix
index 4b39ce824ea..119a67fb7d8 100644
--- a/lib/options.nix
+++ b/lib/options.nix
@@ -247,7 +247,9 @@ rec {
   showDefs = defs: concatMapStrings (def:
     let
       # Pretty print the value for display, if successful
-      prettyEval = builtins.tryEval (lib.generators.toPretty { depthLimit = 10; } def.value);
+      prettyEval = builtins.tryEval
+        (lib.generators.toPretty { }
+          (lib.generators.withRecursion { depthLimit = 10; throwOnDepthLimit = false; } def.value));
       # Split it into its lines
       lines = filter (v: ! isList v) (builtins.split "\n" prettyEval.value);
       # Only display the first 5 lines, and indent them for better visibility
diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix
index 110716ca691..00eeaa2a77d 100644
--- a/lib/tests/misc.nix
+++ b/lib/tests/misc.nix
@@ -534,8 +534,8 @@ runTests {
       a.b = 1;
       a.c = a;
     in {
-      expr = generators.toPretty { depthLimit = 2; } a;
-      expected = "{\n  b = 1;\n  c = {\n    b = 1;\n    c = {\n      b = <unevaluated>;\n      c = <unevaluated>;\n    };\n  };\n}";
+      expr = generators.toPretty { } (generators.withRecursion { throwOnDepthLimit = false; depthLimit = 2; } a);
+      expected = "{\n  b = 1;\n  c = {\n    b = \"<unevaluated>\";\n    c = {\n      b = \"<unevaluated>\";\n      c = \"<unevaluated>\";\n    };\n  };\n}";
     };
 
   testToPrettyLimitThrow =
@@ -543,7 +543,8 @@ runTests {
       a.b = 1;
       a.c = a;
     in {
-      expr = (builtins.tryEval (generators.toPretty { depthLimit = 2; throwOnDepthLimit = true; } a)).success;
+      expr = (builtins.tryEval
+        (generators.toPretty { } (generators.withRecursion { depthLimit = 2; } a))).success;
       expected = false;
     };