Skip to content

Commit 36ea2bb

Browse files
committed
lib.modules: Add mergeAttrDefinitionsWithPrio
This will let us make assertions involving _module.args.pkgs, which is not an option but a value attribute, and therefore doesn't have its own highestPrio to inspect. The new function gives us that info.
1 parent a742767 commit 36ea2bb

3 files changed

Lines changed: 65 additions & 0 deletions

File tree

lib/modules.nix

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,47 @@ let
910910
else opt // { type = opt.type.substSubModules opt.options; options = []; };
911911

912912

913+
/*
914+
Merge an option's definitions in a way that preserves the priority of the
915+
individual attributes in the option value.
916+
917+
This does not account for all option semantics, such as readOnly.
918+
919+
Type:
920+
option -> attrsOf { highestPrio, value }
921+
*/
922+
mergeAttrDefinitionsWithPrio = opt:
923+
let subAttrDefs =
924+
lib.concatMap
925+
({ value, ... }@def:
926+
map
927+
(value: def // { inherit value; })
928+
(lib.pushDownProperties value)
929+
)
930+
opt.definitionsWithLocations;
931+
defsByAttr =
932+
lib.zipAttrs (
933+
lib.concatLists (
934+
lib.concatMap
935+
({ value, ... }@def:
936+
map
937+
(lib.mapAttrsToList (k: value: { ${k} = def // { inherit value; }; }))
938+
(lib.pushDownProperties value)
939+
)
940+
opt.definitionsWithLocations
941+
)
942+
);
943+
in
944+
assert opt.type.name == "attrsOf" || opt.type.name == "lazyAttrsOf";
945+
lib.mapAttrs
946+
(k: v:
947+
let merging = lib.mergeDefinitions (opt.loc ++ [k]) opt.type.nestedTypes.elemType v;
948+
in {
949+
value = merging.mergedValue;
950+
inherit (merging.defsFinal') highestPrio;
951+
})
952+
defsByAttr;
953+
913954
/* Properties. */
914955

915956
mkIf = condition: content:
@@ -1256,6 +1297,7 @@ private //
12561297
importJSON
12571298
importTOML
12581299
mergeDefinitions
1300+
mergeAttrDefinitionsWithPrio
12591301
mergeOptionDecls # should be private?
12601302
mkAfter
12611303
mkAliasAndWrapDefinitions

lib/tests/modules.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ checkConfigError() {
6161
# Shorthand meta attribute does not duplicate the config
6262
checkConfigOutput '^"one two"$' config.result ./shorthand-meta.nix
6363

64+
checkConfigOutput '^true$' config.result ./test-mergeAttrDefinitionsWithPrio.nix
65+
6466
# Check boolean option.
6567
checkConfigOutput '^false$' config.enable ./declare-enable.nix
6668
checkConfigError 'The option .* does not exist. Definition values:\n\s*- In .*: true' config.enable ./define-enable.nix
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{ lib, options, ... }:
2+
3+
let
4+
defs = lib.modules.mergeAttrDefinitionsWithPrio options._module.args;
5+
assertLazy = pos: throw "${pos.file}:${toString pos.line}:${toString pos.column}: The test must not evaluate this the assertLazy thunk, but it did. Unexpected strictness leads to unexpected errors and performance problems.";
6+
in
7+
8+
{
9+
options.result = lib.mkOption { };
10+
config._module.args = {
11+
default = lib.mkDefault (assertLazy __curPos);
12+
regular = null;
13+
force = lib.mkForce (assertLazy __curPos);
14+
unused = assertLazy __curPos;
15+
};
16+
config.result =
17+
assert defs.default.highestPrio == (lib.mkDefault (assertLazy __curPos)).priority;
18+
assert defs.regular.highestPrio == lib.modules.defaultOverridePriority;
19+
assert defs.force.highestPrio == (lib.mkForce (assertLazy __curPos)).priority;
20+
true;
21+
}

0 commit comments

Comments
 (0)