mirror of https://github.com/astral-sh/uv
Invalidate interpreter marker cache (#529)
In a refactor, we lost the cache invalidation behavior for interpreter markers, leading to stale interpreter errors for me when creating environments with different Python versions. Specifically, the modification timestamp used to be part of the _cache key_ when we used `cacache`. Now it's not -- but it's stored within the cache. So we need to validate the key after-the-fact.
This commit is contained in:
parent
ee2fca3a48
commit
2613382747
|
|
@ -133,6 +133,7 @@ pub struct PyProjectToml {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `[build-backend]` from pyproject.toml
|
/// `[build-backend]` from pyproject.toml
|
||||||
|
#[derive(Debug)]
|
||||||
struct Pep517Backend {
|
struct Pep517Backend {
|
||||||
/// The build backend string such as `setuptools.build_meta:__legacy__` or `maturin` from
|
/// The build backend string such as `setuptools.build_meta:__legacy__` or `maturin` from
|
||||||
/// `build-backend.backend` in pyproject.toml
|
/// `build-backend.backend` in pyproject.toml
|
||||||
|
|
@ -252,14 +253,15 @@ impl SourceBuild {
|
||||||
|
|
||||||
let venv = gourgeist::create_venv(&temp_dir.path().join(".venv"), interpreter)?;
|
let venv = gourgeist::create_venv(&temp_dir.path().join(".venv"), interpreter)?;
|
||||||
|
|
||||||
// There are packages such as DTLSSocket 0.1.16 that say
|
// There are packages such as DTLSSocket 0.1.16 that say:
|
||||||
// ```toml
|
// ```toml
|
||||||
// [build-system]
|
// [build-system]
|
||||||
// requires = ["Cython<3", "setuptools", "wheel"]
|
// requires = ["Cython<3", "setuptools", "wheel"]
|
||||||
// ```
|
// ```
|
||||||
// In this case we need to install requires PEP 517 style but then call setup.py in the
|
// In this case, we need to install requires PEP 517 style but then call setup.py in the
|
||||||
// legacy way
|
// legacy way.
|
||||||
let requirements = if let Some(build_system) = &build_system {
|
let requirements = if let Some(build_system) = build_system.as_ref() {
|
||||||
|
// .filter(|build_system| build_system.build_backend.is_some()) {
|
||||||
let resolved_requirements = build_context
|
let resolved_requirements = build_context
|
||||||
.resolve(&build_system.requires)
|
.resolve(&build_system.requires)
|
||||||
.await
|
.await
|
||||||
|
|
@ -324,14 +326,12 @@ impl SourceBuild {
|
||||||
source_dist,
|
source_dist,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else if !source_tree.join("setup.py").is_file() {
|
||||||
if !source_tree.join("setup.py").is_file() {
|
|
||||||
return Err(Error::InvalidSourceDist(
|
return Err(Error::InvalidSourceDist(
|
||||||
"The archive contains neither a pyproject.toml or a setup.py at the top level"
|
"The archive contains neither a `pyproject.toml` nor a `setup.py` file at the top level"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
temp_dir,
|
temp_dir,
|
||||||
|
|
@ -598,8 +598,9 @@ async fn create_pep517_build_environment(
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the directory with the `pyproject.toml`/`setup.py`
|
/// Returns the directory with the `pyproject.toml`/`setup.py`
|
||||||
#[instrument(skip_all, fields(sdist = ?sdist.file_name().unwrap_or(sdist.as_os_str())))]
|
#[instrument(skip_all, fields(sdist = ? sdist.file_name().unwrap_or(sdist.as_os_str())))]
|
||||||
fn extract_archive(sdist: &Path, extracted: &PathBuf) -> Result<PathBuf, Error> {
|
fn extract_archive(sdist: &Path, extracted: &PathBuf) -> Result<PathBuf, Error> {
|
||||||
if sdist
|
if sdist
|
||||||
.extension()
|
.extension()
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ impl Interpreter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub(crate) struct InterpreterQueryResult {
|
pub(crate) struct InterpreterQueryResult {
|
||||||
pub(crate) markers: MarkerEnvironment,
|
pub(crate) markers: MarkerEnvironment,
|
||||||
pub(crate) base_exec_prefix: PathBuf,
|
pub(crate) base_exec_prefix: PathBuf,
|
||||||
|
|
@ -166,24 +166,31 @@ impl InterpreterQueryResult {
|
||||||
let cache_dir = cache.bucket(CacheBucket::Interpreter);
|
let cache_dir = cache.bucket(CacheBucket::Interpreter);
|
||||||
let cache_path = cache_dir.join(format!("{}.json", digest(&executable_bytes)));
|
let cache_path = cache_dir.join(format!("{}.json", digest(&executable_bytes)));
|
||||||
|
|
||||||
|
let modified = fs_err::metadata(executable)?
|
||||||
|
// Note: This is infallible on windows and unix (i.e., all platforms we support).
|
||||||
|
.modified()?
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.map_err(|err| Error::SystemTime(executable.to_path_buf(), err))?;
|
||||||
|
|
||||||
// Read from the cache.
|
// Read from the cache.
|
||||||
if let Ok(data) = fs::read(&cache_path) {
|
if let Ok(data) = fs::read(&cache_path) {
|
||||||
if let Ok(cached) = serde_json::from_slice::<CachedByTimestamp<Self>>(&data) {
|
if let Ok(cached) = serde_json::from_slice::<CachedByTimestamp<Self>>(&data) {
|
||||||
debug!("Using cached markers for {}", executable.display());
|
if cached.timestamp == modified.as_millis() {
|
||||||
|
debug!("Using cached markers for: {}", executable.display());
|
||||||
return Ok(cached.data);
|
return Ok(cached.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"Ignoring stale cached markers for: {}",
|
||||||
|
executable.display()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, run the Python script.
|
// Otherwise, run the Python script.
|
||||||
debug!("Detecting markers for {}", executable.display());
|
debug!("Detecting markers for: {}", executable.display());
|
||||||
let info = Self::query(executable)?;
|
let info = Self::query(executable)?;
|
||||||
|
|
||||||
let modified = fs_err::metadata(executable)?
|
|
||||||
// Note: This is infallible on windows and unix (i.e. all platforms we support)
|
|
||||||
.modified()?
|
|
||||||
.duration_since(UNIX_EPOCH)
|
|
||||||
.map_err(|err| Error::SystemTime(executable.to_path_buf(), err))?;
|
|
||||||
|
|
||||||
// If `executable` is a pyenv shim, a bash script that redirects to the activated
|
// If `executable` is a pyenv shim, a bash script that redirects to the activated
|
||||||
// python executable at another path, we're not allowed to cache the interpreter info
|
// python executable at another path, we're not allowed to cache the interpreter info
|
||||||
if executable == info.sys_executable {
|
if executable == info.sys_executable {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue