diff options
Diffstat (limited to 'lua/mason-core/installer/managers/pypi.lua')
| -rw-r--r-- | lua/mason-core/installer/managers/pypi.lua | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/lua/mason-core/installer/managers/pypi.lua b/lua/mason-core/installer/managers/pypi.lua new file mode 100644 index 00000000..0800b155 --- /dev/null +++ b/lua/mason-core/installer/managers/pypi.lua @@ -0,0 +1,106 @@ +local Optional = require "mason-core.optional" +local _ = require "mason-core.functional" +local a = require "mason-core.async" +local installer = require "mason-core.installer" +local log = require "mason-core.log" +local path = require "mason-core.path" +local platform = require "mason-core.platform" +local Result = require "mason-core.result" + +local M = {} + +local VENV_DIR = "venv" + +---@async +---@param py_executables string[] +local function create_venv(py_executables) + local ctx = installer.context() + return Optional.of_nilable(_.find_first(function(executable) + return ctx.spawn[executable]({ "-m", "venv", VENV_DIR }):is_success() + end, py_executables)):ok_or "Failed to create python3 virtual environment." +end + +---@async +---@param args SpawnArgs +local function venv_python(args) + local ctx = installer.context() + local python_path = path.concat { + ctx.cwd:get(), + VENV_DIR, + platform.is.win and "Scripts" or "bin", + platform.is.win and "python.exe" or "python", + } + return ctx.spawn[python_path](args) +end + +---@async +---@param pkgs string[] +---@param extra_args? string[] +local function pip_install(pkgs, extra_args) + return venv_python { + "-m", + "pip", + "--disable-pip-version-check", + "install", + "-U", + extra_args or vim.NIL, + pkgs, + } +end + +---@async +---@param opts { upgrade_pip: boolean, install_extra_args?: string[] } +function M.init(opts) + return Result.try(function(try) + log.fmt_debug("pypi: init", opts) + local ctx = installer.context() + + if vim.in_fast_event() then + a.scheduler() + end + + local executables = platform.is.win + and _.list_not_nil( + vim.g.python3_host_prog and vim.fn.expand(vim.g.python3_host_prog), + "python", + "python3" + ) + or _.list_not_nil(vim.g.python3_host_prog and vim.fn.expand(vim.g.python3_host_prog), "python3", "python") + + -- pip3 will hardcode the full path to venv executables, so we need to promote cwd to make sure pip uses the final destination path. + ctx:promote_cwd() + + try(create_venv(executables)) + + if opts.upgrade_pip then + try(pip_install({ "pip" }, opts.install_extra_args)) + end + end) +end + +---@async +---@param pkg string +---@param version string +---@param opts? { extra?: string, extra_packages?: string[], install_extra_args?: string[] } +function M.install(pkg, version, opts) + opts = opts or {} + log.fmt_debug("pypi: install %s %s", pkg, version, opts) + return pip_install({ + opts.extra and ("%s[%s]==%s"):format(pkg, opts.extra, version) or ("%s==%s"):format(pkg, version), + opts.extra_packages or vim.NIL, + }, opts.install_extra_args) +end + +---@param exec string +function M.bin_path(exec) + return Result.pcall(platform.when, { + unix = function() + return path.concat { "venv", "bin", exec } + end, + win = function() + return path.concat { "venv", "Scripts", ("%s.exe"):format(exec) } + end, + }) +end + +return M |
