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

Add python (et al.) to used variants even if only referenced via special selectors #5139

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
94 changes: 54 additions & 40 deletions conda_build/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,59 @@ def remove_constructor(cls, tag):
used_vars_cache = {}


def _get_package_dependent_selectors(config: Config) -> dict[str, bool]:
"""Get package-dependent selectors got get_selectors below.

Derives selectors from the config and variants to be injected
into the Jinja environment prior to templating.

Args:
config (Config): The config object

Returns:
dict[str, bool]: Dictionary of selectors for Jinja
"""
defaults = variants.get_default_variant(config)
py = config.variant.get("python", defaults["python"])
# there are times when python comes in as a tuple
if not hasattr(py, "split"):
py = py[0]
# go from "3.6 *_cython" -> "36"
# or from "3.6.9" -> "36"
py_major, py_minor, *_ = py.split(" ")[0].split(".")
py = int(f"{py_major}{py_minor}")
d = dict(
py=py,
py3k=bool(py_major == "3"),
py2k=bool(py_major == "2"),
py26=bool(py == 26),
py27=bool(py == 27),
py33=bool(py == 33),
py34=bool(py == 34),
py35=bool(py == 35),
py36=bool(py == 36),
)

np = config.variant.get("numpy")
if not np:
np = defaults["numpy"]
if config.verbose:
utils.get_logger(__name__).warn(
"No numpy version specified in conda_build_config.yaml. "
"Falling back to default numpy value of {}".format(defaults["numpy"])
)
d["np"] = int("".join(np.split(".")[:2]))

pl = config.variant.get("perl", defaults["perl"])
d["pl"] = pl

lua = config.variant.get("lua", defaults["lua"])
d["lua"] = lua
d["luajit"] = bool(lua[0] == "2")

return d


def get_selectors(config: Config) -> dict[str, bool]:
"""Aggregates selectors for use in recipe templating.

Expand Down Expand Up @@ -152,48 +205,9 @@ def get_selectors(config: Config) -> dict[str, bool]:
if arch == "32":
d["x86"] = plat.endswith(("-32", "-64"))

defaults = variants.get_default_variant(config)
py = config.variant.get("python", defaults["python"])
# there are times when python comes in as a tuple
if not hasattr(py, "split"):
py = py[0]
# go from "3.6 *_cython" -> "36"
# or from "3.6.9" -> "36"
py_major, py_minor, *_ = py.split(" ")[0].split(".")
py = int(f"{py_major}{py_minor}")

d["build_platform"] = config.build_subdir

d.update(
dict(
py=py,
py3k=bool(py_major == "3"),
py2k=bool(py_major == "2"),
py26=bool(py == 26),
py27=bool(py == 27),
py33=bool(py == 33),
py34=bool(py == 34),
py35=bool(py == 35),
py36=bool(py == 36),
)
)

np = config.variant.get("numpy")
if not np:
np = defaults["numpy"]
if config.verbose:
utils.get_logger(__name__).warn(
"No numpy version specified in conda_build_config.yaml. "
"Falling back to default numpy value of {}".format(defaults["numpy"])
)
d["np"] = int("".join(np.split(".")[:2]))

pl = config.variant.get("perl", defaults["perl"])
d["pl"] = pl

lua = config.variant.get("lua", defaults["lua"])
d["lua"] = lua
d["luajit"] = bool(lua[0] == "2")
d.update(_get_package_dependent_selectors(config))

for feature, value in feature_list:
d[feature] = value
Expand Down
25 changes: 23 additions & 2 deletions conda_build/variants.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@
"R": "r_base",
}

PACKAGE_SELECTOR_MAP = {
"python": ("py", "py3k", "py2k", "py26", "py27", "py33", "py34", "py35", "py36"),
"numpy": ("np",),
"perl": ("pl",),
"lua": ("lua", "luajit"),
}


@lru_cache(maxsize=None)
def _get_default_compilers(platform, py_ver):
Expand Down Expand Up @@ -739,14 +746,28 @@ def find_used_variables_in_text(variant, recipe_text, selectors_only=False):
]
else:
variant_lines = [
line for line in recipe_lines if v in line.replace("-", "_")
line
for line in recipe_lines
if v in line.replace("-", "_")
or any(v_sel in line for v_sel in PACKAGE_SELECTOR_MAP.get(v, ()))
]
if not variant_lines:
continue

v_regex = re.escape(v)
# Recognize package-dependent selectors: e.g., "py>2" marks "python" as used.
v_sel_regex = "|".join(
(
v_regex,
*(re.escape(sel) for sel in PACKAGE_SELECTOR_MAP.get(v, ())),
)
)
v_req_regex = "[-_]".join(map(re.escape, v.split("_")))

variant_regex = r"\{\s*(?:pin_[a-z]+\(\s*?['\"])?%s[^'\"]*?\}\}" % v_regex
selector_regex = r"^[^#\[]*?\#?\s\[[^\]]*?(?<![_\w\d])%s[=\s<>!\]]" % v_regex
selector_regex = (
r"^[^#\[]*?\#?\s\[[^\]]*?(?<![_\w\d])(%s)[=\s<>!\]]" % v_sel_regex
)
conditional_regex = (
r"(?:^|[^\{])\{%\s*(?:el)?if\s*.*" + v_regex + r"\s*(?:[^%]*?)?%\}"
)
Expand Down
19 changes: 19 additions & 0 deletions news/5139-special-selector-variants
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
### Enhancements

* Add python (et al.) to used variants even if only referenced via special selectors. (#5139)

### Bug fixes

* <news item>

### Deprecations

* <news item>

### Docs

* <news item>

### Other

* <news item>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package:
name: test_variant_implicit_via_special_selector
version: 1.0

build:
# "python" variant implicitly injected via "py" selector, but not explicit.
skip: true # [py<33]

outputs: # [with_outputs]
- name: test_variant_implicit_via_special_selector_subpackage # [with_outputs]
requirements: # [with_outputs]
host: # [with_outputs]
- python # [with_outputs]
8 changes: 6 additions & 2 deletions tests/test_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import os
import subprocess
import sys
from itertools import chain

import pytest
from conda import __version__ as conda_version
Expand All @@ -24,7 +25,7 @@
yamlize,
)
from conda_build.utils import DEFAULT_SUBDIRS
from conda_build.variants import DEFAULT_VARIANTS
from conda_build.variants import DEFAULT_VARIANTS, PACKAGE_SELECTOR_MAP

from .utils import metadata_dir, metadata_path, thisdir

Expand Down Expand Up @@ -437,7 +438,10 @@ def test_get_selectors(
monkeypatch.setenv("FEATURE_NOMKL", str(nomkl))

config = Config(host_subdir=subdir)
assert get_selectors(config) == {
selectors = get_selectors(config)
for selector in chain(*PACKAGE_SELECTOR_MAP.values()):
assert selector in selectors
assert selectors == {
# defaults
"build_platform": context.subdir,
"lua": DEFAULT_VARIANTS["lua"],
Expand Down
14 changes: 14 additions & 0 deletions tests/test_variants.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from conda_build import api, exceptions
from conda_build.utils import ensure_list, package_has_file
from conda_build.variants import (
PACKAGE_SELECTOR_MAP,
combine_specs,
dict_of_lists_to_list_of_dicts,
filter_combined_spec_to_used_keys,
Expand Down Expand Up @@ -660,6 +661,19 @@ def test_variant_subkeys_retained():
get_all_replacements(outputs[0][1].config.variant)


@pytest.mark.parametrize("with_outputs", [False, True])
def test_variant_implicit_via_special_selector(with_outputs):
ms = api.render(
os.path.join(variants_dir, "32_variant_implicit_via_special_selector"),
variants={"python": ["2.7", "3.3", "3.4"], "with_outputs": [with_outputs]},
finalize=False,
bypass_env_check=True,
)
assert sorted(m.config.variant["python"] for m, _, _ in ms) == ["3.3", "3.4"]
for m, _, _ in ms:
assert m.get_used_vars().intersection(PACKAGE_SELECTOR_MAP) == {"python"}


@pytest.mark.parametrize(
"internal_defaults, low_prio_config, high_prio_config, expected",
[
Expand Down
Loading