Skip to content

Commit 3a0f3c0

Browse files
committed
Support combining trivial negations in v1 recipes
Extend the v1 recipe condition combiner to detect negations of trivial conditions (`x` vs. `not x`, provided that `x` does not use `and`, `or` or `if` operators).
1 parent a80ff00 commit 3a0f3c0

File tree

2 files changed

+53
-29
lines changed

2 files changed

+53
-29
lines changed

conda_forge_tick/migrators/recipe_v1.py

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,50 @@
1212
logger = logging.getLogger(__name__)
1313

1414

15-
def is_same_condition(a: Any, b: Any) -> bool:
15+
def get_condition(node: Any) -> str | None:
16+
if isinstance(node, dict) and "if" in node:
17+
return node["if"].strip()
18+
return None
19+
20+
21+
def is_same_condition(a: str, b: str) -> bool:
22+
return a == b
23+
24+
25+
def is_single_expression(condition: str) -> bool:
26+
return not any(f" {x} " in condition for x in ("and", "or", "if"))
27+
28+
29+
def is_negated_condition(a: str, b: str) -> bool:
30+
# we only handle negating trivial expressions
31+
if not all(map(is_single_expression, (a, b))):
32+
return False
33+
34+
a_not = a.startswith("not")
35+
b_not = b.startswith("not")
1636
return (
17-
isinstance(a, dict)
18-
and isinstance(b, dict)
19-
and "if" in a
20-
and "if" in b
21-
and a["if"] == b["if"]
37+
a_not != b_not
38+
and a.removeprefix("not").lstrip() == b.removeprefix("not").lstrip()
2239
)
2340

2441

25-
def fold_branch(source: Any, dest: Any, branch: str) -> None:
42+
def fold_branch(source: Any, dest: Any, branch: str, dest_branch: str) -> None:
2643
if branch not in source:
2744
return
45+
2846
source_l = source[branch]
2947
if isinstance(source_l, str):
48+
if dest_branch not in dest:
49+
# special-case: do not expand a single string to list
50+
dest[dest_branch] = source_l
51+
return
3052
source_l = [source_l]
3153

32-
if branch not in dest:
33-
dest[branch] = []
34-
elif isinstance(dest[branch], str):
35-
dest[branch] = [dest[branch]]
36-
dest[branch].extend(source_l)
54+
if dest_branch not in dest:
55+
dest[dest_branch] = []
56+
elif isinstance(dest[dest_branch], str):
57+
dest[dest_branch] = [dest[dest_branch]]
58+
dest[dest_branch].extend(source_l)
3759

3860

3961
def combine_conditions(node: Any):
@@ -45,9 +67,18 @@ def combine_conditions(node: Any):
4567
# iterate in reverse order, so we can remove elements on the fly
4668
# start at index 1, since we can only fold to the previous node
4769
for i in reversed(range(1, len(node))):
48-
if is_same_condition(node[i], node[i - 1]):
49-
fold_branch(node[i], node[i - 1], "then")
50-
fold_branch(node[i], node[i - 1], "else")
70+
node_cond = get_condition(node[i])
71+
prev_cond = get_condition(node[i - 1])
72+
if node_cond is None or prev_cond is None:
73+
continue
74+
75+
if is_same_condition(node_cond, prev_cond):
76+
fold_branch(node[i], node[i - 1], "then", "then")
77+
fold_branch(node[i], node[i - 1], "else", "else")
78+
del node[i]
79+
elif is_negated_condition(node_cond, prev_cond):
80+
fold_branch(node[i], node[i - 1], "then", "else")
81+
fold_branch(node[i], node[i - 1], "else", "then")
5182
del node[i]
5283

5384
# then we descend down the tree

tests/test_v1_yaml/version_pytorch_correct.yaml

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ outputs:
3939
then:
4040
- python 3.12.*
4141
- numpy *
42-
- if: not megabuild
43-
then:
42+
else:
4443
- python
4544
- numpy
4645
- cross-python_${{ target_platform }}
@@ -51,8 +50,7 @@ outputs:
5150
then: ${{ compiler('cuda') }}
5251
- if: not win
5352
then: llvm-openmp
54-
- if: win
55-
then:
53+
else:
5654
- intel-openmp ${{ mkl }}
5755
- libuv
5856
- cmake
@@ -123,8 +121,7 @@ outputs:
123121
- liblapack
124122
- if: not win
125123
then: llvm-openmp
126-
- if: win
127-
then: intel-openmp ${{ mkl }}
124+
else: intel-openmp ${{ mkl }}
128125
- libabseil
129126
- libprotobuf
130127
- sleef
@@ -228,14 +225,12 @@ outputs:
228225
then:
229226
- mkl-devel ${{ mkl }}
230227
- libcblas * *_mkl
231-
else:
232-
- libcblas
228+
else: libcblas
233229
- if: "blas_impl != \"mkl\""
234230
then: liblapack
235231
- if: not win
236232
then: llvm-openmp
237-
- if: win
238-
then: intel-openmp ${{ mkl }}
233+
else: intel-openmp ${{ mkl }}
239234
- libabseil
240235
- libprotobuf
241236
- pybind11
@@ -251,12 +246,10 @@ outputs:
251246
then: ${{ pin_subpackage('libtorch', exact=True) }}
252247
# for non-megabuild, allow libtorch from any python version;
253248
# pinning build number would be nice but breaks conda
254-
- if: not megabuild
255-
then: libtorch ${{ version }}.*
249+
else: libtorch ${{ version }}.*
256250
- if: not win
257251
then: llvm-openmp
258-
- if: win
259-
then: intel-openmp ${{ mkl }}
252+
else: intel-openmp ${{ mkl }}
260253
- if: "blas_impl == \"mkl\""
261254
then: libblas * *${{ blas_impl }}
262255
- if: "blas_impl != \"mkl\""

0 commit comments

Comments
 (0)