Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions src/packaging/tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,21 @@ def _abi3_applies(python_version: PythonVersion, threading: bool) -> bool:
"""
Determine if the Python version supports abi3.
PEP 384 was first implemented in Python 3.2. The threaded (`--disable-gil`)
PEP 384 was first implemented in Python 3.2. The free-threaded
builds do not support abi3.
"""
return len(python_version) > 1 and tuple(python_version) >= (3, 2) and not threading


def _abi3t_applies(python_version: PythonVersion, threading: bool) -> bool:
"""
Determine if the Python version supports abi3.abi3t.
PEP 803 was first implemented in Python 3.15.
"""
return len(python_version) > 1 and tuple(python_version) >= (3, 15) and threading


def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> list[str]:
py_version = tuple(py_version) # To allow for version comparison.
abis = []
Expand Down Expand Up @@ -256,16 +265,24 @@ def cpython_tags(

threading = _is_threaded_cpython(abis)
use_abi3 = _abi3_applies(python_version, threading)
use_abi3t = _abi3t_applies(python_version, threading)

if use_abi3:
yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms)
if use_abi3t:
yield from (Tag(interpreter, "abi3t", platform_) for platform_ in platforms)

yield from (Tag(interpreter, "none", platform_) for platform_ in platforms)

if use_abi3:
if use_abi3 or use_abi3t:
for minor_version in range(python_version[1] - 1, 1, -1):
for platform_ in platforms:
version = _version_nodot((python_version[0], minor_version))
interpreter = f"cp{version}"
yield Tag(interpreter, "abi3", platform_)
if use_abi3:
yield Tag(interpreter, "abi3", platform_)
if use_abi3t and minor_version > 14:
Copy link

@encukou encukou Mar 6, 2026

Choose a reason for hiding this comment

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

Suggested change
if use_abi3t and minor_version > 14:
if use_abi3t:

[edit: practically this could be minor_version >= 13, but it's not worth the special case.]

yield Tag(interpreter, "abi3t", platform_)


def _generic_abi() -> list[str]:
Expand Down
36 changes: 36 additions & 0 deletions tests/test_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,42 @@ def test_all_args(self) -> None:
tags.Tag("cp313", "none", "plat2"),
]

result = list(tags.cpython_tags((3, 15), ["cp315t"], ["platform"]))
assert result == [
tags.Tag("cp315", "cp315t", "platform"),
tags.Tag("cp315", "abi3t", "platform"),
tags.Tag("cp315", "none", "platform"),
]

result = list(tags.cpython_tags((3, 16), ["cp316t"], ["platform"]))
assert result == [
tags.Tag("cp316", "cp316t", "platform"),
tags.Tag("cp316", "abi3t", "platform"),
tags.Tag("cp316", "none", "platform"),
tags.Tag("cp315", "abi3t", "platform"),
Copy link
Member

Choose a reason for hiding this comment

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

The Discourse discussion suggests that cp314-abi3t, cp313-abi3t, cp312-abi3t, ... should also be returned here...

]

result = list(tags.cpython_tags((3, 16), ["cp316"], ["platform"]))
assert result == [
tags.Tag("cp316", "cp316", "platform"),
tags.Tag("cp316", "abi3", "platform"),
tags.Tag("cp316", "none", "platform"),
tags.Tag("cp315", "abi3", "platform"),
tags.Tag("cp314", "abi3", "platform"),
tags.Tag("cp313", "abi3", "platform"),
tags.Tag("cp312", "abi3", "platform"),
tags.Tag("cp311", "abi3", "platform"),
tags.Tag("cp310", "abi3", "platform"),
tags.Tag("cp39", "abi3", "platform"),
tags.Tag("cp38", "abi3", "platform"),
tags.Tag("cp37", "abi3", "platform"),
tags.Tag("cp36", "abi3", "platform"),
tags.Tag("cp35", "abi3", "platform"),
tags.Tag("cp34", "abi3", "platform"),
tags.Tag("cp33", "abi3", "platform"),
tags.Tag("cp32", "abi3", "platform"),
]

def test_python_version_defaults(self) -> None:
tag = next(tags.cpython_tags(abis=["abi3"], platforms=["any"]))
interpreter = "cp" + tags._version_nodot(sys.version_info[:2])
Expand Down