Skip to content

Commit b1bceba

Browse files
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.
1 parent e2f7f90 commit b1bceba

File tree

6 files changed

+59
-13
lines changed

6 files changed

+59
-13
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,10 @@ local DEFAULT_SETTINGS = {
252252
-- Whether to upgrade pip to the latest version in the virtual environment before installing packages.
253253
upgrade_pip = false,
254254

255+
---@since 1.8.0
256+
-- Whether to use uv to install packages instead of pip
257+
use_uv = false,
258+
255259
---@since 1.0.0
256260
-- These args will be added to `pip install` calls. Note that setting extra args might impact intended behavior
257261
-- and is not recommended.

doc/mason.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,10 @@ Example:
314314
-- Whether to upgrade pip to the latest version in the virtual environment before installing packages.
315315
upgrade_pip = false,
316316

317+
---@since 1.8.0
318+
-- Whether to use uv to install packages instead of pip
319+
use_uv = false,
320+
317321
---@since 1.0.0
318322
-- These args will be added to `pip install` calls. Note that setting extra args might impact intended behavior
319323
-- and is not recommended.

lua/mason-core/installer/managers/pypi.lua

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,32 @@ local pep440 = require "mason-core.pep440"
99
local platform = require "mason-core.platform"
1010
local providers = require "mason-core.providers"
1111
local semver = require "mason-core.semver"
12+
local settings = require "mason.settings"
1213
local spawn = require "mason-core.spawn"
1314

1415
local M = {}
1516

17+
local use_uv = settings.current.pip.use_uv
1618
local VENV_DIR = "venv"
1719

1820
---@async
1921
---@param candidates string[]
2022
local function resolve_python3(candidates)
2123
local is_executable = _.compose(_.equals(1), vim.fn.executable)
2224
a.scheduler()
25+
if use_uv then
26+
candidates = { "uv" }
27+
end
2328
local available_candidates = _.filter(is_executable, candidates)
2429
for __, candidate in ipairs(available_candidates) do
2530
---@type string
2631
local version_output = spawn[candidate]({ "--version" }):map(_.prop "stdout"):get_or_else ""
27-
local ok, version = pcall(semver.new, version_output:match "Python (3%.%d+.%d+)")
32+
local ok, version
33+
if use_uv then
34+
ok, version = pcall(semver.new, version_output:match "uv (%d+.%d+.%d+)")
35+
else
36+
ok, version = pcall(semver.new, version_output:match "Python (3%.%d+.%d+)")
37+
end
2838
if ok then
2939
return { executable = candidate, version = version }
3040
end
@@ -127,7 +137,14 @@ local function create_venv(pkg)
127137

128138
log.fmt_debug("Found python3 installation version=%s, executable=%s", target.version, target.executable)
129139
ctx.stdio_sink.stdout "Creating virtual environment…\n"
130-
return ctx.spawn[target.executable] { "-m", "venv", "--system-site-packages", VENV_DIR }
140+
141+
if use_uv then
142+
log.fmt_debug("Found uv installation version=%s, executable=%s", target.version, target.executable)
143+
return ctx.spawn[target.executable] { "venv", VENV_DIR }
144+
else
145+
log.fmt_debug("Found python3 installation version=%s, executable=%s", target.version, target.executable)
146+
return ctx.spawn[target.executable] { "-m", "venv", "--system-site-packages", VENV_DIR }
147+
end
131148
end
132149

133150
---@param ctx InstallContext
@@ -162,16 +179,29 @@ end
162179
---@param pkgs string[]
163180
---@param extra_args? string[]
164181
local function pip_install(pkgs, extra_args)
165-
return venv_python {
166-
"-m",
167-
"pip",
168-
"--disable-pip-version-check",
169-
"install",
170-
"--ignore-installed",
171-
"-U",
172-
extra_args or vim.NIL,
173-
pkgs,
174-
}
182+
if use_uv then
183+
local ctx = installer.context()
184+
local task = ctx.spawn["uv"] {
185+
"pip",
186+
"install",
187+
"-U",
188+
extra_args or vim.NIL,
189+
pkgs,
190+
}
191+
-- vim.api.nvim_set_current_dir(curdir)
192+
return task
193+
else
194+
return venv_python {
195+
"-m",
196+
"pip",
197+
"--disable-pip-version-check",
198+
"install",
199+
"--ignore-installed",
200+
"-U",
201+
extra_args or vim.NIL,
202+
pkgs,
203+
}
204+
end
175205
end
176206

177207
---@async
@@ -185,7 +215,7 @@ function M.init(opts)
185215
ctx:promote_cwd()
186216
try(create_venv(opts.package))
187217

188-
if opts.upgrade_pip then
218+
if opts.upgrade_pip and not use_uv then
189219
ctx.stdio_sink.stdout "Upgrading pip inside the virtual environment…\n"
190220
try(pip_install({ "pip" }, opts.install_extra_args))
191221
end

lua/mason-core/installer/registry/providers/pypi.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ function M.parse(source, purl)
2727
pip = {
2828
upgrade = settings.current.pip.upgrade_pip,
2929
extra_args = settings.current.pip.install_args,
30+
use_uv = settings.current.pip.use_uv,
3031
},
3132
}
3233

@@ -48,11 +49,13 @@ function M.install(ctx, source)
4849
},
4950
upgrade_pip = source.pip.upgrade,
5051
install_extra_args = source.pip.extra_args,
52+
use_uv = source.pip.use_uv,
5153
})
5254
try(pypi.install(source.package, source.version, {
5355
extra = source.extra,
5456
extra_packages = source.extra_packages,
5557
install_extra_args = source.pip.extra_args,
58+
use_uv = source.pip.use_uv,
5659
}))
5760
end)
5861
end

lua/mason/settings.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ local DEFAULT_SETTINGS = {
6060
-- Whether to upgrade pip to the latest version in the virtual environment before installing packages.
6161
upgrade_pip = false,
6262

63+
---@since 1.8.0
64+
-- Whether to use uv to install packages instead of pip
65+
use_uv = false,
66+
6367
---@since 1.0.0
6468
-- These args will be added to `pip install` calls. Note that setting extra args might impact intended behavior
6569
-- and is not recommended.

tests/mason-core/installer/registry/providers/pypi_spec.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ describe("pypi provider :: parsing", function()
3131
pip = {
3232
upgrade = true,
3333
extra_args = { "--proxy", "http://localghost" },
34+
use_uv = false,
3435
},
3536
},
3637
pypi.parse({ extra_packages = { "extra" } }, purl())

0 commit comments

Comments
 (0)