{"id":"80850c1101a5d096","repo":"pypa/pip","slug":"undefined-op-r-on-lhs-r-and-rhs-r","errorCode":null,"errorMessage":"Undefined {op!r} on {lhs!r} and {rhs!r}.","messagePattern":"Undefined (.+?) on (.+?) and (.+?)\\.","errorType":"exception","errorClass":"UndefinedComparison","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/markers.py","lineNumber":228,"sourceCode":"    \"!=\": operator.ne,\n    \">=\": operator.eq,\n    \">\": lambda _lhs, _rhs: False,\n}\n\n\ndef _eval_op(lhs: str, op: Op, rhs: str | AbstractSet[str], *, key: str) -> bool:\n    op_str = op.serialize()\n    if key in MARKERS_REQUIRING_VERSION:\n        try:\n            spec = Specifier(f\"{op_str}{rhs}\")\n        except InvalidSpecifier:\n            pass\n        else:\n            return spec.contains(lhs, prereleases=True)\n\n    oper: Operator | None = _operators.get(op_str)\n    if oper is None:\n        raise UndefinedComparison(f\"Undefined {op!r} on {lhs!r} and {rhs!r}.\")\n\n    return oper(lhs, rhs)\n\n\ndef _normalize(\n    lhs: str, rhs: str | AbstractSet[str], key: str\n) -> tuple[str, str | AbstractSet[str]]:\n    # PEP 685 - Comparison of extra names for optional distribution dependencies\n    # https://peps.python.org/pep-0685/\n    # > When comparing extra names, tools MUST normalize the names being\n    # > compared using the semantics outlined in PEP 503 for names\n    if key == \"extra\":\n        assert isinstance(rhs, str), \"extra value must be a string\"\n        # Both sides are normalized at this point already\n        return (lhs, rhs)\n    if key in MARKERS_ALLOWING_SET:\n        if isinstance(rhs, str):  # pragma: no cover\n            return (canonicalize_name(lhs), canonicalize_name(rhs))","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/packaging/markers.py#L210-L246","documentation":"Raised as UndefinedComparison by packaging.markers._eval_op when a marker operator string is not present in the _operators table. The table only contains 'in', 'not in', '<', '<=', '==', '!=', '>=', '>'. Version-comparison keys (python_version etc.) are handled earlier via Specifier, so this error fires for non-version markers combined with an operator packaging does not support (e.g. '===', '~=', '<=>').","triggerScenarios":"A Marker expression such as Marker(\\\"platform_system === 'Linux'\\\"), Marker(\\\"extra ~= 'foo'\\\"), or any marker using '===' or '~=' on a non-version field. Also reachable if a custom/abstract Op with an unknown serialize() value is constructed programmatically and passed into the evaluator.","commonSituations":"Hand-written marker strings authored against PEP 508 examples that include arbitrary operators; copy-pasting a specifier-style operator into a non-version marker; building markers from user input without whitelisting operators.","solutions":["Use only the supported operators for non-version markers: '==', '!=', 'in', 'not in', '<', '<=', '>', '>='.","Move version-style comparisons onto a version marker key (e.g. python_version) so they go through Specifier instead.","Sanitize/validate the marker string against an allow-list of operators before constructing Marker().","Catch UndefinedComparison and surface a clear error pointing at the offending operator."],"exampleFix":"# before\nMarker(\\\"platform_system === 'Linux'\\\")\n# after\nMarker(\\\"platform_system == 'Linux'\\\")","handlingStrategy":"validation","validationCode":"ALLOWED_OPS = {'==', '!=', 'in', 'not in', '<', '<=', '>', '>='}\nimport re\ndef is_safe_marker(expr: str) -> bool:\n    # crude: extract operator tokens\n    for tok in re.findall(r'\\b(?:not in|in|===|==|!=|<=|>=|<|>|~=)\\b', expr):\n        if tok not in ALLOWED_OPS:\n            return False\n    return True","typeGuard":null,"tryCatchPattern":"from packaging.markers import UndefinedComparison\ntry:\n    Marker(expr).evaluate()\nexcept UndefinedComparison as e:\n    log.warning('unsupported marker op: %s', e)","preventionTips":["Restrict marker operators to the allow-list ==, !=, in, not in, <, <=, >, >=.","Keep version-style operators (~=, ===) on version marker keys only.","Validate user-supplied marker strings before constructing Marker.","Catch UndefinedComparison to degrade gracefully."],"tags":["markers","pep508","validation"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}