mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-03 15:24:36 +00:00
cli: conventionally treat -
as "read from stdin" (#1314)
Basically, when a path to a requirements file is `-`, then we should read its contents from `stdin` instead of the file path named `-`. Fixes #1313
This commit is contained in:
parent
c5e413c4b8
commit
b6fba00153
5 changed files with 69 additions and 6 deletions
|
@ -12,6 +12,38 @@ pub use crate::path::*;
|
||||||
|
|
||||||
mod path;
|
mod path;
|
||||||
|
|
||||||
|
/// Reads the contents of the file path given into memory.
|
||||||
|
///
|
||||||
|
/// If the file path is `-`, then contents are read from stdin instead.
|
||||||
|
pub fn read(path: impl AsRef<Path>) -> std::io::Result<Vec<u8>> {
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
let path = path.as_ref();
|
||||||
|
if path == Path::new("-") {
|
||||||
|
let mut buf = Vec::with_capacity(1024);
|
||||||
|
std::io::stdin().read_to_end(&mut buf)?;
|
||||||
|
Ok(buf)
|
||||||
|
} else {
|
||||||
|
fs::read(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the contents of the file path given into memory as a `String`.
|
||||||
|
///
|
||||||
|
/// If the file path is `-`, then contents are read from stdin instead.
|
||||||
|
pub fn read_to_string(path: impl AsRef<Path>) -> std::io::Result<String> {
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
let path = path.as_ref();
|
||||||
|
if path == Path::new("-") {
|
||||||
|
let mut buf = String::with_capacity(1024);
|
||||||
|
std::io::stdin().read_to_string(&mut buf)?;
|
||||||
|
Ok(buf)
|
||||||
|
} else {
|
||||||
|
fs::read_to_string(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a symlink from `src` to `dst`, replacing any existing symlink.
|
/// Create a symlink from `src` to `dst`, replacing any existing symlink.
|
||||||
///
|
///
|
||||||
/// On Windows, this uses the `junction` crate to create a junction point.
|
/// On Windows, this uses the `junction` crate to create a junction point.
|
||||||
|
|
|
@ -158,6 +158,8 @@ fn date_or_datetime(input: &str) -> Result<DateTime<Utc>, String> {
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
struct PipCompileArgs {
|
struct PipCompileArgs {
|
||||||
/// Include all packages listed in the given `requirements.in` files.
|
/// Include all packages listed in the given `requirements.in` files.
|
||||||
|
///
|
||||||
|
/// When the path is `-`, then requirements are read from stdin.
|
||||||
#[clap(required(true))]
|
#[clap(required(true))]
|
||||||
src_file: Vec<PathBuf>,
|
src_file: Vec<PathBuf>,
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use console::Term;
|
use console::Term;
|
||||||
use fs_err as fs;
|
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use distribution_types::{FlatIndexLocation, IndexUrl};
|
use distribution_types::{FlatIndexLocation, IndexUrl};
|
||||||
|
@ -193,7 +192,7 @@ impl RequirementsSpecification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RequirementsSource::PyprojectToml(path) => {
|
RequirementsSource::PyprojectToml(path) => {
|
||||||
let contents = fs::read_to_string(path)?;
|
let contents = puffin_fs::read_to_string(path)?;
|
||||||
let pyproject_toml = toml::from_str::<pyproject_toml::PyProjectToml>(&contents)
|
let pyproject_toml = toml::from_str::<pyproject_toml::PyProjectToml>(&contents)
|
||||||
.with_context(|| format!("Failed to parse `{}`", path.normalized_display()))?;
|
.with_context(|| format!("Failed to parse `{}`", path.normalized_display()))?;
|
||||||
let mut used_extras = FxHashSet::default();
|
let mut used_extras = FxHashSet::default();
|
||||||
|
|
|
@ -47,6 +47,36 @@ fn compile_requirements_in() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve a specific version of Django from a `requirements.in` file on stdin
|
||||||
|
/// when passed a path of `-`.
|
||||||
|
#[test]
|
||||||
|
fn compile_requirements_in_stdin() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
let requirements_in = context.temp_dir.child("requirements.in");
|
||||||
|
requirements_in.write_str("django==5.0b1")?;
|
||||||
|
|
||||||
|
puffin_snapshot!(context
|
||||||
|
.compile()
|
||||||
|
.stdin(fs::File::open(requirements_in)?)
|
||||||
|
.arg("-"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
# This file was autogenerated by Puffin v[VERSION] via the following command:
|
||||||
|
# puffin pip compile --cache-dir [CACHE_DIR] --exclude-newer 2023-11-18T12:00:00Z -
|
||||||
|
asgiref==3.7.2
|
||||||
|
# via django
|
||||||
|
django==5.0b1
|
||||||
|
sqlparse==0.4.4
|
||||||
|
# via django
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 3 packages in [TIME]
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_requirements_in() {
|
fn missing_requirements_in() {
|
||||||
let context = TestContext::new("3.12");
|
let context = TestContext::new("3.12");
|
||||||
|
|
|
@ -39,7 +39,6 @@ use std::fmt::{Display, Formatter};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use fs_err as fs;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use unscanny::{Pattern, Scanner};
|
use unscanny::{Pattern, Scanner};
|
||||||
|
@ -299,10 +298,11 @@ impl RequirementsTxt {
|
||||||
requirements_txt: impl AsRef<Path>,
|
requirements_txt: impl AsRef<Path>,
|
||||||
working_dir: impl AsRef<Path>,
|
working_dir: impl AsRef<Path>,
|
||||||
) -> Result<Self, RequirementsTxtFileError> {
|
) -> Result<Self, RequirementsTxtFileError> {
|
||||||
let content =
|
let content = puffin_fs::read_to_string(&requirements_txt).map_err(|err| {
|
||||||
fs::read_to_string(&requirements_txt).map_err(|err| RequirementsTxtFileError {
|
RequirementsTxtFileError {
|
||||||
file: requirements_txt.as_ref().to_path_buf(),
|
file: requirements_txt.as_ref().to_path_buf(),
|
||||||
error: RequirementsTxtParserError::IO(err),
|
error: RequirementsTxtParserError::IO(err),
|
||||||
|
}
|
||||||
})?;
|
})?;
|
||||||
let data = Self::parse_inner(&content, working_dir.as_ref()).map_err(|err| {
|
let data = Self::parse_inner(&content, working_dir.as_ref()).map_err(|err| {
|
||||||
RequirementsTxtFileError {
|
RequirementsTxtFileError {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue