mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 19:08:04 +00:00
Support freethreading python (#2805)
freethreaded python reintroduces abiflags since it is incompatible with regular native modules and abi3. Tests: None yet! We're lacking cpython 3.13 no-gil builds we can use in ci. My test setup: ``` PYTHON_CONFIGURE_OPTS="--enable-shared --disable-gil" pyenv install 3.13.0a5 cargo run -q -- venv -q -p python3.13 .venv3.13 --no-cache-dir && cargo run -q -- pip install -v psutil --no-cache-dir && .venv3.13/bin/python -c "import psutil" ``` Fixes #2429
This commit is contained in:
parent
98fd9d7d14
commit
7f70849e3c
8 changed files with 68 additions and 32 deletions
|
@ -1,6 +1,5 @@
|
|||
use std::collections::BTreeSet;
|
||||
use std::fmt::Formatter;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::{cmp, num::NonZeroU32};
|
||||
|
||||
|
@ -18,6 +17,8 @@ pub enum TagsError {
|
|||
UnknownImplementation(String),
|
||||
#[error("Invalid priority: {0}")]
|
||||
InvalidPriority(usize, #[source] std::num::TryFromIntError),
|
||||
#[error("Only CPython can be freethreading, not: {0}")]
|
||||
GilIsACpythonProblem(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd, Clone)]
|
||||
|
@ -93,8 +94,9 @@ impl Tags {
|
|||
python_version: (u8, u8),
|
||||
implementation_name: &str,
|
||||
implementation_version: (u8, u8),
|
||||
gil_disabled: bool,
|
||||
) -> Result<Self, TagsError> {
|
||||
let implementation = Implementation::from_str(implementation_name)?;
|
||||
let implementation = Implementation::parse(implementation_name, gil_disabled)?;
|
||||
let platform_tags = compatible_tags(platform)?;
|
||||
|
||||
let mut tags = Vec::with_capacity(5 * platform_tags.len());
|
||||
|
@ -108,15 +110,18 @@ impl Tags {
|
|||
));
|
||||
}
|
||||
// 2. abi3 and no abi (e.g. executable binary)
|
||||
if matches!(implementation, Implementation::CPython) {
|
||||
if let Implementation::CPython { gil_disabled } = implementation {
|
||||
// For some reason 3.2 is the minimum python for the cp abi
|
||||
for minor in (2..=python_version.1).rev() {
|
||||
for platform_tag in &platform_tags {
|
||||
tags.push((
|
||||
implementation.language_tag((python_version.0, minor)),
|
||||
"abi3".to_string(),
|
||||
platform_tag.clone(),
|
||||
));
|
||||
// No abi3 for freethreading python
|
||||
if !gil_disabled {
|
||||
for platform_tag in &platform_tags {
|
||||
tags.push((
|
||||
implementation.language_tag((python_version.0, minor)),
|
||||
"abi3".to_string(),
|
||||
platform_tag.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
// Only include `none` tags for the current CPython version
|
||||
if minor == python_version.1 {
|
||||
|
@ -151,9 +156,9 @@ impl Tags {
|
|||
}
|
||||
}
|
||||
// 4. no binary
|
||||
if matches!(implementation, Implementation::CPython) {
|
||||
if matches!(implementation, Implementation::CPython { .. }) {
|
||||
tags.push((
|
||||
format!("cp{}{}", python_version.0, python_version.1),
|
||||
implementation.language_tag(python_version),
|
||||
"none".to_string(),
|
||||
"any".to_string(),
|
||||
));
|
||||
|
@ -291,7 +296,7 @@ impl std::fmt::Display for Tags {
|
|||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Implementation {
|
||||
CPython,
|
||||
CPython { gil_disabled: bool },
|
||||
PyPy,
|
||||
Pyston,
|
||||
}
|
||||
|
@ -302,7 +307,7 @@ impl Implementation {
|
|||
fn language_tag(self, python_version: (u8, u8)) -> String {
|
||||
match self {
|
||||
// Ex) `cp39`
|
||||
Self::CPython => format!("cp{}{}", python_version.0, python_version.1),
|
||||
Self::CPython { .. } => format!("cp{}{}", python_version.0, python_version.1),
|
||||
// Ex) `pp39`
|
||||
Self::PyPy => format!("pp{}{}", python_version.0, python_version.1),
|
||||
// Ex) `pt38``
|
||||
|
@ -313,11 +318,20 @@ impl Implementation {
|
|||
fn abi_tag(self, python_version: (u8, u8), implementation_version: (u8, u8)) -> String {
|
||||
match self {
|
||||
// Ex) `cp39`
|
||||
Self::CPython => {
|
||||
Self::CPython { gil_disabled } => {
|
||||
if python_version.1 <= 7 {
|
||||
format!("cp{}{}m", python_version.0, python_version.1)
|
||||
} else if gil_disabled {
|
||||
// https://peps.python.org/pep-0703/#build-configuration-changes
|
||||
// Python 3.13+ only, but it makes more sense to just rely on the sysconfig var.
|
||||
format!("cp{}{}t", python_version.0, python_version.1,)
|
||||
} else {
|
||||
format!("cp{}{}", python_version.0, python_version.1)
|
||||
format!(
|
||||
"cp{}{}{}",
|
||||
python_version.0,
|
||||
python_version.1,
|
||||
if gil_disabled { "t" } else { "" }
|
||||
)
|
||||
}
|
||||
}
|
||||
// Ex) `pypy39_pp73`
|
||||
|
@ -338,23 +352,22 @@ impl Implementation {
|
|||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Implementation {
|
||||
type Err = TagsError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, TagsError> {
|
||||
match s {
|
||||
fn parse(name: &str, gil_disabled: bool) -> Result<Self, TagsError> {
|
||||
if gil_disabled && name != "cpython" {
|
||||
return Err(TagsError::GilIsACpythonProblem(name.to_string()));
|
||||
}
|
||||
match name {
|
||||
// Known and supported implementations.
|
||||
"cpython" => Ok(Self::CPython),
|
||||
"cpython" => Ok(Self::CPython { gil_disabled }),
|
||||
"pypy" => Ok(Self::PyPy),
|
||||
"pyston" => Ok(Self::Pyston),
|
||||
// Known but unsupported implementations.
|
||||
"python" => Err(TagsError::UnsupportedImplementation(s.to_string())),
|
||||
"ironpython" => Err(TagsError::UnsupportedImplementation(s.to_string())),
|
||||
"jython" => Err(TagsError::UnsupportedImplementation(s.to_string())),
|
||||
"python" => Err(TagsError::UnsupportedImplementation(name.to_string())),
|
||||
"ironpython" => Err(TagsError::UnsupportedImplementation(name.to_string())),
|
||||
"jython" => Err(TagsError::UnsupportedImplementation(name.to_string())),
|
||||
// Unknown implementations.
|
||||
_ => Err(TagsError::UnknownImplementation(s.to_string())),
|
||||
_ => Err(TagsError::UnknownImplementation(name.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -547,7 +560,7 @@ mod tests {
|
|||
/// The list is displayed in decreasing priority.
|
||||
///
|
||||
/// A reference list can be generated with:
|
||||
/// ```
|
||||
/// ```text
|
||||
/// $ python -c "from packaging import tags; [print(tag) for tag in tags.platform_tags()]"`
|
||||
/// ````
|
||||
#[test]
|
||||
|
@ -908,7 +921,7 @@ mod tests {
|
|||
/// The list is displayed in decreasing priority.
|
||||
///
|
||||
/// A reference list can be generated with:
|
||||
/// ```
|
||||
/// ```text
|
||||
/// $ python -c "from packaging import tags; [print(tag) for tag in tags.sys_tags()]"`
|
||||
/// ```
|
||||
#[test]
|
||||
|
@ -924,6 +937,7 @@ mod tests {
|
|||
(3, 9),
|
||||
"cpython",
|
||||
(3, 9),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
assert_snapshot!(
|
||||
|
@ -1546,6 +1560,7 @@ mod tests {
|
|||
(3, 9),
|
||||
"cpython",
|
||||
(3, 9),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
assert_snapshot!(
|
||||
|
|
|
@ -17,7 +17,7 @@ workspace = true
|
|||
cache-key = { workspace = true }
|
||||
distribution-types = { workspace = true }
|
||||
pypi-types = { workspace = true }
|
||||
uv-fs = { workspace = true }
|
||||
uv-fs = { workspace = true, features = ["tokio"] }
|
||||
uv-normalize = { workspace = true }
|
||||
|
||||
cachedir = { workspace = true }
|
||||
|
|
|
@ -610,7 +610,7 @@ impl CacheBucket {
|
|||
Self::BuiltWheels => "built-wheels-v3",
|
||||
Self::FlatIndex => "flat-index-v0",
|
||||
Self::Git => "git-v0",
|
||||
Self::Interpreter => "interpreter-v0",
|
||||
Self::Interpreter => "interpreter-v1",
|
||||
Self::Simple => "simple-v7",
|
||||
Self::Wheels => "wheels-v1",
|
||||
Self::Archive => "archive-v0",
|
||||
|
|
|
@ -42,6 +42,6 @@ winapi = { workspace = true }
|
|||
[dev-dependencies]
|
||||
anyhow = { version = "1.0.80" }
|
||||
indoc = { version = "2.0.4" }
|
||||
insta = { version = "1.36.1" }
|
||||
insta = { version = "1.36.1", features = ["filters"] }
|
||||
itertools = { version = "0.12.1" }
|
||||
tempfile = { version = "3.9.0" }
|
||||
|
|
|
@ -534,6 +534,9 @@ def main() -> None:
|
|||
"scheme": get_scheme(),
|
||||
"virtualenv": get_virtualenv(),
|
||||
"platform": get_operating_system_and_architecture(),
|
||||
# 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")),
|
||||
}
|
||||
print(json.dumps(interpreter_info))
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ pub struct Interpreter {
|
|||
sys_executable: PathBuf,
|
||||
stdlib: PathBuf,
|
||||
tags: OnceCell<Tags>,
|
||||
gil_disabled: bool,
|
||||
}
|
||||
|
||||
impl Interpreter {
|
||||
|
@ -55,6 +56,7 @@ impl Interpreter {
|
|||
virtualenv: info.virtualenv,
|
||||
prefix: info.prefix,
|
||||
base_exec_prefix: info.base_exec_prefix,
|
||||
gil_disabled: info.gil_disabled,
|
||||
base_prefix: info.base_prefix,
|
||||
base_executable: info.base_executable,
|
||||
sys_executable: info.sys_executable,
|
||||
|
@ -89,6 +91,7 @@ impl Interpreter {
|
|||
sys_executable: PathBuf::from("/dev/null"),
|
||||
stdlib: PathBuf::from("/dev/null"),
|
||||
tags: OnceCell::new(),
|
||||
gil_disabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,6 +126,7 @@ impl Interpreter {
|
|||
self.python_tuple(),
|
||||
self.implementation_name(),
|
||||
self.implementation_tuple(),
|
||||
self.gil_disabled,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -290,6 +294,15 @@ impl Interpreter {
|
|||
&self.virtualenv
|
||||
}
|
||||
|
||||
/// Return whether this is a Python 3.13+ freethreading Python, as specified by the sysconfig var
|
||||
/// `Py_GIL_DISABLED`.
|
||||
///
|
||||
/// freethreading Python is incompatible with earlier native modules, re-introducing
|
||||
/// abiflags with a `t` flag. <https://peps.python.org/pep-0703/#build-configuration-changes>
|
||||
pub fn gil_disabled(&self) -> bool {
|
||||
self.gil_disabled
|
||||
}
|
||||
|
||||
/// Return the [`Layout`] environment used to install wheels into this interpreter.
|
||||
pub fn layout(&self) -> Layout {
|
||||
Layout {
|
||||
|
@ -374,6 +387,7 @@ struct InterpreterInfo {
|
|||
base_executable: Option<PathBuf>,
|
||||
sys_executable: PathBuf,
|
||||
stdlib: PathBuf,
|
||||
gil_disabled: bool,
|
||||
}
|
||||
|
||||
impl InterpreterInfo {
|
||||
|
@ -603,7 +617,8 @@ mod tests {
|
|||
"platlib": "lib/python3.12/site-packages",
|
||||
"purelib": "lib/python3.12/site-packages",
|
||||
"scripts": "bin"
|
||||
}
|
||||
},
|
||||
"gil_disabled": true
|
||||
}
|
||||
"##};
|
||||
|
||||
|
|
|
@ -700,6 +700,7 @@ static TAGS_311: Lazy<Tags> = Lazy::new(|| {
|
|||
(3, 11),
|
||||
"cpython",
|
||||
(3, 11),
|
||||
false,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
@ -732,6 +733,7 @@ static TAGS_310: Lazy<Tags> = Lazy::new(|| {
|
|||
(3, 10),
|
||||
"cpython",
|
||||
(3, 10),
|
||||
false,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
|
|
@ -189,6 +189,7 @@ pub(crate) async fn pip_compile(
|
|||
(python_version.major(), python_version.minor()),
|
||||
interpreter.implementation_name(),
|
||||
interpreter.implementation_tuple(),
|
||||
interpreter.gil_disabled(),
|
||||
)?)
|
||||
} else {
|
||||
Cow::Borrowed(interpreter.tags()?)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue