Library / SDK
derek73/python-nameparser avatar
derek73/python-nameparser

nameparser: parsing human names into their parts

A simple Python module for parsing human names into their individual components

720 stars118 forksPythonNOASSERTION

At a glance

What is it?
nameparser is an LGPL licensed Python module that splits a human name into seven fields, title, given, middle, family, suffix, nickname and maiden, with immutable results, composable configuration and opt-in locale packs for East and South Asian naming. The judgement is that the hard part is not the fields but the conventions, and that the 2.5 release is a list of the awkward conventions the author has finally handled.
Who is it for?
nameparser fits a Python codebase that has to store, sort or match people by name and has run into the fact that names are not a first name and a last name, because the library encodes the conventions instead of leaving them to a regular expression.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Seven fields, immutable results

nameparser parses human names into seven fields, and the list is the whole model: title, given, middle, family, suffix, nickname and maiden.

The readme is explicit about three properties of that model. The results are immutable, the configuration is composable, and the locale packs are opt-in. Those three words describe a library that was rewritten rather than patched, because a name parser that lets you mutate a parsed name is a name parser whose results depend on the order you touched it.

The quick start shows what a result looks like:

python
>>> from nameparser import parse
>>> name = parse("Dr. Juan Q. Xavier de la Vega III (Doc Vega)")
>>> name
<ParsedName: [
    title: 'Dr.'
    given: 'Juan'
    middle: 'Q. Xavier'
    family: 'de la Vega'
    suffix: 'III'
    nickname: 'Doc Vega'
]>

One name produces a title, a given name, a compound middle name, a family name with a particle, a suffix and a nickname in parentheses. The parser also exposes the pieces in the shapes a caller usually wants, the base of the family name and its particles separately, the given names and the surnames as joined strings, and a render method that formats the fields back into a string with a template, so parse("Dr. Juan...").render("{family}, {given}") gives back the inverted form.

Version 2.5 is a list of awkward conventions

The release notes quoted in the readme are the best description of what the library does, because each item is a naming convention that broke something.

The 2.5 release reads junior and senior written in 45 languages, so a name like Antonio Pavlović mlađi is understood rather than truncated. It keeps a title written before a family comma out of the surname, so Dr. Smith, John is parsed with Dr. as the title and Smith as the family name, rather than treating Smith, John as one thing. It reads Chinese and Japanese credential and office words, so a name ending in an office title yields the title rather than gluing it to the name. And it gives a lone first name followed by V, I or X its last name back, which is the rule that makes Malcolm X parse with X as the family name.

Each of these is a case where a simpler parser produces a wrong answer quietly, and a wrong answer in a name is the kind of bug that is discovered years later in a letter to the wrong person.

The release log is published on the documentation site, and the readme asks for an issue for anything that parses wrong, which for a library whose entire job is judgement calls is the right way to collect the remaining cases.

Capitalisation is treated as evidence

One of the oldest conventions in Western name handling is that capitalisation marks a credential, and the library follows it.

The readme shows it as a series of contrasts. parse("Jack MA").suffix is MA, with the comment that capitals mark a credential, while parse("Jack Ma").family is Ma, with the comment that it is a surname. And parse("Jane Smith, PhD MA").suffix is PhD MA, so several credentials after a family comma are gathered into the suffix field.

This is a heuristic and it is a consequential one. A person whose surname is typed in capitals by a form letter will be parsed as having a credential suffix, and a person whose credential is typed in lower case will not. The library makes a decision, and it makes it consistently, which is more than a regular expression would do.

It is also the kind of rule that a caller sometimes wants to override, and that is what the customising documentation is for, alongside the vocabulary and the behaviour settings. The readme describes the customisation page as covering vocabulary, behaviour and presentation, so the three axes of the parser are separately adjustable.

East Asian names need you to say which language they are in

The readme is careful about the scripts that do not fit the Western order, and the care is the point.

Chinese, Japanese and Korean names written in their own scripts are read family first, unspaced Korean names are split against the census surname list, and CJK honorifics are recognised. The examples show the mechanism. A Korean name written without a space, 김민준, has the family name 김, split using the census surname list. A Japanese name with kanji and kana, 高橋 みなみ, has the family name 高橋. The Korean honorific written against the name, 김민준씨, puts 씨 in the suffix field.

The catch is that Han script does not say which language it is, so the split is opt-in. The readme shows the mechanism for choosing a locale, with a Chinese parser and a Russian parser built from a locale pack, and a list of the packs that exist:

python
>>> from nameparser import locales, parser_for
>>> chinese = parser_for(locales.ZH)
>>> chinese.parse("毛泽东").family
'毛'
>>> russian = parser_for(locales.RU)
>>> russian.parse("Сидоров Иван Петрович").family
'Сидоров'
>>> locales.available()
('ja', 'ru', 'tr_az', 'zh')

Arabic and Cyrillic work without a pack. The Arabic example chains the bin particle onto the family name, and the Cyrillic example reads г-н as a title. The packs exist for the cases where the script alone is not enough, and the availability list shows how few there are, four of them.

Migrating from 1.x, and the change that fails silently

The compatibility section is the most useful thing in the readme for anyone on the old API, and it contains one warning that deserves attention.

Existing HumanName code from 1.x keeps working through 2.x, and most of it needs no changes. The HumanName class and the CONSTANTS object keep the same imports, the same attributes and the same mutation API. What 2.0 removes is the batch of deprecations that 1.3 and 1.4 announced, so if your test suite runs clean on 1.4 with the deprecation warnings promoted to errors, you are nearly done.

Then the readme names two things that check will not catch. Four removals that 1.4 never warned about, three of which raise on contact and the fourth of which only warns. And one that changes results silently, where name == "John Smith" is now False.

That last one is the interesting case. The comparison between a parsed name and a string used to be true for the obvious name, and it is now False, because the comparison is on the parsed representation and the parsed representation of a name is not the string you typed. Nothing raises, nothing warns, and a test that only checked the equality of a parsed name against a literal passes or fails depending on whether you updated it.

Later 2.x releases add a few breaks of their own, and the migration guide is said to cover all of them and to translate a 1.x customisation into the new API where that is convenient.

Zero dependencies, and the vocabulary shipped as data

The packaging configuration says something about how the library is meant to be used.

The dependencies list is empty. There is no runtime dependency at all, and the only optional extra is for Japanese kanji name segmentation, namedivider-python at 0.4 or newer, which is the floor its own API dates from. The comment in the manifest is explicit that the extra is optional and never bundled, so that the core stays zero-dependency, and the Japanese segmentation is wrapped by a locale function so a user who wants it opts in.

For a library that other people embed in their services, that is a deliberate choice. A name parser that drags in a numerical stack or a machine learning dependency becomes a dependency conflict in somebody else's project, and the fix is to keep the vocabulary small enough to ship as a table of tokens.

How that vocabulary is delivered is also specified. The package data includes py.typed and the vocab directory of TOML files, and the comment says the per-word vocabulary ships as data for tools and later phases to read, while the parser itself reads the compiled module and never the TOML. So the word lists are data, inspectable and diffable, and the parser runs against a compiled form of them, which is the arrangement that lets a user customise the vocabulary without a build step.

The development dependencies are conventional for a typed Python project, with pytest, mypy, ruff, sphinx and the furo theme for the documentation, plus dill for serialising objects in tests. The version is dynamic, read from the package's own version module at build time.

A command line tool, a documentation set and a fast release line

The last pieces of the readme are the practical ones.

There is a command line interface, invoked as python -m nameparser with a JSON option, so a name can be parsed from a shell without writing a Python script. For a library whose input is a line of text and whose output is a structure, that is exactly the shape where a command line tool earns its place, and the JSON flag makes it usable from a script that does not want to parse a repr.

The documentation is organised into three pages that map onto the three properties of the library. The using the parser page is the tour, covering the input shapes, the aggregates, the rendering, the comparison, the ambiguities and the tokens. The customising page covers the vocabulary, the behaviour and the presentation. The locale packs page describes the opt-in bundles for East Slavic patronymics, Turkic markers and the others.

The release history is unusually fast. Version 2.4.1 was published on 2026-10-13, 2.4.2 on 2026-10-14 and 2.5.0 on 2026-10-19, three releases in a week, with the last push to the repository on 2026-10-20. The project is LGPL licensed, with the details in the LICENSE file, and the repository carries a readthedocs configuration, a CITATION.cff for citation metadata, an authors file and a uv lock file, which is the modern Python toolchain.

Editorial conclusion

nameparser fits a Python codebase that has to store, sort or match people by name and has run into the fact that names are not a first name and a last name, because the library encodes the conventions instead of leaving them to a regular expression. It is the wrong choice if you need to segment Japanese kanji names, because that part needs an optional dependency and a separate locale, and it is the wrong choice if your codebase is on the 1.x API, because the 2.x line removes the deprecations and changes one comparison result silently. Before you upgrade, read the migration guide and run your test suite with the deprecation warnings turned into errors, and before you parse non-Latin names, choose the locale explicitly rather than letting the default guess.

Frequently asked questions

What does the nameparser module do?

It parses human names into seven fields, title, given, middle, family, suffix, nickname and maiden, with immutable results, composable configuration and opt-in locale packs. The result also exposes family base and particles, given names and surnames as joined strings, and a render method for formatting the fields back into a string.

How does nameparser handle East Asian names?

Names written in Chinese, Japanese and Korean scripts are read family first, unspaced Korean names are split against the census surname list, and CJK honorifics are recognised. Because Han script does not indicate the language, the split is opt-in through a locale pack, built with parser_for and the locales module.

Does nameparser depend on anything at runtime?

No. The dependency list is empty, and the only optional extra is for Japanese kanji name segmentation, namedivider-python at 0.4 or newer, which is never bundled so the core stays dependency free. The per-word vocabulary ships as TOML data while the parser reads a compiled module.

What breaks when upgrading from nameparser 1.x?

The HumanName class and the CONSTANTS object keep working with the same imports, attributes and mutation API, and 2.0 removes the deprecations announced in 1.3 and 1.4. The readme warns that four removals were never warned about and that one change is silent, since a parsed name compared to a string literal is now False.

Official sources

  1. derek73/python-nameparser on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/derek73-python-nameparser.svg)](https://hysenlabs.com/projects/derek73-python-nameparser)

Was this useful?