Skip to content

Commit 9da9645

Browse files
committed
Normalize str values in SetManager.__contains__
After #238's follow-ups, SetManager normalizes (lowercase, strip leading/ trailing periods) in add(), remove(), the constructor, and every set operator -- __contains__ was the lone holdout, so 'Dr.' in c.titles returned False even though every other operation on the same value succeeded. The ABC comparison mixins (__le__, __eq__, etc.) route through __contains__, so this also fixes subset/equality checks against un-normalized operands. lc() is idempotent, so the parser's own lc()-normalized lookups are unaffected. Fixes #244
1 parent 69bc98c commit 9da9645

2 files changed

Lines changed: 25 additions & 0 deletions

File tree

nameparser/config/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,14 @@ def __iter__(self) -> Iterator[str]:
145145
return iter(self.elements)
146146

147147
def __contains__(self, value: object) -> bool:
148+
# add()/remove()/the constructor/the operators all normalize (lowercase,
149+
# strip leading/trailing periods) before comparing; without the same
150+
# normalization here, `'Dr.' in c.titles` returns False even though
151+
# every other operation on the same value succeeds (#244). lc() is
152+
# idempotent, so this doesn't change the parser's own lc()-normalized
153+
# lookups (e.g. `piece.lower() in self.C.conjunctions`).
154+
if isinstance(value, str):
155+
value = lc(value)
148156
return value in self.elements
149157

150158
def __len__(self) -> int:

tests/test_constants.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,23 @@ def test_set_manager_operators_normalize_like_add(self) -> None:
124124
# pins __rxor__ separately in case it ever stops aliasing __xor__
125125
self.assertEqual((['Dr.', 'Esq.'] ^ sm).elements, {'mr', 'esq'})
126126

127+
def test_set_manager_contains_normalizes_like_add(self) -> None:
128+
# add()/remove()/the constructor/the operators all normalize (lowercase,
129+
# strip leading/trailing periods) -- __contains__ was the lone holdout,
130+
# so `'Dr.' in c.titles` returned False even though every other
131+
# operation on the same value succeeded (#244)
132+
sm = SetManager(['dr', 'mr'])
133+
self.assertIn('Dr.', sm)
134+
self.assertIn('MR', sm)
135+
self.assertNotIn('Esq.', sm)
136+
137+
def test_set_manager_le_uses_normalizing_contains(self) -> None:
138+
# the ABC comparison mixins route through __contains__, so an
139+
# un-normalized membership check would leak into subset/equality too
140+
sm = SetManager(['dr', 'mr'])
141+
self.assertTrue(sm <= SetManager(['dr', 'mr', 'esq']))
142+
self.assertTrue({'Dr.', 'Mr.'} <= sm)
143+
127144
def test_set_manager_rsub_is_order_sensitive(self) -> None:
128145
# __sub__ and __rsub__ are hand-written separately (subtraction
129146
# isn't commutative, unlike |/&/^), so a copy-paste operand swap

0 commit comments

Comments
 (0)