{"record":{"id":"b15eb58452cdd5af","repo":"pypa/pip","slug":"versionrange-contains-expected-str-or-version-g","errorCode":null,"errorMessage":"VersionRange.contains() expected str or Version, got {type(item).__name__}","messagePattern":"VersionRange\\.contains\\(\\) expected str or Version, got (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/ranges.py","lineNumber":1938,"sourceCode":"        opt-in region; it reads only the configured policy. This mirrors\n        :meth:`~packaging.specifiers.SpecifierSet.contains` versus\n        :meth:`~packaging.specifiers.SpecifierSet.filter`.\n\n        Unparsable strings do not match, except where the full\n        ``SpecifierSet`` would also match: the full range admits any string,\n        and a ``===`` range admits items equal to the literal\n        case-insensitively.\n\n        >>> r = SpecifierSet(\">=1.0,<2.0\").to_range()\n        >>> r.contains(\"1.5\")\n        True\n        >>> r.contains(\"2.0\")\n        False\n\n        :raises TypeError: if item is not a str or Version.\n        \"\"\"\n        if not isinstance(item, (str, Version)):\n            raise TypeError(\n                f\"VersionRange.contains() expected str or Version, \"\n                f\"got {type(item).__name__}\"\n            )\n\n        parsed: Version | None = item if isinstance(item, Version) else None\n        if installed and parsed is None:\n            parsed = coerce_version(item)\n        if installed and parsed is not None and parsed.is_prerelease:\n            prereleases = True\n\n        effective_pre = (\n            self._prereleases_configured if prereleases is None else prereleases\n        )\n\n        if self._admit or self._reject:\n            item_str = str(item).lower()\n            if item_str in self._reject:\n                return False","sourceCodeStart":1920,"sourceCodeEnd":1956,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/ranges.py#L1920-L1956","documentation":"TypeError raised by VersionRange.contains when the 'item' argument is neither a str nor a packaging.version.Version. Membership testing requires something parseable as a version (or already a Version object); other types are rejected upfront rather than coerced.","triggerScenarios":"Calling range.contains(123) (int), range.contains(1.5) (float), range.contains(None), or range.contains(some_other_object). Note: the 'in' operator (__contains__) forwards to contains with default args, so '1.5 in range_float_bug' where you passed a float also hits this.","commonSituations":"Passing a numeric version (int/float) instead of its string form; feeding a parsed Version from a different library; an unguarded user input that arrived as a non-string.","solutions":["Convert the value to a string before testing: range.contains(str(value)).","For numeric inputs, format deterministically (e.g. str(1.5) -> '1.5') or build a Version explicitly: Version('1.5').","Validate/normalize incoming version data at your API boundary so only str or Version reaches contains().","If you often have a Version already, pass it directly — that path avoids re-parsing."],"exampleFix":"# before\nr = SpecifierSet('>=1.0').to_range()\nr.contains(1.5)  # TypeError\n\n# after\nr = SpecifierSet('>=1.0').to_range()\nr.contains('1.5')\n# or\nfrom packaging.version import Version\nr.contains(Version('1.5'))","handlingStrategy":"type-guard","validationCode":"null","typeGuard":"from packaging.version import Version\n\ndef coerce_for_contains(item):\n    if isinstance(item, (str, Version)):\n        return item\n    if isinstance(item, (int, float)):\n        return str(item)\n    raise TypeError(f\"version membership needs str/Version, got {type(item).__name__}\")\n\n# then: r.contains(coerce_for_contains(value))","tryCatchPattern":"try:\n    ok = r.contains(value)\nexcept TypeError:\n    # value was not str/Version; coerce via coerce_for_contains() and retry\n    raise","preventionTips":["Normalize version inputs to str at your API boundary; never pass raw int/float to contains().","Where you already hold a Version, pass it to skip re-parsing.","Guard user-supplied version fields with isinstance(x, (str, Version)) before testing membership."],"tags":["packaging","versionrange","type-error","membership"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}