🐍 Enhanced Neovim Python Virtual Environment Manager
A full-featured Neovim Python virtual environment management plugin with support for multiple virtual environment managers and deep LSP integration.
- Zero Configuration: Works out of the box with automatic environment detection and activation
- Multi-Manager Support: Supports UV, Poetry, Pipenv, Conda, Pyenv, local venv, virtualenvwrapper
- Smart Detection: Automatically detects project's virtual environment manager by priority
- Deep LSP Integration: Seamless integration with Pyright, Basedpyright, Pylsp, Jedi Language Server
- Per-Buffer Isolation: Different buffers can use different virtual environments, perfect for monorepos
- Persistent Cache: Maintains virtual environment configuration across sessions for fast startup
- Rich Command System: Complete user commands for managing virtual environments
- Statusline Integration: Provides API for statusline plugins like lualine to display current virtual environment
- Zero Configuration First: Fully automatic by default, no configuration required
- Progressive Enhancement: Supports manual management and advanced configuration for complex needs
- Performance First: Asynchronous detection, multi-layer caching, minimal startup impact
- Extensibility: Plugin architecture makes it easy to add new managers and LSP servers
{
'nvim-python-venv',
ft = 'python',
config = function()
require('nvim-python-venv').setup()
end,
}use { 'nvim-python-venv', ft = 'python', config = function() require('nvim-python-venv').setup() end, }
The plugin provides complete default configuration and can be used without any configuration:
require('nvim-python-venv').setup()
require('nvim-python-venv').setup({ -- Auto detect virtual environment auto_detect = true, -- Auto activate virtual environment auto_activate = true, -- Auto restart LSP when virtual environment changes auto_restart_lsp = true, -- Cache configuration cache = { enabled = true, file_path = vim.fn.stdpath('cache') .. '/nvim-python-venv/cache.json', expire_time = 0, -- 0 = never expire auto_clean = true, -- Auto clean invalid cache }, -- Virtual environment manager configuration managers = { -- Priority (lower number = higher priority) priority = { 'uv', 'poetry', 'pipenv', 'conda', 'pyenv', 'local_venv', 'virtualenvwrapper', }, -- Enabled managers enabled = { uv = true, poetry = true, pipenv = true, conda = true, pyenv = true, local_venv = true, virtualenvwrapper = true, }, }, -- LSP configuration lsp = { servers = { 'pyright', 'basedpyright', 'pylsp', 'jedi_language_server' }, restart_on_venv_change = true, timeout = 5000, }, -- UI configuration ui = { selector = 'auto', -- 'auto' | 'telescope' | 'fzf-lua' | 'fzf-vim' | 'nui' | 'vim-ui' notify = true, notify_level = 'info', -- 'info' | 'warn' | 'error' statusline = true, }, -- Hook functions hooks = { on_venv_activate = function(venv_path) -- Execute when virtual environment is activated end, on_venv_deactivate = function() -- Execute when virtual environment is deactivated end, on_lsp_attach = function(client, bufnr) -- Execute when LSP is attached end, }, })
| Command | Function |
|---|---|
:VenvSelect |
Select and activate virtual environment |
:VenvActivate <path> |
Activate virtual environment at specified path |
:VenvDeactivate |
Deactivate current virtual environment |
:VenvAdd |
Manually add virtual environment mapping |
:VenvRemove |
Remove virtual environment mapping |
:VenvRefresh |
Refresh virtual environment list |
:VenvInfo |
Show current virtual environment information |
:VenvCacheOpen |
Open cache file |
:VenvCacheClear |
Clear cache |
:VenvLspRestart |
Restart Python LSP |
local venv = require('nvim-python-venv') -- Get current active virtual environment name (for statusline) local name = venv.get_active_venv() -- Get virtual environment status (with complete information) local status = venv.get_venv_status() -- { -- name = "my-project", -- path = "/path/to/.venv", -- python_version = "3.11.5", -- manager = "poetry" -- } -- Get virtual environment icon local icon = venv.get_venv_icon() -- 🐍 📜 🅒 etc.
require('lualine').setup({ sections = { lualine_x = { { function() local venv = require('nvim-python-venv') local status = venv.get_venv_status() if status then return venv.get_venv_icon() .. ' ' .. status.name end return '' end, color = { fg = '#98c379' }, }, }, }, })
The plugin automatically detects virtual environments according to the following priority:
graph TD
Start[Start Detection] --> Cache{Check Cache}
Cache -->|Hit| Return1[Return Cached Env]
Cache -->|Miss| UV{UV Project?}
UV -->|Yes| Return2[Return UV Env]
UV -->|No| Poetry{Poetry Project?}
Poetry -->|Yes| Return3[Return Poetry Env]
Poetry -->|No| Pipenv{Pipenv Project?}
Pipenv -->|Yes| Return4[Return Pipenv Env]
Pipenv -->|No| Conda{Conda Project?}
Conda -->|Yes| Return5[Return Conda Env]
Conda -->|No| Pyenv{Pyenv Project?}
Pyenv -->|Yes| Return6[Return Pyenv Env]
Pyenv -->|No| Local{Local .venv/venv?}
Local -->|Yes| Return7[Return Local Env]
Local -->|No| Wrapper{Virtualenvwrapper?}
Wrapper -->|Yes| Return8[Return Wrapper Env]
Wrapper -->|No| Return9[Not Found]
| Manager | Detection Markers |
|---|---|
| UV | uv.lock file + .venv directory |
| Poetry | poetry.lock or [tool.poetry] in pyproject.toml |
| Pipenv | Pipfile or Pipfile.lock |
| Conda | environment.yml or environment.yaml |
| Pyenv | .python-version file |
| Local venv | .venv, venv, .env, env directories |
| Virtualenvwrapper | $WORKON_HOME/<project-name> directory |
┌─────────────────────────────────────────────────────────┐
│ User Layer │
│ Commands / API / Autocommands / Keybindings │
└─────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────┐
│ Core Layer │
│ Setup Manager / Config / Venv Core / LSP Core │
└─────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────┐
│ Virtual Environment Manager Layer │
│ UV / Poetry / Pipenv / Conda / Pyenv / Local / Wrapper │
└─────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────┐
│ Storage Layer │
│ Cache System (Memory + File) / State Management │
└─────────────────────────────────────────────────────────┘
The plugin deeply integrates with LSP through Hook injection:
- root_dir Hook: Prioritizes known project root directories from cache
- on_new_config Hook: Detects and activates virtual environment before LSP initialization
- on_attach Hook: Saves virtual environment path to buffer variable
Three-layer cache architecture ensures performance:
- L1 Memory Cache: Current session's root_dir -> venv mapping
- L2 File Cache: Persistent JSON cache across sessions
- L3 Global Environment Cache: Global virtual environment list for each manager
- File watch system (auto refresh virtual environments)
- Telescope/fzf selector integration
- Unit tests and integration tests
- Performance optimization and benchmarks
- Complete Vim documentation
- Windows support not fully tested
- Conda environment parsing may not be robust enough
Issues and Pull Requests are welcome!
MIT License