mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-22 08:12:44 +00:00
allow manylinux compatibility override via _manylinux
module. (#6039)
## Summary resolves https://github.com/astral-sh/uv/issues/5915, not entirely sure if `manylinux_compatible` should be a separate field in the JSON returned by the interpreter or there's some way to use the existing `platform` for it. ## Test Plan ran the below ``` rm -rf .venv target/debug/uv venv # commenting out the line below triggers the change.. # target/debug/uv pip install no-manylinux target/debug/uv pip install cryptography --no-cache ``` is there an easy way to add this into the existing snapshot-based test suite? looking around to see if there's a way that doesn't involve something implementation-dependent like mocks. ~update: i think the output does differ between these two, so probably we can use that.~ i lied - that "building..." output seems to be discarded.
This commit is contained in:
parent
2e02d579a0
commit
c9774e9c43
7 changed files with 128 additions and 4 deletions
|
@ -127,8 +127,9 @@ mod resolver {
|
||||||
Arch::Aarch64,
|
Arch::Aarch64,
|
||||||
);
|
);
|
||||||
|
|
||||||
static TAGS: LazyLock<Tags> =
|
static TAGS: LazyLock<Tags> = LazyLock::new(|| {
|
||||||
LazyLock::new(|| Tags::from_env(&PLATFORM, (3, 11), "cpython", (3, 11), false).unwrap());
|
Tags::from_env(&PLATFORM, (3, 11), "cpython", (3, 11), false, false).unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
pub(crate) async fn resolve(
|
pub(crate) async fn resolve(
|
||||||
manifest: Manifest,
|
manifest: Manifest,
|
||||||
|
|
|
@ -94,10 +94,19 @@ impl Tags {
|
||||||
python_version: (u8, u8),
|
python_version: (u8, u8),
|
||||||
implementation_name: &str,
|
implementation_name: &str,
|
||||||
implementation_version: (u8, u8),
|
implementation_version: (u8, u8),
|
||||||
|
manylinux_compatible: bool,
|
||||||
gil_disabled: bool,
|
gil_disabled: bool,
|
||||||
) -> Result<Self, TagsError> {
|
) -> Result<Self, TagsError> {
|
||||||
let implementation = Implementation::parse(implementation_name, gil_disabled)?;
|
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());
|
let mut tags = Vec::with_capacity(5 * platform_tags.len());
|
||||||
|
|
||||||
|
@ -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.
|
/// Check full tag ordering.
|
||||||
/// The list is displayed in decreasing priority.
|
/// The list is displayed in decreasing priority.
|
||||||
///
|
///
|
||||||
|
@ -951,6 +1018,7 @@ mod tests {
|
||||||
(3, 9),
|
(3, 9),
|
||||||
"cpython",
|
"cpython",
|
||||||
(3, 9),
|
(3, 9),
|
||||||
|
true,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -1575,6 +1643,7 @@ mod tests {
|
||||||
"cpython",
|
"cpython",
|
||||||
(3, 9),
|
(3, 9),
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
|
|
|
@ -542,6 +542,17 @@ def main() -> None:
|
||||||
"python_version": ".".join(platform.python_version_tuple()[:2]),
|
"python_version": ".".join(platform.python_version_tuple()[:2]),
|
||||||
"sys_platform": sys.platform,
|
"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 = {
|
interpreter_info = {
|
||||||
"result": "success",
|
"result": "success",
|
||||||
"markers": markers,
|
"markers": markers,
|
||||||
|
@ -554,7 +565,8 @@ def main() -> None:
|
||||||
"stdlib": sysconfig.get_path("stdlib"),
|
"stdlib": sysconfig.get_path("stdlib"),
|
||||||
"scheme": get_scheme(),
|
"scheme": get_scheme(),
|
||||||
"virtualenv": get_virtualenv(),
|
"virtualenv": get_virtualenv(),
|
||||||
"platform": get_operating_system_and_architecture(),
|
"platform": os_and_arch,
|
||||||
|
"manylinux_compatible": manylinux_compatible,
|
||||||
# The `t` abiflag for freethreading Python.
|
# The `t` abiflag for freethreading Python.
|
||||||
# https://peps.python.org/pep-0703/#build-configuration-changes
|
# https://peps.python.org/pep-0703/#build-configuration-changes
|
||||||
"gil_disabled": bool(sysconfig.get_config_var("Py_GIL_DISABLED")),
|
"gil_disabled": bool(sysconfig.get_config_var("Py_GIL_DISABLED")),
|
||||||
|
|
|
@ -33,6 +33,7 @@ pub struct Interpreter {
|
||||||
markers: Box<MarkerEnvironment>,
|
markers: Box<MarkerEnvironment>,
|
||||||
scheme: Scheme,
|
scheme: Scheme,
|
||||||
virtualenv: Scheme,
|
virtualenv: Scheme,
|
||||||
|
manylinux_compatible: bool,
|
||||||
sys_prefix: PathBuf,
|
sys_prefix: PathBuf,
|
||||||
sys_base_exec_prefix: PathBuf,
|
sys_base_exec_prefix: PathBuf,
|
||||||
sys_base_prefix: PathBuf,
|
sys_base_prefix: PathBuf,
|
||||||
|
@ -63,6 +64,7 @@ impl Interpreter {
|
||||||
markers: Box::new(info.markers),
|
markers: Box::new(info.markers),
|
||||||
scheme: info.scheme,
|
scheme: info.scheme,
|
||||||
virtualenv: info.virtualenv,
|
virtualenv: info.virtualenv,
|
||||||
|
manylinux_compatible: info.manylinux_compatible,
|
||||||
sys_prefix: info.sys_prefix,
|
sys_prefix: info.sys_prefix,
|
||||||
sys_base_exec_prefix: info.sys_base_exec_prefix,
|
sys_base_exec_prefix: info.sys_base_exec_prefix,
|
||||||
pointer_size: info.pointer_size,
|
pointer_size: info.pointer_size,
|
||||||
|
@ -176,6 +178,7 @@ impl Interpreter {
|
||||||
self.python_tuple(),
|
self.python_tuple(),
|
||||||
self.implementation_name(),
|
self.implementation_name(),
|
||||||
self.implementation_tuple(),
|
self.implementation_tuple(),
|
||||||
|
self.manylinux_compatible,
|
||||||
self.gil_disabled,
|
self.gil_disabled,
|
||||||
)?;
|
)?;
|
||||||
self.tags.set(tags).expect("tags should not be set");
|
self.tags.set(tags).expect("tags should not be set");
|
||||||
|
@ -373,6 +376,11 @@ impl Interpreter {
|
||||||
&self.virtualenv
|
&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).
|
/// Return the [`PointerSize`] of the Python interpreter (i.e., 32- vs. 64-bit).
|
||||||
pub fn pointer_size(&self) -> PointerSize {
|
pub fn pointer_size(&self) -> PointerSize {
|
||||||
self.pointer_size
|
self.pointer_size
|
||||||
|
@ -555,6 +563,7 @@ struct InterpreterInfo {
|
||||||
markers: MarkerEnvironment,
|
markers: MarkerEnvironment,
|
||||||
scheme: Scheme,
|
scheme: Scheme,
|
||||||
virtualenv: Scheme,
|
virtualenv: Scheme,
|
||||||
|
manylinux_compatible: bool,
|
||||||
sys_prefix: PathBuf,
|
sys_prefix: PathBuf,
|
||||||
sys_base_exec_prefix: PathBuf,
|
sys_base_exec_prefix: PathBuf,
|
||||||
sys_base_prefix: PathBuf,
|
sys_base_prefix: PathBuf,
|
||||||
|
@ -785,6 +794,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
"arch": "x86_64"
|
"arch": "x86_64"
|
||||||
},
|
},
|
||||||
|
"manylinux_compatible": false,
|
||||||
"markers": {
|
"markers": {
|
||||||
"implementation_name": "cpython",
|
"implementation_name": "cpython",
|
||||||
"implementation_version": "3.12.0",
|
"implementation_version": "3.12.0",
|
||||||
|
|
|
@ -210,6 +210,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
"arch": "x86_64"
|
"arch": "x86_64"
|
||||||
},
|
},
|
||||||
|
"manylinux_compatible": true,
|
||||||
"markers": {
|
"markers": {
|
||||||
"implementation_name": "{IMPLEMENTATION}",
|
"implementation_name": "{IMPLEMENTATION}",
|
||||||
"implementation_version": "{FULL_VERSION}",
|
"implementation_version": "{FULL_VERSION}",
|
||||||
|
|
|
@ -29,6 +29,7 @@ pub(crate) fn resolution_environment(
|
||||||
(python_version.major(), python_version.minor()),
|
(python_version.major(), python_version.minor()),
|
||||||
interpreter.implementation_name(),
|
interpreter.implementation_name(),
|
||||||
interpreter.implementation_tuple(),
|
interpreter.implementation_tuple(),
|
||||||
|
interpreter.manylinux_compatible(),
|
||||||
interpreter.gil_disabled(),
|
interpreter.gil_disabled(),
|
||||||
)?),
|
)?),
|
||||||
(Some(python_platform), None) => Cow::Owned(Tags::from_env(
|
(Some(python_platform), None) => Cow::Owned(Tags::from_env(
|
||||||
|
@ -36,6 +37,7 @@ pub(crate) fn resolution_environment(
|
||||||
interpreter.python_tuple(),
|
interpreter.python_tuple(),
|
||||||
interpreter.implementation_name(),
|
interpreter.implementation_name(),
|
||||||
interpreter.implementation_tuple(),
|
interpreter.implementation_tuple(),
|
||||||
|
interpreter.manylinux_compatible(),
|
||||||
interpreter.gil_disabled(),
|
interpreter.gil_disabled(),
|
||||||
)?),
|
)?),
|
||||||
(None, Some(python_version)) => Cow::Owned(Tags::from_env(
|
(None, Some(python_version)) => Cow::Owned(Tags::from_env(
|
||||||
|
@ -43,6 +45,7 @@ pub(crate) fn resolution_environment(
|
||||||
(python_version.major(), python_version.minor()),
|
(python_version.major(), python_version.minor()),
|
||||||
interpreter.implementation_name(),
|
interpreter.implementation_name(),
|
||||||
interpreter.implementation_tuple(),
|
interpreter.implementation_tuple(),
|
||||||
|
interpreter.manylinux_compatible(),
|
||||||
interpreter.gil_disabled(),
|
interpreter.gil_disabled(),
|
||||||
)?),
|
)?),
|
||||||
(None, None) => Cow::Borrowed(interpreter.tags()?),
|
(None, None) => Cow::Borrowed(interpreter.tags()?),
|
||||||
|
|
|
@ -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
|
Additionally, and in contrast to pip, uv's resolver will still read metadata from pre-built binary
|
||||||
distributions when `--no-binary` is provided.
|
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
|
## Bytecode compilation
|
||||||
|
|
||||||
Unlike pip, uv does not compile `.py` files to `.pyc` files during installation by default (i.e., uv
|
Unlike pip, uv does not compile `.py` files to `.pyc` files during installation by default (i.e., uv
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue