nameparser 2.6.0__tar.gz → 2.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nameparser-2.6.0/nameparser.egg-info → nameparser-2.7.0}/PKG-INFO +9 -6
- {nameparser-2.6.0 → nameparser-2.7.0}/README.rst +8 -5
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/__init__.py +5 -1
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_config_shim.py +91 -5
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_lexicon.py +122 -10
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_pipeline/_assemble.py +36 -3
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_pipeline/_assign.py +490 -86
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_pipeline/_classify.py +154 -34
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_pipeline/_comma.py +142 -52
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_pipeline/_extract.py +4 -3
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_pipeline/_group.py +142 -37
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_pipeline/_pieces.py +405 -99
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_pipeline/_post_rules.py +116 -43
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_pipeline/_segment.py +4 -4
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_pipeline/_state.py +93 -5
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_pipeline/_vocab.py +632 -72
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_policy.py +12 -8
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_render.py +60 -1
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_types.py +189 -30
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_version.py +4 -4
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/config/_compiled.py +437 -23
- nameparser-2.7.0/nameparser/config/_titles_2_6_0.py +826 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/config/capitalization.py +2 -1
- nameparser-2.7.0/nameparser/config/given_names.py +18 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/config/particles.py +8 -7
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/config/suffixes.py +23 -2
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/config/titles.py +42 -3
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/ar.toml +25 -12
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/bn.toml +1 -2
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/cs.toml +1 -3
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/de.toml +14 -9
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/en.toml +168 -91
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/es.toml +12 -10
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/fil.toml +9 -1
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/fr.toml +12 -12
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/ga.toml +2 -2
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/gd.toml +1 -1
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/he.toml +3 -2
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/hi.toml +1 -2
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/it.toml +11 -10
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/ms.toml +3 -3
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/mul.toml +1 -1
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/nl.toml +23 -20
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/pa.toml +2 -1
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/pl.toml +2 -1
- nameparser-2.7.0/nameparser/vocab/pt-BR.toml +49 -0
- nameparser-2.7.0/nameparser/vocab/pt.toml +101 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/sr.toml +1 -2
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/sw.toml +1 -2
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/tr.toml +3 -1
- nameparser-2.7.0/nameparser/vocab/und.toml +21 -0
- {nameparser-2.6.0 → nameparser-2.7.0/nameparser.egg-info}/PKG-INFO +9 -6
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser.egg-info/SOURCES.txt +5 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_conjunctions.py +2 -2
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_first_name.py +11 -2
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_suffixes.py +49 -34
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_titles.py +4 -4
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/_differential_fixtures.py +35 -5
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/cases.py +2024 -457
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/pipeline/test_assemble.py +2 -1
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/pipeline/test_assign.py +51 -17
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/pipeline/test_classify.py +4 -4
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/pipeline/test_comma.py +16 -12
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/pipeline/test_group.py +36 -27
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/pipeline/test_pieces.py +44 -10
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/pipeline/test_post_rules.py +13 -6
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/pipeline/test_script_segment.py +14 -11
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/pipeline/test_state.py +7 -6
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/pipeline/test_vocab.py +311 -19
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/rules_doc.py +5 -4
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_benchmark.py +62 -2
- nameparser-2.7.0/tests/v2/test_cases.py +814 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_config_shim.py +191 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_contracts.py +3 -1
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_differential.py +18 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_facade.py +21 -7
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_facade_cases.py +7 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_lexicon.py +22 -8
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_local_agreement.py +173 -3
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_local_corpora.py +404 -12
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_locales.py +4 -3
- nameparser-2.7.0/tests/v2/test_measure.py +178 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_name_usage_tool.py +7 -5
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_parser.py +156 -34
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_policy.py +1 -1
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_properties.py +293 -21
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_regex_sync.py +24 -2
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_render.py +53 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_reprs.py +6 -1
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_snapshot.py +78 -4
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_types.py +140 -5
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_vocab.py +304 -51
- nameparser-2.7.0/tests/v2/test_word_report.py +309 -0
- nameparser-2.6.0/nameparser/vocab/pt.toml +0 -70
- nameparser-2.6.0/nameparser/vocab/und.toml +0 -21
- nameparser-2.6.0/tests/v2/test_cases.py +0 -415
- {nameparser-2.6.0 → nameparser-2.7.0}/AUTHORS +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/LICENSE +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/MANIFEST.in +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/__main__.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_facade.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_locale.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_parser.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_pipeline/__init__.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_pipeline/_script_segment.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/_pipeline/_tokenize.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/config/__init__.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/config/_deprecated.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/config/_invariants.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/config/bound_first_names.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/config/bound_given_names.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/config/conjunctions.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/config/maiden_markers.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/config/prefixes.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/config/regexes.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/config/surnames.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/locales/__init__.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/locales/ja.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/locales/ru.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/locales/tr_az.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/locales/zh.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/parser.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/py.typed +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/util.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/ak.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/am.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/as.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/be.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/bg.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/bin.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/bo.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/bs.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/ca.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/da.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/el.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/et.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/fa.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/ff.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/fi.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/gu.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/ha.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/hr.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/hu.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/hy.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/id.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/is.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/ja.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/ka.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/kk.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/km.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/kn.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/ko.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/kok.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/lo.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/lt.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/lv.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/mi.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/mk.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/ml.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/mn.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/mr.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/my.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/nb.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/ne.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/nn.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/or.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/ro.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/ru.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/sd.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/shn.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/si.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/sk.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/sl.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/sv.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/ta.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/te.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/th.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/ti.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/uk.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/ur.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/vi.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/yi.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/yo.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/zh.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser/vocab/zu.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser.egg-info/dependency_links.txt +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser.egg-info/requires.txt +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/nameparser.egg-info/top_level.txt +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/pyproject.toml +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/setup.cfg +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/__init__.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/base.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/conftest.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_bound_given_names.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_brute_force.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_capitalization.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_comma_variants.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_constants.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_east_slavic_patronymic_order.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_initials.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_middle_name_as_last.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_nicknames.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_output_format.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_particles.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_python_api.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_turkic_patronymic_order.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/test_variations.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/__init__.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/conftest.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/design_docs.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/pipeline/__init__.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/pipeline/test_extract.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/pipeline/test_segment.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/pipeline/test_tokenize.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/rules_budget.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/rules_skeleton.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_cldr_agreement.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_cldr_corpus.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_cli.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_config_aliases.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_corpora.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_corpus_build.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_design_docs.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_doc_citations.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_doc_spellings.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_gnd_corpus.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_layering.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_local_jrc.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_locale.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_order_correspondence.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_rules_budget.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_rules_doc.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_rules_doc_grammar.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_rules_skeleton.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_wikidata_corpus.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_wikidata_native_corpus.py +0 -0
- {nameparser-2.6.0 → nameparser-2.7.0}/tests/v2/test_zh_surnames_tool.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nameparser
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.0
|
|
4
4
|
Summary: A simple Python module for parsing human names into their individual components.
|
|
5
5
|
Author-email: Derek Gulbranson <derek73@gmail.com>
|
|
6
6
|
License-Expression: LGPL-2.1-or-later
|
|
@@ -35,11 +35,14 @@ nameparser parses human names into seven fields — title, given, middle,
|
|
|
35
35
|
family, suffix, nickname, maiden. Results are immutable, configuration is
|
|
36
36
|
composable, and locale packs are opt-in.
|
|
37
37
|
|
|
38
|
-
📣 **nameparser 2.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
📣 **nameparser 2.7 is out.** It is the first release working towards
|
|
39
|
+
the new language and vocabulary model described in the `vocabulary
|
|
40
|
+
model RFC <https://github.com/derek73/python-nameparser/blob/master/docs/design/vocabulary-model-rfc.md>`__.
|
|
41
|
+
It introduces more nuanced handling of titles and particles
|
|
42
|
+
(``Chief Justice John Roberts`` reads the title ``Chief Justice``,
|
|
43
|
+
while ``Justice Smith`` keeps the given name ``Justice``), adjusts the
|
|
44
|
+
default vocabulary to better align with example name corpora, and
|
|
45
|
+
surfaces alternative name readings in the published API. The `release log
|
|
43
46
|
<https://nameparser.readthedocs.io/en/latest/release_log.html>`__ has
|
|
44
47
|
the details.
|
|
45
48
|
|
|
@@ -7,11 +7,14 @@ nameparser parses human names into seven fields — title, given, middle,
|
|
|
7
7
|
family, suffix, nickname, maiden. Results are immutable, configuration is
|
|
8
8
|
composable, and locale packs are opt-in.
|
|
9
9
|
|
|
10
|
-
📣 **nameparser 2.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
📣 **nameparser 2.7 is out.** It is the first release working towards
|
|
11
|
+
the new language and vocabulary model described in the `vocabulary
|
|
12
|
+
model RFC <https://github.com/derek73/python-nameparser/blob/master/docs/design/vocabulary-model-rfc.md>`__.
|
|
13
|
+
It introduces more nuanced handling of titles and particles
|
|
14
|
+
(``Chief Justice John Roberts`` reads the title ``Chief Justice``,
|
|
15
|
+
while ``Justice Smith`` keeps the given name ``Justice``), adjusts the
|
|
16
|
+
default vocabulary to better align with example name corpora, and
|
|
17
|
+
surfaces alternative name readings in the published API. The `release log
|
|
15
18
|
<https://nameparser.readthedocs.io/en/latest/release_log.html>`__ has
|
|
16
19
|
the details.
|
|
17
20
|
|
|
@@ -27,6 +27,9 @@ from nameparser._types import (
|
|
|
27
27
|
Ambiguity,
|
|
28
28
|
AmbiguityKind,
|
|
29
29
|
ParsedName,
|
|
30
|
+
Reading,
|
|
31
|
+
ReadingKind,
|
|
32
|
+
ReadingSource,
|
|
30
33
|
Role,
|
|
31
34
|
Segmentation,
|
|
32
35
|
Segmenter,
|
|
@@ -38,7 +41,8 @@ __all__ = [
|
|
|
38
41
|
# v1 (compatibility layer)
|
|
39
42
|
"HumanName",
|
|
40
43
|
# v2 core
|
|
41
|
-
"Span", "Role", "Token", "Ambiguity", "AmbiguityKind", "
|
|
44
|
+
"Span", "Role", "Token", "Ambiguity", "AmbiguityKind", "Reading",
|
|
45
|
+
"ReadingKind", "ReadingSource", "ParsedName",
|
|
42
46
|
"STABLE_TAGS", "Segmentation", "Segmenter",
|
|
43
47
|
"Lexicon", "Policy", "PolicyPatch", "PatronymicRule", "Script", "UNSET",
|
|
44
48
|
"CapsSuffixes",
|
|
@@ -731,13 +731,19 @@ def _default_vocab() -> dict[str, frozenset[str]]:
|
|
|
731
731
|
from nameparser.config.suffixes import (
|
|
732
732
|
SUFFIX_ACRONYMS, SUFFIX_ACRONYMS_AMBIGUOUS, SUFFIX_WORDS,
|
|
733
733
|
)
|
|
734
|
-
from nameparser.config.
|
|
734
|
+
from nameparser.config._titles_2_6_0 import TITLES_2_6_0
|
|
735
|
+
from nameparser.config.titles import (
|
|
736
|
+
GIVEN_NAME_TITLES, TITLES, TITLES_AMBIGUOUS)
|
|
735
737
|
return {
|
|
736
738
|
"prefixes": PARTICLES,
|
|
737
739
|
"suffix_acronyms": SUFFIX_ACRONYMS,
|
|
738
740
|
"suffix_not_acronyms": SUFFIX_WORDS,
|
|
739
741
|
"suffix_acronyms_ambiguous": SUFFIX_ACRONYMS_AMBIGUOUS,
|
|
740
|
-
|
|
742
|
+
# 2.6.0's titles, less any word that is no title now: one a
|
|
743
|
+
# later release made a title only inside a title phrase
|
|
744
|
+
# (rules/H9) stays a v1 title, so remove() and `in` answer as
|
|
745
|
+
# they did; _build_snapshot reads it by the phrase
|
|
746
|
+
"titles": TITLES | (TITLES_2_6_0 & TITLES_AMBIGUOUS),
|
|
741
747
|
"first_name_titles": GIVEN_NAME_TITLES,
|
|
742
748
|
"conjunctions": CONJUNCTIONS,
|
|
743
749
|
"bound_first_names": BOUND_GIVEN_NAMES,
|
|
@@ -1045,10 +1051,15 @@ class Constants:
|
|
|
1045
1051
|
removal path.
|
|
1046
1052
|
"""
|
|
1047
1053
|
from nameparser.config.conjunctions import CONJUNCTIONS_AMBIGUOUS
|
|
1054
|
+
from nameparser.config.given_names import GIVEN_NAME_ABBREVIATIONS
|
|
1048
1055
|
from nameparser.config.maiden_markers import MAIDEN_MARKERS
|
|
1049
|
-
from nameparser.config.suffixes import
|
|
1056
|
+
from nameparser.config.suffixes import (
|
|
1057
|
+
AGNOMENS, GLUED_HONORIFICS, SUFFIX_WORDS_AMBIGUOUS)
|
|
1050
1058
|
from nameparser.config.surnames import KOREAN_SURNAMES
|
|
1051
|
-
from nameparser.config.
|
|
1059
|
+
from nameparser.config._titles_2_6_0 import TITLES_2_6_0
|
|
1060
|
+
from nameparser.config.titles import (
|
|
1061
|
+
AFTER_GIVEN_TITLES, TITLE_MODIFIERS, TITLE_PHRASES, TITLE_STYLES,
|
|
1062
|
+
TITLES_AMBIGUOUS)
|
|
1052
1063
|
# Every set field and every capitalization_exceptions key passes
|
|
1053
1064
|
# _v1_matchable BEFORE any set algebra below: an entry v1 could
|
|
1054
1065
|
# never match, or one Lexicon folds to empty (#582), is dropped
|
|
@@ -1071,6 +1082,27 @@ class Constants:
|
|
|
1071
1082
|
conjunctions = matchable("conjunctions")
|
|
1072
1083
|
bound = matchable("bound_first_names")
|
|
1073
1084
|
titles = matchable("titles")
|
|
1085
|
+
# A title 2.6.0 listed that a later release made a title only
|
|
1086
|
+
# inside a title phrase (rules/H9) is in v1's default `titles`,
|
|
1087
|
+
# where 2.6.0 had it, so remove(), discard() and `in` answer as
|
|
1088
|
+
# they did (decisions/H9, 2026-10-26). Kept, it is read as the
|
|
1089
|
+
# default reads it, by the phrase; removed, it is no title at
|
|
1090
|
+
# all, as removing it made it in 2.6.0. The one 2.6.0 reading
|
|
1091
|
+
# with no v1 spelling left is the word as a title everywhere:
|
|
1092
|
+
# v1 can say "a title" or "not a title", and "a title" now means
|
|
1093
|
+
# the phrase reading. A word 2.6.0 never listed (`dean`) is not
|
|
1094
|
+
# in `moved`, so adding it is still a title everywhere, the 1.4
|
|
1095
|
+
# recipe the phrase test honours below. Compared folded, as the
|
|
1096
|
+
# two cross-set computations below are (#582), so a spelling
|
|
1097
|
+
# Lexicon folds onto a moved word (`king。`) is read as it is.
|
|
1098
|
+
moved = TITLES_2_6_0 & TITLES_AMBIGUOUS
|
|
1099
|
+
kept_moved = frozenset(w for w in titles if _normalize(w) in moved)
|
|
1100
|
+
phrase_only = ((TITLES_AMBIGUOUS - moved) | kept_moved
|
|
1101
|
+
if titles else frozenset())
|
|
1102
|
+
# ...and the three intersections with the phrase vocabulary read
|
|
1103
|
+
# the same folded spellings, or a folded moved word kept its
|
|
1104
|
+
# phrase reading but lost its modifier one (`wing。`)
|
|
1105
|
+
any_title = frozenset(_normalize(w) for w in titles | phrase_only)
|
|
1074
1106
|
first_name_titles = matchable("first_name_titles")
|
|
1075
1107
|
non_given = matchable("non_first_name_prefixes")
|
|
1076
1108
|
ambiguous_acronyms = matchable("suffix_acronyms_ambiguous") & acronyms
|
|
@@ -1136,7 +1168,7 @@ class Constants:
|
|
|
1136
1168
|
# v1 also had, later, at capitalize().
|
|
1137
1169
|
try:
|
|
1138
1170
|
lexicon = Lexicon(
|
|
1139
|
-
titles=titles,
|
|
1171
|
+
titles=titles - kept_moved,
|
|
1140
1172
|
# TRANSLATE, do not filter. The two versions build the same
|
|
1141
1173
|
# lookup key differently: v1 joins the raw title run and
|
|
1142
1174
|
# then applies lc(), which strips only the whole string's
|
|
@@ -1193,6 +1225,15 @@ class Constants:
|
|
|
1193
1225
|
# through this intersection.
|
|
1194
1226
|
conjunctions_ambiguous=CONJUNCTIONS_AMBIGUOUS & conjunctions,
|
|
1195
1227
|
bound_given_names=bound,
|
|
1228
|
+
# likewise no v1 manager: an abbreviated given name
|
|
1229
|
+
# reading over rules/H2's shape is 2.7 behavior (#789),
|
|
1230
|
+
# so the set rides in the snapshot only, passed whole.
|
|
1231
|
+
# No v1 knob reaches it, and none needs to: a word a v1
|
|
1232
|
+
# caller ADDS to `titles` is a title wherever it stands,
|
|
1233
|
+
# since the leading peel asks the title vocabulary before
|
|
1234
|
+
# it weighs the shape, as it did in 1.4
|
|
1235
|
+
# (`c.titles.add("chas")`)
|
|
1236
|
+
given_name_abbreviations=GIVEN_NAME_ABBREVIATIONS,
|
|
1196
1237
|
# v1 Constants has no manager for these (#274 is 2.0
|
|
1197
1238
|
# behavior); the data module is the only source
|
|
1198
1239
|
maiden_markers=MAIDEN_MARKERS,
|
|
@@ -1216,6 +1257,51 @@ class Constants:
|
|
|
1216
1257
|
# so the intersection is kept for `_snapshot() ==
|
|
1217
1258
|
# Lexicon.default()` legibility, as conjunctions_ambiguous'.
|
|
1218
1259
|
after_given_titles=AFTER_GIVEN_TITLES & titles,
|
|
1260
|
+
# likewise no v1 manager: a suffix word that is also a
|
|
1261
|
+
# name is 2.7 behavior (#752), so it rides in the
|
|
1262
|
+
# snapshot only. Intersect with the word set, as the
|
|
1263
|
+
# tails are: Lexicon enforces the subset, and deleting
|
|
1264
|
+
# the suffix word is the one v1 knob, which turns the
|
|
1265
|
+
# reading off. agnomens is consulted only inside it, so
|
|
1266
|
+
# its intersection is kept for legibility alone.
|
|
1267
|
+
# A v1 caller who added such a word to suffix_acronyms
|
|
1268
|
+
# had it read as a suffix through the acronym branch,
|
|
1269
|
+
# so the word leaves the gate rather than raising at
|
|
1270
|
+
# Lexicon's word-gate check (folded, as the two
|
|
1271
|
+
# cross-set computations above are).
|
|
1272
|
+
suffix_words_ambiguous=frozenset(
|
|
1273
|
+
w for w in SUFFIX_WORDS_AMBIGUOUS & suffix_words
|
|
1274
|
+
if _normalize(w) not in {
|
|
1275
|
+
_normalize(a) for a in acronyms - ambiguous_acronyms}),
|
|
1276
|
+
agnomens=AGNOMENS & SUFFIX_WORDS_AMBIGUOUS & suffix_words,
|
|
1277
|
+
# likewise no v1 manager: the title phrase (rules/H9) is
|
|
1278
|
+
# 2.7 behavior (#752), so its sets ride in the snapshot
|
|
1279
|
+
# only. A title that may be a given name is not in
|
|
1280
|
+
# TITLES, so v1's `titles` holds one only where 2.6.0
|
|
1281
|
+
# listed it (`moved`, above, read by the phrase), and one
|
|
1282
|
+
# 2.6.0 never listed that a v1 caller ADDS to `titles` is
|
|
1283
|
+
# a title wherever it stands, as it was in 1.4
|
|
1284
|
+
# (`c.titles.add("dean")`, the documented recipe): the
|
|
1285
|
+
# phrase test reads a word of titles_ambiguous only where
|
|
1286
|
+
# it is not in titles. The
|
|
1287
|
+
# knob that reaches the other three is deleting a title,
|
|
1288
|
+
# which turns its modifier, style or phrase reading off
|
|
1289
|
+
# with it; Lexicon checks none of them, so the
|
|
1290
|
+
# intersections are kept for legibility, as
|
|
1291
|
+
# after_given_titles' is.
|
|
1292
|
+
# ...save where the caller emptied `titles`, v1's way to
|
|
1293
|
+
# turn title detection off (customize.rst). A caller who
|
|
1294
|
+
# REPLACES the list keeps the phrase reading of the words
|
|
1295
|
+
# 2.6.0 never listed: v1 has no knob for them, and what a
|
|
1296
|
+
# replaced list means for words it never held is not
|
|
1297
|
+
# something it says. A moved word reads by whether the
|
|
1298
|
+
# replaced list holds it, as any v1 title does
|
|
1299
|
+
titles_ambiguous=phrase_only,
|
|
1300
|
+
title_modifiers=TITLE_MODIFIERS & any_title,
|
|
1301
|
+
title_styles=TITLE_STYLES & any_title,
|
|
1302
|
+
title_phrases=frozenset(
|
|
1303
|
+
p for p in TITLE_PHRASES
|
|
1304
|
+
if all(w in any_title for w in p.split(" "))),
|
|
1219
1305
|
# TupleManager is dict[str, object] (v1 parity: values were
|
|
1220
1306
|
# never statically str-typed); every real entry is a str,
|
|
1221
1307
|
# same assumption _DelimiterManager's sentinel lookup makes.
|
|
@@ -25,7 +25,10 @@ _VOCAB_FIELDS = (
|
|
|
25
25
|
"titles", "given_name_titles", "suffix_acronyms", "suffix_words",
|
|
26
26
|
"suffix_acronyms_ambiguous", "particles", "particles_ambiguous",
|
|
27
27
|
"conjunctions", "conjunctions_ambiguous", "bound_given_names",
|
|
28
|
+
"given_name_abbreviations",
|
|
28
29
|
"maiden_markers", "surnames", "honorific_tails", "after_given_titles",
|
|
30
|
+
"suffix_words_ambiguous", "agnomens", "titles_ambiguous",
|
|
31
|
+
"title_modifiers", "title_styles", "title_phrases",
|
|
29
32
|
)
|
|
30
33
|
|
|
31
34
|
#: (marker, base, why) triples. Each marker QUALIFIES how entries of
|
|
@@ -68,6 +71,14 @@ _VOCAB_FIELDS = (
|
|
|
68
71
|
#: decides nothing, and AGENTS.md's invariants rule guards harm, not
|
|
69
72
|
#: no-ops. after_given_titles -- the same: classify tags a word from it
|
|
70
73
|
#: only where the word is in titles too, so an orphan is never read.
|
|
74
|
+
#: agnomens -- the same: classify reads it only for a word already in
|
|
75
|
+
#: suffix_words_ambiguous, so an orphan decides nothing.
|
|
76
|
+
#: title_modifiers, title_styles and title_phrases -- the same: the
|
|
77
|
+
#: title phrase test (rules/H9) reads them only for words that are
|
|
78
|
+
#: titles, so an orphan decides nothing. titles_ambiguous is no subset
|
|
79
|
+
#: at all: its words are titles only inside a phrase, so a caller lists
|
|
80
|
+
#: one there without listing it in titles, and one in both is a title
|
|
81
|
+
#: everywhere.
|
|
71
82
|
_SUBSET_FIELDS = (
|
|
72
83
|
("particles_ambiguous", "particles",
|
|
73
84
|
"an orphan emits a spurious particle-or-given ambiguity"),
|
|
@@ -75,6 +86,8 @@ _SUBSET_FIELDS = (
|
|
|
75
86
|
"an orphan silently becomes a period-gated suffix"),
|
|
76
87
|
("honorific_tails", "suffix_words",
|
|
77
88
|
"an orphan splits the name and leaves the tail inside it"),
|
|
89
|
+
("suffix_words_ambiguous", "suffix_words",
|
|
90
|
+
"an orphan silently becomes a suffix gated by the count"),
|
|
78
91
|
)
|
|
79
92
|
|
|
80
93
|
|
|
@@ -98,7 +111,7 @@ _SUBSET_FIELDS = (
|
|
|
98
111
|
#: by a genuine multi-token lookahead instead --
|
|
99
112
|
#: _pipeline._vocab.maiden_marker_run, longest first -- and only the
|
|
100
113
|
#: STORAGE rule is shared with titles.
|
|
101
|
-
_PHRASE_FIELDS = ("given_name_titles", "maiden_markers")
|
|
114
|
+
_PHRASE_FIELDS = ("given_name_titles", "maiden_markers", "title_phrases")
|
|
102
115
|
|
|
103
116
|
|
|
104
117
|
#: Every character the parser reads as a full stop at a WORD'S EDGE
|
|
@@ -567,12 +580,14 @@ class Lexicon:
|
|
|
567
580
|
Entries are normalized at construction -- lowercased, NFC-composed,
|
|
568
581
|
edge full stops stripped -- so matching is case-insensitive.
|
|
569
582
|
Vocabulary entries are single words -- a multi-word entry warns at
|
|
570
|
-
construction and can never match.
|
|
583
|
+
construction and can never match. Three fields are exempt, and they
|
|
571
584
|
differ in HOW they match: ``given_name_titles`` is looked up
|
|
572
585
|
against the run of words the parse has ALREADY read as titles --
|
|
573
|
-
the whole run space-joined, or that run's last word --
|
|
586
|
+
the whole run space-joined, or that run's last word --
|
|
574
587
|
``maiden_markers`` is matched by lookahead, longest first, over
|
|
575
|
-
words that need not be markers on their own (``"z domu"``)
|
|
588
|
+
words that need not be markers on their own (``"z domu"``), and
|
|
589
|
+
``title_phrases`` holds two-word phrases, stored space-joined and
|
|
590
|
+
matched against two adjacent title words (``"chief justice"``).
|
|
576
591
|
Field docs below show examples, not full
|
|
577
592
|
contents; inspect any field's shipped vocabulary directly, e.g.
|
|
578
593
|
``Lexicon.default().conjunctions``."""
|
|
@@ -581,7 +596,7 @@ class Lexicon:
|
|
|
581
596
|
#: list: :data:`~nameparser.config.titles.TITLES`.
|
|
582
597
|
titles: frozenset[str] = frozenset()
|
|
583
598
|
#: Titles whose single name word, before or after them, reads as a
|
|
584
|
-
#: GIVEN name ("
|
|
599
|
+
#: GIVEN name ("sir", "sister", ...) rather than a family name. Full
|
|
585
600
|
#: default list: :data:`~nameparser.config.titles.GIVEN_NAME_TITLES`.
|
|
586
601
|
given_name_titles: frozenset[str] = frozenset()
|
|
587
602
|
#: Post-nominal acronym suffixes, matched with or without periods
|
|
@@ -656,6 +671,17 @@ class Lexicon:
|
|
|
656
671
|
#: Full default list:
|
|
657
672
|
#: :data:`~nameparser.config.bound_given_names.BOUND_GIVEN_NAMES`.
|
|
658
673
|
bound_given_names: frozenset[str] = frozenset()
|
|
674
|
+
#: Given names written short ("chas" for Charles, "wm" for William,
|
|
675
|
+
#: Filipino "ma" for María). Period-marked and opening the part of
|
|
676
|
+
#: the name that carries the given name, an entry reads as that
|
|
677
|
+
#: given name, where an unlisted abbreviation there reads as a
|
|
678
|
+
#: title by its shape (rules/H2): "Chas. Smith" reads given
|
|
679
|
+
#: "Chas.", "Xyz. Smith" title "Xyz.". An entry also in ``titles``
|
|
680
|
+
#: is a title there, which is what listing it as one asks; anywhere
|
|
681
|
+
#: else an entry is read as it would be unlisted. Full default
|
|
682
|
+
#: list:
|
|
683
|
+
#: :data:`~nameparser.config.given_names.GIVEN_NAME_ABBREVIATIONS`.
|
|
684
|
+
given_name_abbreviations: frozenset[str] = frozenset()
|
|
659
685
|
#: Marker words introducing a birth surname, routed to the maiden
|
|
660
686
|
#: field ("née", "geb.", "rozená", ...). An entry may be a PHRASE
|
|
661
687
|
#: ("z domu"): entries are matched by lookahead, longest first, so
|
|
@@ -693,12 +719,70 @@ class Lexicon:
|
|
|
693
719
|
#: Salutations that read as a title where one ENDS the given part
|
|
694
720
|
#: after a family comma, written bare: "Doe, Jane Mrs" reads title
|
|
695
721
|
#: "Mrs" where an office or rank word there stays a middle name
|
|
696
|
-
#: ("Doe, John
|
|
722
|
+
#: ("Doe, John Pope"), rules/H8. A period-marked title word reads as
|
|
697
723
|
#: a title there whether listed here or not (rules/H5). Entries need
|
|
698
724
|
#: not also be in ``titles``; one that is not is never consulted.
|
|
699
725
|
#: Full default list:
|
|
700
726
|
#: :data:`~nameparser.config.titles.AFTER_GIVEN_TITLES`.
|
|
701
727
|
after_given_titles: frozenset[str] = frozenset()
|
|
728
|
+
#: Subset of suffix_words that are also borne as names ("fils",
|
|
729
|
+
#: "neto"). Read as a suffix only where the name has words to
|
|
730
|
+
#: spare, as an ambiguous acronym is: "Pedro Alves Neto" ends in a
|
|
731
|
+
#: suffix, "Ana Neto" keeps her family name, and either reading is
|
|
732
|
+
#: reported (rules/S11). Written in Title case in a name of mixed
|
|
733
|
+
#: case it leans to the name even with words to spare, and in lower
|
|
734
|
+
#: case it is the suffix after a comma that names the family,
|
|
735
|
+
#: unless it is also in ``agnomens``. Full default list:
|
|
736
|
+
#: :data:`~nameparser.config.suffixes.SUFFIX_WORDS_AMBIGUOUS`.
|
|
737
|
+
suffix_words_ambiguous: frozenset[str] = frozenset()
|
|
738
|
+
#: Subset of suffix_words_ambiguous whose writing says nothing:
|
|
739
|
+
#: an agnomen ("filho", "neto") is written in the case of the name
|
|
740
|
+
#: around it, so only the words to spare decide (rules/S11). An
|
|
741
|
+
#: entry outside suffix_words_ambiguous is never consulted. Full
|
|
742
|
+
#: default list: :data:`~nameparser.config.suffixes.AGNOMENS`.
|
|
743
|
+
agnomens: frozenset[str] = frozenset()
|
|
744
|
+
#: Titles that may also be a given name ("dean", "justice",
|
|
745
|
+
#: "major"), each read as a title only inside a title phrase: after a
|
|
746
|
+
#: modifier, a style, or a title it stands in ``title_phrases``
|
|
747
|
+
#: behind; or before "of" or a title it stands in ``title_phrases``
|
|
748
|
+
#: in front of. Elsewhere it is a name word, and where it opens the
|
|
749
|
+
#: name the parse reports ``title-or-name`` if a phrase of
|
|
750
|
+
#: ``title_phrases`` the parse can read holds it (rules/H9), so "Grace Smith", which no
|
|
751
|
+
#: phrase holds, reports nothing, and emptying ``title_phrases``
|
|
752
|
+
#: silences the report: "Dean Smith"
|
|
753
|
+
#: and "Mr Major Smith" read given "Dean" and "Major", "Dean of
|
|
754
|
+
#: Students Smith" and "Major General Smith" a title. An entry also
|
|
755
|
+
#: in ``titles`` is a title wherever it stands, which is what listing
|
|
756
|
+
#: it there asks, so the default lists these words here alone. Full
|
|
757
|
+
#: default list:
|
|
758
|
+
#: :data:`~nameparser.config.titles.TITLES_AMBIGUOUS`.
|
|
759
|
+
titles_ambiguous: frozenset[str] = frozenset()
|
|
760
|
+
#: Titles that only qualify a head title ("vice", "wing", "grand").
|
|
761
|
+
#: At the head of the name one is a title only with a head title
|
|
762
|
+
#: behind it in its run of titles, so "Wing Commander Lau" keeps the
|
|
763
|
+
#: title where "Wing Lau" reads "Wing" as the given name; after a
|
|
764
|
+
#: comma it is read as any title is (rules/H9), save one also in
|
|
765
|
+
#: ``titles_ambiguous``, which is read everywhere as at the head. One
|
|
766
|
+
#: standing before a word of ``titles_ambiguous`` puts that word in
|
|
767
|
+
#: a title phrase. Entries in neither ``titles`` nor
|
|
768
|
+
#: ``titles_ambiguous`` are never consulted. Full default
|
|
769
|
+
#: list: :data:`~nameparser.config.titles.TITLE_MODIFIERS`.
|
|
770
|
+
title_modifiers: frozenset[str] = frozenset()
|
|
771
|
+
#: Honorific styles ("hon", "excellency", "highness"): one standing
|
|
772
|
+
#: before a word of ``titles_ambiguous`` puts that word in a title
|
|
773
|
+
#: phrase, as in "The Hon. Justice Smith" (rules/H9). Entries in
|
|
774
|
+
#: neither ``titles`` nor ``titles_ambiguous`` are never consulted.
|
|
775
|
+
#: Full default list:
|
|
776
|
+
#: :data:`~nameparser.config.titles.TITLE_STYLES`.
|
|
777
|
+
title_styles: frozenset[str] = frozenset()
|
|
778
|
+
#: Two-word title phrases, a title and the title it qualifies
|
|
779
|
+
#: ("chief justice", "major general"), stored space-joined: a word
|
|
780
|
+
#: of ``titles_ambiguous`` on either side of one is in a title
|
|
781
|
+
#: phrase (rules/H9). A phrase is consulted only if it is two
|
|
782
|
+
#: words, each in ``titles`` or ``titles_ambiguous``. Full default
|
|
783
|
+
#: list:
|
|
784
|
+
#: :data:`~nameparser.config.titles.TITLE_PHRASES`.
|
|
785
|
+
title_phrases: frozenset[str] = frozenset()
|
|
702
786
|
#: Lowercase word -> case mask used by capitalized(): the word's
|
|
703
787
|
#: own letters and digits, each in the case it takes ("phd" ->
|
|
704
788
|
#: "PhD"), laid over the word as written, so the one entry
|
|
@@ -773,6 +857,22 @@ class Lexicon:
|
|
|
773
857
|
f"period gate suffix_acronyms_ambiguous exists to impose"
|
|
774
858
|
)
|
|
775
859
|
|
|
860
|
+
# the mirror of the check above: suffix_as_written ORs the
|
|
861
|
+
# unambiguous acronym branch too, so a gated suffix word listed
|
|
862
|
+
# there is claimed as a suffix with no count ('Ana Neto' would
|
|
863
|
+
# lose her family name, rules/S11)
|
|
864
|
+
word_gate_bypassed = self.suffix_words_ambiguous & (
|
|
865
|
+
self.suffix_acronyms - self.suffix_acronyms_ambiguous)
|
|
866
|
+
if word_gate_bypassed:
|
|
867
|
+
raise ValueError(
|
|
868
|
+
f"an ambiguous suffix word must not also be an unambiguous "
|
|
869
|
+
f"suffix acronym; in both: "
|
|
870
|
+
f"{', '.join(sorted(word_gate_bypassed))}. The acronym "
|
|
871
|
+
f"branch matches it outright, which bypasses the count "
|
|
872
|
+
f"suffix_words_ambiguous exists to impose; drop it from "
|
|
873
|
+
f"suffix_acronyms or from suffix_words_ambiguous"
|
|
874
|
+
)
|
|
875
|
+
|
|
776
876
|
# -- constructors ----------------------------------------------------
|
|
777
877
|
|
|
778
878
|
@classmethod
|
|
@@ -828,7 +928,10 @@ class Lexicon:
|
|
|
828
928
|
# Lexicon field layout (version skew) -- silently loading it
|
|
829
929
|
# would defer the failure to some distant attribute read.
|
|
830
930
|
# Message kept in sync with _types._guarded_setstate by design
|
|
831
|
-
# (layering keeps this module import-free of _types).
|
|
931
|
+
# (layering keeps this module import-free of _types). Unlike
|
|
932
|
+
# that guard it admits no field added later (`ADDED_IN`, 2.7):
|
|
933
|
+
# every Lexicon field is still required, so a release adding one
|
|
934
|
+
# breaks older Lexicon pickles until this learns the marker.
|
|
832
935
|
expected = {f.name for f in dataclasses.fields(Lexicon)} - {"_cap_map"}
|
|
833
936
|
if set(state) != expected:
|
|
834
937
|
missing = ", ".join(sorted(expected - set(state))) or "none"
|
|
@@ -945,18 +1048,20 @@ class Lexicon:
|
|
|
945
1048
|
def _default_lexicon() -> Lexicon:
|
|
946
1049
|
# v1 data modules are the single source of vocabulary through 2.x.
|
|
947
1050
|
from nameparser.config.bound_given_names import BOUND_GIVEN_NAMES
|
|
1051
|
+
from nameparser.config.given_names import GIVEN_NAME_ABBREVIATIONS
|
|
948
1052
|
from nameparser.config.capitalization import CAPITALIZATION_EXCEPTIONS
|
|
949
1053
|
from nameparser.config.conjunctions import (
|
|
950
1054
|
CONJUNCTIONS, CONJUNCTIONS_AMBIGUOUS)
|
|
951
1055
|
from nameparser.config.maiden_markers import MAIDEN_MARKERS
|
|
952
1056
|
from nameparser.config.particles import NON_GIVEN_NAME_PARTICLES, PARTICLES
|
|
953
1057
|
from nameparser.config.suffixes import (
|
|
954
|
-
GLUED_HONORIFICS, SUFFIX_ACRONYMS, SUFFIX_ACRONYMS_AMBIGUOUS,
|
|
955
|
-
SUFFIX_WORDS,
|
|
1058
|
+
AGNOMENS, GLUED_HONORIFICS, SUFFIX_ACRONYMS, SUFFIX_ACRONYMS_AMBIGUOUS,
|
|
1059
|
+
SUFFIX_WORDS, SUFFIX_WORDS_AMBIGUOUS,
|
|
956
1060
|
)
|
|
957
1061
|
from nameparser.config.surnames import KOREAN_SURNAMES
|
|
958
1062
|
from nameparser.config.titles import (
|
|
959
|
-
AFTER_GIVEN_TITLES, GIVEN_NAME_TITLES,
|
|
1063
|
+
AFTER_GIVEN_TITLES, GIVEN_NAME_TITLES, TITLE_MODIFIERS, TITLE_PHRASES,
|
|
1064
|
+
TITLE_STYLES, TITLES, TITLES_AMBIGUOUS)
|
|
960
1065
|
|
|
961
1066
|
# every vocabulary constant is a frozenset since #293, so each one
|
|
962
1067
|
# feeds its strictly-typed frozenset[str] field as it stands -- and
|
|
@@ -981,10 +1086,17 @@ def _default_lexicon() -> Lexicon:
|
|
|
981
1086
|
conjunctions=CONJUNCTIONS,
|
|
982
1087
|
conjunctions_ambiguous=CONJUNCTIONS_AMBIGUOUS,
|
|
983
1088
|
bound_given_names=BOUND_GIVEN_NAMES,
|
|
1089
|
+
given_name_abbreviations=GIVEN_NAME_ABBREVIATIONS,
|
|
984
1090
|
maiden_markers=MAIDEN_MARKERS,
|
|
985
1091
|
surnames=KOREAN_SURNAMES,
|
|
986
1092
|
honorific_tails=GLUED_HONORIFICS,
|
|
987
1093
|
after_given_titles=AFTER_GIVEN_TITLES,
|
|
1094
|
+
suffix_words_ambiguous=SUFFIX_WORDS_AMBIGUOUS,
|
|
1095
|
+
agnomens=AGNOMENS,
|
|
1096
|
+
titles_ambiguous=TITLES_AMBIGUOUS,
|
|
1097
|
+
title_modifiers=TITLE_MODIFIERS,
|
|
1098
|
+
title_styles=TITLE_STYLES,
|
|
1099
|
+
title_phrases=TITLE_PHRASES,
|
|
988
1100
|
# pass canonical pair-tuples so this strictly-typed call site never
|
|
989
1101
|
# feeds a Mapping to the tuple-annotated field; __post_init__
|
|
990
1102
|
# still tolerates a Mapping at runtime for interactive use
|
|
@@ -21,12 +21,42 @@ tests/v2/pipeline/test_assemble.py.
|
|
|
21
21
|
"""
|
|
22
22
|
from __future__ import annotations
|
|
23
23
|
|
|
24
|
-
from nameparser._pipeline._state import
|
|
24
|
+
from nameparser._pipeline._state import (
|
|
25
|
+
FIELD_READING, NAME_ROLES, Beside, Deferred, ParseState,
|
|
26
|
+
PendingAmbiguity,
|
|
27
|
+
)
|
|
25
28
|
from nameparser._types import (
|
|
26
|
-
Ambiguity, AmbiguityKind, ParsedName, Role, Token,
|
|
29
|
+
FOLDED_TAG, Ambiguity, AmbiguityKind, ParsedName, Reading, Role, Token,
|
|
27
30
|
)
|
|
28
31
|
|
|
29
32
|
|
|
33
|
+
def _alternative(pending: PendingAmbiguity, materialized: tuple[Token, ...],
|
|
34
|
+
final: dict[int, Token],
|
|
35
|
+
middle_as_family: bool) -> Reading | None:
|
|
36
|
+
"""The public Reading of a report's PendingReading, its field named
|
|
37
|
+
from the final roles where the emitter deferred it (O5's other
|
|
38
|
+
primary field) and folded as `middle_as_family` folds every middle
|
|
39
|
+
(rules/O6's neighbour, the policy's own fold)."""
|
|
40
|
+
alt = pending.alternative
|
|
41
|
+
if alt is None or not materialized:
|
|
42
|
+
return None
|
|
43
|
+
role = alt.role
|
|
44
|
+
if role is Deferred.OTHER_PRIMARY:
|
|
45
|
+
role = (Role.FAMILY if materialized[0].role is Role.GIVEN
|
|
46
|
+
else Role.GIVEN)
|
|
47
|
+
elif isinstance(role, Beside) and role.index in final:
|
|
48
|
+
front = final[role.index]
|
|
49
|
+
role = (role.unfolded
|
|
50
|
+
if FOLDED_TAG in front.tags and role.unfolded is not None
|
|
51
|
+
else front.role)
|
|
52
|
+
elif isinstance(role, (Deferred, Beside)):
|
|
53
|
+
# assign found no name position to name it from
|
|
54
|
+
return None
|
|
55
|
+
if middle_as_family and role is Role.MIDDLE:
|
|
56
|
+
role = Role.FAMILY
|
|
57
|
+
return Reading(alt.kind, role, alt.source)
|
|
58
|
+
|
|
59
|
+
|
|
30
60
|
# rules/A2: "a name with no name content parses to the empty
|
|
31
61
|
# name — every field empty, false as a boolean — while ambiguities
|
|
32
62
|
# born from its punctuation survive on the empty result"
|
|
@@ -143,7 +173,10 @@ def assemble(state: ParseState) -> ParsedName:
|
|
|
143
173
|
continue
|
|
144
174
|
detail = (f"{detail}{FIELD_READING[materialized[0].role]}"
|
|
145
175
|
f"{pending.field_tail}")
|
|
146
|
-
ambiguities.append(Ambiguity(
|
|
176
|
+
ambiguities.append(Ambiguity(
|
|
177
|
+
pending.kind, detail, materialized,
|
|
178
|
+
_alternative(pending, materialized, final,
|
|
179
|
+
state.policy.middle_as_family)))
|
|
147
180
|
return ParsedName(original=state.original,
|
|
148
181
|
tokens=tuple(final.values()),
|
|
149
182
|
ambiguities=tuple(ambiguities))
|