Skip to content

Commit

Permalink
Fix wheel tag determination (#591)
Browse files Browse the repository at this point in the history
Co-authored-by: Joshua C. Randall <jcrandall@alum.mit.edu>
Co-authored-by: Randy Döring <30527984+radoering@users.noreply.github.com>
  • Loading branch information
3 people authored May 22, 2023
1 parent 73dd45a commit 5da9a2e
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 3 deletions.
51 changes: 48 additions & 3 deletions src/poetry/core/masonry/builders/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import shutil
import stat
import subprocess
import sys
import tempfile
import zipfile

Expand All @@ -17,7 +18,7 @@
from typing import TYPE_CHECKING
from typing import TextIO

from packaging.tags import sys_tags
import packaging.tags

from poetry.core import __version__
from poetry.core.constraints.version import parse_constraint
Expand All @@ -26,6 +27,7 @@
from poetry.core.masonry.utils.helpers import distribution_name
from poetry.core.masonry.utils.helpers import normalize_file_permissions
from poetry.core.masonry.utils.package_include import PackageInclude
from poetry.core.utils.helpers import decode
from poetry.core.utils.helpers import temporary_directory


Expand Down Expand Up @@ -327,16 +329,59 @@ def dist_info_name(self, name: NormalizedName, version: str) -> str:
escaped_name = distribution_name(name)
return f"{escaped_name}-{version}.dist-info"

def _get_sys_tags(self) -> list[str]:
"""Get sys_tags via subprocess.
Required if poetry-core is not run inside the build environment.
"""
try:
output = subprocess.check_output(
[
self.executable.as_posix(),
"-c",
f"""
import importlib.util
import sys
from pathlib import Path
spec = importlib.util.spec_from_file_location(
"packaging", Path(r"{packaging.__file__}")
)
packaging = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = packaging
spec = importlib.util.spec_from_file_location(
"packaging.tags", Path(r"{packaging.tags.__file__}")
)
packaging_tags = importlib.util.module_from_spec(spec)
spec.loader.exec_module(packaging_tags)
for t in packaging_tags.sys_tags():
print(t.interpreter, t.abi, t.platform, sep="-")
""",
],
stderr=subprocess.STDOUT,
)
except subprocess.CalledProcessError as e:
raise RuntimeError(
"Failed to get sys_tags for python interpreter"
f" '{self.executable.as_posix()}':\n{decode(e.output)}"
)
return decode(output).strip().splitlines()

@property
def tag(self) -> str:
if self._package.build_script:
sys_tag = next(sys_tags())
if self.executable != Path(sys.executable):
# poetry-core is not run in the build environment
# -> this is probably not a PEP 517 build but a poetry build
return self._get_sys_tags()[0]
sys_tag = next(packaging.tags.sys_tags())
tag = (sys_tag.interpreter, sys_tag.abi, sys_tag.platform)
else:
platform = "any"
impl = "py2.py3" if self.supports_python2() else "py3"
tag = (impl, "none", platform)

return "-".join(tag)

def _add_file(
Expand Down
14 changes: 14 additions & 0 deletions src/poetry/core/utils/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import warnings

from contextlib import contextmanager
from contextlib import suppress
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
Expand All @@ -27,6 +28,19 @@ def combine_unicode(string: str) -> str:
return unicodedata.normalize("NFC", string)


def decode(string: bytes | str, encodings: list[str] | None = None) -> str:
if not isinstance(string, bytes):
return string

encodings = encodings or ["utf-8", "latin1", "ascii"]

for encoding in encodings:
with suppress(UnicodeEncodeError, UnicodeDecodeError):
return string.decode(encoding)

return string.decode(encodings[0], errors="ignore")


def module_name(name: str) -> str:
return canonicalize_name(name).replace("-", "_")

Expand Down
20 changes: 20 additions & 0 deletions tests/masonry/builders/test_wheel.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import os
import re
import shutil
import zipfile

Expand Down Expand Up @@ -350,3 +351,22 @@ def capturing_fdopen(*args: Any, **kwargs: Any) -> TextIO | None:

assert fd_file[0] is not None
assert fd_file[0].closed


@pytest.mark.parametrize("in_venv_build", [True, False])
def test_tag(in_venv_build: bool, mocker: MockerFixture) -> None:
"""Tests that tag returns a valid tag if a build script is used,
no matter if poetry-core lives inside the build environment or not.
"""
root = fixtures_dir / "extended"
builder = WheelBuilder(Factory().create_poetry(root))

get_sys_tags_spy = mocker.spy(builder, "_get_sys_tags")
if not in_venv_build:
mocker.patch("sys.executable", "other/python")

assert re.match("^cp[23]_?\\d+-cp[23]_?\\d+m?u?-.+$", builder.tag)
if in_venv_build:
get_sys_tags_spy.assert_not_called()
else:
get_sys_tags_spy.assert_called()

0 comments on commit 5da9a2e

Please sign in to comment.