mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-14 12:29:04 +00:00
Allow selection of debug build interpreters (#11520)
Some checks are pending
CI / typos (push) Waiting to run
CI / Determine changes (push) Waiting to run
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / ecosystem test | pydantic/pydantic-core (push) Blocked by required conditions
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | aarch64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / mkdocs (push) Waiting to run
CI / build binary | linux libc (push) Blocked by required conditions
CI / build binary | linux aarch64 (push) Blocked by required conditions
CI / build binary | linux musl (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows x86_64 (push) Blocked by required conditions
CI / build binary | windows aarch64 (push) Blocked by required conditions
CI / build binary | msrv (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / smoke test | linux (push) Blocked by required conditions
CI / smoke test | linux aarch64 (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / smoke test | macos (push) Blocked by required conditions
CI / smoke test | windows x86_64 (push) Blocked by required conditions
CI / smoke test | windows aarch64 (push) Blocked by required conditions
CI / integration test | activate nushell venv (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | deadsnakes python3.9 on ubuntu (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | aarch64 windows implicit (push) Blocked by required conditions
CI / integration test | aarch64 windows explicit (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | pyodide on ubuntu (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | free-threaded python on github actions (push) Blocked by required conditions
CI / integration test | pyenv on wsl x86-64 (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | registries (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / integration test | uv_build (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | x86-64 python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
zizmor / Run zizmor (push) Waiting to run
Some checks are pending
CI / typos (push) Waiting to run
CI / Determine changes (push) Waiting to run
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / ecosystem test | pydantic/pydantic-core (push) Blocked by required conditions
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | aarch64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / mkdocs (push) Waiting to run
CI / build binary | linux libc (push) Blocked by required conditions
CI / build binary | linux aarch64 (push) Blocked by required conditions
CI / build binary | linux musl (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows x86_64 (push) Blocked by required conditions
CI / build binary | windows aarch64 (push) Blocked by required conditions
CI / build binary | msrv (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / smoke test | linux (push) Blocked by required conditions
CI / smoke test | linux aarch64 (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / smoke test | macos (push) Blocked by required conditions
CI / smoke test | windows x86_64 (push) Blocked by required conditions
CI / smoke test | windows aarch64 (push) Blocked by required conditions
CI / integration test | activate nushell venv (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | deadsnakes python3.9 on ubuntu (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | aarch64 windows implicit (push) Blocked by required conditions
CI / integration test | aarch64 windows explicit (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | pyodide on ubuntu (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | free-threaded python on github actions (push) Blocked by required conditions
CI / integration test | pyenv on wsl x86-64 (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | registries (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / integration test | uv_build (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | x86-64 python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
zizmor / Run zizmor (push) Waiting to run
Extends the `PythonVariant` logic to support interpreters with the debug flag enabled.
This commit is contained in:
parent
8917b00fd9
commit
8f3583a6e6
17 changed files with 8015 additions and 109 deletions
|
@ -6,13 +6,6 @@ fn process_json(data: &serde_json::Value) -> serde_json::Value {
|
|||
|
||||
if let Some(obj) = data.as_object() {
|
||||
for (key, value) in obj {
|
||||
if let Some(variant) = value.get("variant") {
|
||||
// Exclude debug variants for now, we don't support them
|
||||
if variant == "debug" {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
out_data.insert(key.clone(), value.clone());
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -142,6 +142,20 @@ class ImplementationName(StrEnum):
|
|||
class Variant(StrEnum):
|
||||
FREETHREADED = "freethreaded"
|
||||
DEBUG = "debug"
|
||||
FREETHREADED_DEBUG = "freethreaded+debug"
|
||||
|
||||
@classmethod
|
||||
def from_build_options(
|
||||
cls: type["Variant"], build_options: list[str]
|
||||
) -> "Variant" | None:
|
||||
if "debug" in build_options and "freethreaded" in build_options:
|
||||
return cls.FREETHREADED_DEBUG
|
||||
elif "debug" in build_options:
|
||||
return cls.DEBUG
|
||||
elif "freethreaded" in build_options:
|
||||
return cls.FREETHREADED
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -208,7 +222,10 @@ class CPythonFinder(Finder):
|
|||
cpython-
|
||||
(?P<ver>\d+\.\d+\.\d+(?:(?:a|b|rc)\d+)?)(?:\+\d+)?\+
|
||||
(?P<date>\d+)-
|
||||
(?P<triple>[a-z\d_]+-[a-z\d]+(?>-[a-z\d]+)?-[a-z\d]+)-
|
||||
# Note we lookahead to avoid matching "debug" as a triple as we'd
|
||||
# prefer it matches as a build option; we could enumerate all known
|
||||
# build options instead but this is the easy path forward
|
||||
(?P<triple>[a-z\d_]+-[a-z\d]+(?>-[a-z\d]+)?-(?!debug(?:-|$))[a-z\d_]+)-
|
||||
(?:(?P<build_options>.+)-)?
|
||||
(?P<flavor>[a-z_]+)?
|
||||
\.tar\.(?:gz|zst)
|
||||
|
@ -377,13 +394,7 @@ class CPythonFinder(Finder):
|
|||
flavor = groups.get("flavor", "full")
|
||||
|
||||
build_options = build_options.split("+") if build_options else []
|
||||
variant: Variant | None
|
||||
for variant in Variant:
|
||||
if variant in build_options:
|
||||
break
|
||||
else:
|
||||
variant = None
|
||||
|
||||
variant = Variant.from_build_options(build_options)
|
||||
version = Version.from_str(version)
|
||||
triple = self._normalize_triple(triple)
|
||||
if triple is None:
|
||||
|
@ -766,8 +777,10 @@ def render(downloads: list[PythonDownload]) -> None:
|
|||
match variant:
|
||||
case Variant.FREETHREADED:
|
||||
return 1
|
||||
case Variant.DEBUG:
|
||||
case Variant.FREETHREADED_DEBUG:
|
||||
return 2
|
||||
case Variant.DEBUG:
|
||||
return 3
|
||||
raise ValueError(f"Missing sort key implementation for variant: {variant}")
|
||||
|
||||
def sort_key(download: PythonDownload) -> tuple:
|
||||
|
|
|
@ -654,6 +654,8 @@ def main() -> None:
|
|||
# The `t` abiflag for freethreading Python.
|
||||
# https://peps.python.org/pep-0703/#build-configuration-changes
|
||||
"gil_disabled": bool(sysconfig.get_config_var("Py_GIL_DISABLED")),
|
||||
# https://docs.python.org/3/using/configure.html#debug-build
|
||||
"debug_enabled": bool(sysconfig.get_config_var("Py_DEBUG")),
|
||||
# Determine if the interpreter is 32-bit or 64-bit.
|
||||
# https://github.com/python/cpython/blob/b228655c227b2ca298a8ffac44d14ce3d22f6faa/Lib/venv/__init__.py#L136
|
||||
"pointer_size": "64" if sys.maxsize > 2**32 else "32",
|
||||
|
|
|
@ -166,7 +166,9 @@ pub(crate) struct DiscoveryPreferences {
|
|||
pub enum PythonVariant {
|
||||
#[default]
|
||||
Default,
|
||||
Debug,
|
||||
Freethreaded,
|
||||
FreethreadedDebug,
|
||||
}
|
||||
|
||||
/// A Python discovery version request.
|
||||
|
@ -1296,6 +1298,7 @@ pub(crate) fn find_python_installation(
|
|||
let installations =
|
||||
find_python_installations(request, environments, preference, cache, preview);
|
||||
let mut first_prerelease = None;
|
||||
let mut first_debug = None;
|
||||
let mut first_managed = None;
|
||||
let mut first_error = None;
|
||||
for result in installations {
|
||||
|
@ -1340,6 +1343,20 @@ pub(crate) fn find_python_installation(
|
|||
continue;
|
||||
}
|
||||
|
||||
// If it's a debug build and debug builds aren't allowed, skip it — but store it for later
|
||||
// since we'll use a debug build if no other versions are available.
|
||||
if installation.key().variant().is_debug()
|
||||
&& !request.allows_debug()
|
||||
&& !installation.source.allows_debug()
|
||||
&& !has_default_executable_name
|
||||
{
|
||||
debug!("Skipping debug installation {}", installation.key());
|
||||
if first_debug.is_none() {
|
||||
first_debug = Some(installation.clone());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it's an alternative implementation and alternative implementations aren't allowed,
|
||||
// skip it. Note we avoid querying these interpreters at all if they're on the search path
|
||||
// and are not requested, but other sources such as the managed installations can include
|
||||
|
@ -1382,6 +1399,16 @@ pub(crate) fn find_python_installation(
|
|||
return Ok(Ok(installation));
|
||||
}
|
||||
|
||||
// If we only found debug installations, they're implicitly allowed and we should return the
|
||||
// first one.
|
||||
if let Some(installation) = first_debug {
|
||||
debug!(
|
||||
"Allowing debug installation {}: no non-debug installations",
|
||||
installation.key()
|
||||
);
|
||||
return Ok(Ok(installation));
|
||||
}
|
||||
|
||||
// If we only found pre-releases, they're implicitly allowed and we should return the first one.
|
||||
if let Some(installation) = first_prerelease {
|
||||
debug!(
|
||||
|
@ -1641,18 +1668,47 @@ fn is_windows_store_shim(_path: &Path) -> bool {
|
|||
impl PythonVariant {
|
||||
fn matches_interpreter(self, interpreter: &Interpreter) -> bool {
|
||||
match self {
|
||||
// TODO(zanieb): Right now, we allow debug interpreters to be selected by default for
|
||||
// backwards compatibility, but we may want to change this in the future.
|
||||
Self::Default => !interpreter.gil_disabled(),
|
||||
Self::Debug => interpreter.debug_enabled(),
|
||||
Self::Freethreaded => interpreter.gil_disabled(),
|
||||
Self::FreethreadedDebug => interpreter.gil_disabled() && interpreter.debug_enabled(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the lib or executable suffix for the variant, e.g., `t` for `python3.13t`.
|
||||
/// Return the executable suffix for the variant, e.g., `t` for `python3.13t`.
|
||||
///
|
||||
/// Returns an empty string for the default Python variant.
|
||||
pub fn suffix(self) -> &'static str {
|
||||
match self {
|
||||
Self::Default => "",
|
||||
Self::Debug => "d",
|
||||
Self::Freethreaded => "t",
|
||||
Self::FreethreadedDebug => "td",
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the lib suffix for the variant, e.g., `t` for `python3.13t` but an empty string for
|
||||
/// `python3.13d` or `python3.13`.
|
||||
pub fn lib_suffix(self) -> &'static str {
|
||||
match self {
|
||||
Self::Default | Self::Debug => "",
|
||||
Self::Freethreaded | Self::FreethreadedDebug => "t",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_freethreaded(self) -> bool {
|
||||
match self {
|
||||
Self::Default | Self::Debug => false,
|
||||
Self::Freethreaded | Self::FreethreadedDebug => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_debug(self) -> bool {
|
||||
match self {
|
||||
Self::Default | Self::Freethreaded => false,
|
||||
Self::Debug | Self::FreethreadedDebug => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1984,6 +2040,19 @@ impl PythonRequest {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether this request opts-in to a debug Python version.
|
||||
pub(crate) fn allows_debug(&self) -> bool {
|
||||
match self {
|
||||
Self::Default => false,
|
||||
Self::Any => true,
|
||||
Self::Version(version) => version.is_debug(),
|
||||
Self::Directory(_) | Self::File(_) | Self::ExecutableName(_) => true,
|
||||
Self::Implementation(_) => false,
|
||||
Self::ImplementationVersion(_, _) => true,
|
||||
Self::Key(request) => request.allows_debug(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this request opts-in to an alternative Python implementation, e.g., PyPy.
|
||||
pub(crate) fn allows_alternative_implementations(&self) -> bool {
|
||||
match self {
|
||||
|
@ -2043,6 +2112,21 @@ impl PythonSource {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether a debug Python installation from this source can be used without opt-in.
|
||||
pub(crate) fn allows_debug(self) -> bool {
|
||||
match self {
|
||||
Self::Managed | Self::Registry | Self::MicrosoftStore => false,
|
||||
Self::SearchPath
|
||||
| Self::SearchPathFirst
|
||||
| Self::CondaPrefix
|
||||
| Self::BaseCondaPrefix
|
||||
| Self::ProvidedPath
|
||||
| Self::ParentInterpreter
|
||||
| Self::ActiveEnvironment
|
||||
| Self::DiscoveredEnvironment => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether an alternative Python implementation from this source can be used without opt-in.
|
||||
pub(crate) fn allows_alternative_implementations(self) -> bool {
|
||||
match self {
|
||||
|
@ -2423,12 +2507,14 @@ impl VersionRequest {
|
|||
}
|
||||
|
||||
// Include free-threaded variants
|
||||
if self.is_freethreaded() {
|
||||
if let Some(variant) = self.variant() {
|
||||
if variant != PythonVariant::Default {
|
||||
for i in 0..names.len() {
|
||||
let name = names[i].with_variant(PythonVariant::Freethreaded);
|
||||
let name = names[i].with_variant(variant);
|
||||
names.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
names.sort_unstable_by_key(|name| name.into_comparator(self, implementation));
|
||||
names.reverse();
|
||||
|
@ -2725,6 +2811,18 @@ impl VersionRequest {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether this request is for a debug Python variant.
|
||||
pub(crate) fn is_debug(&self) -> bool {
|
||||
match self {
|
||||
Self::Any | Self::Default => false,
|
||||
Self::Major(_, variant)
|
||||
| Self::MajorMinor(_, _, variant)
|
||||
| Self::MajorMinorPatch(_, _, _, variant)
|
||||
| Self::MajorMinorPrerelease(_, _, _, variant)
|
||||
| Self::Range(_, variant) => variant.is_debug(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this request is for a free-threaded Python variant.
|
||||
pub(crate) fn is_freethreaded(&self) -> bool {
|
||||
match self {
|
||||
|
@ -2733,7 +2831,7 @@ impl VersionRequest {
|
|||
| Self::MajorMinor(_, _, variant)
|
||||
| Self::MajorMinorPatch(_, _, _, variant)
|
||||
| Self::MajorMinorPrerelease(_, _, _, variant)
|
||||
| Self::Range(_, variant) => variant == &PythonVariant::Freethreaded,
|
||||
| Self::Range(_, variant) => variant.is_freethreaded(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2778,24 +2876,43 @@ impl FromStr for VersionRequest {
|
|||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
// Stripping the 't' suffix produces awkward error messages if the user tries a version
|
||||
// like "latest". HACK: If the version is all letters, don't even try to parse it further.
|
||||
/// Extract the variant from the end of a version request string, returning the prefix and
|
||||
/// the variant type.
|
||||
fn parse_variant(s: &str) -> Result<(&str, PythonVariant), Error> {
|
||||
// This cannot be a valid version, just error immediately
|
||||
if s.chars().all(char::is_alphabetic) {
|
||||
return Err(Error::InvalidVersionRequest(s.to_string()));
|
||||
}
|
||||
|
||||
// Check if the version request is for a free-threaded Python version
|
||||
let (s, variant) = s
|
||||
.strip_suffix('t')
|
||||
.map_or((s, PythonVariant::Default), |s| {
|
||||
(s, PythonVariant::Freethreaded)
|
||||
});
|
||||
let Some(mut start) = s.rfind(|c: char| c.is_numeric()) else {
|
||||
return Ok((s, PythonVariant::Default));
|
||||
};
|
||||
|
||||
if variant == PythonVariant::Freethreaded && s.ends_with('t') {
|
||||
// More than one trailing "t" is not allowed
|
||||
return Err(Error::InvalidVersionRequest(format!("{s}t")));
|
||||
// Advance past the first digit
|
||||
start += 1;
|
||||
|
||||
// Ensure we're not out of bounds
|
||||
if start + 1 > s.len() {
|
||||
return Ok((s, PythonVariant::Default));
|
||||
}
|
||||
|
||||
let variant = &s[start..];
|
||||
let prefix = &s[..start];
|
||||
|
||||
// Strip a leading `+` if present
|
||||
let variant = variant.strip_prefix('+').unwrap_or(variant);
|
||||
|
||||
// TODO(zanieb): Special-case error for use of `dt` instead of `td`
|
||||
|
||||
// If there's not a valid variant, fallback to failure in [`Version::from_str`]
|
||||
let Ok(variant) = PythonVariant::from_str(variant) else {
|
||||
return Ok((s, PythonVariant::Default));
|
||||
};
|
||||
|
||||
Ok((prefix, variant))
|
||||
}
|
||||
|
||||
let (s, variant) = parse_variant(s)?;
|
||||
let Ok(version) = Version::from_str(s) else {
|
||||
return parse_version_specifiers_request(s, variant);
|
||||
};
|
||||
|
@ -2808,27 +2925,12 @@ impl FromStr for VersionRequest {
|
|||
return Err(Error::InvalidVersionRequest(s.to_string()));
|
||||
}
|
||||
|
||||
// Check if the local version includes a variant
|
||||
let variant = if version.local().is_empty() {
|
||||
variant
|
||||
} else {
|
||||
// If we already have a variant, do not allow another to be requested
|
||||
if variant != PythonVariant::Default {
|
||||
// We don't allow local version suffixes unless they're variants, in which case they'd
|
||||
// already be stripped.
|
||||
if !version.local().is_empty() {
|
||||
return Err(Error::InvalidVersionRequest(s.to_string()));
|
||||
}
|
||||
|
||||
let uv_pep440::LocalVersionSlice::Segments([uv_pep440::LocalSegment::String(local)]) =
|
||||
version.local()
|
||||
else {
|
||||
return Err(Error::InvalidVersionRequest(s.to_string()));
|
||||
};
|
||||
|
||||
match local.as_str() {
|
||||
"freethreaded" => PythonVariant::Freethreaded,
|
||||
_ => return Err(Error::InvalidVersionRequest(s.to_string())),
|
||||
}
|
||||
};
|
||||
|
||||
// Cast the release components into u8s since that's what we use in `VersionRequest`
|
||||
let Ok(release) = try_into_u8_slice(&version.release()) else {
|
||||
return Err(Error::InvalidVersionRequest(s.to_string()));
|
||||
|
@ -2879,6 +2981,8 @@ impl FromStr for PythonVariant {
|
|||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"t" | "freethreaded" => Ok(Self::Freethreaded),
|
||||
"d" | "debug" => Ok(Self::Debug),
|
||||
"td" | "freethreaded+debug" => Ok(Self::FreethreadedDebug),
|
||||
"" => Ok(Self::Default),
|
||||
_ => Err(()),
|
||||
}
|
||||
|
@ -2889,7 +2993,9 @@ impl fmt::Display for PythonVariant {
|
|||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Default => f.write_str("default"),
|
||||
Self::Debug => f.write_str("debug"),
|
||||
Self::Freethreaded => f.write_str("freethreaded"),
|
||||
Self::FreethreadedDebug => f.write_str("freethreaded+debug"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2919,23 +3025,15 @@ impl fmt::Display for VersionRequest {
|
|||
match self {
|
||||
Self::Any => f.write_str("any"),
|
||||
Self::Default => f.write_str("default"),
|
||||
Self::Major(major, PythonVariant::Default) => write!(f, "{major}"),
|
||||
Self::Major(major, PythonVariant::Freethreaded) => write!(f, "{major}t"),
|
||||
Self::MajorMinor(major, minor, PythonVariant::Default) => write!(f, "{major}.{minor}"),
|
||||
Self::MajorMinor(major, minor, PythonVariant::Freethreaded) => {
|
||||
write!(f, "{major}.{minor}t")
|
||||
Self::Major(major, variant) => write!(f, "{major}{}", variant.suffix()),
|
||||
Self::MajorMinor(major, minor, variant) => {
|
||||
write!(f, "{major}.{minor}{}", variant.suffix())
|
||||
}
|
||||
Self::MajorMinorPatch(major, minor, patch, PythonVariant::Default) => {
|
||||
write!(f, "{major}.{minor}.{patch}")
|
||||
Self::MajorMinorPatch(major, minor, patch, variant) => {
|
||||
write!(f, "{major}.{minor}.{patch}{}", variant.suffix())
|
||||
}
|
||||
Self::MajorMinorPatch(major, minor, patch, PythonVariant::Freethreaded) => {
|
||||
write!(f, "{major}.{minor}.{patch}t")
|
||||
}
|
||||
Self::MajorMinorPrerelease(major, minor, prerelease, PythonVariant::Default) => {
|
||||
write!(f, "{major}.{minor}{prerelease}")
|
||||
}
|
||||
Self::MajorMinorPrerelease(major, minor, prerelease, PythonVariant::Freethreaded) => {
|
||||
write!(f, "{major}.{minor}{prerelease}t")
|
||||
Self::MajorMinorPrerelease(major, minor, prerelease, variant) => {
|
||||
write!(f, "{major}.{minor}{prerelease}{}", variant.suffix())
|
||||
}
|
||||
Self::Range(specifiers, _) => write!(f, "{specifiers}"),
|
||||
}
|
||||
|
|
|
@ -497,6 +497,11 @@ impl PythonDownloadRequest {
|
|||
})
|
||||
}
|
||||
|
||||
/// Whether this download request opts-in to a debug Python version.
|
||||
pub fn allows_debug(&self) -> bool {
|
||||
self.version.as_ref().is_some_and(VersionRequest::is_debug)
|
||||
}
|
||||
|
||||
/// Whether this download request opts-in to alternative Python implementations.
|
||||
pub fn allows_alternative_implementations(&self) -> bool {
|
||||
self.implementation
|
||||
|
|
|
@ -491,7 +491,7 @@ impl fmt::Display for PythonInstallationKey {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let variant = match self.variant {
|
||||
PythonVariant::Default => String::new(),
|
||||
PythonVariant::Freethreaded => format!("+{}", self.variant),
|
||||
_ => format!("+{}", self.variant),
|
||||
};
|
||||
write!(
|
||||
f,
|
||||
|
@ -632,7 +632,7 @@ impl fmt::Display for PythonInstallationMinorVersionKey {
|
|||
// and prerelease (with special formatting for the variant).
|
||||
let variant = match self.0.variant {
|
||||
PythonVariant::Default => String::new(),
|
||||
PythonVariant::Freethreaded => format!("+{}", self.0.variant),
|
||||
_ => format!("+{}", self.0.variant),
|
||||
};
|
||||
write!(
|
||||
f,
|
||||
|
|
|
@ -37,6 +37,7 @@ use crate::{
|
|||
use windows::Win32::Foundation::{APPMODEL_ERROR_NO_PACKAGE, ERROR_CANT_ACCESS_FILE, WIN32_ERROR};
|
||||
|
||||
/// A Python executable and its associated platform markers.
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Interpreter {
|
||||
platform: Platform,
|
||||
|
@ -59,6 +60,7 @@ pub struct Interpreter {
|
|||
pointer_size: PointerSize,
|
||||
gil_disabled: bool,
|
||||
real_executable: PathBuf,
|
||||
debug_enabled: bool,
|
||||
}
|
||||
|
||||
impl Interpreter {
|
||||
|
@ -82,6 +84,7 @@ impl Interpreter {
|
|||
sys_base_exec_prefix: info.sys_base_exec_prefix,
|
||||
pointer_size: info.pointer_size,
|
||||
gil_disabled: info.gil_disabled,
|
||||
debug_enabled: info.debug_enabled,
|
||||
sys_base_prefix: info.sys_base_prefix,
|
||||
sys_base_executable: info.sys_base_executable,
|
||||
sys_executable: info.sys_executable,
|
||||
|
@ -212,7 +215,13 @@ impl Interpreter {
|
|||
|
||||
pub fn variant(&self) -> PythonVariant {
|
||||
if self.gil_disabled() {
|
||||
if self.debug_enabled() {
|
||||
PythonVariant::FreethreadedDebug
|
||||
} else {
|
||||
PythonVariant::Freethreaded
|
||||
}
|
||||
} else if self.debug_enabled() {
|
||||
PythonVariant::Debug
|
||||
} else {
|
||||
PythonVariant::default()
|
||||
}
|
||||
|
@ -508,6 +517,12 @@ impl Interpreter {
|
|||
self.gil_disabled
|
||||
}
|
||||
|
||||
/// Return whether this is a debug build of Python, as specified by the sysconfig var
|
||||
/// `Py_DEBUG`.
|
||||
pub fn debug_enabled(&self) -> bool {
|
||||
self.debug_enabled
|
||||
}
|
||||
|
||||
/// Return the `--target` directory for this interpreter, if any.
|
||||
pub fn target(&self) -> Option<&Target> {
|
||||
self.target.as_ref()
|
||||
|
@ -877,6 +892,7 @@ pub enum InterpreterInfoError {
|
|||
EmscriptenNotPyodide,
|
||||
}
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
struct InterpreterInfo {
|
||||
platform: Platform,
|
||||
|
@ -895,6 +911,7 @@ struct InterpreterInfo {
|
|||
standalone: bool,
|
||||
pointer_size: PointerSize,
|
||||
gil_disabled: bool,
|
||||
debug_enabled: bool,
|
||||
}
|
||||
|
||||
impl InterpreterInfo {
|
||||
|
@ -1299,7 +1316,8 @@ mod tests {
|
|||
"scripts": "bin"
|
||||
},
|
||||
"pointer_size": "64",
|
||||
"gil_disabled": true
|
||||
"gil_disabled": true,
|
||||
"debug_enabled": false
|
||||
}
|
||||
"##};
|
||||
|
||||
|
|
|
@ -294,7 +294,8 @@ mod tests {
|
|||
"scripts": "bin"
|
||||
},
|
||||
"pointer_size": "64",
|
||||
"gil_disabled": {FREE_THREADED}
|
||||
"gil_disabled": {FREE_THREADED},
|
||||
"debug_enabled": false
|
||||
}
|
||||
"##};
|
||||
|
||||
|
@ -385,7 +386,8 @@ mod tests {
|
|||
"data": ""
|
||||
},
|
||||
"pointer_size": "32",
|
||||
"gil_disabled": false
|
||||
"gil_disabled": false,
|
||||
"debug_enabled": false
|
||||
}
|
||||
"##};
|
||||
|
||||
|
|
|
@ -574,7 +574,7 @@ impl ManagedPythonInstallation {
|
|||
let stdlib = if self.key.os().is_windows() {
|
||||
self.python_dir().join("Lib")
|
||||
} else {
|
||||
let lib_suffix = self.key.variant.suffix();
|
||||
let lib_suffix = self.key.variant.lib_suffix();
|
||||
let python = if matches!(
|
||||
self.key.implementation,
|
||||
LenientImplementationName::Known(ImplementationName::PyPy)
|
||||
|
@ -605,7 +605,7 @@ impl ManagedPythonInstallation {
|
|||
self.path(),
|
||||
self.key.major,
|
||||
self.key.minor,
|
||||
self.key.variant.suffix(),
|
||||
self.key.variant.lib_suffix(),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ fn find_sysconfigdata(
|
|||
.join("lib")
|
||||
.join(format!("python{major}.{minor}{suffix}"));
|
||||
if !lib.exists() {
|
||||
return Err(Error::MissingLib);
|
||||
return Err(Error::MissingLib(lib));
|
||||
}
|
||||
|
||||
// Probe the `lib` directory for `_sysconfigdata_`.
|
||||
|
@ -270,8 +270,8 @@ fn patch_pkgconfig(contents: &str) -> Option<String> {
|
|||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("Python installation is missing a `lib` directory")]
|
||||
MissingLib,
|
||||
#[error("Python installation is missing a `lib` directory at: {0}")]
|
||||
MissingLib(PathBuf),
|
||||
#[error("Python installation is missing a `_sysconfigdata_` file")]
|
||||
MissingSysconfigdata,
|
||||
#[error(transparent)]
|
||||
|
|
|
@ -109,7 +109,9 @@ pub(crate) async fn list(
|
|||
.map(|a| PythonDownloadRequest::iter_downloads(a, python_downloads_json_url.as_deref()))
|
||||
.transpose()?
|
||||
.into_iter()
|
||||
.flatten();
|
||||
.flatten()
|
||||
// TODO(zanieb): Add a way to show debug downloads, we just hide them for now
|
||||
.filter(|download| download.key().variant() != &uv_python::PythonVariant::Debug);
|
||||
|
||||
for download in downloads {
|
||||
output.insert((
|
||||
|
|
|
@ -233,10 +233,10 @@ impl TestContext {
|
|||
// On Unix, we'll strip version numbers
|
||||
if name == "python" {
|
||||
// We can't require them in this case since `/python` is common
|
||||
r"(\d\.\d+|\d)?".to_string()
|
||||
r"(\d\.\d+|\d)?(t|d|td)?".to_string()
|
||||
} else {
|
||||
// However, for other names we'll require them to avoid over-matching
|
||||
r"(\d\.\d+|\d)".to_string()
|
||||
r"(\d\.\d+|\d)(t|d|td)?".to_string()
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -412,7 +412,7 @@ impl TestContext {
|
|||
)? # (we allow the patch version to be missing entirely, e.g., in a request)
|
||||
(?:(?:a|b|rc)[0-9]+)? # Pre-release version component, e.g., `a6` or `rc2`
|
||||
(?:[td])? # A short variant, such as `t` (for freethreaded) or `d` (for debug)
|
||||
(?:\+[a-z]+)? # A long variant, such as `+free-threaded`
|
||||
(?:(\+[a-z]+)+)? # A long variant, such as `+freethreaded` or `+freethreaded+debug`
|
||||
)
|
||||
-
|
||||
[a-z0-9]+ # Operating system (e.g., 'macos')
|
||||
|
|
|
@ -2439,7 +2439,7 @@ fn init_requires_python_specifiers() -> Result<()> {
|
|||
})?;
|
||||
|
||||
let child = context.temp_dir.join("foo");
|
||||
uv_snapshot!(context.filters(), context.init().current_dir(&context.temp_dir).arg(&child).arg("--python").arg("==3.9.*"), @r###"
|
||||
uv_snapshot!(context.filters(), context.init().current_dir(&context.temp_dir).arg(&child).arg("--python").arg("==3.9.*"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
@ -2447,7 +2447,7 @@ fn init_requires_python_specifiers() -> Result<()> {
|
|||
----- stderr -----
|
||||
Adding `foo` as member of workspace `[TEMP_DIR]/`
|
||||
Initialized project `foo` at `[TEMP_DIR]/foo`
|
||||
"###);
|
||||
");
|
||||
|
||||
let pyproject_toml = fs_err::read_to_string(child.join("pyproject.toml"))?;
|
||||
insta::with_settings!({
|
||||
|
|
|
@ -11836,14 +11836,14 @@ fn lock_local_index() -> Result<()> {
|
|||
Url::from_file_path(&root).unwrap().as_str()
|
||||
})?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.lock().env_remove(EnvVars::UV_EXCLUDE_NEWER), @r###"
|
||||
uv_snapshot!(context.filters(), context.lock().env_remove(EnvVars::UV_EXCLUDE_NEWER), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 2 packages in [TIME]
|
||||
"###);
|
||||
");
|
||||
|
||||
let lock = context.read("uv.lock");
|
||||
|
||||
|
|
|
@ -42,6 +42,16 @@ fn python_find() {
|
|||
----- stderr -----
|
||||
"###);
|
||||
|
||||
// Request Python 3.12
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("==3.12.*"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[PYTHON-3.12]
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
|
||||
// Request Python 3.11
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.11"), @r###"
|
||||
success: true
|
||||
|
|
|
@ -1060,9 +1060,11 @@ fn python_install_preview_upgrade() {
|
|||
fn python_install_freethreaded() {
|
||||
let context: TestContext = TestContext::new_with_versions(&[])
|
||||
.with_filtered_python_keys()
|
||||
.with_filtered_exe_suffix()
|
||||
.with_managed_python_dirs()
|
||||
.with_python_download_cache();
|
||||
.with_python_download_cache()
|
||||
.with_filtered_python_install_bin()
|
||||
.with_filtered_python_names()
|
||||
.with_filtered_exe_suffix();
|
||||
|
||||
// Install the latest version
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("3.13t"), @r"
|
||||
|
@ -1097,6 +1099,26 @@ fn python_install_freethreaded() {
|
|||
----- stderr -----
|
||||
"###);
|
||||
|
||||
// We should find it with opt-in
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.13t"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13+freethreaded-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
// We should be able to select it with `+freethreaded`
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.13+freethreaded"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13+freethreaded-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
// Create a virtual environment with the freethreaded Python
|
||||
uv_snapshot!(context.filters(), context.venv().arg("--python").arg("3.13t"), @r"
|
||||
success: true
|
||||
|
@ -1190,6 +1212,284 @@ fn python_install_freethreaded() {
|
|||
");
|
||||
}
|
||||
|
||||
// We only support debug builds on Unix
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn python_install_debug() {
|
||||
let context: TestContext = TestContext::new_with_versions(&[])
|
||||
.with_filtered_python_keys()
|
||||
.with_filtered_exe_suffix()
|
||||
.with_managed_python_dirs();
|
||||
|
||||
// Install the latest version
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("3.13+debug"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Installed Python 3.13.7 in [TIME]
|
||||
+ cpython-3.13.7+debug-[PLATFORM] (python3.13d)
|
||||
");
|
||||
|
||||
let bin_python = context
|
||||
.bin_dir
|
||||
.child(format!("python3.13d{}", std::env::consts::EXE_SUFFIX));
|
||||
|
||||
// The executable should be installed in the bin directory
|
||||
bin_python.assert(predicate::path::exists());
|
||||
|
||||
// On Unix, it should be a link
|
||||
#[cfg(unix)]
|
||||
bin_python.assert(predicate::path::is_symlink());
|
||||
|
||||
// The executable should "work"
|
||||
uv_snapshot!(context.filters(), Command::new(bin_python.as_os_str())
|
||||
.arg("-c").arg("import subprocess; print('hello world')"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
hello world
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
|
||||
// We should find it with opt-in
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.13d"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13+debug-[PLATFORM]/bin/python3.13d
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
// We should find it without opt-in
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.13"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13+debug-[PLATFORM]/bin/python3.13d
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
// Should be distinct from 3.13
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.13"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Installed Python 3.13.7 in [TIME]
|
||||
+ cpython-3.13.7-[PLATFORM] (python3.13)
|
||||
");
|
||||
|
||||
// Now we should prefer the non-debug version without opt-in
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.13"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13.7-[PLATFORM]/bin/python3.13
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
// But still select it with opt-in
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.13d"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13+debug-[PLATFORM]/bin/python3.13d
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
// We should allow selection with `+debug`
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.13+debug"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13+debug-[PLATFORM]/bin/python3.13d
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
// Should work with older Python versions too
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.12d"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Installed Python 3.12.11 in [TIME]
|
||||
+ cpython-3.12.11+debug-[PLATFORM] (python3.12d)
|
||||
");
|
||||
|
||||
uv_snapshot!(context.filters(), context.python_uninstall().arg("--all"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Searching for Python installations
|
||||
Uninstalled 3 versions in [TIME]
|
||||
- cpython-3.12.11+debug-[PLATFORM] (python3.12d)
|
||||
- cpython-3.13.7+debug-[PLATFORM] (python3.13d)
|
||||
- cpython-3.13.7-[PLATFORM] (python3.13)
|
||||
");
|
||||
}
|
||||
|
||||
// We only support debug builds on Unix
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn python_install_debug_freethreaded() {
|
||||
let context: TestContext = TestContext::new_with_versions(&[])
|
||||
.with_filtered_python_keys()
|
||||
.with_filtered_exe_suffix()
|
||||
.with_managed_python_dirs();
|
||||
|
||||
// Install the latest version
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("3.13td"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Installed Python 3.13.7 in [TIME]
|
||||
+ cpython-3.13.7+freethreaded+debug-[PLATFORM] (python3.13td)
|
||||
");
|
||||
|
||||
let bin_python = context
|
||||
.bin_dir
|
||||
.child(format!("python3.13td{}", std::env::consts::EXE_SUFFIX));
|
||||
|
||||
// The executable should be installed in the bin directory
|
||||
bin_python.assert(predicate::path::exists());
|
||||
|
||||
// On Unix, it should be a link
|
||||
#[cfg(unix)]
|
||||
bin_python.assert(predicate::path::is_symlink());
|
||||
|
||||
// The executable should "work"
|
||||
uv_snapshot!(context.filters(), Command::new(bin_python.as_os_str())
|
||||
.arg("-c").arg("import subprocess; print('hello world')"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
hello world
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
|
||||
// We should find it with opt-in
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.13td"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13+freethreaded+debug-[PLATFORM]/bin/python3.13td
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
// We should not find it without opt-in
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.13"), @r"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: No interpreter found for Python 3.13 in virtual environments, managed installations, or search path
|
||||
");
|
||||
|
||||
// We should allow selection with `+freethread+debug`
|
||||
// TODO(zanieb): We don't support this yet
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.13+freethreaded+debug"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13+freethreaded+debug-[PLATFORM]/bin/python3.13td
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
// Should be distinct from 3.13
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.13"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Installed Python 3.13.7 in [TIME]
|
||||
+ cpython-3.13.7-[PLATFORM] (python3.13)
|
||||
");
|
||||
|
||||
// Should be distinct from 3.13t
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.13t"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Installed Python 3.13.7 in [TIME]
|
||||
+ cpython-3.13.7+freethreaded-[PLATFORM] (python3.13t)
|
||||
");
|
||||
|
||||
// Should be distinct from 3.13d
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.13d"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Installed Python 3.13.7 in [TIME]
|
||||
+ cpython-3.13.7+debug-[PLATFORM] (python3.13d)
|
||||
");
|
||||
|
||||
// Now we should prefer the non-debug version without opt-in
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.13"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13.7-[PLATFORM]/bin/python3.13
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.13t"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13.7+freethreaded-[PLATFORM]/bin/python3.13t
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
// But still select it with opt-in
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.13td"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13+freethreaded+debug-[PLATFORM]/bin/python3.13td
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
uv_snapshot!(context.filters(), context.python_uninstall().arg("--all"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Searching for Python installations
|
||||
Uninstalled 4 versions in [TIME]
|
||||
- cpython-3.13.7+freethreaded+debug-[PLATFORM] (python3.13td)
|
||||
- cpython-3.13.7+freethreaded-[PLATFORM] (python3.13t)
|
||||
- cpython-3.13.7+debug-[PLATFORM] (python3.13d)
|
||||
- cpython-3.13.7-[PLATFORM] (python3.13)
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn python_install_invalid_request() {
|
||||
let context: TestContext = TestContext::new_with_versions(&[])
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue