Skip to content

Commit 31416b3

Browse files
ruromeroclaude
andcommitted
fix(python): reverse directional operators in PEP 508 reversed marker form
When a marker comparison is written as 'value' op variable (e.g., '3.8' >= python_version), parseAtom swaps the operands but must also reverse directional operators: < ↔ >, <= ↔ >=. Symmetric operators (==, !=) remain unchanged. Resolves TC-4314 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 74fe05b commit 31416b3

2 files changed

Lines changed: 44 additions & 2 deletions

File tree

src/providers/marker_evaluator.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,13 @@ function parseAtom(expr) {
103103
let m = expr.match(/^\s*([\w.]+)\s*(~=|!=|==|>=|<=|>|<|not\s+in|in)\s*["']([^"']*)["']\s*$/)
104104
if (m) { return { variable: m[1], op: m[2].replace(/\s+/g, ' '), value: m[3] } }
105105

106-
// Reversed form: 'value' op variable
106+
// Reversed form: 'value' op variable — reverse directional operators
107107
let mReverse = expr.match(/^\s*["']([^"']*)['"]\s*(~=|!=|==|>=|<=|>|<|not\s+in|in)\s*([\w.]+)\s*$/)
108-
if (mReverse) { return { variable: mReverse[3], op: mReverse[2].replace(/\s+/g, ' '), value: mReverse[1] } }
108+
if (mReverse) {
109+
let reverseOp = { '<': '>', '>': '<', '<=': '>=', '>=': '<=' }
110+
let op = mReverse[2].replace(/\s+/g, ' ')
111+
return { variable: mReverse[3], op: reverseOp[op] || op, value: mReverse[1] }
112+
}
109113

110114
return null
111115
}

test/providers/marker_evaluator.test.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,42 @@ suite('PEP 508 marker evaluator', () => {
4444
expect(evaluateMarker("sys_platform not in 'lin'")).to.be.true
4545
})
4646
})
47+
48+
suite('reversed form — directional operator reversal', () => {
49+
test('>= is reversed to <= when operands are swapped', () => {
50+
expect(evaluateMarker("'3.8' >= python_version")).to.equal(
51+
evaluateMarker("python_version <= '3.8'")
52+
)
53+
})
54+
55+
test('< is reversed to > when operands are swapped', () => {
56+
expect(evaluateMarker("'3.8' < python_version")).to.equal(
57+
evaluateMarker("python_version > '3.8'")
58+
)
59+
})
60+
61+
test('> is reversed to < when operands are swapped', () => {
62+
expect(evaluateMarker("'3.8' > python_version")).to.equal(
63+
evaluateMarker("python_version < '3.8'")
64+
)
65+
})
66+
67+
test('<= is reversed to >= when operands are swapped', () => {
68+
expect(evaluateMarker("'3.8' <= python_version")).to.equal(
69+
evaluateMarker("python_version >= '3.8'")
70+
)
71+
})
72+
73+
test('== is unchanged when operands are swapped', () => {
74+
expect(evaluateMarker("'linux' == sys_platform")).to.equal(
75+
evaluateMarker("sys_platform == 'linux'")
76+
)
77+
})
78+
79+
test('!= is unchanged when operands are swapped', () => {
80+
expect(evaluateMarker("'win32' != sys_platform")).to.equal(
81+
evaluateMarker("sys_platform != 'win32'")
82+
)
83+
})
84+
})
4785
})

0 commit comments

Comments
 (0)