WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 2619074

Browse files
fix: address bugs in gen-build-spec
Signed-off-by: Abhinav Pradeep <[email protected]>
1 parent b9da4b0 commit 2619074

File tree

4 files changed

+64
-21
lines changed

4 files changed

+64
-21
lines changed

src/macaron/build_spec_generator/build_spec_generator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ def gen_build_spec_for_purl(
9797
case BuildSpecFormat.DOCKERFILE:
9898
try:
9999
build_spec_content = gen_dockerfile(build_spec)
100-
except ValueError as error:
101-
logger.error("Error while serializing the build spec: %s.", error)
100+
except GenerateBuildSpecError as error:
101+
logger.error("Error while generating the build spec: %s.", error)
102102
return os.EX_DATAERR
103103
build_spec_file_path = os.path.join(build_spec_dir_path, "dockerfile.buildspec")
104104

src/macaron/build_spec_generator/common_spec/core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ def gen_generic_build_spec(
378378
"purl": str(purl),
379379
"language": target_language,
380380
"build_tools": build_tool_names,
381-
"build_commands": [selected_build_command],
381+
"build_commands": [selected_build_command] if selected_build_command else [],
382382
}
383383
)
384384
ECOSYSTEMS[purl.type.upper()].value(base_build_spec_dict).resolve_fields(purl)

src/macaron/build_spec_generator/common_spec/pypi_spec.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,16 @@ def resolve_fields(self, purl: PackageURL) -> None:
155155
chronologically_likeliest_version = (
156156
pypi_package_json.get_chronologically_suitable_setuptools_version()
157157
)
158+
try:
159+
# Get information from the wheel file name.
160+
logger.debug(pypi_package_json.wheel_filename)
161+
_, _, _, tags = parse_wheel_filename(pypi_package_json.wheel_filename)
162+
for tag in tags:
163+
wheel_name_python_version_list.append(tag.interpreter)
164+
wheel_name_platforms.add(tag.platform)
165+
logger.debug(python_version_set)
166+
except InvalidWheelFilename:
167+
logger.debug("Could not parse wheel file name to extract version")
158168
except SourceCodeError:
159169
logger.debug("Could not find pure wheel matching this PURL")
160170

@@ -214,17 +224,6 @@ def resolve_fields(self, purl: PackageURL) -> None:
214224
except (InvalidRequirement, InvalidSpecifier) as error:
215225
logger.debug("Malformed requirement encountered %s : %s", requirement, error)
216226

217-
try:
218-
# Get information from the wheel file name.
219-
logger.debug(pypi_package_json.wheel_filename)
220-
_, _, _, tags = parse_wheel_filename(pypi_package_json.wheel_filename)
221-
for tag in tags:
222-
wheel_name_python_version_list.append(tag.interpreter)
223-
wheel_name_platforms.add(tag.platform)
224-
logger.debug(python_version_set)
225-
except InvalidWheelFilename:
226-
logger.debug("Could not parse wheel file name to extract version")
227-
228227
self.data["language_version"] = list(python_version_set) or wheel_name_python_version_list
229228

230229
# Use the default build command for pure Python packages.
@@ -243,9 +242,18 @@ def resolve_fields(self, purl: PackageURL) -> None:
243242

244243
if not patched_build_commands:
245244
# Resolve and patch build commands.
246-
selected_build_commands = self.data["build_commands"] or self.get_default_build_commands(
247-
self.data["build_tools"]
248-
)
245+
246+
# To ensure that selected_build_commands is never empty, we seed with the fallback
247+
# command of python -m build --wheel -n
248+
if self.data["build_commands"]:
249+
selected_build_commands = self.data["build_commands"]
250+
else:
251+
self.data["build_commands"] = ["python -m build --wheel -n".split()]
252+
selected_build_commands = (
253+
self.get_default_build_commands(self.data["build_tools"]) or self.data["build_commands"]
254+
)
255+
256+
logger.debug(selected_build_commands)
249257

250258
patched_build_commands = (
251259
patch_commands(

src/macaron/build_spec_generator/dockerfile/pypi_dockerfile_output.py

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"""This module implements the logic to generate a dockerfile from a Python buildspec."""
55

66
import logging
7+
import re
78
from textwrap import dedent
89

910
from packaging.specifiers import InvalidSpecifier, SpecifierSet
@@ -35,8 +36,7 @@ def gen_dockerfile(buildspec: BaseBuildSpecDict) -> str:
3536
"""
3637
language_version: str | None = pick_specific_version(buildspec)
3738
if language_version is None:
38-
logger.debug("Could not derive a specific interpreter version.")
39-
raise GenerateBuildSpecError("Could not derive specific interpreter version.")
39+
raise GenerateBuildSpecError("Could not derive specific interpreter version")
4040
backend_install_commands: str = " && ".join(build_backend_commands(buildspec))
4141
build_tool_install: str = ""
4242
if (
@@ -124,8 +124,18 @@ def pick_specific_version(buildspec: BaseBuildSpecDict) -> str | None:
124124
try:
125125
version_set &= SpecifierSet(version)
126126
except InvalidSpecifier as error:
127-
logger.debug("Malformed interpreter version encountered: %s (%s)", version, error)
128-
return None
127+
logger.debug("Non-standard interpreter version encountered: %s (%s)", version, error)
128+
# Whilst the Python tags specify interpreter implementation
129+
# as well as version, with no standard way to parse out the
130+
# implementation, we can attempt to heuristically:
131+
try_parse_version = infer_interpreter_version(version)
132+
if try_parse_version:
133+
try:
134+
version_set &= SpecifierSet(f">={try_parse_version}")
135+
except InvalidSpecifier as error_for_retry:
136+
logger.debug("Could not parse interpreter version from: %s (%s)", version, error_for_retry)
137+
138+
logger.debug(version_set)
129139

130140
# Now to get the latest acceptable one, we can step through all interpreter
131141
# versions. For the most accurate result, we can query python.org for a
@@ -141,6 +151,31 @@ def pick_specific_version(buildspec: BaseBuildSpecDict) -> str | None:
141151
return None
142152

143153

154+
def infer_interpreter_version(tag: str) -> str | None:
155+
"""Infer interpreter version from Python-tag.
156+
157+
Parameters
158+
----------
159+
tag: Python-tag, likely inferred from wheel name.
160+
161+
162+
Returns
163+
-------
164+
str: interpreter version inferred from Python-tag
165+
"""
166+
# We will parse the interpreter version of CPython or just
167+
# whatever generic Python version is specified.
168+
pattern = re.compile(r"^(py|cp)(\d{1,3})$")
169+
parsed_tag = pattern.match(tag)
170+
if parsed_tag:
171+
digits = parsed_tag.group(2)
172+
# As match succeeded len(digits) \in {1,2,3}
173+
if len(digits) == 1:
174+
return parsed_tag.group(2)
175+
return f"{digits[0]}.{digits[1:]}"
176+
return None
177+
178+
144179
def build_backend_commands(buildspec: BaseBuildSpecDict) -> list[str]:
145180
"""Generate the installation commands for each inferred build backend.
146181

0 commit comments

Comments
 (0)