Commit 605e31f
Fix bare string to SetManager silently shredding into characters (#240)
* Fix bare string to SetManager silently shredding into characters
Every set-backed Constants argument is annotated Iterable[str], which a
bare str satisfies — as an iterable of its characters. SetManager's
set(elements) then silently replaced the whole default set with single
characters (Constants(titles='dr') -> titles == {'d', 'r'}), producing
wrong parses with no error anywhere. The type system cannot catch this,
so it must be a runtime check.
Reject str and bytes in SetManager.__init__ with a TypeError that
includes the wrap-it-in-a-list fix, covering all nine set-backed
Constants arguments and direct SetManager construction in one place.
All internal call sites pass module-level tuples/sets, and pickling
restores SetManager by state without re-running __init__, so only the
buggy input path is affected.
Closes #238
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Review follow-ups: guard set operators and fix the bytes hint
The Set mixin's __or__/__and__ hand _from_iterable a generator, so the
new __init__ guard never saw a bare-string operand: c.titles |= 'esq'
still silently added 'e', 's', 'q' as titles, while __sub__/__xor__
raised only by accident of constructing from the operand. Route all
four operators (and reflections) through a shared bare-string check.
typeshed declares narrower AbstractSet operands and omits the runtime
ABC's __rsub__, hence the targeted type ignores.
The bytes branch of the error hint was itself a trap: following "wrap
it in a list: [b'dr']" produces a set whose bytes elements never match
parsed str tokens — another silent failure. bytes now get "decode it
first: [b'dr'.decode()]".
Also correct the SetManager class docstring's "Only special
functionality" claim (the guards are user-visible API now) and drop
add()'s false "Can pass a list of strings" (that raises
AttributeError).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Normalize SetManager operator operands like add() does
The Set mixin operators build results from raw operand elements and
test them against the stored (normalized) ones, so even with the
bare-string guard, (titles | ['Esq.']) kept a raw 'Esq.' the parser's
lc() lookups could never match, and (titles & ['Dr.']) missed 'dr' —
the same silently-broken-config family as #238, one step from the
guarded case. Operands now pass through lc() in the existing operator
overrides, so operator results obey the same normalized-elements
invariant as add()-built sets. Idempotent for already-normalized
operands, so the internal suffixes_prefixes_titles union is unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Normalize SetManager constructor elements and validate element types
Review follow-up on the operand-normalization commit: the constructor
still stored raw elements, which that commit turned from a latent
inconsistency into active misfires — SetManager(['Dr.']) & ['Dr.']
returned empty and - silently no-opped against the exact spelling
stored in the set, Constants(titles=[...]) remained the last
silently-dead config path (raw 'Chemistry' can never match the
parser's lc() lookups), and titles vs the suffixes_prefixes_titles
union disagreed about the same token.
Fold the operand helper into _normalized_elements, shared by __init__
and all operators, so every entry is stored in the form lookups expect.
Junk elements now raise a curated TypeError instead of failing
cryptically inside lc() (bytes, int) or being silently transmuted
(None became '').
Also scope the docstring claim that operators normalize "the same way
add() does" (add() additionally decodes bytes), correct "strips
periods" to leading/trailing in prose, and pin __rxor__ separately.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Simplify and speed up SetManager normalization paths
The operand-normalization work left a compounding perf regression:
operators delegated to the ABC mixins, whose _from_iterable re-entered
__init__ and re-validated the entire result element by element, and
Constants() re-checked ~1,400 already-normalized default constants on
every construction. Measured on the headline per-instance-config path,
HumanName(constants=None) was 6.8x slower than master (101 -> 690us)
and Constants() 21.6x slower (7.5 -> 162us).
Build operator results with plain set ops on already-normalized
elements via a private _from_normalized constructor (also dropping the
super()-delegation type ignores), short-circuit SetManager operands in
_normalized_elements (a SetManager is normalized by construction), and
validate the nine default config sets once at import, copied on
identity-checked defaults. Back to master parity: Constants() 8.2us,
HumanName(constants=None) 58us.
Also fold the single-caller _reject_bare_string into
_normalized_elements, note the deliberate bytes-policy divergence from
add_with_encoding(), merge two tests that pinned the same |= parse
end-to-end, trim the triple-stated operator rationale, and update the
stale AGENTS.md SetManager normalization note.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Review follow-ups: document mutation caveat, harden _from_normalized docs, add tests
- Document that the _DEFAULT_* identity fast path in Constants.__init__
snapshots module constants at import time, so mutating a raw constant
(e.g. TITLES.add(...)) after import isn't picked up by later
Constants() instances — only the documented CONSTANTS.titles.add(...)
path is supported.
- Strengthen _from_normalized's docstring to state explicitly that it
performs no validation and callers must pre-normalize elements.
- Add tests pinning __rsub__'s operand order, confirming Constants()
instances don't alias mutable state via the identity fast path, and
confirming an equal-but-not-identical titles list still validates
fully instead of false-matching the fast path.
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>1 parent 192d35e commit 605e31f
4 files changed
Lines changed: 250 additions & 16 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
85 | 85 | | |
86 | 86 | | |
87 | 87 | | |
88 | | - | |
| 88 | + | |
89 | 89 | | |
90 | 90 | | |
91 | 91 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
| 5 | + | |
4 | 6 | | |
5 | 7 | | |
6 | 8 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
56 | 56 | | |
57 | 57 | | |
58 | 58 | | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
63 | 68 | | |
64 | 69 | | |
65 | 70 | | |
66 | 71 | | |
67 | 72 | | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
68 | 128 | | |
69 | | - | |
| 129 | + | |
70 | 130 | | |
71 | 131 | | |
72 | 132 | | |
| |||
90 | 150 | | |
91 | 151 | | |
92 | 152 | | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
93 | 183 | | |
94 | 184 | | |
95 | 185 | | |
| |||
111 | 201 | | |
112 | 202 | | |
113 | 203 | | |
114 | | - | |
| 204 | + | |
115 | 205 | | |
116 | 206 | | |
117 | 207 | | |
| |||
153 | 243 | | |
154 | 244 | | |
155 | 245 | | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
156 | 271 | | |
157 | 272 | | |
158 | 273 | | |
| |||
487 | 602 | | |
488 | 603 | | |
489 | 604 | | |
490 | | - | |
491 | | - | |
492 | | - | |
493 | | - | |
494 | | - | |
495 | | - | |
496 | | - | |
497 | | - | |
498 | | - | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
499 | 617 | | |
500 | 618 | | |
501 | 619 | | |
| |||
0 commit comments