Skip to content

Commit f724f1a

Browse files
Merge pull request #1 from KingMichaelPark/feat/add-uv-as-pypi-source
Feat/add uv as pypi source fix: venv dir can stay venv
2 parents e2f7f90 + db211a4 commit f724f1a

File tree

6 files changed

+69
-21
lines changed

6 files changed

+69
-21
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: 53 additions & 21 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
---@async
@@ -22,11 +24,20 @@ local function resolve_python3(candidates)
2224
a.scheduler()
2325
local available_candidates = _.filter(is_executable, candidates)
2426
for __, candidate in ipairs(available_candidates) do
25-
---@type string
26-
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+)")
28-
if ok then
29-
return { executable = candidate, version = version }
27+
if use_uv and candidate == "uv" then
28+
---@type string
29+
local version_output = spawn[candidate]({ "--version" }):map(_.prop "stdout"):get_or_else ""
30+
local ok, version = pcall(semver.new, version_output:match "uv (%d+.%d+.%d+).*")
31+
if ok then
32+
return { executable = candidate, version = version }
33+
end
34+
elseif not use_uv then
35+
---@type string
36+
local version_output = spawn[candidate]({ "--version" }):map(_.prop "stdout"):get_or_else ""
37+
local ok, version = pcall(semver.new, version_output:match "Python (3%.%d+.%d+)")
38+
if ok then
39+
return { executable = candidate, version = version }
40+
end
3041
end
3142
end
3243
return nil
@@ -76,14 +87,14 @@ local function create_venv(pkg)
7687
local supported_python_versions = providers.pypi.get_supported_python_versions(pkg.name, pkg.version):get_or_nil()
7788

7889
-- 1. Resolve stock python3 installation.
79-
local stock_candidates = platform.is.win and { "python", "python3" } or { "python3", "python" }
90+
local stock_candidates = platform.is.win and { "python", "python3", "uv" } or { "python3", "python", "uv" }
8091
local stock_target = resolve_python3(stock_candidates)
8192
if stock_target then
8293
log.fmt_debug("Resolved stock python3 installation version %s", stock_target.version)
8394
end
8495

8596
-- 2. Resolve suitable versioned python3 installation (python3.12, python3.11, etc.).
86-
local versioned_candidates = {}
97+
local versioned_candidates = { "uv" }
8798
if supported_python_versions ~= nil then
8899
if stock_target and not pep440_check_version(tostring(stock_target.version), supported_python_versions) then
89100
log.fmt_debug("Finding versioned candidates for %s", supported_python_versions)
@@ -103,7 +114,8 @@ local function create_venv(pkg)
103114
-- 3. If a versioned python3 installation was not found, warn the user if the stock python3 installation is outside
104115
-- the supported version range.
105116
if
106-
target == stock_target
117+
use_uv == false
118+
and target == stock_target
107119
and supported_python_versions ~= nil
108120
and not pep440_check_version(tostring(target.version), supported_python_versions)
109121
then
@@ -125,9 +137,14 @@ local function create_venv(pkg)
125137
end
126138
end
127139

128-
log.fmt_debug("Found python3 installation version=%s, executable=%s", target.version, target.executable)
129140
ctx.stdio_sink.stdout "Creating virtual environment…\n"
130-
return ctx.spawn[target.executable] { "-m", "venv", "--system-site-packages", VENV_DIR }
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
@@ -153,6 +170,9 @@ end
153170
---@param args SpawnArgs
154171
local function venv_python(args)
155172
local ctx = installer.context()
173+
if use_uv then
174+
return ctx.spawn["uv"](args)
175+
end
156176
return find_venv_executable(ctx, "python"):and_then(function(python_path)
157177
return ctx.spawn[path.concat { ctx.cwd:get(), python_path }](args)
158178
end)
@@ -162,16 +182,28 @@ end
162182
---@param pkgs string[]
163183
---@param extra_args? string[]
164184
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-
}
185+
if use_uv then
186+
return venv_python {
187+
"pip",
188+
"install",
189+
"--directory",
190+
"venv",
191+
"-U",
192+
extra_args or vim.NIL,
193+
pkgs,
194+
}
195+
else
196+
return venv_python {
197+
"-m",
198+
"pip",
199+
"--disable-pip-version-check",
200+
"install",
201+
"--ignore-installed",
202+
"-U",
203+
extra_args or vim.NIL,
204+
pkgs,
205+
}
206+
end
175207
end
176208

177209
---@async
@@ -185,7 +217,7 @@ function M.init(opts)
185217
ctx:promote_cwd()
186218
try(create_venv(opts.package))
187219

188-
if opts.upgrade_pip then
220+
if opts.upgrade_pip and not use_uv then
189221
ctx.stdio_sink.stdout "Upgrading pip inside the virtual environment…\n"
190222
try(pip_install({ "pip" }, opts.install_extra_args))
191223
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)