Refactor PythonVersionFile global loading (#14107)

I was looking into `uv tool` not supporting version files, and noticed
this implementation was confusing and skipped handling like a tracing
log if `--no-config` excludes selection a file. I've refactored it in
preparation for the next change.
This commit is contained in:
Zanie Blue 2025-06-20 15:31:32 -05:00 committed by GitHub
parent 563e9495ba
commit 1dbe750452
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 46 additions and 47 deletions

View file

@ -37,11 +37,14 @@ pub enum FilePreference {
pub struct DiscoveryOptions<'a> {
/// The path to stop discovery at.
stop_discovery_at: Option<&'a Path>,
/// When `no_config` is set, Python version files will be ignored.
/// Ignore Python version files.
///
/// Discovery will still run in order to display a log about the ignored file.
no_config: bool,
/// Whether `.python-version` or `.python-versions` should be preferred.
preference: FilePreference,
/// Whether to ignore local version files, and only search for a global one.
no_local: bool,
}
impl<'a> DiscoveryOptions<'a> {
@ -62,6 +65,11 @@ impl<'a> DiscoveryOptions<'a> {
..self
}
}
#[must_use]
pub fn with_no_local(self, no_local: bool) -> Self {
Self { no_local, ..self }
}
}
impl PythonVersionFile {
@ -70,33 +78,38 @@ impl PythonVersionFile {
working_directory: impl AsRef<Path>,
options: &DiscoveryOptions<'_>,
) -> Result<Option<Self>, std::io::Error> {
let Some(path) = Self::find_nearest(&working_directory, options) else {
if let Some(stop_discovery_at) = options.stop_discovery_at {
if stop_discovery_at == working_directory.as_ref() {
debug!(
"No Python version file found in workspace: {}",
working_directory.as_ref().display()
);
let allow_local = !options.no_local;
let Some(path) = allow_local.then(|| {
// First, try to find a local version file.
let local = Self::find_nearest(&working_directory, options);
if local.is_none() {
// Log where we searched for the file, if not found
if let Some(stop_discovery_at) = options.stop_discovery_at {
if stop_discovery_at == working_directory.as_ref() {
debug!(
"No Python version file found in workspace: {}",
working_directory.as_ref().display()
);
} else {
debug!(
"No Python version file found between working directory `{}` and workspace root `{}`",
working_directory.as_ref().display(),
stop_discovery_at.display()
);
}
} else {
debug!(
"No Python version file found between working directory `{}` and workspace root `{}`",
working_directory.as_ref().display(),
stop_discovery_at.display()
"No Python version file found in ancestors of working directory: {}",
working_directory.as_ref().display()
);
}
} else {
debug!(
"No Python version file found in ancestors of working directory: {}",
working_directory.as_ref().display()
);
}
// Not found in directory or its ancestors. Looking in user-level config.
return Ok(match user_uv_config_dir() {
Some(user_dir) => Self::discover_user_config(user_dir, options)
.await?
.or(None),
None => None,
});
local
}).flatten().or_else(|| {
// Search for a global config
Self::find_global(options)
}) else {
return Ok(None);
};
if options.no_config {
@ -111,20 +124,9 @@ impl PythonVersionFile {
Self::try_from_path(path).await
}
pub async fn discover_user_config(
user_config_working_directory: impl AsRef<Path>,
options: &DiscoveryOptions<'_>,
) -> Result<Option<Self>, std::io::Error> {
if !options.no_config {
if let Some(path) =
Self::find_in_directory(user_config_working_directory.as_ref(), options)
.into_iter()
.find(|path| path.is_file())
{
return Self::try_from_path(path).await;
}
}
Ok(None)
fn find_global(options: &DiscoveryOptions<'_>) -> Option<PathBuf> {
let user_config_dir = user_uv_config_dir()?;
Self::find_in_directory(&user_config_dir, options)
}
fn find_nearest(path: impl AsRef<Path>, options: &DiscoveryOptions<'_>) -> Option<PathBuf> {

View file

@ -57,16 +57,13 @@ pub(crate) async fn pin(
}
};
let version_file = if global {
if let Some(path) = user_uv_config_dir() {
PythonVersionFile::discover_user_config(path, &VersionFileDiscoveryOptions::default())
.await
} else {
Ok(None)
}
} else {
PythonVersionFile::discover(project_dir, &VersionFileDiscoveryOptions::default()).await
};
// Search for an existing file, we won't necessarily write to this, we'll construct a target
// path if there's a request later on.
let version_file = PythonVersionFile::discover(
project_dir,
&VersionFileDiscoveryOptions::default().with_no_local(global),
)
.await;
if rm {
let Some(file) = version_file? else {