Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/nameparser/config/suffixes.py: 95%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

19 statements  

1from nameparser.config._invariants import assert_normalized 

2 

3SUFFIX_WORDS = frozenset({ 

4 # #269: Cyrillic мл/ст (junior/senior, the jr/sr analogs) deferred 

5 # pending the within-script collision vetting the issue asks for; 

6 # 'ст' especially is a plausible false-positive risk (many two- 

7 # letter Cyrillic abbreviations exist), and both are short enough 

8 # to worry about. Not shipped in this pass. 

9 'esq', 

10 'esquire', 

11 'jr', 

12 'jnr', 

13 'junior', 

14 'sr', 

15 'snr', 

16 '2', 

17 'i', 

18 'ii', 

19 'iii', 

20 'iv', 

21 'v', 

22 # Bare, not '(ret)'/'(vet)': moved here from literal parenthesized 

23 # entries in SUFFIX_ACRONYMS. parse_nicknames()'s handle_match() now 

24 # strips parens/quotes before this set is consulted, so the bare form 

25 # is correct -- do not re-add the parenthesized form, that would 

26 # silently reintroduce the #111 bug (parenthesized "(Ret)" matching 

27 # literally instead of going through nickname/suffix disambiguation). 

28 'ret', 

29 'vet', 

30 

31 # #269 follow-up: Hebrew post-nominals, both gershayim spellings 

32 # (ASCII '"' and U+05F4); the mid-word quote is inert in 

33 # extraction, like the ד"ר title. Neither is ever a name. 

34 'ז"ל', # "of blessed memory" (deceased), ASCII quote 

35 'ז״ל', # same, U+05F4 gershayim 

36 'שליט"א', # honorific for a living rabbi, ASCII quote 

37 'שליט״א', # same, U+05F4 gershayim 

38 

39 # #307/#308: CJK postnominal honorifics and degrees. Matched 

40 # whole-token here, which reaches the SPACED forms; the GLUED 

41 # forms (田中さん, 김민준씨) are reached by the peel #308 adds to 

42 # _pipeline/_script_segment.py: it splits a listed tail off the 

43 # last name token and hands the piece back to this vocabulary -- 

44 # so every GLUED_HONORIFICS entry below is also an entry here, 

45 # asserted at the foot of this module. 

46 # Self-selecting like the Korean surnames: a Han, kana or hangul 

47 # entry can only ever match CJK text. Vetting per the мл/ст 

48 # standard above, and note the two bars are different -- an entry 

49 # can be safe in the spaced position and unsafe glued, never the 

50 # other way round, because the spaced position is a token boundary 

51 # its writer drew: 

52 # - 氏: a rare Japanese surname reading exists, but a bare 

53 # trailing 氏 after a name is the news-style honorific, and a 

54 # 氏-surnamed person writes it FIRST. 

55 # - 양/군: 양 is also a top-tier surname (Yang) -- but a surname 

56 # LEADS, so the trailing-only suffix gate never sees it there 

57 # (양 미선 keeps family 양); as single-syllable GIVEN names in 

58 # final position both are vanishingly rare in practice. 

59 # - 博士: an attested Japanese given name (ひろし, Hiroshi) -- 

60 # the doctorate reading vastly dominates the spaced trailing 

61 # position this set matches. 

62 # - 殿: some ninety Japanese surnames end in it (鵜殿, 真殿), which 

63 # is what keeps it out of GLUED_HONORIFICS below -- but the 

64 # surname-LEADS argument that clears 양/군 clears it here too. 

65 # - 님: a single hangul syllable, the risk class 양/군 are in, but 

66 # it has no hanja reading at all, so it cannot sit inside a 

67 # Sino-Korean given name. 

68 # Bare hangul 선생/교수 are deliberately absent (only the -님 

69 # honorific forms ship): the bare forms read as common nouns as 

70 # readily as address terms. 박사 is the exception among the three, 

71 # shipping bare as well as with -님, because it names a degree 

72 # rather than a role -- but all three -님 forms ship together 

73 # (선생님, 교수님, 박사님 are the standard professional honorifics, 

74 # and shipping two of the three was an oversight). Further 

75 # candidates (여사, 太太) wait on the same case-by-case argument. 

76 '씨', # ko Mr./Ms. -- standardly spaced in Korean orthography 

77 '박사', # ko doctorate holder ("Dr.") 

78 '박사님', # ko the same, honorific form 

79 '선생님', # ko teacher/respected elder 

80 '교수님', # ko professor (honorific form) 

81 '군', # ko young man ("Master") 

82 '양', # ko young woman ("Miss") 

83 '님', # ko -- the bare honorific of online/formal address 

84 '先生', # zh Mr. / ja teacher-master -- honorific in both 

85 '女士', # zh Ms./Madam 

86 '小姐', # zh Miss 

87 '博士', # zh+ja doctorate holder, shared Han 

88 '教授', # zh+ja professor, shared Han 

89 '様', # ja formal Mr./Ms. (the mail-addressing honorific) 

90 '氏', # ja news-style Mr. (田中氏) 

91 '殿', # ja formal, official/rank-flavored 

92 'さん', # ja the everyday honorific, kana 

93 'さま', # ja the kana spelling of 様 

94 'くん', # ja the kana spelling of 君 

95 'ちゃん', # ja familiar/diminutive 

96 # #346/#344/#343: spaced trailing honorifics, South Asian and 

97 # Tibetan. SPACED ONLY -- none of these is in GLUED_HONORIFICS 

98 # below, and none may be added to it. जी is the reason and the 

99 # reason generalizes: Banerjee, Mukherjee and Chatterjee are 

100 # written बनर्जी, मुखर्जी, चटर्जी, so a glued peel would strand a 

101 # fragment on a bare virama (बनर् + जी) -- the 殿 criterion in a 

102 # non-CJK script. गांधीजी therefore stays unpeeled, which is the 

103 # accepted trade (decisions.md#indic-honorifics). 

104 # NOTE the leading/trailing split across the two scripts, which is 

105 # real and must not be "harmonized": Devanagari बाबू is a LEADING 

106 # honorific -- a TITLES entry (#344), not a suffix -- while Bengali 

107 # বাবু is TRAILING (অমল বাবু). Different codepoints, so the two 

108 # entries cannot interact. 

109 'rinpoche', # bo postpositional (Sogyal Rinpoche, Lama Zopa Rinpoche) 

110 'जी', # hi/mr the universal respect particle (मोदी जी) 

111 'साहब', # hi Sahib 

112 'साहिब', # hi/pa the Sahib spelling with the i-matra 

113 'साहेब', # mr the Marathi Saheb spelling 

114 'महाराज', # hi Maharaj, trailing; the leading महाराजा is a TITLES entry (#344) 

115 'সাহেব', # bn Saheb (রহমান সাহেব) 

116 'বাবু', # bn Babu -- TRAILING; see the note above 

117 'মহারাজ', # bn Maharaj 

118}) 

119""" 

120 

121Post-nominal suffixes matched as WORDS: the lookup uses the normalized token, 

122so only EDGE periods come off and interior ones survive -- "Junior." matches 

123here, "J.u.n.o.r." does not and stays name text ("John J.u.n.o.r." parses a 

124family name, on both APIs). The example is deliberately not "J.u.n.i.o.r.", 

125which fails this lookup too and is a suffix anyway: an interior-period token 

126that no whole-token set claims goes to ``period_joined_vocab``, which splits 

127it on its periods and, no chunk being a title, calls the whole thing a 

128suffix if ANY chunk is suffix vocabulary -- and the chunk "i" is the Roman 

129numeral listed above. 

130So membership here is not the last word on a dotted token; the sentence is 

131about this set's lookup alone. :data:`SUFFIX_ACRONYMS` is the set matched 

132with every period removed, so it alone covers a multi-dot spelling: both 

133"P.H.D." and the bare "PhD" reach its `phd` entry, the two normalizing to 

134the same string once the periods come off. The two sets are asserted 

135DISJOINT (see the guard block at the bottom): a post-nominal belongs to 

136one of them or the other, and which one holds it is what decides whether 

137its multi-dot spelling reaches a whole-token lookup at all -- 

138``period_joined_vocab`` may still claim the token chunk by chunk, as the 

139"J.u.n.i.o.r." example above shows. 

140 

141""" 

142GLUED_HONORIFICS = frozenset({ 

143 # #308: the entries above that may also be peeled off the END of a 

144 # name token -- 田中さん, 山田太郎様, 김민준씨. A separate set, not 

145 # SUFFIX_WORDS reused, because the glued position has no token 

146 # boundary to lean on: the vetting question is not "is this a 

147 # name?" but "can this END a name?", and only entries that can 

148 # never end one belong here. 

149 # kana -- name-final never, in any of the four, and the kana/kanji 

150 # split is itself a vetting result: くん ships where 君 cannot, 

151 # since 王君 is a complete Chinese name while the hiragana spelling 

152 # is unavailable to Chinese at all. Scoped to Chinese on purpose -- 

153 # hiragana of course spells Japanese GIVEN names (高橋みなみ is one, 

154 # pinned in the case table), which is why the four entries above 

155 # are vetted one at a time as never name-FINAL rather than waved 

156 # through as kana. 

157 'さん', 'さま', 'くん', 'ちゃん', 

158 # Han -- two-character honorifics with no name-final reading in 

159 # either language, plus 様. 殿 is deliberately absent though it 

160 # ships spaced above; see the exclusions below. 

161 '様', '先生', '教授', '女士', '小姐', 

162 # hangul -- the -님 compounds too: longest-first peels 선생님 off 

163 # 김선생님 whole, rather than leaving 김선생 to segment into a 

164 # family 김 and a given 선생, and 박사님 off 김민준박사님 rather 

165 # than stranding 박사 in the given name. 박사 is safe glued where 

166 # its Han twin 博士 is not: that collision is Japanese (博士 = 

167 # ひろし) and the hangul spelling carries none of it. 

168 '씨', '님', '선생님', '교수님', '박사', '박사님', 

169}) 

170""" 

171 

172The subset of :data:`SUFFIX_WORDS` a name token may end WITH, peeled off as 

173its own token before segmentation (#308). Deliberately harsher than the 

174spaced set, because a glued tail has no writer-drawn token boundary to lean 

175on -- these entries are recognized in the SPACED position only: 

176 

177* 양, 군 -- 김지양 and 김지군 are given names ending in these syllables, and 

178 양 is a top-tier surname besides. 

179* 氏 -- 王氏 is a historical name form ("the Wang woman"). 

180* 博士 -- glued 田中博士 IS Tanaka Hiroshi, an attested given name. 

181* 殿 -- some ninety Japanese surnames END in it, 鵜殿 (Udono) and 真殿 

182 (Madono) with four-figure populations, so peeling it would cut a real 

183 family name in two. Spaced 殿 is safe for the reason 양/군 are: a 

184 殿-surnamed person's name LEADS, and the suffix gate is trailing-only. 

185* The Indic trailing set (जी, साहब, साहिब, साहेब, महाराज, সাহেব, বাবু, 

186 মহারাজ) and Latin rinpoche are spaced-only for the same reason 殿 is: 

187 जी ends Banerjee/Mukherjee/Chatterjee (बनर्जी, मुखर्जी, चटर्जी) and a 

188 glued peel would strand बनर् on a bare virama. The criterion is not 

189 CJK-specific, which is the point (decisions.md#indic-honorifics). 

190 

191Three more are in NEITHER set, so neither spelling is recognized. 君: 王君 is 

192a complete Chinese name (君 is a common given-name final), so the honorific 

193reading never gets the benefit of the doubt -- while its kana spelling くん 

194ships glued, above. Bare 선생 and 교수: they read as common nouns as readily 

195as address terms, and only their -님 forms ship. 

196 

197""" 

198SUFFIX_ACRONYMS_AMBIGUOUS = frozenset({ 

199 # Suffix acronyms that also commonly work as given-name nicknames on 

200 # their own (e.g. "Ed", "JD"). Two readers in 2.x, not the single v1 

201 # one this comment used to name: _extract._suffix_shaped, deciding 

202 # whether parenthesized/quoted content is a nickname or a suffix 

203 # (content matching one of these stays a nickname, the more common 

204 # reading in ambiguous, delimiter-only context), and _vocab's 

205 # suffix_as_written, which excludes the ambiguous subset from plain 

206 # acronym membership so the period gate below is not dead code. 

207 # _classify also tags membership as "vocab:suffix-ambiguous". 

208 # 

209 # When adding a new entry to SUFFIX_ACRONYMS, also add it here only if 

210 # the exact letter sequence could plausibly be someone's name on its 

211 # own -- a given name or nickname (e.g. 'jd', 'ed') or a common 

212 # surname (e.g. 'ma', 'do'). Unambiguous certifications/degrees 

213 # (e.g. 'mba', 'cpa', 'phd') don't need an entry. In 2.0 this set 

214 # also gates bare recognition: an ambiguous acronym counts as a 

215 # suffix only when written with periods ('M.A.' yes, 'Ma' no), so 

216 # 'Jack Ma' keeps its family name. 

217 # 

218 # The other half of the criterion, added 2026-09-07 with #342. 

219 # Being borne at all is only the entry ticket; what decides among 

220 # the three answers is a comparison of FREQUENCIES -- how common 

221 # the word is as a borne name in the TRAILING position against how 

222 # common it is as a credential. Roughly balanced earns the marking 

223 # here, and the parse reports the fork: 'ba' is the entry that 

224 # earned it that day, BA being a common credential and Ba a real 

225 # surname (Vietnamese; Senegalese Fula) about as common as the 

226 # credential, which is the ma/Ma shape exactly. Where the NAME 

227 # reading dominates, the entry is REMOVED from SUFFIX_ACRONYMS 

228 # instead of marked: 'rai' and 'cha' left the set that day, both 

229 # far more common as surnames than their credentials are as 

230 # credentials (RAI is "RETA Authorized Instructor", CHA is 

231 # Certified Hotel Administrator or Certified Healthcare Auditor, 

232 # both tenuous or specialized; Rai is a common surname across 

233 # Hindi- and Bengali-speaking regions and Cha the Korean 차). 

234 # Where the CREDENTIAL dominates, the entry stays unambiguous. 

235 # LENGTH is a correlate and not the test -- a short acronym is 

236 # more often a common credential AND more often a name -- so do 

237 # not read the letter counts here as a rule. 

238 # 

239 # Removal takes the DOTTED spelling with it too, except by 

240 # accident: "John Smith R.A.I." still reads suffix 'R.A.I.' only 

241 # because rules.md#S3 splits an interior-period token on its 

242 # periods and the chunk 'i' happens to be a Roman numeral in 

243 # SUFFIX_WORDS. "John Smith R.A.X." reads family, and so does 

244 # "John Smith C.H.A." after the removal. Do not count on a dotted 

245 # spelling surviving a removal. A caller who needs an entry back 

246 # adds it -- Lexicon.default().add(suffix_acronyms={"cha"}) -- 

247 # which is the answer this library gives for every 

248 # locale-specific vocabulary. The cost is stated and accepted: 

249 # with the entry gone, "John Smith RAI" reads family 'RAI'. See 

250 # decisions.md#suffix-acronym-collisions. 

251 # 

252 # NOT 'ms' or 'sa', though #296's audit table put them here for the 

253 # leading-title collision (bare "Ms" the honorific, "M.S." the 

254 # degree): the gate is position-blind and the collision is not. 

255 # Gated, 'John Smith, MS' lost its suffix-comma route and read 

256 # title 'MS', and 'Smith, Ms.' passed the gate on its one period 

257 # and read as a credential anyway. Both words are genuine duals -- 

258 # title and unambiguous suffix -- and position decides, as for 

259 # 'sr' and 'lt' (decisions.md#C1). 

260 # 

261 # NOT 'se' or 'om' either, weighed 2026-09-07 with #342 and left 

262 # alone: no surname evidence worth standing behind, and OM is the 

263 # Order of Merit. And NOT 'mc' or 'vd', which are also PARTICLES: 

264 # #454 closed by design -- neither is a borne name, so a bare 

265 # trailing one is the decoration, and rules.md#P6's Accepted 

266 # clause names them as the two words whose positional reading 

267 # does not hold. 

268 'ba', 

269 'do', 

270 'ed', 

271 'jd', 

272 'ma', 

273}) 

274""" 

275 

276Acronym suffixes from SUFFIX_ACRONYMS that also plausibly collide with a 

277common given-name nickname. Not a partition of SUFFIX_ACRONYMS -- a small, 

278standalone exception list, read by the delimited-content escape in 

279``_pipeline/_extract.py`` and by ``_pipeline/_vocab.py``'s period gate. 

280 

281""" 

282SUFFIX_ACRONYMS = frozenset({ 

283 '8-vsb', 

284 'aas', 

285 'aba', 

286 'abc', 

287 # "All But Dissertation". Also the Latin transliteration of the 

288 # Arabic bound given-name word in BOUND_GIVEN_NAMES (whose 

289 # Arabic-script counterpart عبد is in that set only) -- the one 

290 # word in both sets, kept in both deliberately. Position decides 

291 # at the two ends but not in a family comma's given slot; see 

292 # decisions.md#P5. 

293 'abd', 

294 'abpp', 

295 'abr', 

296 'aca', 

297 'acas', 

298 'ace', 

299 'acha', 

300 'acp', 

301 'ae', 

302 'ae', 

303 'aem', 

304 'afasma', 

305 'afc', 

306 'afc', 

307 'afm', 

308 'afm', 

309 'agsf', 

310 'aia', 

311 'aicp', 

312 'ala', 

313 'alc', 

314 'alp', 

315 'am', 

316 'amd', 

317 'ame', 

318 'amieee', 

319 'ams', 

320 'aphr', 

321 'apn', 

322 'aprn', 

323 'apr', 

324 'apss', 

325 'aqp', 

326 'arm', 

327 'arrc', 

328 'asa', 

329 'asc', 

330 'asid', 

331 'asla', 

332 'asp', 

333 'atc', 

334 'awb', 

335 'ba', 

336 'bca', 

337 'bcl', 

338 'bcss', 

339 'bds', 

340 'bem', 

341 'bls-i', 

342 'bn', 

343 'bpe', 

344 'bpi', 

345 'bpt', 

346 'bsc', 

347 'bt', 

348 'btcs', 

349 'bts', 

350 'cacts', 

351 'cae', 

352 'caha', 

353 'caia', 

354 'cams', 

355 'cap', 

356 'capa', 

357 'capm', 

358 'capp', 

359 'caps', 

360 'caro', 

361 'cas', 

362 'casp', 

363 'cb', 

364 'cbe', 

365 'cbm', 

366 'cbne', 

367 'cbnt', 

368 'cbp', 

369 'cbrte', 

370 'cbs', 

371 'cbsp', 

372 'cbt', 

373 'cbte', 

374 'cbv', 

375 'cca', 

376 'ccc', 

377 'ccca', 

378 'cccm', 

379 'cce', 

380 'cchp', 

381 'ccie', 

382 'ccim', 

383 'cciso', 

384 'ccm', 

385 'ccmt', 

386 'ccna', 

387 'ccnp', 

388 'ccp', 

389 'ccp-c', 

390 'ccpr', 

391 'ccs', 

392 'ccufc', 

393 'cd', 

394 'cdal', 

395 'cdfm', 

396 'cdmp', 

397 'cds', 

398 'cdt', 

399 'cea', 

400 'ceas', 

401 'cebs', 

402 'ceds', 

403 'ceh', 

404 'cela', 

405 'cem', 

406 'cep', 

407 'cera', 

408 'cet', 

409 'cfa', 

410 'cfc', 

411 'cfcc', 

412 'cfce', 

413 'cfcm', 

414 'cfe', 

415 'cfeds', 

416 'cfi', 

417 'cfm', 

418 'cfp', 

419 'cfps', 

420 'cfr', 

421 'cfre', 

422 'cga', 

423 'cgap', 

424 'cgb', 

425 'cgc', 

426 'cgfm', 

427 'cgfo', 

428 'cgm', 

429 'cgm', 

430 'cgma', 

431 'cgp', 

432 'cgr', 

433 'cgsp', 

434 'ch', 

435 'chba', 

436 'chdm', 

437 'che', 

438 'ches', 

439 'chfc', 

440 'chfc', 

441 'chi', 

442 'chmc', 

443 'chmm', 

444 'chp', 

445 'chpa', 

446 'chpe', 

447 'chpln', 

448 'chpse', 

449 'chrm', 

450 'chsc', 

451 'chse', 

452 'chse-a', 

453 'chsos', 

454 'chss', 

455 'cht', 

456 'cia', 

457 'cic', 

458 'cie', 

459 'cig', 

460 'cip', 

461 'cipm', 

462 'cips', 

463 'ciro', 

464 'cisa', 

465 'cism', 

466 'cissp', 

467 'cla', 

468 'clsd', 

469 'cltd', 

470 'clu', 

471 'cm', 

472 'cma', 

473 'cmas', 

474 'cmc', 

475 'cmfo', 

476 'cmg', 

477 'cmp', 

478 'cms', 

479 'cmsp', 

480 'cmt', 

481 'cna', 

482 'cnm', 

483 'cnp', 

484 'cp', 

485 'cp-c', 

486 'cpa', 

487 'cpacc', 

488 'cpbe', 

489 'cpcm', 

490 'cpcu', 

491 'cpe', 

492 'cpfa', 

493 'cpfo', 

494 'cpg', 

495 'cph', 

496 'cpht', 

497 'cpim', 

498 'cpl', 

499 'cplp', 

500 'cpm', 

501 'cpo', 

502 'cpp', 

503 'cppm', 

504 'cprc', 

505 'cpre', 

506 'cprp', 

507 'cpsc', 

508 'cpsi', 

509 'cpss', 

510 'cpt', 

511 'cpwa', 

512 'crde', 

513 'crisc', 

514 'crma', 

515 'crme', 

516 'crna', 

517 'cro', 

518 'crp', 

519 'crt', 

520 'crtt', 

521 'csa', 

522 'csbe', 

523 'csc', 

524 'cscp', 

525 'cscu', 

526 'csep', 

527 'csi', 

528 'csm', 

529 'csp', 

530 'cspo', 

531 'csre', 

532 'csrte', 

533 'csslp', 

534 'cssm', 

535 'cst', 

536 'cste', 

537 'ctbs', 

538 'ctfa', 

539 'cto', 

540 'ctp', 

541 'cts', 

542 'cua', 

543 'cusp', 

544 'cva', 

545 'cva[22]', 

546 'cvo', 

547 'cvp', 

548 'cvrs', 

549 'cwap', 

550 'cwb', 

551 'cwdp', 

552 'cwep', 

553 'cwna', 

554 'cwne', 

555 'cwp', 

556 'cwsp', 

557 'cxa', 

558 'cyds', 

559 'cysa', 

560 'dabfm', 

561 'dabvlm', 

562 'dacvim', 

563 'dbe', 

564 'dc', 

565 'dcb', 

566 'dcm', 

567 'dcmg', 

568 'dcvo', 

569 'dd', 

570 'dds', 

571 'ded', 

572 'dep', 

573 'dfc', 

574 'dfm', 

575 'diplac', 

576 'diplom', 

577 'djur', 

578 'dma', 

579 'dmd', 

580 'dmin', 

581 'dnp', 

582 'do', 

583 'dpm', 

584 'dpt', 

585 'drb', 

586 'drmp', 

587 'drph', 

588 'dsc', 

589 'dsm', 

590 'dso', 

591 'dss', 

592 'dtr', 

593 'dvep', 

594 'dvm', 

595 'ea', 

596 'ed', 

597 'edd', 

598 'ei', 

599 'eit', 

600 'els', 

601 'emd', 

602 'emt-b', 

603 'emt-i/85', 

604 'emt-i/99', 

605 'emt-p', 

606 'enp', 

607 'erd', 

608 'evp', 

609 'faafp', 

610 'faan', 

611 'faap', 

612 'fac-c', 

613 'facc', 

614 'facd', 

615 'facem', 

616 'facep', 

617 'facha', 

618 'facofp', 

619 'facog', 

620 'facp', 

621 'facph', 

622 'facs', 

623 'faia', 

624 'faicp', 

625 'fala', 

626 'fashp', 

627 'fasid', 

628 'fasla', 

629 'fasma', 

630 'faspen', 

631 'fca', 

632 'fcas', 

633 'fcela', 

634 'fd', 

635 'fec', 

636 'fhames', 

637 'fic', 

638 'ficf', 

639 'fieee', 

640 'fmp', 

641 'fmva', 

642 'fnss', 

643 'fp&a', 

644 'fp-c', 

645 'fpc', 

646 'frm', 

647 'fsa', 

648 'fsdp', 

649 'fws', 

650 'gaee[14]', 

651 'gba', 

652 'gbe', 

653 'gc', 

654 'gcb', 

655 'gcb', 

656 'gchs', 

657 'gcie', 

658 'gcmg', 

659 'gcmg', 

660 'gcsi', 

661 'gcvo', 

662 'gcvo', 

663 'gisp', 

664 'git', 

665 'gm', 

666 'gmb', 

667 'gmr', 

668 'gphr', 

669 'gri', 

670 'grp', 

671 'gsmieee', 

672 'hccp', 

673 'hrs', 

674 'iaccp', 

675 'iaee', 

676 'iccm-d', 

677 'iccm-f', 

678 'idsm', 

679 'ifgict', 

680 'iom', 

681 'ipep', 

682 'ipm', 

683 'iso', 

684 'issp-csp', 

685 'issp-sa', 

686 'itil', 

687 'jd', 

688 'jp', 

689 'kbe', 

690 'kcb', 

691 'kchs/dchs', 

692 'kcie', 

693 'kcie', 

694 'kcmg', 

695 'kcsi', 

696 'kcsi', 

697 'kcvo', 

698 'kg', 

699 'khs/dhs', 

700 'kp', 

701 'kt', 

702 'lac', 

703 'lcmt', 

704 'lcpc', 

705 'lcsw', 

706 'lg', 

707 'litk', 

708 'litl', 

709 'litp', 

710 'llm', 

711 'lm', 

712 'lmsw', 

713 'lmt', 

714 'lp', 

715 'lpa', 

716 'lpc', 

717 'lpn', 

718 'lpss', 

719 'lsi', 

720 'lsit', 

721 'lt', 

722 'lvn', 

723 'lvo', 

724 'lvt', 

725 'ma', 

726 'maaa', 

727 'mai', 

728 'mba', 

729 'mbe', 

730 'mbs', 

731 'mc', 

732 'mcct', 

733 'mcdba', 

734 'mches', 

735 'mcm', 

736 'mcp', 

737 'mcpd', 

738 'mcsa', 

739 'mcsd', 

740 'mcse', 

741 'mct', 

742 'md', 

743 'mda', 

744 'mdb', 

745 'mdbb', 

746 'mdep', 

747 'mdhb', 

748 'mdiv', 

749 'mdl', 

750 'mem', 

751 'meng', 

752 'mfa', 

753 'micp', 

754 'mieee', 

755 'mirm', 

756 'mle', 

757 'mls', 

758 'mlse', 

759 'mlt', 

760 'mm', 

761 'mmad', 

762 'mmas', 

763 'mnaa', 

764 'mnae', 

765 'mp', 

766 'mpa', 

767 'mph', 

768 'mpse', 

769 'mra', 

770 'ms', 

771 'msa', 

772 'msc', 

773 'mscmsm', 

774 'msm', 

775 'mt', 

776 'mts', 

777 'mvo', 

778 'nbc-his', 

779 'nbcch', 

780 'nbcch-ps', 

781 'nbcdch', 

782 'nbcdch-ps', 

783 'nbcfch', 

784 'nbcfch-ps', 

785 'nbct', 

786 'ncarb', 

787 'nccp', 

788 'ncidq', 

789 'ncps', 

790 'ncso', 

791 'ncto', 

792 'nd', 

793 'ndtr', 

794 'nmd', 

795 'np', 

796 'np[18]', 

797 'nraemt', 

798 'nremr', 

799 'nremt', 

800 'nrp', 

801 'obe', 

802 'obi', 

803 'oca', 

804 'ocm', 

805 'ocp', 

806 'od', 

807 'om', 

808 'oscp', 

809 'ot', 

810 'pa-c', 

811 'pcc', 

812 'pci', 

813 'pe', 

814 'pfmp', 

815 'pg', 

816 'pgmp', 

817 'ph', 

818 'pharmd', 

819 'phc', 

820 'phd', 

821 'phr', 

822 'phrca', 

823 'pla', 

824 'pls', 

825 'pmc', 

826 'pmi-acp', 

827 'pmp', 

828 'pp', 

829 'pps', 

830 'prm', 

831 'psm', 

832 'psp', 

833 'psyd', 

834 'pt', 

835 'pta', 

836 'qam', 

837 'qc', 

838 'qcsw', 

839 'qfsm', 

840 'qgm', 

841 'qpm', 

842 'qsd', 

843 'qsp', 

844 'ra', 

845 'rba', 

846 'rci', 

847 'rcp', 

848 'rd', 

849 'rdcs', 

850 'rdh', 

851 'rdms', 

852 'rdn', 

853 'res', 

854 'rfp', 

855 'rhca', 

856 'rid', 

857 'rls', 

858 'rmsks', 

859 'rn', 

860 'rp', 

861 'rpa', 

862 'rph', 

863 'rpl', 

864 'rrc', 

865 'rrt', 

866 'rrt-accs', 

867 'rrt-nps', 

868 'rrt-sds', 

869 'rtrp', 

870 'rvm', 

871 'rvt', 

872 'sa', 

873 'same', 

874 'sasm', 

875 'sccp', 

876 'scmp', 

877 'se', 

878 'secb', 

879 'sfp', 

880 'sgm', 

881 'shrm-cp', 

882 'shrm-scp', 

883 'si', 

884 'siie', 

885 'smieee', 

886 'sphr', 

887 'sscp', 

888 'stb', 

889 'stmieee', 

890 'tbr-ct', 

891 'td', 

892 'thd', 

893 'thm', 

894 'ud', 

895 'usa', 

896 'usaf', 

897 'usar', 

898 'uscg', 

899 'usmc', 

900 'usn', 

901 'usnr', 

902 'uxc', 

903 'uxmc', 

904 'vc', 

905 'vc', 

906 'vcp', 

907 'vd', 

908 'vrd', 

909}) 

910""" 

911 

912Post-nominal acronyms. Titles, degrees and other things people stick after their name 

913that may or may not have periods between the letters. The parser removes periods 

914when matching against these pieces. 

915 

916""" 

917 

918 

919# Guard the invariants the docstrings above promise, so a future edit that 

920# breaks them fails at import time instead of silently drifting until a test 

921# happens to catch it (same rationale as particles.py). Note `assert` is 

922# stripped under `python -O`; Lexicon re-checks the relationships at 

923# construction, which is what protects a caller's own vocabulary. 

924assert SUFFIX_ACRONYMS_AMBIGUOUS <= SUFFIX_ACRONYMS, \ 

925 "SUFFIX_ACRONYMS_AMBIGUOUS must stay a subset of SUFFIX_ACRONYMS" 

926# The two sets normalize differently -- the word test strips only edge 

927# periods, the acronym test strips all of them -- so a word in both is 

928# matched twice by two rules, and which one fired is unreadable from the 

929# outside. The single overlap was 'esq', dropped 2026-09-08 

930# (decisions.md#suffix-acronym-collisions), and the assert is what keeps 

931# a bulk import from quietly re-creating one. It guards the SHIPPED sets 

932# only: Lexicon has no matching invariant, so a caller who wants the 

933# overlap in their own vocabulary may still have it. 

934# 

935# It carries the AMBIGUOUS set's stake too, which is the sharper one: 

936# suffix_as_written ORs the word branch and the acronym branch, so an 

937# ambiguous acronym that were also a suffix word would be claimed 

938# through the word membership and bypass the period gate the ambiguous 

939# set exists to impose. The ambiguous set is a subset of the acronyms 

940# (the assert above), so this one covers it -- a separate assert of 

941# `SUFFIX_ACRONYMS_AMBIGUOUS & SUFFIX_WORDS` cannot fail while both of 

942# these hold. 

943assert not (SUFFIX_ACRONYMS & SUFFIX_WORDS), \ 

944 "a post-nominal belongs to one set or the other, never both (the " \ 

945 "two normalize differently, and the word branch would bypass an " \ 

946 "ambiguous acronym's period gate): " \ 

947 f"{sorted(SUFFIX_ACRONYMS & SUFFIX_WORDS)}" 

948# The peel splits its tail off as a TOKEN and suffix classification is 

949# what claims it downstream, so a tail that is not also a suffix word 

950# would split the name and then leave the piece sitting in it. The 

951# reverse direction is deliberately unguarded: a suffix word that is 

952# not a tail is the ordinary case, and an empty tail set is inert 

953# rather than wrong. 

954assert GLUED_HONORIFICS <= SUFFIX_WORDS, \ 

955 "GLUED_HONORIFICS must stay a subset of SUFFIX_WORDS: " \ 

956 f"{sorted(GLUED_HONORIFICS - SUFFIX_WORDS)}" 

957assert_normalized("suffix", SUFFIX_ACRONYMS | SUFFIX_WORDS) 

958 

959 

960# 1.x name, deprecated in 2.2 and removed in 3.0 (#293). The constant 

961# did not change module, so this aliases a name to one of this module's 

962# own globals: a module __getattr__ runs only once the body has finished 

963# and the module is in sys.modules, so the lookup resolves rather than 

964# recursing. 

965from typing import TYPE_CHECKING # noqa: E402 

966 

967from nameparser.config._deprecated import alias_getattr # noqa: E402 

968 

969# Declared for the type checker, served by __getattr__ at runtime -- 

970# the split is what keeps mypy checking this module's LIVE names; see 

971# the note in titles.py and alias_getattr's docstring. 

972if TYPE_CHECKING: 

973 SUFFIX_NOT_ACRONYMS: frozenset[str] 

974else: 

975 __getattr__, __dir__ = alias_getattr(__name__, { 

976 "SUFFIX_NOT_ACRONYMS": ( 

977 "nameparser.config.suffixes", "SUFFIX_WORDS"), 

978 }) 

979 

980# Star imports read __all__ and never the module __getattr__ -- see the 

981# note in prefixes.py. Live constants listed alongside the retired name 

982# for the same reason titles.py lists its own. 

983# 

984# In SOURCE order, not alphabetical: `automodule :members:` follows 

985# __all__ where a module defines one, so an alphabetical list here would 

986# silently reorder this module's entries in modules.html. The retired 

987# name goes last because autodoc does not document it, and so it has no 

988# position to preserve. Not for want of SEEING it: the module member 

989# scan walks dir(), which our __dir__ lists the retired name in, and 

990# then calls safe_getattr on it -- so an html build resolves this name 

991# and titles.py's retired one alike, emitting a real DeprecationWarning 

992# for each. (Invisible in a "build succeeded, 0 warnings" line: Sphinx 

993# warnings and Python warnings are different channels. Wrap 

994# sphinx.cmd.build.build_main in warnings.catch_warnings to see them.) 

995# What declines it is the attribute-doc scan: ModuleAnalyzer parses the 

996# SOURCE and finds no assignment statement for a name served by 

997# __getattr__, so autodoc computes is_attr=False, and at module level a 

998# member that is not an attribute and is neither a class nor a callable 

999# matches no object type at all -- no documenter is chosen and the 

1000# member is skipped. 

1001__all__ = [ 

1002 "SUFFIX_WORDS", 

1003 "GLUED_HONORIFICS", 

1004 "SUFFIX_ACRONYMS_AMBIGUOUS", 

1005 "SUFFIX_ACRONYMS", 

1006 "SUFFIX_NOT_ACRONYMS", 

1007]