NeoVim LSP and DAP for Python (and Django)

One of the most read article of my blog is my guide about how to configure LSP for PHP but this time is for Python!

In this guide we will see how to configure everything that works with Poetry (as we are using various wrappers, it will works also with Conda or Venv) and PyLSP, with DebugPy installed in the same virtual environment of the Django project.
This decision to have everything in the same Venv simplify a lot, avoid conflicts and allows a nicer integration with the various tools like Flake8 or MyPy.

Neovim plugins to install

For LSP you need:

For DAP you need:

LSP settings

The py_lsp.nvim plugin got 2 pull requests by me that improve the LSP server detection inside the Venv with more alerts and settings for pylsp plugins.

require'py_lsp'.setup{
    language_server = "pylsp",
    source_strategies = {"poetry", "default", "system"},
    capabilities = capabilities,
    on_attach = on_attach,
    pylsp_plugins = {
        autopep8 = {
            enabled = true
        },
        pyls_mypy = {
            enabled = true
        },
        pyls_isort = {
            enabled = true
        },
        flake8 = {
            enabled = true,
            executable = ".venv/bin/flake8",
        },
    },
}

With this piece of code as you can see we are defining the source strategies that the plugin will use to find Python and also the LSP, the language server and the plugins enabled (you can change those as you need) in pylsp.
The capabilities and on_attach properties are something that you can customize with plugins or custom code, so if you have no idea you can remove those 2 lines as it will use the defaults one.

Anyway with the latest plugin version, automatically, when you are in a Python project or in a file, it will load everything from the Venv (also lsp if is installed) otherwise will use the global one in your machine.
The purpose of this plugin is the automatic path detection for python (and the packages) otherwise you have to code it manually in your configuration.

To get the better from neovim and avoid problems is better to launch from the folder where is the project, otherwise you can try with the :PyLSPReloadVenv command that will recheck the path etc.

Also doesn’t forget to run :TSinstall python in this way your treesitter module will have the python support.

DAP settings

Taking experience from this ticket I was able to write the best settings that let’s to use NeoVim as IDE and run Django from the editor itself.

local pythonPath = function()
    local cwd = vim.loop.cwd()
    if vim.fn.executable(cwd .. '/.venv/bin/python') == 1 then
        return cwd .. '/.venv/bin/python'
    else
        return '/usr/bin/python'
    end
end

local set_python_dap = function()
    require('dap-python').setup() -- earlier so setup the various defaults ready to be replaced
    dap.configurations.python = {
        {
            type = 'python';
            request = 'launch';
            name = "Launch file";
            program = "${file}";
            pythonPath = pythonPath()
        },
        {
            type = 'python',
            request = 'launch',
            name = 'DAP Django',
            program = vim.loop.cwd() .. '/manage.py',
            args = {'runserver', '--noreload'},
            justMyCode = true,
            django = true,
            console = "integratedTerminal",
        },
        {
            type = 'python';
            request = 'attach';
            name = 'Attach remote';
            connect = function()
                return {
                    host = '127.0.0.1',
                    port = 5678
                }
            end;
        },
        {
            type = 'python';
            request = 'launch';
            name = 'Launch file with arguments';
            program = '${file}';
            args = function()
                local args_string = vim.fn.input('Arguments: ')
                return vim.split(args_string, " +")
            end;
            console = "integratedTerminal",
            pythonPath = pythonPath()
        }
    }

    dap.adapters.python = {
        type = 'executable',
        command = pythonPath(),
        args = {'-m', 'debugpy.adapter'}
    }
end

set_python_dap()
vim.api.nvim_create_autocmd({"DirChanged"}, {
    callback = function() set_python_dap() end,
})

In this code you can see a function to do the python path detection for Venv (something that the LSP plugin was automatically doing for you), next step is a custom function that will be executed on start and on directory changed. The function will overwrite the various run modes for the Python language with support for the pythonPath function we created above, also we add the DAP Django and the adapter command for DebugPy.

We specify also to add DAP support only in our projects and not in the dependencies and Django support (debugpy has a parameter for that but I wasn’t able to find what does).

So in your Neovim you just need to run DAP Django and you will have the server running and you can inspecting:

The issue right now is that nvim-dap, should support the subProcess feature that allows to enable the auto reload in Django (I am participating in the ticket). Infact right now you have to restart the DAP server like in this screen to see the updates at every change.

Conclusion

It was more simpler than I thought after reading various dotfiles, configuration preset and tutorial/documentation.
With this plugins and integration everything will be simplified but I didn’t mentioned the mappings for DAP as I invite you to read the readme and create your own mappings (mine are there).

Why PyLSP instead of Pyright? Well I was already using it with Spyder so in this way I have the same LSP everywhere but this solutions works also with the other LSP (everyone is different about plugins and tools supported).

If you are looking for instructions on how to get a single NeoVim instance just read here.

Liked it? Take a second to support Mte90 on Patreon!
Become a patron at Patreon!

Leave a Reply

Your email address will not be published. Required fields are marked *