Skip to content
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

Add PEP 513 support (manylinux1 platform tag) #3497

Merged
merged 2 commits into from
Mar 4, 2016

Conversation

ogrisel
Copy link
Contributor

@ogrisel ogrisel commented Feb 19, 2016

This is a rebased and squashed version of #3446 with fixes for the failed flake8 checks. The goal is to add support for the manylinux1 platform tag.

Review on Reviewable

@ogrisel
Copy link
Contributor Author

ogrisel commented Feb 19, 2016

The TOXENV=docs failure is unrelated to this PR: caused by an outage on https://www.pypa.io/en/latest/

@xavfernandez xavfernandez added this to the 8.1 milestone Feb 19, 2016
@@ -189,6 +236,11 @@ def get_supported(versions=None, noarch=False):
else:
# arch pattern didn't match (?!)
arches = [arch]
elif sys.platform == 'linux':
if is_manylinux1_compatible():
arches = [arch, arch.replace('linux', 'manylinux1')]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rmcgibbo @njsmith is the order important here? For instance in case both the linux_x86_64 and the manylinux1_x86_64 wheel files are available in a wheel index, would pip choose the first or the later?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know, either what the effect is or what the preferred behavior is... To check what the effect is I guess it would be easy to check (you can trivially run pip out of a source checkout by running python -m pipwhile in the root of the checkout). And in terms of what behavior we want, I think it's pretty arbitrary really -- the case where it comes up is very unlikely, and in this unlikely case, I can't think of any strong reason to prefer one outcome or another.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did the test, and the current ordering make pip install select the linux arch over the manylinux1 arch when both wheels are available on the index (in my case a local folder). Switching the ordering at that line yield the opposite behavior.

  • pro: this is backward compatible with the behavior of pip 8.0 (and earlier)
  • con: this is less specific, one could expect pip to select the most platform specific wheel instead.

Note that pip wheel does not seem to be impacted by this change as it calls python setup.py bdist_wheel in a subprocess (I think) and therefore always name the generated file with the linux arch. I think this is good as to generate a valid manylinux1 wheel file extract care is required (e.g. using auditwheel repair to embed system shared libraries not provided a manylinux1 compatible system).

@dstufft any opinion on this ordering issue?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pip should always select the most specific available.

@ogrisel
Copy link
Contributor Author

ogrisel commented Feb 22, 2016

@dstufft I amended the change to make sure the most specific supported tag comes first and added a test for this.

@@ -198,7 +247,8 @@ def get_supported(versions=None, noarch=False):
supported.append(('%s%s' % (impl, versions[0]), abi, arch))

# Has binaries, does not use the Python API:
supported.append(('py%s' % (versions[0][0]), 'none', arch))
for arch in arches:
supported.append(('py%s' % (versions[0][0]), 'none', arch))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dstufft this change also impacts macosx: previously it would use the arch from get_platform() instead of all possible arches expanded in this function. I think this was a bug but I am not 100% sure.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think it is a bug fix, and for a bug that was never noticed before because it only affects wheels that combine a none ABI tag + a non-trivial platform tag, which is a rare (but totally valid) configuration.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a partial fix to an issue described in #3202; I have a PR open with a full fix at #3357. The full fix generates an order of magnitude more tags on my machine than this PR (198 vs 2196, though these both include impossible tags as described in #3403).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think ~2000 should not cause any performance issue for pip but it would be worth auditing the codebase to check that this list of tags is not used by any algorithm with quadratic complexity or more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_supported() is used directly in 3 places:

  • index.py#L245: len(tags): O(1)
  • wheel.py#L656: [tags.index(c) for c in self.file_tags if c in tags]: self.file_tags is the set of tag combinations in a wheel filename so won't be very large. c in tags complexity is O(len(tags)) = O(n). Not sure what the complexity of tags.index(c) is but I assume it's implemented the same way as c in tags and should then be O(n), so this whole statement is O(n).
  • wheel.py#L663: bool(set(tags).intersection(self.file_tags)): Set construction is O(len(tags)) = O(n) and set intersection is O(len(tags)) = O(n) so the whole thing is also O(n).

I don't see any loops or recursion in the callers that would cause any of these to be called len(tags) times since they're used on lists of wheels discovered e.g. from the index or in a cache directory. So I suppose the danger would be if an index had thousands of wheels.

@ogrisel
Copy link
Contributor Author

ogrisel commented Feb 22, 2016

Also do we need a functional test for this new feature or are the unit tests enough?

@Ivoz
Copy link
Contributor

Ivoz commented Feb 23, 2016

@ogrisel I think pep425tags unit tests should be moved to a new file, rather than shoved under wheel.

@ogrisel
Copy link
Contributor Author

ogrisel commented Feb 23, 2016

@Ivoz alright, I will do that.

@ogrisel ogrisel force-pushed the pr-3446-followup branch 2 times, most recently from aa27185 to 309d403 Compare February 23, 2016 08:20
@ogrisel
Copy link
Contributor Author

ogrisel commented Feb 23, 2016

Done.

@ogrisel
Copy link
Contributor Author

ogrisel commented Feb 23, 2016

@rmcgibbo sorry I lost authorship information in the process or splitting the commit into 2.

@ogrisel
Copy link
Contributor Author

ogrisel commented Feb 24, 2016

I fixed the authorship of the last commit. Let me know if there are any other problem with this PR.

@ogrisel ogrisel changed the title Add PEP 513 support Add PEP 513 support (manylinux1 platform tag) Feb 25, 2016
@@ -124,7 +124,54 @@ def get_platform():
split_ver = release.split('.')
return 'macosx_{0}_{1}_{2}'.format(split_ver[0], split_ver[1], machine)
# XXX remove distutils dependency
return distutils.util.get_platform().replace('.', '_').replace('-', '_')
result = distutils.util.get_platform().replace('.', '_').replace('-', '_')
if result == "linux_x86_64" and sys.maxsize == 2147483647:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's up with this check? Can we add a comment as to why it exists?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, is it somewhat related to this quite old PR #1377 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you run 32 bit Python on a 64 bit linux (e.g. via anaconda or even if you debootstrap a i686 ubuntu inside a 64 bit ubuntu), then distutils.util.get_platform() returns "linux-x86_64" even though Python itself is 32 bit.

I don't know if this is a bug of distutils or not.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This occurs because distutils.util.get_platform() relies on os.uname() (and thus POSIX uname()) for the architecture of the platform. It does not make any attempt to determine the "platform" of the interpreter itself.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this fixes the same bug that PR #1377 was trying to address; see also this mailing list message. The bug has been around a long time, but it becomes much more urgent to fix as soon manylinux support lands; in the past it only caused a problem if you specifically did pip install -f <directory with 64 bit wheels> from a 32-bit userland, but once 64-bit linux wheels are on pypi [edit: and people are using manylinux-enabled pip], then it will become possible to hit by just doing pip install. So it's important to land this fix at the same time we enable manylinux support.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note, that it's likely that the wheel project has this bug too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result = distutils.util.get_platform().replace('.', '_').replace('-', '_')
if result == "linux_x86_64" and sys.maxsize == 2147483647:
# 32 bit Python program (running on a 64 bit Linux): pip should only
# install and run 32 bit compiled extensions in that case.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dstufft I amended the commit to add this comment to explain this particular case. Let me know if would prefer another phrasing.

@ogrisel
Copy link
Contributor Author

ogrisel commented Mar 3, 2016

I amended the last commit to move the ctypes import. Github has folded the unrelated #3497 (comment) comment as the result even though this remark has not been addressed yet.


# Parse string and check against requested version.
version = [int(piece) for piece in version_str.split(".")]
assert len(version) == 2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want an assert here: we'd rather return False and not crash pip.

@ogrisel
Copy link
Contributor Author

ogrisel commented Mar 4, 2016

Thanks @xavfernandez, I addressed your comments.

dstufft added a commit that referenced this pull request Mar 4, 2016
Add PEP 513 support (manylinux1 platform tag)
@dstufft dstufft merged commit 05baa9f into pypa:develop Mar 4, 2016
@lock lock bot added the auto-locked Outdated issues that have been locked by automation label Jun 3, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Jun 3, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
auto-locked Outdated issues that have been locked by automation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants