From ed23343d1c5cc711e3c0ff6ed551546b751badbd Mon Sep 17 00:00:00 2001 From: jdx <216188+jdx@users.noreply.github.com> Date: Wed, 11 Dec 2024 19:37:02 -0600 Subject: [PATCH] feat: add settings for python venv creation (#3489) Fixes #1721 Fixes #1720 --- docs/lang/python.md | 3 ++ schema/mise.json | 20 ++++++++ settings.toml | 12 +++++ src/config/config_file/mise_toml.rs | 30 +++++++++++- src/config/env_directive.rs | 76 ++++++++++++++++++++++++++--- 5 files changed, 132 insertions(+), 9 deletions(-) diff --git a/docs/lang/python.md b/docs/lang/python.md index bb2d74b5eb..d58544e710 100644 --- a/docs/lang/python.md +++ b/docs/lang/python.md @@ -136,6 +136,9 @@ _.python.venv = ".venv" # relative to this file's directory _.python.venv = "/root/.venv" # can be absolute _.python.venv = "{{env.HOME}}/.cache/venv/myproj" # can use templates _.python.venv = { path = ".venv", create = true } # create the venv if it doesn't exist +_.python.venv = { path = ".venv", create = true, python = "3.10" } # use a specific python version +_.python.venv = { path = ".venv", create = true, python_create_args = "--without-pip" } # pass args to python -m venv +_.python.venv = { path = ".venv", create = true, uv_create_args = "--system-site-packages" } # pass args to uv venv ``` The venv will need to be created manually with `python -m venv /path/to/venv` unless `create=true`. diff --git a/schema/mise.json b/schema/mise.json index 18c0df7870..6d4b167223 100644 --- a/schema/mise.json +++ b/schema/mise.json @@ -77,6 +77,18 @@ "path": { "description": "path to python virtual environment to use", "type": "string" + }, + "python": { + "description": "python version to use", + "type": "string" + }, + "python_create_args": { + "description": "additional arguments to pass to python when creating a virtual environment", + "type": "string" + }, + "uv_create_args": { + "description": "additional arguments to pass to uv when creating a virtual environment", + "type": "string" } }, "required": ["path"], @@ -505,11 +517,19 @@ "description": "Integrate with uv to automatically create/source venvs if uv.lock is present.", "type": "boolean" }, + "uv_venv_create_args": { + "description": "Arguments to pass to uv when creating a venv.", + "type": "string" + }, "venv_auto_create": { "description": "Automatically create virtualenvs for python tools.", "type": "boolean", "deprecated": true }, + "venv_create_args": { + "description": "Arguments to pass to python when creating a venv. (not used for uv venv creation)", + "type": "string" + }, "venv_stdlib": { "description": "Prefer to use venv from Python's standard library.", "type": "boolean" diff --git a/settings.toml b/settings.toml index 48333cf488..5edaa8e305 100644 --- a/settings.toml +++ b/settings.toml @@ -651,6 +651,12 @@ env = "MISE_PYTHON_UV_VENV_AUTO" type = "Bool" description = "Integrate with uv to automatically create/source venvs if uv.lock is present." +[python.uv_venv_create_args] +env = "MISE_PYTHON_UV_VENV_CREATE_ARGS" +type = "String" +optional = true +description = "Arguments to pass to uv when creating a venv." + [python.venv_auto_create] env = "MISE_PYTHON_VENV_AUTO_CREATE" type = "Bool" @@ -658,6 +664,12 @@ hide = true deprecated = "Use env._python.venv instead." description = "Automatically create virtualenvs for python tools." +[python.venv_create_args] +env = "MISE_PYTHON_VENV_CREATE_ARGS" +type = "String" +optional = true +description = "Arguments to pass to python when creating a venv. (not used for uv venv creation)" + [python.venv_stdlib] env = "MISE_VENV_STDLIB" type = "Bool" diff --git a/src/config/config_file/mise_toml.rs b/src/config/config_file/mise_toml.rs index 011d0b1696..2160c73178 100644 --- a/src/config/config_file/mise_toml.rs +++ b/src/config/config_file/mise_toml.rs @@ -692,6 +692,9 @@ impl<'de> de::Deserialize<'de> for EnvList { struct EnvDirectivePythonVenv { path: PathBuf, create: bool, + python: Option, + uv_create_args: Option>, + python_create_args: Option>, } #[derive(Deserialize, Default)] @@ -739,6 +742,9 @@ impl<'de> de::Deserialize<'de> for EnvList { Ok(EnvDirectivePythonVenv { path: v.into(), create: false, + python: None, + uv_create_args: None, + python_create_args: None, }) } @@ -751,6 +757,9 @@ impl<'de> de::Deserialize<'de> for EnvList { { let mut path = None; let mut create = false; + let mut python = None; + let mut uv_create_args = None; + let mut python_create_args = None; while let Some(key) = map.next_key::()? { match key.as_str() { "path" => { @@ -759,6 +768,16 @@ impl<'de> de::Deserialize<'de> for EnvList { "create" => { create = map.next_value()?; } + "python" => { + python = Some(map.next_value()?); + } + "uv_create_args" => { + uv_create_args = Some(map.next_value()?); + } + "python_create_args" => { + python_create_args = + Some(map.next_value()?); + } _ => { return Err(de::Error::unknown_field( &key, @@ -769,7 +788,13 @@ impl<'de> de::Deserialize<'de> for EnvList { } let path = path .ok_or_else(|| de::Error::missing_field("path"))?; - Ok(EnvDirectivePythonVenv { path, create }) + Ok(EnvDirectivePythonVenv { + path, + create, + python, + uv_create_args, + python_create_args, + }) } } @@ -800,6 +825,9 @@ impl<'de> de::Deserialize<'de> for EnvList { env.push(EnvDirective::PythonVenv { path: venv.path, create: venv.create, + python: venv.python, + uv_create_args: venv.uv_create_args, + python_create_args: venv.python_create_args, }); } } diff --git a/src/config/env_directive.rs b/src/config/env_directive.rs index 3ab018efa0..11194797a4 100644 --- a/src/config/env_directive.rs +++ b/src/config/env_directive.rs @@ -87,6 +87,9 @@ pub enum EnvDirective { PythonVenv { path: PathBuf, create: bool, + python: Option, + uv_create_args: Option>, + python_create_args: Option>, }, Module(String, toml::Value), } @@ -112,11 +115,26 @@ impl Display for EnvDirective { EnvDirective::Path(path) => write!(f, "path_add {}", display_path(path)), EnvDirective::Source(path) => write!(f, "source {}", display_path(path)), EnvDirective::Module(name, _) => write!(f, "module {}", name), - EnvDirective::PythonVenv { path, create } => { + EnvDirective::PythonVenv { + path, + create, + python, + uv_create_args, + python_create_args, + } => { write!(f, "python venv path={}", display_path(path))?; if *create { write!(f, " create")?; } + if let Some(python) = python { + write!(f, " python={}", python)?; + } + if let Some(args) = uv_create_args { + write!(f, " uv_create_args={:?}", args)?; + } + if let Some(args) = python_create_args { + write!(f, " python_create_args={:?}", args)?; + } Ok(()) } } @@ -251,7 +269,13 @@ impl EnvResults { } } } - EnvDirective::PythonVenv { path, create } => { + EnvDirective::PythonVenv { + path, + create, + python, + uv_create_args, + python_create_args, + } => { trace!("python venv: {} create={create}", display_path(&path)); trust_check(&source)?; let venv = r.parse_template(&ctx, &source, path.to_string_lossy().as_ref())?; @@ -278,14 +302,44 @@ impl EnvResults { let use_uv = !SETTINGS.python.venv_stdlib && has_uv_bin; let cmd = if use_uv { info!("creating venv with uv at: {}", display_path(&venv)); - CmdLineRunner::new("uv").args(["venv", &venv.to_string_lossy()]) + let extra = SETTINGS + .python + .uv_venv_create_args + .as_ref() + .and_then(|a| match shell_words::split(a) { + Ok(a) => Some(a), + Err(err) => { + warn!("failed to split uv_venv_create_args: {}", err); + None + } + }) + .or(uv_create_args) + .unwrap_or_default(); + let mut cmd = CmdLineRunner::new("uv") + .args(["venv", &venv.to_string_lossy()]); + if let Some(python) = python { + cmd = cmd.args(["--python", &python]); + } + cmd.args(extra) } else { info!("creating venv with stdlib at: {}", display_path(&venv)); - CmdLineRunner::new("python3").args([ - "-m", - "venv", - &venv.to_string_lossy(), - ]) + let extra = SETTINGS + .python + .venv_create_args + .as_ref() + .and_then(|a| match shell_words::split(a) { + Ok(a) => Some(a), + Err(err) => { + warn!("failed to split venv_create_args: {}", err); + None + } + }) + .or(python_create_args) + .unwrap_or_default(); + let bin = format!("python{}", python.unwrap_or("3".into())); + CmdLineRunner::new(bin) + .args(["-m", "venv", &venv.to_string_lossy()]) + .args(extra) } .envs(&env_vars) .env( @@ -460,6 +514,9 @@ mod tests { EnvDirective::PythonVenv { path: PathBuf::from("/"), create: false, + python: None, + uv_create_args: None, + python_create_args: None, }, Default::default(), ), @@ -467,6 +524,9 @@ mod tests { EnvDirective::PythonVenv { path: PathBuf::from("./"), create: false, + python: None, + uv_create_args: None, + python_create_args: None, }, Default::default(), ),