{"record":{"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":256,"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":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/markers.py#L238-L274","documentation":"Raised by _eval_op in markers.py:254-256 during Marker.evaluate. _operators supports only 'in','not in','<','<=','==','!=','>=','>'. Version-requiring keys (python_version, python_full_version, implementation_version, platform_release) are routed through a Specifier first, so any operator works for them. For all other keys, an operator not in the table — most notably '~=' (compatible-release) — yields UndefinedComparison (a ValueError).","triggerScenarios":"Evaluating Marker('os_name ~= \"posix\"'), Marker('sys_platform ~= \"linux\"'), or any marker using ~= against a non-version environment variable (os_name, sys_platform, platform_machine, platform_python_implementation, implementation_name, extra, etc.).","commonSituations":"Copying a PEP 440 version-specifier operator into a platform/OS marker; assuming every PEP 440 operator works on string-valued markers.","solutions":["Use '==' / '!=' (or 'in'/'not in') for non-version string markers.","Reserve '~=' for version markers: python_version, python_full_version, implementation_version, platform_release."],"exampleFix":"# before\nMarker('os_name ~= \"posix\"').evaluate()\n# after\nMarker('os_name == \"posix\"').evaluate()","handlingStrategy":"validation","validationCode":"from packaging.markers import MARKERS_REQUIRING_VERSION\n\n_ALLOWED_STRING_OPS = {\"in\", \"not in\", \"<\", \"<=\", \"==\", \"!=\", \">=\", \">\"}\n\ndef op_allowed_for_key(op_str: str, env_key: str) -> bool:\n    return env_key in MARKERS_REQUIRING_VERSION or op_str in _ALLOWED_STRING_OPS\n","typeGuard":null,"tryCatchPattern":"from packaging.markers import Marker, UndefinedComparison\n\ntry:\n    Marker(expr).evaluate()\nexcept UndefinedComparison as e:\n    # unsupported operator for this marker variable\n    ...\n","preventionTips":["Reserve '~=' for version markers (python_version, python_full_version, implementation_version, platform_release).","Use ==/!=/in/not in for string-valued markers."],"tags":["markers","pep508","pep440","packaging"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}