Add .stdout() and .stderr() outputs to Printer (#2227)

## Summary

This adds a `.stdout()` stream to `Printer`, so that it automatically
respects `--quiet`.

Motivated by
https://github.com/astral-sh/uv/pull/2115/files#r1513753101.
This commit is contained in:
Charlie Marsh
2024-03-05 19:22:00 -08:00
committed by GitHub
parent 395be442fc
commit 511e32e406
12 changed files with 176 additions and 128 deletions
+25 -17
View File
@@ -14,11 +14,11 @@ use crate::printer::Printer;
pub(crate) fn cache_clean(
packages: &[PackageName],
cache: &Cache,
mut printer: Printer,
printer: Printer,
) -> Result<ExitStatus> {
if !cache.root().exists() {
writeln!(
printer,
printer.stderr(),
"No cache found at: {}",
cache.root().simplified_display().cyan()
)?;
@@ -27,7 +27,7 @@ pub(crate) fn cache_clean(
if packages.is_empty() {
writeln!(
printer,
printer.stderr(),
"Clearing cache at: {}",
cache.root().simplified_display().cyan()
)?;
@@ -42,19 +42,19 @@ pub(crate) fn cache_clean(
// Write a summary of the number of files and directories removed.
match (summary.num_files, summary.num_dirs) {
(0, 0) => {
write!(printer, "No cache entries found")?;
write!(printer.stderr(), "No cache entries found")?;
}
(0, 1) => {
write!(printer, "Removed 1 directory")?;
write!(printer.stderr(), "Removed 1 directory")?;
}
(0, num_dirs_removed) => {
write!(printer, "Removed {num_dirs_removed} directories")?;
write!(printer.stderr(), "Removed {num_dirs_removed} directories")?;
}
(1, _) => {
write!(printer, "Removed 1 file")?;
write!(printer.stderr(), "Removed 1 file")?;
}
(num_files_removed, _) => {
write!(printer, "Removed {num_files_removed} files")?;
write!(printer.stderr(), "Removed {num_files_removed} files")?;
}
}
@@ -66,10 +66,10 @@ pub(crate) fn cache_clean(
let (bytes, unit) = human_readable_bytes(summary.total_bytes);
format!("{bytes:.1}{unit}")
};
write!(printer, " ({})", bytes.green())?;
write!(printer.stderr(), " ({})", bytes.green())?;
}
writeln!(printer)?;
writeln!(printer.stderr())?;
} else {
for package in packages {
let summary = cache.remove(package)?;
@@ -77,24 +77,32 @@ pub(crate) fn cache_clean(
// Write a summary of the number of files and directories removed.
match (summary.num_files, summary.num_dirs) {
(0, 0) => {
write!(printer, "No cache entries found for {}", package.cyan())?;
write!(
printer.stderr(),
"No cache entries found for {}",
package.cyan()
)?;
}
(0, 1) => {
write!(printer, "Removed 1 directory for {}", package.cyan())?;
write!(
printer.stderr(),
"Removed 1 directory for {}",
package.cyan()
)?;
}
(0, num_dirs_removed) => {
write!(
printer,
printer.stderr(),
"Removed {num_dirs_removed} directories for {}",
package.cyan()
)?;
}
(1, _) => {
write!(printer, "Removed 1 file for {}", package.cyan())?;
write!(printer.stderr(), "Removed 1 file for {}", package.cyan())?;
}
(num_files_removed, _) => {
write!(
printer,
printer.stderr(),
"Removed {num_files_removed} files for {}",
package.cyan()
)?;
@@ -109,10 +117,10 @@ pub(crate) fn cache_clean(
let (bytes, unit) = human_readable_bytes(summary.total_bytes);
format!("{bytes:.1}{unit}")
};
write!(printer, " ({})", bytes.green())?;
write!(printer.stderr(), " ({})", bytes.green())?;
}
writeln!(printer)?;
writeln!(printer.stderr())?;
}
}
+2 -2
View File
@@ -114,7 +114,7 @@ pub(crate) enum ListFormat {
pub(super) async fn compile_bytecode(
venv: &PythonEnvironment,
cache: &Cache,
mut printer: Printer,
printer: Printer,
) -> anyhow::Result<()> {
let start = std::time::Instant::now();
let files = compile_tree(venv.site_packages(), venv.python_executable(), cache.root())
@@ -127,7 +127,7 @@ pub(super) async fn compile_bytecode(
})?;
let s = if files == 1 { "" } else { "s" };
writeln!(
printer,
printer.stderr(),
"{}",
format!(
"Bytecode compiled {} in {}",
+4 -4
View File
@@ -67,7 +67,7 @@ pub(crate) async fn pip_compile(
annotation_style: AnnotationStyle,
quiet: bool,
cache: Cache,
mut printer: Printer,
printer: Printer,
) -> Result<ExitStatus> {
let start = std::time::Instant::now();
@@ -271,7 +271,7 @@ pub(crate) async fn pip_compile(
let s = if editables.len() == 1 { "" } else { "s" };
writeln!(
printer,
printer.stderr(),
"{}",
format!(
"Built {} in {}",
@@ -326,7 +326,7 @@ pub(crate) async fn pip_compile(
let s = if resolution.len() == 1 { "" } else { "s" };
writeln!(
printer,
printer.stderr(),
"{}",
format!(
"Resolved {} in {}",
@@ -339,7 +339,7 @@ pub(crate) async fn pip_compile(
// Notify the user of any diagnostics.
for diagnostic in resolution.diagnostics() {
writeln!(
printer,
printer.stderr(),
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
+5 -6
View File
@@ -1,6 +1,5 @@
use std::fmt::Write;
use anstream::println;
use anyhow::Result;
use itertools::Itertools;
use owo_colors::OwoColorize;
@@ -22,7 +21,7 @@ pub(crate) fn pip_freeze(
python: Option<&str>,
system: bool,
cache: &Cache,
mut printer: Printer,
printer: Printer,
) -> Result<ExitStatus> {
// Detect the current Python interpreter.
let platform = Platform::current()?;
@@ -54,13 +53,13 @@ pub(crate) fn pip_freeze(
{
match dist {
InstalledDist::Registry(dist) => {
println!("{}=={}", dist.name().bold(), dist.version);
writeln!(printer.stdout(), "{}=={}", dist.name().bold(), dist.version)?;
}
InstalledDist::Url(dist) => {
if dist.editable {
println!("-e {}", dist.url);
writeln!(printer.stdout(), "-e {}", dist.url)?;
} else {
println!("{} @ {}", dist.name().bold(), dist.url);
writeln!(printer.stdout(), "{} @ {}", dist.name().bold(), dist.url)?;
}
}
}
@@ -70,7 +69,7 @@ pub(crate) fn pip_freeze(
if strict {
for diagnostic in site_packages.diagnostics()? {
writeln!(
printer,
printer.stderr(),
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
+16 -16
View File
@@ -66,7 +66,7 @@ pub(crate) async fn pip_install(
python: Option<String>,
system: bool,
cache: Cache,
mut printer: Printer,
printer: Printer,
) -> Result<ExitStatus> {
let start = std::time::Instant::now();
@@ -152,7 +152,7 @@ pub(crate) async fn pip_install(
let num_requirements = requirements.len() + editables.len();
let s = if num_requirements == 1 { "" } else { "s" };
writeln!(
printer,
printer.stderr(),
"{}",
format!(
"Audited {} in {}",
@@ -366,7 +366,7 @@ async fn build_editables(
tags: &Tags,
client: &RegistryClient,
build_dispatch: &BuildDispatch<'_>,
mut printer: Printer,
printer: Printer,
) -> Result<Vec<BuiltEditable>, Error> {
let start = std::time::Instant::now();
@@ -409,7 +409,7 @@ async fn build_editables(
let s = if editables.len() == 1 { "" } else { "s" };
writeln!(
printer,
printer.stderr(),
"{}",
format!(
"Built {} in {}",
@@ -441,7 +441,7 @@ async fn resolve(
index: &InMemoryIndex,
build_dispatch: &BuildDispatch<'_>,
options: Options,
mut printer: Printer,
printer: Printer,
) -> Result<ResolutionGraph, Error> {
let start = std::time::Instant::now();
@@ -504,7 +504,7 @@ async fn resolve(
let s = if resolution.len() == 1 { "" } else { "s" };
writeln!(
printer,
printer.stderr(),
"{}",
format!(
"Resolved {} in {}",
@@ -534,7 +534,7 @@ async fn install(
build_dispatch: &BuildDispatch<'_>,
cache: &Cache,
venv: &PythonEnvironment,
mut printer: Printer,
printer: Printer,
) -> Result<(), Error> {
let start = std::time::Instant::now();
@@ -570,7 +570,7 @@ async fn install(
if remote.is_empty() && local.is_empty() && reinstalls.is_empty() {
let s = if resolution.len() == 1 { "" } else { "s" };
writeln!(
printer,
printer.stderr(),
"{}",
format!(
"Audited {} in {}",
@@ -610,7 +610,7 @@ async fn install(
let s = if wheels.len() == 1 { "" } else { "s" };
writeln!(
printer,
printer.stderr(),
"{}",
format!(
"Downloaded {} in {}",
@@ -649,7 +649,7 @@ async fn install(
let s = if wheels.len() == 1 { "" } else { "s" };
writeln!(
printer,
printer.stderr(),
"{}",
format!(
"Installed {} in {}",
@@ -685,7 +685,7 @@ async fn install(
match event.kind {
ChangeEventKind::Added => {
writeln!(
printer,
printer.stderr(),
" {} {}{}",
"+".green(),
event.dist.name().as_ref().bold(),
@@ -694,7 +694,7 @@ async fn install(
}
ChangeEventKind::Removed => {
writeln!(
printer,
printer.stderr(),
" {} {}{}",
"-".red(),
event.dist.name().as_ref().bold(),
@@ -714,7 +714,7 @@ async fn install(
None | Some(Yanked::Bool(false)) => {}
Some(Yanked::Bool(true)) => {
writeln!(
printer,
printer.stderr(),
"{}{} {dist} is yanked.",
"warning".yellow().bold(),
":".bold(),
@@ -722,7 +722,7 @@ async fn install(
}
Some(Yanked::Reason(reason)) => {
writeln!(
printer,
printer.stderr(),
"{}{} {dist} is yanked (reason: \"{reason}\").",
"warning".yellow().bold(),
":".bold(),
@@ -738,7 +738,7 @@ async fn install(
fn validate(
resolution: &Resolution,
venv: &PythonEnvironment,
mut printer: Printer,
printer: Printer,
) -> Result<(), Error> {
let site_packages = SitePackages::from_executable(venv)?;
let diagnostics = site_packages.diagnostics()?;
@@ -749,7 +749,7 @@ fn validate(
.any(|package| diagnostic.includes(package))
{
writeln!(
printer,
printer.stderr(),
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
+10 -6
View File
@@ -1,7 +1,6 @@
use std::cmp::max;
use std::fmt::Write;
use anstream::println;
use anyhow::Result;
use itertools::Itertools;
use owo_colors::OwoColorize;
@@ -33,7 +32,7 @@ pub(crate) fn pip_list(
python: Option<&str>,
system: bool,
cache: &Cache,
mut printer: Printer,
printer: Printer,
) -> Result<ExitStatus> {
// Detect the current Python interpreter.
let platform = Platform::current()?;
@@ -112,17 +111,22 @@ pub(crate) fn pip_list(
}
for elems in MultiZip(columns.iter().map(Column::fmt).collect_vec()) {
println!("{}", elems.join(" "));
writeln!(printer.stdout(), "{}", elems.join(" "))?;
}
}
ListFormat::Json => {
let rows = results.iter().copied().map(Entry::from).collect_vec();
let output = serde_json::to_string(&rows)?;
println!("{output}");
writeln!(printer.stdout(), "{output}")?;
}
ListFormat::Freeze => {
for dist in &results {
println!("{}=={}", dist.name().bold(), dist.version());
writeln!(
printer.stdout(),
"{}=={}",
dist.name().bold(),
dist.version()
)?;
}
}
}
@@ -131,7 +135,7 @@ pub(crate) fn pip_list(
if strict {
for diagnostic in site_packages.diagnostics()? {
writeln!(
printer,
printer.stderr(),
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
+37 -45
View File
@@ -4,7 +4,6 @@ use anyhow::Result;
use owo_colors::OwoColorize;
use tracing::debug;
use anstream::{eprintln, println};
use distribution_types::Name;
use platform_host::Platform;
use uv_cache::Cache;
@@ -22,18 +21,18 @@ pub(crate) fn pip_show(
strict: bool,
python: Option<&str>,
system: bool,
quiet: bool,
cache: &Cache,
mut printer: Printer,
printer: Printer,
) -> Result<ExitStatus> {
if packages.is_empty() {
#[allow(clippy::print_stderr)]
{
eprintln!(
writeln!(
printer.stderr(),
"{}{} Please provide a package name or names.",
"warning".yellow().bold(),
":".bold(),
);
)?;
}
return Ok(ExitStatus::Failure);
}
@@ -76,7 +75,7 @@ pub(crate) fn pip_show(
let installed = site_packages.get_packages(package);
if installed.is_empty() {
writeln!(
printer,
printer.stderr(),
"{}{} Package(s) not found for: {}",
"warning".yellow().bold(),
":".bold(),
@@ -95,47 +94,40 @@ pub(crate) fn pip_show(
return Ok(ExitStatus::Failure);
}
if !quiet {
// Print the information for each package.
let mut first = true;
for distribution in &distributions {
if first {
first = false;
} else {
// Print a separator between packages.
#[allow(clippy::print_stdout)]
{
println!("---");
}
}
// Print the name, version, and location (e.g., the `site-packages` directory).
#[allow(clippy::print_stdout)]
{
println!("Name: {}", distribution.name());
println!("Version: {}", distribution.version());
println!(
"Location: {}",
distribution
.path()
.parent()
.expect("package path is not root")
.simplified_display()
);
}
// Print the information for each package.
let mut first = true;
for distribution in &distributions {
if first {
first = false;
} else {
// Print a separator between packages.
writeln!(printer.stdout(), "---")?;
}
// Validate that the environment is consistent.
if strict {
for diagnostic in site_packages.diagnostics()? {
writeln!(
printer,
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
diagnostic.message().bold()
)?;
}
// Print the name, version, and location (e.g., the `site-packages` directory).
writeln!(printer.stdout(), "Name: {}", distribution.name())?;
writeln!(printer.stdout(), "Version: {}", distribution.version())?;
writeln!(
printer.stdout(),
"Location: {}",
distribution
.path()
.parent()
.expect("package path is not root")
.simplified_display()
)?;
}
// Validate that the environment is consistent.
if strict {
for diagnostic in site_packages.diagnostics()? {
writeln!(
printer.stderr(),
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
diagnostic.message().bold()
)?;
}
}
+14 -14
View File
@@ -44,7 +44,7 @@ pub(crate) async fn pip_sync(
python: Option<String>,
system: bool,
cache: Cache,
mut printer: Printer,
printer: Printer,
) -> Result<ExitStatus> {
let start = std::time::Instant::now();
@@ -64,7 +64,7 @@ pub(crate) async fn pip_sync(
let num_requirements = requirements.len() + editables.len();
if num_requirements == 0 {
writeln!(printer, "No requirements found")?;
writeln!(printer.stderr(), "No requirements found")?;
return Ok(ExitStatus::Success);
}
@@ -184,7 +184,7 @@ pub(crate) async fn pip_sync(
if remote.is_empty() && local.is_empty() && reinstalls.is_empty() && extraneous.is_empty() {
let s = if num_requirements == 1 { "" } else { "s" };
writeln!(
printer,
printer.stderr(),
"{}",
format!(
"Audited {} in {}",
@@ -210,7 +210,7 @@ pub(crate) async fn pip_sync(
let s = if resolution.len() == 1 { "" } else { "s" };
writeln!(
printer,
printer.stderr(),
"{}",
format!(
"Resolved {} in {}",
@@ -239,7 +239,7 @@ pub(crate) async fn pip_sync(
let s = if wheels.len() == 1 { "" } else { "s" };
writeln!(
printer,
printer.stderr(),
"{}",
format!(
"Downloaded {} in {}",
@@ -274,7 +274,7 @@ pub(crate) async fn pip_sync(
"s"
};
writeln!(
printer,
printer.stderr(),
"{}",
format!(
"Uninstalled {} in {}",
@@ -296,7 +296,7 @@ pub(crate) async fn pip_sync(
let s = if wheels.len() == 1 { "" } else { "s" };
writeln!(
printer,
printer.stderr(),
"{}",
format!(
"Installed {} in {}",
@@ -334,7 +334,7 @@ pub(crate) async fn pip_sync(
match event.kind {
ChangeEventKind::Added => {
writeln!(
printer,
printer.stderr(),
" {} {}{}",
"+".green(),
event.dist.name().as_ref().bold(),
@@ -343,7 +343,7 @@ pub(crate) async fn pip_sync(
}
ChangeEventKind::Removed => {
writeln!(
printer,
printer.stderr(),
" {} {}{}",
"-".red(),
event.dist.name().as_ref().bold(),
@@ -358,7 +358,7 @@ pub(crate) async fn pip_sync(
let site_packages = SitePackages::from_executable(&venv)?;
for diagnostic in site_packages.diagnostics()? {
writeln!(
printer,
printer.stderr(),
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
@@ -377,7 +377,7 @@ pub(crate) async fn pip_sync(
None | Some(Yanked::Bool(false)) => {}
Some(Yanked::Bool(true)) => {
writeln!(
printer,
printer.stderr(),
"{}{} {dist} is yanked. Refresh your lockfile to pin an un-yanked version.",
"warning".yellow().bold(),
":".bold(),
@@ -385,7 +385,7 @@ pub(crate) async fn pip_sync(
}
Some(Yanked::Reason(reason)) => {
writeln!(
printer,
printer.stderr(),
"{}{} {dist} is yanked (reason: \"{reason}\"). Refresh your lockfile to pin an un-yanked version.",
"warning".yellow().bold(),
":".bold(),
@@ -418,7 +418,7 @@ async fn resolve_editables(
cache: &Cache,
client: &RegistryClient,
build_dispatch: &BuildDispatch<'_>,
mut printer: Printer,
printer: Printer,
) -> Result<ResolvedEditables> {
// Partition the editables into those that are already installed, and those that must be built.
let mut installed = Vec::with_capacity(editables.len());
@@ -519,7 +519,7 @@ async fn resolve_editables(
let s = if built_editables.len() == 1 { "" } else { "s" };
writeln!(
printer,
printer.stderr(),
"{}",
format!(
"Built {} in {}",
+6 -6
View File
@@ -20,7 +20,7 @@ pub(crate) async fn pip_uninstall(
python: Option<String>,
system: bool,
cache: Cache,
mut printer: Printer,
printer: Printer,
) -> Result<ExitStatus> {
let start = std::time::Instant::now();
@@ -105,7 +105,7 @@ pub(crate) async fn pip_uninstall(
let installed = site_packages.get_packages(package);
if installed.is_empty() {
writeln!(
printer,
printer.stderr(),
"{}{} Skipping {} as it is not installed.",
"warning".yellow().bold(),
":".bold(),
@@ -121,7 +121,7 @@ pub(crate) async fn pip_uninstall(
let installed = site_packages.get_editables(editable);
if installed.is_empty() {
writeln!(
printer,
printer.stderr(),
"{}{} Skipping {} as it is not installed.",
"warning".yellow().bold(),
":".bold(),
@@ -140,7 +140,7 @@ pub(crate) async fn pip_uninstall(
if distributions.is_empty() {
writeln!(
printer,
printer.stderr(),
"{}{} No packages to uninstall.",
"warning".yellow().bold(),
":".bold(),
@@ -162,7 +162,7 @@ pub(crate) async fn pip_uninstall(
}
writeln!(
printer,
printer.stderr(),
"{}",
format!(
"Uninstalled {} in {}",
@@ -179,7 +179,7 @@ pub(crate) async fn pip_uninstall(
for distribution in distributions {
writeln!(
printer,
printer.stderr(),
" {} {}{}",
"-".red(),
distribution.name().as_ref().bold(),
+5 -5
View File
@@ -94,7 +94,7 @@ async fn venv_impl(
seed: bool,
exclude_newer: Option<DateTime<Utc>>,
cache: &Cache,
mut printer: Printer,
printer: Printer,
) -> miette::Result<ExitStatus> {
// Locate the Python interpreter.
let platform = Platform::current().into_diagnostic()?;
@@ -108,7 +108,7 @@ async fn venv_impl(
};
writeln!(
printer,
printer.stderr(),
"Using Python {} interpreter at: {}",
interpreter.python_version(),
interpreter.sys_executable().simplified_display().cyan()
@@ -116,7 +116,7 @@ async fn venv_impl(
.into_diagnostic()?;
writeln!(
printer,
printer.stderr(),
"Creating virtualenv at: {}",
path.simplified_display().cyan()
)
@@ -201,7 +201,7 @@ async fn venv_impl(
.sorted_unstable_by(|a, b| a.name().cmp(b.name()).then(a.version().cmp(&b.version())))
{
writeln!(
printer,
printer.stderr(),
" {} {}{}",
"+".green(),
distribution.name().as_ref().bold(),
@@ -233,7 +233,7 @@ async fn venv_impl(
Some(Shell::Powershell) => Some(shlex_windows(path.join("Scripts").join("activate"))),
};
if let Some(act) = activation {
writeln!(printer, "Activate with: {}", act.green()).into_diagnostic()?;
writeln!(printer.stderr(), "Activate with: {}", act.green()).into_diagnostic()?;
}
Ok(ExitStatus::Success)
-1
View File
@@ -1486,7 +1486,6 @@ async fn run() -> Result<ExitStatus> {
args.strict,
args.python.as_deref(),
args.system,
cli.quiet,
&cache,
printer,
),
+52 -6
View File
@@ -1,4 +1,4 @@
use anstream::eprint;
use anstream::{eprint, print};
use indicatif::ProgressDrawTarget;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -22,18 +22,64 @@ impl Printer {
Self::Verbose => ProgressDrawTarget::hidden(),
}
}
/// Return the [`Stdout`] for this printer.
pub(crate) fn stdout(self) -> Stdout {
match self {
Self::Default => Stdout::Enabled,
Self::Quiet => Stdout::Disabled,
Self::Verbose => Stdout::Enabled,
}
}
/// Return the [`Stderr`] for this printer.
pub(crate) fn stderr(self) -> Stderr {
match self {
Self::Default => Stderr::Enabled,
Self::Quiet => Stderr::Disabled,
Self::Verbose => Stderr::Enabled,
}
}
}
impl std::fmt::Write for Printer {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Stdout {
Enabled,
Disabled,
}
impl std::fmt::Write for Stdout {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
match self {
Self::Default | Self::Verbose => {
#[allow(clippy::print_stderr, clippy::ignored_unit_patterns)]
Self::Enabled => {
#[allow(clippy::print_stdout, clippy::ignored_unit_patterns)]
{
eprint!("{s}");
print!("{s}");
}
}
Self::Quiet => {}
Self::Disabled => {}
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Stderr {
Enabled,
Disabled,
}
impl std::fmt::Write for Stderr {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
match self {
Self::Enabled => {
#[allow(clippy::print_stderr, clippy::ignored_unit_patterns)]
{
eprint!("{s}");
}
}
Self::Disabled => {}
}
Ok(())