2.1.0 - Unreleased
East Asian name support
- Add the Chinese locale pack
locales.ZH— opt-in Han segmentation for unspaced names like毛泽东, with the surname vocabulary it needs. A pack rather than a default because a Chinese surname list corrupts the Japanese names written in the same characters (高橋一郎would split高+橋一郎); Japanese data goes throughlocales.JAand its segmenter instead. It sets no name order, since native-script Han already reads family-first without it (#271) - Add
Lexicon.surnames,Policy.script_orders,Policy.segment_scripts, theScriptenum and theDEFAULT_SCRIPT_ORDERSconstant to the public API. This is the first behavior nameparser keys on the script a name is written in; it is allowed only where the script itself settles a convention, never as a proxy for guessing the language (#271) - Add
Lexicon.honorific_tails, the vocabulary the glued-honorific peel matches: entries that may be split off the END of a name token, matched longest-first. Deliberately a separate, narrower set than the spaced honorific vocabulary — a glued tail has no token boundary to lean on — and every entry is also asuffix_wordsentry, since the peeled piece is claimed by ordinary suffix classification. That subset rule is enforced, so extend both fields in the one call —Lexicon.default().add(suffix_words={"ちゃま"}, honorific_tails={"ちゃま"}); adding tohonorific_tailsalone raisesValueErrornaming the orphan (#308) - Add
AmbiguityKind.SEGMENTATION, reported when a surname split had a vocabulary-supported alternative:"남궁민수"is 남궁 + 민수 by the compound surname but 남 + 궁민수 by the single-syllable one, and longest-match had to pick. A name with only one possible split decided nothing and reports nothing (#271) - Add the Japanese locale pack
locales.JAand the segmenter factorylocales.ja_segmenter(), which together divide an unspaced Japanese name:parser_for(locales.JA, segmenter=locales.ja_segmenter())reads山田太郎as family山田, given太郎. The two halves are separate because no surname list can do this job — family and given names draw on the same kanji and the reading, not the spelling, decides most divisions — so the pack activates the stage and a third-party divider performs it.ja_segmenter()wraps namedivider-python, installed with the newnameparser[ja]extra; the core stays dependency-free, andja_segmenter(gbdt=True)selects namedivider's more accurate gradient-boosted model, which downloads its data on first use. With both packs registered,locales.available()is now('ja', 'ru', 'tr_az', 'zh')(closes #272) - Add
Segmentationand theSegmentertype alias to the public API, plus the keyword-onlyParser(segmenter=...)hook they describe: any callable from a token's text to aSegmentation(the interior offsets to cut at, and a confidence) orNoneto decline. It is consulted only for scripts listed inPolicy.segment_scripts, and only where the surname vocabulary declined first, soparser_for(locales.ZH, locales.JA, segmenter=...)composes — a listed Chinese surname wins, the segmenter takes the rest. Read that composition with the zh pack's own warning still attached: vocabulary-first means a Japanese kanji name opening on a listed Chinese surname never reaches the segmenter, so高橋一郎still splits高+橋一郎under the stack exactly as it does underlocales.ZHalone. The two packs are alternatives, one per corpus; stack them only for genuinely mixed data that accepts that trade (#272) - Add the
ScriptmembersHIRAGANAandKATAKANA. Two members rather than oneKANAbecause the parser treats them differently: hiragana never transcribes a foreign name, while a wholly-katakana name usually is one (#272)
Breaking Changes
- Change the pickle compatibility of the 2.0 API's
PolicyandLexicon: the new fields change the field layout their guarded__setstate__checks, so a pickle written by 2.0.0 raisesValueErrornaming the missing fields instead of loading. Re-pickle after upgrading.HumanNamepickles are unaffected — the facade pickles v1-shaped component state, not these objects - Change the pickle compatibility of
Parserin the same way and for the same reason: the newsegmenterfield changes the layout its guarded__setstate__checks, so aParserpickled by 2.0.x raisesValueErrornaming the missing field rather than loading silently with the field absent. That guard is the design, not a regression — re-pickle after upgrading. AParsercarrying a segmenter pickles only if that segmenter does, which a module-level function does and a closure or lambda does not (#272) - Change one thing about parse totality:
parse()still never raises on any input, but a user-suppliedParser(segmenter=...)runs inside the parse and its own exceptions propagate rather than being absorbed. A failure there is a bug in your callable, not a fact about the name, and hiding it would only make it harder to find (#272)
Behavior Changes
- Fix names written wholly in Han or Hangul parsing given-first: native-script CJK now reads family-first by default, through the new
Policy.script_orderstable, so"毛 泽东"gives family毛where 1.x gave family泽东. A name that is a single unspaced token moves the same way —"毛泽东"and"山田太郎"now land infamily/lastwhere 1.x put them ingiven/first, with the string itself untouched, which makes it the easiest form of this change to miss — a lone token renders the same whichever field holds it, whereas the spaced form does show up in the output (str(HumanName("毛 泽东"))is now"泽东 毛"). No language detection is involved: Chinese and Japanese both write the family name first in native script, so the script settles the order without anyone having to know which language it is. Latin-script and mixed-script names are never affected, and an explicit comma still wins. Default-on: changes parse output for wholly-CJK names, throughHumanNameas well as the 2.0 API (closes #271) - Fix unspaced Korean names not splitting: the census surname list now ships as default vocabulary (
Lexicon.surnames) with hangul segmentation on by default (Policy.segment_scripts), so"김민준"parses family김, given민준where 1.x returned the whole string asfirst. Rendering follows the split, sostr(HumanName("김민준"))is now"민준 김"where 1.x echoed the input back unchanged. Nothing but Korean is written in hangul and its surnames are a closed census set, which is what makes the split safe as a default rather than a pack. Default-on, and it reachesHumanNametoo.Policy(segment_scripts=())turns the split off;Policy(script_orders={})separately restores the positional reading; clearing both restores 2.0 behavior exactly (#271) - Fix Japanese names carrying kana parsing given-first: the family-first rule above extends to any name whose characters stay within kanji and kana while carrying at least one kana character, so
"高橋 みなみ"gives family高橋and"山田 エミ"family山田where 1.x read both the other way round, and a lone such token ("高橋みなみ","みなみ") lands infamily/lastwhere 1.x put it ingiven/first. The reasoning is the one hangul already uses: hiragana never transcribes a foreign name, and a transcription is kana ALONE, so kanji-plus-kana is a Japanese person's name written in Japanese order. A name written wholly in katakana is deliberately excluded and stays positional — it is predominantly a transcribed foreign name ("マイケル ジャクソン") already in given-first order. Default-on: changes parse output for kana-bearing Japanese names, throughHumanNameas well as the 2.0 API, and the spaced forms change what the name renders as (str(HumanName("高橋 みなみ"))is now"みなみ 高橋").Policy(script_orders={})clears this entry along with the Han and Hangul ones (#272) - Fix names containing 〆 (U+3006, the shime mark that opens Japanese surnames like 〆木 and 〆谷) parsing given-first: the script classifier now counts 〆 as Han, extending the 々 entry the table already carries, and going a step further than it — 々 is Script=Han and merely outside the ideograph blocks, while 〆 is Script=Common — so these names take the East Asian family-first reading like any other wholly-Han name.
Policy(script_orders={})restores the positional reading for these names exactly as for other wholly-Han names (#303) - Fix the katakana middle dot
・(U+30FB, and its halfwidth twin U+FF65) being read as part of a name rather than as the divider it is: it now separates tokens exactly as a space does. A transcribed foreign name therefore divides into its parts and, being wholly katakana, keeps its source order —"マイケル・ジャクソン"gives givenマイケル, familyジャクソン, where 1.x left the whole string infirst— while a kanji pair written the same way takes the family-first rule ("高橋・一郎"→ family高橋). Native Japanese names never contain this character and no other script's names use it, so the separation is unconditional, which also means it is not covered by the two policy opt-outs. Rendering follows, the way the Korean split's does: the dot comes back as a space, sostr(HumanName("マイケル・ジャクソン"))is now"マイケル ジャクソン", and that reaches delimited content too — the nickname in"山田 太郎 (マイケル・ジャクソン)"renders"マイケル ジャクソン". The Chinese interpunct·is not an unconditional separator like these two: U+00B7 is also the Catalan punt volat and appears inside legitimate names (Gal·la), so it divides only between classified-script characters — the transcription treatment it marks is #298's (#272) - Fix 间隔号-divided transcriptions parsing as one unsplit token: U+00B7 — the interpunct Chinese text divides a transcribed foreign name with,
威廉·莎士比亚for William Shakespeare — is now a token separator between characters of a classified script, and a name it divides keeps its source order and is never segmented: the dot is the transcription marker, playing the role pure katakana plays in the kana license. It divides ONLY between classified-script characters, so the Catalan punt volat interior to names likeGal·lais untouched. The Japanese nakaguro is deliberately not a transcription marker —高橋・一郎is roster formatting, 姓・名, and keeps its family-first reading — so a transcription typed with the wrong dot (威廉・莎士比亚) reads by the convention of the codepoint it was typed with; like the spaced form, only the Chinese dot rescues it. Rendering stays space-joined (str(HumanName("威廉·莎士比亚"))is"威廉 莎士比亚"); a customstring_formatsuch as"{first}·{last}"reinstates the dot (#298) - Fix spaced CJK postnominal honorifics parsing as name parts: 씨, 박사, 선생님, 교수님, 군, 양 (Korean — standardly written as their own token), 先生, 女士, 小姐, 博士, 教授 (Chinese, with 先生/博士/教授 shared with Japanese), and 様, 氏 (Japanese) now route to
suffix, so王小明 先生reads family王小明where the family-first default had confidently made 先生 the given name. Whole-token matching, which also reaches a glued surname+honorific token, since segmentation splits off the surname first (김씨reads family 김, suffix 씨). The glued forms whole-token matching cannot reach are handled by the peel described below (closes #307) - Fix glued CJK honorifics parsing as part of the name: an honorific written against the name —
田中さん,山田太郎様,김민준씨,김민준님,王小明先生— is now split off the end of the last name token and routed tosuffix, where it had been swallowed by the name (the whole of田中さんwas the family name;김민준씨gave given 민준씨). The peeled name then goes through the ordinary machinery, so the Korean split still happens (김민준씨→ family 김, given 민준, suffix 씨) and the田中さんcase stops misreading as a kana-licensed composite. Only entries that can never END a name peel — 씨, 님, 박사, 박사님, 선생님, 교수님, さん, さま, くん, ちゃん, 様, 先生, 教授, 女士, 小姐 — while 양, 군, 氏, 博士 and 殿 are recognized in their spaced form only, because 김지양 is a given name, 田中博士 is Tanaka Hiroshi as readily as Doctor Tanaka, and some ninety Japanese surnames end in 殿 (鵜殿, 真殿); 君 is recognized in neither form, since 王君 is a complete Chinese name, though its kana spelling くん peels. Seven entries are new vocabulary in this change (さん, さま, くん, ちゃん, 殿, 님, 박사님), so their spaced forms route tosuffixtoo (田中 さん,田中 殿,김민준 님,김민준 박사님). 박사님 closes a gap in the shipped set rather than opening new ground: 선생님 and 교수님 shipped in 2.1 without it, so김민준박사님stranded 박사 in the given name and the spaced김민준 박사님came back as two suffixes for one honorific. Exactly one honorific peels off a token, and every entry is a whole honorific rather than a part of one. A token that is nothing but an honorific is no longer taken apart either —선생님and박사now stay whole where hangul segmentation had split them 선 + 생님 and 박 + 사, since 선 and 박 are listed surnames — which is default-on in its own right, hangul segmentation being a default. A configured segmenter benefits twice over: it is handed the name without the honorific, and the honorific the peel just cut off does not then look to it like a boundary the writer drew — soparser_for(locales.JA, segmenter=ja_segmenter())reads山田太郎様as family 山田, given 太郎, suffix 様. Worth knowing before you upgrade: that exemption is what makes a GLUED honorific stop protecting a name from division, so a family name written alone with one —田中さん— now divides the way bare田中already did (family 田, given 中, suffix さん). It is exactly and only the peeled tail that is exempt. A SPACED honorific is a token boundary its writer typed, and anything standing beside a name calls the segmenter off, so the name is left as written:田中 さんand佐藤 氏keep family 田中 and 佐藤 under the pack — the division the pack gives them without this change, whichever field the honorific itself lands in. That is the conservative reading rather than a claim about intent: a spaced honorific cannot be told apart from a spaced given name by position, and counting it as one keeps four real surnames whole (佐藤 氏,田中 様,鈴木 先生,中村 教授) at the price of the one division it then declines to make (山田太郎 様). Writing the honorific spaced is therefore an opt-out in its own right on the SEGMENTER path, alongside declining the pack or the segmenter. It is no lever where the VOCABULARY divides the name, the two spellings agreeing exactly there —김민준 씨and김민준씨both give family 김, given 민준, suffix 씨, as do王小明 先生and王小明先生under the Chinese pack — and Korean data has no pack to decline either, hangul segmentation being on by default. Default-on: changes parse output for glued CJK honorific forms, throughHumanNameas well as the 2.0 API (closes #308) - Fix NFD-decomposed input missing the East Asian defaults entirely: script classification now normalizes to NFC before deciding, so a Korean or Japanese name typed on macOS — where decomposed text is routine — gets the same order rule as its composed twin, which it silently did not before. Segmentation MATCHING deliberately stays raw, so an unspaced NFD hangul name is ordered correctly but not split, rather than being split in the wrong place. One gotcha worth stating plainly: parse output preserves the encoding it was given, so for NFD input
name.family == "김"isFalseeven though it is the same name — compare NFC-normalized text when comparing across encodings (#272) - Fix the Ukrainian conjunction
йnot joining the pieces around it: it is the euphonic alternate ofі, the two chosen by the surrounding vowel and consonant rather than by meaning ("Олесь і Олена"but"Марія й Петро"), so real Ukrainian data carries both spellings and shipping onlyіrecognized just one of them."Олесь й Олена Коваленки"now gives given"Олесь й Олена"where theйpreviously landed inmiddle. Same treatment as theи/іentries added in 2.0.0, single-letter carve-out included: the conjunction joins only once the name has enough pieces, and a punctuated initial still wins, so"Й. Сліпий"is unaffected. Raised in a comment on #267
- Add the Chinese locale pack
2.0.0 - July 27, 2026
Two release candidates preceded this release (rc1 on 2026-07-23, rc2 on 2026-07-26). Please report anything the migration missed on issue #284. The notes below describe 2.0.0 as a whole.
nameparser 2.0 adds a new parsing API alongside
HumanName.parse()returns an immutableParsedNamewhose seven fields are named for what they are (given,family) rather than where they sit in a Western name, configured by two frozen value objects -- aLexiconof vocabulary and aPolicyof behavior -- instead of a mutable global.HumanNamekeeps working: it is now a compatibility facade over the same pipeline, and it stays through 2.x. The removals below are the deprecations announced in 1.3.0 and 1.4.0 coming due; if your code runs warning-free on 1.4.0, most of them will not affect you. See :doc:`migrate` for the field-by-field map.The 2.0 API
- Add
parse(text), returning an immutableParsedNamewith seven fields --title,given,middle,family,suffix,nickname,maiden-- plus the derived viewsgiven_names,surnames,family_baseandfamily_particles. Parsing is a pure function of the text, aLexiconand aPolicy: nothing global is consulted and nothing is mutated - Add
Lexicon, the frozen vocabulary object.Lexicon.default()is the shipped vocabulary andLexicon.empty()is a blank one;add(**entries),remove(**entries)and field-wise|all return new instances. Its fields aretitles,given_name_titles,suffix_acronyms,suffix_words,suffix_acronyms_ambiguous,particles,particles_ambiguous,conjunctions,bound_given_names,maiden_markersandcapitalization_exceptions - Add
Policy, the frozen behavior object:name_order,patronymic_rules,middle_as_family,nickname_delimiters,maiden_delimiters,extra_suffix_delimiters,lenient_comma_suffixes,strip_emojiandstrip_bidi - Add
Parser, a reusable parser bound to a lexicon and policy (Parser(lexicon=..., policy=...).parse(text)), andparser_for(*locales, base=None), which folds locale packs onto a base parser - Add
name_orderand the constantsGIVEN_FIRST,FAMILY_FIRSTandFAMILY_FIRST_GIVEN_LAST, so a family-first name can be parsed as written rather than reordered by hand (the configuration half of #270; the locale packs below complete it) - Add
PatronymicRulewith the membersEAST_SLAVICandTURKIC. v1's singlepatronymic_name_orderflag enabled both detectors at once;Policy(patronymic_rules=...)lets you enable either one alone - Add
PolicyPatchand theUNSETsentinel for partial policy deltas that compose -- set-valued fields union, scalar fields override with later winning. This is the mechanism locale packs are built from, andUNSETis only needed when you must distinguish "not set" from a realFalseorNone - Add
Token,SpanandRole: every field is backed by tokens carrying exact(start, end)offsets into the original string, reachable withtokens_for(Role.GIVEN). This replaces v1's*_listattributes and makes it possible to highlight or re-slice the input the parse came from.Roleis aStrEnum, so members compare and stringify as their field names (Role.GIVEN == "given"), matchingAmbiguityKind;tokens_for()accepts a role's string name too, and raisesValueErrorfor an unknown role - Add
STABLE_TAGSto the public API: the four documentedToken.tagsvalues (particle,conjunction,initial,joined) - Add
Policy.patched(patch), applying aPolicyPatchdirectly without wrapping it in a locale pack - Add
Parser.matches(a, b)andParser.capitalized(name): theParsedNamemethods of the same names fall back to the default configuration for str/omitted arguments, which is silently wrong for names parsed with a customParser - Add
Parser.revise(name, **fields):ParsedName.replace()with the replacement text classified by the parser's vocabulary, so particle/initial/suffix-join behavior survives the edit - Add
Ambiguityand theAmbiguityKindenum, so a parse reports what it had to guess at instead of guessing silently. The kinds emitted today areparticle-or-given,suffix-or-name,suffix-or-nickname,unbalanced-delimiterandcomma-structure;orderis reserved and not yet emitted. The two suffix kinds cover the post-nominals that are also ordinary words:"John Smith MA"reports thatMAwas read as a credential rather than a surname, and"JEFFREY (JD) BRICKEN"that the delimitedJDwas read as a nickname rather than a suffix. A reading the vocabulary settles on its own —"John Smith M.A.","Andrew Perkins (MBA)"— is not a guess and reports nothing - Add
ParsedNameoutput and comparison methods:render(spec),initials(),capitalized()(which returns a new value rather than mutating in place),as_dict()(whoseinclude_emptyflag is keyword-only, unlikeHumanName.as_dict()'s),replace(**fields),matches()andcomparison_key() - Add
Locale, the public pack type. Writing your own needs no registration -- construct aLocaleand pass it toparser_for() - Ship a fully typed public API (PEP 561): the core modules are checked under strict mypy settings, and nameparser 2.0 has no runtime dependencies
Breaking Changes
- Raise the minimum Python to 3.11 and drop the last runtime dependency,
typing_extensions(#257). Python 3.10 is no longer supported - Remove
HumanName.__eq__and__hash__(deprecated in 1.3.0, #223): instances now compare and hash by identity, soHumanName("John Smith") == "John Smith"isFalsewhere 1.x returnedTrue. This changes result silently rather than raising -- it is the one removal that can pass unnoticed into production. Usematches()to ask whether two names are the same, andcomparison_key()as a dict key or sort key - Remove every v1 parsing hook and subclass extension point:
pre_process,post_process,parse_pieces,parse_nicknames,join_on_conjunctions, theis_*predicates,cap_word/cap_piece,handle_firstnames,fix_phdand the rest. A subclass that overrides one gets aDeprecationWarningat construction naming the hooks, because the facade delegates to the coreParserand never calls them (closes #280). Customize throughLexicon/Policyinstead - Remove regex configuration:
CONSTANTS.regexesis now a read-only proxy. Reads still work, butCONSTANTS.regexes.bidi = False, item assignment andConstants(regexes=...)all raiseTypeError. If you followed 1.3.1's advice to keep bidi marks withCONSTANTS.regexes.bidi = False, that opt-out is nowPolicy(strip_bidi=False)on the 2.0 API; the same applies toregexes.emojiandPolicy(strip_emoji=False) - Remove
bytesinput and theencodingargument (#245): passingbytestoHumanNameor to a set manager raisesTypeErrorwith a decode hint, andSetManager.add_with_encoding()andDEFAULT_ENCODINGare gone. Decode first, then useadd() - Remove
SetManager.__call__(#243); iterate the manager or callset(manager).remove()of a missing member now raisesKeyErrorlikeset.remove--discard()is the ignore-missing form -- and the set operators|,&,-and^return a plainset - Remove
HumanNameslice access and item assignment (#258):name[1:-3]andname['first'] = valueraiseTypeError. String-key reads (name['first']) and iteration are unchanged; assign fields as plain attributes - Remove
Constants.empty_attribute_default(#255): empty fields are always''. Assigning it raisesAttributeError; a pickle carrying the key still loads, with the value ignored - Remove
constants=None(#261): bothHumanName(..., constants=None)andhn.C = NoneraiseTypeError. UseConstants()for library defaults orCONSTANTS.copy()for a private snapshot - Remove silent unknown-key access on the mapping managers (#256):
CONSTANTS.capitalization_exceptions.typoandCONSTANTS.regexes.typoraiseAttributeErrornaming the miss, instead of returningNone/EMPTY_REGEX(capitalization_exceptionsalso lists the known keys)..get()remains available on both for intentional soft access - Remove support for
Constantspickles written by nameparser 1.2.x or earlier (#279): loading one raisesValueErrortelling you to re-pickle under 1.3/1.4 first - Remove the dead
regexes.no_vowelspattern (#268) and theConstants.suffixes_prefixes_titlescached union; neither was read by the parser - Change
HumanName's*_listattributes to read-only properties: they remain readable snapshots, butname.first_list = [...]now raisesAttributeError - Change the
Constantsconstructor to keyword-only; positional construction no longer works
Behavior Changes
- Recognize maiden-name markers --
née,nee,geb.,roz.and the Scandinavian participle forms -- and route the following name to the newmaidenfield. 1.x folded them intomiddle/last, so"Jane Smith née Jones"parsed asmiddle="Smith née"(closes #274) - Recognize typographic nickname delimiters by default in both APIs (closes #273): smart quotes (
“Jack”), German and Polish low-high quotes („Hansi“), Swedish right-right quotes (”Ann”), guillemets in either direction («Petit»,»Hansi«), CJK corner brackets (「タロ」,『ハナ』) and fullwidth parentheses. In 1.x these leaked intomiddleas literal text. Curly single quotes stay excluded, because U+2019 is the apostrophe in "O’Connor" - Fix the pre-comma piece being routed to
firstwhen everything after the comma is a suffix or title:"Andrews, M.D."now reads familyAndrews/ suffixM.D.where 1.x read givenM.D./ familyAndrews, and"Smith, Dr."movesSmithfromfirsttofamily(the title was already correct in 1.x). The piece before a comma is definitionally the family name - Fix a lone recognized trailing suffix with no comma being routed to
first/last:"Johnson PhD"and"Mr. Johnson PhD"now keep the suffix insuffix - Fix a split
"Ph. D."credential being read as two tokens; it now classifies as one suffix, replacing v1'sfix_phdhook. This now holds wherever the credential sits: 1.x healed the pair only when it trailed, so"Ph. D. John Smith"parsed as titlePh./ givenD.with the real given name pushed tomiddle; it now reads givenJohn, familySmith, suffixPh. D. - Fix
chargé d'affaires: shipped as one unmatchableTITLESentry since it was added, it is now two chainable entries (chargé,d'affaires), so the title is recognized; the unaccented spellingchargeships too, likeattaché/attache - Remove seven multi-word
SUFFIX_ACRONYMSentries (leed ap,nicet i–nicet iv,psm i,psm ii) that could never match in any release; splitting them would swallow real names ("John Leed", "Smith, A.P."), so they are dropped instead - Add a
UserWarningwhen a multi-word entry is stored in a per-wordLexiconfield or as acapitalization_exceptionskey -- such entries can never match - Parse an input with no alphanumeric character to an empty name in both APIs. 1.x kept pure punctuation as a name part, so
"."gavefirst="."andbool()wasTrue; 2.0 empties it, keepingbool(parse(x))an honest "did I get a name?" test. The check is Unicode-aware, so names in any script are unaffected; only inputs that are entirely punctuation or symbols (".","- -") change. Junk embedded in a name with real content -- the stray dot in"John . Smith"-- is still kept, since that parse is already truthy - Fold a leading never-given particle into the family name. Note that
Lexicon.particles_ambiguousis the complement of v1'snon_first_name_prefixes, not a rename -- it lists the particles that may double as a given name, where v1 listed the ones that may not. Copying a v1 customization across without inverting it silently reverses the behavior; see :doc:`migrate` - Add
maanddotosuffix_acronyms_ambiguous, the set of post-nominals that are also ordinary surnames. An entry there is read as a credential when the name can spare it — written with periods ("John Smith M.A."), or when removing it still leaves a given and a family name ("John Smith MA"→ suffixMA). With only two pieces to go around, the surname reading wins instead:"Jack Ma"and"Anh Do"keep their family names. As a side effect, a parenthesized or quoted"(MA)"/"(DO)"now falls through to nickname parsing rather than escaping tosuffix, since inside delimiters the nickname reading is the plausible one - Change
comparison_key()andmatches()in both APIs to fold withstr.casefold()where 1.4 usedstr.lower(), so Unicode case pairs compare equal --"STRASSE"matches"Straße", and a Greek final sigma matches its regular form. This is strictly more permissive: anything 1.4 matched still matches. Vocabulary normalization deliberately still useslower(), for v1 parity - Change the 2.0 API's default
render()/str()spec to show every non-empty field:'{title} {given} "{nickname}" {middle} {family} ({maiden}) {suffix}'. The quoted nickname round-trips exactly; the parenthesized maiden re-parses as a nickname, a deliberate choice of presentation over lossless round-trip -- usenée {maiden}in a custom spec if you need it to survive a reparse.HumanNamekeeps v1's ownstring_formatdefault, unchanged - Change delimiter-overlap precedence in the 2.0 API: a pair listed in
Policy.maiden_delimitersis dropped from the effective nickname set, soPolicy(maiden_delimiters={("(", ")")})alone routes parenthesized content tomaiden. The default nickname set is exported asDEFAULT_NICKNAME_DELIMITERS.HumanNamekeeps v1's nickname-wins precedence, so no existing behavior changes - Change suffix-delimiter rendering when a custom suffix delimiter is configured: for
suffix_delimiter="/"and"John Smith, RN/CRNA", 1.x split the token and renderedsuffix="RN, CRNA"where 2.0 keeps it whole as"RN/CRNA". Role assignment is unchanged; only rendering differs - Correct a long-standing typo in the shipped vocabulary that 1.x carried:
actorandtelevisionhad trailing spaces inTITLES, so"actor" in TITLESwasFalse. Parse output is unchanged -- the parser normalizes entries on ingest, which is exactly what let the typo go unnoticed -- but code that tests membership against the exported constants directly will see corrected results. The data modules now assert their invariants at import time, alongside the onesprefixes.pyalready checked; entries must be stored lowercase and whitespace-free, so this class of typo now fails the build
International name support
- Add
nameparser.localeswith the first two packs:locales.RU(East Slavic patronymic order) andlocales.TR_AZ(Turkic patronymic markers). Packs are pure data folded in atparser_for(locales.RU), they compose (parser_for(locales.RU, locales.TR_AZ)unions the rules), and they are never auto-detected -- there is no reliable way to detect a name's language from the name alone.locales.available()andlocales.get(code)look packs up by code; loading is lazy, so importing the package imports no pack (completes #270; #271/#272/#146 stay staged for 2.x) - Add non-Latin vocabulary to the default lexicon (#269): Cyrillic, Greek, Arabic and Hebrew titles, conjunctions and name particles. Native-script entries cannot collide with Latin-script names, which is what makes them safe to enable by default. Deferred pending vetting: the Cyrillic
мл/стsuffixes and the bare Greekκtitle, which collides with the initial-plus-surname shape. Behavior note:محمد بن سلمانnow chainsبنonto the family name where 1.x read it as a middle name - Add Arabic-script bound given names (#269):
عبد, the kunya pairأبو/ابو, andأم/امjoin the following word into the given name exactly as their transliterations (abdul,abu,umm) always did, soعبد الرحمن محمدparses givenعبد الرحمن, familyمحمدwhere 1.x split it into given plus middle - Add Arabic honorific titles and the conjunction
و(#269): the doctor, professor, hajj, sheikha and engineer forms (الدكتور/الدكتورة/دكتور/دكتورة,الأستاذ/الأستاذة/أستاذ/أستاذة,الحاج/الحاجة,الشيخة,مهندس) as given-name titles, since Arabic honorifics precede the given name likeالشيخ. Deferred under the collision rule: bareسيد/شيخ/أمير/سلطان(all common given names), theد.abbreviation (bareدwould swallow initials), and the Ottoman post-nominalsباشا/بك/أفندي(which survive as family names) - Add Hebrew honorifics and post-nominals, and Devanagari titles (#269): the Israeli honorifics
גברת,פרופ'/פרופ׳,פרופסור,עו"ד/עו״דandהרבas titles;ז"ל/ז״לandשליט"א/שליט״אas suffixes, in both gershayim spellings; and Devanagariश्री,श्रीमतीandडॉ. Latinsri/shriwere deliberately not added, because they collide with real given names where the native script cannot. Deferred: bareרב(an ordinary word meaning "many") andברas a particle (Bar is a common modern given name)
Compatibility layer
- Reimplement
HumanNameas a facade over the 2.0 pipeline, andConstantsas a shim resolving to a(Lexicon, Policy)snapshot with a shared parser cache. Fields, aggregates, mutation throughname.C.titles.add(...), rendering defaults,capitalize(),matches(),comparison_key(), iteration,as_dict()and pickling are all preserved, andnameparser.parserandnameparser.configremain importable. The compatibility layer ships through 2.x and is removed in 3.0 - Note that
CONSTANTS.capitalize_nameandforce_mixed_case_capitalizationare still honored through the facade, but the 2.0 API never capitalizes duringparse()-- callcapitalized()when you want it - Add
Rolemembers (and their string valuesgiven/family) as validHumanNamesubscript keys:hn[Role.GIVEN]returnshn.first - Add a
UserWarningwhen assigningHumanName.givenor.family: the facade spells those attributesfirst/last, so the assignment creates an inert stray attribute while the parse keeps the old value. The assignment still happens (v1-legal ad-hoc attributes keep working); the warning names the v1 spelling to use
Command line
- Rewrite
python -m nameparserover the 2.0 API with a real argument parser. It prints the parse plus its capitalized form and initials by default, takes--jsonto emit the fields as JSON (python -m nameparser --json "Doe, John"), takes--locale CODEto apply a pack, and exits with a usage message on bad arguments
Documentation
- Rewrite the documentation new-API-first: a new front page and README,
usage.rstas a tour of the 2.0 API, a principle-firstcustomize.rst, a reference split into the 2.0 API and the compatibility layer, and new pages for :doc:`concepts`, :doc:`locales` and :doc:`migrate` -- the last carrying full attribute and configuration maps from v1 names to 2.0 names (#262). The 1.x documentation remains online as the readthedocsstablebuild
Parsing changes were checked against a 652-name differential corpus -- names harvested from the v1 test banks, plus names reported in the issue tracker -- and everything not listed above parses identically between 1.4.0 and 2.0. The harness lives in
tools/differential/in the source repository (it is development tooling, not part of the installed package) -- see its README to reproduce the comparison against your own names.Changed since 2.0.0rc1 (for anyone who tested the release candidate)
Rolebecame aStrEnum;str(Role.GIVEN)is now"given"ParsedName.tokens_for()raisesValueErrorfor unknown roles instead of returning no tokens; it also accepts role-name stringsParsedName.as_dict()'sinclude_emptyis keyword-onlyHumanNamesubscripting acceptsRolemembers- Assigning
HumanName.given/.familywarns (the facade spells themfirst/last; the assignment was and remains an inert stray attribute) - The eight multi-word vocabulary entries that could never match were repaired (
chargé d'affairessplit; seven credential acronyms removed), and storing a new multi-word entry now warns; those eight are dropped silently, not warned about, when a restoredConstantspickle carries all eight of them (the pre-2.0 signature) PolicyPatch's repr shows only the fields a patch sets, instead of all nine with UNSET sentinels- New since rc1:
STABLE_TAGS,Policy.patched(),Parser.matches(),Parser.capitalized(), andParser.revise()-- see the API section above
- Add
1.4.0 - July 12, 2026
- Add
Constants.copy(), a detached deep copy that preserves the source instance's current customizations (unlikeConstants(), which always starts from library defaults) -- useful asCONSTANTS.copy()for a private snapshot of the shared config (#260) - Deprecate passing
constants=NonetoHumanName(or assigninghn.C = None): it silently builds a freshConstants(), discarding any customizations the caller may have expected to carry over from the sharedCONSTANTS. EmitsDeprecationWarning; will raiseTypeErrorin 2.0. Useconstants=Constants()for fresh library defaults orconstants=CONSTANTS.copy()for a private snapshot instead (closes #260) - Deprecate assigning
Constants.empty_attribute_defaultfor removal in 2.0 (#255): onceNonesupport goes, the only legal value left is the default'', so a dial with one position isn't configuration. EmitsDeprecationWarning; reading the attribute is unaffected - Deprecate unknown-key attribute access on
TupleManager/RegexTupleManager(CONSTANTS.regexes.typo,CONSTANTS.capitalization_exceptions.typo, etc.) for removal in 2.0 (#256): a misspelled or omitted key currently degrades silently (None/EMPTY_REGEX) with no traceback pointing at the typo. EmitsDeprecationWarningnaming the miss and the known keys; will raiseAttributeErrorin 2.0..get()remains available for intentional soft access - Deprecate
HumanNameslice access (name[1:-3]) and item assignment (name['first'] = value) for removal in 2.0 (#258): field access by position has no real use case, and item assignment duplicates plain attribute assignment. Both emitDeprecationWarning; string-key access (name['first']) is unaffected - Deprecate
SetManager.add_with_encoding()itself for removal in 2.0 (#245), regardless of argument type: useadd()instead (decoding bytes first). Previously only thebytespath warned; thestrpath was silent even though the whole method goes away - Deprecate loading a legacy-format
Constantspickle (written by nameparser <= 1.2.x, before the 1.3.0 pickle fix) for removal in 2.0 (#279):__setstate__'s migration shim currently skips the stale computed-property key silently. EmitsDeprecationWarningonce per call telling users to re-pickle; will raiseValueErrorin 2.0 - Fix the
"Lastname, Firstname"comma format not being recognized when the input uses the Arabic comma،(U+060C, the standard comma in Arabic/Persian/Urdu text) or the fullwidth CJK comma,(U+FF0C) instead of the ASCII comma: both variants now also split the format and no longer leak into the parsed output (closes #265)
- Add
1.3.1 - July 11, 2026
- Fix invisible Unicode bidirectional control characters (LRM/RLM/ALM, the embedding/override marks, and the isolates U+2066–U+2069) surviving parsing and sticking to
first/last/etc., so a copy-pasted right-to-left name silently failed equality and dedup. They are now stripped in preprocessing like emoji; disable viaCONSTANTS.regexes.bidi = False(closes #266) - Fix
str()corrupting name text containing the substring"None"whenempty_attribute_defaultisNone(e.g."Nonez Smith"rendered as"z Smith"): empty attributes are now substituted as''before the format string is applied, instead of scrubbing the interpolated"None"from the output afterward (closes #254)
- Fix invisible Unicode bidirectional control characters (LRM/RLM/ALM, the embedding/override marks, and the isolates U+2066–U+2069) surviving parsing and sticking to
1.3.0 - July 5, 2026
Breaking Changes & Deprecations
- Deprecate
HumanName.__eq__and__hash__for removal in 2.0 (#223): the current design's three promises — case-insensitive equality, equality with plain strings, and hashability — are mutually inconsistent (equal objects can hash differently), equality depends onstring_format, andmaidenis invisible to it. Both now emitDeprecationWarningnaming the replacement; behavior is otherwise unchanged until 2.0 (closes #224) - Deprecate
bytesinput for removal in 2.0 (#245): passingbytestoHumanName/full_nameor toSetManager.add()/add_with_encoding()now emitsDeprecationWarning— decode first, e.g.value.decode('utf-8'). Theencodingconstructor argument is deprecated with it - Deprecate
SetManager.__call__for removal in 2.0 (#243): calling a manager returns the raw underlying set, so mutating the result bypasses normalization and cache invalidation; iterate the manager or copy withset(manager)instead - Add
SetManager.discard(), and deprecateremove()of a missing member (#243): it currently does nothing but will raiseKeyErrorin 2.0, matchingset.remove; usediscard()for intentional ignore-missing removal. Removing present members is unchanged and does not warn - Fix
HumanNameacting as its own iterator with a stored cursor: breaking out of a loop, iterating in nested loops, or callinglen(name)mid-loop corrupted subsequent iteration;iter(name)now returns a fresh independent iterator each time.next(name)on the instance itself (undocumented) now raisesTypeError— callnext(iter(name))instead (closes #225) - Remove the vestigial
unparsableattribute: the guard that was meant to set it has been unreachable since 2013 (v0.2.9), so it has reportedFalsefor every parsed name for over a decade; checklen(name) == 0to detect an empty parse - Remove
__ne__; Python 3 derives!=from__eq__automatically - Change internal initials helper
__process_initial__to_process_initial: double-underscore-both-sides names are reserved for Python special methods; subclasses overriding the old name must rename their override - Change
REGEXESfrom asetof(name, pattern)tuples to adict, so a duplicate name is a visible overwrite in the source instead of a nondeterministic winner at import time; code iteratingREGEXESdirectly now gets keys instead of pairs — use.items()(#227) - Change
CAPITALIZATION_EXCEPTIONSfrom a tuple of(key, value)tuples to adict; code iterating it directly now gets keys instead of pairs — use.items()(#233)
Behavior Changes (affect existing parse output)
- Add
bound_first_namesset toConstants; bound Arabic given-name prefixes (abdul,abu, etc.) now join forward to form a single first name (e.g."abdul salam ahmed salem"→first="abdul salam",middle="ahmed",last="salem"). Disable viaCONSTANTS.bound_first_names.clear(). Default-on: changes parsing output for names with these prefixes. (#150) - Treat an unrecognized, multi-letter token ending in a period in the leading title run (before the first name is set), e.g.
"Major.", as atitleinstead of afirstname; internal-period abbreviations ("E.T.") and single-letter initials ("J.") are unaffected. Default-on: changes parsing of names with a leading unknown period-abbreviation (closes #109) - Fix parsing writing back into the
Constantsit reads (usually the shared module-levelCONSTANTS): pieces derived while parsing a name — period-joined titles/suffixes like"Lt.Gov."and conjunction-joined pieces like"Mr. and Mrs."or"von und zu"— are now tracked per parse instead of being permanentlyadd()-ed to the config, so parse results no longer depend on which names were parsed earlier in the process and parsing no longer mutates shared state across threads - Fix
__hash__to lowercase the name like__eq__does, so equalHumanNameinstances hash equal and behave correctly in sets and dicts
New comparison methods
- Add
matches()andcomparison_key()for explicit name comparison:matches()compares parsed components case-insensitively (parsingstrarguments first, soname.matches("Smith, John")andname.matches("John Smith")both match) andcomparison_key()returns a hashable tuple of the seven components for dedup, dict keys, and sorting (#224)
New name fields
- Add a first-class
maidenfield andmaiden_delimiterstoConstants, so a delimiter (e.g. parenthesis) can be routed tomaideninstead ofnicknamefor alternate/maiden surnames, e.g."Baker (Johnson), Jenny"(closes #22) - Add
given_names(andgiven_names_list) attribute as aggregate of first and middle names, mirroringsurnames(closes #157) - Add
last_base,last_prefixes(and_listvariants) for splitting last-name prefix particles (tussenvoegsels) from the core surname (#130, #132)
New customization options
- Add
initials_separatortoConstantsandHumanNameto control spacing between consecutive initials within a name group (#171) - Add
suffix_delimitertoConstantsandHumanNamefor parsing suffixes separated by arbitrary delimiters, e.g."RN - CRNA"(#156) - Add
nickname_delimiterstoConstantsfor registering additional nickname-delimiter regex patterns at runtime, without subclassing (closes #110, #112) - Add
suffix_acronyms_ambiguoustoConstantsfor acronym suffixes that also read as given-name nicknames (e.g."JD","Ed"), used when disambiguating parenthesized/quoted content (#111)
International name support
- Add
patronymic_name_orderflag toConstantsandHumanNamefor opt-in detection and reordering of Russian formal-order names (Surname GivenName Patronymic) (#85) - Add Turkic (Azerbaijani/Central-Asian) patronymic detection to
patronymic_name_order, rotating the reversed 4-token formal shape (Surname GivenName PatronymicRoot Marker, e.g.oglu/qizi) into Western order (#185) - Add
middle_name_as_lastflag toConstantsandHumanNamefor opt-in folding of middle names into the last name, for naming systems with no middle-name concept (e.g. Arabic patronymic chaining) (#133) - Add
non_first_name_prefixestoConstants: a leading particle that is never a first name (e.g."de Mesnil","dos Santos") now parses as a surname with an empty first name, instead of treating the particle as the first name (closes #121) - Add international honorifics to
TITLES(#187) - Add German/Austrian nobility and ecclesiastical titles to
TITLES(closes #101) - Add German/Dutch last-name prefixes and title/degree suffixes; fix
join_on_conjunctions()to register multi-word prefix chains (e.g."von und zu") as prefixes, mirroring existing title handling (closes #18)
Parsing fixes
- Fix suffix boundary lookup for prefixed last names with a title before and after (e.g.
"dr Vincent van Gogh dr"producing a corrupted middle name) (closes #100) - Fix a repeated prefix word in a prefix chain (e.g.
"Juan de la de la Vega") silently dropping the earlier occurrence injoin_on_conjunctions(): value-basedpieces.index(prefix)lookups re-found the wrong occurrence once the list had already been mutated by prior joins; prefix positions are now tracked positionally instead of re-derived by value (closes #208) - Fix a trailing suffix being silently dropped after an empty comma segment, e.g.
"Doe, John,, Jr."losing the"Jr." - Fix degenerate comma input (a bare
","or an empty comma segment, e.g."Doe,, Jr.","John Doe, Jr.,,") leaving an empty-string member infirst_list,last_list, orsuffix_list; whitespace-only tokens assigned via the setters are dropped the same way - Fix suffix-shaped parenthesized/quoted content (e.g.
"(Ret)","(MBA)") being misclassified as a nickname instead of a suffix (closes #111) - Fix single-character symbol conjunctions (e.g.
"&","/") being ignored in short names (#173) - Fix recognition of single-letter roman numeral suffixes (e.g.
"I","V") in suffix-comma format (closes #136) - Fix recognition of trailing
suffix_not_acronyms(e.g."Jr.") in lastname-comma format (closes #144) - Fix missing comma between
'msc'and'mscmsm'insuffix_acronyms, which silently concatenated them into a bogus'mscmscmsm'entry (#111) - Fix
'apn aprn'split into separatesuffix_acronymsentries so each is recognized independently (closes #155)
Formatting and output fixes
- Fix
IndexErrorininitials()/initials_list()when a*_listattribute was assigned directly with an element containing unnormalized whitespace (e.g.name.middle_list = ['Q R']), bypassing the parser's whitespace normalization (closes #232) - Fix
initials()emitting a stray empty initial (e.g."J. . V.") -- or raisingTypeErrorwhenempty_attribute_defaultisNone-- for name parts with no initialable words, e.g. a prefix-only middle name like"de la" - Fix capitalization of suffix acronyms written with dots, e.g.
"M.D."(closes #141) - Fix extra whitespace before punctuation in
str()output when astring_formatfield is empty (closes #139) - Fix spurious leading space in surnames and empty token in suffix list after
capitalize()with an empty middle or suffix (#164)
API correctness and cleanup
- Fix the five non-cached-union
SetManager-backedConstantsattributes (first_name_titles,conjunctions,bound_first_names,non_first_name_prefixes,suffix_acronyms_ambiguous) accepting non-SetManagerassignment silently (e.g.constants.conjunctions = 'and'), degrading membership checks into substring tests with no error; assignment now raisesTypeErrorlike the four cached-union attributes already did (closes #241) - Fix
HumanName.Caccepting an invalidconstantsvalue on post-construction assignment (e.g.hn.C = 'garbage'), bypassing the constructor's validation and failing later with an unrelatedAttributeError;Cis now a property that validates on assignment too (closes #239) - Fix
TupleManager(andRegexTupleManager) accepting a bare string/bytes argument (raising a crypticdict-internalsValueError) or an iterable of 2-character strings (silently shredding each into a key/value pair, e.g.Constants(capitalization_exceptions=['ii'])becoming{'i': 'i'}); both now raiseTypeErrorwith a clear message (closes #242) - Fix
SetManager.__contains__being the one operation that didn't normalize (lowercase, strip leading/trailing periods) its operand, so e.g.'Dr.' in constants.titlescould returnFalseeven though the title was correctly configured; membership checks now normalize likeadd()/remove()/the constructor/the set operators (closes #244) - Fix a bare string passed to a set-backed
Constantsargument (e.g.Constants(titles='dr')), toSetManager, or as aSetManagerset-operator operand (e.g.constants.titles |= 'esq') being silently split into single characters, replacing or polluting the set and producing wrong parses with no error; it now raisesTypeErrorwith the suggested fix — wrap strings in a list, decodebytesfirst (closes #238) - Fix
SetManagerset operators and the constructor skipping the lowercase/strip-edge-periods normalization thatadd()applies:constants.titles |= ['Esq.']kept a raw'Esq.'the parser's lookups could never match,titles & ['Dr.']missed'dr', andConstants(titles=[...])stored raw elements that silently never matched; elements and operands are now normalized everywhere, and non-strelements (bytes,None, numbers) raiseTypeErrorinstead of crashing cryptically or being coerced - Fix the
constantsconstructor argument silently discardingConstantssubclass instances: the exact-type check replaced them with fresh defaults, throwing away the caller's configuration. Subclass instances are now used as given; anything that is neitherNonenor aConstantsinstance now raisesTypeErrorinstead of being silently swapped for defaults (closes #226) - Fix
Constantscustomizations, singleton identity, andTupleManagersubclass being lost acrosspickle/deepcopyround-trips (#167, #168, #169) - Fix
is_rootname()returning stale results afteradd()/remove()ontitles,prefixes,suffix_acronyms, orsuffix_not_acronyms(#166) - Fix the library logger calling
setLevel(logging.ERROR)on import, which silently discarded log records regardless of an application's own logging configuration; the logger now leaves its level atNOTSETand lets the application control verbosity (closes #228) - Minor internal cleanups: drop a dead length check in the initials helper, simplify double-wrapped
len(list(...))calls, and other small parser tidy-ups with no behavior change (closes #229) - Change
Constants.__repr__to report collection sizes and non-default scalar config, replacing the uninformative<Constants() instance>(#221)
- Deprecate
- 1.2.1 - June 19, 2026
- Fix
initials()interpolating the literalNonefor empty name parts whenempty_attribute_default = None(e.g."J. None D."); empty parts now render as an empty string and a fully-empty result returnsempty_attribute_default - Add
python -m nameparser "Name String"command-line helper that prints a parsed name - Reorganize the test suite from a single
tests.pyinto atests/pytest package
- Fix
- 1.2.0 - June 11, 2026
- Drop Python 2 and Python < 3.10 support; Python 3.10–3.14 now required
- Add type hints and type declarations (PEP 561
py.typedmarker) - Migrate build tooling to
pyproject.toml, dropsetup.py - Remove dead Python 2 compatibility shims (
ENCODINGconstant,next()aliases) - Modernize CI: uv-based workflow, trusted publishing to PyPI, Dependabot
- 1.1.3 - September 20, 2023
- Fix case when we have two same prefixes in the name ()#147)
- 1.1.2 - November 13, 2022
- Add support for attributes in constructor (#140)
- Make HumanName instances hashable (#138)
- Update repr for names with single quotes (#137)
- 1.1.1 - January 28, 2022
- Fix bug in is_suffix handling of lists (#129)
- 1.1.0 - January 3, 2022
- Add initials support (#128)
- Add more titles and prefixes (#120, #127, #128, #119)
- 1.0.6 - February 8, 2020
- Fix Python 3.8 syntax error (#104)
- 1.0.5 - Dec 12, 2019
- Fix suffix parsing bug in comma parts (#98)
- Fix deprecation warning on Python 3.7 (#94)
- Improved capitalization support of mixed case names (#90)
- Remove "elder" from titles (#96)
- Add post-nominal list from Wikipedia to suffixes (#93)
- 1.0.4 - June 26, 2019
- Better nickname handling of multiple single quotes (#86)
- full_name attribute now returns formatted string output instead of original string (#87)
- 1.0.3 - April 18, 2019
- fix sys.stdin usage when stdin doesn't exist (#82)
- support for escaping log entry arguments (#84)
- 1.0.2 - Oct 26, 2018
- Fix handling of only nickname and last name (#78)
- 1.0.1 - August 30, 2018
- Fix overzealous regex for "Ph. D." (#43)
- Add surnames attribute as aggregate of middle and last names
- 1.0.0 - August 30, 2018
- Fix support for nicknames in single quotes (#74)
- Change prefix handling to support prefixes on first names (#60)
- Fix prefix capitalization when not part of lastname (#70)
- Handle erroneous space in "Ph. D." (#43)
- 0.5.8 - August 19, 2018
- Add "Junior" to suffixes (#76)
- Add "dra" and "srta" to titles (#77)
- 0.5.7 - June 16, 2018
- Fix doc link (#73)
- Fix handling of "do" and "dos" Portuguese prefixes (#71, #72)
- 0.5.6 - January 15, 2018
- Fix python version check (#64)
- 0.5.5 - January 10, 2018
- Support J.D. as suffix and Wm. as title
- 0.5.4 - December 10, 2017
- Add Dr to suffixes (#62)
- Add the full set of Italian derivatives from "di" (#59)
- Add parameter to specify the encoding of strings added to constants, use 'UTF-8' as fallback (#67)
- Fix handling of names composed entirely of conjunctions (#66)
- 0.5.3 - June 27, 2017
- Remove emojis from initial string by default with option to include emojis (#58)
- 0.5.2 - March 19, 2017
- Added names scrapped from VIAF data, thanks daryanypl (#57)
- 0.5.1 - August 12, 2016
- Fix error for names that end with conjunction (#54)
- 0.5.0 - August 4, 2016
- Refactor join_on_conjunctions(), fix #53
- 0.4.1 - July 25, 2016
- Remove "bishop" from titles because it also could be a first name
- Fix handling of lastname prefixes with periods, e.g. "Jane St. John" (#50)
- 0.4.0 - June 2, 2016
- Remove "CONSTANTS.suffixes", replaced by "suffix_acronyms" and "suffix_not_acronyms" (#49)
- Add "du" to prefixes
- Add "sheikh" variations to titles
- Add parameter to force capitalization of mixed case strings
- 0.3.16 - March 24, 2016
- Clarify LGPL licence version (#47)
- Skip pickle tests if pickle not installed (#48)
- 0.3.15 - March 21, 2016
- Fix string format when empty_attribute_default = None (#45)
- Include tests in release source tarball (#46)
- 0.3.14 - March 18, 2016
- Add CONSTANTS.empty_attribute_default to customize value returned for empty attributes (#44)
- 0.3.13 - March 14, 2016
- Improve string format handling (#41)
- 0.3.12 - March 13, 2016
- Fix first name clash with suffixes (#42)
- Fix encoding of constants added via the python shell
- Add "MSC" to suffixes, fix #41
- 0.3.11 - October 17, 2015
- Fix bug capitalization exceptions (#39)
- 0.3.10 - September 19, 2015
- Fix encoding of byte strings on python 2.x (#37)
- 0.3.9 - September 5, 2015
- Separate suffixes that are acronyms to handle periods differently, fixes #29, #21
- Don't find titles after first name is filled, fixes (#27)
- Add "chair" titles (#37)
- 0.3.8 - September 2, 2015
- Use regex to check for roman numerals at end of name (#36)
- Add DVM to suffixes
- 0.3.7 - August 30, 2015
- Speed improvement, 3x faster
- Make HumanName instances pickleable
- 0.3.6 - August 6, 2015
- Fix strings that start with conjunctions (#20)
- handle assigning lists of names to a name attribute
- support dictionary-like assignment of name attributes
- 0.3.5 - August 4, 2015
- Fix handling of string encoding in python 2.x (#34)
- Add support for dictionary key access, e.g. name['first']
- add 'santa' to prefixes, add 'cpa', 'csm', 'phr', 'pmp' to suffixes (#35)
- Fix prefixes before multi-part last names (#23)
- Fix capitalization bug (#30)
- 0.3.4 - March 1, 2015
- Fix #24, handle first name also a prefix
- Fix #26, last name comma format when lastname is also a title
- 0.3.3 - Aug 4, 2014
- Allow suffixes to be chained (#8)
- Handle trailing suffix in last name comma format (#3). Removes support for titles with periods but no spaces in them, e.g. "Lt.Gen.". (#21)
- 0.3.2 - July 16, 2014
- Retain original string in "original" attribute.
- Collapse white space when using custom string format.
- Fix #19, single comma name format may have trailing suffix
- 0.3.1 - July 5, 2014
- Fix Pypi package, include new config module.
- 0.3.0 - July 4, 2014
- Refactor configuration to simplify modifications to constants (backwards incompatible)
- use unicode_literals to simplify Python 2 & 3 support.
- Generate documentation using sphinx and host on readthedocs.
- 0.2.10 - May 6, 2014
- If name is only a title and one part, assume it's a last name instead of a first name, with exceptions for some titles like 'Sir'. (#7).
- Add some judicial and other common titles. (#9)
- 0.2.9 - Apr 1, 2014
- Add a new nickname attribute containing anything in parenthesis or double quotes (Issue 33).
- 0.2.8 - Oct 25, 2013
- Add support for Python 3.3+. Thanks to @corbinbs.
- 0.2.7 - Feb 13, 2013
- Fix bug with multiple conjunctions in title
- add legal and crown titles
- 0.2.6 - Feb 12, 2013
- Fix python 2.6 import error on logging.NullHandler
- 0.2.5 - Feb 11, 2013
- Set logging handler to NullHandler
- Remove 'ben' from PREFIXES because it's more common as a name than a prefix.
- Deprecate BlankHumanNameError. Do not raise exceptions if full_name is empty string.
- 0.2.4 - Feb 10, 2013
- Adjust logging, don't set basicConfig. Fix Issue 10 and Issue 26.
- Fix handling of single lower case initials that are also conjunctions, e.g. "john e smith". Re Issue 11.
- Fix handling of initials with no space separation, e.g. "E.T. Jones". Fix #11.
- Do not remove period from first name, when present.
- Remove 'e' from PREFIXES because it is handled as a conjunction.
- Python 2.7+ required to run the tests. Mark known failures.
- tests/test.py can now take an optional name argument that will return repr() for that name.
0.2.3 - Fix overzealous "Mac" regex
0.2.2 - Fix parsing error
- 0.2.0
- Significant refactor of parsing logic. Handle conjunctions and prefixes before parsing into attribute buckets.
- Support attribute overriding by assignment.
- Support multiple titles.
- Lowercase titles constants to fix bug with comparison.
- Move documentation to README.rst, add release log.
0.1.4 - Use set() in constants for improved speed. setuptools compatibility - sketerpot
0.1.3 - Add capitalization feature - twotwo
0.1.2 - Add slice support