Skip to content

istr is documented as a case-folded string with case-insensitive comparison, but compares as a plain str #1397

Description

@Mukller

Describe the discrepancy

The documentation for istr describes it as a case-folded string with case-insensitive comparison, but in current releases istr behaves as a plain str: equality and hashing are case-sensitive, and the case-insensitive folding only happens inside CIMultiDict internals (via the __is_istr__ / __istr_identity__ markers).

Docs (docs/multidict.rst):

  • "Create a new case-folded string object from the given object"
  • "..versionchanged:: 3.7 — istr doesn't title-case its argument anymore but uses internal lower-cased data for fast case-insensitive comparison."

Reproducer

>>> from multidict import istr, CIMultiDict
>>> istr("Key") == "key"
False
>>> hash(istr("Key")) == hash("key")
False
>>> len({istr("Header"), istr("HEADER"), "header"})
3
>>> str(istr("Content-Type"))
'Content-Type'

Case-insensitive lookup still works through the dict itself:

>>> CIMultiDict({"Content-Type": "x"})["content-type"]
'x'

Observed vs documented

Documented Actual (6.7.1)
"case-folded string object" original case preserved (str(istr('Key')) == 'Key')
"internal lower-cased data for ... comparison" no __eq__/__hash__ override; direct comparisons are plain-str

Same behavior in 6.1.0 and in the pure-Python fallback (class istr(str) carries only marker attributes), so this is long-standing rather than a recent regression.

Question

Which contract is intended going forward?

  1. If the marker-based design (folding handled by CIMultiDict only) is final, the istr section of the docs could be reworded so users don't expect istr('Key') == 'key' or set/dict deduplication by folded value.
  2. If the documented per-object semantics are still the goal, then __eq__/__hash__ would need to compare on the folded identity.

Happy to send a docs PR for option 1 if that is the intended reading.

Versions

  • multidict 6.7.1 (C extension) and pure-Python fallback: identical behavior
  • Python 3.13

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions