diff options
author | Maximilian Bosch <maximilian@mbosch.me> | 2021-08-26 00:28:49 +0200 |
---|---|---|
committer | Maximilian Bosch <maximilian@mbosch.me> | 2021-08-26 00:28:49 +0200 |
commit | 5773ae93f75cfd504d7971c1b26955fa50a00744 (patch) | |
tree | d5f19ba068e4efee4f711e1dad969cf3b3c61470 | |
parent | b6d3c9f821b704fbfb68d20a76520fa9e160df36 (diff) | |
download | nixpkgs-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.nix | 45 | ||||
-rw-r--r-- | lib/options.nix | 4 | ||||
-rw-r--r-- | lib/tests/misc.nix | 7 |
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; }; |