Skip to content

Commit 48e57e7

Browse files
committed
Create missing dir for uv export
1 parent d4d6da1 commit 48e57e7

2 files changed

Lines changed: 78 additions & 0 deletions

File tree

crates/uv/src/commands/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,12 @@ impl<'a> OutputWriter<'a> {
231231
/// Commit the buffer to the output file.
232232
async fn commit(self) -> std::io::Result<()> {
233233
if let Some(output_file) = self.output_file {
234+
if let Some(parent_dir) = output_file.parent() {
235+
if !parent_dir.is_empty() && !parent_dir.is_dir() {
236+
fs_err::tokio::create_dir(parent_dir).await?;
237+
}
238+
}
239+
234240
// If the output file is an existing symlink, write to the destination instead.
235241
let output_file = fs_err::read_link(output_file)
236242
.map(Cow::Owned)

crates/uv/tests/it/export.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,78 @@ fn frozen() -> Result<()> {
761761
Ok(())
762762
}
763763

764+
#[test]
765+
fn create_missing_dir() -> Result<()> {
766+
let context = TestContext::new("3.12");
767+
768+
let pyproject_toml = context.temp_dir.child("pyproject.toml");
769+
pyproject_toml.write_str(
770+
r#"
771+
[project]
772+
name = "project"
773+
version = "0.1.0"
774+
requires-python = ">=3.12"
775+
dependencies = ["anyio==3.7.0"]
776+
777+
[build-system]
778+
requires = ["setuptools>=42"]
779+
build-backend = "setuptools.build_meta"
780+
"#,
781+
)?;
782+
783+
context.lock().assert().success();
784+
785+
uv_snapshot!(context.filters(), context.export()
786+
.arg("--output-file")
787+
.arg("requirements/requirements.txt"), @r###"
788+
success: true
789+
exit_code: 0
790+
----- stdout -----
791+
# This file was autogenerated by uv via the following command:
792+
# uv export --cache-dir [CACHE_DIR] --output-file requirements/requirements.txt
793+
-e .
794+
anyio==3.7.0 \
795+
--hash=sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce \
796+
--hash=sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0
797+
idna==3.6 \
798+
--hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
799+
--hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
800+
sniffio==1.3.1 \
801+
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
802+
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
803+
804+
----- stderr -----
805+
Resolved 4 packages in [TIME]
806+
"###);
807+
//
808+
// Read the file contents.
809+
let contents = apply_filters(
810+
fs_err::read_to_string(
811+
context
812+
.temp_dir
813+
.child("requirements")
814+
.child("requirements.txt"),
815+
)
816+
.unwrap(),
817+
context.filters(),
818+
);
819+
insta::assert_snapshot!(contents, @r###"
820+
# This file was autogenerated by uv via the following command:
821+
# uv export --cache-dir [CACHE_DIR] --output-file requirements/requirements.txt
822+
-e .
823+
anyio==3.7.0 \
824+
--hash=sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce \
825+
--hash=sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0
826+
idna==3.6 \
827+
--hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
828+
--hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
829+
sniffio==1.3.1 \
830+
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
831+
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
832+
"###);
833+
Ok(())
834+
}
835+
764836
#[test]
765837
fn non_project() -> Result<()> {
766838
let context = TestContext::new("3.12");

0 commit comments

Comments
 (0)