NixOS / nixpkgs

Nix Packages collection & NixOS
MIT License
17.42k stars 13.63k forks source link

python-language-server packaging issues #229337

Open mweinelt opened 1 year ago

mweinelt commented 1 year ago

I'm surprised we have multiple pylsp plugins packaged up, but I don't see a clear way to actually use them in a clean way.

They all depend on python-lsp-server, which makes propagating them into a python-lsp-server environment an infinite recursion.

Paired with the necessary overrides to make use of optional dependencies, this makes for a horrible usability, that warrants some explanation and documentation - ideally by its maintainers.

How are all of you using and especially combining those packages?

Crimes were committed while trying to come up with a way to use these together. What follows isn't a reasonable solution an enduser can come up with.


let
  python = pkgs.python3.override {
    packageOverrides = self: super: {
      python-lsp-server = super.python-lsp-server.overridePythonAttrs (oldAttrs: {
        propagatedBuildInputs = oldAttrs.propagatedBuildInputs or [] ++ [
          self.python-lsp-black
          self.python-lsp-ruff
        ]
        # completions & renaming
        ++ oldAttrs.passthru.optional-dependencies.rope
        # complexity
        ++ oldAttrs.passthru.optional-dependencies.mccabe;
      });

      python-lsp-black = super.python-lsp-black.overridePythonAttrs (oldAttrs: {
        postPatch = oldAttrs.postPatch or '''' + ''
          substituteInPlace setup.cfg \
            --replace "python-lsp-server>=1.4.0;" ""
        '';

        propagatedBuildInputs = with super; [
          black
          toml        
        ];

        doCheck = false;
      });

      python-lsp-ruff = super.python-lsp-ruff.overridePythonAttrs (oldAttrs: {
        postPatch = oldAttrs.postPatch or '''' + ''
          sed -i '/python-lsp-server/d' pyproject.toml
        '';

        nativeBuildInputs = with super; [
          setuptools          
        ];

        propagatedBuildInputs = with super; [
          lsprotocol
          tomli
        ];

        doCheck = false;
      });
    };
  };
in
  environment.systemPackages = [ python.pkgs.python-lsp-server ];
linsui commented 1 year ago

I just use

    (python3.withPackages (p: (with p; [
      python-lsp-server
      python-lsp-ruff
    ])))

and it just works so I'm not aware the problem. Is that a problem when multiple plugins are used?

mweinelt commented 1 year ago

That gets me a python interpreter with these two libraries in the PYTHONPATH and coincidentally pylsp in the PATH.

But it also installs python into my environment and pollutes the python instance with those libraries.

I kinda want our pylsp package to be an extensible, but otherwise self-contained application.

fabaff commented 1 year ago

python-lsp-jsonrpc is not a plugin/extension/addon per se but I guess that it would be treated like one. Not sure, if pylsb would work without it, though.

For a nice user experience it would preferably to follow the way we do it with pytest or flake8 if that make sense.

SomeoneSerge commented 10 months ago

Tangential question: has anyone managed to get nvim-lspconfig + python3Packages.ruff-lsp to work? I'm trying signatureHelp and it keeps saying it's not supported

GaetanLepage commented 10 months ago

Tangential question: has anyone managed to get nvim-lspconfig + python3Packages.ruff-lsp to work? I'm trying signatureHelp and it keeps saying it's not supported

I remember making it work yes. However, I know use the ruff plugin of python-lsp-server.

More generally, here is what we have implemented in nixvim to get around this plugin problem.

13k commented 8 months ago

Just mentioning this if anyone is interested in a workaround for a standalone package.

Following nixvim's approach, I also got it working with home-manager (flake setup) and a custom package:

# ./packages/python-lsp-server/default.nix
{
  pylsp-mypy,
  pylsp-rope,
  python-lsp-server,
}:
with builtins; let
  removeInput = inputs: filter (pkg: pkg.pname != "python-lsp-server") (inputs or []);

  patchSetupCfg = postPatch:
    (postPatch or "")
    + ''
      sed -i '/python-lsp-server/d' setup.cfg
    '';

  pylsp-mypy' = pylsp-mypy.overridePythonAttrs (oldAttrs: {
    doCheck = false;
    propagatedBuildInputs = removeInput oldAttrs.propagatedBuildInputs;
    postPatch = patchSetupCfg oldAttrs.postPatch;
  });

  pylsp-rope' = pylsp-rope.overridePythonAttrs (oldAttrs: {
    doCheck = false;
    propagatedBuildInputs = removeInput oldAttrs.propagatedBuildInputs;
    postPatch = patchSetupCfg oldAttrs.postPatch;
  });

  thirdPartyPlugins = [pylsp-mypy' pylsp-rope'];
in
  python-lsp-server.overridePythonAttrs (oldAttrs: {
    propagatedBuildInputs =
      (oldAttrs.propagatedBuildInputs or [])
      ++ python-lsp-server.optional-dependencies.rope
      ++ thirdPartyPlugins;

    disabledTests =
      (oldAttrs.disabledTests or [])
      ++ [
        "test_notebook_document__did_open"
        "test_notebook_document__did_change"
      ];
  })
# flake.nix
{
  # ...
  modules = [./home.nix];
  # ...
}

# home.nix
{
  # ...
  home = {
    # ...
    packages = [
      (pkgs.python3Packages.callPackage ./packages/python-lsp-server {})
      # ...
    ];
    # ...
  },
  # ...
}
Pleune commented 4 months ago

Is seems like use cases where additional packages add functionality to python-lsp-server, such as ruff / black / etc, should be handled in its packing code as optional flags. Then users could easily .override as needed.