Use cached environments in PEP 723 execution (#4789)

## Summary

This seems like another good candidate for environment caching. If you
run a script repeatedly, we can just use the existing cached
environment.
This commit is contained in:
Charlie Marsh 2024-07-04 13:23:13 -04:00 committed by GitHub
parent 892106fef0
commit 6a27135a65
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 25 additions and 22 deletions

View file

@ -126,4 +126,9 @@ impl CachedEnvironment {
Ok(Self(venv)) Ok(Self(venv))
} }
/// Convert the [`EphemeralEnvironment`] into an [`Interpreter`].
pub(crate) fn into_interpreter(self) -> Interpreter {
self.0.into_interpreter()
}
} }

View file

@ -21,6 +21,7 @@ use uv_requirements::{RequirementsSource, RequirementsSpecification};
use uv_warnings::warn_user_once; use uv_warnings::warn_user_once;
use crate::commands::pip::operations::Modifications; use crate::commands::pip::operations::Modifications;
use crate::commands::project::environment::CachedEnvironment;
use crate::commands::{project, ExitStatus, SharedState}; use crate::commands::{project, ExitStatus, SharedState};
use crate::printer::Printer; use crate::printer::Printer;
use crate::settings::ResolverInstallerSettings; use crate::settings::ResolverInstallerSettings;
@ -55,19 +56,10 @@ pub(crate) async fn run(
let state = SharedState::default(); let state = SharedState::default();
// Determine whether the command to execute is a PEP 723 script. // Determine whether the command to execute is a PEP 723 script.
let temp_dir;
let script_interpreter = if let RunCommand::Python(target, _) = &command { let script_interpreter = if let RunCommand::Python(target, _) = &command {
if let Some(metadata) = uv_scripts::read_pep723_metadata(&target).await? { if let Some(metadata) = uv_scripts::read_pep723_metadata(&target).await? {
debug!("Found PEP 723 script at: {}", target.display()); debug!("Found PEP 723 script at: {}", target.display());
let spec = RequirementsSpecification::from_requirements(
metadata
.dependencies
.into_iter()
.map(Requirement::from)
.collect(),
);
// (1) Explicit request from user // (1) Explicit request from user
let python_request = if let Some(request) = python.as_deref() { let python_request = if let Some(request) = python.as_deref() {
Some(PythonRequest::parse(request)) Some(PythonRequest::parse(request))
@ -96,20 +88,15 @@ pub(crate) async fn run(
.await? .await?
.into_interpreter(); .into_interpreter();
// Create a virtual environment
temp_dir = cache.environment()?;
let venv = uv_virtualenv::create_venv(
temp_dir.path(),
interpreter,
uv_virtualenv::Prompt::None,
false,
false,
)?;
// Install the script requirements. // Install the script requirements.
let environment = project::update_environment( let requirements = metadata
venv, .dependencies
spec, .into_iter()
.map(Requirement::from)
.collect();
let environment = CachedEnvironment::get_or_create(
requirements,
interpreter,
&settings, &settings,
&state, &state,
preview, preview,

View file

@ -223,6 +223,7 @@ fn run_script() -> Result<()> {
"# "#
})?; })?;
// Running the script should install the requirements.
uv_snapshot!(context.filters(), context.run().arg("--preview").arg("main.py"), @r###" uv_snapshot!(context.filters(), context.run().arg("--preview").arg("main.py"), @r###"
success: true success: true
exit_code: 0 exit_code: 0
@ -235,6 +236,16 @@ fn run_script() -> Result<()> {
+ iniconfig==2.0.0 + iniconfig==2.0.0
"###); "###);
// Running again should use the existing environment.
uv_snapshot!(context.filters(), context.run().arg("--preview").arg("main.py"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 1 package in [TIME]
"###);
// Otherwise, the script requirements should _not_ be available, but the project requirements // Otherwise, the script requirements should _not_ be available, but the project requirements
// should. // should.
let test_non_script = context.temp_dir.child("main.py"); let test_non_script = context.temp_dir.child("main.py");