NixOS modules: Add error context on module arguments evaluation.

+14 -5
lib/modules.nix
···
let
toClosureList = file: parentKey: imap (n: x:
if isAttrs x || isFunction x then
-
unifyModuleSyntax file "${parentKey}:anon-${toString n}" (unpackSubmodule applyIfFunction x args)
+
let key = "${parentKey}:anon-${toString n}"; in
+
unifyModuleSyntax file key (unpackSubmodule (applyIfFunction key) x args)
else
-
unifyModuleSyntax (toString x) (toString x) (applyIfFunction (import x) args));
+
let file = toString x; key = toString x; in
+
unifyModuleSyntax file key (applyIfFunction key (import x) args));
in
builtins.genericClosure {
startSet = toClosureList unknownModule "" modules;
···
config = removeAttrs m ["key" "_file" "require" "imports"];
};
-
applyIfFunction = f: arg@{ config, options, lib, ... }: if isFunction f then
+
applyIfFunction = key: f: args@{ config, options, lib, ... }: if isFunction f then
let
# Module arguments are resolved in a strict manner when attribute set
# deconstruction is used. As the arguments are now defined with the
···
# not their values. The values are forwarding the result of the
# evaluation of the option.
requiredArgs = builtins.attrNames (builtins.functionArgs f);
+
context = name: ''while evaluating the module argument `${name}' in "${key}":'';
extraArgs = builtins.listToAttrs (map (name: {
inherit name;
-
value = config._module.args.${name};
+
value = addErrorContext (context name)
+
(args.${name} or config._module.args.${name});
}) requiredArgs);
-
in f (extraArgs // arg)
+
+
# Note: we append in the opposite order such that we can add an error
+
# context on the explicited arguments of "args" too. This update
+
# operator is used to make the "args@{ ... }: with args.lib;" notation
+
# works.
+
in f (args // extraArgs)
else
f;
+10 -2
lib/tests/modules.sh
···
local attr=$1
shift;
local script="import ./default.nix { modules = [ $@ ];}"
-
nix-instantiate --timeout 1 -E "$script" -A "$attr" --eval-only
+
nix-instantiate --timeout 1 -E "$script" -A "$attr" --eval-only --show-trace
}
reportFailure() {
···
checkConfigOutput 'true' "$@" ./define-enable.nix ./define-loaOfSub-foo-enable-if.nix
# Check _module.args.
-
checkConfigOutput "true" config.enable ./declare-enable.nix ./custom-arg-define-enable.nix
+
set -- config.enable ./declare-enable.nix ./define-enable-with-custom-arg.nix
+
checkConfigError 'while evaluating the module argument .*custom.* in .*define-enable-with-custom-arg.nix.*:' "$@"
+
checkConfigOutput "true" "$@" ./define-_module-args-custom.nix
+
+
# Check that using _module.args on imports cause infinite recursions, with
+
# the proper error context.
+
set -- "$@" ./define-_module-args-custom.nix ./import-custom-arg.nix
+
checkConfigError 'while evaluating the module argument .*custom.* in .*import-custom-arg.nix.*:' "$@"
+
checkConfigError 'infinite recursion encountered' "$@"
# Check _module.check.
set -- config.enable ./declare-enable.nix ./define-enable.nix ./define-loaOfSub-foo.nix
-1
lib/tests/modules/custom-arg-define-enable.nix lib/tests/modules/define-enable-with-custom-arg.nix
···
{
config = {
-
_module.args.custom = true;
enable = custom;
};
}
+7
lib/tests/modules/define-_module-args-custom.nix
···
+
{ lib, ... }:
+
+
{
+
config = {
+
_module.args.custom = true;
+
};
+
}
+6
lib/tests/modules/import-custom-arg.nix
···
+
{ lib, custom, ... }:
+
+
{
+
imports = []
+
++ lib.optional custom ./define-enable-force.nix;
+
}