Skip to content

v2.0.0rc1

Pre-release
Pre-release

Choose a tag to compare

@derek73 derek73 released this 24 Jul 04:34
6a35b49

This is a release candidate. A plain pip install nameparser will not pick it up — pre-releases are opt-in:

pip install --pre nameparser==2.0.0rc1

Please try it against your real code and report anything the migration missed on #284. The final 2.0.0 follows once the RC settles.

nameparser 2.0 adds a new parsing API alongside HumanName. HumanName keeps working unchanged as a compatibility facade over the same pipeline, and stays through the 2.x series — if your code runs warning-free on 1.4.0, most of 2.0 will not affect you.

What's new: the 2.0 API

parse(text) returns an immutable ParsedName whose seven fields are named for what they aregiven, family — rather than where they sit in a Western name:

from nameparser import parse

name = parse("Dr. Juan Q. Xavier de la Vega III")
name.given          # 'Juan'
name.family         # 'de la Vega'
name.family_base    # 'Vega'   (particles split off)
name.suffix         # 'III'
  • Frozen, explicit configuration. A Lexicon of vocabulary and a Policy of behavior replace the mutable global Constants. Both are immutable, hashable value objects; parsing is a pure function of the text, a lexicon, and a policy — nothing global is read or mutated. Build a reusable Parser(lexicon=..., policy=...).
  • Family-first name order, as written rather than reordered by hand — Policy(name_order=FAMILY_FIRST) and FAMILY_FIRST_GIVEN_LAST (#270).
  • Locale packs. parser_for(locales.RU) folds in East Slavic patronymic order; locales.TR_AZ covers Turkic markers. Packs are pure data, they compose, and they're never auto-detected.
  • New recognition: maiden markers (née, geb. → a maiden field, #274), typographic nickname delimiters (smart quotes, guillemets, CJK brackets, #273), and non-Latin vocabulary — Cyrillic, Greek, Arabic, Hebrew titles and particles (#269).
  • Honest ambiguity. When a parse has to guess, it says so on ParsedName.ambiguities instead of guessing silently — e.g. "John Smith MA" reports that MA was read as a credential rather than a surname. A reading the vocabulary settles on its own reports nothing.
  • Exact provenance. Every field is backed by Tokens carrying (start, end) offsets into the original string, reachable via tokens_for(Role.GIVEN) — so you can highlight or re-slice the input a parse came from.
  • Fully typed (PEP 561), checked under strict mypy, with no runtime dependencies.

Breaking changes — read before upgrading

The removals are the deprecations announced in 1.3.0 and 1.4.0 coming due. The one that matters most:

  • ⚠️ HumanName.__eq__ and __hash__ are removed (#223). Instances now compare and hash by identity, so HumanName("John Smith") == "John Smith" is now False where 1.x returned True. This changes results silently rather than raising — it's the one removal that can slip into production unnoticed. Use matches() to compare names and comparison_key() as a dict/sort key.

Also: minimum Python is now 3.11 (#257); bytes input is gone (decode first, #245); regex configuration, subclass parsing hooks, HumanName slicing, and empty_attribute_default are removed. Each raises with a migration hint (except the __eq__ case above).

Migrating

HumanName is unchanged, so most users need to do nothing yet. If you customized Constants, subclassed HumanName, or compared names with ==, see the migration guide for the field-by-field and attribute-by-attribute map.