mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
support refresh args (default: cache for 10 minutes)
This commit is contained in:
parent
15978e285b
commit
cc8c7218ba
4 changed files with 117 additions and 78 deletions
|
@ -637,6 +637,9 @@ pub struct UpgradeProjectArgs {
|
|||
)]
|
||||
pub allow: Vec<Maybe<usize>>,
|
||||
|
||||
#[command(flatten)]
|
||||
pub refresh: RefreshArgs,
|
||||
|
||||
/// The Python interpreter to use during resolution (overrides pyproject.toml).
|
||||
///
|
||||
/// A Python interpreter is required for building source distributions to determine package
|
||||
|
|
|
@ -1,10 +1,3 @@
|
|||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::Write;
|
||||
use std::io::ErrorKind;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::commands::ExitStatus;
|
||||
use crate::commands::pip::latest::LatestClient;
|
||||
use crate::printer::Printer;
|
||||
|
@ -15,9 +8,13 @@ use owo_colors::OwoColorize;
|
|||
use prettytable::format::FormatBuilder;
|
||||
use prettytable::row;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::Write;
|
||||
use std::io::ErrorKind;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use tokio::sync::Semaphore;
|
||||
use uv_cache::{Cache, Refresh};
|
||||
use uv_cache_info::Timestamp;
|
||||
use uv_cache::Cache;
|
||||
use uv_cli::{Maybe, UpgradeProjectArgs};
|
||||
use uv_client::{BaseClientBuilder, RegistryClientBuilder};
|
||||
use uv_configuration::Concurrency;
|
||||
|
@ -34,7 +31,10 @@ use walkdir::WalkDir;
|
|||
/// Upgrade all dependencies in the project requirements (pyproject.toml).
|
||||
///
|
||||
/// This doesn't read or modify uv.lock, only constraints like `<1.0` are bumped.
|
||||
pub(crate) async fn upgrade_project_dependencies(args: UpgradeProjectArgs) -> Result<ExitStatus> {
|
||||
pub(crate) async fn upgrade_project_dependencies(
|
||||
args: UpgradeProjectArgs,
|
||||
cache: Cache,
|
||||
) -> Result<ExitStatus> {
|
||||
let tables: Vec<_> = match args
|
||||
.types
|
||||
.iter()
|
||||
|
@ -65,19 +65,15 @@ pub(crate) async fn upgrade_project_dependencies(args: UpgradeProjectArgs) -> Re
|
|||
let printer = Printer::Default;
|
||||
let info = format!("{}{}", "info".cyan().bold(), ":".bold());
|
||||
|
||||
#[allow(deprecated)]
|
||||
let cache_dir = env::home_dir().unwrap().join(".cache/uv");
|
||||
let cache = Cache::from_settings(false, Some(cache_dir))?.init()?;
|
||||
let capabilities = IndexCapabilities::default();
|
||||
let client_builder = BaseClientBuilder::new();
|
||||
|
||||
// Initialize the registry client.
|
||||
let client = RegistryClientBuilder::try_from(client_builder)?
|
||||
.cache(cache.clone().with_refresh(Refresh::All(Timestamp::now())))
|
||||
.cache(cache)
|
||||
.index_locations(&IndexLocations::default())
|
||||
.build();
|
||||
let concurrency = Concurrency::default();
|
||||
let download_concurrency = Semaphore::new(concurrency.downloads);
|
||||
|
||||
let (mut item_written, mut all_found, mut all_bumped, mut all_skipped) =
|
||||
(false, 0, 0, VersionDigit::default());
|
||||
|
@ -86,37 +82,13 @@ pub(crate) async fn upgrade_project_dependencies(args: UpgradeProjectArgs) -> Re
|
|||
|
||||
for toml_dir in &tomls {
|
||||
let pyproject_toml = Path::new(toml_dir).join("pyproject.toml");
|
||||
let content = match fs_err::tokio::read_to_string(pyproject_toml.clone()).await {
|
||||
Ok(content) => content,
|
||||
Err(err) => {
|
||||
if err.kind() == ErrorKind::NotFound {
|
||||
warn_user!("No pyproject.toml found in current directory");
|
||||
return Ok(ExitStatus::Error);
|
||||
}
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
let mut toml = match PyProjectTomlMut::from_toml(&content, DependencyTarget::PyProjectToml)
|
||||
{
|
||||
Ok(toml) => toml,
|
||||
Err(err) => {
|
||||
warn_user!("Couldn't read pyproject.toml: {}", err);
|
||||
return Ok(ExitStatus::Error);
|
||||
}
|
||||
let mut toml = match read_pyproject_toml(&pyproject_toml).await {
|
||||
Ok(value) => value,
|
||||
Err(value) => return value,
|
||||
};
|
||||
|
||||
let python = args
|
||||
.python
|
||||
.clone()
|
||||
.and_then(Maybe::into_option)
|
||||
.or_else(|| {
|
||||
toml.get_requires_python()
|
||||
.map(std::string::ToString::to_string)
|
||||
});
|
||||
let version_specifiers = python.and_then(|s| VersionSpecifiers::from_str(&s).ok());
|
||||
let requires_python = version_specifiers
|
||||
.map(|v| RequiresPython::from_specifiers(&v))
|
||||
.unwrap_or_else(|| RequiresPython::greater_than_equal_version(&Version::new([4]))); // allow any by default
|
||||
let python = get_python(&args, &toml);
|
||||
let requires_python = create_requires_python(python);
|
||||
|
||||
// Initialize the client to fetch the latest version of each package.
|
||||
let client = LatestClient {
|
||||
|
@ -128,25 +100,6 @@ pub(crate) async fn upgrade_project_dependencies(args: UpgradeProjectArgs) -> Re
|
|||
requires_python: &requires_python,
|
||||
};
|
||||
|
||||
let find_latest = async |names: &FxHashSet<PackageName>| {
|
||||
let mut fetches = futures::stream::iter(names.iter())
|
||||
.map(async |name| {
|
||||
let latest = client
|
||||
.find_latest(name, None, &download_concurrency)
|
||||
.await?;
|
||||
Ok::<(&PackageName, Option<DistFilename>), uv_client::Error>((name, latest))
|
||||
})
|
||||
.buffer_unordered(concurrency.downloads);
|
||||
|
||||
let mut map = FxHashMap::default();
|
||||
while let Ok(Some((package, version))) = fetches.next().await.transpose() {
|
||||
if let Some(version) = version.as_ref() {
|
||||
map.insert(package.clone(), version.clone().into_version());
|
||||
}
|
||||
}
|
||||
map
|
||||
};
|
||||
|
||||
let relative = if toml_dir == "." {
|
||||
String::new()
|
||||
} else {
|
||||
|
@ -160,7 +113,7 @@ pub(crate) async fn upgrade_project_dependencies(args: UpgradeProjectArgs) -> Re
|
|||
.into_iter()
|
||||
.filter(|p| !all_latest_versions.contains_key(p))
|
||||
.collect();
|
||||
let latest_versions = find_latest(&query_versions).await;
|
||||
let latest_versions = find_latest(&client, &query_versions, concurrency.downloads).await;
|
||||
all_latest_versions.extend(latest_versions.clone());
|
||||
|
||||
let (found, upgrades) =
|
||||
|
@ -292,6 +245,72 @@ pub(crate) async fn upgrade_project_dependencies(args: UpgradeProjectArgs) -> Re
|
|||
Ok(ExitStatus::Success)
|
||||
}
|
||||
|
||||
fn create_requires_python(python: Option<String>) -> RequiresPython {
|
||||
let version_specifiers = python.and_then(|s| VersionSpecifiers::from_str(&s).ok());
|
||||
version_specifiers
|
||||
.map(|v| RequiresPython::from_specifiers(&v))
|
||||
.unwrap_or_else(|| RequiresPython::greater_than_equal_version(&Version::new([4]))) // allow any by default
|
||||
}
|
||||
|
||||
fn get_python(args: &UpgradeProjectArgs, toml: &PyProjectTomlMut) -> Option<String> {
|
||||
let python = args
|
||||
.python
|
||||
.clone()
|
||||
.and_then(Maybe::into_option)
|
||||
.or_else(|| {
|
||||
toml.get_requires_python()
|
||||
.map(std::string::ToString::to_string)
|
||||
});
|
||||
python
|
||||
}
|
||||
|
||||
async fn read_pyproject_toml(
|
||||
pyproject_toml: &Path,
|
||||
) -> Result<PyProjectTomlMut, Result<ExitStatus>> {
|
||||
let content = match fs_err::tokio::read_to_string(pyproject_toml.to_path_buf()).await {
|
||||
Ok(content) => content,
|
||||
Err(err) => {
|
||||
if err.kind() == ErrorKind::NotFound {
|
||||
warn_user!("No pyproject.toml found in current directory");
|
||||
return Err(Ok(ExitStatus::Error));
|
||||
}
|
||||
return Err(Err(err.into()));
|
||||
}
|
||||
};
|
||||
let toml = match PyProjectTomlMut::from_toml(&content, DependencyTarget::PyProjectToml) {
|
||||
Ok(toml) => toml,
|
||||
Err(err) => {
|
||||
warn_user!("Couldn't read pyproject.toml: {}", err);
|
||||
return Err(Ok(ExitStatus::Error));
|
||||
}
|
||||
};
|
||||
Ok(toml)
|
||||
}
|
||||
|
||||
async fn find_latest(
|
||||
client: &LatestClient<'_>,
|
||||
names: &FxHashSet<PackageName>,
|
||||
downloads: usize,
|
||||
) -> FxHashMap<PackageName, Version> {
|
||||
let download_concurrency = Semaphore::new(downloads);
|
||||
let mut fetches = futures::stream::iter(names.iter())
|
||||
.map(async |name| {
|
||||
let latest = client
|
||||
.find_latest(name, None, &download_concurrency)
|
||||
.await?;
|
||||
Ok::<(&PackageName, Option<DistFilename>), uv_client::Error>((name, latest))
|
||||
})
|
||||
.buffer_unordered(downloads);
|
||||
|
||||
let mut map = FxHashMap::default();
|
||||
while let Ok(Some((package, version))) = fetches.next().await.transpose() {
|
||||
if let Some(version) = version.as_ref() {
|
||||
map.insert(package.clone(), version.clone().into_version());
|
||||
}
|
||||
}
|
||||
map
|
||||
}
|
||||
|
||||
/// Recursively search for pyproject.toml files.
|
||||
fn search_pyproject_tomls(root: &Path) -> Result<Vec<String>, anyhow::Error> {
|
||||
let metadata = match fs_err::symlink_metadata(root) {
|
||||
|
|
|
@ -1,15 +1,3 @@
|
|||
use std::borrow::Cow;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::OsString;
|
||||
use std::fmt::Write;
|
||||
use std::io::stdout;
|
||||
#[cfg(feature = "self-update")]
|
||||
use std::ops::Bound;
|
||||
use std::path::Path;
|
||||
use std::process::ExitCode;
|
||||
use std::str::FromStr;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use anstream::eprintln;
|
||||
use anyhow::{Context, Result, bail};
|
||||
use clap::error::{ContextKind, ContextValue};
|
||||
|
@ -17,6 +5,19 @@ use clap::{CommandFactory, Parser};
|
|||
use futures::FutureExt;
|
||||
use owo_colors::OwoColorize;
|
||||
use settings::PipTreeSettings;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::OsString;
|
||||
use std::fmt::Write;
|
||||
use std::io::stdout;
|
||||
#[cfg(feature = "self-update")]
|
||||
use std::ops::Bound;
|
||||
use std::ops::Sub;
|
||||
use std::path::Path;
|
||||
use std::process::ExitCode;
|
||||
use std::str::FromStr;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use tokio::task::spawn_blocking;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
|
@ -2072,7 +2073,21 @@ async fn run_project(
|
|||
.await
|
||||
}
|
||||
ProjectCommand::Upgrade(args) => {
|
||||
Box::pin(commands::upgrade_project_dependencies(args)).await
|
||||
// --refresh -> now() -> uncached
|
||||
// --no-refresh -> cached (overrides --refresh)
|
||||
// otherwise: cache subsequent runs for 10 minutes
|
||||
let timestamp = Timestamp::from(SystemTime::now().sub(Duration::from_secs(60 * 10)));
|
||||
let refresh_package = args.refresh.refresh_package.clone();
|
||||
let _refresh = if args.refresh.refresh || args.refresh.no_refresh {
|
||||
Refresh::from_args(Some(!args.refresh.no_refresh), refresh_package)
|
||||
} else if refresh_package.is_empty() {
|
||||
Refresh::All(timestamp) // user didn't pass flags or package
|
||||
} else {
|
||||
Refresh::Packages(refresh_package, vec![], timestamp)
|
||||
};
|
||||
let cache = cache.init()?.with_refresh(_refresh);
|
||||
|
||||
Box::pin(commands::upgrade_project_dependencies(args, cache)).await
|
||||
}
|
||||
ProjectCommand::Tree(args) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
|
|
|
@ -998,7 +998,9 @@ metadata when there are not wheels.</p>
|
|||
<p>May also be set with the <code>UV_PYTHON</code> environment variable.</p></dd><dt id="uv-upgrade--quiet"><a href="#uv-upgrade--quiet"><code>--quiet</code></a>, <code>-q</code></dt><dd><p>Use quiet output.</p>
|
||||
<p>Repeating this option, e.g., <code>-qq</code>, will enable a silent mode in which uv will write no output to stdout.</p>
|
||||
</dd><dt id="uv-upgrade--recursive"><a href="#uv-upgrade--recursive"><code>--recursive</code></a></dt><dd><p>Search recursively for pyproject.toml files</p>
|
||||
<p>May also be set with the <code>UV_UPGRADE_RECURSIVE</code> environment variable.</p></dd><dt id="uv-upgrade--types"><a href="#uv-upgrade--types"><code>--types</code></a> <i>types</i></dt><dd><p>Only search specific tables in pyproject.toml: <code>prod,dev,optional,groups</code></p>
|
||||
<p>May also be set with the <code>UV_UPGRADE_RECURSIVE</code> environment variable.</p></dd><dt id="uv-upgrade--refresh"><a href="#uv-upgrade--refresh"><code>--refresh</code></a></dt><dd><p>Refresh all cached data</p>
|
||||
</dd><dt id="uv-upgrade--refresh-package"><a href="#uv-upgrade--refresh-package"><code>--refresh-package</code></a> <i>refresh-package</i></dt><dd><p>Refresh cached data for a specific package</p>
|
||||
</dd><dt id="uv-upgrade--types"><a href="#uv-upgrade--types"><code>--types</code></a> <i>types</i></dt><dd><p>Only search specific tables in pyproject.toml: <code>prod,dev,optional,groups</code></p>
|
||||
<p>May also be set with the <code>UV_UPGRADE_TYPES</code> environment variable.</p></dd><dt id="uv-upgrade--verbose"><a href="#uv-upgrade--verbose"><code>--verbose</code></a>, <code>-v</code></dt><dd><p>Use verbose output.</p>
|
||||
<p>You can configure fine-grained logging using the <code>RUST_LOG</code> environment variable. (<a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives">https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives</a>)</p>
|
||||
</dd></dl>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue