lib.importApply: init (#230588)

* lib.modules.importApply: init

Brings variables from rich scopes to modules defined in separate files.

A helper for functions in files that return a module.

* lib.modules.importApply: Edit doc

Generally improve the quality. Notes:

- Not rendered to the manual yet, so probably the syntax could be
  improved, but I have no way to test this now.

- The docs use `arg` vs `staticArg` in the code. This is intentional,
  because the doc is pretty clear about the role of `arg` whereas
  the code exists in a context where ambiguities are more harmful.

* Format
This commit is contained in:
Robert Hensing 2024-08-31 01:12:43 +02:00 committed by GitHub
parent ef0bb1fc69
commit 0abfc619bc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 75 additions and 0 deletions

View File

@ -1366,6 +1366,58 @@ let
]);
};
/**
`importApply file arg :: Path -> a -> Module`, where `import file :: a -> Module`
`importApply` imports a Nix expression file much like the module system would,
after passing an extra positional argument to the function in the file.
This function should be used when declaring a module in a file that refers to
values from a different scope, such as that in a flake.
It solves the problems of alternative solutions:
- While `importApply file arg` is _mostly_ equivalent to
`import file arg`, the latter returns a module without a location,
as `import` only returns the contained expression. This leads to worse
error messages.
- Using `specialArgs` to provide arguments to all modules. This effectively
creates an incomplete module, and requires the user of the module to
manually pass the `specialArgs` to the configuration, which is error-prone,
verbose, and unnecessary.
The nix file must contain a function that returns a module.
A module may itself be a function, so the file is often a function with two
positional arguments instead of one. See the example below.
This function does not add support for deduplication and `disabledModules`,
although that could be achieved by wrapping the returned module and setting
the `_key` module attribute.
The reason for this omission is that the file path is not guaranteed to be
a unique identifier for the module, as two instances of the module may
reference different `arg`s in their closures.
Example
# lib.nix
imports = [
(lib.modules.importApply ./module.nix { bar = bar; })
];
# module.nix
{ bar }:
{ lib, config, ... }:
{
options = ...;
config = ... bar ...;
}
*/
importApply =
modulePath: staticArg:
lib.setDefaultModuleLocation modulePath (import modulePath staticArg);
/* Use this function to import a JSON file as NixOS configuration.
modules.importJSON :: path -> attrs
@ -1415,6 +1467,7 @@ private //
filterOverrides'
fixMergeModules
fixupOptionType # should be private?
importApply
importJSON
importTOML
mergeDefinitions

View File

@ -247,6 +247,14 @@ checkConfigOutput '^true$' "$@" ./define-enable.nix ./define-attrsOfSub-if-foo-e
checkConfigOutput '^true$' "$@" ./define-enable.nix ./define-attrsOfSub-foo-if-enable.nix
checkConfigOutput '^true$' "$@" ./define-enable.nix ./define-attrsOfSub-foo-enable-if.nix
# Check importApply
checkConfigOutput '"abc"' config.value ./importApply.nix
# importApply does not set a key.
# Disabling the function file is not sufficient, because importApply can't reasonably assume that the key is unique.
# e.g. user may call it multiple times with different arguments and expect each of the module to apply.
# While this is excusable for the disabledModules aspect, it is not for the deduplication of modules.
checkConfigOutput '"abc"' config.value ./importApply-disabling.nix
# Check disabledModules with config definitions and option declarations.
set -- config.enable ./define-enable.nix ./declare-enable.nix
checkConfigOutput '^true$' "$@"

View File

@ -0,0 +1,4 @@
{
imports = [ ./importApply.nix ];
disabledModules = [ ./importApply-function.nix ];
}

View File

@ -0,0 +1,5 @@
{ foo }:
{ lib, config, ... }:
{
value = foo;
}

View File

@ -0,0 +1,5 @@
{ lib, ... }:
{
options.value = lib.mkOption { default = 1; };
imports = [ (lib.modules.importApply ./importApply-function.nix { foo = "abc"; }) ];
}