Skip to content

Commit

Permalink
feat: allow passing extra config k,v pairs for pyvenv.cfg when creati…
Browse files Browse the repository at this point in the history
…ng venv
  • Loading branch information
samypr100 committed Feb 22, 2024
1 parent da3a7ec commit 1f95a17
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 28 deletions.
86 changes: 62 additions & 24 deletions crates/gourgeist/src/bare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const VIRTUALENV_PATCH: &str = include_str!("_virtualenv.py");
/// Very basic `.cfg` file format writer.
fn write_cfg(
f: &mut impl Write,
data: &[(&str, String); 8],
data: &[(String, String)],
prompt: Option<String>,
) -> io::Result<()> {
for (key, value) in data {
Expand Down Expand Up @@ -73,6 +73,7 @@ pub fn create_bare_venv(
location: &Utf8Path,
interpreter: &Interpreter,
prompt: Prompt,
extra_cfg: Vec<(String, String)>,
) -> io::Result<VenvPaths> {
// We have to canonicalize the interpreter path, otherwise the home is set to the venv dir instead of the real root.
// This would make python-build-standalone fail with the encodings module not being found because its home is wrong.
Expand Down Expand Up @@ -226,31 +227,68 @@ pub fn create_bare_venv(
} else {
unimplemented!("Only Windows and Unix are supported")
};
let pyvenv_cfg_data = &[
("home", python_home),
(
"implementation",
interpreter.markers().platform_python_implementation.clone(),
),
(
"version_info",
interpreter.markers().python_version.string.clone(),
),
("gourgeist", env!("CARGO_PKG_VERSION").to_string()),
// I wouldn't allow this option anyway
("include-system-site-packages", "false".to_string()),
(
"base-prefix",
interpreter.base_prefix().to_string_lossy().to_string(),
),
(
"base-exec-prefix",
interpreter.base_exec_prefix().to_string_lossy().to_string(),
),
("base-executable", base_python.to_string()),

// Validate extra_cfg
let reserved_keys = [
"home",
"implementation",
"version_info",
"gourgeist",
"include-system-site-packages",
"base-prefix",
"base-exec-prefix",
"base-executable",
"prompt",
];
for (key, _) in &extra_cfg {
if reserved_keys.contains(&key.as_str()) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Reserved key found in extra_cfg: {key}"),
));
}
}

let pyvenv_cfg_data: Vec<(String, String)> = [
vec![
("home".to_string(), python_home),
(
"implementation".to_string(),
interpreter.markers().platform_python_implementation.clone(),
),
(
"version_info".to_string(),
interpreter.markers().python_version.string.clone(),
),
(
"gourgeist".to_string(),
env!("CARGO_PKG_VERSION").to_string(),
),
],
// Put custom cfg pairs after "gourgeist"
extra_cfg,
vec![
(
"include-system-site-packages".to_string(),
"false".to_string(),
),
(
"base-prefix".to_string(),
interpreter.base_prefix().to_string_lossy().to_string(),
),
(
"base-exec-prefix".to_string(),
interpreter.base_exec_prefix().to_string_lossy().to_string(),
),
("base-executable".to_string(), base_python.to_string()),
],
]
.into_iter()
.flatten()
.collect();

let mut pyvenv_cfg = BufWriter::new(File::create(location.join("pyvenv.cfg"))?);
write_cfg(&mut pyvenv_cfg, pyvenv_cfg_data, prompt)?;
write_cfg(&mut pyvenv_cfg, &pyvenv_cfg_data, prompt)?;
drop(pyvenv_cfg);

let site_packages = if cfg!(unix) {
Expand Down
3 changes: 2 additions & 1 deletion crates/gourgeist/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@ pub fn create_venv(
location: &Path,
interpreter: Interpreter,
prompt: Prompt,
extra_cfg: Vec<(String, String)>,
) -> Result<Virtualenv, Error> {
let location: &Utf8Path = location
.try_into()
.map_err(|err: FromPathError| err.into_io_error())?;
let paths = create_bare_venv(location, &interpreter, prompt)?;
let paths = create_bare_venv(location, &interpreter, prompt, extra_cfg)?;
Ok(Virtualenv::from_interpreter(
interpreter,
paths.root.as_std_path(),
Expand Down
2 changes: 1 addition & 1 deletion crates/gourgeist/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fn run() -> Result<(), gourgeist::Error> {
Cache::from_path(".gourgeist_cache")?
};
let info = Interpreter::query(python.as_std_path(), &platform, &cache).unwrap();
create_bare_venv(&location, &info, Prompt::from_args(cli.prompt))?;
create_bare_venv(&location, &info, Prompt::from_args(cli.prompt), Vec::new())?;
Ok(())
}

Expand Down
7 changes: 7 additions & 0 deletions crates/uv-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,10 +325,17 @@ impl SourceBuild {
let pep517_backend = Self::get_pep517_backend(setup_py, &source_tree, &default_backend)
.map_err(|err| *err)?;

// Extra cfg for pyvenv.cfg to specify build backend
let extra_cfg = vec![(
"uv-build".to_string(),
env!("CARGO_PKG_VERSION").to_string(),
)];

let venv = gourgeist::create_venv(
&temp_dir.path().join(".venv"),
interpreter.clone(),
gourgeist::Prompt::None,
extra_cfg,
)?;

// Setup the build environment.
Expand Down
6 changes: 5 additions & 1 deletion crates/uv/src/commands/venv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,12 @@ async fn venv_impl(
)
.into_diagnostic()?;

// Extra cfg for pyvenv.cfg to specify uv version
let extra_cfg = vec![("uv".to_string(), env!("CARGO_PKG_VERSION").to_string())];

// Create the virtual environment.
let venv = gourgeist::create_venv(path, interpreter, prompt).map_err(VenvError::Creation)?;
let venv = gourgeist::create_venv(path, interpreter, prompt, extra_cfg)
.map_err(VenvError::Creation)?;

// Install seed packages.
if seed {
Expand Down
21 changes: 20 additions & 1 deletion crates/uv/tests/venv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use assert_fs::prelude::*;

use uv_fs::Normalized;

use crate::common::{create_bin_with_executables, get_bin, uv_snapshot, EXCLUDE_NEWER};
use crate::common::{
create_bin_with_executables, get_bin, uv_snapshot, TestContext, EXCLUDE_NEWER,
};

mod common;

Expand Down Expand Up @@ -575,3 +577,20 @@ fn virtualenv_compatibility() -> Result<()> {

Ok(())
}

#[test]
fn verify_pyvenv_cfg() {
let context = TestContext::new("3.12");
let venv = context.temp_dir.child(".venv");
let pyvenv_cfg = venv.child("pyvenv.cfg");

venv.assert(predicates::path::is_dir());

// Check pyvenv.cfg exists
pyvenv_cfg.assert(predicates::path::is_file());

// Check if "uv = version" is present in the file
let version = env!("CARGO_PKG_VERSION").to_string();
let search_string = format!("uv = {version}");
pyvenv_cfg.assert(predicates::str::contains(search_string));
}

0 comments on commit 1f95a17

Please sign in to comment.