Expand scope of archive timestamping (#1960)

## Summary

Instead of looking at _either_ `pyproject.toml` or `setup.py`, we should
just be conservative and take the most-recent timestamp out of
`pyproject.toml`, `setup.py`, and `setup.cfg`. That will help prevent
staleness issues like those described in
https://github.com/astral-sh/uv/issues/1913#issuecomment-1961544084.
This commit is contained in:
Charlie Marsh 2024-02-24 19:36:45 -05:00 committed by GitHub
parent 8d706b0f2a
commit f449bd41fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,3 +1,4 @@
use std::cmp::max;
use std::fmt::{Display, Formatter};
use std::io;
use std::io::Write;
@ -638,33 +639,48 @@ impl ArchiveTimestamp {
/// Return the modification timestamp for an archive, which could be a file (like a wheel or a zip
/// archive) or a directory containing a Python package.
///
/// If the path is to a directory with no entrypoint (i.e., no `pyproject.toml` or `setup.py`),
/// returns `None`.
/// If the path is to a directory with no entrypoint (i.e., no `pyproject.toml`, `setup.py`, or
/// `setup.cfg`), returns `None`.
pub fn from_path(path: impl AsRef<Path>) -> Result<Option<Self>, io::Error> {
let metadata = fs_err::metadata(path.as_ref())?;
if metadata.is_file() {
Ok(Some(Self::Exact(Timestamp::from_metadata(&metadata))))
} else {
// TODO(charlie): Take the maximum of `pyproject.toml`, `setup.py`, and `setup.cfg`.
if let Some(metadata) = path
// Compute the modification timestamp for the `pyproject.toml`, `setup.py`, and
// `setup.cfg` files, if they exist.
let pyproject_toml = path
.as_ref()
.join("pyproject.toml")
.metadata()
.ok()
.filter(std::fs::Metadata::is_file)
{
Ok(Some(Self::Approximate(Timestamp::from_metadata(&metadata))))
} else if let Some(metadata) = path
.as_ref()
.map(Timestamp::from_metadata);
let setup_py = path
.as_ref()
.join("setup.py")
.metadata()
.ok()
.filter(std::fs::Metadata::is_file)
{
Ok(Some(Self::Approximate(Timestamp::from_metadata(&metadata))))
} else {
Ok(None)
}
.as_ref()
.map(Timestamp::from_metadata);
let setup_cfg = path
.as_ref()
.join("setup.cfg")
.metadata()
.ok()
.filter(std::fs::Metadata::is_file)
.as_ref()
.map(Timestamp::from_metadata);
// Take the most recent timestamp of the three files.
let Some(timestamp) = max(pyproject_toml, max(setup_py, setup_cfg)) else {
return Ok(None);
};
Ok(Some(Self::Approximate(timestamp)))
}
}