diff --git a/Cargo.Bazel.lock b/Cargo.Bazel.lock index 21293c97..bb80ea19 100644 --- a/Cargo.Bazel.lock +++ b/Cargo.Bazel.lock @@ -1,5 +1,5 @@ { - "checksum": "32b494853fa6e3f2c2ea61a76720e873e7384da14889aadcba90bfcd4f1916e3", + "checksum": "7b5d832f30b618fcf788084bf90341a4f9f48026f51817e91a5250c6e53648ab", "crates": { "addr2line 0.21.0": { "name": "addr2line", @@ -5940,59 +5940,6 @@ }, "license": "MIT OR Apache-2.0" }, - "is-terminal 0.4.10": { - "name": "is-terminal", - "version": "0.4.10", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/is-terminal/0.4.10/download", - "sha256": "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" - } - }, - "targets": [ - { - "Library": { - "crate_name": "is_terminal", - "crate_root": "src/lib.rs", - "srcs": [ - "**/*.rs" - ] - } - } - ], - "library_target_name": "is_terminal", - "common_attrs": { - "compile_data_glob": [ - "**" - ], - "deps": { - "common": [], - "selects": { - "cfg(not(any(windows, target_os = \"hermit\", target_os = \"unknown\")))": [ - { - "id": "rustix 0.38.31", - "target": "rustix" - } - ], - "cfg(target_os = \"hermit\")": [ - { - "id": "hermit-abi 0.3.5", - "target": "hermit_abi" - } - ], - "cfg(windows)": [ - { - "id": "windows-sys 0.52.0", - "target": "windows_sys" - } - ] - } - }, - "edition": "2018", - "version": "0.4.10" - }, - "license": "MIT" - }, "is_ci 1.2.0": { "name": "is_ci", "version": "1.2.0", @@ -6734,6 +6681,68 @@ } ], "library_target_name": "miette", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "once_cell 1.19.0", + "target": "once_cell" + }, + { + "id": "thiserror 1.0.56", + "target": "thiserror" + }, + { + "id": "unicode-width 0.1.11", + "target": "unicode_width" + } + ], + "selects": {} + }, + "edition": "2018", + "proc_macro_deps": { + "common": [ + { + "id": "miette-derive 5.10.0", + "target": "miette_derive" + } + ], + "selects": {} + }, + "version": "5.10.0" + }, + "license": "Apache-2.0" + }, + "miette 7.2.0": { + "name": "miette", + "version": "7.2.0", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/miette/7.2.0/download", + "sha256": "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" + } + }, + "targets": [ + { + "Library": { + "crate_name": "miette", + "crate_root": "src/lib.rs", + "srcs": [ + "**/*.rs" + ] + } + } + ], + "library_target_name": "miette", "common_attrs": { "compile_data_glob": [ "**" @@ -6743,9 +6752,11 @@ "backtrace", "backtrace-ext", "default", + "derive", "fancy", + "fancy-base", "fancy-no-backtrace", - "is-terminal", + "miette-derive", "owo-colors", "supports-color", "supports-hyperlinks", @@ -6766,35 +6777,31 @@ "target": "backtrace_ext" }, { - "id": "is-terminal 0.4.10", - "target": "is_terminal" - }, - { - "id": "once_cell 1.19.0", - "target": "once_cell" + "id": "cfg-if 1.0.0", + "target": "cfg_if" }, { - "id": "owo-colors 3.5.0", + "id": "owo-colors 4.0.0", "target": "owo_colors" }, { - "id": "supports-color 2.1.0", + "id": "supports-color 3.0.0", "target": "supports_color" }, { - "id": "supports-hyperlinks 2.1.0", + "id": "supports-hyperlinks 3.0.0", "target": "supports_hyperlinks" }, { - "id": "supports-unicode 2.1.0", + "id": "supports-unicode 3.0.0", "target": "supports_unicode" }, { - "id": "terminal_size 0.1.17", + "id": "terminal_size 0.3.0", "target": "terminal_size" }, { - "id": "textwrap 0.15.2", + "id": "textwrap 0.16.1", "target": "textwrap" }, { @@ -6812,13 +6819,13 @@ "proc_macro_deps": { "common": [ { - "id": "miette-derive 5.10.0", + "id": "miette-derive 7.2.0", "target": "miette_derive" } ], "selects": {} }, - "version": "5.10.0" + "version": "7.2.0" }, "license": "Apache-2.0" }, @@ -6869,6 +6876,53 @@ }, "license": "Apache-2.0" }, + "miette-derive 7.2.0": { + "name": "miette-derive", + "version": "7.2.0", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/miette-derive/7.2.0/download", + "sha256": "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" + } + }, + "targets": [ + { + "ProcMacro": { + "crate_name": "miette_derive", + "crate_root": "src/lib.rs", + "srcs": [ + "**/*.rs" + ] + } + } + ], + "library_target_name": "miette_derive", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "proc-macro2 1.0.78", + "target": "proc_macro2" + }, + { + "id": "quote 1.0.35", + "target": "quote" + }, + { + "id": "syn 2.0.48", + "target": "syn" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "7.2.0" + }, + "license": "Apache-2.0" + }, "mime 0.3.17": { "name": "mime", "version": "0.3.17", @@ -7308,13 +7362,13 @@ }, "license": "MIT OR Apache-2.0" }, - "owo-colors 3.5.0": { + "owo-colors 4.0.0": { "name": "owo-colors", - "version": "3.5.0", + "version": "4.0.0", "repository": { "Http": { - "url": "https://static.crates.io/crates/owo-colors/3.5.0/download", - "sha256": "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + "url": "https://static.crates.io/crates/owo-colors/4.0.0/download", + "sha256": "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" } }, "targets": [ @@ -7334,7 +7388,7 @@ "**" ], "edition": "2018", - "version": "3.5.0" + "version": "4.0.0" }, "license": "MIT" }, @@ -8263,7 +8317,7 @@ "deps": { "common": [ { - "id": "miette 5.10.0", + "id": "miette 7.2.0", "target": "miette" }, { @@ -9570,100 +9624,10 @@ "default", "fs", "std", + "termios", "use-libc-auxv" ], - "selects": { - "aarch64-apple-darwin": [ - "termios" - ], - "aarch64-apple-ios": [ - "termios" - ], - "aarch64-apple-ios-sim": [ - "termios" - ], - "aarch64-fuchsia": [ - "termios" - ], - "aarch64-linux-android": [ - "termios" - ], - "aarch64-unknown-linux-gnu": [ - "termios" - ], - "aarch64-unknown-nixos-gnu": [ - "termios" - ], - "aarch64-unknown-nto-qnx710": [ - "termios" - ], - "arm-unknown-linux-gnueabi": [ - "termios" - ], - "armv7-linux-androideabi": [ - "termios" - ], - "armv7-unknown-linux-gnueabi": [ - "termios" - ], - "i686-apple-darwin": [ - "termios" - ], - "i686-linux-android": [ - "termios" - ], - "i686-unknown-freebsd": [ - "termios" - ], - "i686-unknown-linux-gnu": [ - "termios" - ], - "powerpc-unknown-linux-gnu": [ - "termios" - ], - "riscv32imc-unknown-none-elf": [ - "termios" - ], - "riscv64gc-unknown-none-elf": [ - "termios" - ], - "s390x-unknown-linux-gnu": [ - "termios" - ], - "thumbv7em-none-eabi": [ - "termios" - ], - "thumbv8m.main-none-eabi": [ - "termios" - ], - "wasm32-wasi": [ - "termios" - ], - "x86_64-apple-darwin": [ - "termios" - ], - "x86_64-apple-ios": [ - "termios" - ], - "x86_64-fuchsia": [ - "termios" - ], - "x86_64-linux-android": [ - "termios" - ], - "x86_64-unknown-freebsd": [ - "termios" - ], - "x86_64-unknown-linux-gnu": [ - "termios" - ], - "x86_64-unknown-nixos-gnu": [ - "termios" - ], - "x86_64-unknown-none": [ - "termios" - ] - } + "selects": {} }, "deps": { "common": [ @@ -11057,13 +11021,13 @@ }, "license": "BSD-3-Clause" }, - "supports-color 2.1.0": { + "supports-color 3.0.0": { "name": "supports-color", - "version": "2.1.0", + "version": "3.0.0", "repository": { "Http": { - "url": "https://static.crates.io/crates/supports-color/2.1.0/download", - "sha256": "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89" + "url": "https://static.crates.io/crates/supports-color/3.0.0/download", + "sha256": "9829b314621dfc575df4e409e79f9d6a66a3bd707ab73f23cb4aa3a854ac854f" } }, "targets": [ @@ -11084,10 +11048,6 @@ ], "deps": { "common": [ - { - "id": "is-terminal 0.4.10", - "target": "is_terminal" - }, { "id": "is_ci 1.2.0", "target": "is_ci" @@ -11096,17 +11056,17 @@ "selects": {} }, "edition": "2018", - "version": "2.1.0" + "version": "3.0.0" }, "license": "Apache-2.0" }, - "supports-hyperlinks 2.1.0": { + "supports-hyperlinks 3.0.0": { "name": "supports-hyperlinks", - "version": "2.1.0", + "version": "3.0.0", "repository": { "Http": { - "url": "https://static.crates.io/crates/supports-hyperlinks/2.1.0/download", - "sha256": "f84231692eb0d4d41e4cdd0cabfdd2e6cd9e255e65f80c9aa7c98dd502b4233d" + "url": "https://static.crates.io/crates/supports-hyperlinks/3.0.0/download", + "sha256": "2c0a1e5168041f5f3ff68ff7d95dcb9c8749df29f6e7e89ada40dd4c9de404ee" } }, "targets": [ @@ -11125,27 +11085,18 @@ "compile_data_glob": [ "**" ], - "deps": { - "common": [ - { - "id": "is-terminal 0.4.10", - "target": "is_terminal" - } - ], - "selects": {} - }, "edition": "2021", - "version": "2.1.0" + "version": "3.0.0" }, "license": "Apache-2.0" }, - "supports-unicode 2.1.0": { + "supports-unicode 3.0.0": { "name": "supports-unicode", - "version": "2.1.0", + "version": "3.0.0", "repository": { "Http": { - "url": "https://static.crates.io/crates/supports-unicode/2.1.0/download", - "sha256": "f850c19edd184a205e883199a261ed44471c81e39bd95b1357f5febbef00e77a" + "url": "https://static.crates.io/crates/supports-unicode/3.0.0/download", + "sha256": "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" } }, "targets": [ @@ -11164,17 +11115,8 @@ "compile_data_glob": [ "**" ], - "deps": { - "common": [ - { - "id": "is-terminal 0.4.10", - "target": "is_terminal" - } - ], - "selects": {} - }, "edition": "2018", - "version": "2.1.0" + "version": "3.0.0" }, "license": "Apache-2.0" }, @@ -11567,13 +11509,13 @@ }, "license": "MIT OR Apache-2.0" }, - "terminal_size 0.1.17": { + "terminal_size 0.3.0": { "name": "terminal_size", - "version": "0.1.17", + "version": "0.3.0", "repository": { "Http": { - "url": "https://static.crates.io/crates/terminal_size/0.1.17/download", - "sha256": "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" + "url": "https://static.crates.io/crates/terminal_size/0.3.0/download", + "sha256": "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" } }, "targets": [ @@ -11597,30 +11539,30 @@ "selects": { "cfg(not(windows))": [ { - "id": "libc 0.2.153", - "target": "libc" + "id": "rustix 0.38.31", + "target": "rustix" } ], "cfg(windows)": [ { - "id": "winapi 0.3.9", - "target": "winapi" + "id": "windows-sys 0.48.0", + "target": "windows_sys" } ] } }, - "edition": "2018", - "version": "0.1.17" + "edition": "2021", + "version": "0.3.0" }, "license": "MIT OR Apache-2.0" }, - "textwrap 0.15.2": { + "textwrap 0.16.1": { "name": "textwrap", - "version": "0.15.2", + "version": "0.16.1", "repository": { "Http": { - "url": "https://static.crates.io/crates/textwrap/0.15.2/download", - "sha256": "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" + "url": "https://static.crates.io/crates/textwrap/0.16.1/download", + "sha256": "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" } }, "targets": [ @@ -11665,8 +11607,8 @@ ], "selects": {} }, - "edition": "2018", - "version": "0.15.2" + "edition": "2021", + "version": "0.16.1" }, "license": "MIT" }, @@ -13353,7 +13295,7 @@ "target": "clap" }, { - "id": "miette 5.10.0", + "id": "miette 7.2.0", "target": "miette" } ], @@ -13535,7 +13477,7 @@ "target": "clap" }, { - "id": "miette 5.10.0", + "id": "miette 7.2.0", "target": "miette" } ], @@ -14268,7 +14210,6 @@ "consoleapi", "errhandlingapi", "fileapi", - "handleapi", "minwindef", "processenv", "std", @@ -14596,6 +14537,7 @@ "Win32_Storage", "Win32_Storage_FileSystem", "Win32_System", + "Win32_System_Console", "Win32_System_Diagnostics", "Win32_System_Diagnostics_Debug", "Win32_System_IO", @@ -16388,38 +16330,6 @@ "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" ], - "cfg(not(any(windows, target_os = \"hermit\", target_os = \"unknown\")))": [ - "aarch64-apple-darwin", - "aarch64-apple-ios", - "aarch64-apple-ios-sim", - "aarch64-fuchsia", - "aarch64-linux-android", - "aarch64-unknown-linux-gnu", - "aarch64-unknown-nixos-gnu", - "aarch64-unknown-nto-qnx710", - "arm-unknown-linux-gnueabi", - "armv7-linux-androideabi", - "armv7-unknown-linux-gnueabi", - "i686-apple-darwin", - "i686-linux-android", - "i686-unknown-freebsd", - "i686-unknown-linux-gnu", - "powerpc-unknown-linux-gnu", - "riscv32imc-unknown-none-elf", - "riscv64gc-unknown-none-elf", - "s390x-unknown-linux-gnu", - "thumbv7em-none-eabi", - "thumbv8m.main-none-eabi", - "wasm32-wasi", - "x86_64-apple-darwin", - "x86_64-apple-ios", - "x86_64-fuchsia", - "x86_64-linux-android", - "x86_64-unknown-freebsd", - "x86_64-unknown-linux-gnu", - "x86_64-unknown-nixos-gnu", - "x86_64-unknown-none" - ], "cfg(not(target_arch = \"wasm32\"))": [ "aarch64-apple-darwin", "aarch64-apple-ios", @@ -16662,7 +16572,7 @@ }, "direct_deps": [ "clap 4.4.18", - "miette 5.10.0", + "miette 7.2.0", "rattler_installs_packages 0.6.0" ], "direct_dev_deps": [] diff --git a/Cargo.lock b/Cargo.lock index d5d1e266..eb7fc6c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -324,7 +324,7 @@ dependencies = [ "hex", "libc", "memmap2 0.5.10", - "miette", + "miette 5.10.0", "reflink-copy", "serde", "serde_derive", @@ -1117,17 +1117,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" -[[package]] -name = "is-terminal" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" -dependencies = [ - "hermit-abi", - "rustix", - "windows-sys 0.52.0", -] - [[package]] name = "is_ci" version = "1.2.0" @@ -1243,12 +1232,23 @@ name = "miette" version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e" +dependencies = [ + "miette-derive 5.10.0", + "once_cell", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" dependencies = [ "backtrace", "backtrace-ext", - "is-terminal", - "miette-derive", - "once_cell", + "cfg-if", + "miette-derive 7.2.0", "owo-colors", "supports-color", "supports-hyperlinks", @@ -1270,6 +1270,17 @@ dependencies = [ "syn", ] +[[package]] +name = "miette-derive" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "mime" version = "0.3.17" @@ -1348,9 +1359,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "owo-colors" -version = "3.5.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" [[package]] name = "parking_lot" @@ -1532,7 +1543,7 @@ dependencies = [ name = "py" version = "0.1.0" dependencies = [ - "miette", + "miette 7.2.0", "rattler_installs_packages", ] @@ -1614,7 +1625,7 @@ dependencies = [ "include_dir", "indexmap 2.2.2", "itertools 0.12.1", - "miette", + "miette 5.10.0", "mime", "once_cell", "parking_lot", @@ -2029,7 +2040,7 @@ dependencies = [ "base64", "digest", "hex", - "miette", + "miette 5.10.0", "serde", "sha-1", "sha2", @@ -2057,31 +2068,24 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "supports-color" -version = "2.1.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89" +checksum = "9829b314621dfc575df4e409e79f9d6a66a3bd707ab73f23cb4aa3a854ac854f" dependencies = [ - "is-terminal", "is_ci", ] [[package]] name = "supports-hyperlinks" -version = "2.1.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84231692eb0d4d41e4cdd0cabfdd2e6cd9e255e65f80c9aa7c98dd502b4233d" -dependencies = [ - "is-terminal", -] +checksum = "2c0a1e5168041f5f3ff68ff7d95dcb9c8749df29f6e7e89ada40dd4c9de404ee" [[package]] name = "supports-unicode" -version = "2.1.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f850c19edd184a205e883199a261ed44471c81e39bd95b1357f5febbef00e77a" -dependencies = [ - "is-terminal", -] +checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" [[package]] name = "syn" @@ -2161,19 +2165,19 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.1.17" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ - "libc", - "winapi", + "rustix", + "windows-sys 0.48.0", ] [[package]] name = "textwrap" -version = "0.15.2" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" dependencies = [ "smawk", "unicode-linebreak", @@ -2449,7 +2453,7 @@ name = "unpack_bin" version = "0.1.0" dependencies = [ "clap", - "miette", + "miette 7.2.0", "py", ] @@ -2488,7 +2492,7 @@ name = "venv_bin" version = "0.1.0" dependencies = [ "clap", - "miette", + "miette 7.2.0", "py", ] diff --git a/Cargo.toml b/Cargo.toml index 123a2ede..3ef6cf89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,6 @@ members = [ "py/tools/unpack_bin", ] - [workspace.package] version = "0.1.0" categories = ["development-tools"] @@ -17,5 +16,8 @@ edition = "2021" readme = "README.md" rust-version = "1.74.1" +[workspace.dependencies] +miette = { version = "7.2", features = ["fancy"] } + [profile.release] strip = true diff --git a/docs/rules.md b/docs/rules.md index 97414efd..4eea2bdc 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -7,7 +7,8 @@ Public API re-exports ## py_binary_rule
-py_binary_rule(name, data, deps, env, imports, main, python_version, resolutions, srcs)
+py_binary_rule(name, data, deps, env, imports, main, package_collisions, python_version,
+               resolutions, srcs)
 
Run a Python program under Bazel. Most users should use the [py_binary macro](#py_binary) instead of loading this directly. @@ -23,6 +24,7 @@ Run a Python program under Bazel. Most users should use the [py_binary macro](#p | env | Environment variables to set when running the binary. | Dictionary: String -> String | optional | {} | | imports | List of import directories to be added to the PYTHONPATH. | List of strings | optional | [] | | main | Script to execute with the Python interpreter. | Label | required | | +| package_collisions | The action that should be taken when a symlink collision is encountered when creating the venv. A collision can occour when multiple packages providing the same file are installed into the venv. The possible values are:

* "error": When conflicting symlinks are found, an error is reported and venv creation halts. * "warning": When conflicting symlinks are found, an warning is reported, however venv creation continues. * "ignore": When conflicting symlinks are found, no message is reported and venv creation continues. | String | optional | "error" | | python_version | Whether to build this target and its transitive deps for a specific python version.

Note that setting this attribute alone will not be enough as the python toolchain for the desired version also needs to be registered in the WORKSPACE or MODULE.bazel file.

When using WORKSPACE, this may look like this,

 load("@rules_python//python:repositories.bzl", "py_repositories", "python_register_toolchains")

python_register_toolchains( name = "python_toolchain_3_8", python_version = "3.8.12", # setting set_python_version_constraint makes it so that only matches py_* rule # which has this exact version set in the python_version attribute. set_python_version_constraint = True, )

# It's important to register the default toolchain last it will match any py_* target. python_register_toolchains( name = "python_toolchain", python_version = "3.9", )


Configuring for MODULE.bazel may look like this:

 python = use_extension("@rules_python//python/extensions:python.bzl", "python") python.toolchain(python_version = "3.8.12", is_default = False) python.toolchain(python_version = "3.9", is_default = True) 
| String | optional | "" | | resolutions | FIXME | Dictionary: Label -> String | optional | {} | | srcs | Python source files. | List of labels | optional | [] | @@ -57,7 +59,8 @@ py_library(name, data< ## py_test_rule
-py_test_rule(name, data, deps, env, imports, main, python_version, resolutions, srcs)
+py_test_rule(name, data, deps, env, imports, main, package_collisions, python_version, resolutions,
+             srcs)
 
Run a Python program under Bazel. Most users should use the [py_test macro](#py_test) instead of loading this directly. @@ -73,6 +76,7 @@ Run a Python program under Bazel. Most users should use the [py_test macro](#py_ | env | Environment variables to set when running the binary. | Dictionary: String -> String | optional | {} | | imports | List of import directories to be added to the PYTHONPATH. | List of strings | optional | [] | | main | Script to execute with the Python interpreter. | Label | required | | +| package_collisions | The action that should be taken when a symlink collision is encountered when creating the venv. A collision can occour when multiple packages providing the same file are installed into the venv. The possible values are:

* "error": When conflicting symlinks are found, an error is reported and venv creation halts. * "warning": When conflicting symlinks are found, an warning is reported, however venv creation continues. * "ignore": When conflicting symlinks are found, no message is reported and venv creation continues. | String | optional | "error" | | python_version | Whether to build this target and its transitive deps for a specific python version.

Note that setting this attribute alone will not be enough as the python toolchain for the desired version also needs to be registered in the WORKSPACE or MODULE.bazel file.

When using WORKSPACE, this may look like this,

 load("@rules_python//python:repositories.bzl", "py_repositories", "python_register_toolchains")

python_register_toolchains( name = "python_toolchain_3_8", python_version = "3.8.12", # setting set_python_version_constraint makes it so that only matches py_* rule # which has this exact version set in the python_version attribute. set_python_version_constraint = True, )

# It's important to register the default toolchain last it will match any py_* target. python_register_toolchains( name = "python_toolchain", python_version = "3.9", )


Configuring for MODULE.bazel may look like this:

 python = use_extension("@rules_python//python/extensions:python.bzl", "python") python.toolchain(python_version = "3.8.12", is_default = False) python.toolchain(python_version = "3.9", is_default = True) 
| String | optional | "" | | resolutions | FIXME | Dictionary: Label -> String | optional | {} | | srcs | Python source files. | List of labels | optional | [] | diff --git a/examples/pytest/BUILD.bazel b/examples/pytest/BUILD.bazel index 380ab0c0..24c0d607 100644 --- a/examples/pytest/BUILD.bazel +++ b/examples/pytest/BUILD.bazel @@ -13,10 +13,11 @@ py_test( ], imports = ["../.."], main = ":__test__.py", + package_collisions = "warning", deps = [ ":__test__", - "@pypi_pytest//:pkg", "@pypi_ftfy//:pkg", "@pypi_neptune//:pkg", + "@pypi_pytest//:pkg", ], ) diff --git a/py/defs.bzl b/py/defs.bzl index 9a27b52f..a5e5a87e 100644 --- a/py/defs.bzl +++ b/py/defs.bzl @@ -30,12 +30,15 @@ def _py_binary_or_test(name, rule, srcs, main, deps = [], resolutions = {}, **kw **propagate_common_rule_attributes(kwargs) ) + package_collisions = kwargs.pop("package_collisions", None) + rule( name = name, srcs = srcs, main = main_target, deps = deps, resolutions = resolutions, + package_collisions = package_collisions, **kwargs ) @@ -44,6 +47,7 @@ def _py_binary_or_test(name, rule, srcs, main, deps = [], resolutions = {}, **kw deps = deps, imports = kwargs.get("imports"), resolutions = resolutions, + package_collisions = package_collisions, tags = ["manual"], testonly = kwargs.get("testonly", False), ) diff --git a/py/private/py_binary.bzl b/py/private/py_binary.bzl index 35acfd35..8fa8e74c 100644 --- a/py/private/py_binary.bzl +++ b/py/private/py_binary.bzl @@ -70,6 +70,7 @@ def _py_binary_rule_impl(ctx): "{{BASH_RLOCATION_FN}}": BASH_RLOCATION_FUNCTION, "{{INTERPRETER_FLAGS}}": " ".join(py_toolchain.flags), "{{VENV_TOOL}}": to_rlocation_path(ctx, venv_toolchain.bin), + "{{ARG_COLLISION_STRATEGY}}": ctx.attr.package_collisions, "{{ARG_PYTHON}}": to_rlocation_path(ctx, py_toolchain.python) if py_toolchain.runfiles_interpreter else py_toolchain.python.path, "{{ARG_VENV_NAME}}": ".{}.venv".format(ctx.attr.name), "{{ARG_VENV_PYTHON_VERSION}}": "{}.{}.{}".format( @@ -172,7 +173,18 @@ python = use_extension("@rules_python//python/extensions:python.bzl", "python") python.toolchain(python_version = "3.8.12", is_default = False) python.toolchain(python_version = "3.9", is_default = True) ``` -""" +""", + ), + "package_collisions": attr.string( + doc = """The action that should be taken when a symlink collision is encountered when creating the venv. +A collision can occour when multiple packages providing the same file are installed into the venv. The possible values are: + +* "error": When conflicting symlinks are found, an error is reported and venv creation halts. +* "warning": When conflicting symlinks are found, an warning is reported, however venv creation continues. +* "ignore": When conflicting symlinks are found, no message is reported and venv creation continues. + """, + default = "error", + values = ["error", "warning", "ignore"], ), "_run_tmpl": attr.label( allow_single_file = True, @@ -211,7 +223,7 @@ py_base = struct( PY_TOOLCHAIN, VENV_TOOLCHAIN, ], - cfg = _python_version_transition + cfg = _python_version_transition, ) py_binary = rule( @@ -220,7 +232,7 @@ py_binary = rule( attrs = py_base.attrs, toolchains = py_base.toolchains, executable = True, - cfg = py_base.cfg + cfg = py_base.cfg, ) py_test = rule( @@ -229,5 +241,5 @@ py_test = rule( attrs = py_base.attrs, toolchains = py_base.toolchains, test = True, - cfg = py_base.cfg + cfg = py_base.cfg, ) diff --git a/py/private/py_venv.bzl b/py/private/py_venv.bzl index 4395b870..691757d3 100644 --- a/py/private/py_venv.bzl +++ b/py/private/py_venv.bzl @@ -45,6 +45,7 @@ def _py_venv_rule_imp(ctx): "{{INTERPRETER_FLAGS}}": " ".join(py_toolchain.flags), "{{VENV_TOOL}}": to_rlocation_path(ctx, venv_toolchain.bin), "{{ARG_PYTHON}}": to_rlocation_path(ctx, py_toolchain.python) if py_toolchain.runfiles_interpreter else py_toolchain.python.path, + "{{ARG_COLLISION_STRATEGY}}": ctx.attr.package_collisions, "{{ARG_VENV_LOCATION}}": paths.join(ctx.attr.location, ctx.attr.venv_name), "{{ARG_VENV_PYTHON_VERSION}}": "{}.{}.{}".format( py_toolchain.interpreter_version_info.major, @@ -108,6 +109,17 @@ _py_venv = rule( "resolutions": attr.label_keyed_string_dict( doc = "FIXME", ), + "package_collisions": attr.string( + doc = """The action that should be taken when a symlink collision is encountered when creating the venv. +A collision can occour when multiple packages providing the same file are installed into the venv. The possible values are: + +* "error": When conflicting symlinks are found, an error is reported and venv creation halts. +* "warning": When conflicting symlinks are found, an warning is reported, however venv creation continues. +* "ignore": When conflicting symlinks are found, no message is reported and venv creation continues. + """, + default = "error", + values = ["error", "warning", "ignore"], + ), "_venv_tmpl": attr.label( allow_single_file = True, default = "//py/private:venv.tmpl.sh", diff --git a/py/private/run.tmpl.sh b/py/private/run.tmpl.sh index f6923202..1841937c 100644 --- a/py/private/run.tmpl.sh +++ b/py/private/run.tmpl.sh @@ -39,7 +39,8 @@ export VIRTUAL_ENV --location "${VIRTUAL_ENV}" \ --python "$(python_location)" \ --python-version "{{ARG_VENV_PYTHON_VERSION}}" \ - --pth-file "$(rlocation {{ARG_PTH_FILE}})" + --pth-file "$(rlocation {{ARG_PTH_FILE}})" \ + --collision-strategy "{{ARG_COLLISION_STRATEGY}}" PATH="${VIRTUAL_ENV}/bin:${PATH}" export PATH diff --git a/py/private/venv.tmpl.sh b/py/private/venv.tmpl.sh index 92936914..d129a215 100644 --- a/py/private/venv.tmpl.sh +++ b/py/private/venv.tmpl.sh @@ -29,4 +29,5 @@ VIRTUAL_ENV="$(alocation "${VENV_ROOT}/{{ARG_VENV_LOCATION}}")" --python "$(alocation $(rlocation {{ARG_PYTHON}}))" \ --python-version "{{ARG_VENV_PYTHON_VERSION}}" \ --pth-file "$(rlocation {{ARG_PTH_FILE}})" \ - --pth-entry-prefix "${RUNFILES_DIR}" + --pth-entry-prefix "${RUNFILES_DIR}" \ + --collision-strategy "{{ARG_COLLISION_STRATEGY}}" diff --git a/py/tests/virtual/django/BUILD.bazel b/py/tests/virtual/django/BUILD.bazel index 6241691c..b6f82fc2 100644 --- a/py/tests/virtual/django/BUILD.bazel +++ b/py/tests/virtual/django/BUILD.bazel @@ -29,6 +29,7 @@ py_library( py_binary( name = "manage", srcs = ["proj/manage.py"], + package_collisions = "warning", # Resolve django to the "standard" one from our requirements.txt resolutions = django_resolutions, deps = [ diff --git a/py/tests/virtual/django/requirements.in b/py/tests/virtual/django/requirements.in index 7f11a0dc..6f2040ba 100644 --- a/py/tests/virtual/django/requirements.in +++ b/py/tests/virtual/django/requirements.in @@ -1 +1,2 @@ -django==4.2.7 \ No newline at end of file +django==4.2.7 +PyQt6==6.6.1 \ No newline at end of file diff --git a/py/tests/virtual/django/requirements.txt b/py/tests/virtual/django/requirements.txt index dead34c3..c0cb2f74 100644 --- a/py/tests/virtual/django/requirements.txt +++ b/py/tests/virtual/django/requirements.txt @@ -12,6 +12,42 @@ django==4.2.7 \ --hash=sha256:8e0f1c2c2786b5c0e39fe1afce24c926040fad47c8ea8ad30aaf1188df29fc41 \ --hash=sha256:e1d37c51ad26186de355cbcec16613ebdabfa9689bbade9c538835205a8abbe9 # via -r py/tests/virtual/django/requirements.in +pyqt6==6.6.1 \ + --hash=sha256:03a656d5dc5ac31b6a9ad200f7f4f7ef49fa00ad7ce7a991b9bb691617141d12 \ + --hash=sha256:5aa0e833cb5a79b93813f8181d9f145517dd5a46f4374544bcd1e93a8beec537 \ + --hash=sha256:6b43878d0bbbcf8b7de165d305ec0cb87113c8930c92de748a11c473a6db5085 \ + --hash=sha256:9f158aa29d205142c56f0f35d07784b8df0be28378d20a97bcda8bd64ffd0379 + # via -r py/tests/virtual/django/requirements.in +pyqt6-qt6==6.7.2 \ + --hash=sha256:05f2c7d195d316d9e678a92ecac0252a24ed175bd2444cc6077441807d756580 \ + --hash=sha256:065415589219a2f364aba29d6a98920bb32810286301acbfa157e522d30369e3 \ + --hash=sha256:7f817efa86a0e8eda9152c85b73405463fbf3266299090f32bbb2266da540ead \ + --hash=sha256:b2d7e5ddb1b9764cd60f1d730fa7bf7a1f0f61b2630967c81761d3d0a5a8a2e0 \ + --hash=sha256:fc93945eaef4536d68bd53566535efcbe78a7c05c2a533790a8fd022bac8bfaa + # via pyqt6 +pyqt6-sip==13.8.0 \ + --hash=sha256:056af69d1d8d28d5968066ec5da908afd82fc0be07b67cf2b84b9f02228416ce \ + --hash=sha256:08dd81037a2864982ece2bf9891f3bf4558e247034e112993ea1a3fe239458cb \ + --hash=sha256:2559afa68825d08de09d71c42f3b6ad839dcc30f91e7c6d0785e07830d5541a5 \ + --hash=sha256:2f74cf3d6d9cab5152bd9f49d570b2dfb87553ebb5c4919abfde27f5b9fd69d4 \ + --hash=sha256:33d9b399fc9c9dc99496266842b0fb2735d924604774e97cf9b555667cc0fc59 \ + --hash=sha256:6bce6bc5870d9e87efe5338b1ee4a7b9d7d26cdd16a79a5757d80b6f25e71edc \ + --hash=sha256:755beb5d271d081e56618fb30342cdd901464f721450495cb7cb0212764da89e \ + --hash=sha256:7a0bbc0918eab5b6351735d40cf22cbfa5aa2476b55e0d5fe881aeed7d871c29 \ + --hash=sha256:7f84c472afdc7d316ff683f63129350d645ef82d9b3fd75a609b08472d1f7291 \ + --hash=sha256:835ed22eab977f75fd77e60d4ff308a1fa794b1d0c04849311f36d2a080cdf3b \ + --hash=sha256:9ea9223c94906efd68148f12ae45b51a21d67e86704225ddc92bce9c54e4d93c \ + --hash=sha256:a5c086b7c9c7996ea9b7522646cc24eebbf3591ec9dd38f65c0a3fdb0dbeaac7 \ + --hash=sha256:b1bf29e95f10a8a00819dac804ca7e5eba5fc1769adcd74c837c11477bf81954 \ + --hash=sha256:b203b6fbae4a8f2d27f35b7df46200057033d9ecd9134bcf30e3eab66d43572c \ + --hash=sha256:beaddc1ec96b342f4e239702f91802706a80cb403166c2da318cec4ad8b790cb \ + --hash=sha256:cd81144b0770084e8005d3a121c9382e6f9bc8d0bb320dd618718ffe5090e0e6 \ + --hash=sha256:cedd554c643e54c4c2e12b5874781a87441a1b405acf3650a4a2e1df42aae231 \ + --hash=sha256:d8b22a6850917c68ce83fc152a8b606ecb2efaaeed35be53110468885d6cdd9d \ + --hash=sha256:dd168667addf01f8a4b0fa7755323e43e4cd12ca4bade558c61f713a5d48ba1a \ + --hash=sha256:f57275b5af774529f9838adcfb58869ba3ebdaf805daea113bb0697a96a3f3cb \ + --hash=sha256:fbb249b82c53180f1420571ece5dc24fea1188ba435923edd055599dffe7abfb + # via pyqt6 sqlparse==0.4.4 \ --hash=sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3 \ --hash=sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c diff --git a/py/tools/py/Cargo.toml b/py/tools/py/Cargo.toml index d2cac748..978c9be5 100644 --- a/py/tools/py/Cargo.toml +++ b/py/tools/py/Cargo.toml @@ -12,5 +12,5 @@ rust-version.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -miette = { version = "5.10", features = ["fancy"] } +miette = { workspace = true } rattler_installs_packages = { git = "https://github.com/prefix-dev/rip", rev = "b047c9ec0b42125a67d35346f08b7e7848ac24f4", default-features = false, features = ["rustls-tls"] } \ No newline at end of file diff --git a/py/tools/py/src/lib.rs b/py/tools/py/src/lib.rs index 361edeeb..bd29ce1c 100644 --- a/py/tools/py/src/lib.rs +++ b/py/tools/py/src/lib.rs @@ -7,4 +7,4 @@ pub use unpack::unpack_wheel; pub use venv::create_venv; pub(crate) use interpreter::Interpreter; -pub use pth::PthFile; +pub use pth::{PthFile, SymlinkCollisionResolutionStrategy}; diff --git a/py/tools/py/src/pth.rs b/py/tools/py/src/pth.rs index 15d364f2..aac713b5 100644 --- a/py/tools/py/src/pth.rs +++ b/py/tools/py/src/pth.rs @@ -4,7 +4,30 @@ use std::{ path::{Path, PathBuf}, }; -use miette::{miette, Context, IntoDiagnostic}; +use miette::{miette, Context, IntoDiagnostic, LabeledSpan, MietteDiagnostic, Severity}; + +/// Strategy that will be used when creating the virtual env symlink and +/// a collision is found. +#[derive(Default, Debug)] +pub enum SymlinkCollisionResolutionStrategy { + /// Collisions cause a hard error. + #[default] + Error, + + /// The last file to provide a target wins. + /// If inner is true, then a warning is produced, otherwise the last target silently wins. + LastWins(bool), +} + +/// Options for the creation of the `site-packages` folder layout. +#[derive(Default, Debug)] +pub struct SitePackageOptions { + /// Destination path, where the `site-package` folder lives. + pub dest: PathBuf, + + /// Collision strategy determining the action taken when sylinks in the venv collide. + pub collision_strategy: SymlinkCollisionResolutionStrategy, +} pub struct PthFile { pub src: PathBuf, @@ -19,7 +42,9 @@ impl PthFile { } } - pub fn set_up_site_packages(&self, dest: &Path) -> miette::Result<()> { + pub fn set_up_site_packages(&self, opts: SitePackageOptions) -> miette::Result<()> { + let dest = &opts.dest; + let source_pth = File::open(self.src.as_path()) .into_diagnostic() .wrap_err("Unable to open source .pth file")?; @@ -47,7 +72,7 @@ impl PthFile { .canonicalize() .into_diagnostic() .wrap_err("Unable to get full source dir path")?; - create_symlinks(&src_dir, &src_dir, &dest)?; + create_symlinks(&src_dir, &src_dir, &dest, &opts.collision_strategy)?; } _ => { writeln!(writer, "{}", entry.to_string_lossy()) @@ -61,7 +86,12 @@ impl PthFile { } } -fn create_symlinks(dir: &Path, root_dir: &Path, dst_dir: &Path) -> miette::Result<()> { +fn create_symlinks( + dir: &Path, + root_dir: &Path, + dst_dir: &Path, + collision_strategy: &SymlinkCollisionResolutionStrategy, +) -> miette::Result<()> { // Create this directory at the destination. let tgt_dir = dst_dir.join(dir.strip_prefix(root_dir).unwrap()); std::fs::create_dir_all(&tgt_dir) @@ -90,50 +120,24 @@ fn create_symlinks(dir: &Path, root_dir: &Path, dst_dir: &Path) -> miette::Resul // `__init__.py` files in the root site-packages directory. The site-packages directory // itself is not a regular package and is not supposed to have an `__init__.py` file. if path.is_dir() { - create_symlinks(&path, root_dir, dst_dir)?; + create_symlinks(&path, root_dir, dst_dir, collision_strategy)?; } else if dir != root_dir || entry.file_name() != "__init__.py" { - create_symlink(&entry, root_dir, dst_dir)?; + create_symlink(&entry, root_dir, dst_dir, collision_strategy)?; } } Ok(()) } -fn create_symlink(e: &DirEntry, root_dir: &Path, dst_dir: &Path) -> miette::Result<()> { +fn create_symlink( + e: &DirEntry, + root_dir: &Path, + dst_dir: &Path, + collision_strategy: &SymlinkCollisionResolutionStrategy, +) -> miette::Result<()> { let tgt = e.path(); let link = dst_dir.join(tgt.strip_prefix(root_dir).unwrap()); - // If the link already exists, do not return an error if the link is for an `__init__.py` file - // with the same content as the new destination. Some packages that should ideally be namespace - // packages have copies of `__init__.py` files in their distributions. For example, all the - // Nvidia PyPI packages have the same `nvidia/__init__.py`. So we need to either overwrite the - // previous symlink, or check that the new location also has the same content. - if link.exists() - && link.file_name().is_some_and(|x| x == "__init__.py") - && is_same_file(link.as_path(), tgt.as_path())? - { - return Ok(()); - } - - // Is the link at the root of the `site-packages`? - // We can get into situations where files in wheels are at the root of the wheel, - // while this is valid, it leads to errors being thrown here as the symlinks will - // already exist. In `pip`, the last file wins, so the logic is the same here. - if link.exists() && link.parent().is_some_and(|x| x.ends_with("site-packages")) { - // File is being placed at the root of the `site-packages`, remove the link. - fs::remove_file(&link) - .into_diagnostic() - .wrap_err( - miette!( - "Unable to remove conflicting symlink in site-packages root. Existing symlink {} conflicts with new target {}", - link.to_string_lossy(), - tgt.to_string_lossy() - ) - )? - } - - // If the link already exists, then there is going to be a conflict. - // Bail early here before attempting to link as to provide a better error message. - if link.exists() { + fn conflict_report(link: &Path, tgt: &Path, severity: Severity) -> miette::Report { let path_to_conflict = link .to_str() .and_then(|s| s.split_once("site-packages/")) @@ -144,11 +148,53 @@ fn create_symlink(e: &DirEntry, root_dir: &Path, dst_dir: &Path) -> miette::Resu .and_then(|s| s.split_once(".runfiles/")) .map(|s| s.1) .unwrap(); - return Err( - miette!(format!("site-packages/{}", path_to_conflict)) - .wrap_err(format!("{}", next_conflict)) - .wrap_err("Conflicting symlinks found when attempting to create venv. More than one package provides the file at these paths") - ); + + let mut diag = MietteDiagnostic::new("Conflicting symlinks found when attempting to create venv. More than one package provides the file at these paths".to_string()) + .with_severity(severity) + .with_labels([ + LabeledSpan::at(0..path_to_conflict.len(), "Existing file in virtual environment"), + LabeledSpan::at((path_to_conflict.len() + 1)..(path_to_conflict.len() + 1 + next_conflict.len()), "Next file to link"), + ]); + + diag = if severity == Severity::Error { + diag.with_help("Set `package_collisions = \"warning\"` on the binary or test rule to downgrade this error to a warning") + } else { + diag.with_help("Set `package_collisions = \"ignore\"` on the binary or test rule to ignore this warning") + }; + + miette!(diag).with_source_code(format!("{}\n{}", path_to_conflict, next_conflict)) + } + + if link.exists() { + // If the link already exists and is the same file, then there is no need to link this new one. + // Assume that if the files are the same, then there is no need to warn or error. + if is_same_file(&link, &tgt)? { + return Ok(()); + } + + match collision_strategy { + SymlinkCollisionResolutionStrategy::LastWins(warn) => { + fs::remove_file(&link) + .into_diagnostic() + .wrap_err( + miette!( + "Unable to remove conflicting symlink in site-packages. Existing symlink {} conflicts with new target {}", + link.to_string_lossy(), + tgt.to_string_lossy() + ) + )?; + + if *warn { + let conflicts = conflict_report(&link, &tgt, Severity::Warning); + eprintln!("{:?}", conflicts); + } + } + SymlinkCollisionResolutionStrategy::Error => { + // If the link already exists, then there is going to be a conflict. + let conflicts = conflict_report(&link, &tgt, Severity::Error); + return Err(conflicts); + } + }; } std::os::unix::fs::symlink(&tgt, &link) diff --git a/py/tools/py/src/unpack.rs b/py/tools/py/src/unpack.rs index c8df83bf..992dbcf4 100644 --- a/py/tools/py/src/unpack.rs +++ b/py/tools/py/src/unpack.rs @@ -1,6 +1,6 @@ use std::path::Path; -use miette::IntoDiagnostic; +use miette::{miette, IntoDiagnostic, Result}; use rattler_installs_packages::{ artifacts::wheel::UnpackWheelOptions, artifacts::Wheel, types::PackageName, }; @@ -13,7 +13,7 @@ pub fn unpack_wheel( location: &Path, pkg_name: &str, wheel: &Path, -) -> miette::Result<()> { +) -> Result<()> { let interpreter = Interpreter::new(python, version)?; let python_executable = interpreter.executable()?; let install_paths = interpreter.install_paths(false); @@ -25,7 +25,8 @@ pub fn unpack_wheel( }; let package_name: PackageName = pkg_name.parse().unwrap(); - let wheel = Wheel::from_path(wheel, &package_name.into())?; + let wheel = Wheel::from_path(wheel, &package_name.into()) + .map_err(|_| miette!("Failed to create wheel from path"))?; wheel .unpack( diff --git a/py/tools/py/src/venv.rs b/py/tools/py/src/venv.rs index 14704275..8c3ad357 100644 --- a/py/tools/py/src/venv.rs +++ b/py/tools/py/src/venv.rs @@ -6,13 +6,17 @@ use std::{ use miette::{Context, IntoDiagnostic}; use rattler_installs_packages::python_env::VEnv; -use crate::{Interpreter, PthFile}; +use crate::{ + pth::{SitePackageOptions, SymlinkCollisionResolutionStrategy}, + Interpreter, PthFile, +}; pub fn create_venv( python: &Path, version: &str, location: &Path, pth_file: Option, + collision_strategy: SymlinkCollisionResolutionStrategy, ) -> miette::Result<()> { // Parse and find the interpreter to use. // Do this first so that incase we can't find or parse the version, we don't @@ -51,7 +55,12 @@ pub fn create_venv( .into_diagnostic()?; if let Some(pth) = pth_file { - pth.set_up_site_packages(&venv_location.join(install_paths.platlib()))? + let site_packages_options = SitePackageOptions { + dest: venv_location.join(install_paths.platlib()), + collision_strategy, + }; + + pth.set_up_site_packages(site_packages_options)? } Ok(()) diff --git a/py/tools/unpack_bin/Cargo.toml b/py/tools/unpack_bin/Cargo.toml index d360fcb8..cc9aaa14 100644 --- a/py/tools/unpack_bin/Cargo.toml +++ b/py/tools/unpack_bin/Cargo.toml @@ -17,5 +17,5 @@ path = "src/main.rs" [dependencies] clap = { version = "4.1.11", features = ["derive"] } -miette = { version = "5.10", features = ["fancy"] } +miette = { workspace = true } py = { path = "../py" } diff --git a/py/tools/venv_bin/Cargo.toml b/py/tools/venv_bin/Cargo.toml index c5774c9f..cc747d1e 100644 --- a/py/tools/venv_bin/Cargo.toml +++ b/py/tools/venv_bin/Cargo.toml @@ -17,5 +17,5 @@ path = "src/main.rs" [dependencies] clap = { version = "4.1.11", features = ["derive"] } -miette = { version = "5.10", features = ["fancy"] } +miette = { workspace = true } py = { path = "../py" } \ No newline at end of file diff --git a/py/tools/venv_bin/src/main.rs b/py/tools/venv_bin/src/main.rs index e23c8dad..b6a307ba 100644 --- a/py/tools/venv_bin/src/main.rs +++ b/py/tools/venv_bin/src/main.rs @@ -5,6 +5,28 @@ use miette::Context; use py; +#[derive(clap::ValueEnum, Clone, Debug, Default)] +enum SymlinkCollisionStrategy { + #[default] + Error, + Warning, + Ignore, +} + +impl Into for SymlinkCollisionStrategy { + fn into(self) -> py::SymlinkCollisionResolutionStrategy { + match self { + SymlinkCollisionStrategy::Error => py::SymlinkCollisionResolutionStrategy::Error, + SymlinkCollisionStrategy::Warning => { + py::SymlinkCollisionResolutionStrategy::LastWins(true) + } + SymlinkCollisionStrategy::Ignore => { + py::SymlinkCollisionResolutionStrategy::LastWins(false) + } + } + } +} + #[derive(Parser, Debug)] struct VenvArgs { /// Source Python interpreter path to symlink into the venv. @@ -27,6 +49,12 @@ struct VenvArgs { /// Prefix to append to each .pth path entry. #[arg(long)] pth_entry_prefix: Option, + + /// The collision strategy to use when multiple packages providing the same file are + /// encountered when creating the venv. + /// If none is given, an error will be thrown. + #[arg(long)] + collision_strategy: Option, } fn venv_cmd_handler(args: VenvArgs) -> miette::Result<()> { @@ -36,6 +64,7 @@ fn venv_cmd_handler(args: VenvArgs) -> miette::Result<()> { &args.python_version, &args.location, Some(pth_file), + args.collision_strategy.unwrap_or_default().into(), ) }