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

Missing valid mac tags #578

Open
FFY00 opened this issue Jul 20, 2022 · 43 comments · May be fixed by #579
Open

Missing valid mac tags #578

FFY00 opened this issue Jul 20, 2022 · 43 comments · May be fixed by #579

Comments

@FFY00
Copy link
Member

FFY00 commented Jul 20, 2022

PEP 425 says (https://peps.python.org/pep-0425/#platform-tag)

The platform tag is simply distutils.util.get_platform() with all hyphens - and periods . replaced with underscore _.

On recent mac versions

>>> distutils.util.get_platform()
'macosx-12-arm64'

However, pypa/packaging does not consider macosx_12_arm64 a valid tag, only macosx_12_0_arm64

>>> list(packaging.tags.mac_platforms())
['macosx_12_0_arm64',
 'macosx_12_0_universal2',
 'macosx_11_0_arm64',
 'macosx_11_0_universal2',
 'macosx_10_16_universal2',
 'macosx_10_15_universal2',
 'macosx_10_14_universal2',
 'macosx_10_13_universal2',
 'macosx_10_12_universal2',
 'macosx_10_11_universal2',
 'macosx_10_10_universal2',
 'macosx_10_9_universal2',
 'macosx_10_8_universal2',
 'macosx_10_7_universal2',
 'macosx_10_6_universal2',
 'macosx_10_5_universal2',
 'macosx_10_4_universal2']
@brettcannon
Copy link
Member

Do we know if that tag makes sense?

@FFY00
Copy link
Member Author

FFY00 commented Jul 20, 2022

Can you clarify? The tag seems fine to me, just missing the minor version.

@uranusjr
Copy link
Member

uranusjr commented Jul 21, 2022

The tag makes sense. Apple used to use 10.x (major dot minor), but recently starts to use only one single number to version the platform (11, 12 onward). 11.0 and 12.0 are arguably not valid, but we might want to keep them for compatibility since currently existing wheel generators may be using them now.

@FFY00
Copy link
Member Author

FFY00 commented Jul 21, 2022

That's great to know! I don't use mac so I have not kept up to date with the versioning scheme.

And I agree that we probably want to keep this old tags valid.

I will update the PR when I am back to the computer. The proposed change would be for 11 and 12 generate both version, with and without the minor version, and for 13 and onwards only generate tags with the major version. Does that sound good?

@uranusjr
Copy link
Member

Yeah, I guess that makes sense. Apple never tells anyone how they do things and it’s impossible to know if the version scheme is going to change again, but the strategy you outlined is likely the best we can do.

Another issue not related to packaging but wheel installers: When macOS 11 was announced, Apple did mention the possibility of having minor versions. Would a hypothetical macOS 12.1 correctly match macosx_12_universal2?

@FFY00
Copy link
Member Author

FFY00 commented Jul 21, 2022

Well, the PEP does not specify anything about matching older versions, it just maps the platform tag to distutils.util.get_platform, but given the current implementations, I'd say yes. I think the easiest thing to do is kinda interpret it as 12.0, or rather, 12 comes before 12.1.

@FFY00
Copy link
Member Author

FFY00 commented Jul 21, 2022

Also, I think it would make sense to update the PEP to say to use sysconfig.get_platform when distutils is not available. Any thoughts?

@brettcannon
Copy link
Member

When macOS 11 was announced, Apple did mention the possibility of having minor versions. Would a hypothetical macOS 12.1 correctly match macosx_12_universal2?

That's what my question was about; does a minor-version-free macOS version make sense?

I think the easiest thing to do is kinda interpret it as 12.0, or rather, 12 comes before 12.1.

What would happen is we list all the appropriate tags in preference order, and then tools can use that order however they choose. So in this instance we would probably list macosx_12_universal2 after all the other macosx_12_*_universal2 wheels since it's the least specific.

I think it would make sense to update the PEP to say to use sysconfig.get_platform when distutils is not available. Any thoughts?

We should probably migrate that entire PEP to packaging.python.org. But in this specific case I don't know what makes the most sense since the tag creation logic is rather complicated and not well-specified already, so I don't know if continuing to specify any part of it is important.

@FFY00
Copy link
Member Author

FFY00 commented Jul 21, 2022

What would happen is we list all the appropriate tags in preference order, and then tools can use that order however they choose. So in this instance we would probably list macosx_12_universal2 after all the other macosx_12_*_universal2 wheels since it's the least specific.

In this specific case, I think we should probably list it earlier, and basically treat it as an alternative to macosx_12_0_universal2, given that we probably shouldn't be generating macosx_12_0_universal2 in the first place. Basically, macosx_12_0_universal2 would be an undesirable alias to macosx_12_universal2. Does that make sense?

We should probably migrate that entire PEP to packaging.python.org.

Agreed.

But in this specific case I don't know what makes the most sense since the tag creation logic is rather complicated and not well-specified already, so I don't know if continuing to specify any part of it is important.

I don't know how you would define this field in that case.

@uranusjr
Copy link
Member

uranusjr commented Jul 22, 2022

That's what my question was about; does a minor-version-free macOS version make sense?

It does in the sense that macOS does return a minor-less 12. So it makes sense for a wheel generator to use this version scheme when creating a wheel. The problem here is though, are our installer implementations smart enough to correctly detect compatibility if a wheel with minor-less version is being installed on a platform that does contain a minor segment. This is where specs are vague; PEPs don’t specify how platform version compatibility should be detected, and Apple doesn’t say if it’s ever possible to have a minor version segment in the future, and how that minor version bump would affect compatibility.

@rgommers
Copy link

Apple used to use 10.x (major dot minor), but recently starts to use only one single number to version the platform (11, 12 onward).

This seems incorrect, the current macOS version is 12.4:

image

11.0 and 12.0 are arguably not valid, but we might want to keep them for compatibility since currently existing wheel generators may be using them now.

These version numbers do not depend directly on macOS, they're generated by Python so it depends where you install Python from. On macOS 12.4 with Python 3.9.6 from conda-forge, I get:

>>> distutils.util.get_platform()
'macosx-11.0-arm64'
>>> sysconfig.get_platform()
'macosx-11.0-arm64'

The original bug report on https://github.com/FFY00/meson-python/issues/91 came from a build with Python 3.8 from Homebrew.

The Python shipped by Apple (/usr/bin/python3) actually gives 10.14:

>>> os.__file__
'/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/os.py'
>>> import distutils.util
>>> distutils.util.get_platform()
'macosx-10.14-arm64'

I suggest to simply add the missing tags and make no other changes.

@FFY00
Copy link
Member Author

FFY00 commented Sep 30, 2022

Another issue: https://github.com/FFY00/meson-python/issues/160

The minor versions are also not generated. Should I be generating 12.0 tags for 12.6? That seems like the only option right now to make it work with the current packaging code.

@brettcannon
Copy link
Member

Should I be generating 12.0 tags for 12.6?

I think so. I believe the last time we discussed how to do version number support for the "new" macOS versions the guidance this was what was suggested. We can discuss doing something different (e.g. all versions, no minor number), but I think the concern with the latter was whether anyone made weird assumptions about always having a minor number.

But my memory may be faulty with all of this, so reading the old issues is the best way to double-check me.

@uranusjr
Copy link
Member

uranusjr commented Oct 3, 2022

Yes I think from previous discussions, a wheel against 12.0 should be installable on 12.6.

@rgommers
Copy link

rgommers commented Oct 3, 2022

That is all correct - and yes, we should generate 12_0 tags right now for them to be installable.

That said, there is no reason for packaging to reject 12_6 wheels for users who are on >= 12.6. And it is possible for packages to need fixes that are part of 12.6 but not 12.0. In that case, they want to set MACOSX_DEPLOYMENT_TARGET=12.6 and produce wheels containing the 12_6 tag. So packaging needs to be updated to accept such tags - in addition to the plain 12 major-version-only tag.

@brettcannon
Copy link
Member

Now I remember why we didn't want to do midyear updates in the tags; we don't know how to back-fill older macOS versions without hard-coding the final minor version number. For instance, while we can infer 12.6 to 12.0, what do we do for macOS 11? We don't know what the upper limit is for a minor version until Apple announces that version is EOL. So until that happens we can only guess what the upper bound is for macOS 11 (e.g. is it 11.5, 11.10, 11.100?). So either we have to hard-code the upper limit known at any moment in time and then push a new version of packaging every time Apple releases a new version of macOS, set some crazy upper limit we think they will never go past, or do what we do and only set the lower-bound of *.0.

@rgommers
Copy link

rgommers commented Oct 5, 2022

I don't really see why that is required - why wouldn't you just say that any 11_x (or in general X_Y with X and Y integers) tag is valid? There's no practical problems with that that I can think of, it's just a valid PEP 3149 compliant tag. Analogy: manylinux tags are valid for any glibc major_minor version: https://peps.python.org/pep-0600/#specification.

@brettcannon
Copy link
Member

I don't really see why that is required - why wouldn't you just say that any 11_x (or in general X_Y with X and Y integers) tag is valid?

Potentially, but then why do we care about 12_x? If we are concerned about a change in 12.6 that is only forward-compatible, then we can't assume 12.0 is okay once 13.0 comes out for some feature. And since we list every compatible wheel tag, we can't really make the declaration that, "macOS 12.6 is compatible with the 12_0/12 tag when running under macOS 13," without listing 12_6 explicitly.

Or put another way (to make sure I understand), if you're running macOS 13.2 and have a wheel that wants 12_6, how do you handle that when the wheel tags you have are:

  • 13_2
  • 13_1
  • 13_0
  • 12/12_0

without making every consumer of packaging.tags specify handling macOS platform tags?

Analogy: manylinux tags are valid for any glibc major_minor version: https://peps.python.org/pep-0600/#specification.

That would be true if the glibc team told us they have no plans to ever change the major version number, but they said they didn't when that discussion came up. Otherwise we will have a similar problem. But glibc also bumps their major version number way less than macOS, so hard-coding whatever glibc 2 stops at is much less effort than trying to keep up with macOS' release cadence.

@rgommers
Copy link

rgommers commented Oct 5, 2022

Potentially, but then why do we care about 12_x?

Aside from possible incompatibilities - which could indeed occur - because it makes the tags easier to understand and specify. These issues came up to begin with because we implemented tag support in meson-python and then ran into pip not understanding 12_6. It's entirely logical to think that if you can set a deployment target explicitly to 10.9, you should also be able to set it to 12.6 (via the MACOSX_DEPLOYMENT_TARGET). Right now, what is a backend to do if a user does that? Explicitly error out? Or printing a warning message and then resetting it to 12.0?

It's the same for 11 rather than 11_0 as a tag. Right now this is all simply undocumented stuff that bites you unless you read the packaging source code, for what looks like valid tags. So it's be nice to be permissive rather than overly strict.

Potentially, but then why do we care about 12_x? If we are concerned about a change in 12.6 that is only forward-compatible, then we can't assume 12.0 is okay once 13.0 comes out for some feature

It's a major version number change - I think there are no watertight guarantees about 13.0 either way. But besides that, I don't think this argument holds. In corner cases 12.6 can be a breaking release, and so can 13.0. The latter is more likely, but both are possible. For 99.9x% of wheels there will be no problem (because the breakage will likely be for something obscure), so you don't have to change today's assumptions.

so hard-coding whatever glibc 2 stops at is much less effort

I may be missing something here. Is that actually being done anywhere?

@rgommers
Copy link

rgommers commented Oct 6, 2022

To make the case for an ability to set macOS deployment target more concrete, here is a real-world case:

  • SciPy sets the deployment target for all its macOS arm64 wheels to 12.0 (by manually renaming them, we have to build on macOS 11 CI runners right now)
  • The reason is that a kernel panic (which is way worse than a regular crash) may happen due to a bug in older macOS system libraries, which is triggered by some linear algebra functionality
  • This bug happens to be fixed from macOS 12.0.1 onwards (relevant issue comment), and the fix could not be backported. nor was it clear which exact system library was responsible.
  • We want to be able to set the deployment target to the minimum version with the fix. Right now 12_0 isn't too bad, we're just ignoring that 12.0.0 is not working (not many users will have that version, so it's okay in practice).
    • Clearly, if the bug fix had landed in say 12.5, we'd want 12_5 - and 12_0 would be pretty horrible.

So this has nothing to do with "will it run on 13.0 or not" (it will, unless Apple happens to break something unrelated in the future), but with "the older OS version has an unpatched bug that we need to avoid".

Or put another way (to make sure I understand), if you're running macOS 13.2 and have a wheel that wants 12_6, how do you handle that when the wheel tags you have are:

* `13_2`
* `13_1`
* `13_0`
* `12`/`12_0`
  • When you are building a wheel, you honor the explicitly requested deployment target of 12.6 and the resulting wheel tag will be 12_6.
  • At install time, you install it if the OS version is >=12.6 (which includes all 13.x, but not 12.0).

without making every consumer of packaging.tags specify handling macOS platform tags?

I don't understand this part of your question. You have to know less. All you have to do is use a major_minor version scheme. Right now you have to know that that is not going to work because of packaging, and explicitly replace the minor version part with .0 .

You seem to be working under the assumption that the packaging tooling must know whether 12.6 was actually released by Apple or not. But why is this relevant at all? As far as I can tell, nothing will go wrong if it doesn't.

@brettcannon
Copy link
Member

You seem to be working under the assumption that the packaging tooling must know whether 12.6 was actually released by Apple or not. But why is this relevant at all? As far as I can tell, nothing will go wrong if it doesn't.

Because I don't know where we would need to stop generating those tags.

To make this concrete, if you are on macOS 13, what tags would you have us generate to cover macOS 12?

@rgommers
Copy link

rgommers commented Oct 7, 2022

Because I don't know where we would need to stop generating those tags.

We do not need an explicit list of all valid tags for either tag generation at build time or tag validation at install time (see the bullet points in my comment above). I think the only reason you are asking is because that is what packaging.tags.mac_platforms currently does. Is that right?

Unless I'm missing a use case here, a better design would be to have a function that validates a given tag given the target interpreter, rather than "return a list of all valid tags for this platform" and then have pip & co. just use that to check if the tag they are seeing is in that huge list of valid tags.

To make this concrete, if you are on macOS 13, what tags would you have us generate to cover macOS 12?

If you don't want to change what mac_platforms currently does, just pick a minor version number that's high enough. Say generate 12_0 ... 12_42. It really shouldn't matter, since they are all valid major_minor version numbers.

It also doesn't seem to matter for performance by the way. I checked what pip currently uses (a 12_6 tag will fail at https://github.com/pypa/pip/blob/0d4e9eb72253c008f2790482e664ce92198c5240/src/pip/_internal/resolution/resolvelib/factory.py#L131-L138), and the list of tags it generates to compare against is already huge:

ipdb> for tag in self._finder.target_python.get_tags():
    print(tag)

cp39-cp39-macosx_12_0_arm64
cp39-cp39-macosx_12_0_universal2
cp39-cp39-macosx_11_0_arm64
cp39-cp39-macosx_11_0_universal2
cp39-cp39-macosx_10_16_universal2
cp39-cp39-macosx_10_15_universal2
cp39-cp39-macosx_10_14_universal2
cp39-cp39-macosx_10_13_universal2
cp39-cp39-macosx_10_12_universal2
cp39-cp39-macosx_10_11_universal2
cp39-cp39-macosx_10_10_universal2
cp39-cp39-macosx_10_9_universal2
cp39-cp39-macosx_10_8_universal2
cp39-cp39-macosx_10_7_universal2
cp39-cp39-macosx_10_6_universal2
cp39-cp39-macosx_10_5_universal2
cp39-cp39-macosx_10_4_universal2
cp39-abi3-macosx_12_0_arm64
cp39-abi3-macosx_12_0_universal2
cp39-abi3-macosx_11_0_arm64
cp39-abi3-macosx_11_0_universal2
cp39-abi3-macosx_10_16_universal2
cp39-abi3-macosx_10_15_universal2
cp39-abi3-macosx_10_14_universal2
cp39-abi3-macosx_10_13_universal2
cp39-abi3-macosx_10_12_universal2
cp39-abi3-macosx_10_11_universal2
cp39-abi3-macosx_10_10_universal2
cp39-abi3-macosx_10_9_universal2
cp39-abi3-macosx_10_8_universal2
cp39-abi3-macosx_10_7_universal2
cp39-abi3-macosx_10_6_universal2
cp39-abi3-macosx_10_5_universal2
cp39-abi3-macosx_10_4_universal2
cp39-none-macosx_12_0_arm64
cp39-none-macosx_12_0_universal2
cp39-none-macosx_11_0_arm64
cp39-none-macosx_11_0_universal2
cp39-none-macosx_10_16_universal2
cp39-none-macosx_10_15_universal2
cp39-none-macosx_10_14_universal2
cp39-none-macosx_10_13_universal2
cp39-none-macosx_10_12_universal2
cp39-none-macosx_10_11_universal2
cp39-none-macosx_10_10_universal2
cp39-none-macosx_10_9_universal2
cp39-none-macosx_10_8_universal2
cp39-none-macosx_10_7_universal2
cp39-none-macosx_10_6_universal2
cp39-none-macosx_10_5_universal2
cp39-none-macosx_10_4_universal2
cp38-abi3-macosx_12_0_arm64
cp38-abi3-macosx_12_0_universal2
cp38-abi3-macosx_11_0_arm64
cp38-abi3-macosx_11_0_universal2
cp38-abi3-macosx_10_16_universal2
cp38-abi3-macosx_10_15_universal2
cp38-abi3-macosx_10_14_universal2
cp38-abi3-macosx_10_13_universal2
cp38-abi3-macosx_10_12_universal2
cp38-abi3-macosx_10_11_universal2
cp38-abi3-macosx_10_10_universal2
cp38-abi3-macosx_10_9_universal2
cp38-abi3-macosx_10_8_universal2
cp38-abi3-macosx_10_7_universal2
cp38-abi3-macosx_10_6_universal2
cp38-abi3-macosx_10_5_universal2
cp38-abi3-macosx_10_4_universal2
cp37-abi3-macosx_12_0_arm64
cp37-abi3-macosx_12_0_universal2
cp37-abi3-macosx_11_0_arm64
cp37-abi3-macosx_11_0_universal2
cp37-abi3-macosx_10_16_universal2
cp37-abi3-macosx_10_15_universal2
cp37-abi3-macosx_10_14_universal2
cp37-abi3-macosx_10_13_universal2
cp37-abi3-macosx_10_12_universal2
cp37-abi3-macosx_10_11_universal2
cp37-abi3-macosx_10_10_universal2
cp37-abi3-macosx_10_9_universal2
cp37-abi3-macosx_10_8_universal2
cp37-abi3-macosx_10_7_universal2
cp37-abi3-macosx_10_6_universal2
cp37-abi3-macosx_10_5_universal2
cp37-abi3-macosx_10_4_universal2
cp36-abi3-macosx_12_0_arm64
cp36-abi3-macosx_12_0_universal2
cp36-abi3-macosx_11_0_arm64
cp36-abi3-macosx_11_0_universal2
cp36-abi3-macosx_10_16_universal2
cp36-abi3-macosx_10_15_universal2
cp36-abi3-macosx_10_14_universal2
cp36-abi3-macosx_10_13_universal2
cp36-abi3-macosx_10_12_universal2
cp36-abi3-macosx_10_11_universal2
cp36-abi3-macosx_10_10_universal2
cp36-abi3-macosx_10_9_universal2
cp36-abi3-macosx_10_8_universal2
cp36-abi3-macosx_10_7_universal2
cp36-abi3-macosx_10_6_universal2
cp36-abi3-macosx_10_5_universal2
cp36-abi3-macosx_10_4_universal2
cp35-abi3-macosx_12_0_arm64
cp35-abi3-macosx_12_0_universal2
cp35-abi3-macosx_11_0_arm64
cp35-abi3-macosx_11_0_universal2
cp35-abi3-macosx_10_16_universal2
cp35-abi3-macosx_10_15_universal2
cp35-abi3-macosx_10_14_universal2
cp35-abi3-macosx_10_13_universal2
cp35-abi3-macosx_10_12_universal2
cp35-abi3-macosx_10_11_universal2
cp35-abi3-macosx_10_10_universal2
cp35-abi3-macosx_10_9_universal2
cp35-abi3-macosx_10_8_universal2
cp35-abi3-macosx_10_7_universal2
cp35-abi3-macosx_10_6_universal2
cp35-abi3-macosx_10_5_universal2
cp35-abi3-macosx_10_4_universal2
cp34-abi3-macosx_12_0_arm64
cp34-abi3-macosx_12_0_universal2
cp34-abi3-macosx_11_0_arm64
cp34-abi3-macosx_11_0_universal2
cp34-abi3-macosx_10_16_universal2
cp34-abi3-macosx_10_15_universal2
cp34-abi3-macosx_10_14_universal2
cp34-abi3-macosx_10_13_universal2
cp34-abi3-macosx_10_12_universal2
cp34-abi3-macosx_10_11_universal2
cp34-abi3-macosx_10_10_universal2
cp34-abi3-macosx_10_9_universal2
cp34-abi3-macosx_10_8_universal2
cp34-abi3-macosx_10_7_universal2
cp34-abi3-macosx_10_6_universal2
cp34-abi3-macosx_10_5_universal2
cp34-abi3-macosx_10_4_universal2
cp33-abi3-macosx_12_0_arm64
cp33-abi3-macosx_12_0_universal2
cp33-abi3-macosx_11_0_arm64
cp33-abi3-macosx_11_0_universal2
cp33-abi3-macosx_10_16_universal2
cp33-abi3-macosx_10_15_universal2
cp33-abi3-macosx_10_14_universal2
cp33-abi3-macosx_10_13_universal2
cp33-abi3-macosx_10_12_universal2
cp33-abi3-macosx_10_11_universal2
cp33-abi3-macosx_10_10_universal2
cp33-abi3-macosx_10_9_universal2
cp33-abi3-macosx_10_8_universal2
cp33-abi3-macosx_10_7_universal2
cp33-abi3-macosx_10_6_universal2
cp33-abi3-macosx_10_5_universal2
cp33-abi3-macosx_10_4_universal2
cp32-abi3-macosx_12_0_arm64
cp32-abi3-macosx_12_0_universal2
cp32-abi3-macosx_11_0_arm64
cp32-abi3-macosx_11_0_universal2
cp32-abi3-macosx_10_16_universal2
cp32-abi3-macosx_10_15_universal2
cp32-abi3-macosx_10_14_universal2
cp32-abi3-macosx_10_13_universal2
cp32-abi3-macosx_10_12_universal2
cp32-abi3-macosx_10_11_universal2
cp32-abi3-macosx_10_10_universal2
cp32-abi3-macosx_10_9_universal2
cp32-abi3-macosx_10_8_universal2
cp32-abi3-macosx_10_7_universal2
cp32-abi3-macosx_10_6_universal2
cp32-abi3-macosx_10_5_universal2
cp32-abi3-macosx_10_4_universal2
py39-none-macosx_12_0_arm64
py39-none-macosx_12_0_universal2
py39-none-macosx_11_0_arm64
py39-none-macosx_11_0_universal2
py39-none-macosx_10_16_universal2
py39-none-macosx_10_15_universal2
py39-none-macosx_10_14_universal2
py39-none-macosx_10_13_universal2
py39-none-macosx_10_12_universal2
py39-none-macosx_10_11_universal2
py39-none-macosx_10_10_universal2
py39-none-macosx_10_9_universal2
py39-none-macosx_10_8_universal2
py39-none-macosx_10_7_universal2
py39-none-macosx_10_6_universal2
py39-none-macosx_10_5_universal2
py39-none-macosx_10_4_universal2
py3-none-macosx_12_0_arm64
py3-none-macosx_12_0_universal2
py3-none-macosx_11_0_arm64
py3-none-macosx_11_0_universal2
py3-none-macosx_10_16_universal2
py3-none-macosx_10_15_universal2
py3-none-macosx_10_14_universal2
py3-none-macosx_10_13_universal2
py3-none-macosx_10_12_universal2
py3-none-macosx_10_11_universal2
py3-none-macosx_10_10_universal2
py3-none-macosx_10_9_universal2
py3-none-macosx_10_8_universal2
py3-none-macosx_10_7_universal2
py3-none-macosx_10_6_universal2
py3-none-macosx_10_5_universal2
py3-none-macosx_10_4_universal2
py38-none-macosx_12_0_arm64
py38-none-macosx_12_0_universal2
py38-none-macosx_11_0_arm64
py38-none-macosx_11_0_universal2
py38-none-macosx_10_16_universal2
py38-none-macosx_10_15_universal2
py38-none-macosx_10_14_universal2
py38-none-macosx_10_13_universal2
py38-none-macosx_10_12_universal2
py38-none-macosx_10_11_universal2
py38-none-macosx_10_10_universal2
py38-none-macosx_10_9_universal2
py38-none-macosx_10_8_universal2
py38-none-macosx_10_7_universal2
py38-none-macosx_10_6_universal2
py38-none-macosx_10_5_universal2
py38-none-macosx_10_4_universal2
py37-none-macosx_12_0_arm64
py37-none-macosx_12_0_universal2
py37-none-macosx_11_0_arm64
py37-none-macosx_11_0_universal2
py37-none-macosx_10_16_universal2
py37-none-macosx_10_15_universal2
py37-none-macosx_10_14_universal2
py37-none-macosx_10_13_universal2
py37-none-macosx_10_12_universal2
py37-none-macosx_10_11_universal2
py37-none-macosx_10_10_universal2
py37-none-macosx_10_9_universal2
py37-none-macosx_10_8_universal2
py37-none-macosx_10_7_universal2
py37-none-macosx_10_6_universal2
py37-none-macosx_10_5_universal2
py37-none-macosx_10_4_universal2
py36-none-macosx_12_0_arm64
py36-none-macosx_12_0_universal2
py36-none-macosx_11_0_arm64
py36-none-macosx_11_0_universal2
py36-none-macosx_10_16_universal2
py36-none-macosx_10_15_universal2
py36-none-macosx_10_14_universal2
py36-none-macosx_10_13_universal2
py36-none-macosx_10_12_universal2
py36-none-macosx_10_11_universal2
py36-none-macosx_10_10_universal2
py36-none-macosx_10_9_universal2
py36-none-macosx_10_8_universal2
py36-none-macosx_10_7_universal2
py36-none-macosx_10_6_universal2
py36-none-macosx_10_5_universal2
py36-none-macosx_10_4_universal2
py35-none-macosx_12_0_arm64
py35-none-macosx_12_0_universal2
py35-none-macosx_11_0_arm64
py35-none-macosx_11_0_universal2
py35-none-macosx_10_16_universal2
py35-none-macosx_10_15_universal2
py35-none-macosx_10_14_universal2
py35-none-macosx_10_13_universal2
py35-none-macosx_10_12_universal2
py35-none-macosx_10_11_universal2
py35-none-macosx_10_10_universal2
py35-none-macosx_10_9_universal2
py35-none-macosx_10_8_universal2
py35-none-macosx_10_7_universal2
py35-none-macosx_10_6_universal2
py35-none-macosx_10_5_universal2
py35-none-macosx_10_4_universal2
py34-none-macosx_12_0_arm64
py34-none-macosx_12_0_universal2
py34-none-macosx_11_0_arm64
py34-none-macosx_11_0_universal2
py34-none-macosx_10_16_universal2
py34-none-macosx_10_15_universal2
py34-none-macosx_10_14_universal2
py34-none-macosx_10_13_universal2
py34-none-macosx_10_12_universal2
py34-none-macosx_10_11_universal2
py34-none-macosx_10_10_universal2
py34-none-macosx_10_9_universal2
py34-none-macosx_10_8_universal2
py34-none-macosx_10_7_universal2
py34-none-macosx_10_6_universal2
py34-none-macosx_10_5_universal2
py34-none-macosx_10_4_universal2
py33-none-macosx_12_0_arm64
py33-none-macosx_12_0_universal2
py33-none-macosx_11_0_arm64
py33-none-macosx_11_0_universal2
py33-none-macosx_10_16_universal2
py33-none-macosx_10_15_universal2
py33-none-macosx_10_14_universal2
py33-none-macosx_10_13_universal2
py33-none-macosx_10_12_universal2
py33-none-macosx_10_11_universal2
py33-none-macosx_10_10_universal2
py33-none-macosx_10_9_universal2
py33-none-macosx_10_8_universal2
py33-none-macosx_10_7_universal2
py33-none-macosx_10_6_universal2
py33-none-macosx_10_5_universal2
py33-none-macosx_10_4_universal2
py32-none-macosx_12_0_arm64
py32-none-macosx_12_0_universal2
py32-none-macosx_11_0_arm64
py32-none-macosx_11_0_universal2
py32-none-macosx_10_16_universal2
py32-none-macosx_10_15_universal2
py32-none-macosx_10_14_universal2
py32-none-macosx_10_13_universal2
py32-none-macosx_10_12_universal2
py32-none-macosx_10_11_universal2
py32-none-macosx_10_10_universal2
py32-none-macosx_10_9_universal2
py32-none-macosx_10_8_universal2
py32-none-macosx_10_7_universal2
py32-none-macosx_10_6_universal2
py32-none-macosx_10_5_universal2
py32-none-macosx_10_4_universal2
py31-none-macosx_12_0_arm64
py31-none-macosx_12_0_universal2
py31-none-macosx_11_0_arm64
py31-none-macosx_11_0_universal2
py31-none-macosx_10_16_universal2
py31-none-macosx_10_15_universal2
py31-none-macosx_10_14_universal2
py31-none-macosx_10_13_universal2
py31-none-macosx_10_12_universal2
py31-none-macosx_10_11_universal2
py31-none-macosx_10_10_universal2
py31-none-macosx_10_9_universal2
py31-none-macosx_10_8_universal2
py31-none-macosx_10_7_universal2
py31-none-macosx_10_6_universal2
py31-none-macosx_10_5_universal2
py31-none-macosx_10_4_universal2
py30-none-macosx_12_0_arm64
py30-none-macosx_12_0_universal2
py30-none-macosx_11_0_arm64
py30-none-macosx_11_0_universal2
py30-none-macosx_10_16_universal2
py30-none-macosx_10_15_universal2
py30-none-macosx_10_14_universal2
py30-none-macosx_10_13_universal2
py30-none-macosx_10_12_universal2
py30-none-macosx_10_11_universal2
py30-none-macosx_10_10_universal2
py30-none-macosx_10_9_universal2
py30-none-macosx_10_8_universal2
py30-none-macosx_10_7_universal2
py30-none-macosx_10_6_universal2
py30-none-macosx_10_5_universal2
py30-none-macosx_10_4_universal2
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

@brettcannon
Copy link
Member

We do not need an explicit list of all valid tags for either tag generation at build time or tag validation at install time (see the bullet points in my comment above). I think the only reason you are asking is because that is what packaging.tags.mac_platforms currently does. Is that right?

I ask because the library is designed to generate the list of valid tags, not to ask the library if the current system is valid for a specific tag.

Unless I'm missing a use case here, a better design would be to have a function that validates a given tag given the target interpreter, rather than "return a list of all valid tags for this platform" and then have pip & co. just use that to check if the tag they are seeing is in that huge list of valid tags.

The specs around tags are not structured to require asking a library if something is valid; it's meant to work against a static list of tags you were given. You're meant to be able to take a list of wheel tags and a list of platform tags and figure out what to use w/o using packaging. A JSON dump of what a platform supports should be an acceptable input to a tool resolving which wheel tags are acceptable; think of a SaaS provider giving you such a JSON file to let you ship the appropriate wheels/code to work on their service.

Another thing to point out is we have gone this long w/o finer granularity than the annual release version and basically been fine (i.e. all we never listed the micro releases from the 10.x version range).

I think we're at enough of an impasse that bringing this up on discuss.python.org is appropriate.

@rgommers
Copy link

rgommers commented Oct 10, 2022

Another thing to point out is we have gone this long w/o finer granularity than the annual release version and basically been fine (i.e. all we never listed the micro releases from the 10.x version range).

Yes agreed. I think we typically ignore the corner cases, and in practice it won't be so bad. Similar to Python releases, it's very common to deal with minor releases, but very rare to worry about a 3.x.0 release having a bug and therefore needing a wheel to only work on .1 and up. I guess when it does come up, folks just raise an exception at runtime with instructions to upgrade.

I think we're at enough of an impasse that bringing this up on discuss.python.org is appropriate.

If you don't mind, I'll leave it at this. I've already spent more time on investigating this than I had available, and moving this to Discourse is unlikely to bring a quick resolution. So I'll leave it to the next person with a real need for this.

@uranusjr
Copy link
Member

Can we design the interface to provide something like is_tag_compatible instead? I’ve always felt generating a full list of tags cumbersome, but assumed there are reasons for that.

@brettcannon
Copy link
Member

brettcannon commented Oct 14, 2022

Can we design the interface to provide something like is_tag_compatible instead?

We could have a convenience function for that which simply does the logic of searching the list of tags. But you will probably also want a utility function which takes a set of wheel tags (or file names) and then returns which tag matches "the best" (i.e. highest in the list of supported tags). This would also let you define "best" in either terms of fastest or most compatible/generic based on the order of the list.

I’ve always felt generating a full list of tags cumbersome, but assumed there are reasons for that.

Well, the API is based on generators so you don't have to generate the full list if you get an answer early enough. 😉 But yes, the reason for the regeneration has been that a platform effectively states what tags it supports; we just happens to be calculated constantly because CPython and other interpreters don't record these details externally in some JSON file (which is something I have thought about).

@pradyunsg
Copy link
Member

pradyunsg commented Oct 15, 2022

IIUC, what @uranusjr and @rgommers are suggesting is that we avoid the need to generate all the tags to determine compatibility with the current platform in a new API -- have the ability to validate the tag without needing to generate the entire itertools.product(pyvers, abis, platforms) matrix.

For example, instead of generating the entire sequence of tags in mac_platforms, having the ability to do has_compatible_mac_platform(tag) that's conditional instead of any(tag == incoming for incoming in mac_platforms()), looking something like...

def has_compatible_mac_platform(tag):
    parts = tag.platform.split("_")

    if len(parts) != 4 or parts[0] != "maxosx":
        return False

    major, minor, binary_format = int(parts[1]), int(parts[2]), parts[3]
    version_str, _, cpu_arch = platform.mac_ver()
    version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2])))
    arch = _mac_arch(cpu_arch)

    # Check version
    if (major, minor) > version:
        return False
    # TODO: Handle 11.0 vs 10.6 style comparisons, and other complexities

    # Check format
    if binary_format not in _mac_binary_formats(version, arch):
        return False

    return True

@brettcannon
Copy link
Member

The other "fun" part of tags is they were designed to be opaque strings, so parsing them was never part of the design. And I personally wouldn't want to try and make that be forward/backwards-compatible as I feel like that's asking for trouble. I also bet generating the tag combinations isn't the expensive part, but the parts that query the OS which you have to do anyway to validate compatibility. If we were dealing with list lengths in the millions I would understand, but we are dealing with maybe a thousand at worst, and we have platform_tags() if you want to control which part of the tag triple you want to look at.

If people want a more ergonomic, high-level I'm happy to talk about that, but I don't want to get sucked into premature optimization below that API surface when I don't know how much computation we can actually avoid in the common case. Or put another way, any(tag == incoming for incoming in mac_platforms()) is already lazy thanks to mac_platforms() being a generator, so how much are you really shaving off in CPU costs by parsing the tag itself?

@rgommers
Copy link

For me it has nothing to do with cost of computation, that seems basically irrelevant for any packaging tools that create, install, or modify wheels. And I don't think anyone suggested otherwise? The API is cumbersome, not slow.

@brettcannon
Copy link
Member

The API is cumbersome, not slow.

I've opened #602 so we can discuss what sort of API people may want.

@brettcannon
Copy link
Member

So, back to the original intent of this issue 😅, do we want to bother with minor-version-free macOS versions?

@rgommers
Copy link

rgommers commented Nov 4, 2022

So, back to the original intent of this issue 😅, do we want to bother with minor-version-free macOS versions?

I'd say yes from first principles, it's clearer and communicates better what is happening. Plus it matches PEP 425 as @FFY00 pointed out.

The hesitation I have is transition - given that old pip versions with vendored packaging keep floating around for years, it seems to me like there's a real risk of minor-version-free wheels being produced, that are then going to fail for users with old pip versions. So adding these tags should probably come with a warning to build tool authors that they should not be used to produce wheels for the next couple of years.

@brettcannon
Copy link
Member

I'll see if @ronaldoussoren or @ned-deily have an opinion, otherwise I'm okay with adding minor-less tags starting from macOS 11 on, listing them at the lowest priority for that macOS version.

@ned-deily
Copy link

Yes, that's the right approach, IMHO. Thanks!

@ronaldoussoren
Copy link
Contributor

I'll see if @ronaldoussoren or @ned-deily have an opinion, otherwise I'm okay with adding minor-less tags starting from macOS 11 on, listing them at the lowest priority for that macOS version.

Adding minor-less tags is fine, especially as it is easily possible to get a python build that generates them.

Personally I don't particularly like minor-less versions, especially here where macosx_12_0_arm64 and macosx_12_arm64 are semantically equivalent for our purposes.

IMO the tags with a minor version should be kept as well, there are minor versions of macOS 11 and later and those can introduce new APIs. It should be possible to tag a wheel for 12.4 when a C extension uses an API introduced in 12.4.

@brettcannon
Copy link
Member

IMO the tags with a minor version should be kept as well

I'm definitely not dropping any tags, just potentially adding some.

@stessaris
Copy link

There are wheels with tags using minor versions different from 0 cropping up on pypi (e.g. https://pypi.org/project/z3-solver/4.12.4.0/#files), I guess that developers should be made aware of the problem because for a user is difficult to understand why, e.g., the wheel z3_solver-4.12.4.0-py2.py3-none-macosx_11_7_x86_64.whl cannot be installed in Ventura

@rgommers
Copy link

rgommers commented Dec 7, 2023

IMO the tags with a minor version should be kept as well, there are minor versions of macOS 11 and later and those can introduce new APIs. It should be possible to tag a wheel for 12.4 when a C extension uses an API introduced in 12.4.

+1. As a data point for this: macOS 13.3 added a new version of the Accelerate framework, including a LAPACK update from 3.2 to 3.9 and 64-bit support. This was about as large and important an update as new features get - it makes NumPy's macOS wheels ~3x smaller and improves performance of linear algebra APIs by up to 7x. We've had to avoid shipping these wheels for ~8 months now because of the lack of minor version support in packaging/pip, and still can't ship them today for x86-64 because we're still waiting for GitHub Actions to support macOS 14.

@brettcannon
Copy link
Member

Someone still needs to figure out either how to backfill minor versions for older macOS releases without requiring a packaging update, or someone needs to re-architect how wheel tag compatibility is verified and change the spec accordingly (and I'm no longer on macOS, so it won't be me).

@brettcannon
Copy link
Member

Someone still needs to figure out either how to backfill minor versions for older macOS releases without requiring a packaging update

What's the largest minor version we could expect Apple to produce? If we can all agree on what that is (plus margin of error), we could backfill to that when it hasn't been hardcoded yet due to the install of packaging being too old to have that info. I don't love this solution, but pragmatically it might be our easiest, simplest solution.

@ronaldoussoren
Copy link
Contributor

The highest bug fix release was 10.4.11 (2007), in the post "macOS 10.x" era the highest has been 12.7. It should be safe to assume that the minor version for macOS 11 onwards will stay below 10.

@brettcannon
Copy link
Member

The highest bug fix release was 10.4.11 (2007), in the post "macOS 10.x" era the highest has been 12.7. It should be safe to assume that the minor version for macOS 11 onwards will stay below 10.

@ronaldoussoren is that w/ a margin of error?

And can we safely just assume this w/o having to manually track the eventual, final minor version?

@ronaldoussoren
Copy link
Contributor

ronaldoussoren commented Dec 12, 2023

The highest bug fix release was 10.4.11 (2007), in the post "macOS 10.x" era the highest has been 12.7. It should be safe to assume that the minor version for macOS 11 onwards will stay below 10.

@ronaldoussoren is that w/ a margin of error?

And can we safely just assume this w/o having to manually track the eventual, final minor version?

That's with a small margin of error based on the final releases of macOS 11, 12 and 13. The max minor version of those was 7, backfilling to .9 gives a margin of two. To be extra save assume that the max version is 12, that would also work for the 10.4 release which had 10.4.11 as its final release. But TBH that shouldn't be necessary with the current yearly release cadence of major versions.

The current release cadence at Apple appears to be slightly faster than every other month for minor releases.

See https://en.wikipedia.org/wiki/MacOS_version_history for the detailed information.

And take all of this with a pinch of salt, I have no insight in the internal workings of Apple.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants