Skip to content

Commit 30554f5

Browse files
mattipbrettcannon
andauthored
refactor tags._generic_api to use EXT_SUFFIX (#607)
Co-authored-by: Brett Cannon <[email protected]>
1 parent 97db717 commit 30554f5

File tree

2 files changed

+114
-19
lines changed

2 files changed

+114
-19
lines changed

src/packaging/tags.py

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -225,10 +225,45 @@ def cpython_tags(
225225
yield Tag(interpreter, "abi3", platform_)
226226

227227

228-
def _generic_abi() -> Iterator[str]:
229-
abi = sysconfig.get_config_var("SOABI")
230-
if abi:
231-
yield _normalize_string(abi)
228+
def _generic_abi() -> List[str]:
229+
"""
230+
Return the ABI tag based on EXT_SUFFIX.
231+
"""
232+
# The following are examples of `EXT_SUFFIX`.
233+
# We want to keep the parts which are related to the ABI and remove the
234+
# parts which are related to the platform:
235+
# - linux: '.cpython-310-x86_64-linux-gnu.so' => cp310
236+
# - mac: '.cpython-310-darwin.so' => cp310
237+
# - win: '.cp310-win_amd64.pyd' => cp310
238+
# - win: '.pyd' => cp37 (uses _cpython_abis())
239+
# - pypy: '.pypy38-pp73-x86_64-linux-gnu.so' => pypy38_pp73
240+
# - graalpy: '.graalpy-38-native-x86_64-darwin.dylib'
241+
# => graalpy_38_native
242+
243+
ext_suffix = _get_config_var("EXT_SUFFIX", warn=True)
244+
if not isinstance(ext_suffix, str) or ext_suffix[0] != ".":
245+
raise SystemError("invalid sysconfig.get_config_var('EXT_SUFFIX')")
246+
parts = ext_suffix.split(".")
247+
if len(parts) < 3:
248+
# CPython3.7 and earlier uses ".pyd" on Windows.
249+
return _cpython_abis(sys.version_info[:2])
250+
soabi = parts[1]
251+
if soabi.startswith("cpython"):
252+
# non-windows
253+
abi = "cp" + soabi.split("-")[1]
254+
elif soabi.startswith("cp"):
255+
# windows
256+
abi = soabi.split("-")[0]
257+
elif soabi.startswith("pypy"):
258+
abi = "-".join(soabi.split("-")[:2])
259+
elif soabi.startswith("graalpy"):
260+
abi = "-".join(soabi.split("-")[:3])
261+
elif soabi:
262+
# pyston, ironpython, others?
263+
abi = soabi
264+
else:
265+
return []
266+
return [_normalize_string(abi)]
232267

233268

234269
def generic_tags(
@@ -252,8 +287,9 @@ def generic_tags(
252287
interpreter = "".join([interp_name, interp_version])
253288
if abis is None:
254289
abis = _generic_abi()
290+
else:
291+
abis = list(abis)
255292
platforms = list(platforms or platform_tags())
256-
abis = list(abis)
257293
if "none" not in abis:
258294
abis.append("none")
259295
for abi in abis:
@@ -463,6 +499,9 @@ def platform_tags() -> Iterator[str]:
463499
def interpreter_name() -> str:
464500
"""
465501
Returns the name of the running interpreter.
502+
503+
Some implementations have a reserved, two-letter abbreviation which will
504+
be returned when appropriate.
466505
"""
467506
name = sys.implementation.name
468507
return INTERPRETER_SHORT_NAMES.get(name) or name

tests/test_tags.py

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -823,22 +823,78 @@ def test_no_abi3_python27(self):
823823

824824

825825
class TestGenericTags:
826-
@pytest.mark.skipif(
827-
not sysconfig.get_config_var("SOABI"), reason="SOABI not defined"
828-
)
829-
def test__generic_abi_soabi_provided(self):
830-
abi = sysconfig.get_config_var("SOABI").replace(".", "_").replace("-", "_")
831-
assert [abi] == list(tags._generic_abi())
832-
833-
def test__generic_abi(self, monkeypatch):
826+
def test__generic_abi_macos(self, monkeypatch):
834827
monkeypatch.setattr(
835-
sysconfig, "get_config_var", lambda key: "cpython-37m-darwin"
828+
sysconfig, "get_config_var", lambda key: ".cpython-37m-darwin.so"
836829
)
837-
assert list(tags._generic_abi()) == ["cpython_37m_darwin"]
830+
monkeypatch.setattr(tags, "interpreter_name", lambda: "cp")
831+
assert tags._generic_abi() == ["cp37m"]
832+
833+
def test__generic_abi_linux_cpython(self, monkeypatch):
834+
config = {
835+
"Py_DEBUG": False,
836+
"WITH_PYMALLOC": True,
837+
"EXT_SUFFIX": ".cpython-37m-x86_64-linux-gnu.so",
838+
}
839+
monkeypatch.setattr(sysconfig, "get_config_var", config.__getitem__)
840+
monkeypatch.setattr(tags, "interpreter_name", lambda: "cp")
841+
# They are identical
842+
assert tags._cpython_abis((3, 7)) == ["cp37m"]
843+
assert tags._generic_abi() == ["cp37m"]
844+
845+
def test__generic_abi_jp(self, monkeypatch):
846+
config = {"EXT_SUFFIX": ".return exactly this.so"}
847+
monkeypatch.setattr(sysconfig, "get_config_var", config.__getitem__)
848+
assert tags._generic_abi() == ["return exactly this"]
849+
850+
def test__generic_abi_graal(self, monkeypatch):
851+
config = {"EXT_SUFFIX": ".graalpy-38-native-x86_64-darwin.so"}
852+
monkeypatch.setattr(sysconfig, "get_config_var", config.__getitem__)
853+
assert tags._generic_abi() == ["graalpy_38_native"]
854+
855+
def test__generic_abi_none(self, monkeypatch):
856+
config = {"EXT_SUFFIX": "..so"}
857+
monkeypatch.setattr(sysconfig, "get_config_var", config.__getitem__)
858+
assert tags._generic_abi() == []
859+
860+
@pytest.mark.parametrize("ext_suffix", ["invalid", None])
861+
def test__generic_abi_error(self, ext_suffix, monkeypatch):
862+
config = {"EXT_SUFFIX": ext_suffix}
863+
monkeypatch.setattr(sysconfig, "get_config_var", config.__getitem__)
864+
with pytest.raises(SystemError) as e:
865+
tags._generic_abi()
866+
assert "EXT_SUFFIX" in str(e.value)
867+
868+
def test__generic_abi_linux_pypy(self, monkeypatch):
869+
# issue gh-606
870+
config = {
871+
"Py_DEBUG": False,
872+
"EXT_SUFFIX": ".pypy39-pp73-x86_64-linux-gnu.so",
873+
}
874+
monkeypatch.setattr(sysconfig, "get_config_var", config.__getitem__)
875+
monkeypatch.setattr(tags, "interpreter_name", lambda: "pp")
876+
assert tags._generic_abi() == ["pypy39_pp73"]
877+
878+
def test__generic_abi_old_windows(self, monkeypatch):
879+
config = {
880+
"EXT_SUFFIX": ".pyd",
881+
"Py_DEBUG": 0,
882+
"WITH_PYMALLOC": 0,
883+
}
884+
monkeypatch.setattr(sysconfig, "get_config_var", config.__getitem__)
885+
assert tags._generic_abi() == tags._cpython_abis(sys.version_info[:2])
886+
887+
def test__generic_abi_windows(self, monkeypatch):
888+
config = {
889+
"EXT_SUFFIX": ".cp310-win_amd64.pyd",
890+
}
891+
monkeypatch.setattr(sysconfig, "get_config_var", config.__getitem__)
892+
assert tags._generic_abi() == ["cp310"]
838893

839-
def test__generic_abi_no_soabi(self, monkeypatch):
840-
monkeypatch.setattr(sysconfig, "get_config_var", lambda key: None)
841-
assert not list(tags._generic_abi())
894+
@pytest.mark.skipif(sys.implementation.name != "cpython", reason="CPython-only")
895+
def test__generic_abi_agree(self):
896+
"""Test that the two methods of finding the abi tag agree"""
897+
assert tags._generic_abi() == tags._cpython_abis(sys.version_info[:2])
842898

843899
def test_generic_platforms(self):
844900
platform = sysconfig.get_platform().replace("-", "_")
@@ -874,7 +930,7 @@ def test_interpreter_default(self, monkeypatch):
874930
assert result == [tags.Tag("sillywalkNN", "none", "any")]
875931

876932
def test_abis_default(self, monkeypatch):
877-
monkeypatch.setattr(tags, "_generic_abi", lambda: iter(["abi"]))
933+
monkeypatch.setattr(tags, "_generic_abi", lambda: ["abi"])
878934
result = list(tags.generic_tags(interpreter="sillywalk", platforms=["any"]))
879935
assert result == [
880936
tags.Tag("sillywalk", "abi", "any"),

0 commit comments

Comments
 (0)