summary refs log tree commit diff
path: root/lib/tests
diff options
context:
space:
mode:
authorMaximilian Bosch <maximilian@mbosch.me>2021-07-23 10:43:44 +0200
committerMaximilian Bosch <maximilian@mbosch.me>2021-08-25 23:18:26 +0200
commit55ea29fd8c7a4b130a114b0f06b3fadfaf028356 (patch)
tree328cf78611ffedb3a7a06a778596f5469eff076f /lib/tests
parent81f586e8b8d808c58116267cb9c0f45b3e61e4c8 (diff)
downloadnixpkgs-55ea29fd8c7a4b130a114b0f06b3fadfaf028356.tar
nixpkgs-55ea29fd8c7a4b130a114b0f06b3fadfaf028356.tar.gz
nixpkgs-55ea29fd8c7a4b130a114b0f06b3fadfaf028356.tar.bz2
nixpkgs-55ea29fd8c7a4b130a114b0f06b3fadfaf028356.tar.lz
nixpkgs-55ea29fd8c7a4b130a114b0f06b3fadfaf028356.tar.xz
nixpkgs-55ea29fd8c7a4b130a114b0f06b3fadfaf028356.tar.zst
nixpkgs-55ea29fd8c7a4b130a114b0f06b3fadfaf028356.zip
lib/generators/toPretty: add evaluation-limit
When having e.g. recursive attr-set, it cannot be printed which is
solved by Nix itself like this:

    $ nix-instantiate --eval -E 'let a.b = 1; a.c = a; in builtins.trace a 1'
    trace: { b = 1; c = <CYCLE>; }
    1

However, `generators.toPretty` tries to evaluate something until it's
done which can result in a spurious `stack-overflow`-error:

    $ nix-instantiate --eval -E 'with import <nixpkgs/lib>; generators.toPretty {  } (mkOption { type = types.str; })'
    error: stack overflow (possible infinite recursion)

Those attr-sets are in fact rather common, one example is shown above, a
`types.<type>`-declaration is such an example. By adding an optional
`depthLimit`-argument, `toPretty` will stop evaluating as soon as the
limit is reached:

    $ nix-instantiate --eval -E 'with import ./Projects/nixpkgs-update-int/lib; generators.toPretty { depthLimit = 2; } (mkOption { type = types.str; })' |xargs -0 echo -e
    "{
      _type = \"option\";
      type = {
        _type = \"option-type\";
        check = <function>;
        deprecationMessage = null;
        description = \"string\";
        emptyValue = { };
        functor = {
          binOp = <unevaluated>;
          name = <unevaluated>;
          payload = <unevaluated>;
          type = <unevaluated>;
          wrapped = <unevaluated>;
        };
        getSubModules = null;
        getSubOptions = <function>;
        merge = <function>;
        name = \"str\";
        nestedTypes = { };
        substSubModules = <function>;
        typeMerge = <function>;
      };
    }"

Optionally, it's also possible to let `toPretty` throw an error if the
limit is exceeded.
Diffstat (limited to 'lib/tests')
-rw-r--r--lib/tests/misc.nix18
1 files changed, 18 insertions, 0 deletions
diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix
index 4b2e5afc1d6..110716ca691 100644
--- a/lib/tests/misc.nix
+++ b/lib/tests/misc.nix
@@ -529,6 +529,24 @@ runTests {
     };
   };
 
+  testToPrettyLimit =
+    let
+      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}";
+    };
+
+  testToPrettyLimitThrow =
+    let
+      a.b = 1;
+      a.c = a;
+    in {
+      expr = (builtins.tryEval (generators.toPretty { depthLimit = 2; throwOnDepthLimit = true; } a)).success;
+      expected = false;
+    };
+
   testToPrettyMultiline = {
     expr = mapAttrs (const (generators.toPretty { })) rec {
       list = [ 3 4 [ false ] ];