Release Log¶
2.0.0 - unreleased
2.0.0rc1 released 2026-07-23 for testing ahead of the final 2.0.0. Install it with
pip install --pre nameparser(a pre-release is not selected by a plainpip install); 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 Migrating from HumanName 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 mutatedAdd
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_exceptionsAdd
Policy, the frozen behavior object:name_order,patronymic_rules,middle_as_family,nickname_delimiters,maiden_delimiters,extra_suffix_delimiters,lenient_comma_suffixes,strip_emojiandstrip_bidiAdd
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 parserAdd
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 (closes the configuration half of #270)Add
PatronymicRulewith the membersEAST_SLAVICandTURKIC. v1’s singlepatronymic_name_orderflag enabled both detectors at once;Policy(patronymic_rules=...)lets you enable either one aloneAdd
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 realFalseorNoneAdd
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 fromAdd
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 nothingAdd
ParsedNameoutput and comparison methods:render(spec),initials(),capitalized()(which returns a new value rather than mutating in place),as_dict(),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 supportedRemove
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 keyRemove 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/PolicyinsteadRemove 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 plainsetRemove
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 attributesRemove
Constants.empty_attribute_default(#255): empty fields are always''. Assigning it raisesAttributeError; a pickle carrying the key still loads, with the value ignoredRemove
constants=None(#261): bothHumanName(..., constants=None)andhn.C = NoneraiseTypeError. UseConstants()for library defaults orCONSTANTS.copy()for a private snapshotRemove silent unknown-key access on the mapping managers (#256):
CONSTANTS.capitalization_exceptions.typoandCONSTANTS.regexes.typoraiseAttributeErrornaming the miss, instead of returningNone/EMPTY_REGEX(the mapping manager also lists the known keys)..get()remains available on both for intentional soft accessRemove support for
Constantspickles written by nameparser 1.2.x or earlier (#279): loading one raisesValueErrortelling you to re-pickle under 1.3/1.4 firstRemove the dead
regexes.no_vowelspattern (#268) and theConstants.suffixes_prefixes_titlescached union; neither was read by the parserChange
HumanName’s*_listattributes to read-only properties: they remain readable snapshots, butname.first_list = [...]now raisesAttributeErrorChange the
Constantsconstructor to keyword-only; positional construction no longer works
Behavior Changes
Recognize maiden-name markers –
née,nee,born,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 nameFix a lone recognized trailing suffix with no comma being routed to
first/last:"Johnson PhD"and"Mr. Johnson PhD"now keep the suffix insuffixFix 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.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 truthyFold 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 Migrating from HumanNameAdd
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 oneChange
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 parityChange 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, unchangedChange 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 changesChange 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 differsCorrect 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 (closes the pack half of #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 nameAdd 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 middleAdd 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.0Note 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
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 How the parser works, Locale packs and Migrating from HumanName – 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.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 unaffectedDeprecate 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 accessDeprecate
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 unaffectedDeprecate
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 awayDeprecate 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.0Fix 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)
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)
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 itDeprecate
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)insteadAdd
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 warnFix
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 parseRemove
__ne__; Python 3 derives!=from__eq__automaticallyChange 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 overrideChange
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 threadsFix
__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 wayFix 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 coercedFix 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)
- 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_defaultAdd
python -m nameparser "Name String"command-line helper that prints a parsed nameReorganize the test suite from a single
tests.pyinto atests/pytest package
- 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.pyRemove 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