nameparser 2.6.0__tar.gz → 2.8.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.
Files changed (243) hide show
  1. {nameparser-2.6.0/nameparser.egg-info → nameparser-2.8.0}/PKG-INFO +22 -14
  2. {nameparser-2.6.0 → nameparser-2.8.0}/README.rst +21 -13
  3. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/__init__.py +5 -1
  4. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_config_shim.py +244 -27
  5. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_facade.py +48 -3
  6. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_lexicon.py +122 -10
  7. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_pipeline/_assemble.py +36 -3
  8. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_pipeline/_assign.py +490 -86
  9. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_pipeline/_classify.py +154 -34
  10. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_pipeline/_comma.py +142 -52
  11. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_pipeline/_extract.py +4 -3
  12. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_pipeline/_group.py +142 -37
  13. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_pipeline/_pieces.py +405 -99
  14. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_pipeline/_post_rules.py +117 -44
  15. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_pipeline/_segment.py +4 -4
  16. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_pipeline/_state.py +93 -5
  17. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_pipeline/_vocab.py +636 -72
  18. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_policy.py +12 -8
  19. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_render.py +60 -1
  20. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_types.py +189 -30
  21. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_version.py +4 -4
  22. nameparser-2.8.0/nameparser/config/__init__.py +76 -0
  23. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/config/_compiled.py +534 -31
  24. nameparser-2.8.0/nameparser/config/_titles_2_6_0.py +826 -0
  25. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/config/capitalization.py +2 -1
  26. nameparser-2.8.0/nameparser/config/given_names.py +18 -0
  27. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/config/particles.py +8 -7
  28. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/config/suffixes.py +23 -2
  29. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/config/titles.py +43 -4
  30. nameparser-2.8.0/nameparser/util.py +46 -0
  31. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/ar.toml +25 -12
  32. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/bn.toml +1 -2
  33. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/cs.toml +1 -3
  34. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/de.toml +14 -9
  35. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/en.toml +201 -104
  36. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/es.toml +12 -10
  37. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/fil.toml +9 -1
  38. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/fr.toml +12 -12
  39. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/ga.toml +2 -2
  40. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/gd.toml +1 -1
  41. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/he.toml +3 -2
  42. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/hi.toml +1 -2
  43. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/it.toml +11 -10
  44. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/ms.toml +3 -3
  45. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/mul.toml +1 -1
  46. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/nl.toml +23 -20
  47. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/pa.toml +2 -1
  48. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/pl.toml +2 -1
  49. nameparser-2.8.0/nameparser/vocab/pt-BR.toml +49 -0
  50. nameparser-2.8.0/nameparser/vocab/pt.toml +101 -0
  51. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/sr.toml +1 -2
  52. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/sw.toml +1 -2
  53. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/tr.toml +3 -1
  54. nameparser-2.8.0/nameparser/vocab/und.toml +21 -0
  55. {nameparser-2.6.0 → nameparser-2.8.0/nameparser.egg-info}/PKG-INFO +22 -14
  56. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser.egg-info/SOURCES.txt +9 -0
  57. {nameparser-2.6.0 → nameparser-2.8.0}/pyproject.toml +9 -0
  58. nameparser-2.8.0/tests/removals.py +26 -0
  59. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_conjunctions.py +2 -2
  60. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_constants.py +9 -3
  61. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_first_name.py +18 -5
  62. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_python_api.py +11 -8
  63. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_suffixes.py +49 -34
  64. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_titles.py +4 -4
  65. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/_differential_fixtures.py +35 -5
  66. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/cases.py +2199 -457
  67. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/pipeline/test_assemble.py +2 -1
  68. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/pipeline/test_assign.py +51 -17
  69. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/pipeline/test_classify.py +4 -4
  70. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/pipeline/test_comma.py +16 -12
  71. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/pipeline/test_group.py +36 -27
  72. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/pipeline/test_pieces.py +44 -10
  73. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/pipeline/test_post_rules.py +13 -6
  74. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/pipeline/test_script_segment.py +14 -11
  75. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/pipeline/test_state.py +7 -6
  76. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/pipeline/test_vocab.py +311 -19
  77. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/rules_doc.py +5 -4
  78. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_benchmark.py +62 -2
  79. nameparser-2.8.0/tests/v2/test_cases.py +820 -0
  80. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_config_shim.py +197 -1
  81. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_contracts.py +3 -1
  82. nameparser-2.8.0/tests/v2/test_deprecations.py +457 -0
  83. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_differential.py +18 -0
  84. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_facade.py +24 -7
  85. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_facade_cases.py +7 -0
  86. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_lexicon.py +22 -8
  87. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_local_agreement.py +255 -9
  88. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_local_corpora.py +551 -19
  89. nameparser-2.8.0/tests/v2/test_local_orcid.py +561 -0
  90. nameparser-2.8.0/tests/v2/test_local_patentsview.py +366 -0
  91. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_locales.py +4 -3
  92. nameparser-2.8.0/tests/v2/test_measure.py +181 -0
  93. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_name_usage_tool.py +107 -5
  94. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_parser.py +156 -34
  95. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_policy.py +1 -1
  96. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_properties.py +293 -21
  97. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_regex_sync.py +24 -2
  98. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_render.py +57 -0
  99. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_reprs.py +6 -1
  100. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_snapshot.py +78 -4
  101. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_types.py +140 -5
  102. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_vocab.py +304 -51
  103. nameparser-2.8.0/tests/v2/test_word_report.py +309 -0
  104. nameparser-2.6.0/nameparser/config/__init__.py +0 -43
  105. nameparser-2.6.0/nameparser/util.py +0 -14
  106. nameparser-2.6.0/nameparser/vocab/pt.toml +0 -70
  107. nameparser-2.6.0/nameparser/vocab/und.toml +0 -21
  108. nameparser-2.6.0/tests/v2/test_cases.py +0 -415
  109. {nameparser-2.6.0 → nameparser-2.8.0}/AUTHORS +0 -0
  110. {nameparser-2.6.0 → nameparser-2.8.0}/LICENSE +0 -0
  111. {nameparser-2.6.0 → nameparser-2.8.0}/MANIFEST.in +0 -0
  112. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/__main__.py +0 -0
  113. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_locale.py +0 -0
  114. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_parser.py +0 -0
  115. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_pipeline/__init__.py +0 -0
  116. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_pipeline/_script_segment.py +0 -0
  117. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/_pipeline/_tokenize.py +0 -0
  118. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/config/_deprecated.py +0 -0
  119. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/config/_invariants.py +0 -0
  120. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/config/bound_first_names.py +0 -0
  121. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/config/bound_given_names.py +0 -0
  122. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/config/conjunctions.py +0 -0
  123. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/config/maiden_markers.py +0 -0
  124. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/config/prefixes.py +0 -0
  125. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/config/regexes.py +0 -0
  126. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/config/surnames.py +0 -0
  127. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/locales/__init__.py +0 -0
  128. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/locales/ja.py +0 -0
  129. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/locales/ru.py +0 -0
  130. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/locales/tr_az.py +0 -0
  131. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/locales/zh.py +0 -0
  132. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/parser.py +0 -0
  133. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/py.typed +0 -0
  134. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/ak.toml +0 -0
  135. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/am.toml +0 -0
  136. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/as.toml +0 -0
  137. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/be.toml +0 -0
  138. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/bg.toml +0 -0
  139. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/bin.toml +0 -0
  140. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/bo.toml +0 -0
  141. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/bs.toml +0 -0
  142. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/ca.toml +0 -0
  143. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/da.toml +0 -0
  144. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/el.toml +0 -0
  145. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/et.toml +0 -0
  146. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/fa.toml +0 -0
  147. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/ff.toml +0 -0
  148. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/fi.toml +0 -0
  149. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/gu.toml +0 -0
  150. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/ha.toml +0 -0
  151. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/hr.toml +0 -0
  152. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/hu.toml +0 -0
  153. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/hy.toml +0 -0
  154. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/id.toml +0 -0
  155. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/is.toml +0 -0
  156. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/ja.toml +0 -0
  157. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/ka.toml +0 -0
  158. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/kk.toml +0 -0
  159. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/km.toml +0 -0
  160. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/kn.toml +0 -0
  161. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/ko.toml +0 -0
  162. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/kok.toml +0 -0
  163. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/lo.toml +0 -0
  164. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/lt.toml +0 -0
  165. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/lv.toml +0 -0
  166. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/mi.toml +0 -0
  167. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/mk.toml +0 -0
  168. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/ml.toml +0 -0
  169. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/mn.toml +0 -0
  170. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/mr.toml +0 -0
  171. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/my.toml +0 -0
  172. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/nb.toml +0 -0
  173. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/ne.toml +0 -0
  174. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/nn.toml +0 -0
  175. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/or.toml +0 -0
  176. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/ro.toml +0 -0
  177. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/ru.toml +0 -0
  178. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/sd.toml +0 -0
  179. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/shn.toml +0 -0
  180. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/si.toml +0 -0
  181. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/sk.toml +0 -0
  182. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/sl.toml +0 -0
  183. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/sv.toml +0 -0
  184. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/ta.toml +0 -0
  185. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/te.toml +0 -0
  186. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/th.toml +0 -0
  187. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/ti.toml +0 -0
  188. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/uk.toml +0 -0
  189. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/ur.toml +0 -0
  190. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/vi.toml +0 -0
  191. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/yi.toml +0 -0
  192. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/yo.toml +0 -0
  193. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/zh.toml +0 -0
  194. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser/vocab/zu.toml +0 -0
  195. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser.egg-info/dependency_links.txt +0 -0
  196. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser.egg-info/requires.txt +0 -0
  197. {nameparser-2.6.0 → nameparser-2.8.0}/nameparser.egg-info/top_level.txt +0 -0
  198. {nameparser-2.6.0 → nameparser-2.8.0}/setup.cfg +0 -0
  199. {nameparser-2.6.0 → nameparser-2.8.0}/tests/__init__.py +0 -0
  200. {nameparser-2.6.0 → nameparser-2.8.0}/tests/base.py +0 -0
  201. {nameparser-2.6.0 → nameparser-2.8.0}/tests/conftest.py +0 -0
  202. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_bound_given_names.py +0 -0
  203. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_brute_force.py +0 -0
  204. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_capitalization.py +0 -0
  205. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_comma_variants.py +0 -0
  206. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_east_slavic_patronymic_order.py +0 -0
  207. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_initials.py +0 -0
  208. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_middle_name_as_last.py +0 -0
  209. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_nicknames.py +0 -0
  210. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_output_format.py +0 -0
  211. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_particles.py +0 -0
  212. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_turkic_patronymic_order.py +0 -0
  213. {nameparser-2.6.0 → nameparser-2.8.0}/tests/test_variations.py +0 -0
  214. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/__init__.py +0 -0
  215. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/conftest.py +0 -0
  216. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/design_docs.py +0 -0
  217. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/pipeline/__init__.py +0 -0
  218. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/pipeline/test_extract.py +0 -0
  219. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/pipeline/test_segment.py +0 -0
  220. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/pipeline/test_tokenize.py +0 -0
  221. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/rules_budget.py +0 -0
  222. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/rules_skeleton.py +0 -0
  223. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_cldr_agreement.py +0 -0
  224. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_cldr_corpus.py +0 -0
  225. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_cli.py +0 -0
  226. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_config_aliases.py +0 -0
  227. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_corpora.py +0 -0
  228. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_corpus_build.py +0 -0
  229. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_design_docs.py +0 -0
  230. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_doc_citations.py +0 -0
  231. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_doc_spellings.py +0 -0
  232. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_gnd_corpus.py +0 -0
  233. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_layering.py +0 -0
  234. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_local_jrc.py +0 -0
  235. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_locale.py +0 -0
  236. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_order_correspondence.py +0 -0
  237. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_rules_budget.py +0 -0
  238. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_rules_doc.py +0 -0
  239. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_rules_doc_grammar.py +0 -0
  240. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_rules_skeleton.py +0 -0
  241. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_wikidata_corpus.py +0 -0
  242. {nameparser-2.6.0 → nameparser-2.8.0}/tests/v2/test_wikidata_native_corpus.py +0 -0
  243. {nameparser-2.6.0 → nameparser-2.8.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.6.0
3
+ Version: 2.8.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,24 +35,30 @@ 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.6 is out.** It keeps surnames that earlier releases
39
- read as credentials (``Luo Si``, ``Maria de Sa``, ``Ram Bahadur B.K.``),
40
- reads ``Wm`` and ``Md`` as the given names they abbreviate, recognizes more credentials after a comma
41
- (``John Smith, MBChB``), and adds an opt-in reading for names with two
42
- family names (``Gabriel García Márquez``). The `release log
38
+ 📣 **nameparser 2.8 is out, the last 2.x feature release.**
39
+
40
+ ⚠️ **HumanName is deprecated, and 3.0 removes it.** From 2.8 the 1.x
41
+ compatibility layer — ``HumanName``, ``Constants`` and the shared
42
+ ``CONSTANTS`` — emits a ``DeprecationWarning`` where your code
43
+ constructs it, loads it from a pickle, or changes the shared
44
+ ``CONSTANTS``. It keeps working through 2.x; ``parse()`` is the
45
+ replacement, and `Coming from 1.x`_ below says where to start. After
46
+ 2.8 the 2.x line takes fixes only, and new work goes into 3.0.
47
+
48
+ 2.8 also carries on 2.7's reading of titles that are also given names:
49
+ a title only inside a title phrase, so ``Queen Latifah`` keeps the
50
+ given name ``Queen`` while ``Her Majesty Queen Elizabeth`` reads the
51
+ title. The `release log
43
52
  <https://nameparser.readthedocs.io/en/latest/release_log.html>`__ has
44
- the details.
53
+ the details. Please `open an
54
+ issue <https://github.com/derek73/python-nameparser/issues>`__ for anything
55
+ that parses wrong.
45
56
 
46
57
  Chinese, Japanese and Korean names written in their own scripts are read
47
58
  family-first, unspaced Korean names are split against the census surname
48
59
  list, and CJK honorifics are recognized. See `East Asian names
49
60
  <https://nameparser.readthedocs.io/en/latest/usage.html#east-asian-names>`__.
50
61
 
51
- Existing ``HumanName`` code from 1.x keeps working through 2.x, and most
52
- of it needs no changes; see `Coming from 1.x`_ below. Please `open an
53
- issue <https://github.com/derek73/python-nameparser/issues>`__ for anything
54
- that parses wrong.
55
-
56
62
  Installation
57
63
  ------------
58
64
 
@@ -124,8 +130,10 @@ Learn more
124
130
  Coming from 1.x
125
131
  ----------------
126
132
 
127
- ``HumanName`` and ``CONSTANTS`` keep working in 2.0 — same imports, same
128
- attributes, same mutation API. What 2.0 removes is the batch of
133
+ ``HumanName`` and ``CONSTANTS`` keep working through 2.x — same imports,
134
+ same attributes, same mutation API — and warn from 2.8 on, since 3.0
135
+ removes them. Run your tests under ``python -W error::DeprecationWarning``
136
+ to find the sites; a read of ``CONSTANTS`` does not warn. What 2.0 removes is the batch of
129
137
  deprecations 1.3 and 1.4 announced, so if your test suite runs clean on
130
138
  1.4 under ``python -W error::DeprecationWarning``, you are nearly done.
131
139
  Two things that check will not catch: four removals 1.4 never warned
@@ -7,24 +7,30 @@ 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.6 is out.** It keeps surnames that earlier releases
11
- read as credentials (``Luo Si``, ``Maria de Sa``, ``Ram Bahadur B.K.``),
12
- reads ``Wm`` and ``Md`` as the given names they abbreviate, recognizes more credentials after a comma
13
- (``John Smith, MBChB``), and adds an opt-in reading for names with two
14
- family names (``Gabriel García Márquez``). The `release log
10
+ 📣 **nameparser 2.8 is out, the last 2.x feature release.**
11
+
12
+ ⚠️ **HumanName is deprecated, and 3.0 removes it.** From 2.8 the 1.x
13
+ compatibility layer — ``HumanName``, ``Constants`` and the shared
14
+ ``CONSTANTS`` — emits a ``DeprecationWarning`` where your code
15
+ constructs it, loads it from a pickle, or changes the shared
16
+ ``CONSTANTS``. It keeps working through 2.x; ``parse()`` is the
17
+ replacement, and `Coming from 1.x`_ below says where to start. After
18
+ 2.8 the 2.x line takes fixes only, and new work goes into 3.0.
19
+
20
+ 2.8 also carries on 2.7's reading of titles that are also given names:
21
+ a title only inside a title phrase, so ``Queen Latifah`` keeps the
22
+ given name ``Queen`` while ``Her Majesty Queen Elizabeth`` reads the
23
+ title. The `release log
15
24
  <https://nameparser.readthedocs.io/en/latest/release_log.html>`__ has
16
- the details.
25
+ the details. Please `open an
26
+ issue <https://github.com/derek73/python-nameparser/issues>`__ for anything
27
+ that parses wrong.
17
28
 
18
29
  Chinese, Japanese and Korean names written in their own scripts are read
19
30
  family-first, unspaced Korean names are split against the census surname
20
31
  list, and CJK honorifics are recognized. See `East Asian names
21
32
  <https://nameparser.readthedocs.io/en/latest/usage.html#east-asian-names>`__.
22
33
 
23
- Existing ``HumanName`` code from 1.x keeps working through 2.x, and most
24
- of it needs no changes; see `Coming from 1.x`_ below. Please `open an
25
- issue <https://github.com/derek73/python-nameparser/issues>`__ for anything
26
- that parses wrong.
27
-
28
34
  Installation
29
35
  ------------
30
36
 
@@ -96,8 +102,10 @@ Learn more
96
102
  Coming from 1.x
97
103
  ----------------
98
104
 
99
- ``HumanName`` and ``CONSTANTS`` keep working in 2.0 — same imports, same
100
- attributes, same mutation API. What 2.0 removes is the batch of
105
+ ``HumanName`` and ``CONSTANTS`` keep working through 2.x — same imports,
106
+ same attributes, same mutation API — and warn from 2.8 on, since 3.0
107
+ removes them. Run your tests under ``python -W error::DeprecationWarning``
108
+ to find the sites; a read of ``CONSTANTS`` does not warn. What 2.0 removes is the batch of
101
109
  deprecations 1.3 and 1.4 announced, so if your test suite runs clean on
102
110
  1.4 under ``python -W error::DeprecationWarning``, you are nearly done.
103
111
  Two things that check will not catch: four removals 1.4 never warned
@@ -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", "ParsedName",
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",
@@ -4,7 +4,7 @@ re-exports these names from the swap
4
4
  commit onward; the whole module is deleted in 3.0 with the facade.
5
5
 
6
6
  Layering: facade layer -- may import anything public; here that's
7
- ``nameparser.util`` for ``lc()``, ``nameparser.config.regexes`` for
7
+ ``nameparser.util`` for ``lc()`` (as ``_lc``, the name that does not warn), ``nameparser.config.regexes`` for
8
8
  the read-only regexes proxy's underlying compiled patterns, and
9
9
  ``nameparser._lexicon``/``_policy``/``_parser`` for the ``_snapshot()``
10
10
  translation and the shared parser cache.
@@ -20,12 +20,16 @@ that cycle.
20
20
  """
21
21
  from __future__ import annotations
22
22
 
23
+ import copy
23
24
  import functools
25
+ import re
26
+ import sys
24
27
  import warnings
25
28
  from collections.abc import (
26
29
  Callable, ItemsView, Iterable, Iterator, KeysView, Mapping, Set,
27
30
  ValuesView,
28
31
  )
32
+ from types import FrameType
29
33
  from typing import NamedTuple, Self
30
34
 
31
35
  from nameparser._lexicon import (
@@ -33,7 +37,7 @@ from nameparser._lexicon import (
33
37
  )
34
38
  from nameparser._parser import Parser
35
39
  from nameparser._policy import PatronymicRule, Policy
36
- from nameparser.util import lc
40
+ from nameparser.util import _lc as lc
37
41
 
38
42
 
39
43
  #: The eight multi-word entries the pre-2.0 DEFAULT vocabulary shipped.
@@ -712,12 +716,64 @@ _UNSET = object()
712
716
  _UNSET_KWARG = object()
713
717
 
714
718
 
719
+ #: The migration guide every 2.8 removal message points at.
720
+ MIGRATION_GUIDE = "https://nameparser.readthedocs.io/en/latest/migrate.html"
721
+
722
+ # The 2.8 removal messages (decisions/3-0-reevaluations, 2026-10-28).
723
+ # Each names code that type-checks and runs, pinned by
724
+ # tests/v2/test_deprecations.py, and each opens with a fixed phrase
725
+ # pyproject's filterwarnings ignores by message.
715
726
  _SHARED_MUTATION_MESSAGE = (
716
727
  "mutating the shared CONSTANTS singleton is deprecated and will be "
717
- "removed in 3.0; build a Lexicon/Policy (or a private Constants "
718
- "passed as HumanName(constants=...)) instead. See the migration "
719
- "guide."
728
+ "removed in 3.0 with HumanName and Constants; configure a Parser "
729
+ "instead, e.g. Parser(lexicon=Lexicon.default().add(titles={'dean'})). "
730
+ f"See {MIGRATION_GUIDE}"
731
+ )
732
+ CONSTANTS_DEPRECATION = (
733
+ "Constants is deprecated and will be removed in 3.0 with HumanName; "
734
+ "configure a Parser instead, e.g. "
735
+ "Parser(lexicon=Lexicon.default().add(titles={'dean'})). "
736
+ f"See {MIGRATION_GUIDE}"
737
+ )
738
+ CONSTANTS_PICKLE_DEPRECATION = (
739
+ "Constants pickles stop loading in 3.0, when Constants is removed; "
740
+ "pickle a Parser instead, e.g. "
741
+ "Parser(lexicon=Lexicon.default().add(titles={'dean'})). "
742
+ f"See {MIGRATION_GUIDE}"
720
743
  )
744
+ #: one message per manager class, keyed by name, for
745
+ #: nameparser.config.__getattr__
746
+ MANAGER_DEPRECATIONS = {
747
+ name: (
748
+ f"nameparser.config.{name} is deprecated and will be removed in "
749
+ f"3.0 with Constants; Lexicon fields take plain sets, e.g. "
750
+ f"Lexicon.default().add(titles={{'dean'}}). See {MIGRATION_GUIDE}"
751
+ )
752
+ for name in ("SetManager", "TupleManager", "RegexTupleManager")
753
+ }
754
+
755
+
756
+ def _quiet_setstate(new: object, state: dict[str, object], base: type,
757
+ message: str) -> None:
758
+ """Restore a copy through its class's own ``__setstate__``.
759
+
760
+ ``copy.copy``/``copy.deepcopy`` reconstruct through ``__setstate__``,
761
+ which since 2.8 warns that a pickle was loaded, and a copy is no
762
+ load. ``base``'s own hook restores privately; a subclass's override
763
+ is called, as it was before 2.8, with only the load warning
764
+ filtered, by message. The filter is taken only then: entering
765
+ ``catch_warnings`` clears every module's warnings registry, so on
766
+ every copy it would re-show warnings the caller's filters show once
767
+ per line. A copy of the shared ``CONSTANTS`` stays quiet, as before:
768
+ a ``HumanName`` built on the copy still warns.
769
+ """
770
+ if type(new).__setstate__ is base.__setstate__: # type: ignore[attr-defined]
771
+ new._restore(state) # type: ignore[attr-defined]
772
+ return
773
+ with warnings.catch_warnings():
774
+ warnings.filterwarnings("ignore", message=re.escape(message),
775
+ category=DeprecationWarning)
776
+ new.__setstate__(state) # type: ignore[attr-defined]
721
777
 
722
778
 
723
779
  def _default_vocab() -> dict[str, frozenset[str]]:
@@ -731,13 +787,19 @@ def _default_vocab() -> dict[str, frozenset[str]]:
731
787
  from nameparser.config.suffixes import (
732
788
  SUFFIX_ACRONYMS, SUFFIX_ACRONYMS_AMBIGUOUS, SUFFIX_WORDS,
733
789
  )
734
- from nameparser.config.titles import GIVEN_NAME_TITLES, TITLES
790
+ from nameparser.config._titles_2_6_0 import TITLES_2_6_0
791
+ from nameparser.config.titles import (
792
+ GIVEN_NAME_TITLES, TITLES, TITLES_AMBIGUOUS)
735
793
  return {
736
794
  "prefixes": PARTICLES,
737
795
  "suffix_acronyms": SUFFIX_ACRONYMS,
738
796
  "suffix_not_acronyms": SUFFIX_WORDS,
739
797
  "suffix_acronyms_ambiguous": SUFFIX_ACRONYMS_AMBIGUOUS,
740
- "titles": TITLES,
798
+ # 2.6.0's titles, less any word that is no title now: one a
799
+ # later release made a title only inside a title phrase
800
+ # (rules/H9) stays a v1 title, so remove() and `in` answer as
801
+ # they did; _build_snapshot reads it by the phrase
802
+ "titles": TITLES | (TITLES_2_6_0 & TITLES_AMBIGUOUS),
741
803
  "first_name_titles": GIVEN_NAME_TITLES,
742
804
  "conjunctions": CONJUNCTIONS,
743
805
  "bound_first_names": BOUND_GIVEN_NAMES,
@@ -780,9 +842,11 @@ class Constants:
780
842
 
781
843
  The module-level ``CONSTANTS`` singleton (below) has ``_shared``
782
844
  flipped to ``True``: any mutation reached through it emits
783
- ``DeprecationWarning`` pointing at ``Lexicon``/``Policy`` and
784
- ``HumanName(constants=...)``. A private ``Constants()`` never
785
- warns -- only the shared instance is on the 3.0 removal path.
845
+ ``DeprecationWarning`` pointing at ``Lexicon``/``Policy``. Since 2.8
846
+ constructing a ``Constants``, copying one with ``copy()`` and
847
+ loading one from a pickle warn too, since the whole class goes in
848
+ 3.0; the singleton is built without the warning, so importing
849
+ ``nameparser`` stays quiet.
786
850
  """
787
851
 
788
852
  _shared = False # the CONSTANTS singleton flips this to True
@@ -830,6 +894,41 @@ class Constants:
830
894
  regexes: object = _UNSET_KWARG,
831
895
  patronymic_name_order: bool = False,
832
896
  middle_name_as_last: bool = False,
897
+ ) -> None:
898
+ # every construction warns, at the caller's line; the warnings
899
+ # registry, not a flag here, dedups repeats from one line
900
+ # (decisions/3-0-reevaluations, 2026-10-28)
901
+ warnings.warn(CONSTANTS_DEPRECATION, DeprecationWarning,
902
+ stacklevel=2)
903
+ self._init(
904
+ prefixes=prefixes, suffix_acronyms=suffix_acronyms,
905
+ suffix_not_acronyms=suffix_not_acronyms,
906
+ suffix_acronyms_ambiguous=suffix_acronyms_ambiguous,
907
+ titles=titles, first_name_titles=first_name_titles,
908
+ conjunctions=conjunctions, bound_first_names=bound_first_names,
909
+ non_first_name_prefixes=non_first_name_prefixes,
910
+ capitalization_exceptions=capitalization_exceptions,
911
+ regexes=regexes, patronymic_name_order=patronymic_name_order,
912
+ middle_name_as_last=middle_name_as_last)
913
+
914
+ def _init(
915
+ self,
916
+ *,
917
+ prefixes: Iterable[str] | object = _UNSET_KWARG,
918
+ suffix_acronyms: Iterable[str] | object = _UNSET_KWARG,
919
+ suffix_not_acronyms: Iterable[str] | object = _UNSET_KWARG,
920
+ suffix_acronyms_ambiguous: Iterable[str] | object = _UNSET_KWARG,
921
+ titles: Iterable[str] | object = _UNSET_KWARG,
922
+ first_name_titles: Iterable[str] | object = _UNSET_KWARG,
923
+ conjunctions: Iterable[str] | object = _UNSET_KWARG,
924
+ bound_first_names: Iterable[str] | object = _UNSET_KWARG,
925
+ non_first_name_prefixes: Iterable[str] | object = _UNSET_KWARG,
926
+ capitalization_exceptions:
927
+ Mapping[str, str] | Iterable[tuple[str, str]] | object
928
+ = _UNSET_KWARG,
929
+ regexes: object = _UNSET_KWARG,
930
+ patronymic_name_order: bool = False,
931
+ middle_name_as_last: bool = False,
833
932
  ) -> None:
834
933
  # v1.4 parity constructor kwargs (#238/#242/#244 hardening); the
835
934
  # signature is spelled out rather than **kwargs so an unknown
@@ -915,19 +1014,20 @@ class Constants:
915
1014
  """
916
1015
 
917
1016
  def _bump(self) -> None:
918
- # stacklevel=3 is exact for the direct scalar-assignment path
919
- # (user code -> Constants.__setattr__ -> here) and lands one
920
- # frame short -- inside the manager's own add()/remove()/
921
- # __setitem__ -- for the indirect manager-mutation path (user
922
- # code -> manager method -> _changed() -> here), since a
923
- # single warn() call can't be exact for both call depths at
924
- # once. Either way the warning still fires from this module,
925
- # not the manager's true caller, which is enough: only the
926
- # DeprecationWarning's presence/category/message are load-
927
- # bearing (see the specified test), not the reported line.
1017
+ # A fixed stacklevel lands on the caller only for one call depth:
1018
+ # a scalar arrives through __setattr__, a set's add() through
1019
+ # _changed(), a dict's update() and |= through __setitem__ per
1020
+ # key. Walk out of this module instead, as _lexicon's
1021
+ # _warn_dead_entry does, so the warning names the caller's line;
1022
+ # attributed here, Python's default filters hid it.
928
1023
  if self._shared:
1024
+ level = 2
1025
+ frame: FrameType | None = sys._getframe(1)
1026
+ while (frame is not None
1027
+ and frame.f_globals.get("__name__") == __name__):
1028
+ frame, level = frame.f_back, level + 1
929
1029
  warnings.warn(_SHARED_MUTATION_MESSAGE, DeprecationWarning,
930
- stacklevel=3)
1030
+ stacklevel=level)
931
1031
  object.__setattr__(self, "_generation", self._generation + 1)
932
1032
 
933
1033
  def __setattr__(self, name: str, value: object) -> None:
@@ -981,7 +1081,27 @@ class Constants:
981
1081
  v1 (which deepcopied __dict__): attributes OUTSIDE the known
982
1082
  field surface -- e.g. ad-hoc names stashed on the instance --
983
1083
  are not carried by copy() or pickling; only the enumerated
984
- fields survive.""" # #260
1084
+ fields survive. Warns since 2.8, as construction does: a copy
1085
+ is a new private ``Constants``, and ``CONSTANTS.copy()`` builds
1086
+ one without calling the constructor.""" # #260
1087
+ warnings.warn(CONSTANTS_DEPRECATION, DeprecationWarning,
1088
+ stacklevel=2)
1089
+ return self._copy()
1090
+
1091
+ def __copy__(self) -> Constants:
1092
+ new = object.__new__(type(self))
1093
+ _quiet_setstate(new, self.__getstate__(), Constants,
1094
+ CONSTANTS_PICKLE_DEPRECATION)
1095
+ return new
1096
+
1097
+ def __deepcopy__(self, memo: dict[int, object]) -> Constants:
1098
+ new = object.__new__(type(self))
1099
+ memo[id(self)] = new
1100
+ _quiet_setstate(new, copy.deepcopy(self.__getstate__(), memo),
1101
+ Constants, CONSTANTS_PICKLE_DEPRECATION)
1102
+ return new
1103
+
1104
+ def _copy(self) -> Constants:
985
1105
  # An independent instance with its own generation counter and
986
1106
  # its own manager callbacks -- not a shared-state alias like a
987
1107
  # naive attribute-for-attribute copy would produce. v1's copy()
@@ -1045,10 +1165,15 @@ class Constants:
1045
1165
  removal path.
1046
1166
  """
1047
1167
  from nameparser.config.conjunctions import CONJUNCTIONS_AMBIGUOUS
1168
+ from nameparser.config.given_names import GIVEN_NAME_ABBREVIATIONS
1048
1169
  from nameparser.config.maiden_markers import MAIDEN_MARKERS
1049
- from nameparser.config.suffixes import GLUED_HONORIFICS
1170
+ from nameparser.config.suffixes import (
1171
+ AGNOMENS, GLUED_HONORIFICS, SUFFIX_WORDS_AMBIGUOUS)
1050
1172
  from nameparser.config.surnames import KOREAN_SURNAMES
1051
- from nameparser.config.titles import AFTER_GIVEN_TITLES
1173
+ from nameparser.config._titles_2_6_0 import TITLES_2_6_0
1174
+ from nameparser.config.titles import (
1175
+ AFTER_GIVEN_TITLES, TITLE_MODIFIERS, TITLE_PHRASES, TITLE_STYLES,
1176
+ TITLES_AMBIGUOUS)
1052
1177
  # Every set field and every capitalization_exceptions key passes
1053
1178
  # _v1_matchable BEFORE any set algebra below: an entry v1 could
1054
1179
  # never match, or one Lexicon folds to empty (#582), is dropped
@@ -1071,6 +1196,27 @@ class Constants:
1071
1196
  conjunctions = matchable("conjunctions")
1072
1197
  bound = matchable("bound_first_names")
1073
1198
  titles = matchable("titles")
1199
+ # A title 2.6.0 listed that a later release made a title only
1200
+ # inside a title phrase (rules/H9) is in v1's default `titles`,
1201
+ # where 2.6.0 had it, so remove(), discard() and `in` answer as
1202
+ # they did (decisions/H9, 2026-10-26). Kept, it is read as the
1203
+ # default reads it, by the phrase; removed, it is no title at
1204
+ # all, as removing it made it in 2.6.0. The one 2.6.0 reading
1205
+ # with no v1 spelling left is the word as a title everywhere:
1206
+ # v1 can say "a title" or "not a title", and "a title" now means
1207
+ # the phrase reading. A word 2.6.0 never listed (`dean`) is not
1208
+ # in `moved`, so adding it is still a title everywhere, the 1.4
1209
+ # recipe the phrase test honours below. Compared folded, as the
1210
+ # two cross-set computations below are (#582), so a spelling
1211
+ # Lexicon folds onto a moved word (`king。`) is read as it is.
1212
+ moved = TITLES_2_6_0 & TITLES_AMBIGUOUS
1213
+ kept_moved = frozenset(w for w in titles if _normalize(w) in moved)
1214
+ phrase_only = ((TITLES_AMBIGUOUS - moved) | kept_moved
1215
+ if titles else frozenset())
1216
+ # ...and the three intersections with the phrase vocabulary read
1217
+ # the same folded spellings, or a folded moved word kept its
1218
+ # phrase reading but lost its modifier one (`wing。`)
1219
+ any_title = frozenset(_normalize(w) for w in titles | phrase_only)
1074
1220
  first_name_titles = matchable("first_name_titles")
1075
1221
  non_given = matchable("non_first_name_prefixes")
1076
1222
  ambiguous_acronyms = matchable("suffix_acronyms_ambiguous") & acronyms
@@ -1136,7 +1282,7 @@ class Constants:
1136
1282
  # v1 also had, later, at capitalize().
1137
1283
  try:
1138
1284
  lexicon = Lexicon(
1139
- titles=titles,
1285
+ titles=titles - kept_moved,
1140
1286
  # TRANSLATE, do not filter. The two versions build the same
1141
1287
  # lookup key differently: v1 joins the raw title run and
1142
1288
  # then applies lc(), which strips only the whole string's
@@ -1193,6 +1339,15 @@ class Constants:
1193
1339
  # through this intersection.
1194
1340
  conjunctions_ambiguous=CONJUNCTIONS_AMBIGUOUS & conjunctions,
1195
1341
  bound_given_names=bound,
1342
+ # likewise no v1 manager: an abbreviated given name
1343
+ # reading over rules/H2's shape is 2.7 behavior (#789),
1344
+ # so the set rides in the snapshot only, passed whole.
1345
+ # No v1 knob reaches it, and none needs to: a word a v1
1346
+ # caller ADDS to `titles` is a title wherever it stands,
1347
+ # since the leading peel asks the title vocabulary before
1348
+ # it weighs the shape, as it did in 1.4
1349
+ # (`c.titles.add("chas")`)
1350
+ given_name_abbreviations=GIVEN_NAME_ABBREVIATIONS,
1196
1351
  # v1 Constants has no manager for these (#274 is 2.0
1197
1352
  # behavior); the data module is the only source
1198
1353
  maiden_markers=MAIDEN_MARKERS,
@@ -1216,6 +1371,51 @@ class Constants:
1216
1371
  # so the intersection is kept for `_snapshot() ==
1217
1372
  # Lexicon.default()` legibility, as conjunctions_ambiguous'.
1218
1373
  after_given_titles=AFTER_GIVEN_TITLES & titles,
1374
+ # likewise no v1 manager: a suffix word that is also a
1375
+ # name is 2.7 behavior (#752), so it rides in the
1376
+ # snapshot only. Intersect with the word set, as the
1377
+ # tails are: Lexicon enforces the subset, and deleting
1378
+ # the suffix word is the one v1 knob, which turns the
1379
+ # reading off. agnomens is consulted only inside it, so
1380
+ # its intersection is kept for legibility alone.
1381
+ # A v1 caller who added such a word to suffix_acronyms
1382
+ # had it read as a suffix through the acronym branch,
1383
+ # so the word leaves the gate rather than raising at
1384
+ # Lexicon's word-gate check (folded, as the two
1385
+ # cross-set computations above are).
1386
+ suffix_words_ambiguous=frozenset(
1387
+ w for w in SUFFIX_WORDS_AMBIGUOUS & suffix_words
1388
+ if _normalize(w) not in {
1389
+ _normalize(a) for a in acronyms - ambiguous_acronyms}),
1390
+ agnomens=AGNOMENS & SUFFIX_WORDS_AMBIGUOUS & suffix_words,
1391
+ # likewise no v1 manager: the title phrase (rules/H9) is
1392
+ # 2.7 behavior (#752), so its sets ride in the snapshot
1393
+ # only. A title that may be a given name is not in
1394
+ # TITLES, so v1's `titles` holds one only where 2.6.0
1395
+ # listed it (`moved`, above, read by the phrase), and one
1396
+ # 2.6.0 never listed that a v1 caller ADDS to `titles` is
1397
+ # a title wherever it stands, as it was in 1.4
1398
+ # (`c.titles.add("dean")`, the documented recipe): the
1399
+ # phrase test reads a word of titles_ambiguous only where
1400
+ # it is not in titles. The
1401
+ # knob that reaches the other three is deleting a title,
1402
+ # which turns its modifier, style or phrase reading off
1403
+ # with it; Lexicon checks none of them, so the
1404
+ # intersections are kept for legibility, as
1405
+ # after_given_titles' is.
1406
+ # ...save where the caller emptied `titles`, v1's way to
1407
+ # turn title detection off (customize.rst). A caller who
1408
+ # REPLACES the list keeps the phrase reading of the words
1409
+ # 2.6.0 never listed: v1 has no knob for them, and what a
1410
+ # replaced list means for words it never held is not
1411
+ # something it says. A moved word reads by whether the
1412
+ # replaced list holds it, as any v1 title does
1413
+ titles_ambiguous=phrase_only,
1414
+ title_modifiers=TITLE_MODIFIERS & any_title,
1415
+ title_styles=TITLE_STYLES & any_title,
1416
+ title_phrases=frozenset(
1417
+ p for p in TITLE_PHRASES
1418
+ if all(w in any_title for w in p.split(" "))),
1219
1419
  # TupleManager is dict[str, object] (v1 parity: values were
1220
1420
  # never statically str-typed); every real entry is a str,
1221
1421
  # same assumption _DelimiterManager's sentinel lookup makes.
@@ -1290,7 +1490,22 @@ class Constants:
1290
1490
  # DROPPED (#255: empty is always '' in 2.0).
1291
1491
  state = {k: v for k, v in state.items()
1292
1492
  if k != "empty_attribute_default"}
1293
- self.__init__() # type: ignore[misc] # defaults, then overlay
1493
+ warnings.warn(CONSTANTS_PICKLE_DEPRECATION, DeprecationWarning,
1494
+ stacklevel=2)
1495
+ self._restore(state)
1496
+
1497
+ def _restore(self, state: dict[str, object]) -> None:
1498
+ # defaults, then overlay. A subclass's own __init__ runs here as
1499
+ # it did before 2.8; its super().__init__() would warn that a
1500
+ # Constants was constructed, which a restore is not
1501
+ if type(self).__init__ is Constants.__init__:
1502
+ self._init()
1503
+ else:
1504
+ with warnings.catch_warnings():
1505
+ warnings.filterwarnings(
1506
+ "ignore", message=re.escape(CONSTANTS_DEPRECATION),
1507
+ category=DeprecationWarning)
1508
+ self.__init__() # type: ignore[misc]
1294
1509
  # (managers re-wrapped below so _on_change points at THIS
1295
1510
  # instance, not whatever produced the incoming state)
1296
1511
  managers: dict[str, SetManager] = {}
@@ -1331,5 +1546,7 @@ class Constants:
1331
1546
  object.__setattr__(self, name, state[name])
1332
1547
 
1333
1548
 
1334
- CONSTANTS = Constants()
1549
+ # built past __init__, so importing nameparser never warns
1550
+ CONSTANTS = object.__new__(Constants)
1551
+ CONSTANTS._init()
1335
1552
  object.__setattr__(CONSTANTS, "_shared", True)
@@ -7,6 +7,7 @@ Layering: facade layer -- may import anything public plus _render.
7
7
  """
8
8
  from __future__ import annotations
9
9
 
10
+ import copy
10
11
  import dataclasses
11
12
  import warnings
12
13
  from collections.abc import Iterator, Mapping
@@ -29,7 +30,8 @@ from typing import Any
29
30
  import nameparser.config # noqa: F401
30
31
 
31
32
  import nameparser._render as _render
32
- from nameparser._config_shim import CONSTANTS, Constants, _cached_parser
33
+ from nameparser._config_shim import (CONSTANTS, MIGRATION_GUIDE, Constants,
34
+ _cached_parser, _quiet_setstate)
33
35
  from nameparser._lexicon import _normalize, _spells_an_initial
34
36
  from nameparser._parser import Parser
35
37
  from nameparser._types import (FOLDED_TAG, UNCLASSIFIED_TAG,
@@ -60,6 +62,19 @@ _V1_HOOKS = (
60
62
  # whole module is deleted with the facade layer in 3.0.
61
63
  _WARNED_SUBCLASSES: set[type] = set()
62
64
 
65
+ # The 2.8 removal messages (decisions/3-0-reevaluations, 2026-10-28);
66
+ # the code each offers is pinned by tests/v2/test_deprecations.py.
67
+ HUMANNAME_DEPRECATION = (
68
+ "HumanName is deprecated and will be removed in 3.0; use "
69
+ "nameparser.parse(text), whose fields are given and family where "
70
+ f"HumanName has first and last. See {MIGRATION_GUIDE}"
71
+ )
72
+ HUMANNAME_PICKLE_DEPRECATION = (
73
+ "HumanName pickles stop loading in 3.0, when HumanName is removed; "
74
+ "store parse(text).as_dict() or the original string instead. "
75
+ f"See {MIGRATION_GUIDE}"
76
+ )
77
+
63
78
 
64
79
  def _empty_parsed() -> ParsedName:
65
80
  return ParsedName(original="", tokens=(), ambiguities=())
@@ -71,7 +86,8 @@ class HumanName:
71
86
  resolved from the bound ``Constants`` shim (dirty-tracked via its
72
87
  ``_generation``). Keeps every v1 spelling (``first``/``last`` over
73
88
  the core ``given``/``family``); the v1 parsing hooks are never
74
- called (#280). Deleted with the facade layer in 3.0.
89
+ called (#280). Deprecated since 2.8, warning at every construction,
90
+ and deleted with the facade layer in 3.0.
75
91
  """
76
92
 
77
93
  def __init__(
@@ -101,6 +117,12 @@ class HumanName:
101
117
  raise TypeError(
102
118
  f"constants must be a Constants instance, got {constants!r}"
103
119
  )
120
+ # every construction warns, at the caller's line (a subclass's
121
+ # super().__init__() is its author's line); never once per
122
+ # process, so a dependency's first HumanName cannot consume the
123
+ # warning meant for the caller's own
124
+ warnings.warn(HUMANNAME_DEPRECATION, DeprecationWarning,
125
+ stacklevel=2)
104
126
  self._warn_overridden_hooks()
105
127
  self._C = constants
106
128
  self._snapshot_gen = -1 # forces first resolve
@@ -151,7 +173,8 @@ class HumanName:
151
173
  f"{cls.__name__} overrides v1 parsing hooks "
152
174
  f"({', '.join(overridden)}) that the 2.0 facade never "
153
175
  f"calls; parsing is delegated to the core Parser. "
154
- f"Migrate to the Lexicon/Policy API. See "
176
+ f"Migrate to the Lexicon/Policy API; HumanName is "
177
+ f"removed in 3.0. See "
155
178
  f"https://github.com/derek73/python-nameparser/issues/280",
156
179
  DeprecationWarning, stacklevel=3)
157
180
 
@@ -888,6 +911,28 @@ class HumanName:
888
911
  return state
889
912
 
890
913
  def __setstate__(self, state: dict[str, Any]) -> None:
914
+ # unpickling skips __init__, so the construction warning never
915
+ # reaches a pickle; this one does, at the pickle.loads line
916
+ warnings.warn(HUMANNAME_PICKLE_DEPRECATION, DeprecationWarning,
917
+ stacklevel=2)
918
+ self._restore(state)
919
+
920
+ def __copy__(self) -> HumanName:
921
+ # copy.copy/deepcopy go through the state hooks, a subclass's
922
+ # included, and a copy is no pickle load (_quiet_setstate)
923
+ new = object.__new__(type(self))
924
+ _quiet_setstate(new, self.__getstate__(), HumanName,
925
+ HUMANNAME_PICKLE_DEPRECATION)
926
+ return new
927
+
928
+ def __deepcopy__(self, memo: dict[int, object]) -> HumanName:
929
+ new = object.__new__(type(self))
930
+ memo[id(self)] = new
931
+ _quiet_setstate(new, copy.deepcopy(self.__getstate__(), memo),
932
+ HumanName, HUMANNAME_PICKLE_DEPRECATION)
933
+ return new
934
+
935
+ def _restore(self, state: dict[str, Any]) -> None:
891
936
  c = state.get("C")
892
937
  self._C = CONSTANTS if c is None else c
893
938
  self._snapshot_gen = -1