mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-31 07:47:27 +00:00
Refactor .python-version
discovery (#6359)
In preparation for more comprehensive discovery
This commit is contained in:
parent
1377c6807d
commit
2fbe12ee1b
9 changed files with 186 additions and 133 deletions
|
@ -14,8 +14,7 @@ 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, write_version_file,
|
PythonVersionFile, PYTHON_VERSIONS_FILENAME, PYTHON_VERSION_FILENAME,
|
||||||
PYTHON_VERSIONS_FILENAME, PYTHON_VERSION_FILENAME,
|
|
||||||
};
|
};
|
||||||
pub use crate::virtualenv::{Error as VirtualEnvError, PyVenvConfiguration, VirtualEnvironment};
|
pub use crate::virtualenv::{Error as VirtualEnvError, PyVenvConfiguration, VirtualEnvironment};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
|
use itertools::Itertools;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::PythonRequest;
|
use crate::PythonRequest;
|
||||||
|
@ -11,63 +12,51 @@ pub static PYTHON_VERSION_FILENAME: &str = ".python-version";
|
||||||
/// The file name for multiple Python version declarations.
|
/// The file name for multiple Python version declarations.
|
||||||
pub static PYTHON_VERSIONS_FILENAME: &str = ".python-versions";
|
pub static PYTHON_VERSIONS_FILENAME: &str = ".python-versions";
|
||||||
|
|
||||||
/// Read [`PythonRequest`]s from a version file, if present.
|
/// A `.python-version` or `.python-versions` file.
|
||||||
///
|
#[derive(Debug, Clone)]
|
||||||
/// Prefers `.python-versions` then `.python-version`.
|
pub struct PythonVersionFile {
|
||||||
/// If only one Python version is desired, use [`request_from_version_files`] which prefers the `.python-version` file.
|
/// The path to the version file.
|
||||||
pub async fn requests_from_version_file(
|
path: PathBuf,
|
||||||
directory: &Path,
|
/// The Python version requests declared in the file.
|
||||||
) -> Result<Option<Vec<PythonRequest>>, std::io::Error> {
|
versions: Vec<PythonRequest>,
|
||||||
if let Some(versions) = read_versions_file(directory).await? {
|
}
|
||||||
Ok(Some(
|
|
||||||
versions
|
impl PythonVersionFile {
|
||||||
.into_iter()
|
/// Find a Python version file in the given directory.
|
||||||
.map(|version| PythonRequest::parse(&version))
|
pub async fn discover(
|
||||||
.collect(),
|
working_directory: impl AsRef<Path>,
|
||||||
))
|
no_config: bool,
|
||||||
} else if let Some(version) = read_version_file(directory).await? {
|
) -> Result<Option<Self>, std::io::Error> {
|
||||||
Ok(Some(vec![PythonRequest::parse(&version)]))
|
let versions_path = working_directory.as_ref().join(PYTHON_VERSIONS_FILENAME);
|
||||||
} else {
|
let version_path = working_directory.as_ref().join(PYTHON_VERSION_FILENAME);
|
||||||
|
|
||||||
|
if no_config {
|
||||||
|
if version_path.exists() {
|
||||||
|
debug!("Ignoring `.python-version` file due to `--no-config`");
|
||||||
|
} else if versions_path.exists() {
|
||||||
|
debug!("Ignoring `.python-versions` file due to `--no-config`");
|
||||||
|
};
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(result) = Self::try_from_path(version_path).await? {
|
||||||
|
return Ok(Some(result));
|
||||||
|
};
|
||||||
|
if let Some(result) = Self::try_from_path(versions_path).await? {
|
||||||
|
return Ok(Some(result));
|
||||||
|
};
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a [`PythonRequest`] from a version file, if present.
|
/// Try to read a Python version file at the given path.
|
||||||
///
|
///
|
||||||
/// Find the version file inside directory, or the current directory
|
/// If the file does not exist, `Ok(None)` is returned.
|
||||||
/// if None.
|
pub async fn try_from_path(path: PathBuf) -> Result<Option<Self>, std::io::Error> {
|
||||||
///
|
match fs::tokio::read_to_string(&path).await {
|
||||||
/// Prefers `.python-version` then the first entry of `.python-versions`.
|
Ok(content) => {
|
||||||
/// If multiple Python versions are desired, use [`requests_from_version_files`] instead.
|
debug!("Reading requests from `{}`", path.display());
|
||||||
pub async fn request_from_version_file(
|
let versions = content
|
||||||
directory: &Path,
|
|
||||||
) -> Result<Option<PythonRequest>, std::io::Error> {
|
|
||||||
if let Some(version) = read_version_file(directory).await? {
|
|
||||||
Ok(Some(PythonRequest::parse(&version)))
|
|
||||||
} else if let Some(versions) = read_versions_file(directory).await? {
|
|
||||||
Ok(versions
|
|
||||||
.into_iter()
|
|
||||||
.next()
|
|
||||||
.inspect(|_| debug!("Using the first version from `{PYTHON_VERSIONS_FILENAME}`"))
|
|
||||||
.map(|version| PythonRequest::parse(&version)))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a version to a .`python-version` file.
|
|
||||||
pub async fn write_version_file(version: &str) -> Result<(), std::io::Error> {
|
|
||||||
debug!("Writing Python version `{version}` to `{PYTHON_VERSION_FILENAME}`");
|
|
||||||
fs::tokio::write(PYTHON_VERSION_FILENAME, format!("{version}\n")).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn read_versions_file(directory: &Path) -> Result<Option<Vec<String>>, std::io::Error> {
|
|
||||||
let path = directory.join(PYTHON_VERSIONS_FILENAME);
|
|
||||||
match fs::tokio::read_to_string(&path).await {
|
|
||||||
Ok(content) => {
|
|
||||||
debug!("Reading requests from `{}`", path.display());
|
|
||||||
Ok(Some(
|
|
||||||
content
|
|
||||||
.lines()
|
.lines()
|
||||||
.filter(|line| {
|
.filter(|line| {
|
||||||
// Skip comments and empty lines.
|
// Skip comments and empty lines.
|
||||||
|
@ -75,29 +64,84 @@ async fn read_versions_file(directory: &Path) -> Result<Option<Vec<String>>, std
|
||||||
!(trimmed.is_empty() || trimmed.starts_with('#'))
|
!(trimmed.is_empty() || trimmed.starts_with('#'))
|
||||||
})
|
})
|
||||||
.map(ToString::to_string)
|
.map(ToString::to_string)
|
||||||
.collect(),
|
.map(|version| PythonRequest::parse(&version))
|
||||||
))
|
.collect();
|
||||||
|
Ok(Some(Self { path, versions }))
|
||||||
|
}
|
||||||
|
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
|
||||||
|
Err(err) => Err(err),
|
||||||
}
|
}
|
||||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
|
|
||||||
Err(err) => Err(err),
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async fn read_version_file(directory: &Path) -> Result<Option<String>, std::io::Error> {
|
/// Read a Python version file at the given path.
|
||||||
let path = directory.join(PYTHON_VERSION_FILENAME);
|
///
|
||||||
match fs::tokio::read_to_string(&path).await {
|
/// If the file does not exist, an error is returned.
|
||||||
Ok(content) => {
|
pub async fn from_path(path: PathBuf) -> Result<Self, std::io::Error> {
|
||||||
debug!("Reading requests from `{}`", path.display());
|
let Some(result) = Self::try_from_path(path).await? else {
|
||||||
Ok(content
|
return Err(std::io::Error::new(
|
||||||
.lines()
|
std::io::ErrorKind::NotFound,
|
||||||
.find(|line| {
|
"Version file not found".to_string(),
|
||||||
// Skip comments and empty lines.
|
));
|
||||||
let trimmed = line.trim();
|
};
|
||||||
!(trimmed.is_empty() || trimmed.starts_with('#'))
|
Ok(result)
|
||||||
})
|
}
|
||||||
.map(ToString::to_string))
|
|
||||||
|
/// Create a new representation of a version file at the given path.
|
||||||
|
///
|
||||||
|
/// The file will not any versions; see [`PythonVersionFile::with_versions`].
|
||||||
|
/// The file will not be created; see [`PythonVersionFile::write`].
|
||||||
|
pub fn new(path: PathBuf) -> Self {
|
||||||
|
Self {
|
||||||
|
path,
|
||||||
|
versions: vec![],
|
||||||
}
|
}
|
||||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
|
}
|
||||||
Err(err) => Err(err),
|
|
||||||
|
/// Return the first version declared in the file, if any.
|
||||||
|
pub fn version(&self) -> Option<&PythonRequest> {
|
||||||
|
self.versions.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate of all versions declared in the file.
|
||||||
|
pub fn versions(&self) -> impl Iterator<Item = &PythonRequest> {
|
||||||
|
self.versions.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cast to a list of all versions declared in the file.
|
||||||
|
pub fn into_versions(self) -> Vec<PythonRequest> {
|
||||||
|
self.versions
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cast to the first version declared in the file, if any.
|
||||||
|
pub fn into_version(self) -> Option<PythonRequest> {
|
||||||
|
self.versions.into_iter().next()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the path to the version file.
|
||||||
|
pub fn path(&self) -> &Path {
|
||||||
|
&self.path
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the versions for the file.
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_versions(self, versions: Vec<PythonRequest>) -> Self {
|
||||||
|
Self {
|
||||||
|
path: self.path,
|
||||||
|
versions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the version file on the file system.
|
||||||
|
pub async fn write(&self) -> Result<(), std::io::Error> {
|
||||||
|
debug!("Writing Python versions to `{}`", self.path.display());
|
||||||
|
fs::tokio::write(
|
||||||
|
&self.path,
|
||||||
|
self.versions
|
||||||
|
.iter()
|
||||||
|
.map(PythonRequest::to_canonical_string)
|
||||||
|
.join("\n")
|
||||||
|
.as_bytes(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,8 @@ use uv_fs::{Simplified, CWD};
|
||||||
use uv_git::GIT_STORE;
|
use uv_git::GIT_STORE;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_python::{
|
use uv_python::{
|
||||||
request_from_version_file, EnvironmentPreference, Interpreter, PythonDownloads,
|
EnvironmentPreference, Interpreter, PythonDownloads, PythonEnvironment, PythonInstallation,
|
||||||
PythonEnvironment, PythonInstallation, PythonPreference, PythonRequest, VersionRequest,
|
PythonPreference, PythonRequest, PythonVersionFile, VersionRequest,
|
||||||
};
|
};
|
||||||
use uv_requirements::{NamedRequirementsResolver, RequirementsSource, RequirementsSpecification};
|
use uv_requirements::{NamedRequirementsResolver, RequirementsSource, RequirementsSpecification};
|
||||||
use uv_resolver::{FlatIndex, RequiresPython};
|
use uv_resolver::{FlatIndex, RequiresPython};
|
||||||
|
@ -125,7 +125,10 @@ pub(crate) async fn add(
|
||||||
let python_request = if let Some(request) = python.as_deref() {
|
let python_request = if let Some(request) = python.as_deref() {
|
||||||
// (1) Explicit request from user
|
// (1) Explicit request from user
|
||||||
PythonRequest::parse(request)
|
PythonRequest::parse(request)
|
||||||
} else if let Some(request) = request_from_version_file(&CWD).await? {
|
} else if let Some(request) = PythonVersionFile::discover(&*CWD, false)
|
||||||
|
.await?
|
||||||
|
.and_then(PythonVersionFile::into_version)
|
||||||
|
{
|
||||||
// (2) Request from `.python-version`
|
// (2) Request from `.python-version`
|
||||||
request
|
request
|
||||||
} else {
|
} else {
|
||||||
|
@ -153,7 +156,10 @@ pub(crate) async fn add(
|
||||||
let python_request = if let Some(request) = python.as_deref() {
|
let python_request = if let Some(request) = python.as_deref() {
|
||||||
// (1) Explicit request from user
|
// (1) Explicit request from user
|
||||||
Some(PythonRequest::parse(request))
|
Some(PythonRequest::parse(request))
|
||||||
} else if let Some(request) = request_from_version_file(&CWD).await? {
|
} else if let Some(request) = PythonVersionFile::discover(&*CWD, false)
|
||||||
|
.await?
|
||||||
|
.and_then(PythonVersionFile::into_version)
|
||||||
|
{
|
||||||
// (2) Request from `.python-version`
|
// (2) Request from `.python-version`
|
||||||
Some(request)
|
Some(request)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -18,8 +18,8 @@ use uv_fs::Simplified;
|
||||||
use uv_installer::{SatisfiesResult, SitePackages};
|
use uv_installer::{SatisfiesResult, SitePackages};
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_python::{
|
use uv_python::{
|
||||||
request_from_version_file, EnvironmentPreference, Interpreter, PythonDownloads,
|
EnvironmentPreference, Interpreter, PythonDownloads, PythonEnvironment, PythonInstallation,
|
||||||
PythonEnvironment, PythonInstallation, PythonPreference, PythonRequest, VersionRequest,
|
PythonPreference, PythonRequest, PythonVersionFile, VersionRequest,
|
||||||
};
|
};
|
||||||
use uv_requirements::{NamedRequirementsResolver, RequirementsSpecification};
|
use uv_requirements::{NamedRequirementsResolver, RequirementsSpecification};
|
||||||
use uv_resolver::{
|
use uv_resolver::{
|
||||||
|
@ -177,7 +177,10 @@ impl WorkspacePython {
|
||||||
let python_request = if let Some(request) = python_request {
|
let python_request = if let Some(request) = python_request {
|
||||||
Some(request)
|
Some(request)
|
||||||
// (2) Request from `.python-version`
|
// (2) Request from `.python-version`
|
||||||
} else if let Some(request) = request_from_version_file(workspace.install_path()).await? {
|
} else if let Some(request) = PythonVersionFile::discover(workspace.install_path(), false)
|
||||||
|
.await?
|
||||||
|
.and_then(PythonVersionFile::into_version)
|
||||||
|
{
|
||||||
Some(request)
|
Some(request)
|
||||||
// (3) `Requires-Python` in `pyproject.toml`
|
// (3) `Requires-Python` in `pyproject.toml`
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -20,8 +20,8 @@ use uv_fs::{PythonExt, Simplified, CWD};
|
||||||
use uv_installer::{SatisfiesResult, SitePackages};
|
use uv_installer::{SatisfiesResult, SitePackages};
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_python::{
|
use uv_python::{
|
||||||
request_from_version_file, EnvironmentPreference, Interpreter, PythonDownloads,
|
EnvironmentPreference, Interpreter, PythonDownloads, PythonEnvironment, PythonInstallation,
|
||||||
PythonEnvironment, PythonInstallation, PythonPreference, PythonRequest, VersionRequest,
|
PythonPreference, PythonRequest, PythonVersionFile, VersionRequest,
|
||||||
};
|
};
|
||||||
use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
||||||
use uv_scripts::{Pep723Error, Pep723Script};
|
use uv_scripts::{Pep723Error, Pep723Script};
|
||||||
|
@ -109,7 +109,10 @@ pub(crate) async fn run(
|
||||||
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))
|
||||||
// (2) Request from `.python-version`
|
// (2) Request from `.python-version`
|
||||||
} else if let Some(request) = request_from_version_file(&CWD).await? {
|
} else if let Some(request) = PythonVersionFile::discover(&*CWD, false)
|
||||||
|
.await?
|
||||||
|
.and_then(PythonVersionFile::into_version)
|
||||||
|
{
|
||||||
Some(request)
|
Some(request)
|
||||||
// (3) `Requires-Python` in `pyproject.toml`
|
// (3) `Requires-Python` in `pyproject.toml`
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
|
@ -8,16 +7,12 @@ use futures::stream::FuturesUnordered;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
use tracing::debug;
|
|
||||||
|
|
||||||
use uv_client::Connectivity;
|
use uv_client::Connectivity;
|
||||||
use uv_fs::CWD;
|
use uv_fs::CWD;
|
||||||
use uv_python::downloads::{DownloadResult, ManagedPythonDownload, PythonDownloadRequest};
|
use uv_python::downloads::{DownloadResult, ManagedPythonDownload, PythonDownloadRequest};
|
||||||
use uv_python::managed::{ManagedPythonInstallation, ManagedPythonInstallations};
|
use uv_python::managed::{ManagedPythonInstallation, ManagedPythonInstallations};
|
||||||
use uv_python::{
|
use uv_python::{PythonDownloads, PythonRequest, PythonVersionFile};
|
||||||
requests_from_version_file, PythonDownloads, PythonRequest, PYTHON_VERSIONS_FILENAME,
|
|
||||||
PYTHON_VERSION_FILENAME,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::commands::python::{ChangeEvent, ChangeEventKind};
|
use crate::commands::python::{ChangeEvent, ChangeEventKind};
|
||||||
use crate::commands::reporters::PythonDownloadReporter;
|
use crate::commands::reporters::PythonDownloadReporter;
|
||||||
|
@ -43,18 +38,10 @@ pub(crate) async fn install(
|
||||||
|
|
||||||
let targets = targets.into_iter().collect::<BTreeSet<_>>();
|
let targets = targets.into_iter().collect::<BTreeSet<_>>();
|
||||||
let requests: Vec<_> = if targets.is_empty() {
|
let requests: Vec<_> = if targets.is_empty() {
|
||||||
// Read from the version file, unless `--no-config` was requested
|
PythonVersionFile::discover(&*CWD, no_config)
|
||||||
let version_file_requests = if no_config {
|
.await?
|
||||||
if PathBuf::from(PYTHON_VERSION_FILENAME).exists() {
|
.map(uv_python::PythonVersionFile::into_versions)
|
||||||
debug!("Ignoring `.python-version` file due to `--no-config`");
|
.unwrap_or_else(|| vec![PythonRequest::Any])
|
||||||
} else if PathBuf::from(PYTHON_VERSIONS_FILENAME).exists() {
|
|
||||||
debug!("Ignoring `.python-versions` file due to `--no-config`");
|
|
||||||
}
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
requests_from_version_file(&CWD).await?
|
|
||||||
};
|
|
||||||
version_file_requests.unwrap_or_else(|| vec![PythonRequest::Any])
|
|
||||||
} else {
|
} else {
|
||||||
targets
|
targets
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -8,8 +8,7 @@ use tracing::debug;
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_fs::{Simplified, CWD};
|
use uv_fs::{Simplified, CWD};
|
||||||
use uv_python::{
|
use uv_python::{
|
||||||
request_from_version_file, requests_from_version_file, write_version_file,
|
EnvironmentPreference, PythonInstallation, PythonPreference, PythonRequest, PythonVersionFile,
|
||||||
EnvironmentPreference, PythonInstallation, PythonPreference, PythonRequest,
|
|
||||||
PYTHON_VERSION_FILENAME,
|
PYTHON_VERSION_FILENAME,
|
||||||
};
|
};
|
||||||
use uv_warnings::warn_user_once;
|
use uv_warnings::warn_user_once;
|
||||||
|
@ -39,14 +38,16 @@ pub(crate) async fn pin(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let version_file = PythonVersionFile::discover(&*CWD, false).await;
|
||||||
|
|
||||||
let Some(request) = request else {
|
let Some(request) = request else {
|
||||||
// Display the current pinned Python version
|
// Display the current pinned Python version
|
||||||
if let Some(pins) = requests_from_version_file(&CWD).await? {
|
if let Some(file) = version_file? {
|
||||||
for pin in pins {
|
for pin in file.versions() {
|
||||||
writeln!(printer.stdout(), "{}", pin.to_canonical_string())?;
|
writeln!(printer.stdout(), "{}", pin.to_canonical_string())?;
|
||||||
if let Some(virtual_project) = &virtual_project {
|
if let Some(virtual_project) = &virtual_project {
|
||||||
warn_if_existing_pin_incompatible_with_project(
|
warn_if_existing_pin_incompatible_with_project(
|
||||||
&pin,
|
pin,
|
||||||
virtual_project,
|
virtual_project,
|
||||||
python_preference,
|
python_preference,
|
||||||
cache,
|
cache,
|
||||||
|
@ -106,38 +107,46 @@ pub(crate) async fn pin(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = if resolved {
|
let request = if resolved {
|
||||||
// SAFETY: We exit early if Python is not found and resolved is `true`
|
// SAFETY: We exit early if Python is not found and resolved is `true`
|
||||||
python
|
// TODO(zanieb): Maybe avoid reparsing here?
|
||||||
.unwrap()
|
PythonRequest::parse(
|
||||||
.interpreter()
|
&python
|
||||||
.sys_executable()
|
.unwrap()
|
||||||
.user_display()
|
.interpreter()
|
||||||
.to_string()
|
.sys_executable()
|
||||||
|
.user_display()
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
request.to_canonical_string()
|
request
|
||||||
};
|
};
|
||||||
|
|
||||||
let existing = request_from_version_file(&CWD).await.ok().flatten();
|
let existing = version_file.ok().flatten();
|
||||||
write_version_file(&output).await?;
|
// TODO(zanieb): Allow updating the discovered version file with an `--update` flag.
|
||||||
|
let new =
|
||||||
|
PythonVersionFile::new(CWD.join(PYTHON_VERSION_FILENAME)).with_versions(vec![request]);
|
||||||
|
|
||||||
|
new.write().await?;
|
||||||
|
|
||||||
if let Some(existing) = existing
|
if let Some(existing) = existing
|
||||||
.map(|existing| existing.to_canonical_string())
|
.as_ref()
|
||||||
.filter(|existing| existing != &output)
|
.and_then(PythonVersionFile::version)
|
||||||
|
.filter(|version| *version != new.version().unwrap())
|
||||||
{
|
{
|
||||||
writeln!(
|
writeln!(
|
||||||
printer.stdout(),
|
printer.stdout(),
|
||||||
"Updated `{}` from `{}` -> `{}`",
|
"Updated `{}` from `{}` -> `{}`",
|
||||||
PYTHON_VERSION_FILENAME.cyan(),
|
new.path().user_display().cyan(),
|
||||||
existing.green(),
|
existing.to_canonical_string().green(),
|
||||||
output.green()
|
new.version().unwrap().to_canonical_string().green()
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
writeln!(
|
writeln!(
|
||||||
printer.stdout(),
|
printer.stdout(),
|
||||||
"Pinned `{}` to `{}`",
|
"Pinned `{}` to `{}`",
|
||||||
PYTHON_VERSION_FILENAME.cyan(),
|
new.path().user_display().cyan(),
|
||||||
output.green()
|
new.version().unwrap().to_canonical_string().green()
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,8 @@ use uv_configuration::{
|
||||||
use uv_dispatch::BuildDispatch;
|
use uv_dispatch::BuildDispatch;
|
||||||
use uv_fs::{Simplified, CWD};
|
use uv_fs::{Simplified, CWD};
|
||||||
use uv_python::{
|
use uv_python::{
|
||||||
request_from_version_file, EnvironmentPreference, PythonDownloads, PythonInstallation,
|
EnvironmentPreference, PythonDownloads, PythonInstallation, PythonPreference, PythonRequest,
|
||||||
PythonPreference, PythonRequest, VersionRequest,
|
PythonVersionFile, VersionRequest,
|
||||||
};
|
};
|
||||||
use uv_resolver::{ExcludeNewer, FlatIndex, RequiresPython};
|
use uv_resolver::{ExcludeNewer, FlatIndex, RequiresPython};
|
||||||
use uv_shell::Shell;
|
use uv_shell::Shell;
|
||||||
|
@ -145,10 +145,11 @@ async fn venv_impl(
|
||||||
|
|
||||||
// (2) Request from `.python-version`
|
// (2) Request from `.python-version`
|
||||||
if interpreter_request.is_none() {
|
if interpreter_request.is_none() {
|
||||||
interpreter_request =
|
// TODO(zanieb): Support `--no-config` here
|
||||||
request_from_version_file(&std::env::current_dir().into_diagnostic()?)
|
interpreter_request = PythonVersionFile::discover(&*CWD, false)
|
||||||
.await
|
.await
|
||||||
.into_diagnostic()?;
|
.into_diagnostic()?
|
||||||
|
.and_then(PythonVersionFile::into_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
// (3) `Requires-Python` in `pyproject.toml`
|
// (3) `Requires-Python` in `pyproject.toml`
|
||||||
|
|
|
@ -649,6 +649,7 @@ fn python_pin_with_comments() -> Result<()> {
|
||||||
exit_code: 0
|
exit_code: 0
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
3.12
|
3.12
|
||||||
|
3.10
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
"###);
|
"###);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue