@@ -9,10 +9,12 @@ 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
17+ local use_uv = settings .current .pip .use_uv
1618local 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
@@ -61,10 +72,10 @@ local function get_versioned_candidates(supported_python_versions)
6172 { semver .new " 3.12.0" , " python3.12" },
6273 { semver .new " 3.11.0" , " python3.11" },
6374 { semver .new " 3.10.0" , " python3.10" },
64- { semver .new " 3.9.0" , " python3.9" },
65- { semver .new " 3.8.0" , " python3.8" },
66- { semver .new " 3.7.0" , " python3.7" },
67- { semver .new " 3.6.0" , " python3.6" },
75+ { semver .new " 3.9.0" , " python3.9" },
76+ { semver .new " 3.8.0" , " python3.8" },
77+ { semver .new " 3.7.0" , " python3.7" },
78+ { semver .new " 3.6.0" , " python3.6" },
6879 })
6980end
7081
@@ -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,31 +114,39 @@ 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
110122 if ctx .opts .force then
111123 ctx .stdio_sink .stderr (
112- (" Warning: The resolved python3 version %s is not compatible with the required Python versions: %s.\n " ):format (
124+ (" Warning: The resolved python3 version %s is not compatible with the required Python versions: %s.\n " )
125+ :format (
113126 target .version ,
114127 supported_python_versions
115128 )
116129 )
117130 else
118131 ctx .stdio_sink .stderr " Run with :MasonInstall --force to bypass this version validation.\n "
119132 return Result .failure (
120- (" Failed to find a python3 installation in PATH that meets the required versions (%s). Found version: %s." ):format (
133+ (" Failed to find a python3 installation in PATH that meets the required versions (%s). Found version: %s." )
134+ :format (
121135 supported_python_versions ,
122136 target .version
123137 )
124138 )
125139 end
126140 end
127141
128- log .fmt_debug (" Found python3 installation version=%s, executable=%s" , target .version , target .executable )
129142 ctx .stdio_sink .stdout " Creating virtual environment…\n "
130- return ctx .spawn [target .executable ] { " -m" , " venv" , " --system-site-packages" , VENV_DIR }
143+ if use_uv then
144+ log .fmt_debug (" Found uv installation version=%s, executable=%s" , target .version , target .executable )
145+ return ctx .spawn [target .executable ] { " venv" , VENV_DIR }
146+ else
147+ log .fmt_debug (" Found python3 installation version=%s, executable=%s" , target .version , target .executable )
148+ return ctx .spawn [target .executable ] { " -m" , " venv" , " --system-site-packages" , VENV_DIR }
149+ end
131150end
132151
133152--- @param ctx InstallContext
153172--- @param args SpawnArgs
154173local function venv_python (args )
155174 local ctx = installer .context ()
175+ if use_uv then
176+ return ctx .spawn [" uv" ](args )
177+ end
156178 return find_venv_executable (ctx , " python" ):and_then (function (python_path )
157179 return ctx .spawn [path .concat { ctx .cwd :get (), python_path }](args )
158180 end )
@@ -162,16 +184,28 @@ end
162184--- @param pkgs string[]
163185--- @param extra_args ? string[]
164186local 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- }
187+ if use_uv then
188+ return venv_python {
189+ " pip" ,
190+ " install" ,
191+ " --directory" ,
192+ " venv" ,
193+ " -U" ,
194+ extra_args or vim .NIL ,
195+ pkgs ,
196+ }
197+ else
198+ return venv_python {
199+ " -m" ,
200+ " pip" ,
201+ " --disable-pip-version-check" ,
202+ " install" ,
203+ " --ignore-installed" ,
204+ " -U" ,
205+ extra_args or vim .NIL ,
206+ pkgs ,
207+ }
208+ end
175209end
176210
177211--- @async
@@ -185,7 +219,7 @@ function M.init(opts)
185219 ctx :promote_cwd ()
186220 try (create_venv (opts .package ))
187221
188- if opts .upgrade_pip then
222+ if opts .upgrade_pip and not use_uv then
189223 ctx .stdio_sink .stdout " Upgrading pip inside the virtual environment…\n "
190224 try (pip_install ({ " pip" }, opts .install_extra_args ))
191225 end
0 commit comments