@@ -9,11 +9,13 @@ local pep440 = require "mason-core.pep440"
99local platform = require " mason-core.platform"
1010local providers = require " mason-core.providers"
1111local semver = require " mason-core.semver"
12+ local settings = require " mason.settings"
1213local spawn = require " mason-core.spawn"
1314
1415local M = {}
1516
16- local VENV_DIR = " venv"
17+ local use_uv = settings .current .pip .use_uv
18+ VENV_DIR = " venv"
1719
1820function M .venv_path (dir )
1921 return path .concat {
@@ -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
@@ -69,10 +80,10 @@ local function get_versioned_candidates(supported_python_versions)
6980 { semver .new " 3.12.0" , " python3.12" },
7081 { semver .new " 3.11.0" , " python3.11" },
7182 { semver .new " 3.10.0" , " python3.10" },
72- { semver .new " 3.9.0" , " python3.9" },
73- { semver .new " 3.8.0" , " python3.8" },
74- { semver .new " 3.7.0" , " python3.7" },
75- { semver .new " 3.6.0" , " python3.6" },
83+ { semver .new " 3.9.0" , " python3.9" },
84+ { semver .new " 3.8.0" , " python3.8" },
85+ { semver .new " 3.7.0" , " python3.7" },
86+ { semver .new " 3.6.0" , " python3.6" },
7687 })
7788end
7889
@@ -84,14 +95,14 @@ local function create_venv(pkg)
8495 local supported_python_versions = providers .pypi .get_supported_python_versions (pkg .name , pkg .version ):get_or_nil ()
8596
8697 -- 1. Resolve stock python3 installation.
87- local stock_candidates = platform .is .win and { " python" , " python3" } or { " python3" , " python" }
98+ local stock_candidates = platform .is .win and { " python" , " python3" , " uv " } or { " python3" , " python" , " uv " }
8899 local stock_target = resolve_python3 (stock_candidates )
89100 if stock_target then
90101 log .fmt_debug (" Resolved stock python3 installation version %s" , stock_target .version )
91102 end
92103
93104 -- 2. Resolve suitable versioned python3 installation (python3.12, python3.11, etc.).
94- local versioned_candidates = {}
105+ local versioned_candidates = { " uv " }
95106 if supported_python_versions ~= nil then
96107 if stock_target and not pep440_check_version (tostring (stock_target .version ), supported_python_versions ) then
97108 log .fmt_debug (" Finding versioned candidates for %s" , supported_python_versions )
@@ -111,31 +122,39 @@ local function create_venv(pkg)
111122 -- 3. If a versioned python3 installation was not found, warn the user if the stock python3 installation is outside
112123 -- the supported version range.
113124 if
114- target == stock_target
125+ use_uv == false
126+ and target == stock_target
115127 and supported_python_versions ~= nil
116128 and not pep440_check_version (tostring (target .version ), supported_python_versions )
117129 then
118130 if ctx .opts .force then
119131 ctx .stdio_sink :stderr (
120- (" Warning: The resolved python3 version %s is not compatible with the required Python versions: %s.\n " ):format (
132+ (" Warning: The resolved python3 version %s is not compatible with the required Python versions: %s.\n " )
133+ :format (
121134 target .version ,
122135 supported_python_versions
123136 )
124137 )
125138 else
126139 ctx .stdio_sink :stderr " Run with :MasonInstall --force to bypass this version validation.\n "
127140 return Result .failure (
128- (" Failed to find a python3 installation in PATH that meets the required versions (%s). Found version: %s." ):format (
141+ (" Failed to find a python3 installation in PATH that meets the required versions (%s). Found version: %s." )
142+ :format (
129143 supported_python_versions ,
130144 target .version
131145 )
132146 )
133147 end
134148 end
135149
136- log .fmt_debug (" Found python3 installation version=%s, executable=%s" , target .version , target .executable )
137150 ctx .stdio_sink :stdout " Creating virtual environment…\n "
138- return ctx .spawn [target .executable ] { " -m" , " venv" , " --system-site-packages" , VENV_DIR }
151+ if use_uv then
152+ log .fmt_debug (" Found uv installation version=%s, executable=%s" , target .version , target .executable )
153+ return ctx .spawn [target .executable ] { " venv" , VENV_DIR }
154+ else
155+ log .fmt_debug (" Found python3 installation version=%s, executable=%s" , target .version , target .executable )
156+ return ctx .spawn [target .executable ] { " -m" , " venv" , " --system-site-packages" , VENV_DIR }
157+ end
139158end
140159
141160--- @param ctx InstallContext
161180--- @param args SpawnArgs
162181local function venv_python (args )
163182 local ctx = installer .context ()
183+ if use_uv then
184+ return ctx .spawn [" uv" ](args )
185+ end
164186 return find_venv_executable (ctx , " python" ):and_then (function (python_path )
165187 return ctx .spawn [path .concat { ctx .cwd :get (), python_path }](args )
166188 end )
@@ -170,15 +192,28 @@ end
170192--- @param pkgs string[]
171193--- @param extra_args ? string[]
172194local function pip_install (pkgs , extra_args )
173- return venv_python {
174- " -m" ,
175- " pip" ,
176- " --disable-pip-version-check" ,
177- " install" ,
178- " --ignore-installed" ,
179- extra_args or vim .NIL ,
180- pkgs ,
181- }
195+ if use_uv then
196+ return venv_python {
197+ " pip" ,
198+ " install" ,
199+ " --directory" ,
200+ " venv" ,
201+ " -U" ,
202+ extra_args or vim .NIL ,
203+ pkgs ,
204+ }
205+ else
206+ return venv_python {
207+ " -m" ,
208+ " pip" ,
209+ " --disable-pip-version-check" ,
210+ " install" ,
211+ " --ignore-installed" ,
212+ " -U" ,
213+ extra_args or vim .NIL ,
214+ pkgs ,
215+ }
216+ end
182217end
183218
184219--- @async
@@ -192,7 +227,7 @@ function M.init(opts)
192227 ctx :promote_cwd ()
193228 try (create_venv (opts .package ))
194229
195- if opts .upgrade_pip then
230+ if opts .upgrade_pip and not use_uv then
196231 ctx .stdio_sink :stdout " Upgrading pip inside the virtual environment…\n "
197232 try (pip_install ({ " pip" }, opts .install_extra_args ))
198233 end
0 commit comments