From b1bceba6c7ffe12f73486c443ba0c3a5b732f23a Mon Sep 17 00:00:00 2001 From: Michael Park Date: Wed, 28 Feb 2024 20:18:02 +0000 Subject: [PATCH] feat: add uv as installer for python UV is a very fast installer for python packages that can be 10-100x faster to resolve packages. This adds an option for Mason to use it instead of pip to resolve python packages that are installed via Mason. More info about the replacement: https://github.com/astral-sh/uv I have no relationship with uv, it is just very fast and it would be nice to have updates for packages like sqlfluff take a lot less time than they currently do to resolve during updates. --- README.md | 4 ++ doc/mason.txt | 4 ++ lua/mason-core/installer/managers/pypi.lua | 56 ++++++++++++++----- .../installer/registry/providers/pypi.lua | 3 + lua/mason/settings.lua | 4 ++ .../registry/providers/pypi_spec.lua | 1 + 6 files changed, 59 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 9e33b653c..89343475e 100644 --- a/README.md +++ b/README.md @@ -252,6 +252,10 @@ local DEFAULT_SETTINGS = { -- Whether to upgrade pip to the latest version in the virtual environment before installing packages. upgrade_pip = false, + ---@since 1.8.0 + -- Whether to use uv to install packages instead of pip + use_uv = false, + ---@since 1.0.0 -- These args will be added to `pip install` calls. Note that setting extra args might impact intended behavior -- and is not recommended. diff --git a/doc/mason.txt b/doc/mason.txt index e7a2d3bf6..4fa4b1b57 100644 --- a/doc/mason.txt +++ b/doc/mason.txt @@ -314,6 +314,10 @@ Example: -- Whether to upgrade pip to the latest version in the virtual environment before installing packages. upgrade_pip = false, + ---@since 1.8.0 + -- Whether to use uv to install packages instead of pip + use_uv = false, + ---@since 1.0.0 -- These args will be added to `pip install` calls. Note that setting extra args might impact intended behavior -- and is not recommended. diff --git a/lua/mason-core/installer/managers/pypi.lua b/lua/mason-core/installer/managers/pypi.lua index f60a8edee..d85d46e42 100644 --- a/lua/mason-core/installer/managers/pypi.lua +++ b/lua/mason-core/installer/managers/pypi.lua @@ -9,10 +9,12 @@ local pep440 = require "mason-core.pep440" local platform = require "mason-core.platform" local providers = require "mason-core.providers" local semver = require "mason-core.semver" +local settings = require "mason.settings" local spawn = require "mason-core.spawn" local M = {} +local use_uv = settings.current.pip.use_uv local VENV_DIR = "venv" ---@async @@ -20,11 +22,19 @@ local VENV_DIR = "venv" local function resolve_python3(candidates) local is_executable = _.compose(_.equals(1), vim.fn.executable) a.scheduler() + if use_uv then + candidates = { "uv" } + end local available_candidates = _.filter(is_executable, candidates) for __, candidate in ipairs(available_candidates) do ---@type string local version_output = spawn[candidate]({ "--version" }):map(_.prop "stdout"):get_or_else "" - local ok, version = pcall(semver.new, version_output:match "Python (3%.%d+.%d+)") + local ok, version + if use_uv then + ok, version = pcall(semver.new, version_output:match "uv (%d+.%d+.%d+)") + else + ok, version = pcall(semver.new, version_output:match "Python (3%.%d+.%d+)") + end if ok then return { executable = candidate, version = version } end @@ -127,7 +137,14 @@ local function create_venv(pkg) log.fmt_debug("Found python3 installation version=%s, executable=%s", target.version, target.executable) ctx.stdio_sink.stdout "Creating virtual environment…\n" - return ctx.spawn[target.executable] { "-m", "venv", "--system-site-packages", VENV_DIR } + + if use_uv then + log.fmt_debug("Found uv installation version=%s, executable=%s", target.version, target.executable) + return ctx.spawn[target.executable] { "venv", VENV_DIR } + else + log.fmt_debug("Found python3 installation version=%s, executable=%s", target.version, target.executable) + return ctx.spawn[target.executable] { "-m", "venv", "--system-site-packages", VENV_DIR } + end end ---@param ctx InstallContext @@ -162,16 +179,29 @@ end ---@param pkgs string[] ---@param extra_args? string[] local function pip_install(pkgs, extra_args) - return venv_python { - "-m", - "pip", - "--disable-pip-version-check", - "install", - "--ignore-installed", - "-U", - extra_args or vim.NIL, - pkgs, - } + if use_uv then + local ctx = installer.context() + local task = ctx.spawn["uv"] { + "pip", + "install", + "-U", + extra_args or vim.NIL, + pkgs, + } + -- vim.api.nvim_set_current_dir(curdir) + return task + else + return venv_python { + "-m", + "pip", + "--disable-pip-version-check", + "install", + "--ignore-installed", + "-U", + extra_args or vim.NIL, + pkgs, + } + end end ---@async @@ -185,7 +215,7 @@ function M.init(opts) ctx:promote_cwd() try(create_venv(opts.package)) - if opts.upgrade_pip then + if opts.upgrade_pip and not use_uv then ctx.stdio_sink.stdout "Upgrading pip inside the virtual environment…\n" try(pip_install({ "pip" }, opts.install_extra_args)) end diff --git a/lua/mason-core/installer/registry/providers/pypi.lua b/lua/mason-core/installer/registry/providers/pypi.lua index 3fe6f89ed..dcf643704 100644 --- a/lua/mason-core/installer/registry/providers/pypi.lua +++ b/lua/mason-core/installer/registry/providers/pypi.lua @@ -27,6 +27,7 @@ function M.parse(source, purl) pip = { upgrade = settings.current.pip.upgrade_pip, extra_args = settings.current.pip.install_args, + use_uv = settings.current.pip.use_uv, }, } @@ -48,11 +49,13 @@ function M.install(ctx, source) }, upgrade_pip = source.pip.upgrade, install_extra_args = source.pip.extra_args, + use_uv = source.pip.use_uv, }) try(pypi.install(source.package, source.version, { extra = source.extra, extra_packages = source.extra_packages, install_extra_args = source.pip.extra_args, + use_uv = source.pip.use_uv, })) end) end diff --git a/lua/mason/settings.lua b/lua/mason/settings.lua index 56fbcfb9f..8eb680378 100644 --- a/lua/mason/settings.lua +++ b/lua/mason/settings.lua @@ -60,6 +60,10 @@ local DEFAULT_SETTINGS = { -- Whether to upgrade pip to the latest version in the virtual environment before installing packages. upgrade_pip = false, + ---@since 1.8.0 + -- Whether to use uv to install packages instead of pip + use_uv = false, + ---@since 1.0.0 -- These args will be added to `pip install` calls. Note that setting extra args might impact intended behavior -- and is not recommended. diff --git a/tests/mason-core/installer/registry/providers/pypi_spec.lua b/tests/mason-core/installer/registry/providers/pypi_spec.lua index 539ba53b9..9cd2418d8 100644 --- a/tests/mason-core/installer/registry/providers/pypi_spec.lua +++ b/tests/mason-core/installer/registry/providers/pypi_spec.lua @@ -31,6 +31,7 @@ describe("pypi provider :: parsing", function() pip = { upgrade = true, extra_args = { "--proxy", "http://localghost" }, + use_uv = false, }, }, pypi.parse({ extra_packages = { "extra" } }, purl())