Aspell dictionaries are not available to enchant

I’m packaging a python application which uses pyenchant, and I’m having trouble making aspell dictionaries available to it. I have a derivation which looks like this (other dependencies omitted):

python3Packages.buildPythonApplication {
  pyproject = true;
  src = ./.;
  nativeBuildInputs = [ python3Packages.hatchling makeWrapper ];
  propagatedBuildInputs = with python3Packages; [
    pyenchant
    (aspellWithDicts (ps: with ps; [ en ]))
  ];

This creates a wrapper /nix/store/.../bin/.myprogram-wrapped which looks like

#! /nix/store/cjbyb45nxiqidj95c4k1mh65azn1x896-bash-5.2-p21/bin/bash -e
PATH=${PATH:+':'$PATH':'}
PATH=${PATH/':''/nix/store/59jaqbyf9dpya8985xhffkl14xfxwqy1-aspell-env/bin'':'/':'}
PATH='/nix/store/59jaqbyf9dpya8985xhffkl14xfxwqy1-aspell-env/bin'$PATH
PATH=${PATH#':'}
PATH=${PATH%':'}
export PATH
# <...snip...>
PATH=${PATH:+':'$PATH':'}
PATH=${PATH/':''/nix/store/hjck0h8qniy99mlgycwxyqgcjcqyahfm-aspell-0.60.8.1/bin'':'/':'}
PATH='/nix/store/hjck0h8qniy99mlgycwxyqgcjcqyahfm-aspell-0.60.8.1/bin'$PATH
PATH=${PATH#':'}
PATH=${PATH%':'}
export PATH

The top nix store path, aspell-env, contains dictionaries in lib/aspell, but the bottom one, which is a transitive dependency via pyenchant -> enchant2 -> aspell, does not contain any dictionaries.

When I run the program, I hit enchant.errors.DictNotFoundError: The file "/nix/store/hjck0h8qniy99mlgycwxyqgcjcqyahfm-aspell-0.60.8.1/lib/aspell/en" can not be opened for reading.

I wasn’t sure how to have enchant prioritize my aspellWithDicts over its own aspell dependency with no dictionaries. Instead, I was able to get this to work by manually setting ASPELL_CONF:

  postInstall = ''
    wrapProgram $out/bin/myprogram --set ASPELL_CONF "dict-dir ${aspellDicts.en}/lib/aspell;"
  '';

I found an old github issue regarding aspell in nixpkgs but not many other details.

  1. Is there a better way of doing this? The tool I’m packaging would ideally have access to dictionaries of several different languages, which I’m not sure is possible via the environment variable. Even if it is, this feels sort of fragile.
  2. Is this expected behavior, or is my aspellWithDicts in propagatedBuildInputs not taking precedence a regression in nixpkgs?

Thanks for your help!

1 Like

aspellWithDicts creates aspell program wrapped to set the environment variable you mention:

Looking at aspell enchant backend, it does not use the binary, though, it uses the library:

I would probably leave it up to user to install appropriate dictionaries with something like the following NixOS option:

environment.etc."aspell.conf".text = ''
  dict-dir ${pkgs.aspellWithDicts (ps: with ps; [ en ])}/lib/aspell
'';

but if you really want to hardcode it into the app, you will probably need to use the wrapper (preferably with --set-default so that user can install custom dictionaries).


propagatedBuildInputs mostly just make the listed dependencies available in derivations depending on the packages that propagate the dependencies as if the dependencies were specified in buildInputs. For example:

  • text-widget containing propagatedBuildInputs = [ enchant ];
  • my-editor containing buildInputs = [ text-widget ];

will result in roughly the same thing as if you had:

  • text-widget containing buildInputs = [ enchant ];
  • my-editor containing buildInputs = [ enchant text-widget ];

But not much else.

It is up to the derivation to turn those inputs to runtime dependencies in a way that the project can find them.

Now, buildPythonApplication will actually do some extra things on top of stdenv.mkDerivation: It will pick up propagated Python packages and add them to PYTHONPATH in the wrapper. It will also add propagated programs to PATH in the wrapper. That will ensure your aspell program with dictionaries will be present, even if nothing actually uses it.

Thanks for the explanations, I didn’t actually understand propagatedBuildInputs correctly until now; and for digging in to the enchant code!

pyenchant has some options to configure the library’s choice of dictionaries, but I don’t think it would be easy to integrate that with nix, and there are currently other users of this (internal/proprietary) tool that aren’t using nix, so accepting a semi-impure solution and putting an aspell config file in my nixos environment is good-enough solution for now. I appreciate the help.

I am trying to use pyenchant within a devShell.
Unfortunately the way with setting of ASPELL_CONF does not work for me.

Here is my flake.nix

{
  inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11";
  outputs = {self, nixpkgs, ... }:
    let
      supportedSystems =
        [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
      forEachSupportedSystem = f:
        nixpkgs.lib.genAttrs supportedSystems
        (system: f { pkgs = import nixpkgs { inherit system; }; });
    in {
      devShells = forEachSupportedSystem ({ pkgs }: {
        default = pkgs.mkShell {
          buildInputs = [
            (pkgs.aspellWithDicts (ps: with ps; [ de ]))
            pkgs.python3
            pkgs.python3Packages.pyenchant
          ];
          shellHook = ''
            export ASPELL_CONF="dict-dir ${pkgs.aspellDicts.de}/lib/aspell"
            python -c 'import enchant; print(enchant.list_languages())'
          '';
        };
      });
    };
}

nix develop . gives me only'[he]' as available language but not german.

Any idea?

I fixed it myself by using hunspell instead of aspell:

{
  inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11";
  outputs = {self, nixpkgs, ... }:
    let
      supportedSystems =
        [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
      forEachSupportedSystem = f:
        nixpkgs.lib.genAttrs supportedSystems
        (system: f { pkgs = import nixpkgs { inherit system; }; });
    in {
      devShells = forEachSupportedSystem ({ pkgs }: {
        default = pkgs.mkShell {
          buildInputs = [
            pkgs.hunspellDicts.de_DE
            pkgs.python3
            pkgs.python3Packages.pyenchant
          ];
          shellHook = ''
            export DICPATH="${pkgs.hunspellDicts.de_DE}/share/hunspell"
            python -c 'import enchant; print(enchant.list_languages())'
          '';
        };
      });
    };
}
2 Likes