Open tgolsson opened 1 year ago
As I pointed out to @tgolsson offline, a multi-platform lock example (Pants does not support these) for this case looks like so:
$ pex3 lock create \
--index https://download.pytorch.org/whl/cpu/ \
--platform macosx-10.9-x86_64-cp-39-cp39 \
--platform linux-x86_64-cp-39-cp39 \
"torch==1.11.0+cpu ; platform_system != 'Darwin'" \
"torch===1.11.0 ; platform_system == 'Darwin'" \
--pip-version latest \
--resolver-version pip-2020-resolver \
--indent 2 \
-o lock.mp.json
In real life you'd almost certainly want to use a pair of --complete-platform
- this was just for quick demo. NB: I use ===
to semi-ameliorate the local version abuse footguns of torch here.
The result is a lock file containing 2 locked resolves:
{
"allow_builds": true,
"allow_prereleases": false,
"allow_wheels": true,
"build_isolation": true,
"constraints": [],
"locked_resolves": [
{
"locked_requirements": [
{
"artifacts": [
{
"algorithm": "sha256",
"hash": "50fd9bf85c578c871c28f1cb0ace9dfc6024401c7f399b174fb0f370899f4454",
"url": "https://download.pytorch.org/whl/cpu/torch-1.11.0-cp39-none-macosx_10_9_x86_64.whl"
}
],
"project_name": "torch",
"requires_dists": [
"typing-extensions"
],
"requires_python": ">=3.7.0",
"version": "1.11.0"
},
{
"artifacts": [
{
"algorithm": "sha256",
"hash": "fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4",
"url": "https://files.pythonhosted.org/packages/31/25/5abcd82372d3d4a3932e1fa8c3dbf9efac10cc7c0d16e78467460571b404/typing_extensions-4.5.0-py3-none-any.whl"
}
],
"project_name": "typing-extensions",
"requires_dists": [],
"requires_python": ">=3.7",
"version": "4.5.0"
}
],
"platform_tag": [
"cp39",
"cp39",
"macosx_10_9_x86_64"
]
},
{
"locked_requirements": [
{
"artifacts": [
{
"algorithm": "sha256",
"hash": "544c13ef120531ec2f28a3c858c06e600d514a6dfe09b4dd6fd0262088dd2fa3",
"url": "https://download.pytorch.org/whl/cpu/torch-1.11.0%2Bcpu-cp39-cp39-linux_x86_64.whl"
}
],
"project_name": "torch",
"requires_dists": [
"typing-extensions"
],
"requires_python": ">=3.7.0",
"version": "1.11.0+cpu"
},
{
"artifacts": [
{
"algorithm": "sha256",
"hash": "fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4",
"url": "https://files.pythonhosted.org/packages/31/25/5abcd82372d3d4a3932e1fa8c3dbf9efac10cc7c0d16e78467460571b404/typing_extensions-4.5.0-py3-none-any.whl"
}
],
"project_name": "typing-extensions",
"requires_dists": [],
"requires_python": ">=3.7",
"version": "4.5.0"
}
],
"platform_tag": [
"cp39",
"cp39",
"linux_x86_64"
]
}
],
"path_mappings": {},
"pex_version": "2.1.136",
"pip_version": "23.1.2",
"prefer_older_binary": false,
"requirements": [
"torch==1.11.0+cpu; platform_system != \"Darwin\"",
"torch===1.11.0; platform_system == \"Darwin\""
],
"requires_python": [],
"resolver_version": "pip-2020-resolver",
"style": "strict",
"target_systems": [],
"transitive": true,
"use_pep517": null
}
Pex has the smarts to pick the best fitting lock - if any - at use time.
@jsirois Thank you for the clarification! It even works when not requiring local versions.
$ pex3 lock create \
--index https://download.pytorch.org/whl/cpu/ \
--platform macosx-10.9-x86_64-cp-39-cp39 \
--platform linux-x86_64-cp-39-cp39 \
"torch>=1.11.0,<1.12" \
--pip-version latest \
--resolver-version pip-2020-resolver \
--indent 2 \
-o lock.mp.json
Which still gives the proper selection, doesn't require equality matching. With multiple index it'd require per-resolve indexes though, but I think it'd be nice. Having to use ==
is very unergonomic and a bit against the usage of a lockfile.
{
"allow_builds": true,
"allow_prereleases": false,
"allow_wheels": true,
"build_isolation": true,
"constraints": [],
"locked_resolves": [
{
"locked_requirements": [
{
"artifacts": [
{
"algorithm": "sha256",
"hash": "50fd9bf85c578c871c28f1cb0ace9dfc6024401c7f399b174fb0f370899f4454",
"url": "https://download.pytorch.org/whl/cpu/torch-1.11.0-cp39-none-macosx_10_9_x86_64.whl"
}
],
"project_name": "torch",
"requires_dists": [
"typing-extensions"
],
"requires_python": ">=3.7.0",
"version": "1.11.0"
},
{
"artifacts": [
{
"algorithm": "sha256",
"hash": "fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4",
"url": "https://files.pythonhosted.org/packages/31/25/5abcd82372d3d4a3932e1fa8c3dbf9efac10cc7c0d16e78467460571b404/typing_extensions-4.5.0-py3-none-any.whl"
}
],
"project_name": "typing-extensions",
"requires_dists": [],
"requires_python": ">=3.7",
"version": "4.5.0"
}
],
"platform_tag": [
"cp39",
"cp39",
"macosx_10_9_x86_64"
]
},
{
"locked_requirements": [
{
"artifacts": [
{
"algorithm": "sha256",
"hash": "544c13ef120531ec2f28a3c858c06e600d514a6dfe09b4dd6fd0262088dd2fa3",
"url": "https://download.pytorch.org/whl/cpu/torch-1.11.0%2Bcpu-cp39-cp39-linux_x86_64.whl"
}
],
"project_name": "torch",
"requires_dists": [
"typing-extensions"
],
"requires_python": ">=3.7.0",
"version": "1.11.0+cpu"
},
{
"artifacts": [
{
"algorithm": "sha256",
"hash": "fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4",
"url": "https://files.pythonhosted.org/packages/31/25/5abcd82372d3d4a3932e1fa8c3dbf9efac10cc7c0d16e78467460571b404/typing_extensions-4.5.0-py3-none-any.whl"
}
],
"project_name": "typing-extensions",
"requires_dists": [],
"requires_python": ">=3.7",
"version": "4.5.0"
}
],
"platform_tag": [
"cp39",
"cp39",
"linux_x86_64"
]
}
],
"path_mappings": {},
"pex_version": "2.1.136",
"pip_version": "23.1.2",
"prefer_older_binary": false,
"requirements": [
"torch<1.12.0,>=1.11.0"
],
"requires_python": [],
"resolver_version": "pip-2020-resolver",
"style": "strict",
"target_systems": [],
"transitive": true,
"use_pep517": null
}
The question is if we can "shorthand" the --platform arguments as well? I'm thinking about onboarding/ease-of-adoption...
The question is if we can "shorthand" the --platform arguments as well? I'm thinking about onboarding/ease-of-adoption...
Well, the problem is worse. As I mentioned, --platform
is a toy example and --complete-platform
is what should really be used. Those require actually going to each target machine / python pair and generating a complete platform JSON blob. Now Pants could maybe ship a catalog of common ones, but you'd still need to spell out at least 3 components to identify one: OS, Chip Arch, Python Version.
The only way to do this without platforms is to allow building up a lock instead of creating it all at once. Basically lock on 1 machine, now lock on another, now combine. In that sort of procedure you never need to spell out the complete platform details since you're running the lock operation natively on the target platform. For that convenience though, you buy the thorny problem of combining the results.
See "Rationale" item 2 for one idea how to handle combination of multiple locks. The idea expressed in this rejected PEP is to use a lock directory with many files instead of a single lock file with many locked resolves like PEX currently does it: https://discuss.python.org/t/pep-665-specifying-installation-requirements-for-python-projects/9911
@jsirois Gotcha. I was naively thinking that some information could be derived from the interpreter constraints, but that's only one axis (the others being ISA and OS). But as far as I can tell there's already support for platforms
in Pants, so would adding a lockstyle: PexLockstyle
enumeration for the pex_binary and piping that through be enough to replicate the "naive" variant?
@tgolsson this is exactly what is needed to resolve for Python:
Requires-Python
matching).1 is easy. 2 and 3 are not. Here's what Pex does for 2 when you only give it --platform
(an "abbreviated" platform):
https://github.com/pantsbuild/pex/blob/8b8147cd23256e7c5a141e79f9ab27549283e74f/pex/pep_508.py#L32-L118
Now you seem to be grumbling about this "abbreviated" form! And this form is as "nice" as it gets, and totally insufficient and should actually be killed. I at least have the abbreviated platforms now causing resolves using them to raise whenever they encounter unset environment marker values (used to silently "succeed" with wrong resolve results).
The complete platform is really and truly the only way to go. You can use Pex's pex3
tool to create one. Here's one I use for doing foreign resolves for Windows 11 64 bit CPython 3.11:
{
"marker_environment": {
"implementation_name": "cpython",
"implementation_version": "3.11.0",
"os_name": "nt",
"platform_machine": "AMD64",
"platform_python_implementation": "CPython",
"platform_release": "10",
"platform_system": "Windows",
"platform_version": "10.0.22621",
"python_full_version": "3.11.0",
"python_version": "3.11",
"sys_platform": "win32"
},
"compatible_tags": [
"cp311-cp311-win_amd64",
"cp311-abi3-win_amd64",
"cp311-none-win_amd64",
"cp310-abi3-win_amd64",
"cp39-abi3-win_amd64",
"cp38-abi3-win_amd64",
"cp37-abi3-win_amd64",
"cp36-abi3-win_amd64",
"cp35-abi3-win_amd64",
"cp34-abi3-win_amd64",
"cp33-abi3-win_amd64",
"cp32-abi3-win_amd64",
"py311-none-win_amd64",
"py3-none-win_amd64",
"py310-none-win_amd64",
"py39-none-win_amd64",
"py38-none-win_amd64",
"py37-none-win_amd64",
"py36-none-win_amd64",
"py35-none-win_amd64",
"py34-none-win_amd64",
"py33-none-win_amd64",
"py32-none-win_amd64",
"py31-none-win_amd64",
"py30-none-win_amd64",
"py311-none-any",
"py3-none-any",
"py310-none-any",
"py39-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"
]
}
N.B.: Windows has the smallest compatible tags lists. Linux tends towards ~700 tags for modern glibc machines.
The command to get that:
pex3 interpreter inspect --python /usr/bin/python3.9 --markers --tags --indent 2
{
"path": "/usr/bin/python3.9",
"compatible_tags": [
"cp39-cp39-manylinux_2_35_x86_64",
"cp39-cp39-manylinux_2_34_x86_64",
"cp39-cp39-manylinux_2_33_x86_64",
"cp39-cp39-manylinux_2_32_x86_64",
"cp39-cp39-manylinux_2_31_x86_64",
"cp39-cp39-manylinux_2_30_x86_64",
"cp39-cp39-manylinux_2_29_x86_64",
"cp39-cp39-manylinux_2_28_x86_64",
"cp39-cp39-manylinux_2_27_x86_64",
"cp39-cp39-manylinux_2_26_x86_64",
"cp39-cp39-manylinux_2_25_x86_64",
"cp39-cp39-manylinux_2_24_x86_64",
"cp39-cp39-manylinux_2_23_x86_64",
"cp39-cp39-manylinux_2_22_x86_64",
"cp39-cp39-manylinux_2_21_x86_64",
"cp39-cp39-manylinux_2_20_x86_64",
"cp39-cp39-manylinux_2_19_x86_64",
"cp39-cp39-manylinux_2_18_x86_64",
"cp39-cp39-manylinux_2_17_x86_64",
"cp39-cp39-manylinux2014_x86_64",
"cp39-cp39-manylinux_2_16_x86_64",
"cp39-cp39-manylinux_2_15_x86_64",
"cp39-cp39-manylinux_2_14_x86_64",
"cp39-cp39-manylinux_2_13_x86_64",
"cp39-cp39-manylinux_2_12_x86_64",
"cp39-cp39-manylinux2010_x86_64",
"cp39-cp39-manylinux_2_11_x86_64",
"cp39-cp39-manylinux_2_10_x86_64",
"cp39-cp39-manylinux_2_9_x86_64",
"cp39-cp39-manylinux_2_8_x86_64",
"cp39-cp39-manylinux_2_7_x86_64",
"cp39-cp39-manylinux_2_6_x86_64",
"cp39-cp39-manylinux_2_5_x86_64",
"cp39-cp39-manylinux1_x86_64",
"cp39-cp39-linux_x86_64",
"cp39-abi3-manylinux_2_35_x86_64",
"cp39-abi3-manylinux_2_34_x86_64",
"cp39-abi3-manylinux_2_33_x86_64",
"cp39-abi3-manylinux_2_32_x86_64",
"cp39-abi3-manylinux_2_31_x86_64",
"cp39-abi3-manylinux_2_30_x86_64",
"cp39-abi3-manylinux_2_29_x86_64",
"cp39-abi3-manylinux_2_28_x86_64",
"cp39-abi3-manylinux_2_27_x86_64",
"cp39-abi3-manylinux_2_26_x86_64",
"cp39-abi3-manylinux_2_25_x86_64",
"cp39-abi3-manylinux_2_24_x86_64",
"cp39-abi3-manylinux_2_23_x86_64",
"cp39-abi3-manylinux_2_22_x86_64",
"cp39-abi3-manylinux_2_21_x86_64",
"cp39-abi3-manylinux_2_20_x86_64",
"cp39-abi3-manylinux_2_19_x86_64",
"cp39-abi3-manylinux_2_18_x86_64",
"cp39-abi3-manylinux_2_17_x86_64",
"cp39-abi3-manylinux2014_x86_64",
"cp39-abi3-manylinux_2_16_x86_64",
"cp39-abi3-manylinux_2_15_x86_64",
"cp39-abi3-manylinux_2_14_x86_64",
"cp39-abi3-manylinux_2_13_x86_64",
"cp39-abi3-manylinux_2_12_x86_64",
"cp39-abi3-manylinux2010_x86_64",
"cp39-abi3-manylinux_2_11_x86_64",
"cp39-abi3-manylinux_2_10_x86_64",
"cp39-abi3-manylinux_2_9_x86_64",
"cp39-abi3-manylinux_2_8_x86_64",
"cp39-abi3-manylinux_2_7_x86_64",
"cp39-abi3-manylinux_2_6_x86_64",
"cp39-abi3-manylinux_2_5_x86_64",
"cp39-abi3-manylinux1_x86_64",
"cp39-abi3-linux_x86_64",
"cp39-none-manylinux_2_35_x86_64",
"cp39-none-manylinux_2_34_x86_64",
"cp39-none-manylinux_2_33_x86_64",
"cp39-none-manylinux_2_32_x86_64",
"cp39-none-manylinux_2_31_x86_64",
"cp39-none-manylinux_2_30_x86_64",
"cp39-none-manylinux_2_29_x86_64",
"cp39-none-manylinux_2_28_x86_64",
"cp39-none-manylinux_2_27_x86_64",
"cp39-none-manylinux_2_26_x86_64",
"cp39-none-manylinux_2_25_x86_64",
"cp39-none-manylinux_2_24_x86_64",
"cp39-none-manylinux_2_23_x86_64",
"cp39-none-manylinux_2_22_x86_64",
"cp39-none-manylinux_2_21_x86_64",
"cp39-none-manylinux_2_20_x86_64",
"cp39-none-manylinux_2_19_x86_64",
"cp39-none-manylinux_2_18_x86_64",
"cp39-none-manylinux_2_17_x86_64",
"cp39-none-manylinux2014_x86_64",
"cp39-none-manylinux_2_16_x86_64",
"cp39-none-manylinux_2_15_x86_64",
"cp39-none-manylinux_2_14_x86_64",
"cp39-none-manylinux_2_13_x86_64",
"cp39-none-manylinux_2_12_x86_64",
"cp39-none-manylinux2010_x86_64",
"cp39-none-manylinux_2_11_x86_64",
"cp39-none-manylinux_2_10_x86_64",
"cp39-none-manylinux_2_9_x86_64",
"cp39-none-manylinux_2_8_x86_64",
"cp39-none-manylinux_2_7_x86_64",
"cp39-none-manylinux_2_6_x86_64",
"cp39-none-manylinux_2_5_x86_64",
"cp39-none-manylinux1_x86_64",
"cp39-none-linux_x86_64",
"cp38-abi3-manylinux_2_35_x86_64",
"cp38-abi3-manylinux_2_34_x86_64",
"cp38-abi3-manylinux_2_33_x86_64",
"cp38-abi3-manylinux_2_32_x86_64",
"cp38-abi3-manylinux_2_31_x86_64",
"cp38-abi3-manylinux_2_30_x86_64",
"cp38-abi3-manylinux_2_29_x86_64",
"cp38-abi3-manylinux_2_28_x86_64",
"cp38-abi3-manylinux_2_27_x86_64",
"cp38-abi3-manylinux_2_26_x86_64",
"cp38-abi3-manylinux_2_25_x86_64",
"cp38-abi3-manylinux_2_24_x86_64",
"cp38-abi3-manylinux_2_23_x86_64",
"cp38-abi3-manylinux_2_22_x86_64",
"cp38-abi3-manylinux_2_21_x86_64",
"cp38-abi3-manylinux_2_20_x86_64",
"cp38-abi3-manylinux_2_19_x86_64",
"cp38-abi3-manylinux_2_18_x86_64",
"cp38-abi3-manylinux_2_17_x86_64",
"cp38-abi3-manylinux2014_x86_64",
"cp38-abi3-manylinux_2_16_x86_64",
"cp38-abi3-manylinux_2_15_x86_64",
"cp38-abi3-manylinux_2_14_x86_64",
"cp38-abi3-manylinux_2_13_x86_64",
"cp38-abi3-manylinux_2_12_x86_64",
"cp38-abi3-manylinux2010_x86_64",
"cp38-abi3-manylinux_2_11_x86_64",
"cp38-abi3-manylinux_2_10_x86_64",
"cp38-abi3-manylinux_2_9_x86_64",
"cp38-abi3-manylinux_2_8_x86_64",
"cp38-abi3-manylinux_2_7_x86_64",
"cp38-abi3-manylinux_2_6_x86_64",
"cp38-abi3-manylinux_2_5_x86_64",
"cp38-abi3-manylinux1_x86_64",
"cp38-abi3-linux_x86_64",
"cp37-abi3-manylinux_2_35_x86_64",
"cp37-abi3-manylinux_2_34_x86_64",
"cp37-abi3-manylinux_2_33_x86_64",
"cp37-abi3-manylinux_2_32_x86_64",
"cp37-abi3-manylinux_2_31_x86_64",
"cp37-abi3-manylinux_2_30_x86_64",
"cp37-abi3-manylinux_2_29_x86_64",
"cp37-abi3-manylinux_2_28_x86_64",
"cp37-abi3-manylinux_2_27_x86_64",
"cp37-abi3-manylinux_2_26_x86_64",
"cp37-abi3-manylinux_2_25_x86_64",
"cp37-abi3-manylinux_2_24_x86_64",
"cp37-abi3-manylinux_2_23_x86_64",
"cp37-abi3-manylinux_2_22_x86_64",
"cp37-abi3-manylinux_2_21_x86_64",
"cp37-abi3-manylinux_2_20_x86_64",
"cp37-abi3-manylinux_2_19_x86_64",
"cp37-abi3-manylinux_2_18_x86_64",
"cp37-abi3-manylinux_2_17_x86_64",
"cp37-abi3-manylinux2014_x86_64",
"cp37-abi3-manylinux_2_16_x86_64",
"cp37-abi3-manylinux_2_15_x86_64",
"cp37-abi3-manylinux_2_14_x86_64",
"cp37-abi3-manylinux_2_13_x86_64",
"cp37-abi3-manylinux_2_12_x86_64",
"cp37-abi3-manylinux2010_x86_64",
"cp37-abi3-manylinux_2_11_x86_64",
"cp37-abi3-manylinux_2_10_x86_64",
"cp37-abi3-manylinux_2_9_x86_64",
"cp37-abi3-manylinux_2_8_x86_64",
"cp37-abi3-manylinux_2_7_x86_64",
"cp37-abi3-manylinux_2_6_x86_64",
"cp37-abi3-manylinux_2_5_x86_64",
"cp37-abi3-manylinux1_x86_64",
"cp37-abi3-linux_x86_64",
"cp36-abi3-manylinux_2_35_x86_64",
"cp36-abi3-manylinux_2_34_x86_64",
"cp36-abi3-manylinux_2_33_x86_64",
"cp36-abi3-manylinux_2_32_x86_64",
"cp36-abi3-manylinux_2_31_x86_64",
"cp36-abi3-manylinux_2_30_x86_64",
"cp36-abi3-manylinux_2_29_x86_64",
"cp36-abi3-manylinux_2_28_x86_64",
"cp36-abi3-manylinux_2_27_x86_64",
"cp36-abi3-manylinux_2_26_x86_64",
"cp36-abi3-manylinux_2_25_x86_64",
"cp36-abi3-manylinux_2_24_x86_64",
"cp36-abi3-manylinux_2_23_x86_64",
"cp36-abi3-manylinux_2_22_x86_64",
"cp36-abi3-manylinux_2_21_x86_64",
"cp36-abi3-manylinux_2_20_x86_64",
"cp36-abi3-manylinux_2_19_x86_64",
"cp36-abi3-manylinux_2_18_x86_64",
"cp36-abi3-manylinux_2_17_x86_64",
"cp36-abi3-manylinux2014_x86_64",
"cp36-abi3-manylinux_2_16_x86_64",
"cp36-abi3-manylinux_2_15_x86_64",
"cp36-abi3-manylinux_2_14_x86_64",
"cp36-abi3-manylinux_2_13_x86_64",
"cp36-abi3-manylinux_2_12_x86_64",
"cp36-abi3-manylinux2010_x86_64",
"cp36-abi3-manylinux_2_11_x86_64",
"cp36-abi3-manylinux_2_10_x86_64",
"cp36-abi3-manylinux_2_9_x86_64",
"cp36-abi3-manylinux_2_8_x86_64",
"cp36-abi3-manylinux_2_7_x86_64",
"cp36-abi3-manylinux_2_6_x86_64",
"cp36-abi3-manylinux_2_5_x86_64",
"cp36-abi3-manylinux1_x86_64",
"cp36-abi3-linux_x86_64",
"cp35-abi3-manylinux_2_35_x86_64",
"cp35-abi3-manylinux_2_34_x86_64",
"cp35-abi3-manylinux_2_33_x86_64",
"cp35-abi3-manylinux_2_32_x86_64",
"cp35-abi3-manylinux_2_31_x86_64",
"cp35-abi3-manylinux_2_30_x86_64",
"cp35-abi3-manylinux_2_29_x86_64",
"cp35-abi3-manylinux_2_28_x86_64",
"cp35-abi3-manylinux_2_27_x86_64",
"cp35-abi3-manylinux_2_26_x86_64",
"cp35-abi3-manylinux_2_25_x86_64",
"cp35-abi3-manylinux_2_24_x86_64",
"cp35-abi3-manylinux_2_23_x86_64",
"cp35-abi3-manylinux_2_22_x86_64",
"cp35-abi3-manylinux_2_21_x86_64",
"cp35-abi3-manylinux_2_20_x86_64",
"cp35-abi3-manylinux_2_19_x86_64",
"cp35-abi3-manylinux_2_18_x86_64",
"cp35-abi3-manylinux_2_17_x86_64",
"cp35-abi3-manylinux2014_x86_64",
"cp35-abi3-manylinux_2_16_x86_64",
"cp35-abi3-manylinux_2_15_x86_64",
"cp35-abi3-manylinux_2_14_x86_64",
"cp35-abi3-manylinux_2_13_x86_64",
"cp35-abi3-manylinux_2_12_x86_64",
"cp35-abi3-manylinux2010_x86_64",
"cp35-abi3-manylinux_2_11_x86_64",
"cp35-abi3-manylinux_2_10_x86_64",
"cp35-abi3-manylinux_2_9_x86_64",
"cp35-abi3-manylinux_2_8_x86_64",
"cp35-abi3-manylinux_2_7_x86_64",
"cp35-abi3-manylinux_2_6_x86_64",
"cp35-abi3-manylinux_2_5_x86_64",
"cp35-abi3-manylinux1_x86_64",
"cp35-abi3-linux_x86_64",
"cp34-abi3-manylinux_2_35_x86_64",
"cp34-abi3-manylinux_2_34_x86_64",
"cp34-abi3-manylinux_2_33_x86_64",
"cp34-abi3-manylinux_2_32_x86_64",
"cp34-abi3-manylinux_2_31_x86_64",
"cp34-abi3-manylinux_2_30_x86_64",
"cp34-abi3-manylinux_2_29_x86_64",
"cp34-abi3-manylinux_2_28_x86_64",
"cp34-abi3-manylinux_2_27_x86_64",
"cp34-abi3-manylinux_2_26_x86_64",
"cp34-abi3-manylinux_2_25_x86_64",
"cp34-abi3-manylinux_2_24_x86_64",
"cp34-abi3-manylinux_2_23_x86_64",
"cp34-abi3-manylinux_2_22_x86_64",
"cp34-abi3-manylinux_2_21_x86_64",
"cp34-abi3-manylinux_2_20_x86_64",
"cp34-abi3-manylinux_2_19_x86_64",
"cp34-abi3-manylinux_2_18_x86_64",
"cp34-abi3-manylinux_2_17_x86_64",
"cp34-abi3-manylinux2014_x86_64",
"cp34-abi3-manylinux_2_16_x86_64",
"cp34-abi3-manylinux_2_15_x86_64",
"cp34-abi3-manylinux_2_14_x86_64",
"cp34-abi3-manylinux_2_13_x86_64",
"cp34-abi3-manylinux_2_12_x86_64",
"cp34-abi3-manylinux2010_x86_64",
"cp34-abi3-manylinux_2_11_x86_64",
"cp34-abi3-manylinux_2_10_x86_64",
"cp34-abi3-manylinux_2_9_x86_64",
"cp34-abi3-manylinux_2_8_x86_64",
"cp34-abi3-manylinux_2_7_x86_64",
"cp34-abi3-manylinux_2_6_x86_64",
"cp34-abi3-manylinux_2_5_x86_64",
"cp34-abi3-manylinux1_x86_64",
"cp34-abi3-linux_x86_64",
"cp33-abi3-manylinux_2_35_x86_64",
"cp33-abi3-manylinux_2_34_x86_64",
"cp33-abi3-manylinux_2_33_x86_64",
"cp33-abi3-manylinux_2_32_x86_64",
"cp33-abi3-manylinux_2_31_x86_64",
"cp33-abi3-manylinux_2_30_x86_64",
"cp33-abi3-manylinux_2_29_x86_64",
"cp33-abi3-manylinux_2_28_x86_64",
"cp33-abi3-manylinux_2_27_x86_64",
"cp33-abi3-manylinux_2_26_x86_64",
"cp33-abi3-manylinux_2_25_x86_64",
"cp33-abi3-manylinux_2_24_x86_64",
"cp33-abi3-manylinux_2_23_x86_64",
"cp33-abi3-manylinux_2_22_x86_64",
"cp33-abi3-manylinux_2_21_x86_64",
"cp33-abi3-manylinux_2_20_x86_64",
"cp33-abi3-manylinux_2_19_x86_64",
"cp33-abi3-manylinux_2_18_x86_64",
"cp33-abi3-manylinux_2_17_x86_64",
"cp33-abi3-manylinux2014_x86_64",
"cp33-abi3-manylinux_2_16_x86_64",
"cp33-abi3-manylinux_2_15_x86_64",
"cp33-abi3-manylinux_2_14_x86_64",
"cp33-abi3-manylinux_2_13_x86_64",
"cp33-abi3-manylinux_2_12_x86_64",
"cp33-abi3-manylinux2010_x86_64",
"cp33-abi3-manylinux_2_11_x86_64",
"cp33-abi3-manylinux_2_10_x86_64",
"cp33-abi3-manylinux_2_9_x86_64",
"cp33-abi3-manylinux_2_8_x86_64",
"cp33-abi3-manylinux_2_7_x86_64",
"cp33-abi3-manylinux_2_6_x86_64",
"cp33-abi3-manylinux_2_5_x86_64",
"cp33-abi3-manylinux1_x86_64",
"cp33-abi3-linux_x86_64",
"cp32-abi3-manylinux_2_35_x86_64",
"cp32-abi3-manylinux_2_34_x86_64",
"cp32-abi3-manylinux_2_33_x86_64",
"cp32-abi3-manylinux_2_32_x86_64",
"cp32-abi3-manylinux_2_31_x86_64",
"cp32-abi3-manylinux_2_30_x86_64",
"cp32-abi3-manylinux_2_29_x86_64",
"cp32-abi3-manylinux_2_28_x86_64",
"cp32-abi3-manylinux_2_27_x86_64",
"cp32-abi3-manylinux_2_26_x86_64",
"cp32-abi3-manylinux_2_25_x86_64",
"cp32-abi3-manylinux_2_24_x86_64",
"cp32-abi3-manylinux_2_23_x86_64",
"cp32-abi3-manylinux_2_22_x86_64",
"cp32-abi3-manylinux_2_21_x86_64",
"cp32-abi3-manylinux_2_20_x86_64",
"cp32-abi3-manylinux_2_19_x86_64",
"cp32-abi3-manylinux_2_18_x86_64",
"cp32-abi3-manylinux_2_17_x86_64",
"cp32-abi3-manylinux2014_x86_64",
"cp32-abi3-manylinux_2_16_x86_64",
"cp32-abi3-manylinux_2_15_x86_64",
"cp32-abi3-manylinux_2_14_x86_64",
"cp32-abi3-manylinux_2_13_x86_64",
"cp32-abi3-manylinux_2_12_x86_64",
"cp32-abi3-manylinux2010_x86_64",
"cp32-abi3-manylinux_2_11_x86_64",
"cp32-abi3-manylinux_2_10_x86_64",
"cp32-abi3-manylinux_2_9_x86_64",
"cp32-abi3-manylinux_2_8_x86_64",
"cp32-abi3-manylinux_2_7_x86_64",
"cp32-abi3-manylinux_2_6_x86_64",
"cp32-abi3-manylinux_2_5_x86_64",
"cp32-abi3-manylinux1_x86_64",
"cp32-abi3-linux_x86_64",
"py39-none-manylinux_2_35_x86_64",
"py39-none-manylinux_2_34_x86_64",
"py39-none-manylinux_2_33_x86_64",
"py39-none-manylinux_2_32_x86_64",
"py39-none-manylinux_2_31_x86_64",
"py39-none-manylinux_2_30_x86_64",
"py39-none-manylinux_2_29_x86_64",
"py39-none-manylinux_2_28_x86_64",
"py39-none-manylinux_2_27_x86_64",
"py39-none-manylinux_2_26_x86_64",
"py39-none-manylinux_2_25_x86_64",
"py39-none-manylinux_2_24_x86_64",
"py39-none-manylinux_2_23_x86_64",
"py39-none-manylinux_2_22_x86_64",
"py39-none-manylinux_2_21_x86_64",
"py39-none-manylinux_2_20_x86_64",
"py39-none-manylinux_2_19_x86_64",
"py39-none-manylinux_2_18_x86_64",
"py39-none-manylinux_2_17_x86_64",
"py39-none-manylinux2014_x86_64",
"py39-none-manylinux_2_16_x86_64",
"py39-none-manylinux_2_15_x86_64",
"py39-none-manylinux_2_14_x86_64",
"py39-none-manylinux_2_13_x86_64",
"py39-none-manylinux_2_12_x86_64",
"py39-none-manylinux2010_x86_64",
"py39-none-manylinux_2_11_x86_64",
"py39-none-manylinux_2_10_x86_64",
"py39-none-manylinux_2_9_x86_64",
"py39-none-manylinux_2_8_x86_64",
"py39-none-manylinux_2_7_x86_64",
"py39-none-manylinux_2_6_x86_64",
"py39-none-manylinux_2_5_x86_64",
"py39-none-manylinux1_x86_64",
"py39-none-linux_x86_64",
"py3-none-manylinux_2_35_x86_64",
"py3-none-manylinux_2_34_x86_64",
"py3-none-manylinux_2_33_x86_64",
"py3-none-manylinux_2_32_x86_64",
"py3-none-manylinux_2_31_x86_64",
"py3-none-manylinux_2_30_x86_64",
"py3-none-manylinux_2_29_x86_64",
"py3-none-manylinux_2_28_x86_64",
"py3-none-manylinux_2_27_x86_64",
"py3-none-manylinux_2_26_x86_64",
"py3-none-manylinux_2_25_x86_64",
"py3-none-manylinux_2_24_x86_64",
"py3-none-manylinux_2_23_x86_64",
"py3-none-manylinux_2_22_x86_64",
"py3-none-manylinux_2_21_x86_64",
"py3-none-manylinux_2_20_x86_64",
"py3-none-manylinux_2_19_x86_64",
"py3-none-manylinux_2_18_x86_64",
"py3-none-manylinux_2_17_x86_64",
"py3-none-manylinux2014_x86_64",
"py3-none-manylinux_2_16_x86_64",
"py3-none-manylinux_2_15_x86_64",
"py3-none-manylinux_2_14_x86_64",
"py3-none-manylinux_2_13_x86_64",
"py3-none-manylinux_2_12_x86_64",
"py3-none-manylinux2010_x86_64",
"py3-none-manylinux_2_11_x86_64",
"py3-none-manylinux_2_10_x86_64",
"py3-none-manylinux_2_9_x86_64",
"py3-none-manylinux_2_8_x86_64",
"py3-none-manylinux_2_7_x86_64",
"py3-none-manylinux_2_6_x86_64",
"py3-none-manylinux_2_5_x86_64",
"py3-none-manylinux1_x86_64",
"py3-none-linux_x86_64",
"py38-none-manylinux_2_35_x86_64",
"py38-none-manylinux_2_34_x86_64",
"py38-none-manylinux_2_33_x86_64",
"py38-none-manylinux_2_32_x86_64",
"py38-none-manylinux_2_31_x86_64",
"py38-none-manylinux_2_30_x86_64",
"py38-none-manylinux_2_29_x86_64",
"py38-none-manylinux_2_28_x86_64",
"py38-none-manylinux_2_27_x86_64",
"py38-none-manylinux_2_26_x86_64",
"py38-none-manylinux_2_25_x86_64",
"py38-none-manylinux_2_24_x86_64",
"py38-none-manylinux_2_23_x86_64",
"py38-none-manylinux_2_22_x86_64",
"py38-none-manylinux_2_21_x86_64",
"py38-none-manylinux_2_20_x86_64",
"py38-none-manylinux_2_19_x86_64",
"py38-none-manylinux_2_18_x86_64",
"py38-none-manylinux_2_17_x86_64",
"py38-none-manylinux2014_x86_64",
"py38-none-manylinux_2_16_x86_64",
"py38-none-manylinux_2_15_x86_64",
"py38-none-manylinux_2_14_x86_64",
"py38-none-manylinux_2_13_x86_64",
"py38-none-manylinux_2_12_x86_64",
"py38-none-manylinux2010_x86_64",
"py38-none-manylinux_2_11_x86_64",
"py38-none-manylinux_2_10_x86_64",
"py38-none-manylinux_2_9_x86_64",
"py38-none-manylinux_2_8_x86_64",
"py38-none-manylinux_2_7_x86_64",
"py38-none-manylinux_2_6_x86_64",
"py38-none-manylinux_2_5_x86_64",
"py38-none-manylinux1_x86_64",
"py38-none-linux_x86_64",
"py37-none-manylinux_2_35_x86_64",
"py37-none-manylinux_2_34_x86_64",
"py37-none-manylinux_2_33_x86_64",
"py37-none-manylinux_2_32_x86_64",
"py37-none-manylinux_2_31_x86_64",
"py37-none-manylinux_2_30_x86_64",
"py37-none-manylinux_2_29_x86_64",
"py37-none-manylinux_2_28_x86_64",
"py37-none-manylinux_2_27_x86_64",
"py37-none-manylinux_2_26_x86_64",
"py37-none-manylinux_2_25_x86_64",
"py37-none-manylinux_2_24_x86_64",
"py37-none-manylinux_2_23_x86_64",
"py37-none-manylinux_2_22_x86_64",
"py37-none-manylinux_2_21_x86_64",
"py37-none-manylinux_2_20_x86_64",
"py37-none-manylinux_2_19_x86_64",
"py37-none-manylinux_2_18_x86_64",
"py37-none-manylinux_2_17_x86_64",
"py37-none-manylinux2014_x86_64",
"py37-none-manylinux_2_16_x86_64",
"py37-none-manylinux_2_15_x86_64",
"py37-none-manylinux_2_14_x86_64",
"py37-none-manylinux_2_13_x86_64",
"py37-none-manylinux_2_12_x86_64",
"py37-none-manylinux2010_x86_64",
"py37-none-manylinux_2_11_x86_64",
"py37-none-manylinux_2_10_x86_64",
"py37-none-manylinux_2_9_x86_64",
"py37-none-manylinux_2_8_x86_64",
"py37-none-manylinux_2_7_x86_64",
"py37-none-manylinux_2_6_x86_64",
"py37-none-manylinux_2_5_x86_64",
"py37-none-manylinux1_x86_64",
"py37-none-linux_x86_64",
"py36-none-manylinux_2_35_x86_64",
"py36-none-manylinux_2_34_x86_64",
"py36-none-manylinux_2_33_x86_64",
"py36-none-manylinux_2_32_x86_64",
"py36-none-manylinux_2_31_x86_64",
"py36-none-manylinux_2_30_x86_64",
"py36-none-manylinux_2_29_x86_64",
"py36-none-manylinux_2_28_x86_64",
"py36-none-manylinux_2_27_x86_64",
"py36-none-manylinux_2_26_x86_64",
"py36-none-manylinux_2_25_x86_64",
"py36-none-manylinux_2_24_x86_64",
"py36-none-manylinux_2_23_x86_64",
"py36-none-manylinux_2_22_x86_64",
"py36-none-manylinux_2_21_x86_64",
"py36-none-manylinux_2_20_x86_64",
"py36-none-manylinux_2_19_x86_64",
"py36-none-manylinux_2_18_x86_64",
"py36-none-manylinux_2_17_x86_64",
"py36-none-manylinux2014_x86_64",
"py36-none-manylinux_2_16_x86_64",
"py36-none-manylinux_2_15_x86_64",
"py36-none-manylinux_2_14_x86_64",
"py36-none-manylinux_2_13_x86_64",
"py36-none-manylinux_2_12_x86_64",
"py36-none-manylinux2010_x86_64",
"py36-none-manylinux_2_11_x86_64",
"py36-none-manylinux_2_10_x86_64",
"py36-none-manylinux_2_9_x86_64",
"py36-none-manylinux_2_8_x86_64",
"py36-none-manylinux_2_7_x86_64",
"py36-none-manylinux_2_6_x86_64",
"py36-none-manylinux_2_5_x86_64",
"py36-none-manylinux1_x86_64",
"py36-none-linux_x86_64",
"py35-none-manylinux_2_35_x86_64",
"py35-none-manylinux_2_34_x86_64",
"py35-none-manylinux_2_33_x86_64",
"py35-none-manylinux_2_32_x86_64",
"py35-none-manylinux_2_31_x86_64",
"py35-none-manylinux_2_30_x86_64",
"py35-none-manylinux_2_29_x86_64",
"py35-none-manylinux_2_28_x86_64",
"py35-none-manylinux_2_27_x86_64",
"py35-none-manylinux_2_26_x86_64",
"py35-none-manylinux_2_25_x86_64",
"py35-none-manylinux_2_24_x86_64",
"py35-none-manylinux_2_23_x86_64",
"py35-none-manylinux_2_22_x86_64",
"py35-none-manylinux_2_21_x86_64",
"py35-none-manylinux_2_20_x86_64",
"py35-none-manylinux_2_19_x86_64",
"py35-none-manylinux_2_18_x86_64",
"py35-none-manylinux_2_17_x86_64",
"py35-none-manylinux2014_x86_64",
"py35-none-manylinux_2_16_x86_64",
"py35-none-manylinux_2_15_x86_64",
"py35-none-manylinux_2_14_x86_64",
"py35-none-manylinux_2_13_x86_64",
"py35-none-manylinux_2_12_x86_64",
"py35-none-manylinux2010_x86_64",
"py35-none-manylinux_2_11_x86_64",
"py35-none-manylinux_2_10_x86_64",
"py35-none-manylinux_2_9_x86_64",
"py35-none-manylinux_2_8_x86_64",
"py35-none-manylinux_2_7_x86_64",
"py35-none-manylinux_2_6_x86_64",
"py35-none-manylinux_2_5_x86_64",
"py35-none-manylinux1_x86_64",
"py35-none-linux_x86_64",
"py34-none-manylinux_2_35_x86_64",
"py34-none-manylinux_2_34_x86_64",
"py34-none-manylinux_2_33_x86_64",
"py34-none-manylinux_2_32_x86_64",
"py34-none-manylinux_2_31_x86_64",
"py34-none-manylinux_2_30_x86_64",
"py34-none-manylinux_2_29_x86_64",
"py34-none-manylinux_2_28_x86_64",
"py34-none-manylinux_2_27_x86_64",
"py34-none-manylinux_2_26_x86_64",
"py34-none-manylinux_2_25_x86_64",
"py34-none-manylinux_2_24_x86_64",
"py34-none-manylinux_2_23_x86_64",
"py34-none-manylinux_2_22_x86_64",
"py34-none-manylinux_2_21_x86_64",
"py34-none-manylinux_2_20_x86_64",
"py34-none-manylinux_2_19_x86_64",
"py34-none-manylinux_2_18_x86_64",
"py34-none-manylinux_2_17_x86_64",
"py34-none-manylinux2014_x86_64",
"py34-none-manylinux_2_16_x86_64",
"py34-none-manylinux_2_15_x86_64",
"py34-none-manylinux_2_14_x86_64",
"py34-none-manylinux_2_13_x86_64",
"py34-none-manylinux_2_12_x86_64",
"py34-none-manylinux2010_x86_64",
"py34-none-manylinux_2_11_x86_64",
"py34-none-manylinux_2_10_x86_64",
"py34-none-manylinux_2_9_x86_64",
"py34-none-manylinux_2_8_x86_64",
"py34-none-manylinux_2_7_x86_64",
"py34-none-manylinux_2_6_x86_64",
"py34-none-manylinux_2_5_x86_64",
"py34-none-manylinux1_x86_64",
"py34-none-linux_x86_64",
"py33-none-manylinux_2_35_x86_64",
"py33-none-manylinux_2_34_x86_64",
"py33-none-manylinux_2_33_x86_64",
"py33-none-manylinux_2_32_x86_64",
"py33-none-manylinux_2_31_x86_64",
"py33-none-manylinux_2_30_x86_64",
"py33-none-manylinux_2_29_x86_64",
"py33-none-manylinux_2_28_x86_64",
"py33-none-manylinux_2_27_x86_64",
"py33-none-manylinux_2_26_x86_64",
"py33-none-manylinux_2_25_x86_64",
"py33-none-manylinux_2_24_x86_64",
"py33-none-manylinux_2_23_x86_64",
"py33-none-manylinux_2_22_x86_64",
"py33-none-manylinux_2_21_x86_64",
"py33-none-manylinux_2_20_x86_64",
"py33-none-manylinux_2_19_x86_64",
"py33-none-manylinux_2_18_x86_64",
"py33-none-manylinux_2_17_x86_64",
"py33-none-manylinux2014_x86_64",
"py33-none-manylinux_2_16_x86_64",
"py33-none-manylinux_2_15_x86_64",
"py33-none-manylinux_2_14_x86_64",
"py33-none-manylinux_2_13_x86_64",
"py33-none-manylinux_2_12_x86_64",
"py33-none-manylinux2010_x86_64",
"py33-none-manylinux_2_11_x86_64",
"py33-none-manylinux_2_10_x86_64",
"py33-none-manylinux_2_9_x86_64",
"py33-none-manylinux_2_8_x86_64",
"py33-none-manylinux_2_7_x86_64",
"py33-none-manylinux_2_6_x86_64",
"py33-none-manylinux_2_5_x86_64",
"py33-none-manylinux1_x86_64",
"py33-none-linux_x86_64",
"py32-none-manylinux_2_35_x86_64",
"py32-none-manylinux_2_34_x86_64",
"py32-none-manylinux_2_33_x86_64",
"py32-none-manylinux_2_32_x86_64",
"py32-none-manylinux_2_31_x86_64",
"py32-none-manylinux_2_30_x86_64",
"py32-none-manylinux_2_29_x86_64",
"py32-none-manylinux_2_28_x86_64",
"py32-none-manylinux_2_27_x86_64",
"py32-none-manylinux_2_26_x86_64",
"py32-none-manylinux_2_25_x86_64",
"py32-none-manylinux_2_24_x86_64",
"py32-none-manylinux_2_23_x86_64",
"py32-none-manylinux_2_22_x86_64",
"py32-none-manylinux_2_21_x86_64",
"py32-none-manylinux_2_20_x86_64",
"py32-none-manylinux_2_19_x86_64",
"py32-none-manylinux_2_18_x86_64",
"py32-none-manylinux_2_17_x86_64",
"py32-none-manylinux2014_x86_64",
"py32-none-manylinux_2_16_x86_64",
"py32-none-manylinux_2_15_x86_64",
"py32-none-manylinux_2_14_x86_64",
"py32-none-manylinux_2_13_x86_64",
"py32-none-manylinux_2_12_x86_64",
"py32-none-manylinux2010_x86_64",
"py32-none-manylinux_2_11_x86_64",
"py32-none-manylinux_2_10_x86_64",
"py32-none-manylinux_2_9_x86_64",
"py32-none-manylinux_2_8_x86_64",
"py32-none-manylinux_2_7_x86_64",
"py32-none-manylinux_2_6_x86_64",
"py32-none-manylinux_2_5_x86_64",
"py32-none-manylinux1_x86_64",
"py32-none-linux_x86_64",
"py31-none-manylinux_2_35_x86_64",
"py31-none-manylinux_2_34_x86_64",
"py31-none-manylinux_2_33_x86_64",
"py31-none-manylinux_2_32_x86_64",
"py31-none-manylinux_2_31_x86_64",
"py31-none-manylinux_2_30_x86_64",
"py31-none-manylinux_2_29_x86_64",
"py31-none-manylinux_2_28_x86_64",
"py31-none-manylinux_2_27_x86_64",
"py31-none-manylinux_2_26_x86_64",
"py31-none-manylinux_2_25_x86_64",
"py31-none-manylinux_2_24_x86_64",
"py31-none-manylinux_2_23_x86_64",
"py31-none-manylinux_2_22_x86_64",
"py31-none-manylinux_2_21_x86_64",
"py31-none-manylinux_2_20_x86_64",
"py31-none-manylinux_2_19_x86_64",
"py31-none-manylinux_2_18_x86_64",
"py31-none-manylinux_2_17_x86_64",
"py31-none-manylinux2014_x86_64",
"py31-none-manylinux_2_16_x86_64",
"py31-none-manylinux_2_15_x86_64",
"py31-none-manylinux_2_14_x86_64",
"py31-none-manylinux_2_13_x86_64",
"py31-none-manylinux_2_12_x86_64",
"py31-none-manylinux2010_x86_64",
"py31-none-manylinux_2_11_x86_64",
"py31-none-manylinux_2_10_x86_64",
"py31-none-manylinux_2_9_x86_64",
"py31-none-manylinux_2_8_x86_64",
"py31-none-manylinux_2_7_x86_64",
"py31-none-manylinux_2_6_x86_64",
"py31-none-manylinux_2_5_x86_64",
"py31-none-manylinux1_x86_64",
"py31-none-linux_x86_64",
"py30-none-manylinux_2_35_x86_64",
"py30-none-manylinux_2_34_x86_64",
"py30-none-manylinux_2_33_x86_64",
"py30-none-manylinux_2_32_x86_64",
"py30-none-manylinux_2_31_x86_64",
"py30-none-manylinux_2_30_x86_64",
"py30-none-manylinux_2_29_x86_64",
"py30-none-manylinux_2_28_x86_64",
"py30-none-manylinux_2_27_x86_64",
"py30-none-manylinux_2_26_x86_64",
"py30-none-manylinux_2_25_x86_64",
"py30-none-manylinux_2_24_x86_64",
"py30-none-manylinux_2_23_x86_64",
"py30-none-manylinux_2_22_x86_64",
"py30-none-manylinux_2_21_x86_64",
"py30-none-manylinux_2_20_x86_64",
"py30-none-manylinux_2_19_x86_64",
"py30-none-manylinux_2_18_x86_64",
"py30-none-manylinux_2_17_x86_64",
"py30-none-manylinux2014_x86_64",
"py30-none-manylinux_2_16_x86_64",
"py30-none-manylinux_2_15_x86_64",
"py30-none-manylinux_2_14_x86_64",
"py30-none-manylinux_2_13_x86_64",
"py30-none-manylinux_2_12_x86_64",
"py30-none-manylinux2010_x86_64",
"py30-none-manylinux_2_11_x86_64",
"py30-none-manylinux_2_10_x86_64",
"py30-none-manylinux_2_9_x86_64",
"py30-none-manylinux_2_8_x86_64",
"py30-none-manylinux_2_7_x86_64",
"py30-none-manylinux_2_6_x86_64",
"py30-none-manylinux_2_5_x86_64",
"py30-none-manylinux1_x86_64",
"py30-none-linux_x86_64",
"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"
],
"marker_environment": {
"implementation_name": "cpython",
"implementation_version": "3.9.16",
"os_name": "posix",
"platform_machine": "x86_64",
"platform_python_implementation": "CPython",
"platform_release": "5.15.90.1-microsoft-standard-WSL2",
"platform_system": "Linux",
"platform_version": "#1 SMP Fri Jan 27 02:56:13 UTC 2023",
"python_full_version": "3.9.16",
"python_version": "3.9",
"sys_platform": "linux"
}
}
As far as using the existing platforms (and complete_platforms) support in Pants, yes - that's the data needed to use pex3 lock create --style strict [--platform X]* [--complete-platform Y]*
- which is what I think you mean by lockstyle: PexLockstyle
.
In Pex parlance it's --style {strict|sources|universal}
.
The strict
(default) and sources
styles allow resolving for one or more foreign platforms (abbreviated or complete) as well as any number of local interpreters. The resulting lock file will have a lock per each of those targets, custom for that target. The sources
just always adds an sdist to the lock when there is an sdist available in addition to the most compatible wheel; so each locked project can have 1-2 artifacts locked. For strict
, exactly 1 artifact (the most compatible one) is locked per project.
The universal style only works via interpreter constraints and optional --target-system
filters and produces only 1 lock. You can pass platforms (abbreviated or complete again), but these are only used to validate the lock will work for those platforms after the universal lock is generated. They do not steer the resolve that builds the universal lock.
Alright, trying to keep up here!
<1000' lines of markers> So these lists are what you meant by
maybe ship a catalog of common ones
above, right? Which seems to me like a good solution, or maybe there's one maintained that could be pulled down somewhere. I've hit this before writing a GCS-cached downloader for Poetry, and maintaining that indeed seems a pain. In that case I could at least use the local Python, but as you note that doesn't work here.As far as using the existing platforms (and complete_platforms) support in Pants, yes - that's the data needed to use pex3 lock create --style strict [--platform X] [--complete-platform Y] - which is what I think you mean by lockstyle: PexLockstyle.
In Pex parlance it's --style {strict|sources|universal}.
Yepp indeed! It looks like both platforms
and complete_platforms
already exist, so being able to set lockstyle is the minimal diff to get closer to a "proper" workflow. Maybe it should be called style
, it just feels so generic. I like my bikeshed fwiw. (I just realized: the configuration I've looked at is for the pex_binary but not the lockfile. How does that even work? Does it? Or would we need to add a more config elsewhere?
The strict (default) and sources styles allow resolving for one or more foreign platforms (abbreviated or complete) as well as any number of local interpreters. The resulting lock file will have a lock per each of those targets, custom for that target. The sources just always adds an sdist to the lock when there is an sdist available in addition to the most compatible wheel; so each locked project can have 1-2 artifacts locked. For strict, exactly 1 artifact (the most compatible one) is locked per project.
The universal style only works via interpreter constraints and optional --target-system filters and produces only 1 lock. You can pass platforms (abbreviated or complete again), but these are only used to validate the lock will work for those platforms after the universal lock is generated. They do not steer the resolve that builds the universal lock.
Makes sense. I assume when you say exactly 1
, that's per-complete-platform I assume?
I think there's a bit of a conflict between what I expect from a tool like Pants in terms of abstraction level of workflows - high gain for low effort - and pex maybe focusing more on explicitness and correctness. And that's not an easy thing to resolve. FWIW from my perspective, I think there's a fundamental disconnect between what I'm configuring ("use torch") and the result ("only works on Linux") when I add an extra index elsewhere.
Whatever solution we go for; I think having the option to use "non-exact" version constraints is also an important part, as in my "torch>=1.11.0,<1.12"
lock. And then getting something that picks the right variant. But that would require per-resolve indexes, which I think also makes sense as it precludes specifying the +version. (Or per-requirement-indexes?). I think using ==,!=,===
shouldn't be required if we can do per-platform resolves, but that requires disambiguation elsewhere. I'm not super-certain about this requirement, but it'd be nice. Open to better ideas or thoughts, you're much more familiar with the python specs here.
I added the pants.toml
and BUILD
files at the top for what currently doesn't work. Let's flip that around. I'd expect that given a configuration like:
# pants.toml
[python-repos]
indexes = [
"https://pypi.org/simple/",
"https://download.pytorch.org/whl/cpu/",
]
# BUILD
python_requirement(
name="torch",
requirements=["torch>=1.11.0,<1.12"],
resolve="cpu",
)
This would lock +cpu
for Linux and a generic version for Mac. The question is then how to avoid configuration complexity explosion if our pants.toml
looks like this:
# pants.toml
[python-repos]
indexes = [
"https://pypi.org/simple/",
"https://download.pytorch.org/whl/cpu/",
"https://download.pytorch.org/whl/cu115/",
]
Because I think at a minimum it'd have to look like:
# BUILD
python_requirement(
name="torch",
requirements=["torch>=1.11.0,<1.12,!=1.11.*+cu115"],
resolve="cpu",
)
python_requirement(
name="torch",
requirements=["torch>=1.11.0,<1.12,!=1.11.*+cpu"],
resolve="gpu",
)
Alternatively;
# BUILD
python_requirement(
name="torch",
requirements=[
'torch==1.11.*+cpu ; platform_system != "Darwin"'
'torch==1.11.* ; platform_system == "Darwin"'
],
resolve="cpu",
)
python_requirement(
name="torch",
requirements=[
'torch==1.11.*+cu115 ; platform_system != "Darwin"'
'torch==1.11.* ; platform_system == "Darwin"'
],
resolve="gpu",
)
But I think that's even worse (though less exponential). I'm also not sure if star-versions like that work with local versions.
This thread is intended to focus on actionable solutions for #18293, potentially multi-platform Pex locks, maybe with other components as well.
Is your feature request related to a problem? Please describe.
The big neural network libraries Tensorflow and Pytorch have significant hurdles in usage based on how they're versioned. In general, the basic wheels published to PyPi aren't usable by default, and any important usage requires custom indexes and compute-API-tagged builds. In the case of
tensorflow
these are differently named packages, and in the case oftorch
they are differentiated by local versions. These specific packages also differ in what platforms they support, and the local versions may not exist for all platforms.Currently, the universal lock provided by Pex specifies a maximum set of
{Linux, Mac}
but makes no guarantee either platform will be supported. This compounds the complexity of version specification, as local versions shall be preferred. In practice, one has to jump through hoops to ensure a basic resolve works. For example:This seems like we'd end up with one lock that works on both Mac (CPU) and Linux (CPU + random CUDA), and one for Linux with only CPU support. However; due to the PEP440 requirements both locks end up picking +CPU, and having no install candidates for Mac. The proper requirement is to specify it like this:
This means that the more different compute variants you need the more specific the generic constraints have to be. If we allowed Pex to separate the versions between Mac and Linux (and Windows?) we would have an easier time ensuring something reasonable gets picked. However; even so it'd be required to exclude all available versions in each resolve as we'd not want a massive
+cuda
result in the generic for Linux.Describe the solution you'd like
The thread on Slack discusses multi-platform locks as a potential solution, but I think that still runs afoul of some footguns with the local-version specifiers as one still needs to exclude them. It might therefore be necessary to combine them with other solutions such as per-resolve extra-indexes.
Additional context
https://pantsbuild.slack.com/archives/C046T6T9U/p1683187396031649