Skip to content

Commit

Permalink
Add GraalPy support
Browse files Browse the repository at this point in the history
  • Loading branch information
timfel committed Jul 17, 2024
1 parent 545bbf9 commit 7bb3ef8
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 1 deletion.
14 changes: 14 additions & 0 deletions crates/platform-tags/src/tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ impl std::fmt::Display for Tags {
enum Implementation {
CPython { gil_disabled: bool },
PyPy,
GraalPy,
Pyston,
}

Expand All @@ -310,6 +311,8 @@ impl Implementation {
Self::CPython { .. } => format!("cp{}{}", python_version.0, python_version.1),
// Ex) `pp39`
Self::PyPy => format!("pp{}{}", python_version.0, python_version.1),
// Ex) `graalpy310`
Self::GraalPy => format!("graalpy{}{}", python_version.0, python_version.1),
// Ex) `pt38``
Self::Pyston => format!("pt{}{}", python_version.0, python_version.1),
}
Expand Down Expand Up @@ -342,6 +345,16 @@ impl Implementation {
implementation_version.0,
implementation_version.1
),
// Ex) `graalpy310_graalpy240_310_native
Self::GraalPy => format!(
"graalpy{}{}_graalpy{}{}_{}{}_native",
python_version.0,
python_version.1,
implementation_version.0,
implementation_version.1,
python_version.0,
python_version.1
),
// Ex) `pyston38-pyston_23`
Self::Pyston => format!(
"pyston{}{}-pyston_{}{}",
Expand All @@ -361,6 +374,7 @@ impl Implementation {
// Known and supported implementations.
"cpython" => Ok(Self::CPython { gil_disabled }),
"pypy" => Ok(Self::PyPy),
"graalpy" => Ok(Self::GraalPy),
"pyston" => Ok(Self::Pyston),
// Known but unsupported implementations.
"python" => Err(TagsError::UnsupportedImplementation(name.to_string())),
Expand Down
48 changes: 48 additions & 0 deletions crates/uv-python/src/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1640,6 +1640,14 @@ mod tests {
PythonRequest::parse("pp"),
PythonRequest::Implementation(ImplementationName::PyPy)
);
assert_eq!(
PythonRequest::parse("graalpy"),
PythonRequest::Implementation(ImplementationName::GraalPy)
);
assert_eq!(
PythonRequest::parse("gp"),
PythonRequest::Implementation(ImplementationName::GraalPy)
);
assert_eq!(
PythonRequest::parse("cp"),
PythonRequest::Implementation(ImplementationName::CPython)
Expand All @@ -1658,6 +1666,20 @@ mod tests {
VersionRequest::from_str("3.10").unwrap()
)
);
assert_eq!(
PythonRequest::parse("graalpy3.10"),
PythonRequest::ImplementationVersion(
ImplementationName::GraalPy,
VersionRequest::from_str("3.10").unwrap()
)
);
assert_eq!(
PythonRequest::parse("gp310"),
PythonRequest::ImplementationVersion(
ImplementationName::GraalPy,
VersionRequest::from_str("3.10").unwrap()
)
);
assert_eq!(
PythonRequest::parse("cp38"),
PythonRequest::ImplementationVersion(
Expand All @@ -1679,6 +1701,20 @@ mod tests {
VersionRequest::from_str("3.10").unwrap()
)
);
assert_eq!(
PythonRequest::parse("[email protected]"),
PythonRequest::ImplementationVersion(
ImplementationName::GraalPy,
VersionRequest::from_str("3.10").unwrap()
)
);
assert_eq!(
PythonRequest::parse("graalpy310"),
PythonRequest::ImplementationVersion(
ImplementationName::GraalPy,
VersionRequest::from_str("3.10").unwrap()
)
);

let tempdir = TempDir::new().unwrap();
assert_eq!(
Expand Down Expand Up @@ -1749,6 +1785,18 @@ mod tests {
.to_canonical_string(),
"[email protected]"
);
assert_eq!(
PythonRequest::Implementation(ImplementationName::GraalPy).to_canonical_string(),
"graalpy"
);
assert_eq!(
PythonRequest::ImplementationVersion(
ImplementationName::GraalPy,
VersionRequest::from_str("3.10").unwrap()
)
.to_canonical_string(),
"[email protected]"
);

let tempdir = TempDir::new().unwrap();
assert_eq!(
Expand Down
6 changes: 5 additions & 1 deletion crates/uv-python/src/implementation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub enum ImplementationName {
#[default]
CPython,
PyPy,
GraalPy,
}

#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
Expand All @@ -25,13 +26,14 @@ pub enum LenientImplementationName {

impl ImplementationName {
pub(crate) fn possible_names() -> impl Iterator<Item = &'static str> {
["cpython", "pypy", "cp", "pp"].into_iter()
["cpython", "pypy", "graalpy", "cp", "pp", "gp"].into_iter()
}

pub fn pretty(self) -> &'static str {
match self {
Self::CPython => "CPython",
Self::PyPy => "PyPy",
Self::GraalPy => "GraalPy",
}
}
}
Expand All @@ -50,6 +52,7 @@ impl From<&ImplementationName> for &'static str {
match v {
ImplementationName::CPython => "cpython",
ImplementationName::PyPy => "pypy",
ImplementationName::GraalPy => "graalpy",
}
}
}
Expand All @@ -73,6 +76,7 @@ impl FromStr for ImplementationName {
match s.to_ascii_lowercase().as_str() {
"cpython" | "cp" => Ok(Self::CPython),
"pypy" | "pp" => Ok(Self::PyPy),
"graalpy" | "gp" => Ok(Self::GraalPy),
_ => Err(Error::UnknownImplementation(s.to_string())),
}
}
Expand Down
156 changes: 156 additions & 0 deletions crates/uv-python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1869,4 +1869,160 @@ mod tests {

Ok(())
}

#[test]
fn find_python_graalpy() -> Result<()> {
let mut context = TestContext::new()?;

context.add_python_interpreters(&[(
true,
ImplementationName::GraalPy,
"graalpy",
"3.10.0",
)])?;
let result = context.run(|| {
find_python_installation(
&PythonRequest::Any,
EnvironmentPreference::Any,
PythonPreference::OnlySystem,
&context.cache,
)
})?;
assert!(
matches!(result, Err(PythonNotFound { .. })),
"We should not the graalpy interpreter if not named `python` or requested; got {result:?}"
);

// But we should find it
context.reset_search_path();
context.add_python_interpreters(&[(
true,
ImplementationName::GraalPy,
"python",
"3.10.1",
)])?;
let python = context.run(|| {
find_python_installation(
&PythonRequest::Any,
EnvironmentPreference::Any,
PythonPreference::OnlySystem,
&context.cache,
)
})??;
assert_eq!(
python.interpreter().python_full_version().to_string(),
"3.10.1",
"We should find the graalpy interpreter if it's the only one"
);

let python = context.run(|| {
find_python_installation(
&PythonRequest::parse("graalpy"),
EnvironmentPreference::Any,
PythonPreference::OnlySystem,
&context.cache,
)
})??;
assert_eq!(
python.interpreter().python_full_version().to_string(),
"3.10.1",
"We should find the graalpy interpreter if it's requested"
);

Ok(())
}

#[test]
fn find_python_graalpy_request_ignores_cpython() -> Result<()> {
let mut context = TestContext::new()?;
context.add_python_interpreters(&[
(true, ImplementationName::CPython, "python", "3.10.0"),
(true, ImplementationName::GraalPy, "graalpy", "3.10.1"),
])?;

let python = context.run(|| {
find_python_installation(
&PythonRequest::parse("graalpy"),
EnvironmentPreference::Any,
PythonPreference::OnlySystem,
&context.cache,
)
})??;
assert_eq!(
python.interpreter().python_full_version().to_string(),
"3.10.1",
"We should skip the CPython interpreter"
);

let python = context.run(|| {
find_python_installation(
&PythonRequest::Any,
EnvironmentPreference::Any,
PythonPreference::OnlySystem,
&context.cache,
)
})??;
assert_eq!(
python.interpreter().python_full_version().to_string(),
"3.10.0",
"We should take the first interpreter without a specific request"
);

Ok(())
}

#[test]
fn find_python_graalpy_prefers_executable_with_implementation_name() -> Result<()> {
let mut context = TestContext::new()?;

// We should prefer `graalpy` executables over `python` executables in the same directory
// even if they are both graalpy
TestContext::create_mock_interpreter(
&context.tempdir.join("python"),
&PythonVersion::from_str("3.10.0").unwrap(),
ImplementationName::GraalPy,
true,
)?;
TestContext::create_mock_interpreter(
&context.tempdir.join("graalpy"),
&PythonVersion::from_str("3.10.1").unwrap(),
ImplementationName::GraalPy,
true,
)?;
context.add_to_search_path(context.tempdir.to_path_buf());

let python = context.run(|| {
find_python_installation(
&PythonRequest::parse("[email protected]"),
EnvironmentPreference::Any,
PythonPreference::OnlySystem,
&context.cache,
)
})??;
assert_eq!(
python.interpreter().python_full_version().to_string(),
"3.10.1",
);

// But `python` executables earlier in the search path will take precedence
context.reset_search_path();
context.add_python_interpreters(&[
(true, ImplementationName::GraalPy, "python", "3.10.2"),
(true, ImplementationName::GraalPy, "graalpy", "3.10.3"),
])?;
let python = context.run(|| {
find_python_installation(
&PythonRequest::parse("[email protected]"),
EnvironmentPreference::Any,
PythonPreference::OnlySystem,
&context.cache,
)
})??;
assert_eq!(
python.interpreter().python_full_version().to_string(),
"3.10.2",
);

Ok(())
}
}
4 changes: 4 additions & 0 deletions crates/uv-virtualenv/src/bare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ pub fn create_bare_venv(
)?;
uv_fs::replace_symlink("python", scripts.join("pypy"))?;
}

if interpreter.markers().implementation_name() == "graalpy" {
uv_fs::replace_symlink("python", scripts.join("graalpy"))?;
}
}

// No symlinking on Windows, at least not on a regular non-dev non-admin Windows install.
Expand Down

0 comments on commit 7bb3ef8

Please sign in to comment.