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

Support --disable-gil builds (PEP 703) in packaging.tags #728

Merged
merged 3 commits into from Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Expand Up @@ -6,6 +6,8 @@ Changelog

* Do specifier matching correctly when the specifier contains an epoch number
and has more components than the version (:issue:`683`)
* Support the experimental ``--disable-gil`` builds in packaging.tags
(:issue:`727`)

23.2 - 2023-10-01
~~~~~~~~~~~~~~~~~
Expand Down
44 changes: 31 additions & 13 deletions src/packaging/tags.py
Expand Up @@ -4,6 +4,7 @@

import logging
import platform
import re
import struct
import subprocess
import sys
Expand Down Expand Up @@ -124,20 +125,37 @@ def _normalize_string(string: str) -> str:
return string.replace(".", "_").replace("-", "_").replace(" ", "_")


def _abi3_applies(python_version: PythonVersion) -> bool:
def _is_threaded_cpython(abis: List[str]) -> bool:
"""
Determine if the ABI corresponds to a threaded (`--disable-gil`) build.

The threaded builds are indicated by a "t" in the abiflags.
"""
if len(abis) == 0:
return False
# expect e.g., cp313
m = re.match(r"cp\d+(.*)", abis[0])
if not m:
return False
abiflags = m.group(1)
return "t" in abiflags


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.
PEP 384 was first implemented in Python 3.2. The threaded (`--disable-gil`)
builds do not support abi3.
"""
return len(python_version) > 1 and tuple(python_version) >= (3, 2)
return len(python_version) > 1 and tuple(python_version) >= (3, 2) and not threading


def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]:
py_version = tuple(py_version) # To allow for version comparison.
abis = []
version = _version_nodot(py_version[:2])
debug = pymalloc = ucs4 = ""
threading = debug = pymalloc = ucs4 = ""
with_debug = _get_config_var("Py_DEBUG", warn)
has_refcount = hasattr(sys, "gettotalrefcount")
# Windows doesn't set Py_DEBUG, so checking for support of debug-compiled
Expand All @@ -146,6 +164,8 @@ def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]:
has_ext = "_d.pyd" in EXTENSION_SUFFIXES
if with_debug or (with_debug is None and (has_refcount or has_ext)):
debug = "d"
if py_version >= (3, 13) and _get_config_var("Py_NOGIL", warn):
threading = "t"
if py_version < (3, 8):
with_pymalloc = _get_config_var("WITH_PYMALLOC", warn)
if with_pymalloc or with_pymalloc is None:
Expand All @@ -159,13 +179,8 @@ def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]:
elif debug:
# Debug builds can also load "normal" extension modules.
# We can also assume no UCS-4 or pymalloc requirement.
abis.append(f"cp{version}")
abis.insert(
0,
"cp{version}{debug}{pymalloc}{ucs4}".format(
version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4
),
)
abis.append(f"cp{version}{threading}")
abis.insert(0, f"cp{version}{threading}{debug}{pymalloc}{ucs4}")
return abis


Expand Down Expand Up @@ -213,11 +228,14 @@ def cpython_tags(
for abi in abis:
for platform_ in platforms:
yield Tag(interpreter, abi, platform_)
if _abi3_applies(python_version):

threading = _is_threaded_cpython(abis)
use_abi3 = _abi3_applies(python_version, threading)
if use_abi3:
yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms)
yield from (Tag(interpreter, "none", platform_) for platform_ in platforms)

if _abi3_applies(python_version):
if use_abi3:
for minor_version in range(python_version[1] - 1, 1, -1):
for platform_ in platforms:
interpreter = "cp{version}".format(
Expand Down
20 changes: 20 additions & 0 deletions tests/test_tags.py
Expand Up @@ -781,6 +781,14 @@ def test_all_args(self):
tags.Tag("cp32", "abi3", "plat2"),
]

result = list(tags.cpython_tags((3, 13), ["cp313t"], ["plat1", "plat2"]))
assert result == [
tags.Tag("cp313", "cp313t", "plat1"),
tags.Tag("cp313", "cp313t", "plat2"),
tags.Tag("cp313", "none", "plat1"),
tags.Tag("cp313", "none", "plat2"),
]

def test_python_version_defaults(self):
tag = next(tags.cpython_tags(abis=["abi3"], platforms=["any"]))
interpreter = "cp" + tags._version_nodot(sys.version_info[:2])
Expand Down Expand Up @@ -894,6 +902,17 @@ def test__generic_abi_graal(self, monkeypatch):
monkeypatch.setattr(sysconfig, "get_config_var", config.__getitem__)
assert tags._generic_abi() == ["graalpy_38_native"]

def test__generic_abi_disable_gil(self, monkeypatch):
config = {
"Py_DEBUG": False,
"EXT_SUFFIX": ".cpython-313t-x86_64-linux-gnu.so",
"WITH_PYMALLOC": 0,
"Py_NOGIL": 1,
}
monkeypatch.setattr(sysconfig, "get_config_var", config.__getitem__)
assert tags._generic_abi() == ["cp313t"]
assert tags._generic_abi() == tags._cpython_abis((3, 13))

def test__generic_abi_none(self, monkeypatch):
config = {"EXT_SUFFIX": "..so"}
monkeypatch.setattr(sysconfig, "get_config_var", config.__getitem__)
Expand Down Expand Up @@ -922,6 +941,7 @@ def test__generic_abi_old_windows(self, monkeypatch):
"EXT_SUFFIX": ".pyd",
"Py_DEBUG": 0,
"WITH_PYMALLOC": 0,
"Py_NOGIL": 0,
}
monkeypatch.setattr(sysconfig, "get_config_var", config.__getitem__)
assert tags._generic_abi() == tags._cpython_abis(sys.version_info[:2])
Expand Down