Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ pyrepl.setup({
preferred_kernel = "python3",
-- automatically prompt to convert notebook files into python scripts
jupytext_hook = true,
-- auto-install missing runtime packages on `:PyreplOpen`
-- set to "pip" or "uv" to enable; false to disable
auto_install = false,
})

-- repl ui-related commands
Expand Down Expand Up @@ -86,6 +89,13 @@ Then install REPL runtime packages with `uv` or `pip` directly from Neovim:
:PyreplInstall uv
```

Alternatively, enable `auto_install` to have pyrepl silently install missing packages
on `:PyreplOpen`:

```lua
require("pyrepl").setup({ auto_install = "uv" }) -- or "pip"
```

To use jupytext integration, make sure jupytext is available in Neovim:

```bash
Expand Down
7 changes: 7 additions & 0 deletions doc/pyrepl.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ Minimal `vim.pack` setup with the default config and example keymaps:
preferred_kernel = "python3",
-- automatically prompt to convert notebook files into python scripts
jupytext_hook = true,
-- auto-install missing runtime packages on `:PyreplOpen`
-- set to "pip" or "uv" to enable; false to disable
auto_install = false,
})

-- repl ui-related commands
Expand Down Expand Up @@ -238,6 +241,7 @@ Default configuration:
python_path = "python",
preferred_kernel = "python3",
jupytext_hook = true,
auto_install = false,
})
<

Expand Down Expand Up @@ -293,6 +297,9 @@ Options:
Default: "python3".
jupytext_hook (boolean)
Enable jupytext hook to automatically convert notebook buffers to `py:percent`. Default: true.
auto_install (false | "pip" | "uv")
When |PyreplOpen| is invoked and required runtime packages are
missing, install them using the specified tool. Default: false.

==============================================================================
TIPS & TRICKS *pyrepl-tips*
Expand Down
1 change: 1 addition & 0 deletions lua/pyrepl/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ local defaults = {
python_path = "python",
preferred_kernel = "python3",
jupytext_hook = true,
auto_install = false,
}

local image_provider_cache
Expand Down
7 changes: 4 additions & 3 deletions lua/pyrepl/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ local send = require("pyrepl.send")

local group = vim.api.nvim_create_augroup("Pyrepl", { clear = true })

---@param args table|nil
function M.open_repl(args)
core.open_repl(args)
function M.open_repl()
python.ensure_dependencies(function()
core.open_repl()
end)
end

function M.hide_repl()
Expand Down
63 changes: 63 additions & 0 deletions lua/pyrepl/python.lua
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,67 @@ function M.get_tool_completions(arglead)
end, vim.tbl_keys(tools))
end

---Check if required Python packages are importable.
---@return boolean
function M.check_dependencies()
local ok, python_path = pcall(M.get_python_path)
if not ok then
return false
end

local obj = vim.system(
{ python_path, "-c", "import jupyter_console, pynvim" },
{ text = true }
):wait()

return obj.code == 0
end

---Check dependencies and, if `auto_install` is set, install them silently.
---Calls callback only after dependencies are confirmed present.
---@param callback fun()
function M.ensure_dependencies(callback)
if M.check_dependencies() then
callback()
return
end

local tool = config.get_state().auto_install
if not tool or not tools[tool] then
vim.notify(
config.get_message_prefix()
.. "dependencies missing, run `:PyreplInstall pip|uv` or set `auto_install`",
vim.log.levels.ERROR
)
return
end

local ok, python_path = pcall(M.get_python_path)
if not ok then
vim.notify(python_path, vim.log.levels.ERROR)
return
end

local packages_string = table.concat(packages, " ")
local cmd_str = tools[tool]:format(python_path) .. " " .. packages_string

vim.notify(config.get_message_prefix() .. "installing dependencies...")

vim.system(
{ "/bin/sh", "-c", cmd_str },
{ text = true },
vim.schedule_wrap(function(obj)
if obj.code == 0 then
vim.notify(config.get_message_prefix() .. "dependencies installed")
callback()
else
vim.notify(
config.get_message_prefix() .. "installation failed\n" .. (obj.stderr or ""),
vim.log.levels.ERROR
)
end
end)
)
end

return M
2 changes: 2 additions & 0 deletions lua/pyrepl/types.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
---@field python_path string|nil
---@field preferred_kernel string|nil
---@field jupytext_hook boolean
---@field auto_install false | "pip" | "uv"

---Plugin setup opts (all arguments are optional).
---@class pyrepl.ConfigOpts
Expand All @@ -29,6 +30,7 @@
---@field python_path? string
---@field preferred_kernel? string
---@field jupytext_hook? boolean
---@field auto_install? false | "pip" | "uv"

---Image provider interface.
---@class pyrepl.Image
Expand Down