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

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

8 statements  

1from nameparser.config._invariants import assert_normalized 

2from nameparser.config.bound_given_names import BOUND_GIVEN_NAMES 

3 

4#: The sub-set of :py:data:`PARTICLES` that no culture uses as a standalone 

5#: given name. Where one of these stands ALONE as the piece opening a name, that 

6#: name has no given name -- the whole thing is a surname (e.g. "de Mesnil" 

7#: -> family name "de Mesnil") -- and that reading holds under EVERY 

8#: ``name_order`` (#359). It is not scoped to the default order the way the 

9#: rest of the positional read is: what settles it is that a particle 

10#: opening the name has the rest of the name to JOIN FORWARD TO, which 

11#: is evidence about the writing rather than about the word, so 

12#: ``Policy(name_order=FAMILY_FIRST)`` reads "de Mesnil" as the family 

13#: name too. Membership here does NOT mean the word can never be a 

14#: given name -- where position forces the reading it is one, and every 

15#: member of this set reports given "de"-style for a bare parse. What is asked about is the 

16#: opening *piece*, not the first word of the string: a particle that has 

17#: already chained onto the word behind it is part of that piece rather 

18#: than standing alone. 

19#: OPENING the name is the whole of that rule's subject (rules.md#P1; 

20#: the pre-2.2 docstrings called it rule 1b). A member standing alone 

21#: where a declared order puts the GIVEN name was a second site until 

22#: #467, and is not one now: that slot holds what the caller declared, 

23#: so ``Policy(name_order=FAMILY_FIRST)`` reads "Mesnil de" as family 

24#: "Mesnil", given "de". A member ENDING the name under a family-first 

25#: order is rules.md#P6's, which joins it to the family. A bare "de" stays as it is. Where a chain is 

26#: reported as the given name anyway it is because the member is no 

27#: longer standing alone: under ``Policy(name_order=FAMILY_FIRST)`` the 

28#: given position of "Juan de la Vega" holds the whole three-token 

29#: chain, so the rule declines and given "de la Vega" stands. A title 

30#: in front is NOT such a case -- since #367 a title is transparent to 

31#: the leading-particle exception, so "Sir de Mesnil" leaves "de" a lone 

32#: piece and reads family "de Mesnil", exactly as the untitled form does. 

33#: Membership also decides the ambiguity report -- see 

34#: :py:data:`PARTICLES` below. 

35#: Curated to exclude anything that can be a given name in some culture 

36#: (`al`, `van`, `von`, `della`, `di`, `del`, `da`, `vander`, ...) and 

37#: anything that is also a bound given-name particle (`abu`). When unsure, 

38#: leave a word out: a missing member just means that name is not 

39#: auto-fixed, whereas a wrong member misparses a real person. Must stay a 

40#: subset of :py:data:`PARTICLES` and disjoint from 

41#: :py:data:`~nameparser.config.bound_given_names.BOUND_GIVEN_NAMES`. 

42NON_GIVEN_NAME_PARTICLES = frozenset({ 

43 # Latin-script members. Every entry here is a grammatical particle -- 

44 # an article, a preposition, or a patronymic marker -- and that is the 

45 # form a never-given record has to take. One attested bearer settles 

46 # the AMBIGUOUS side; no amount of searching settles absence, so what 

47 # is recorded is what the word IS rather than a failed search. 

48 # 

49 # The test is POSITIONAL (decisions.md#vocabulary-collisions C-i, 

50 # corrected 2026-08-17): membership asks whether the word is borne as 

51 # a name in the position this rule ACTS on -- leading, or alone in the 

52 # given role -- not whether a bearer exists anywhere. 'de' is why the 

53 # qualifier is needed: "De" is a borne Bengali/Odia surname, but a 

54 # TRAILING one, so it never meets the rule. 

55 # 

56 # A BARE entry, here or in PARTICLES below, means nobody has examined 

57 # it. Absence of a comment is the audit record. 

58 "'t", # Dutch contraction of 'het' ("'t Hooft"): the article itself 

59 'af', # Danish/Norwegian nobiliary "of" 

60 'auf', # German "upon" ("auf der Heide") 

61 'av', # Swedish/Norwegian "of" 

62 'bint', # Arabic "daughter of"; native-script بنت below 

63 'das', # Portuguese/German "of the", fem. pl. Added #360: its 

64 # own plural partner 'dos' was already here. "Das" IS a 

65 # common Bengali surname, but a TRAILING one -- measured, 

66 # "Anjali Das" and "Bimal Das" are untouched, while 

67 # "Maria das Neves" gains the particle it loses today. 

68 'de', # French/Iberian/Italian "of". "De" IS a borne Bengali and 

69 # Odia surname -- trailing, so it never reaches this rule. 

70 # The case that forced C-i's positional qualifier. 

71 "de'", # Italian elided "dei" ("de' Medici") 

72 'degli', # Italian "of the", masc. pl. 

73 'dei', # Italian "of the", masc. pl. 

74 'delle', # Italian "of the", fem. pl. 

75 'delli', # Italian "of the", regional variant of 'dei' 

76 'dello', # Italian "of the", masc. sg. 

77 'dem', # German dative article 

78 'der', # German article ("von der Leyen") 

79 'dos', # Portuguese "of the", masc. pl. 

80 'het', # Dutch definite article 

81 'ibn', # Arabic "son of"; native-script ابن below 

82 'las', # Spanish "the", fem. pl. Added #360: 'la' was here as 

83 # AMBIGUOUS (La Shawn, La Toya) but the plurals are not 

84 # name elements -- no bearer in any position. 

85 'los', # Spanish "the", masc. pl. Same as 'las' -- and the pair 

86 # is why the #390 fold narrowing regressed 

87 # "de los Santos" to given='Santos', family='de los'. 

88 'mc', # Contraction of 'Mac'; unlike 'mac' (Mac Miller) it is 

89 # not a standalone given name. Measured misparse: "Mc 

90 # Donald" gave given='Mc'. Also in SUFFIX_ACRONYMS as 

91 # Master of Ceremonies, but that is trailing position and 

92 # unaffected -- "John Smith MC" still reads suffix (#360). 

93 'op', # Dutch "at/on" ("op den Berg") 

94 'ste', # Contraction of 'Sainte'; has a vowel, which is why the 

95 # criterion is "abbreviation of a word that is never 

96 # itself a name" rather than the vowel shape #360 first 

97 # proposed. Measured misparse: "Ste Marie" gave 

98 # given='Ste'. 

99 'ter', # Dutch "at the" ("ter Horst") 

100 'vd', # Dutch abbreviation of "van der". Also the British 

101 # Volunteer Decoration, a suffix acronym: two non-name 

102 # readings, so C-ii decides it on frequency and the Dutch 

103 # one wins (decisions.md#vocabulary-collisions). 

104 'vom', # German "from the" 

105 'zu', # German "at/to", nobiliary ("zu Guttenberg") 

106 

107 # #269: Arabic native-script patronymic/clan particles. Unlike their 

108 # Latin transliterations, these live in a script namespace with no 

109 # collision against an unrelated Latin given name, so each is judged 

110 # on its own semantics rather than mirrored blindly: 

111 'بن', # "bin"/"ibn" (son of) -- never a bare given name. Latin 

112 # 'bin' is in PARTICLES but not in this set; that judgment 

113 # is unchanged by adding the Arabic-script form. 

114 'بنت', # "bint" (daughter of) -- mirrors Latin 'bint' above. 

115 'ابن', # "ibn" (son of, alternate spelling) -- mirrors Latin 

116 # 'ibn' above. 

117 'آل', # "aal" (family/clan of, e.g. "Al Saud") -- distinct from 

118 # the excluded definite article "ال" (#269 explicitly 

119 # excludes standalone "ال"); a clan prefix, never a bare 

120 # given name. 

121 

122 # #269: Hebrew native-script patronymic particles -- same 

123 # reasoning as the Arabic ones above: no Latin-script collision, 

124 # and neither functions as a standalone given name in Hebrew usage. 

125 # Deferred under the collision rule: 'בר' (Aramaic son-of, as in 

126 # Bar-Lev) -- Bar is a common modern Israeli given name, and the 

127 # surname spelling is hyphenated anyway. 

128 'בן', # "ben" (son of) 

129 'בת', # "bat" (daughter of) 

130}) 

131 

132# Maintainer note, deliberately a plain comment ABOVE the `#:` run 

133# rather than inside it: `#:` would publish it into the API reference, 

134# where it is advice to nobody, and a plain comment placed *within* the 

135# run splits it -- autodoc then renders only the fragment below the 

136# split and silently drops everything above it. Frozen by construction 

137# (#293) -- `frozenset | set` returns a frozenset, the LEFT operand's 

138# type wins, so keep the frozenset first. Flipped, this silently yields 

139# a plain set again and unfreezes the constant. 

140#: Family-name particles: a particle joins to the piece that follows it 

141#: to make one new piece, and particles chain, e.g. "von der" and 

142#: "de la". A particle in a non-leading position also pulls the pieces 

143#: after it into the same one, up to the next particle run or suffix, 

144#: which is how a multi-word name piece is recognized. Where that piece 

145#: lands is a later question: in "pennie von bergen wessels MD", "von" 

146#: joins each following piece until the suffix "MD", giving the family 

147#: name "von bergen wessels", while the same chaining in "Smith, Juan 

148#: de la Cruz" gives the middle name "de la Cruz". A leading 

149#: particle is the exception and chains nothing: the chain skips the 

150#: first piece of the NAME, its membership in this set or in 

151#: :py:data:`NON_GIVEN_NAME_PARTICLES` never entering into it. Leading 

152#: is read off the name rather than off the input (#367): a title is 

153#: not part of the name, so it is stepped over and "Dr. Van Johnson" 

154#: reads as the untitled "Van Johnson" does. One kind of word is not 

155#: stepped over, and this is the one place a title's vocabulary and 

156#: this one interact -- ``st``, ``do`` and ``freiherr`` are each BOTH 

157#: a title and a particle, so any of them could be the name's own 

158#: first piece and stops the scan, leaving a particle behind it 

159#: non-leading and free to chain ("Freiherr von Richthofen"). 

160#: Where the pieces then land is again a later 

161#: question, and this is where membership decides something: a leading 

162#: :py:data:`NON_GIVEN_NAME_PARTICLES` member makes the whole name a 

163#: family name ("de la Vega") under every ``name_order``, because a word 

164#: that is never a given name leaves the order nothing to place. A 

165#: leading particle OUTSIDE that set could be either, so there 

166#: ``name_order`` decides after all: the default given-first order reads 

167#: it as the given name ("Van Johnson"), while either family-first order 

168#: splits the same chains-nothing grouping the other way round ("Van 

169#: Johnson" -> family "Van", given "Johnson"). 

170#: What membership decides under ANY of the three orders is also the 

171#: report: a leading particle outside 

172#: :py:data:`NON_GIVEN_NAME_PARTICLES` records a particle-or-given 

173#: ambiguity for the reading not taken, and one inside it records none. 

174#: 

175#: Defined as a static union so every :py:data:`NON_GIVEN_NAME_PARTICLES` 

176#: member is guaranteed to also be a particle (and still join forward), 

177#: with no drift -- mirroring ``TITLES = GIVEN_NAME_TITLES | {...}`` in 

178#: :py:mod:`nameparser.config.titles`. 

179# The AMBIGUOUS half: words that are also borne as names in the position 

180# the leading-particle rule acts on (decisions.md#vocabulary-collisions 

181# C-i). A commented entry has been examined; a BARE one is sitting on the 

182# conservative default and nobody has looked at it. #360 examined the 12 

183# that appear leading in tools/differential's corpora plus a few more, 

184# and left the rest bare rather than record an absence of knowledge as a 

185# judgment. 

186PARTICLES = NON_GIVEN_NAME_PARTICLES | { 

187 'aan', 

188 'aen', 

189 'abu', # "Abu Bakr" -- Abu reads as the given name; also a bound 

190 # given-name word, so it joins forward (#269, P5) 

191 'al', # Al Gore: 'Al' is an ordinary English given name. Also the 

192 # Arabic definite article, which is the particle reading 

193 'bar', # Bar Refaeli; a common modern Israeli given name. Aramaic 

194 # "son of" is the particle reading (#269 excludes bare בר) 

195 'bat', 

196 'bin', # Arabic "son of", but kept ambiguous deliberately: #269 

197 # judged the Latin transliteration separately from the 

198 # native-script بن, which IS never-given 

199 'bon', 

200 'da', 

201 'dal', 

202 'del', # BOTH readings attested: Del Shannon (given name) and 

203 # del Toro (particle). The ambiguity flag is the answer 

204 'dela', # Dela is a short form of Adela, and an African name 

205 # meaning saviour/redeemer -- so the Filipino surname 

206 # particle ("Dela Cruz") is not the only reading 

207 'della', # Della Reese: an ordinary given name 

208 'den', 

209 'di', # BOTH: Di as a short form of Diana, and the Italian 

210 # particle (DiCaprio) 

211 'dí', 

212 'do', # Vietnamese Đỗ leads a great many surnames. Also in 

213 # TITLES -- decisions.md records TITLES n ambiguous == 

214 # {do, freiherr, st} as load-bearing for the emitter 

215 'du', # Du is a Chinese surname (Du Fu), leading under a 

216 # family-first reading 

217 'freiherr', # German noble title; also in TITLES, load-bearing 

218 'freiherrin', # as above 

219 'heer', 

220 'la', # La Shawn, La Toya: a given-name element. The Romance 

221 # article is the particle reading; its PLURALS ('las', 

222 # 'los') are never-given, being no one's name 

223 'le', # Lê is among the most common Vietnamese surnames and 

224 # leads under a family-first reading 

225 'mac', # Mac Miller: a real given name. This is the case that 

226 # shows #360's vowel heuristic was not the criterion -- 

227 # 'mac' and 'mc' differ by attestation, not by shape 

228 'san', # San San Chiou: a given-name element in Burmese and 

229 # Chinese naming 

230 'santa', 

231 'st', # Also in TITLES and consumed there before particle logic 

232 # runs, so moving it changes nothing; kept for the 

233 # load-bearing TITLES intersection above 

234 'te', # Te Awanui-a-Rangi: a Māori given-name element, and in 

235 # the corpus. Looks like the Dutch preposition 'te', which 

236 # is why attestation rather than etymology decides 

237 'tho', 

238 'thoe', 

239 'van', # Vietnamese Văn, and Van Johnson -- the canonical 

240 # particle-or-given ambiguity this whole flag exists for 

241 'vande', 

242 'vander', 

243 'vel', 

244 'von', # Von Miller: a real given name, so this stays ambiguous 

245 # even though "von Braun" is misparsed today. The case 

246 # that shows a word can look like a pure particle, BE 

247 # misparsed as one, and still have a bearer 

248 

249 # #269: Arabic "abu" (father of), left ambiguous like its Latin 

250 # transliteration 'abu' above (both spellings): "Abu Bakr" reads 

251 # "Abu" as a given name, so this stays a PARTICLES-only member, not 

252 # NON_GIVEN_NAME_PARTICLES. 

253 'أبو', 

254 'ابو', 

255} 

256 

257# Guard the two invariants the docstring above promises, so a future edit that 

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

259# happens to catch it. 

260assert NON_GIVEN_NAME_PARTICLES <= PARTICLES, \ 

261 "NON_GIVEN_NAME_PARTICLES must stay a subset of PARTICLES" 

262assert not (NON_GIVEN_NAME_PARTICLES & BOUND_GIVEN_NAMES), \ 

263 "NON_GIVEN_NAME_PARTICLES must stay disjoint from BOUND_GIVEN_NAMES" 

264assert_normalized("PARTICLES", PARTICLES) 

265 

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

267# note in prefixes.py. This module has no retired name of its own to 

268# serve; what __all__ buys here is the other half of that note's point, 

269# which the shim modules got in #354 and the destinations did not: 

270# without it a star import binds every module-level name, so 

271# `assert_normalized` and BOUND_GIVEN_NAMES -- imported only for the 

272# invariants below, and owned by another module -- landed in the 

273# caller's namespace as though they were vocabulary (#356). 

274# Source order, not alphabetical -- see the note in suffixes.py. 

275__all__ = ["NON_GIVEN_NAME_PARTICLES", "PARTICLES"]