Fix LangStr hash/eq contract violation with plain str (closes #255) - #261
Open
trungminhdo4-glitch wants to merge 1 commit into
Open
Conversation
Change __hash__ to return hash(str(self)) so that LangStr instances equal to a plain str also share the same hash. This restores the Python data model invariant a == b => hash(a) == hash(b). Previously __hash__ returned hash((text, lang)) which always included the language code, while __eq__ fell back to value-only comparison when compared with a plain str (no .lang attribute). This meant LangStr instances could not be found in str-keyed dicts or sets. Two LangStrs with the same text but different languages now share a hash bucket, which is fine because __eq__ still separates them.
trungminhdo4-glitch
marked this pull request as ready for review
July 25, 2026 23:11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
LangStr.__hash__returnedhash((text, lang))but__eq__falls back tovalue-only comparison with plain
str. This violates Python'sa == b => hash(a) == hash(b)invariant: aLangStrthat compares equal to aplain
strhas a different hash, making it unfindable in str-keyed dicts/sets.Closes #255.
Reproduction
Root Cause
In
rigour/langs/text.py,__hash__always encodes the(content, lang)tuple, but
__eq__ignoreslangwhen the other operand is a plainstr.Change
This follows Option 1 from the issue: hash like a plain
str, preserving thedocumented "transparent string" behavior. Two
LangStrs that differ only inlangshare a hash bucket (acceptable - equality still separates them).Tests
Updated the existing test assertion and added 5 regression tests:
test_langstr_hash_eq_contract:a == b => hash(a) == hash(b)test_langstr_str_keyed_dict: lookup in str-keyed dict workstest_langstr_str_keyed_set: set deduplication workstest_langstr_lang_differing: same text, different lang share hashtest_langstr_none_lang:Nonelang satisfies contractAll 6 lang tests pass.
Compatibility
__hash__still returnsint__eq__unchangedLangStr == LangStr(lang-sensitive)Risks
Very low. The fix aligns
__hash__with the documented promise that LangStr"behaves like a regular string." Hash collisions between LangStrs with different
languages are benign.
Out of Scope