Pynvim Python Client and Plugin Host for Neovim
Python client and plugin host for Nvim
At a glance
- What is it?
- Pynvim connects Python applications and plugins to Neovim through msgpack-rpc, Vim script interfaces, and Neovim's remote plugin system. It suits Python 3.10 or later users, with one important boundary around Vimscript dictionary references.
- Who is it for?
- Adopt Pynvim when Python code must control Neovim or implement a remote plugin, especially when asynchronous operation and object access to buffers, windows, API functions, and Vimscript functions matter. Do not choose it for Python 3.9 or earlier, for code that requires live Vimscript dictionary references, or for a plugin that must remain entirely inside Neovim without Python.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Pynvim's Python-to-Neovim bridge
Pynvim addresses two related problems. It provides the Python runtime needed by Neovim's `:python3` plugin interface, and it acts as a Python client library for connecting to or embedding Neovim processes. The intended users are Python plugin authors and developers building tools that automate a Neovim session.
The library exposes Neovim objects through a Python-facing API. Built-in and plugin Vimscript functions are available through `nvim.funcs`, while Neovim API functions are available through `vim.api` and through API-bearing objects such as `buffer.api`. Lua functions can be defined with `vim.exec_lua` and called through `vim.lua`. This makes Pynvim useful when a program needs to coordinate editor state with Python data and logic without reproducing Neovim's RPC protocol itself.
Pynvim is not intended to replace Neovim's Lua plugin ecosystem. It is specifically the Python connection layer for users whose implementation language is Python. Its package metadata names Python 3.10 through Python 3.14 as supported versions and requires Python 3.10 or later.
Remote plugins, Vim plugins, and RPC sessions
Pynvim supports both Python remote plugins through Neovim's language-agnostic rplugin interface and Vim plugins through `:python3`. The remote plugin route is the more expressive option described by the README. Such plugins can handle Vimscript function calls, define commands and autocommands, and operate asynchronously without blocking Neovim. Once Pynvim is installed, Neovim reports support for the `+python3` Vim feature.
For library use, Pynvim communicates with Neovim through its msgpack-rpc API. A client can attach to an existing Neovim process by its server address, or it can start a child process with `--embed`. An embedded headless process is useful when Neovim should run without waiting for a user interface. If the Python program is itself a UI, the process can use `--embed` without `--headless`, allowing startup to continue while waiting for `nvim_ui_attach`.
This architecture separates the Python application from the editor process while preserving access to editor state. It is also why Pynvim has responsibilities that a simple syntax parser does not: process attachment, remote calls, object proxies, async requests, and plugin registration.
Installing Pynvim with uv or pipx
The README requires Python 3.10 or later. It recommends `uv` for an isolated command installation. The `--upgrade` option asks the installer to update an existing tool installation.
uv tool install --upgrade pynvimThe README says to rerun this command whenever Neovim is upgraded, so Pynvim and Neovim are expected to move together.
If `pipx` is already available, its installation path is also documented.
pipx install pynvimFor a later Pynvim upgrade through `pipx`, run:
pipx upgrade pynvimThese commands install the package but do not replace the separate `nvim` executable. The REPL and embedded-process examples assume that `nvim` is available through `PATH`.
Connecting to a Neovim socket for a first edit
The simplest documented first use begins by starting Neovim with a known address. This creates the socket that the Python process will attach to.
$ nvim --listen /tmp/nvim.sockIn another terminal, use the Python REPL to import Pynvim and attach to that socket. Once `attach` returns, `nvim` represents the connected Neovim instance.
>>> import pynvim
>>> nvim = pynvim.attach('socket', path='/tmp/nvim.sock')
>>> buffer = nvim.current.buffer
>>> buffer[0] = 'replace first line'The README then demonstrates broader object access. Assigning `buffer[:]` replaces the buffer contents, `nvim.command('vsplit')` runs a Neovim command, and `nvim.windows[1].width = 10` changes a window property. Global variables can be assigned and evaluated through `nvim.vars` and `nvim.eval`.
Embedding Neovim inside a Python application
A Python application does not have to connect to a separately started editor. Pynvim can launch `nvim` as a child process in embedded mode. The README uses `--headless` for a server-style process that does not wait for a UI.
>>> import pynvim
>>> nvim = pynvim.attach('child', argv=["/usr/bin/env", "nvim", "--embed", "--headless"])This is the relevant path for automation, background integrations, and Python programs that own the Neovim lifecycle. The object model remains the same after attachment: the connected instance still exposes its current buffer, commands, windows, variables, and API functions.
The choice between socket attachment and child attachment is operational. Socket attachment suits a client that should connect to an existing editor address. Child attachment suits an application that needs to supply the process arguments and manage the embedded process. The README mentions multiple transports but uses these two approaches for its initial examples; it does not claim that socket and child attachment are the only transports.
The Vimscript dictionary copy limitation
The most concrete limitation is documented by Pynvim and Neovim: Neovim does not support `bindeval`, so a Vimscript dictionary read through `vim.vars`, `vim.vvars`, or `Buffer.vars` is returned as a copy rather than a live reference. Mutating a nested field therefore does not write the change back into Neovim.
This code does not perform the intended update:
vim.vars['my_dict']['field1'] = 'value'The dictionary must instead be read, modified, and assigned as a whole.
my_dict = vim.vars['my_dict']
my_dict['field1'] = 'value'
vim.vars['my_dict'] = my_dictThat requirement affects code architecture. A plugin cannot treat every retrieved Vimscript dictionary as a transparent proxy. Code that edits nested dictionaries must construct a new Python value or at least reassign the complete dictionary after modification. For data structures where Neovim-side mutation is frequent, this is a real reason to prefer Python-owned objects or explicit full assignments.
Pynvim against the Vim `:python3` path
The README directly identifies the Vim `:python3` interface as a comparison point. That path is appropriate when the code is already a Neovim Vim plugin, executes inside Neovim, and only needs Python available for that plugin. It does not need an application to create and manage a separate connection just to manipulate the current editor.
Pynvim adds a broader client role. It can attach to a running Neovim process through a socket, launch an embedded process, expose API objects to an external Python application, and provide async requests with thread-safety support. Its remote plugin interface also allows Python plugins to handle Vimscript function calls and define commands and autocommands without blocking Neovim.
The practical distinction is therefore ownership and execution location. Use the `:python3` model for an in-editor plugin whose Python code runs as part of Neovim's plugin loading model. Use Pynvim when a Python application needs an explicit connection, when the editor process must be embedded, or when the same client surface should reach multiple Neovim objects. Pynvim is the heavier choice for simple in-editor scripting because installation and RPC are part of its purpose.
Maintenance, releases, and Apache-2.0
The repository is not archived, but its last push was on 2026-04-10. The recorded releases include Pynvim 0.6.0 on 2025-09-07, 0.5.2 on 2024-11-13, and 0.5.1 on 2024-11-03. The package metadata currently declares `0.6.1.dev0` as a development version. This is evidence of a release history, not a claim about current support for every Neovim release.
The README explicitly couples upgrades: whenever Neovim is upgraded, it says to rerun the Pynvim installation or upgrade command. That creates a maintenance task for deployments that keep Pynvim installed as a tool. Package updates also need to be assessed against the Python version, because `requires-python` is `>=3.10`.
The package is licensed under Apache-2.0. That identifies the declared license, but it is not a substitute for checking how the license applies to a particular distribution, plugin bundle, or integration.
Development checks and repository layout
For local development, the README recommends a virtual environment. Changes must be reinstalled before they take effect, and the test suite should be invoked through `python -m pytest` so the current directory is first on `sys.path`. That detail matters because a globally discoverable `pytest` command could otherwise resolve a different installed version.
The repository also documents isolated checks through `uvx` for tests, linting, and type checking. Those commands are development checks rather than requirements for using Pynvim as a runtime library. Based on the repository layout, the implementation is split across `pynvim/`, `neovim/`, tests, documentation, packaging files, and release scripts. The packaging metadata declares a `pynvim-python` entry point and dependencies on `msgpack`, `greenlet` outside PyPy, and `typing-extensions` on older supported Python versions.
Editorial conclusion
Adopt Pynvim when Python code must control Neovim or implement a remote plugin, especially when asynchronous operation and object access to buffers, windows, API functions, and Vimscript functions matter. Do not choose it for Python 3.9 or earlier, for code that requires live Vimscript dictionary references, or for a plugin that must remain entirely inside Neovim without Python. Before adoption, verify compatibility with the deployed Neovim version, Python 3.10 or later, the chosen installation method, and whether your code relies on mutating a Vimscript dictionary in place. Once those constraints pass, Pynvim is a direct Python interface rather than a general-purpose editor framework.
Frequently asked questions
How do I install Pynvim for Python?
The README supports Python 3.10 or later and recommends installing it with `uv tool install --upgrade pynvim`. It also documents `pipx install pynvim` as an alternative.
How can I connect Pynvim to a running Neovim instance?
Start Neovim with a known address, such as `nvim --listen /tmp/nvim.sock`, then use `pynvim.attach('socket', path='/tmp/nvim.sock')` from Python. The README also documents attaching with the `v:servername` address of a running instance.
Can Pynvim start Neovim as an embedded child process?
Yes. The documented example uses `pynvim.attach('child', argv=["/usr/bin/env", "nvim", "--embed", "--headless"])`. Remove `--headless` when the client is a UI and should wait for `nvim_ui_attach`.
Why does changing a Vimscript dictionary field not update Neovim?
Neovim does not support `bindeval`, so dictionaries accessed through Pynvim are copies rather than live references. Read the dictionary, modify the Python value, and assign the complete dictionary back.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/neovim-pynvim)Was this useful?