diff options
| author | Maximilian Bosch <maximilian@mbosch.me> | 2021-07-23 10:43:44 +0200 |
|---|---|---|
| committer | Maximilian Bosch <maximilian@mbosch.me> | 2021-08-25 23:18:26 +0200 |
| commit | 55ea29fd8c7a4b130a114b0f06b3fadfaf028356 (patch) | |
| tree | 328cf78611ffedb3a7a06a778596f5469eff076f /lib/generators.nix | |
| parent | Merge pull request #135624 from paperdigits/dateutils-0.4.9 (diff) | |
| download | nixpkgs-55ea29fd8c7a4b130a114b0f06b3fadfaf028356.tar.gz | |
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/generators.nix')
| -rw-r--r-- | lib/generators.nix | 24 |
1 files changed, 17 insertions, 7 deletions
diff --git a/lib/generators.nix b/lib/generators.nix index bcb0f371a9b5..b1639b677f65 100644 --- a/lib/generators.nix +++ b/lib/generators.nix @@ -205,13 +205,23 @@ 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 - }@args: let - go = indent: v: with builtins; + 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 + }@args: + assert depthLimit != null -> builtins.isInt depthLimit; + assert throwOnDepthLimit -> depthLimit != null; + let + go = depth: 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 isInt v then toString v + 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 else if isFloat v then "~${toString v}" else if isString v then let @@ -233,7 +243,7 @@ rec { else if isList v then if v == [] then "[ ]" else "[" + introSpace - + libStr.concatMapStringsSep introSpace (go (indent + " ")) v + + libStr.concatMapStringsSep introSpace (go (depth + 1) (indent + " ")) v + outroSpace + "]" else if isFunction v then let fna = lib.functionArgs v; @@ -252,10 +262,10 @@ rec { else "{" + introSpace + libStr.concatStringsSep introSpace (libAttr.mapAttrsToList (name: value: - "${libStr.escapeNixIdentifier name} = ${go (indent + " ") value};") v) + "${libStr.escapeNixIdentifier name} = ${go (depth + 1) (indent + " ") value};") v) + outroSpace + "}" else abort "generators.toPretty: should never happen (v = ${v})"; - in go ""; + in go 0 ""; # PLIST handling toPlist = {}: v: let |
