mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-03 05:03:46 +00:00
Avoid TOCTOU errors in .python-version reads (#5223)
## Summary Not a big deal, but better to try the operation and handle the failure case than to check if the file exists and _then_ read it.
This commit is contained in:
parent
ed9b820815
commit
2169902bd9
2 changed files with 21 additions and 33 deletions
|
|
@ -14,8 +14,8 @@ pub use crate::prefix::Prefix;
|
||||||
pub use crate::python_version::PythonVersion;
|
pub use crate::python_version::PythonVersion;
|
||||||
pub use crate::target::Target;
|
pub use crate::target::Target;
|
||||||
pub use crate::version_files::{
|
pub use crate::version_files::{
|
||||||
request_from_version_file, requests_from_version_file, version_file_exists,
|
request_from_version_file, requests_from_version_file, PYTHON_VERSIONS_FILENAME,
|
||||||
versions_file_exists, PYTHON_VERSIONS_FILENAME, PYTHON_VERSION_FILENAME,
|
PYTHON_VERSION_FILENAME,
|
||||||
};
|
};
|
||||||
pub use crate::virtualenv::{Error as VirtualEnvError, PyVenvConfiguration, VirtualEnvironment};
|
pub use crate::virtualenv::{Error as VirtualEnvError, PyVenvConfiguration, VirtualEnvironment};
|
||||||
mod discovery;
|
mod discovery;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
use std::{io, path::PathBuf};
|
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::PythonRequest;
|
use crate::PythonRequest;
|
||||||
|
|
@ -14,7 +13,7 @@ pub static PYTHON_VERSIONS_FILENAME: &str = ".python-versions";
|
||||||
///
|
///
|
||||||
/// Prefers `.python-versions` then `.python-version`.
|
/// Prefers `.python-versions` then `.python-version`.
|
||||||
/// If only one Python version is desired, use [`request_from_version_files`] which prefers the `.python-version` file.
|
/// If only one Python version is desired, use [`request_from_version_files`] which prefers the `.python-version` file.
|
||||||
pub async fn requests_from_version_file() -> Result<Option<Vec<PythonRequest>>, io::Error> {
|
pub async fn requests_from_version_file() -> Result<Option<Vec<PythonRequest>>, std::io::Error> {
|
||||||
if let Some(versions) = read_versions_file().await? {
|
if let Some(versions) = read_versions_file().await? {
|
||||||
Ok(Some(
|
Ok(Some(
|
||||||
versions
|
versions
|
||||||
|
|
@ -33,49 +32,38 @@ pub async fn requests_from_version_file() -> Result<Option<Vec<PythonRequest>>,
|
||||||
///
|
///
|
||||||
/// Prefers `.python-version` then the first entry of `.python-versions`.
|
/// Prefers `.python-version` then the first entry of `.python-versions`.
|
||||||
/// If multiple Python versions are desired, use [`requests_from_version_files`] instead.
|
/// If multiple Python versions are desired, use [`requests_from_version_files`] instead.
|
||||||
pub async fn request_from_version_file() -> Result<Option<PythonRequest>, io::Error> {
|
pub async fn request_from_version_file() -> Result<Option<PythonRequest>, std::io::Error> {
|
||||||
if let Some(version) = read_version_file().await? {
|
if let Some(version) = read_version_file().await? {
|
||||||
Ok(Some(PythonRequest::parse(&version)))
|
Ok(Some(PythonRequest::parse(&version)))
|
||||||
} else if let Some(versions) = read_versions_file().await? {
|
} else if let Some(versions) = read_versions_file().await? {
|
||||||
Ok(versions
|
Ok(versions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.next()
|
.next()
|
||||||
.inspect(|_| debug!("Using the first version from `.python-versions`"))
|
.inspect(|_| debug!("Using the first version from `{PYTHON_VERSIONS_FILENAME}`"))
|
||||||
.map(|version| PythonRequest::parse(&version)))
|
.map(|version| PythonRequest::parse(&version)))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn versions_file_exists() -> Result<bool, io::Error> {
|
async fn read_versions_file() -> Result<Option<Vec<String>>, std::io::Error> {
|
||||||
PathBuf::from(PYTHON_VERSIONS_FILENAME).try_exists()
|
match fs::tokio::read_to_string(PYTHON_VERSIONS_FILENAME).await {
|
||||||
}
|
Ok(content) => {
|
||||||
|
debug!("Reading requests from `{PYTHON_VERSIONS_FILENAME}`");
|
||||||
async fn read_versions_file() -> Result<Option<Vec<String>>, io::Error> {
|
Ok(Some(content.lines().map(ToString::to_string).collect()))
|
||||||
if !versions_file_exists()? {
|
}
|
||||||
return Ok(None);
|
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
|
||||||
|
Err(err) => Err(err),
|
||||||
}
|
}
|
||||||
debug!("Reading requests from `{PYTHON_VERSIONS_FILENAME}`");
|
|
||||||
let lines: Vec<String> = fs::tokio::read_to_string(PYTHON_VERSIONS_FILENAME)
|
|
||||||
.await?
|
|
||||||
.lines()
|
|
||||||
.map(ToString::to_string)
|
|
||||||
.collect();
|
|
||||||
Ok(Some(lines))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn version_file_exists() -> Result<bool, io::Error> {
|
async fn read_version_file() -> Result<Option<String>, std::io::Error> {
|
||||||
PathBuf::from(PYTHON_VERSION_FILENAME).try_exists()
|
match fs::tokio::read_to_string(PYTHON_VERSION_FILENAME).await {
|
||||||
}
|
Ok(content) => {
|
||||||
|
debug!("Reading requests from `{PYTHON_VERSION_FILENAME}`");
|
||||||
async fn read_version_file() -> Result<Option<String>, io::Error> {
|
Ok(content.lines().next().map(ToString::to_string))
|
||||||
if !version_file_exists()? {
|
}
|
||||||
return Ok(None);
|
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
|
||||||
|
Err(err) => Err(err),
|
||||||
}
|
}
|
||||||
debug!("Reading requests from `{PYTHON_VERSION_FILENAME}`");
|
|
||||||
Ok(fs::tokio::read_to_string(PYTHON_VERSION_FILENAME)
|
|
||||||
.await?
|
|
||||||
.lines()
|
|
||||||
.next()
|
|
||||||
.map(ToString::to_string))
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue