mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-03 13:14:41 +00:00
Make warnings user-facing (#628)
## Summary Now, `puffin_warnings::warn_once` and `puffin_warnings::warn` will go to `stderr`, as long as the user isn't running under `--quiet`. Previously, these went through `tracing`, and so were only visible when running under `--verbose`.
This commit is contained in:
parent
490fb55ac5
commit
a24eb57e93
16 changed files with 95 additions and 66 deletions
26
Cargo.lock
generated
26
Cargo.lock
generated
|
|
@ -2327,6 +2327,7 @@ dependencies = [
|
||||||
"puffin-normalize",
|
"puffin-normalize",
|
||||||
"puffin-resolver",
|
"puffin-resolver",
|
||||||
"puffin-traits",
|
"puffin-traits",
|
||||||
|
"puffin-warnings",
|
||||||
"puffin-workspace",
|
"puffin-workspace",
|
||||||
"pypi-types",
|
"pypi-types",
|
||||||
"pyproject-toml",
|
"pyproject-toml",
|
||||||
|
|
@ -2560,22 +2561,10 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "puffin-macros"
|
|
||||||
version = "0.0.1"
|
|
||||||
dependencies = [
|
|
||||||
"colored",
|
|
||||||
"once_cell",
|
|
||||||
"rustc-hash",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "puffin-normalize"
|
name = "puffin-normalize"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
|
||||||
"regex",
|
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -2610,9 +2599,9 @@ dependencies = [
|
||||||
"puffin-distribution",
|
"puffin-distribution",
|
||||||
"puffin-git",
|
"puffin-git",
|
||||||
"puffin-interpreter",
|
"puffin-interpreter",
|
||||||
"puffin-macros",
|
|
||||||
"puffin-normalize",
|
"puffin-normalize",
|
||||||
"puffin-traits",
|
"puffin-traits",
|
||||||
|
"puffin-warnings",
|
||||||
"pypi-types",
|
"pypi-types",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
|
|
@ -2641,6 +2630,15 @@ dependencies = [
|
||||||
"waitmap",
|
"waitmap",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "puffin-warnings"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"colored",
|
||||||
|
"once_cell",
|
||||||
|
"rustc-hash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "puffin-workspace"
|
name = "puffin-workspace"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
|
@ -2738,8 +2736,8 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pep440_rs 0.3.12",
|
"pep440_rs 0.3.12",
|
||||||
"pep508_rs",
|
"pep508_rs",
|
||||||
"puffin-macros",
|
|
||||||
"puffin-normalize",
|
"puffin-normalize",
|
||||||
|
"puffin-warnings",
|
||||||
"regex",
|
"regex",
|
||||||
"rfc2047-decoder",
|
"rfc2047-decoder",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
||||||
|
|
@ -84,10 +84,6 @@ Functionality for installing Python packages into a virtual environment.
|
||||||
|
|
||||||
Functionality for detecting and leveraging the current Python interpreter.
|
Functionality for detecting and leveraging the current Python interpreter.
|
||||||
|
|
||||||
## [puffin-macros](./puffin-macros)
|
|
||||||
|
|
||||||
Reusable procedural macros for Puffin.
|
|
||||||
|
|
||||||
## [puffin-normalize](./puffin-normalize)
|
## [puffin-normalize](./puffin-normalize)
|
||||||
|
|
||||||
Normalize package and extra names as per Python specifications.
|
Normalize package and extra names as per Python specifications.
|
||||||
|
|
@ -108,6 +104,10 @@ Shared traits for Puffin, to avoid circular dependencies.
|
||||||
|
|
||||||
General-purpose type definitions for types used in PyPI-compatible APIs.
|
General-purpose type definitions for types used in PyPI-compatible APIs.
|
||||||
|
|
||||||
|
## [puffin-warnings](./puffin-warnings)
|
||||||
|
|
||||||
|
User-facing warnings for Puffin.
|
||||||
|
|
||||||
## [requirements-txt](./requirements-txt)
|
## [requirements-txt](./requirements-txt)
|
||||||
|
|
||||||
Functionality for parsing `requirements.txt` files.
|
Functionality for parsing `requirements.txt` files.
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ name = "puffin"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
distribution-types = { path = "../distribution-types" }
|
||||||
gourgeist = { path = "../gourgeist" }
|
gourgeist = { path = "../gourgeist" }
|
||||||
install-wheel-rs = { path = "../install-wheel-rs", default-features = false }
|
install-wheel-rs = { path = "../install-wheel-rs", default-features = false }
|
||||||
pep440_rs = { path = "../pep440-rs" }
|
pep440_rs = { path = "../pep440-rs" }
|
||||||
|
|
@ -26,13 +27,13 @@ platform-tags = { path = "../platform-tags" }
|
||||||
puffin-cache = { path = "../puffin-cache", features = ["clap"] }
|
puffin-cache = { path = "../puffin-cache", features = ["clap"] }
|
||||||
puffin-client = { path = "../puffin-client" }
|
puffin-client = { path = "../puffin-client" }
|
||||||
puffin-dispatch = { path = "../puffin-dispatch" }
|
puffin-dispatch = { path = "../puffin-dispatch" }
|
||||||
distribution-types = { path = "../distribution-types" }
|
|
||||||
puffin-distribution = { path = "../puffin-distribution" }
|
puffin-distribution = { path = "../puffin-distribution" }
|
||||||
puffin-installer = { path = "../puffin-installer" }
|
puffin-installer = { path = "../puffin-installer" }
|
||||||
puffin-interpreter = { path = "../puffin-interpreter" }
|
puffin-interpreter = { path = "../puffin-interpreter" }
|
||||||
puffin-normalize = { path = "../puffin-normalize" }
|
puffin-normalize = { path = "../puffin-normalize" }
|
||||||
puffin-resolver = { path = "../puffin-resolver", features = ["clap"] }
|
puffin-resolver = { path = "../puffin-resolver", features = ["clap"] }
|
||||||
puffin-traits = { path = "../puffin-traits" }
|
puffin-traits = { path = "../puffin-traits" }
|
||||||
|
puffin-warnings = { path = "../puffin-warnings" }
|
||||||
puffin-workspace = { path = "../puffin-workspace" }
|
puffin-workspace = { path = "../puffin-workspace" }
|
||||||
pypi-types = { path = "../pypi-types" }
|
pypi-types = { path = "../pypi-types" }
|
||||||
requirements-txt = { path = "../requirements-txt" }
|
requirements-txt = { path = "../requirements-txt" }
|
||||||
|
|
|
||||||
|
|
@ -348,12 +348,14 @@ struct RemoveArgs {
|
||||||
async fn inner() -> Result<ExitStatus> {
|
async fn inner() -> Result<ExitStatus> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
// Configure the `tracing` crate, which controls internal logging.
|
||||||
logging::setup_logging(if cli.verbose {
|
logging::setup_logging(if cli.verbose {
|
||||||
logging::Level::Verbose
|
logging::Level::Verbose
|
||||||
} else {
|
} else {
|
||||||
logging::Level::Default
|
logging::Level::Default
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Configure the `Printer`, which controls user-facing output in the CLI.
|
||||||
let printer = if cli.quiet {
|
let printer = if cli.quiet {
|
||||||
printer::Printer::Quiet
|
printer::Printer::Quiet
|
||||||
} else if cli.verbose {
|
} else if cli.verbose {
|
||||||
|
|
@ -362,6 +364,11 @@ async fn inner() -> Result<ExitStatus> {
|
||||||
printer::Printer::Default
|
printer::Printer::Default
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Configure the `warn!` macros, which control user-facing warnings in the CLI.
|
||||||
|
if !cli.quiet {
|
||||||
|
puffin_warnings::enable();
|
||||||
|
}
|
||||||
|
|
||||||
let cache = Cache::try_from(cli.cache_args)?;
|
let cache = Cache::try_from(cli.cache_args)?;
|
||||||
|
|
||||||
match cli.command {
|
match cli.command {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ pub(crate) enum Printer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Printer {
|
impl Printer {
|
||||||
|
/// Return the [`ProgressDrawTarget`] for this printer.
|
||||||
pub(crate) fn target(self) -> ProgressDrawTarget {
|
pub(crate) fn target(self) -> ProgressDrawTarget {
|
||||||
match self {
|
match self {
|
||||||
Self::Default => ProgressDrawTarget::stderr(),
|
Self::Default => ProgressDrawTarget::stderr(),
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,11 @@ info:
|
||||||
- "--constraint"
|
- "--constraint"
|
||||||
- constraints.txt
|
- constraints.txt
|
||||||
- "--cache-dir"
|
- "--cache-dir"
|
||||||
- /tmp/.tmpZMtiDp
|
- /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpzGLCFa
|
||||||
|
- "--exclude-newer"
|
||||||
|
- "2023-11-18T12:00:00Z"
|
||||||
env:
|
env:
|
||||||
VIRTUAL_ENV: /tmp/.tmpYzlRGF/.venv
|
VIRTUAL_ENV: /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmp8uVkTx/.venv
|
||||||
---
|
---
|
||||||
success: true
|
success: true
|
||||||
exit_code: 0
|
exit_code: 0
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,11 @@ info:
|
||||||
- "--constraint"
|
- "--constraint"
|
||||||
- constraints.txt
|
- constraints.txt
|
||||||
- "--cache-dir"
|
- "--cache-dir"
|
||||||
- /tmp/.tmpqKe52b
|
- /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmp1JFjQT
|
||||||
|
- "--exclude-newer"
|
||||||
|
- "2023-11-18T12:00:00Z"
|
||||||
env:
|
env:
|
||||||
VIRTUAL_ENV: /tmp/.tmpUeuA4H/.venv
|
VIRTUAL_ENV: /var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/.tmpjXkDoX/.venv
|
||||||
---
|
---
|
||||||
success: true
|
success: true
|
||||||
exit_code: 0
|
exit_code: 0
|
||||||
|
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use rustc_hash::FxHashSet;
|
|
||||||
|
|
||||||
// macro hygiene: The user might not have direct dependencies on those crates
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub use colored;
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub use tracing;
|
|
||||||
|
|
||||||
pub static WARNINGS: Lazy<Mutex<FxHashSet<String>>> = Lazy::new(Mutex::default);
|
|
||||||
|
|
||||||
/// Warn a user once, with uniqueness determined by the content of the message.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! warn_once {
|
|
||||||
($($arg:tt)*) => {
|
|
||||||
use $crate::colored::Colorize;
|
|
||||||
use $crate::tracing::warn;
|
|
||||||
|
|
||||||
if let Ok(mut states) = $crate::WARNINGS.lock() {
|
|
||||||
let message = format!("{}", format_args!($($arg)*));
|
|
||||||
let formatted = message.bold();
|
|
||||||
if states.insert(message) {
|
|
||||||
warn!("{formatted}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -5,6 +5,4 @@ edition = "2021"
|
||||||
description = "Normalization for distribution, package and extra anmes"
|
description = "Normalization for distribution, package and extra anmes"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
once_cell = { workspace = true }
|
|
||||||
regex = { workspace = true }
|
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ distribution-types = { path = "../distribution-types" }
|
||||||
puffin-distribution = { path = "../puffin-distribution" }
|
puffin-distribution = { path = "../puffin-distribution" }
|
||||||
puffin-git = { path = "../puffin-git" }
|
puffin-git = { path = "../puffin-git" }
|
||||||
puffin-interpreter = { path = "../puffin-interpreter" }
|
puffin-interpreter = { path = "../puffin-interpreter" }
|
||||||
puffin-macros = { path = "../puffin-macros" }
|
puffin-warnings = { path = "../puffin-warnings" }
|
||||||
puffin-normalize = { path = "../puffin-normalize" }
|
puffin-normalize = { path = "../puffin-normalize" }
|
||||||
puffin-traits = { path = "../puffin-traits" }
|
puffin-traits = { path = "../puffin-traits" }
|
||||||
pypi-types = { path = "../pypi-types" }
|
pypi-types = { path = "../pypi-types" }
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ use tracing::warn;
|
||||||
|
|
||||||
use pep508_rs::{MarkerEnvironment, Requirement, VersionOrUrl};
|
use pep508_rs::{MarkerEnvironment, Requirement, VersionOrUrl};
|
||||||
use puffin_cache::CanonicalUrl;
|
use puffin_cache::CanonicalUrl;
|
||||||
use puffin_macros::warn_once;
|
|
||||||
use puffin_normalize::{ExtraName, PackageName};
|
use puffin_normalize::{ExtraName, PackageName};
|
||||||
|
|
||||||
use crate::pubgrub::specifier::PubGrubSpecifier;
|
use crate::pubgrub::specifier::PubGrubSpecifier;
|
||||||
|
|
@ -31,7 +30,7 @@ impl PubGrubDependencies {
|
||||||
for requirement in requirements {
|
for requirement in requirements {
|
||||||
// Avoid self-dependencies.
|
// Avoid self-dependencies.
|
||||||
if source.is_some_and(|source| source == &requirement.name) {
|
if source.is_some_and(|source| source == &requirement.name) {
|
||||||
warn_once!("{} has a dependency on itself", requirement.name);
|
warn!("{} has a dependency on itself", requirement.name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,7 +77,7 @@ impl PubGrubDependencies {
|
||||||
for constraint in constraints {
|
for constraint in constraints {
|
||||||
// Avoid self-dependencies.
|
// Avoid self-dependencies.
|
||||||
if source.is_some_and(|source| source == &constraint.name) {
|
if source.is_some_and(|source| source == &constraint.name) {
|
||||||
warn_once!("{} has a dependency on itself", constraint.name);
|
warn!("{} has a dependency on itself", constraint.name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ use pep508_rs::MarkerEnvironment;
|
||||||
use platform_tags::{TagPriority, Tags};
|
use platform_tags::{TagPriority, Tags};
|
||||||
use puffin_client::SimpleMetadata;
|
use puffin_client::SimpleMetadata;
|
||||||
use puffin_interpreter::Interpreter;
|
use puffin_interpreter::Interpreter;
|
||||||
use puffin_macros::warn_once;
|
|
||||||
use puffin_normalize::PackageName;
|
use puffin_normalize::PackageName;
|
||||||
|
use puffin_warnings::warn_once;
|
||||||
use pypi_types::Yanked;
|
use pypi_types::Yanked;
|
||||||
|
|
||||||
use crate::file::{DistFile, SdistFile, WheelFile};
|
use crate::file::{DistFile, SdistFile, WheelFile};
|
||||||
|
|
@ -64,7 +64,7 @@ impl VersionMap {
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
warn_once!(
|
warn_once!(
|
||||||
"{} is missing an upload date, but user provided {}",
|
"{} is missing an upload date, but user provided: {}",
|
||||||
file.filename,
|
file.filename,
|
||||||
exclude_newer,
|
exclude_newer,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "puffin-macros"
|
name = "puffin-warnings"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
@ -16,4 +16,3 @@ workspace = true
|
||||||
colored = { workspace = true }
|
colored = { workspace = true }
|
||||||
once_cell = { workspace = true }
|
once_cell = { workspace = true }
|
||||||
rustc-hash = { workspace = true }
|
rustc-hash = { workspace = true }
|
||||||
tracing = { workspace = true }
|
|
||||||
51
crates/puffin-warnings/src/lib.rs
Normal file
51
crates/puffin-warnings/src/lib.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
// macro hygiene: The user might not have direct dependencies on those crates
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use colored;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
|
/// Whether user-facing warnings are enabled.
|
||||||
|
pub static ENABLED: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
/// Enable user-facing warnings.
|
||||||
|
pub fn enable() {
|
||||||
|
ENABLED.store(true, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Warn a user, if warnings are enabled.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! warn {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
use $crate::colored::Colorize;
|
||||||
|
|
||||||
|
if $crate::ENABLED.load(std::sync::atomic::Ordering::SeqCst) {
|
||||||
|
let message = format!("{}", format_args!($($arg)*));
|
||||||
|
let formatted = message.bold();
|
||||||
|
eprintln!("{}{} {formatted}", "warning".yellow().bold(), ":".bold());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static WARNINGS: Lazy<Mutex<FxHashSet<String>>> = Lazy::new(Mutex::default);
|
||||||
|
|
||||||
|
/// Warn a user once, if warnings are enabled, with uniqueness determined by the content of the
|
||||||
|
/// message.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! warn_once {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
use $crate::colored::Colorize;
|
||||||
|
|
||||||
|
if $crate::ENABLED.load(std::sync::atomic::Ordering::SeqCst) {
|
||||||
|
if let Ok(mut states) = $crate::WARNINGS.lock() {
|
||||||
|
let message = format!("{}", format_args!($($arg)*));
|
||||||
|
let formatted = message.bold();
|
||||||
|
if states.insert(message) {
|
||||||
|
eprintln!("{}{} {formatted}", "warning".yellow().bold(), ":".bold());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -16,7 +16,7 @@ workspace = true
|
||||||
pep440_rs = { path = "../pep440-rs", features = ["serde"] }
|
pep440_rs = { path = "../pep440-rs", features = ["serde"] }
|
||||||
pep508_rs = { path = "../pep508-rs", features = ["serde"] }
|
pep508_rs = { path = "../pep508-rs", features = ["serde"] }
|
||||||
puffin-normalize = { path = "../puffin-normalize" }
|
puffin-normalize = { path = "../puffin-normalize" }
|
||||||
puffin-macros = { path = "../puffin-macros" }
|
puffin-warnings = { path = "../puffin-warnings" }
|
||||||
|
|
||||||
chrono = { workspace = true, features = ["serde"] }
|
chrono = { workspace = true, features = ["serde"] }
|
||||||
mailparse = { workspace = true }
|
mailparse = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use serde::{de, Deserialize, Deserializer, Serialize};
|
||||||
|
|
||||||
use pep440_rs::{Pep440Error, VersionSpecifiers};
|
use pep440_rs::{Pep440Error, VersionSpecifiers};
|
||||||
use pep508_rs::{Pep508Error, Requirement};
|
use pep508_rs::{Pep508Error, Requirement};
|
||||||
use puffin_macros::warn_once;
|
use puffin_warnings::warn_once;
|
||||||
|
|
||||||
/// Ex) `>=7.2.0<8.0.0`
|
/// Ex) `>=7.2.0<8.0.0`
|
||||||
static MISSING_COMMA: Lazy<Regex> = Lazy::new(|| Regex::new(r"(\d)([<>=~^!])").unwrap());
|
static MISSING_COMMA: Lazy<Regex> = Lazy::new(|| Regex::new(r"(\d)([<>=~^!])").unwrap());
|
||||||
|
|
@ -63,8 +63,8 @@ fn parse_with_fixups<Err, T: FromStr<Err = Err>>(input: &str, type_name: &str) -
|
||||||
|
|
||||||
if let Ok(requirement) = T::from_str(&patched_input) {
|
if let Ok(requirement) = T::from_str(&patched_input) {
|
||||||
warn_once!(
|
warn_once!(
|
||||||
"{} to fix invalid {type_name} (before: `{input}`; after: `{patched_input}`)",
|
"Fixing invalid {type_name} by {} (before: `{input}`; after: `{patched_input}`)",
|
||||||
messages.join(" and ")
|
messages.join(", ")
|
||||||
);
|
);
|
||||||
return Ok(requirement);
|
return Ok(requirement);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue