Skip to content

Commit

Permalink
Add a uv-python shim executable
Browse files Browse the repository at this point in the history
  • Loading branch information
zanieb committed Sep 24, 2024
1 parent 538b0f1 commit e0ecb54
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 1 deletion.
10 changes: 10 additions & 0 deletions .github/workflows/build-binaries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ jobs:
${{ env.MODULE_NAME }} --help
python -m ${{ env.MODULE_NAME }} --help
uvx --help
uv-python --help
- name: "Upload sdist"
uses: actions/upload-artifact@v4
with:
Expand Down Expand Up @@ -128,6 +129,7 @@ jobs:
${{ env.MODULE_NAME }} --help
python -m ${{ env.MODULE_NAME }} --help
uvx --help
uv-python --help
- name: "Upload wheels"
uses: actions/upload-artifact@v4
with:
Expand Down Expand Up @@ -185,6 +187,7 @@ jobs:
${{ env.MODULE_NAME }} --help
python -m ${{ env.MODULE_NAME }} --help
uvx --help
uv-python --help
- name: "Upload wheels"
uses: actions/upload-artifact@v4
with:
Expand Down Expand Up @@ -251,6 +254,7 @@ jobs:
${{ env.MODULE_NAME }} --help
python -m ${{ env.MODULE_NAME }} --help
uvx --help
uv-python --help
- name: "Upload wheels"
uses: actions/upload-artifact@v4
with:
Expand Down Expand Up @@ -325,6 +329,7 @@ jobs:
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
${{ env.MODULE_NAME }} --help
uvx --help
uv-python --help
- name: "Upload wheels"
uses: actions/upload-artifact@v4
with:
Expand Down Expand Up @@ -394,6 +399,7 @@ jobs:
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
${{ env.MODULE_NAME }} --help
uvx --help
uv-python --help
- name: "Upload wheels"
uses: actions/upload-artifact@v4
with:
Expand Down Expand Up @@ -476,6 +482,7 @@ jobs:
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
${{ env.MODULE_NAME }} --help
uvx --help
uv-python --help
- name: "Upload wheels"
uses: actions/upload-artifact@v4
with:
Expand Down Expand Up @@ -540,6 +547,7 @@ jobs:
.venv/bin/pip install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
.venv/bin/${{ env.MODULE_NAME }} --help
.venv/bin/uvx --help
.venv/bin/uv-python --help
- name: "Upload wheels"
uses: actions/upload-artifact@v4
with:
Expand Down Expand Up @@ -615,6 +623,7 @@ jobs:
.venv/bin/pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
.venv/bin/${{ env.MODULE_NAME }} --help
.venv/bin/uvx --help
.venv/bin/uv-python --help
- uses: uraimo/run-on-arch-action@v2
name: Test wheel (manylinux)
if: matrix.platform.arch == 'aarch64'
Expand All @@ -630,6 +639,7 @@ jobs:
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
${{ env.MODULE_NAME }} --help
uvx --help
uv-python --help
- name: "Upload wheels"
uses: actions/upload-artifact@v4
with:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ jobs:
FROM ${BASE_IMAGE}
COPY --from=${{ env.UV_BASE_IMG }}:latest /uv /usr/local/bin/uv
COPY --from=${{ env.UV_BASE_IMG }}:latest /uvx /usr/local/bin/uvx
COPY --from=${{ env.UV_BASE_IMG }}:latest /uv-python /usr/local/bin/uv-python
ENTRYPOINT []
CMD ["/usr/local/bin/uv"]
EOF
Expand Down
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,16 @@ RUN rustup target add $(cat rust_target.txt)
COPY crates crates
COPY ./Cargo.toml Cargo.toml
COPY ./Cargo.lock Cargo.lock
RUN cargo zigbuild --bin uv --bin uvx --target $(cat rust_target.txt) --release
RUN cargo zigbuild --bin uv --bin uvx --bin uv-python --target $(cat rust_target.txt) --release
RUN cp target/$(cat rust_target.txt)/release/uv /uv \
&& cp target/$(cat rust_target.txt)/release/uvx /uvx
&& cp target/$(cat rust_target.txt)/release/uv-python /uv-python
# TODO(konsti): Optimize binary size, with a version that also works when cross compiling
# RUN strip --strip-all /uv

FROM scratch
COPY --from=build /uv /uv
COPY --from=build /uvx /uvx
COPY --from=build /uv-python /uv-python
WORKDIR /io
ENTRYPOINT ["/uv"]
1 change: 1 addition & 0 deletions crates/uv-python/src/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,7 @@ impl PythonRequest {
if let Ok(version) = VersionRequest::from_str(value) {
return Self::Version(version);
}

// e.g. `python3.12.1`
if let Some(remainder) = value.strip_prefix("python") {
if let Ok(version) = VersionRequest::from_str(remainder) {
Expand Down
92 changes: 92 additions & 0 deletions crates/uv/src/bin/uv-python.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
use std::convert::Infallible;
use std::os::unix::ffi::OsStringExt;
use std::{
ffi::OsString,
process::{Command, ExitCode, ExitStatus},
};

/// Spawns a command exec style.
fn exec_spawn(cmd: &mut Command) -> std::io::Result<Infallible> {
#[cfg(unix)]
{
use std::os::unix::process::CommandExt;
let err = cmd.exec();
Err(err)
}
#[cfg(windows)]
{
cmd.stdin(std::process::Stdio::inherit());
let status = cmd.status()?;

#[allow(clippy::exit)]
std::process::exit(status.code().unwrap())
}
}

#[derive(Debug)]
enum Error {
Io(std::io::Error),
Which(which::Error),
NoInterpreter(String),
}

impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Io(err) => write!(f, "{err}"),
Self::Which(err) => write!(f, "Failed to find uv binary: {err}"),
Self::NoInterpreter(inner) => write!(f, "{inner}"),
}
}
}

/// Parse a `+<request>` Python version request from the arguments.
fn parse_python_request(mut args: Vec<OsString>) -> (Vec<OsString>, Option<String>) {
let Some(arg) = args.first() else {
return (args, None);
};
let arg = arg.to_string_lossy();
let Some(version) = arg.strip_prefix('+') else {
return (args, None);
};
let version = version.to_string();
(args.split_off(1), Some(version))
}

fn run() -> Result<ExitStatus, Error> {
let args = std::env::args_os().skip(1).collect::<Vec<_>>();
let (args, request) = parse_python_request(args);
let uv = which::which("uv").map_err(Error::Which)?;
let mut cmd = Command::new(uv);
let uv_args = ["python", "find"]
.iter()
.copied()
.chain(request.iter().map(std::string::String::as_str));
cmd.args(uv_args);
let output = cmd.output().map_err(Error::Io)?;
if !output.status.success() {
return Err(Error::NoInterpreter(
OsString::from_vec(output.stderr)
.to_string_lossy()
.to_string(),
));
}
let python =
std::path::PathBuf::from(OsString::from_vec(output.stdout).to_string_lossy().trim());
let mut cmd = Command::new(python);
cmd.args(&args);
match exec_spawn(&mut cmd).map_err(Error::Io)? {}
}

#[allow(clippy::print_stderr)]
fn main() -> ExitCode {
let result = run();
match result {
// Fail with 2 if the status cannot be cast to an exit code
Ok(status) => u8::try_from(status.code().unwrap_or(2)).unwrap_or(2).into(),
Err(err) => {
eprintln!("error: {err}");
ExitCode::from(2)
}
}
}

0 comments on commit e0ecb54

Please sign in to comment.