Why
Mutating the table returned by require('lint.linters.*') modifies a module-level singleton; any subsequent call that reads the same linter table — including other buffers or a re-lint — will see the mutated value, which can cause subtle, hard-to-reproduce bugs when switching between virtualenvs or project roots.
Current state
Lines 85–89 of python.lua implement get_linter_options by directly assigning into the table returned by require('lint.linters.' .. linter): linter_options.cmd = prefer_venv_executable(linter). Because require caches its return value, this mutates the shared singleton for the lifetime of the Neovim session.
Ideal state
get_linter_options builds and returns a new table containing only the fields it needs to override (e.g. using vim.tbl_extend('force', original, { cmd = prefer_venv_executable(linter) }))
- The table returned by
require('lint.linters.' .. linter) is never modified
- Re-linting in a different virtualenv picks up the correct executable each time
Starting points
lua/config/plugins/specs/python.lua (lines 85–89)
lua/config/util.lua (definition of prefer_venv_executable, for context)
QA plan
- Open two Python buffers from projects with different virtualenvs — Expect each buffer uses the correct linter executable for its project.
- After linting buffer A, require the linter module in the Neovim REPL and inspect its
cmd field — Expect it matches the plugin's original default, not the project-specific path.
Done when
get_linter_options returns a new table and the original lint.linters.* table is unmodified after linting runs.
Why
Mutating the table returned by
require('lint.linters.*')modifies a module-level singleton; any subsequent call that reads the same linter table — including other buffers or a re-lint — will see the mutated value, which can cause subtle, hard-to-reproduce bugs when switching between virtualenvs or project roots.Current state
Lines 85–89 of
python.luaimplementget_linter_optionsby directly assigning into the table returned byrequire('lint.linters.' .. linter):linter_options.cmd = prefer_venv_executable(linter). Becauserequirecaches its return value, this mutates the shared singleton for the lifetime of the Neovim session.Ideal state
get_linter_optionsbuilds and returns a new table containing only the fields it needs to override (e.g. usingvim.tbl_extend('force', original, { cmd = prefer_venv_executable(linter) }))require('lint.linters.' .. linter)is never modifiedStarting points
lua/config/plugins/specs/python.lua(lines 85–89)lua/config/util.lua(definition ofprefer_venv_executable, for context)QA plan
cmdfield — Expect it matches the plugin's original default, not the project-specific path.Done when
get_linter_optionsreturns a new table and the originallint.linters.*table is unmodified after linting runs.