Skip to content

allow manylinux compatibility override via _manylinux module. #6039

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions crates/bench/benches/uv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,9 @@ mod resolver {
Arch::Aarch64,
);

static TAGS: LazyLock<Tags> =
LazyLock::new(|| Tags::from_env(&PLATFORM, (3, 11), "cpython", (3, 11), false).unwrap());
static TAGS: LazyLock<Tags> = LazyLock::new(|| {
Tags::from_env(&PLATFORM, (3, 11), "cpython", (3, 11), false, false).unwrap()
});

pub(crate) async fn resolve(
manifest: Manifest,
Expand Down
71 changes: 70 additions & 1 deletion crates/platform-tags/src/tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,19 @@ impl Tags {
python_version: (u8, u8),
implementation_name: &str,
implementation_version: (u8, u8),
manylinux_compatible: bool,
gil_disabled: bool,
) -> Result<Self, TagsError> {
let implementation = Implementation::parse(implementation_name, gil_disabled)?;
let platform_tags = compatible_tags(platform)?;

// Determine the compatible tags for the current platform.
let platform_tags = {
let mut platform_tags = compatible_tags(platform)?;
if matches!(platform.os(), Os::Manylinux { .. }) && !manylinux_compatible {
platform_tags.retain(|tag| !tag.starts_with("manylinux"));
}
platform_tags
};

let mut tags = Vec::with_capacity(5 * platform_tags.len());

Expand Down Expand Up @@ -931,6 +940,64 @@ mod tests {
);
}

/// Ensure the tags returned do not include the `manylinux` tags
/// when `manylinux_incompatible` is set to `false`.
#[test]
fn test_manylinux_incompatible() {
let tags = Tags::from_env(
&Platform::new(
Os::Manylinux {
major: 2,
minor: 28,
},
Arch::X86_64,
),
(3, 9),
"cpython",
(3, 9),
false,
false,
)
.unwrap();
assert_snapshot!(
tags,
@r###"
cp39-cp39-linux_x86_64
cp39-abi3-linux_x86_64
cp39-none-linux_x86_64
cp38-abi3-linux_x86_64
cp37-abi3-linux_x86_64
cp36-abi3-linux_x86_64
cp35-abi3-linux_x86_64
cp34-abi3-linux_x86_64
cp33-abi3-linux_x86_64
cp32-abi3-linux_x86_64
py39-none-linux_x86_64
py3-none-linux_x86_64
py38-none-linux_x86_64
py37-none-linux_x86_64
py36-none-linux_x86_64
py35-none-linux_x86_64
py34-none-linux_x86_64
py33-none-linux_x86_64
py32-none-linux_x86_64
py31-none-linux_x86_64
py30-none-linux_x86_64
cp39-none-any
py39-none-any
py3-none-any
py38-none-any
py37-none-any
py36-none-any
py35-none-any
py34-none-any
py33-none-any
py32-none-any
py31-none-any
py30-none-any
"###);
}

/// Check full tag ordering.
/// The list is displayed in decreasing priority.
///
Expand All @@ -951,6 +1018,7 @@ mod tests {
(3, 9),
"cpython",
(3, 9),
true,
false,
)
.unwrap();
Expand Down Expand Up @@ -1575,6 +1643,7 @@ mod tests {
"cpython",
(3, 9),
false,
false,
)
.unwrap();
assert_snapshot!(
Expand Down
14 changes: 13 additions & 1 deletion crates/uv-python/python/get_interpreter_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,17 @@ def main() -> None:
"python_version": ".".join(platform.python_version_tuple()[:2]),
"sys_platform": sys.platform,
}
os_and_arch = get_operating_system_and_architecture()

manylinux_compatible = True
if os_and_arch["os"]["name"] == "manylinux":
# noinspection PyProtectedMember
from .packaging._manylinux import _get_glibc_version, _is_compatible

manylinux_compatible = _is_compatible(
arch=os_and_arch["arch"], version=_get_glibc_version()
)

interpreter_info = {
"result": "success",
"markers": markers,
Expand All @@ -554,7 +565,8 @@ def main() -> None:
"stdlib": sysconfig.get_path("stdlib"),
"scheme": get_scheme(),
"virtualenv": get_virtualenv(),
"platform": get_operating_system_and_architecture(),
"platform": os_and_arch,
"manylinux_compatible": manylinux_compatible,
# 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")),
Expand Down
10 changes: 10 additions & 0 deletions crates/uv-python/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub struct Interpreter {
markers: Box<MarkerEnvironment>,
scheme: Scheme,
virtualenv: Scheme,
manylinux_compatible: bool,
sys_prefix: PathBuf,
sys_base_exec_prefix: PathBuf,
sys_base_prefix: PathBuf,
Expand Down Expand Up @@ -63,6 +64,7 @@ impl Interpreter {
markers: Box::new(info.markers),
scheme: info.scheme,
virtualenv: info.virtualenv,
manylinux_compatible: info.manylinux_compatible,
sys_prefix: info.sys_prefix,
sys_base_exec_prefix: info.sys_base_exec_prefix,
pointer_size: info.pointer_size,
Expand Down Expand Up @@ -176,6 +178,7 @@ impl Interpreter {
self.python_tuple(),
self.implementation_name(),
self.implementation_tuple(),
self.manylinux_compatible,
self.gil_disabled,
)?;
self.tags.set(tags).expect("tags should not be set");
Expand Down Expand Up @@ -373,6 +376,11 @@ impl Interpreter {
&self.virtualenv
}

/// Return whether this interpreter is `manylinux` compatible.
pub fn manylinux_compatible(&self) -> bool {
self.manylinux_compatible
}

/// Return the [`PointerSize`] of the Python interpreter (i.e., 32- vs. 64-bit).
pub fn pointer_size(&self) -> PointerSize {
self.pointer_size
Expand Down Expand Up @@ -555,6 +563,7 @@ struct InterpreterInfo {
markers: MarkerEnvironment,
scheme: Scheme,
virtualenv: Scheme,
manylinux_compatible: bool,
sys_prefix: PathBuf,
sys_base_exec_prefix: PathBuf,
sys_base_prefix: PathBuf,
Expand Down Expand Up @@ -785,6 +794,7 @@ mod tests {
},
"arch": "x86_64"
},
"manylinux_compatible": false,
"markers": {
"implementation_name": "cpython",
"implementation_version": "3.12.0",
Expand Down
1 change: 1 addition & 0 deletions crates/uv-python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ mod tests {
},
"arch": "x86_64"
},
"manylinux_compatible": true,
"markers": {
"implementation_name": "{IMPLEMENTATION}",
"implementation_version": "{FULL_VERSION}",
Expand Down
3 changes: 3 additions & 0 deletions crates/uv/src/commands/pip/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,23 @@ pub(crate) fn resolution_environment(
(python_version.major(), python_version.minor()),
interpreter.implementation_name(),
interpreter.implementation_tuple(),
interpreter.manylinux_compatible(),
interpreter.gil_disabled(),
)?),
(Some(python_platform), None) => Cow::Owned(Tags::from_env(
&python_platform.platform(),
interpreter.python_tuple(),
interpreter.implementation_name(),
interpreter.implementation_tuple(),
interpreter.manylinux_compatible(),
interpreter.gil_disabled(),
)?),
(None, Some(python_version)) => Cow::Owned(Tags::from_env(
interpreter.platform(),
(python_version.major(), python_version.minor()),
interpreter.implementation_name(),
interpreter.implementation_tuple(),
interpreter.manylinux_compatible(),
interpreter.gil_disabled(),
)?),
(None, None) => Cow::Borrowed(interpreter.tags()?),
Expand Down
28 changes: 28 additions & 0 deletions docs/pip/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,34 @@ reuse any binary distributions that are already present in the local cache.
Additionally, and in contrast to pip, uv's resolver will still read metadata from pre-built binary
distributions when `--no-binary` is provided.

## `manylinux_compatible` enforcement

[PEP 600](https://peps.python.org/pep-0600/#package-installers) describes a mechanism through which
Python distributors can opt out of `manylinux` compatibility by defining a `manylinux_compatible`
function on the `_manylinux` standard library module.

uv respects `manylinux_compatible`, but only tests against the current glibc version, and applies
the return value of `manylinux_compatible` globally.

In other words, if `manylinux_compatible` returns `True`, uv will treat the system as
`manylinux`-compatible; if it returns `False`, uv will treat the system as `manylinux`-incompatible,
without calling `manylinux_compatible` for every glibc version.

This approach is not a complete implementation of the spec, but is compatible with common blanket
`manylinux_compatible` implementations like
[`no-manylinux`](https://pypi.org/project/no-manylinux/):

```python
from __future__ import annotations
manylinux1_compatible = False
manylinux2010_compatible = False
manylinux2014_compatible = False


def manylinux_compatible(*_, **__): # PEP 600
return False
```

## Bytecode compilation

Unlike pip, uv does not compile `.py` files to `.pyc` files during installation by default (i.e., uv
Expand Down
Loading