nameparser 2.7.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 (240) hide show
  1. {nameparser-2.7.0/nameparser.egg-info → nameparser-2.8.0}/PKG-INFO +22 -17
  2. {nameparser-2.7.0 → nameparser-2.8.0}/README.rst +21 -16
  3. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_config_shim.py +153 -22
  4. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_facade.py +48 -3
  5. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_pipeline/_post_rules.py +1 -1
  6. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_pipeline/_vocab.py +6 -2
  7. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_version.py +4 -4
  8. nameparser-2.8.0/nameparser/config/__init__.py +76 -0
  9. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/config/_compiled.py +97 -8
  10. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/config/titles.py +1 -1
  11. nameparser-2.8.0/nameparser/util.py +46 -0
  12. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/en.toml +37 -17
  13. {nameparser-2.7.0 → nameparser-2.8.0/nameparser.egg-info}/PKG-INFO +22 -17
  14. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser.egg-info/SOURCES.txt +4 -0
  15. {nameparser-2.7.0 → nameparser-2.8.0}/pyproject.toml +9 -0
  16. nameparser-2.8.0/tests/removals.py +26 -0
  17. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_constants.py +9 -3
  18. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_first_name.py +7 -3
  19. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_python_api.py +11 -8
  20. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/cases.py +182 -7
  21. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_cases.py +10 -4
  22. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_config_shim.py +6 -1
  23. nameparser-2.8.0/tests/v2/test_deprecations.py +457 -0
  24. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_facade.py +3 -0
  25. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_local_agreement.py +82 -6
  26. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_local_corpora.py +147 -7
  27. nameparser-2.8.0/tests/v2/test_local_orcid.py +561 -0
  28. nameparser-2.8.0/tests/v2/test_local_patentsview.py +366 -0
  29. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_measure.py +5 -2
  30. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_name_usage_tool.py +100 -0
  31. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_properties.py +1 -1
  32. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_render.py +4 -0
  33. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_word_report.py +65 -65
  34. nameparser-2.7.0/nameparser/config/__init__.py +0 -43
  35. nameparser-2.7.0/nameparser/util.py +0 -14
  36. {nameparser-2.7.0 → nameparser-2.8.0}/AUTHORS +0 -0
  37. {nameparser-2.7.0 → nameparser-2.8.0}/LICENSE +0 -0
  38. {nameparser-2.7.0 → nameparser-2.8.0}/MANIFEST.in +0 -0
  39. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/__init__.py +0 -0
  40. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/__main__.py +0 -0
  41. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_lexicon.py +0 -0
  42. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_locale.py +0 -0
  43. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_parser.py +0 -0
  44. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_pipeline/__init__.py +0 -0
  45. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_pipeline/_assemble.py +0 -0
  46. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_pipeline/_assign.py +0 -0
  47. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_pipeline/_classify.py +0 -0
  48. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_pipeline/_comma.py +0 -0
  49. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_pipeline/_extract.py +0 -0
  50. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_pipeline/_group.py +0 -0
  51. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_pipeline/_pieces.py +0 -0
  52. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_pipeline/_script_segment.py +0 -0
  53. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_pipeline/_segment.py +0 -0
  54. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_pipeline/_state.py +0 -0
  55. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_pipeline/_tokenize.py +0 -0
  56. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_policy.py +0 -0
  57. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_render.py +0 -0
  58. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/_types.py +0 -0
  59. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/config/_deprecated.py +0 -0
  60. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/config/_invariants.py +0 -0
  61. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/config/_titles_2_6_0.py +0 -0
  62. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/config/bound_first_names.py +0 -0
  63. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/config/bound_given_names.py +0 -0
  64. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/config/capitalization.py +0 -0
  65. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/config/conjunctions.py +0 -0
  66. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/config/given_names.py +0 -0
  67. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/config/maiden_markers.py +0 -0
  68. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/config/particles.py +0 -0
  69. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/config/prefixes.py +0 -0
  70. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/config/regexes.py +0 -0
  71. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/config/suffixes.py +0 -0
  72. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/config/surnames.py +0 -0
  73. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/locales/__init__.py +0 -0
  74. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/locales/ja.py +0 -0
  75. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/locales/ru.py +0 -0
  76. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/locales/tr_az.py +0 -0
  77. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/locales/zh.py +0 -0
  78. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/parser.py +0 -0
  79. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/py.typed +0 -0
  80. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/ak.toml +0 -0
  81. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/am.toml +0 -0
  82. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/ar.toml +0 -0
  83. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/as.toml +0 -0
  84. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/be.toml +0 -0
  85. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/bg.toml +0 -0
  86. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/bin.toml +0 -0
  87. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/bn.toml +0 -0
  88. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/bo.toml +0 -0
  89. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/bs.toml +0 -0
  90. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/ca.toml +0 -0
  91. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/cs.toml +0 -0
  92. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/da.toml +0 -0
  93. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/de.toml +0 -0
  94. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/el.toml +0 -0
  95. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/es.toml +0 -0
  96. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/et.toml +0 -0
  97. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/fa.toml +0 -0
  98. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/ff.toml +0 -0
  99. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/fi.toml +0 -0
  100. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/fil.toml +0 -0
  101. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/fr.toml +0 -0
  102. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/ga.toml +0 -0
  103. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/gd.toml +0 -0
  104. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/gu.toml +0 -0
  105. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/ha.toml +0 -0
  106. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/he.toml +0 -0
  107. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/hi.toml +0 -0
  108. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/hr.toml +0 -0
  109. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/hu.toml +0 -0
  110. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/hy.toml +0 -0
  111. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/id.toml +0 -0
  112. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/is.toml +0 -0
  113. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/it.toml +0 -0
  114. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/ja.toml +0 -0
  115. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/ka.toml +0 -0
  116. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/kk.toml +0 -0
  117. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/km.toml +0 -0
  118. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/kn.toml +0 -0
  119. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/ko.toml +0 -0
  120. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/kok.toml +0 -0
  121. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/lo.toml +0 -0
  122. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/lt.toml +0 -0
  123. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/lv.toml +0 -0
  124. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/mi.toml +0 -0
  125. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/mk.toml +0 -0
  126. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/ml.toml +0 -0
  127. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/mn.toml +0 -0
  128. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/mr.toml +0 -0
  129. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/ms.toml +0 -0
  130. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/mul.toml +0 -0
  131. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/my.toml +0 -0
  132. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/nb.toml +0 -0
  133. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/ne.toml +0 -0
  134. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/nl.toml +0 -0
  135. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/nn.toml +0 -0
  136. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/or.toml +0 -0
  137. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/pa.toml +0 -0
  138. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/pl.toml +0 -0
  139. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/pt-BR.toml +0 -0
  140. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/pt.toml +0 -0
  141. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/ro.toml +0 -0
  142. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/ru.toml +0 -0
  143. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/sd.toml +0 -0
  144. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/shn.toml +0 -0
  145. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/si.toml +0 -0
  146. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/sk.toml +0 -0
  147. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/sl.toml +0 -0
  148. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/sr.toml +0 -0
  149. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/sv.toml +0 -0
  150. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/sw.toml +0 -0
  151. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/ta.toml +0 -0
  152. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/te.toml +0 -0
  153. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/th.toml +0 -0
  154. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/ti.toml +0 -0
  155. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/tr.toml +0 -0
  156. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/uk.toml +0 -0
  157. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/und.toml +0 -0
  158. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/ur.toml +0 -0
  159. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/vi.toml +0 -0
  160. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/yi.toml +0 -0
  161. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/yo.toml +0 -0
  162. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/zh.toml +0 -0
  163. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser/vocab/zu.toml +0 -0
  164. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser.egg-info/dependency_links.txt +0 -0
  165. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser.egg-info/requires.txt +0 -0
  166. {nameparser-2.7.0 → nameparser-2.8.0}/nameparser.egg-info/top_level.txt +0 -0
  167. {nameparser-2.7.0 → nameparser-2.8.0}/setup.cfg +0 -0
  168. {nameparser-2.7.0 → nameparser-2.8.0}/tests/__init__.py +0 -0
  169. {nameparser-2.7.0 → nameparser-2.8.0}/tests/base.py +0 -0
  170. {nameparser-2.7.0 → nameparser-2.8.0}/tests/conftest.py +0 -0
  171. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_bound_given_names.py +0 -0
  172. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_brute_force.py +0 -0
  173. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_capitalization.py +0 -0
  174. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_comma_variants.py +0 -0
  175. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_conjunctions.py +0 -0
  176. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_east_slavic_patronymic_order.py +0 -0
  177. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_initials.py +0 -0
  178. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_middle_name_as_last.py +0 -0
  179. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_nicknames.py +0 -0
  180. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_output_format.py +0 -0
  181. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_particles.py +0 -0
  182. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_suffixes.py +0 -0
  183. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_titles.py +0 -0
  184. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_turkic_patronymic_order.py +0 -0
  185. {nameparser-2.7.0 → nameparser-2.8.0}/tests/test_variations.py +0 -0
  186. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/__init__.py +0 -0
  187. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/_differential_fixtures.py +0 -0
  188. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/conftest.py +0 -0
  189. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/design_docs.py +0 -0
  190. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/pipeline/__init__.py +0 -0
  191. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/pipeline/test_assemble.py +0 -0
  192. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/pipeline/test_assign.py +0 -0
  193. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/pipeline/test_classify.py +0 -0
  194. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/pipeline/test_comma.py +0 -0
  195. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/pipeline/test_extract.py +0 -0
  196. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/pipeline/test_group.py +0 -0
  197. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/pipeline/test_pieces.py +0 -0
  198. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/pipeline/test_post_rules.py +0 -0
  199. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/pipeline/test_script_segment.py +0 -0
  200. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/pipeline/test_segment.py +0 -0
  201. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/pipeline/test_state.py +0 -0
  202. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/pipeline/test_tokenize.py +0 -0
  203. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/pipeline/test_vocab.py +0 -0
  204. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/rules_budget.py +0 -0
  205. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/rules_doc.py +0 -0
  206. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/rules_skeleton.py +0 -0
  207. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_benchmark.py +0 -0
  208. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_cldr_agreement.py +0 -0
  209. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_cldr_corpus.py +0 -0
  210. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_cli.py +0 -0
  211. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_config_aliases.py +0 -0
  212. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_contracts.py +0 -0
  213. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_corpora.py +0 -0
  214. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_corpus_build.py +0 -0
  215. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_design_docs.py +0 -0
  216. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_differential.py +0 -0
  217. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_doc_citations.py +0 -0
  218. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_doc_spellings.py +0 -0
  219. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_facade_cases.py +0 -0
  220. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_gnd_corpus.py +0 -0
  221. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_layering.py +0 -0
  222. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_lexicon.py +0 -0
  223. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_local_jrc.py +0 -0
  224. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_locale.py +0 -0
  225. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_locales.py +0 -0
  226. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_order_correspondence.py +0 -0
  227. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_parser.py +0 -0
  228. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_policy.py +0 -0
  229. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_regex_sync.py +0 -0
  230. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_reprs.py +0 -0
  231. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_rules_budget.py +0 -0
  232. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_rules_doc.py +0 -0
  233. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_rules_doc_grammar.py +0 -0
  234. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_rules_skeleton.py +0 -0
  235. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_snapshot.py +0 -0
  236. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_types.py +0 -0
  237. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_vocab.py +0 -0
  238. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_wikidata_corpus.py +0 -0
  239. {nameparser-2.7.0 → nameparser-2.8.0}/tests/v2/test_wikidata_native_corpus.py +0 -0
  240. {nameparser-2.7.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.7.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,27 +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.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
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
46
52
  <https://nameparser.readthedocs.io/en/latest/release_log.html>`__ has
47
- the details.
53
+ the details. Please `open an
54
+ issue <https://github.com/derek73/python-nameparser/issues>`__ for anything
55
+ that parses wrong.
48
56
 
49
57
  Chinese, Japanese and Korean names written in their own scripts are read
50
58
  family-first, unspaced Korean names are split against the census surname
51
59
  list, and CJK honorifics are recognized. See `East Asian names
52
60
  <https://nameparser.readthedocs.io/en/latest/usage.html#east-asian-names>`__.
53
61
 
54
- Existing ``HumanName`` code from 1.x keeps working through 2.x, and most
55
- of it needs no changes; see `Coming from 1.x`_ below. Please `open an
56
- issue <https://github.com/derek73/python-nameparser/issues>`__ for anything
57
- that parses wrong.
58
-
59
62
  Installation
60
63
  ------------
61
64
 
@@ -127,8 +130,10 @@ Learn more
127
130
  Coming from 1.x
128
131
  ----------------
129
132
 
130
- ``HumanName`` and ``CONSTANTS`` keep working in 2.0 — same imports, same
131
- 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
132
137
  deprecations 1.3 and 1.4 announced, so if your test suite runs clean on
133
138
  1.4 under ``python -W error::DeprecationWarning``, you are nearly done.
134
139
  Two things that check will not catch: four removals 1.4 never warned
@@ -7,27 +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.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
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
18
24
  <https://nameparser.readthedocs.io/en/latest/release_log.html>`__ has
19
- the details.
25
+ the details. Please `open an
26
+ issue <https://github.com/derek73/python-nameparser/issues>`__ for anything
27
+ that parses wrong.
20
28
 
21
29
  Chinese, Japanese and Korean names written in their own scripts are read
22
30
  family-first, unspaced Korean names are split against the census surname
23
31
  list, and CJK honorifics are recognized. See `East Asian names
24
32
  <https://nameparser.readthedocs.io/en/latest/usage.html#east-asian-names>`__.
25
33
 
26
- Existing ``HumanName`` code from 1.x keeps working through 2.x, and most
27
- of it needs no changes; see `Coming from 1.x`_ below. Please `open an
28
- issue <https://github.com/derek73/python-nameparser/issues>`__ for anything
29
- that parses wrong.
30
-
31
34
  Installation
32
35
  ------------
33
36
 
@@ -99,8 +102,10 @@ Learn more
99
102
  Coming from 1.x
100
103
  ----------------
101
104
 
102
- ``HumanName`` and ``CONSTANTS`` keep working in 2.0 — same imports, same
103
- 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
104
109
  deprecations 1.3 and 1.4 announced, so if your test suite runs clean on
105
110
  1.4 under ``python -W error::DeprecationWarning``, you are nearly done.
106
111
  Two things that check will not catch: four removals 1.4 never warned
@@ -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]]:
@@ -786,9 +842,11 @@ class Constants:
786
842
 
787
843
  The module-level ``CONSTANTS`` singleton (below) has ``_shared``
788
844
  flipped to ``True``: any mutation reached through it emits
789
- ``DeprecationWarning`` pointing at ``Lexicon``/``Policy`` and
790
- ``HumanName(constants=...)``. A private ``Constants()`` never
791
- 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.
792
850
  """
793
851
 
794
852
  _shared = False # the CONSTANTS singleton flips this to True
@@ -836,6 +894,41 @@ class Constants:
836
894
  regexes: object = _UNSET_KWARG,
837
895
  patronymic_name_order: bool = False,
838
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,
839
932
  ) -> None:
840
933
  # v1.4 parity constructor kwargs (#238/#242/#244 hardening); the
841
934
  # signature is spelled out rather than **kwargs so an unknown
@@ -921,19 +1014,20 @@ class Constants:
921
1014
  """
922
1015
 
923
1016
  def _bump(self) -> None:
924
- # stacklevel=3 is exact for the direct scalar-assignment path
925
- # (user code -> Constants.__setattr__ -> here) and lands one
926
- # frame short -- inside the manager's own add()/remove()/
927
- # __setitem__ -- for the indirect manager-mutation path (user
928
- # code -> manager method -> _changed() -> here), since a
929
- # single warn() call can't be exact for both call depths at
930
- # once. Either way the warning still fires from this module,
931
- # not the manager's true caller, which is enough: only the
932
- # DeprecationWarning's presence/category/message are load-
933
- # 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.
934
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
935
1029
  warnings.warn(_SHARED_MUTATION_MESSAGE, DeprecationWarning,
936
- stacklevel=3)
1030
+ stacklevel=level)
937
1031
  object.__setattr__(self, "_generation", self._generation + 1)
938
1032
 
939
1033
  def __setattr__(self, name: str, value: object) -> None:
@@ -987,7 +1081,27 @@ class Constants:
987
1081
  v1 (which deepcopied __dict__): attributes OUTSIDE the known
988
1082
  field surface -- e.g. ad-hoc names stashed on the instance --
989
1083
  are not carried by copy() or pickling; only the enumerated
990
- 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:
991
1105
  # An independent instance with its own generation counter and
992
1106
  # its own manager callbacks -- not a shared-state alias like a
993
1107
  # naive attribute-for-attribute copy would produce. v1's copy()
@@ -1376,7 +1490,22 @@ class Constants:
1376
1490
  # DROPPED (#255: empty is always '' in 2.0).
1377
1491
  state = {k: v for k, v in state.items()
1378
1492
  if k != "empty_attribute_default"}
1379
- 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]
1380
1509
  # (managers re-wrapped below so _on_change points at THIS
1381
1510
  # instance, not whatever produced the incoming state)
1382
1511
  managers: dict[str, SetManager] = {}
@@ -1417,5 +1546,7 @@ class Constants:
1417
1546
  object.__setattr__(self, name, state[name])
1418
1547
 
1419
1548
 
1420
- CONSTANTS = Constants()
1549
+ # built past __init__, so importing nameparser never warns
1550
+ CONSTANTS = object.__new__(Constants)
1551
+ CONSTANTS._init()
1421
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
@@ -506,7 +506,7 @@ def post_rules(state: ParseState) -> ParseState:
506
506
  # it, and the one name word of a name read family-first. The comma
507
507
  # named no given part, and the order places a word nothing else
508
508
  # decided (O5), so the title decides as it does in 'Sir John'
509
- # ('Elizabeth II, Queen' reads as 'Queen Elizabeth II'), unless
509
+ # ('John III, Sir' reads as 'Sir John III'), unless
510
510
  # something else settled the word (_family_settled).
511
511
  elif (titles and families and not givens and not middles
512
512
  and not _family_settled(state, tokens, families)
@@ -1487,7 +1487,8 @@ def title_readings(texts: Sequence[str], folded: Sequence[str],
1487
1487
  of `title_modifiers` is a title only with a head title behind it in
1488
1488
  its run of titles: any title that is not a modifier, or a word H2's
1489
1489
  shape reads as one, before 'of' ('Mother Superior of the Convent'),
1490
- or in the object of 'of' or 'the' there (decision 10 of
1490
+ behind a title that attaches to it in `title_phrases` ('Princess
1491
+ Royal', #913), or in the object of 'of' or 'the' there (decision 10 of
1491
1492
  decisions/vocabulary-model). Only the leading run is the head: a
1492
1493
  modifier outside it is read as listed, one ending the name being
1493
1494
  H5's. A
@@ -1558,7 +1559,10 @@ def title_readings(texts: Sequence[str], folded: Sequence[str],
1558
1559
  and ((head and i < lead) or (n in contested and n not in titles))):
1559
1560
  reading[i] = behind or linked[i] or (
1560
1561
  i < last and folded[i + 1] in _PHRASE_LINKS
1561
- and folded[i + 1] in conjunctions)
1562
+ and folded[i + 1] in conjunctions) or (
1563
+ # a title before it that attaches to it ('Princess Royal')
1564
+ i > 0 and reading[i - 1]
1565
+ and f"{folded[i - 1]} {n}" in phrases)
1562
1566
  behind = reading[i]
1563
1567
  elif reading[i] or (head and is_title_shaped(texts[i], lexicon)):
1564
1568
  behind = True
@@ -4,16 +4,16 @@
4
4
  #:
5
5
  #: Bumped when a cycle OPENS, not when it ships, so the tree says what
6
6
  #: it is building. Note the cost of the tuple being purely numeric: it
7
- #: cannot carry the dev marker, so `VERSION >= (2, 7, 0)` is already
8
- #: true here while 2.7.0 is unreleased. Compare `__version__` instead
7
+ #: cannot carry the dev marker, so `VERSION >= (2, 8, 0)` is already
8
+ #: true here while 2.8.0 is unreleased. Compare `__version__` instead
9
9
  #: where that distinction matters.
10
- VERSION = (2, 7, 0)
10
+ VERSION = (2, 8, 0)
11
11
  #: PEP 440 pre-release/dev segment appended to the numeric version, or
12
12
  #: "" for a final release. Joined WITHOUT a dot ("2.0.0rc1", not
13
13
  #: "2.0.0.rc1"); setuptools reads __version__ as the package version.
14
14
  #:
15
15
  #: "dev" through the cycle, cleared at release. It normalizes to
16
- #: 2.7.0.dev0, which sorts above 2.6.0 and BELOW 2.7.0, so an install
16
+ #: 2.8.0.dev0, which sorts above 2.7.0 and BELOW 2.8.0, so an install
17
17
  #: from master can never masquerade as the release it precedes.
18
18
  PRE_RELEASE = ""
19
19
  __version__ = ".".join(map(str, VERSION)) + PRE_RELEASE
@@ -0,0 +1,76 @@
1
+ """v1 import-path preservation (mechanisms.md#CONFIG-SHIM-SNAPSHOT):
2
+ the Constants shim
3
+ lives in nameparser._config_shim.
4
+
5
+ Two unrelated things share this package. The names re-exported below --
6
+ ``Constants``, ``CONSTANTS``, ``SetManager``, ``TupleManager``,
7
+ ``RegexTupleManager`` -- are v1 compatibility surface, and go with the
8
+ rest of the facade in 3.0. The vocabulary data modules beside them
9
+ (:mod:`~nameparser.config.titles`, :mod:`~nameparser.config.particles`,
10
+ ...) are not compatibility surface at all: they are the word lists the
11
+ 2.0 :class:`~nameparser.Lexicon` is built from, they are named for its
12
+ fields since 2.2 (#293), and its documentation cross-references them.
13
+ Since 2.6 the word lists (all but ``surnames.py``) re-export sets
14
+ compiled from the per-word TOML files in ``nameparser/vocab/`` (#737).
15
+
16
+ ``RegexTupleManager`` is served from the shim purely for pickle
17
+ compatibility (through ``__getattr__`` since 2.8, so loading such a blob
18
+ warns here too): a v1.4 ``Constants`` blob's ``regexes`` field was
19
+ pickled as ``nameparser.config.RegexTupleManager(...)``, and unpickling
20
+ resolves and reconstructs that nested object before
21
+ ``Constants.__setstate__`` ever runs. Without this name, loading such a
22
+ blob raises ``AttributeError`` looking up the class, not a clean
23
+ compatibility failure.
24
+ """
25
+ # Maintainer note, deliberately outside the docstring: the vocabulary
26
+ # DATA modules keep this package as their home in 3.0 (decided
27
+ # 2026-10-28, decisions/3-0-reevaluations); only the five shim names
28
+ # below go. Constants and CONSTANTS are imported eagerly -- Constants
29
+ # warns at construction, the CONSTANTS singleton when mutated -- while
30
+ # the three managers resolve through __getattr__ so that each caller's
31
+ # read warns, at the caller's line, and nameparser's own reads (from
32
+ # _config_shim directly) stay quiet. The bridge follows
33
+ # config/_deprecated.py's alias_getattr: the hooks sit in the `else` of
34
+ # a TYPE_CHECKING guard, since a module __getattr__ mypy can see turns
35
+ # off its missing-attribute checking for the whole module, and __dir__
36
+ # lists the managers, or autodoc's member scan drops them.
37
+ import sys as _sys
38
+ import warnings as _warnings
39
+ from typing import TYPE_CHECKING
40
+
41
+ import nameparser._config_shim as _config_shim
42
+ from nameparser._config_shim import CONSTANTS as CONSTANTS
43
+ from nameparser._config_shim import Constants as Constants
44
+
45
+ if TYPE_CHECKING:
46
+ from nameparser._config_shim import RegexTupleManager as RegexTupleManager
47
+ from nameparser._config_shim import SetManager as SetManager
48
+ from nameparser._config_shim import TupleManager as TupleManager
49
+ else:
50
+ def __getattr__(name: str) -> object:
51
+ # PEP 562. Per read, never cached into globals, so every
52
+ # reading site warns (why _deprecated.py never caches either).
53
+ # A v1.4 Constants pickle names nameparser.config.
54
+ # RegexTupleManager and SetManager, so loading one warns here as
55
+ # well as in Constants.__setstate__ -- correct, since 3.0
56
+ # refuses that blob.
57
+ message = _config_shim.MANAGER_DEPRECATIONS.get(name)
58
+ if message is None:
59
+ raise AttributeError(
60
+ f"module {__name__!r} has no attribute {name!r}")
61
+ # `from nameparser.config import SetManager` reads the name
62
+ # twice: importlib's _handle_fromlist probes a PACKAGE's
63
+ # fromlist with hasattr() first, then the import statement
64
+ # reads it. Warning on the probe too would report one import
65
+ # twice.
66
+ if _sys._getframe(1).f_code.co_name != "_handle_fromlist":
67
+ _warnings.warn(message, DeprecationWarning, stacklevel=2)
68
+ return getattr(_config_shim, name)
69
+
70
+ def __dir__() -> list[str]:
71
+ # the union, as _deprecated.py's __dir__ explains
72
+ return sorted(set(globals()) | set(_config_shim.MANAGER_DEPRECATIONS))
73
+
74
+ # a star import reads __all__ alone, so listing the managers routes
75
+ # each through __getattr__ and warns, rather than dropping them unwarned
76
+ __all__ = ["CONSTANTS", "Constants", "RegexTupleManager", "SetManager", "TupleManager"]