# Pynvim Python Client and Plugin Host for Neovim

> 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.

**neovim/pynvim** — Python client and plugin host for Nvim

- Repository: https://github.com/neovim/pynvim
- Website: http://pynvim.readthedocs.io/en/latest/
- Stars: 1,707 · Forks: 132
- Language: Python
- License: Apache-2.0
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/neovim-pynvim

## 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.

```bash
uv tool install --upgrade pynvim
```

The 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.

```bash
pipx install pynvim
```

For a later Pynvim upgrade through `pipx`, run:

```bash
pipx upgrade pynvim
```

These 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.

```sh
$ nvim --listen /tmp/nvim.sock
```

In another terminal, use the Python REPL to import Pynvim and attach to that socket. Once `attach` returns, `nvim` represents the connected Neovim instance.

```python
>>> 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.

```python
>>> 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:

```python
vim.vars['my_dict']['field1'] = 'value'
```

The dictionary must instead be read, modified, and assigned as a whole.

```python
my_dict = vim.vars['my_dict']
my_dict['field1'] = 'value'
vim.vars['my_dict'] = my_dict
```

That 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.

## 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.

## FAQ

### 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.

## Sources

- [License: Apache-2.0](https://github.com/neovim/pynvim/blob/master/LICENSE)
- [neovim/pynvim on GitHub](https://github.com/neovim/pynvim)
- [Project website](http://pynvim.readthedocs.io/en/latest/)
- [README](https://github.com/neovim/pynvim/blob/master/README.md)
- [Releases](https://github.com/neovim/pynvim/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/neovim-pynvim
