Skip to content

Commit 32c755a

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. fix: ensure the virtual environment is .venv for uv fix: venv dir can stay venv feat: update stdio ctx reference 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. fix: ensure the virtual environment is .venv for uv fix: venv dir can stay venv feat: update stdio ctx reference feat: add checkhealth entry and fix formatting feat: fix formatting fix: cbfmt 404
1 parent 7dc4fac commit 32c755a

File tree

8 files changed

+87
-20
lines changed

8 files changed

+87
-20
lines changed

.github/workflows/cbfmt.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
- name: Download Shellharden
2626
run: |
2727
mkdir /tmp/shellharden && cd $_
28-
curl -fsSL -o shellharden.tar.gz https://github.com/alsuren/cargo-quickinstall/releases/download/shellharden-4.2.0-x86_64-unknown-linux-gnu/shellharden-4.2.0-x86_64-unknown-linux-gnu.tar.gz
28+
curl -fsSL -o shellharden.tar.gz https://github.com/anordal/shellharden/releases/download/v4.3.1/shellharden-x86_64-unknown-linux-gnu.tar.gz
2929
tar -xvf shellharden.tar.gz
3030
mv shellharden /usr/local/bin/
3131
- name: Run cbfmt check

README.md

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

236+
---@since 1.8.0
237+
-- Whether to use uv to install packages instead of pip
238+
use_uv = false,
239+
236240
---@since 1.0.0
237241
-- These args will be added to `pip install` calls. Note that setting extra args might impact intended behavior
238242
-- and is not recommended.

doc/mason.txt

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

279+
---@since 1.8.0
280+
-- Whether to use uv to install packages instead of pip
281+
use_uv = false,
282+
279283
---@since 1.0.0
280284
-- These args will be added to `pip install` calls. Note that setting extra args might impact intended behavior
281285
-- and is not recommended.

lua/mason-core/installer/compiler/compilers/pypi.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ function M.parse(source, purl)
2020
pip = {
2121
upgrade = settings.current.pip.upgrade_pip,
2222
extra_args = settings.current.pip.install_args,
23+
use_uv = settings.current.pip.use_uv,
2324
},
2425
}
2526

@@ -40,11 +41,13 @@ function M.install(ctx, source)
4041
},
4142
upgrade_pip = source.pip.upgrade,
4243
install_extra_args = source.pip.extra_args,
44+
use_uv = source.pip.use_uv,
4345
})
4446
try(pypi.install(source.package, source.version, {
4547
extra = source.extra,
4648
extra_packages = source.extra_packages,
4749
install_extra_args = source.pip.extra_args,
50+
use_uv = source.pip.use_uv,
4851
}))
4952
end)
5053
end

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

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ 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
function M.venv_path(dir)
@@ -30,11 +32,20 @@ local function resolve_python3(candidates)
3032
a.scheduler()
3133
local available_candidates = _.filter(is_executable, candidates)
3234
for __, candidate in ipairs(available_candidates) do
33-
---@type string
34-
local version_output = spawn[candidate]({ "--version" }):map(_.prop "stdout"):get_or_else ""
35-
local ok, version = pcall(semver.new, version_output:match "Python (3%.%d+.%d+)")
36-
if ok then
37-
return { executable = candidate, version = version }
35+
if use_uv and candidate == "uv" then
36+
---@type string
37+
local version_output = spawn[candidate]({ "--version" }):map(_.prop "stdout"):get_or_else ""
38+
local ok, version = pcall(semver.new, version_output:match "uv (%d+.%d+.%d+).*")
39+
if ok then
40+
return { executable = candidate, version = version }
41+
end
42+
elseif not use_uv then
43+
---@type string
44+
local version_output = spawn[candidate]({ "--version" }):map(_.prop "stdout"):get_or_else ""
45+
local ok, version = pcall(semver.new, version_output:match "Python (3%.%d+.%d+)")
46+
if ok then
47+
return { executable = candidate, version = version }
48+
end
3849
end
3950
end
4051
return nil
@@ -85,13 +96,19 @@ local function create_venv(pkg)
8596

8697
-- 1. Resolve stock python3 installation.
8798
local stock_candidates = platform.is.win and { "python", "python3" } or { "python3", "python" }
99+
if use_uv then
100+
table.insert(stock_candidates, 1, "uv")
101+
end
88102
local stock_target = resolve_python3(stock_candidates)
89103
if stock_target then
90104
log.fmt_debug("Resolved stock python3 installation version %s", stock_target.version)
91105
end
92106

93107
-- 2. Resolve suitable versioned python3 installation (python3.12, python3.11, etc.).
94108
local versioned_candidates = {}
109+
if use_uv then
110+
table.insert(versioned_candidates, "uv")
111+
end
95112
if supported_python_versions ~= nil then
96113
if stock_target and not pep440_check_version(tostring(stock_target.version), supported_python_versions) then
97114
log.fmt_debug("Finding versioned candidates for %s", supported_python_versions)
@@ -111,7 +128,8 @@ local function create_venv(pkg)
111128
-- 3. If a versioned python3 installation was not found, warn the user if the stock python3 installation is outside
112129
-- the supported version range.
113130
if
114-
target == stock_target
131+
use_uv == false
132+
and target == stock_target
115133
and supported_python_versions ~= nil
116134
and not pep440_check_version(tostring(target.version), supported_python_versions)
117135
then
@@ -133,9 +151,14 @@ local function create_venv(pkg)
133151
end
134152
end
135153

136-
log.fmt_debug("Found python3 installation version=%s, executable=%s", target.version, target.executable)
137154
ctx.stdio_sink:stdout "Creating virtual environment…\n"
138-
return ctx.spawn[target.executable] { "-m", "venv", "--system-site-packages", VENV_DIR }
155+
if use_uv then
156+
log.fmt_debug("Found uv installation version=%s, executable=%s", target.version, target.executable)
157+
return ctx.spawn[target.executable] { "venv", VENV_DIR }
158+
else
159+
log.fmt_debug("Found python3 installation version=%s, executable=%s", target.version, target.executable)
160+
return ctx.spawn[target.executable] { "-m", "venv", "--system-site-packages", VENV_DIR }
161+
end
139162
end
140163

141164
---@param ctx InstallContext
@@ -161,6 +184,9 @@ end
161184
---@param args SpawnArgs
162185
local function venv_python(args)
163186
local ctx = installer.context()
187+
if use_uv then
188+
return ctx.spawn["uv"](args)
189+
end
164190
return find_venv_executable(ctx, "python"):and_then(function(python_path)
165191
return ctx.spawn[path.concat { ctx.cwd:get(), python_path }](args)
166192
end)
@@ -170,16 +196,28 @@ end
170196
---@param pkgs string[]
171197
---@param extra_args? string[]
172198
local function pip_install(pkgs, extra_args)
173-
return venv_python {
174-
"-m",
175-
"pip",
176-
"--disable-pip-version-check",
177-
"install",
178-
"--no-user",
179-
"--ignore-installed",
180-
extra_args or vim.NIL,
181-
pkgs,
182-
}
199+
if use_uv then
200+
return venv_python {
201+
"pip",
202+
"install",
203+
"--directory",
204+
"venv",
205+
"-U",
206+
extra_args or vim.NIL,
207+
pkgs,
208+
}
209+
else
210+
return venv_python {
211+
"-m",
212+
"pip",
213+
"--disable-pip-version-check",
214+
"install",
215+
"--no-user",
216+
"--ignore-installed",
217+
extra_args or vim.NIL,
218+
pkgs,
219+
}
220+
end
183221
end
184222

185223
---@async
@@ -193,7 +231,7 @@ function M.init(opts)
193231
ctx:promote_cwd()
194232
try(create_venv(opts.package))
195233

196-
if opts.upgrade_pip then
234+
if opts.upgrade_pip and not use_uv then
197235
ctx.stdio_sink:stdout "Upgrading pip inside the virtual environment…\n"
198236
try(pip_install({ "pip" }, opts.install_extra_args))
199237
end

lua/mason/health.lua

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,19 @@ local function check_languages()
227227
apt-get install python3-venv]],
228228
},
229229
}
230+
check {
231+
cmd = "uv",
232+
args = { "--version" },
233+
name = "uv",
234+
relaxed = true,
235+
advice = {
236+
[[`uv` not installed, if you want to use the `use_uv` argument
237+
in the pip section of the configuration, you must install it.
238+
239+
https://docs.astral.sh/uv/getting-started/installation/
240+
]],
241+
},
242+
}
230243
end,
231244
function()
232245
a.scheduler()

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/compiler/compilers/pypi_spec.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ describe("pypi compiler :: 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)