Skip to content

Commit c95462f

Browse files
committed
Simplify SetManager/TupleManager guards from cleanup review
- Extract the bare-str/bytes top-level argument check shared by SetManager._normalized_elements and TupleManager.__init__ into a module- level _reject_bare_str_or_bytes() helper, removing the duplicated checks the #242 fix had copy-pasted from SetManager's #238 guard. - Short-circuit SetManager.__contains__ to try the raw value first: the parser's own lookups already pass an lc()-normalized value (the hot path during parsing), so this avoids an lc() call on every membership test in the common case, only paying for it on a miss.
1 parent b554820 commit c95462f

1 file changed

Lines changed: 25 additions & 29 deletions

File tree

nameparser/config/__init__.py

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,22 @@
5151
DEFAULT_ENCODING = 'UTF-8'
5252

5353

54+
def _reject_bare_str_or_bytes(value: object, expected: str) -> None:
55+
# A bare string is an iterable of its characters, so e.g. set('dr') or
56+
# dict('ab') would silently shred it, and bytes iterates to ints, which
57+
# can never match parsed str tokens -- shared by SetManager's constructor/
58+
# operands (#238) and TupleManager's constructor (#242).
59+
if isinstance(value, bytes):
60+
raise TypeError(
61+
f"expected {expected}, got a single bytes; "
62+
f"decode it first: [{value!r}.decode()]"
63+
)
64+
if isinstance(value, str):
65+
raise TypeError(
66+
f"expected {expected}, got a single str; wrap it in a list: [{value!r}]"
67+
)
68+
69+
5470
class SetManager(Set):
5571
'''
5672
Easily add and remove config variables per module or instance. Subclass of
@@ -78,19 +94,7 @@ def _normalized_elements(cls, elements: Iterable[str]) -> set[str]:
7894
# construction from re-checking ~1,400 entries per step
7995
if isinstance(elements, SetManager):
8096
return set(elements.elements)
81-
# a bare string is an iterable of its characters, so set(value)
82-
# would silently build a set of single characters — and bytes
83-
# iterates to ints, which can never match parsed tokens (#238)
84-
if isinstance(elements, bytes):
85-
raise TypeError(
86-
"expected an iterable of strings, got a single bytes; "
87-
f"decode it first: [{elements!r}.decode()]"
88-
)
89-
if isinstance(elements, str):
90-
raise TypeError(
91-
"expected an iterable of strings, got a single str; "
92-
f"wrap it in a list: [{elements!r}]"
93-
)
97+
_reject_bare_str_or_bytes(elements, "an iterable of strings")
9498
# apply the same lc() normalization (lowercase, strip leading/
9599
# trailing periods) that add() applies, and reject junk elements:
96100
# lc() on bytes or int crashes without naming the culprit, and
@@ -148,12 +152,13 @@ def __contains__(self, value: object) -> bool:
148152
# add()/remove()/the constructor/the operators all normalize (lowercase,
149153
# strip leading/trailing periods) before comparing; without the same
150154
# 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)
156-
return value in self.elements
155+
# every other operation on the same value succeeds (#244). The parser's
156+
# own lookups (e.g. `piece.lower() in self.C.conjunctions`) already pass
157+
# an lc()-normalized value, which is the hot path during parsing, so
158+
# try the raw value first and only pay for lc() on a miss.
159+
if value in self.elements:
160+
return True
161+
return isinstance(value, str) and lc(value) in self.elements
157162

158163
def __len__(self) -> int:
159164
return len(self.elements)
@@ -296,16 +301,7 @@ def __init__(
296301
# pair, silently shredding it -- mirrors SetManager's guard against
297302
# the same class of mistake (#238), applied to the mapping
298303
# constructor's own failure modes (#242).
299-
if isinstance(arg, bytes):
300-
raise TypeError(
301-
"expected a mapping or iterable of (key, value) pairs, got a "
302-
f"single bytes; decode it first: [{arg!r}.decode()]"
303-
)
304-
if isinstance(arg, str):
305-
raise TypeError(
306-
"expected a mapping or iterable of (key, value) pairs, got a "
307-
f"single str; wrap it in a list: [{arg!r}]"
308-
)
304+
_reject_bare_str_or_bytes(arg, "a mapping or iterable of (key, value) pairs")
309305
if not isinstance(arg, Mapping):
310306
checked = []
311307
for item in arg:

0 commit comments

Comments
 (0)