Some args in NixOS module are not visible but you can still use them

Hi all,

When I trace a NixOS module

flake:

my-machine = nixpkgs.lib.nixosSystem {
    system = "x86_64-linux";
    specialArgs = {
       my-special-0 = "something";
       my-special-1 = "something";
       my-special-2 = "something";
    };
    modules = [
       ./configuration.nix

module:

{ ... }@all-args-passed:

{
  config = {
    system.stateVersion = builtins.trace (builtins.attrNames all-args-passed) "23.05";

I get

[ "config" "my-special-0" "my-special-1" "my-special-2" "lib" "modulesPath" "options" "specialArgs" ]

But then when I change it to:

{ pkgs, ... }@all-args-passed:

I can use pkgs

# stupid example:
config = {
    environment.etc.abc.text = "path: ${pkgs.vim}";

Could you please explain? This is totally against my understanding of the Nix language.

Thank you.

Yeah, this can be a bit confusing.

Nix being very dynamic, we can actually inspect the arguments that a module wants before providing them (using the functionArgs function, see noogle), and decide what to pass to the function based on what the function expects.

So the fact that you add pkgs to the argument list of the module, indeed influences what will be passed in as the argument set when calling the module.

The code responsible for doing that, is here: nixpkgs/modules.nix at 4b36fe99f4fa344332612dc5ffc215524c90ddb2 · NixOS/nixpkgs · GitHub

As explained in the comment in that function, the reason for this is basically that the module arguments are themselves defined in the module system, and so if we would be too eager and always pass every possible module argument to every module, then we’d end up with infinite recursions. So instead we supply only what the module actually needs.

2 Likes

That makes sense. Thank you.

Is it possible to find all arguments I can use without inspecting source code / reading documentation? Some kind of debugging function like builtins.trace to get list of all options?

All the other possible arguments should be in config._module.args, so you could try something like this:

my-machine = nixpkgs.lib.nixosSystem {
  system = "x86_64-linux";
  specialArgs = {
    my-special-0 = "something";
    my-special-1 = "something";
    my-special-2 = "something";
  };
  modules = [
    ./configuration.nix
    ({ config, lib, ... }@args: {
      options.args = lib.mkOption { type = lib.types.raw; };
      config.args = builtins.attrNames (args // config._module.args);
    })
  ];
};

And then in a nix repl do:

:lf .
nixosConfigurations.my-machine.config.args

Or just

  config = {
    system.stateVersion = builtins.trace (builtins.attrNames config._module.args) "23.05";

=>

trace: [ "baseModules" "extendModules" "extraModules" "moduleType" "modules" "noUserModules" "pkgs" "utils" ]

Thank you.

I didn’t know I can do this - that I don’t need a separate file. Thank you.