Skip to content

Commit

Permalink
Merge pull request #1 from KingMichaelPark/feat/add-uv-as-pypi-source
Browse files Browse the repository at this point in the history
Feat/add uv as pypi source
  • Loading branch information
KingMichaelPark authored Oct 25, 2024
2 parents e2f7f90 + 6c3a15e commit f20ef6c
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 22 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ local DEFAULT_SETTINGS = {
-- Whether to upgrade pip to the latest version in the virtual environment before installing packages.
upgrade_pip = false,

---@since 1.8.0
-- Whether to use uv to install packages instead of pip
use_uv = false,

---@since 1.0.0
-- These args will be added to `pip install` calls. Note that setting extra args might impact intended behavior
-- and is not recommended.
Expand Down
4 changes: 4 additions & 0 deletions doc/mason.txt
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,10 @@ Example:
-- Whether to upgrade pip to the latest version in the virtual environment before installing packages.
upgrade_pip = false,

---@since 1.8.0
-- Whether to use uv to install packages instead of pip
use_uv = false,

---@since 1.0.0
-- These args will be added to `pip install` calls. Note that setting extra args might impact intended behavior
-- and is not recommended.
Expand Down
82 changes: 60 additions & 22 deletions lua/mason-core/installer/managers/pypi.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,18 @@ local pep440 = require "mason-core.pep440"
local platform = require "mason-core.platform"
local providers = require "mason-core.providers"
local semver = require "mason-core.semver"
local settings = require "mason.settings"
local spawn = require "mason-core.spawn"

local M = {}

local VENV_DIR = "venv"
local use_uv = settings.current.pip.use_uv
local VENV_DIR
if use_uv then
VENV_DIR = ".venv"
else
VENV_DIR = "venv"
end

---@async
---@param candidates string[]
Expand All @@ -22,11 +29,20 @@ local function resolve_python3(candidates)
a.scheduler()
local available_candidates = _.filter(is_executable, candidates)
for __, candidate in ipairs(available_candidates) do
---@type string
local version_output = spawn[candidate]({ "--version" }):map(_.prop "stdout"):get_or_else ""
local ok, version = pcall(semver.new, version_output:match "Python (3%.%d+.%d+)")
if ok then
return { executable = candidate, version = version }
if use_uv and candidate == "uv" then
---@type string
local version_output = spawn[candidate]({ "--version" }):map(_.prop "stdout"):get_or_else ""
local ok, version = pcall(semver.new, version_output:match "uv (%d+.%d+.%d+).*")
if ok then
return { executable = candidate, version = version }
end
elseif not use_uv then
---@type string
local version_output = spawn[candidate]({ "--version" }):map(_.prop "stdout"):get_or_else ""
local ok, version = pcall(semver.new, version_output:match "Python (3%.%d+.%d+)")
if ok then
return { executable = candidate, version = version }
end
end
end
return nil
Expand Down Expand Up @@ -76,14 +92,14 @@ local function create_venv(pkg)
local supported_python_versions = providers.pypi.get_supported_python_versions(pkg.name, pkg.version):get_or_nil()

-- 1. Resolve stock python3 installation.
local stock_candidates = platform.is.win and { "python", "python3" } or { "python3", "python" }
local stock_candidates = platform.is.win and { "python", "python3", "uv" } or { "python3", "python", "uv" }
local stock_target = resolve_python3(stock_candidates)
if stock_target then
log.fmt_debug("Resolved stock python3 installation version %s", stock_target.version)
end

-- 2. Resolve suitable versioned python3 installation (python3.12, python3.11, etc.).
local versioned_candidates = {}
local versioned_candidates = { "uv" }
if supported_python_versions ~= nil then
if stock_target and not pep440_check_version(tostring(stock_target.version), supported_python_versions) then
log.fmt_debug("Finding versioned candidates for %s", supported_python_versions)
Expand All @@ -103,7 +119,8 @@ local function create_venv(pkg)
-- 3. If a versioned python3 installation was not found, warn the user if the stock python3 installation is outside
-- the supported version range.
if
target == stock_target
use_uv == false
and target == stock_target
and supported_python_versions ~= nil
and not pep440_check_version(tostring(target.version), supported_python_versions)
then
Expand All @@ -125,9 +142,14 @@ local function create_venv(pkg)
end
end

log.fmt_debug("Found python3 installation version=%s, executable=%s", target.version, target.executable)
ctx.stdio_sink.stdout "Creating virtual environment…\n"
return ctx.spawn[target.executable] { "-m", "venv", "--system-site-packages", VENV_DIR }
if use_uv then
log.fmt_debug("Found uv installation version=%s, executable=%s", target.version, target.executable)
return ctx.spawn[target.executable] { "venv", VENV_DIR }
else
log.fmt_debug("Found python3 installation version=%s, executable=%s", target.version, target.executable)
return ctx.spawn[target.executable] { "-m", "venv", "--system-site-packages", VENV_DIR }
end
end

---@param ctx InstallContext
Expand All @@ -153,6 +175,9 @@ end
---@param args SpawnArgs
local function venv_python(args)
local ctx = installer.context()
if use_uv then
return ctx.spawn[{ "uv", "venv" }](args)
end
return find_venv_executable(ctx, "python"):and_then(function(python_path)
return ctx.spawn[path.concat { ctx.cwd:get(), python_path }](args)
end)
Expand All @@ -162,16 +187,29 @@ end
---@param pkgs string[]
---@param extra_args? string[]
local function pip_install(pkgs, extra_args)
return venv_python {
"-m",
"pip",
"--disable-pip-version-check",
"install",
"--ignore-installed",
"-U",
extra_args or vim.NIL,
pkgs,
}
if use_uv then
local ctx = installer.context()

local task = ctx.spawn["uv"] {
"pip",
"install",
"-U",
extra_args or vim.NIL,
pkgs,
}
return task
else
return venv_python {
"-m",
"pip",
"--disable-pip-version-check",
"install",
"--ignore-installed",
"-U",
extra_args or vim.NIL,
pkgs,
}
end
end

---@async
Expand All @@ -185,7 +223,7 @@ function M.init(opts)
ctx:promote_cwd()
try(create_venv(opts.package))

if opts.upgrade_pip then
if opts.upgrade_pip and not use_uv then
ctx.stdio_sink.stdout "Upgrading pip inside the virtual environment…\n"
try(pip_install({ "pip" }, opts.install_extra_args))
end
Expand Down
3 changes: 3 additions & 0 deletions lua/mason-core/installer/registry/providers/pypi.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ function M.parse(source, purl)
pip = {
upgrade = settings.current.pip.upgrade_pip,
extra_args = settings.current.pip.install_args,
use_uv = settings.current.pip.use_uv,
},
}

Expand All @@ -48,11 +49,13 @@ function M.install(ctx, source)
},
upgrade_pip = source.pip.upgrade,
install_extra_args = source.pip.extra_args,
use_uv = source.pip.use_uv,
})
try(pypi.install(source.package, source.version, {
extra = source.extra,
extra_packages = source.extra_packages,
install_extra_args = source.pip.extra_args,
use_uv = source.pip.use_uv,
}))
end)
end
Expand Down
4 changes: 4 additions & 0 deletions lua/mason/settings.lua
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ local DEFAULT_SETTINGS = {
-- Whether to upgrade pip to the latest version in the virtual environment before installing packages.
upgrade_pip = false,

---@since 1.8.0
-- Whether to use uv to install packages instead of pip
use_uv = false,

---@since 1.0.0
-- These args will be added to `pip install` calls. Note that setting extra args might impact intended behavior
-- and is not recommended.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ describe("pypi provider :: parsing", function()
pip = {
upgrade = true,
extra_args = { "--proxy", "http://localghost" },
use_uv = false,
},
},
pypi.parse({ extra_packages = { "extra" } }, purl())
Expand Down

0 comments on commit f20ef6c

Please sign in to comment.