Skip to content

Commit

Permalink
Write to stdout when --output-file is present
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Feb 23, 2024
1 parent eef3ca5 commit 3bd5dd6
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 128 deletions.
44 changes: 37 additions & 7 deletions crates/uv/src/commands/pip_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,13 +335,7 @@ pub(crate) async fn pip_compile(
}

// Write the resolved dependencies to the output channel.
let mut writer: Box<dyn std::io::Write> = if let Some(output_file) = output_file {
Box::new(AutoStream::<std::fs::File>::auto(
fs_err::File::create(output_file)?.into(),
))
} else {
Box::new(AutoStream::auto(stdout()))
};
let mut writer = OutputWriter::new(output_file)?;

if include_header {
writeln!(
Expand Down Expand Up @@ -428,6 +422,42 @@ fn cmd(include_index_url: bool) -> String {
format!("uv {args}")
}

/// A multi-casting writer that writes to both the standard output and an output file, if present.
struct OutputWriter {
stdout: AutoStream<std::io::Stdout>,
output_file: Option<AutoStream<std::fs::File>>,
}

impl OutputWriter {
/// Create a new output writer.
fn new(output_file: Option<&Path>) -> Result<Self> {
let stdout = AutoStream::<std::io::Stdout>::auto(stdout());
let output_file = output_file
.map(|output_file| {
let output_file = fs_err::File::create(output_file)?;
let output_file = AutoStream::auto(output_file.into());
Ok::<AutoStream<std::fs::File>, std::io::Error>(output_file)
})
.transpose()?;
Ok(Self {
stdout,
output_file,
})
}

/// Write the given arguments to both the standard output and the output file, if present.
fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> std::io::Result<()> {
use std::io::Write;

if let Some(output_file) = &mut self.output_file {
write!(output_file, "{args}")?;
}
write!(self.stdout, "{args}")?;

Ok(())
}
}

/// Whether to allow package upgrades.
#[derive(Debug)]
pub(crate) enum Upgrade {
Expand Down
173 changes: 52 additions & 121 deletions crates/uv/tests/pip_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ use anyhow::{bail, Context, Result};
use assert_fs::prelude::*;
use assert_fs::TempDir;
use indoc::indoc;
use insta::assert_snapshot;
use itertools::Itertools;
use url::Url;

use common::{uv_snapshot, TestContext, INSTA_FILTERS};
Expand Down Expand Up @@ -2984,49 +2982,32 @@ fn upgrade_none() -> Result<()> {
# via black
"})?;

let filters = if cfg!(windows) {
[("Resolved 7 packages", "Resolved 6 packages")]
.into_iter()
.chain(INSTA_FILTERS.to_vec())
.collect()
} else {
INSTA_FILTERS.to_vec()
};

uv_snapshot!(filters, context.compile()
.arg("requirements.in")
.arg("--output-file")
.arg("requirements.txt"), @r###"
uv_snapshot!(context.compile()
.arg("requirements.in")
.arg("--output-file")
.arg("requirements.txt"), @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2023-11-18T12:00:00Z requirements.in --output-file requirements.txt
black==23.10.1
click==8.1.2
# via black
mypy-extensions==1.0.0
# via black
packaging==23.2
# via black
pathspec==0.11.0
# via black
platformdirs==4.0.0
# via black
----- stderr -----
Resolved 6 packages in [TIME]
"###
);

// Read the output requirements, but skip the header.
let resolution = fs::read_to_string(requirements_txt.path())?
.lines()
.skip_while(|line| line.trim_start().starts_with('#'))
.join("\n");
if cfg!(unix) {
assert_snapshot!(resolution, @r###"
black==23.10.1
click==8.1.2
# via black
mypy-extensions==1.0.0
# via black
packaging==23.2
# via black
pathspec==0.11.0
# via black
platformdirs==4.0.0
# via black
"###);
}

Ok(())
}

Expand Down Expand Up @@ -3055,68 +3036,33 @@ fn upgrade_all() -> Result<()> {
# via black
"})?;

let filters = if cfg!(windows) {
[("Resolved 7 packages", "Resolved 6 packages")]
.into_iter()
.chain(INSTA_FILTERS.to_vec())
.collect()
} else {
INSTA_FILTERS.to_vec()
};

uv_snapshot!(filters, context.compile()
.arg("requirements.in")
.arg("--output-file")
.arg("requirements.txt")
.arg("--upgrade"), @r###"
uv_snapshot!(context.compile()
.arg("requirements.in")
.arg("--output-file")
.arg("requirements.txt")
.arg("--upgrade"), @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2023-11-18T12:00:00Z requirements.in --output-file requirements.txt --upgrade
black==23.10.1
click==8.1.7
# via black
mypy-extensions==1.0.0
# via black
packaging==23.2
# via black
pathspec==0.11.2
# via black
platformdirs==4.0.0
# via black
----- stderr -----
Resolved 6 packages in [TIME]
"###
);

// Read the output requirements, but skip the header.
let resolution = fs::read_to_string(requirements_txt.path())?
.lines()
.skip_while(|line| line.trim_start().starts_with('#'))
.join("\n");
if cfg!(unix) {
assert_snapshot!(resolution, @r###"
black==23.10.1
click==8.1.7
# via black
mypy-extensions==1.0.0
# via black
packaging==23.2
# via black
pathspec==0.11.2
# via black
platformdirs==4.0.0
# via black
"###);
} else if cfg!(windows) {
assert_snapshot!(resolution, @r###"
black==23.10.1
click==8.1.7
# via black
colorama==0.4.6
# via click
mypy-extensions==1.0.0
# via black
packaging==23.2
# via black
pathspec==0.11.2
# via black
platformdirs==4.0.0
# via black
"###);
} else {
unimplemented!("Only Windows and Unix are supported");
}

Ok(())
}

Expand Down Expand Up @@ -3145,16 +3091,7 @@ fn upgrade_package() -> Result<()> {
# via black
"})?;

let filters = if cfg!(windows) {
[("Resolved 7 packages", "Resolved 6 packages")]
.into_iter()
.chain(INSTA_FILTERS.to_vec())
.collect()
} else {
INSTA_FILTERS.to_vec()
};

uv_snapshot!(filters, context.compile()
uv_snapshot!(context.compile()
.arg("requirements.in")
.arg("--output-file")
.arg("requirements.txt")
Expand All @@ -3163,34 +3100,25 @@ fn upgrade_package() -> Result<()> {
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2023-11-18T12:00:00Z requirements.in --output-file requirements.txt --upgrade-package click
black==23.10.1
click==8.1.7
# via black
mypy-extensions==1.0.0
# via black
packaging==23.2
# via black
pathspec==0.11.0
# via black
platformdirs==4.0.0
# via black
----- stderr -----
Resolved 6 packages in [TIME]
"###
);

// Read the output requirements, but skip the header.
let resolution = fs::read_to_string(requirements_txt.path())?
.lines()
.skip_while(|line| line.trim_start().starts_with('#'))
.join("\n");
if cfg!(unix) {
assert_snapshot!(resolution, @r###"
black==23.10.1
click==8.1.7
# via black
mypy-extensions==1.0.0
# via black
packaging==23.2
# via black
pathspec==0.11.0
# via black
platformdirs==4.0.0
# via black
"###
);
}

Ok(())
}

Expand Down Expand Up @@ -3762,6 +3690,9 @@ fn compile_types_pytz() -> Result<()> {
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2023-11-18T12:00:00Z requirements.in -o requirements.txt
types-pytz==2021.1.0
----- stderr -----
Resolved 1 package in [TIME]
Expand Down

0 comments on commit 3bd5dd6

Please sign in to comment.