Pyright Config File Precedence Over LSP Settings¶
Problem¶
A reportUnannotatedClassAttribute warning kept appearing in Neovim on a Python project even
after reportUnannotatedClassAttribute = 'none' was added to the basedpyright LSP settings in
configs/common/.config/nvim/lsp/basedpyright.lua. The neighboring ignore = { '*' } — which
should have suppressed every basedpyright diagnostic outright, since Ruff does the linting — was
plainly not taking effect either.
Restarting the server and confirming the settings were in flight (:checkhealth vim.lsp shows
each client's resolved settings table) changed nothing.
Solution¶
Put the rule in the project's pyproject.toml:
Diagnostic rules are top-level keys in the config file, not nested under
diagnosticSeverityOverrides the way they are in LSP settings. Accepted values are false/true
or "none"/"information"/"warning"/"error".
Key Learnings¶
- A project config file discards client settings wholesale. In
pyright-langserver.js, once a config file is loaded the client's settings are applied onlyif (!commandLineOptions.fromLanguageServer)— which is never true for a language server.ignore,exclude, anddiagnosticSeverityOverridesall ride in that discarded table. This is by design, not a merge conflict: the config file is authoritative so the editor and CI agree. - "Config file" means the
[tool.pyright]/[tool.basedpyright]section, not the file. A barepyproject.tomlwith no such section is not a config file, which is why the same LSP settings work fine in some repos and are inert in others — the symptom looks machine-specific but is project-specific. - basedpyright defaults to
recommended, notstandard. The server callsinitializeTypeCheckingMode('recommended')before reading any config, which turns on basedpyright-only rules likereportUnannotatedClassAttributethat plain pyright never emits. - Client settings land under
basedpyright.analysis, withpython.analysisas an undocumented fallback. The fallback only fires whenbasedpyright.analysisresolves to nothing, so a partially-populatedbasedpyrighttable can silently shadow thepythonone. - Read the
sourceandcodeoff the diagnostic before chasing config. With the cursor on the line,:lua =vim.diagnostic.get(0, { lnum = vim.fn.line('.') - 1 })returns the owning server and rule name — Ruff'sANN*rules produce near-identical annotation complaints.