nameparser 2.8.0__tar.gz → 2.8.2__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.8.0/nameparser.egg-info → nameparser-2.8.2}/PKG-INFO +10 -1
  2. {nameparser-2.8.0 → nameparser-2.8.2}/README.rst +9 -0
  3. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_config_shim.py +16 -15
  4. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_lexicon.py +179 -16
  5. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_version.py +5 -5
  6. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/config/__init__.py +13 -9
  7. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/config/_deprecated.py +84 -5
  8. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/config/_invariants.py +1 -1
  9. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/config/bound_given_names.py +15 -0
  10. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/config/capitalization.py +20 -0
  11. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/config/conjunctions.py +20 -0
  12. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/config/given_names.py +15 -0
  13. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/config/maiden_markers.py +20 -0
  14. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/config/particles.py +16 -0
  15. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/config/regexes.py +26 -0
  16. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/config/suffixes.py +14 -2
  17. nameparser-2.8.2/nameparser/config/surnames.py +40 -0
  18. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/config/titles.py +19 -2
  19. {nameparser-2.8.0 → nameparser-2.8.2/nameparser.egg-info}/PKG-INFO +10 -1
  20. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser.egg-info/SOURCES.txt +2 -0
  21. {nameparser-2.8.0 → nameparser-2.8.2}/pyproject.toml +5 -0
  22. {nameparser-2.8.0 → nameparser-2.8.2}/tests/removals.py +6 -1
  23. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_config_aliases.py +26 -4
  24. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_contracts.py +13 -5
  25. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_deprecations.py +4 -1
  26. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_lexicon.py +5 -0
  27. nameparser-2.8.2/tests/v2/test_removals_2_8_1.py +456 -0
  28. {nameparser-2.8.0 → nameparser-2.8.2}/AUTHORS +0 -0
  29. {nameparser-2.8.0 → nameparser-2.8.2}/LICENSE +0 -0
  30. {nameparser-2.8.0 → nameparser-2.8.2}/MANIFEST.in +0 -0
  31. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/__init__.py +0 -0
  32. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/__main__.py +0 -0
  33. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_facade.py +0 -0
  34. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_locale.py +0 -0
  35. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_parser.py +0 -0
  36. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_pipeline/__init__.py +0 -0
  37. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_pipeline/_assemble.py +0 -0
  38. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_pipeline/_assign.py +0 -0
  39. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_pipeline/_classify.py +0 -0
  40. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_pipeline/_comma.py +0 -0
  41. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_pipeline/_extract.py +0 -0
  42. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_pipeline/_group.py +0 -0
  43. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_pipeline/_pieces.py +0 -0
  44. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_pipeline/_post_rules.py +0 -0
  45. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_pipeline/_script_segment.py +0 -0
  46. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_pipeline/_segment.py +0 -0
  47. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_pipeline/_state.py +0 -0
  48. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_pipeline/_tokenize.py +0 -0
  49. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_pipeline/_vocab.py +0 -0
  50. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_policy.py +0 -0
  51. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_render.py +0 -0
  52. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/_types.py +0 -0
  53. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/config/_compiled.py +0 -0
  54. /nameparser-2.8.0/nameparser/config/surnames.py → /nameparser-2.8.2/nameparser/config/_ko_surnames.py +0 -0
  55. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/config/_titles_2_6_0.py +0 -0
  56. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/config/bound_first_names.py +0 -0
  57. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/config/prefixes.py +0 -0
  58. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/locales/__init__.py +0 -0
  59. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/locales/ja.py +0 -0
  60. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/locales/ru.py +0 -0
  61. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/locales/tr_az.py +0 -0
  62. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/locales/zh.py +0 -0
  63. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/parser.py +0 -0
  64. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/py.typed +0 -0
  65. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/util.py +0 -0
  66. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/ak.toml +0 -0
  67. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/am.toml +0 -0
  68. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/ar.toml +0 -0
  69. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/as.toml +0 -0
  70. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/be.toml +0 -0
  71. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/bg.toml +0 -0
  72. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/bin.toml +0 -0
  73. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/bn.toml +0 -0
  74. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/bo.toml +0 -0
  75. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/bs.toml +0 -0
  76. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/ca.toml +0 -0
  77. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/cs.toml +0 -0
  78. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/da.toml +0 -0
  79. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/de.toml +0 -0
  80. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/el.toml +0 -0
  81. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/en.toml +0 -0
  82. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/es.toml +0 -0
  83. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/et.toml +0 -0
  84. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/fa.toml +0 -0
  85. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/ff.toml +0 -0
  86. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/fi.toml +0 -0
  87. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/fil.toml +0 -0
  88. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/fr.toml +0 -0
  89. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/ga.toml +0 -0
  90. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/gd.toml +0 -0
  91. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/gu.toml +0 -0
  92. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/ha.toml +0 -0
  93. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/he.toml +0 -0
  94. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/hi.toml +0 -0
  95. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/hr.toml +0 -0
  96. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/hu.toml +0 -0
  97. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/hy.toml +0 -0
  98. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/id.toml +0 -0
  99. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/is.toml +0 -0
  100. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/it.toml +0 -0
  101. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/ja.toml +0 -0
  102. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/ka.toml +0 -0
  103. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/kk.toml +0 -0
  104. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/km.toml +0 -0
  105. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/kn.toml +0 -0
  106. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/ko.toml +0 -0
  107. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/kok.toml +0 -0
  108. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/lo.toml +0 -0
  109. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/lt.toml +0 -0
  110. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/lv.toml +0 -0
  111. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/mi.toml +0 -0
  112. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/mk.toml +0 -0
  113. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/ml.toml +0 -0
  114. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/mn.toml +0 -0
  115. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/mr.toml +0 -0
  116. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/ms.toml +0 -0
  117. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/mul.toml +0 -0
  118. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/my.toml +0 -0
  119. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/nb.toml +0 -0
  120. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/ne.toml +0 -0
  121. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/nl.toml +0 -0
  122. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/nn.toml +0 -0
  123. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/or.toml +0 -0
  124. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/pa.toml +0 -0
  125. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/pl.toml +0 -0
  126. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/pt-BR.toml +0 -0
  127. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/pt.toml +0 -0
  128. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/ro.toml +0 -0
  129. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/ru.toml +0 -0
  130. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/sd.toml +0 -0
  131. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/shn.toml +0 -0
  132. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/si.toml +0 -0
  133. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/sk.toml +0 -0
  134. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/sl.toml +0 -0
  135. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/sr.toml +0 -0
  136. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/sv.toml +0 -0
  137. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/sw.toml +0 -0
  138. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/ta.toml +0 -0
  139. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/te.toml +0 -0
  140. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/th.toml +0 -0
  141. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/ti.toml +0 -0
  142. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/tr.toml +0 -0
  143. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/uk.toml +0 -0
  144. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/und.toml +0 -0
  145. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/ur.toml +0 -0
  146. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/vi.toml +0 -0
  147. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/yi.toml +0 -0
  148. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/yo.toml +0 -0
  149. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/zh.toml +0 -0
  150. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser/vocab/zu.toml +0 -0
  151. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser.egg-info/dependency_links.txt +0 -0
  152. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser.egg-info/requires.txt +0 -0
  153. {nameparser-2.8.0 → nameparser-2.8.2}/nameparser.egg-info/top_level.txt +0 -0
  154. {nameparser-2.8.0 → nameparser-2.8.2}/setup.cfg +0 -0
  155. {nameparser-2.8.0 → nameparser-2.8.2}/tests/__init__.py +0 -0
  156. {nameparser-2.8.0 → nameparser-2.8.2}/tests/base.py +0 -0
  157. {nameparser-2.8.0 → nameparser-2.8.2}/tests/conftest.py +0 -0
  158. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_bound_given_names.py +0 -0
  159. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_brute_force.py +0 -0
  160. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_capitalization.py +0 -0
  161. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_comma_variants.py +0 -0
  162. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_conjunctions.py +0 -0
  163. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_constants.py +0 -0
  164. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_east_slavic_patronymic_order.py +0 -0
  165. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_first_name.py +0 -0
  166. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_initials.py +0 -0
  167. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_middle_name_as_last.py +0 -0
  168. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_nicknames.py +0 -0
  169. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_output_format.py +0 -0
  170. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_particles.py +0 -0
  171. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_python_api.py +0 -0
  172. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_suffixes.py +0 -0
  173. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_titles.py +0 -0
  174. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_turkic_patronymic_order.py +0 -0
  175. {nameparser-2.8.0 → nameparser-2.8.2}/tests/test_variations.py +0 -0
  176. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/__init__.py +0 -0
  177. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/_differential_fixtures.py +0 -0
  178. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/cases.py +0 -0
  179. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/conftest.py +0 -0
  180. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/design_docs.py +0 -0
  181. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/pipeline/__init__.py +0 -0
  182. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/pipeline/test_assemble.py +0 -0
  183. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/pipeline/test_assign.py +0 -0
  184. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/pipeline/test_classify.py +0 -0
  185. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/pipeline/test_comma.py +0 -0
  186. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/pipeline/test_extract.py +0 -0
  187. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/pipeline/test_group.py +0 -0
  188. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/pipeline/test_pieces.py +0 -0
  189. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/pipeline/test_post_rules.py +0 -0
  190. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/pipeline/test_script_segment.py +0 -0
  191. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/pipeline/test_segment.py +0 -0
  192. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/pipeline/test_state.py +0 -0
  193. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/pipeline/test_tokenize.py +0 -0
  194. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/pipeline/test_vocab.py +0 -0
  195. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/rules_budget.py +0 -0
  196. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/rules_doc.py +0 -0
  197. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/rules_skeleton.py +0 -0
  198. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_benchmark.py +0 -0
  199. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_cases.py +0 -0
  200. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_cldr_agreement.py +0 -0
  201. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_cldr_corpus.py +0 -0
  202. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_cli.py +0 -0
  203. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_config_shim.py +0 -0
  204. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_corpora.py +0 -0
  205. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_corpus_build.py +0 -0
  206. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_design_docs.py +0 -0
  207. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_differential.py +0 -0
  208. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_doc_citations.py +0 -0
  209. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_doc_spellings.py +0 -0
  210. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_facade.py +0 -0
  211. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_facade_cases.py +0 -0
  212. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_gnd_corpus.py +0 -0
  213. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_layering.py +0 -0
  214. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_local_agreement.py +0 -0
  215. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_local_corpora.py +0 -0
  216. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_local_jrc.py +0 -0
  217. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_local_orcid.py +0 -0
  218. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_local_patentsview.py +0 -0
  219. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_locale.py +0 -0
  220. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_locales.py +0 -0
  221. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_measure.py +0 -0
  222. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_name_usage_tool.py +0 -0
  223. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_order_correspondence.py +0 -0
  224. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_parser.py +0 -0
  225. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_policy.py +0 -0
  226. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_properties.py +0 -0
  227. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_regex_sync.py +0 -0
  228. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_render.py +0 -0
  229. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_reprs.py +0 -0
  230. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_rules_budget.py +0 -0
  231. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_rules_doc.py +0 -0
  232. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_rules_doc_grammar.py +0 -0
  233. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_rules_skeleton.py +0 -0
  234. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_snapshot.py +0 -0
  235. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_types.py +0 -0
  236. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_vocab.py +0 -0
  237. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_wikidata_corpus.py +0 -0
  238. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_wikidata_native_corpus.py +0 -0
  239. {nameparser-2.8.0 → nameparser-2.8.2}/tests/v2/test_word_report.py +0 -0
  240. {nameparser-2.8.0 → nameparser-2.8.2}/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.8.0
3
+ Version: 2.8.2
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
@@ -37,6 +37,15 @@ composable, and locale packs are opt-in.
37
37
 
38
38
  📣 **nameparser 2.8 is out, the last 2.x feature release.**
39
39
 
40
+ ⚠️ **2.8.1 adds three more DeprecationWarnings** for things 3.0 changes:
41
+ configuring a ``Lexicon`` through its set fields
42
+ (``Lexicon.default().add(titles={"dean"})``), reading the vocabulary
43
+ constants in ``nameparser.config`` (``TITLES`` and the rest; 2.8.2 adds
44
+ ``surnames``), and ``nameparser.config.regexes``. A test run that turns
45
+ warnings into errors (``-W error``, or pytest's ``filterwarnings =
46
+ error``) fails where your code does any of these; see the `migration
47
+ guide <https://nameparser.readthedocs.io/en/stable/migrate.html#deprecated-in-2-8-1>`__.
48
+
40
49
  ⚠️ **HumanName is deprecated, and 3.0 removes it.** From 2.8 the 1.x
41
50
  compatibility layer — ``HumanName``, ``Constants`` and the shared
42
51
  ``CONSTANTS`` — emits a ``DeprecationWarning`` where your code
@@ -9,6 +9,15 @@ composable, and locale packs are opt-in.
9
9
 
10
10
  📣 **nameparser 2.8 is out, the last 2.x feature release.**
11
11
 
12
+ ⚠️ **2.8.1 adds three more DeprecationWarnings** for things 3.0 changes:
13
+ configuring a ``Lexicon`` through its set fields
14
+ (``Lexicon.default().add(titles={"dean"})``), reading the vocabulary
15
+ constants in ``nameparser.config`` (``TITLES`` and the rest; 2.8.2 adds
16
+ ``surnames``), and ``nameparser.config.regexes``. A test run that turns
17
+ warnings into errors (``-W error``, or pytest's ``filterwarnings =
18
+ error``) fails where your code does any of these; see the `migration
19
+ guide <https://nameparser.readthedocs.io/en/stable/migrate.html#deprecated-in-2-8-1>`__.
20
+
12
21
  ⚠️ **HumanName is deprecated, and 3.0 removes it.** From 2.8 the 1.x
13
22
  compatibility layer — ``HumanName``, ``Constants`` and the shared
14
23
  ``CONSTANTS`` — emits a ``DeprecationWarning`` where your code
@@ -533,8 +533,8 @@ class _RegexesProxy:
533
533
  def _regexes() -> Mapping[str, object]:
534
534
  # deferred import: see the module docstring's note on why every
535
535
  # nameparser.config.<data module> import in this file is lazy
536
- from nameparser.config.regexes import REGEXES
537
- return REGEXES
536
+ from nameparser.config.regexes import _REGEXES
537
+ return _REGEXES
538
538
 
539
539
  #: dict methods this read-only proxy deliberately does not carry.
540
540
  #: Without this, __getattr__ reports them as missing regexes and
@@ -777,18 +777,19 @@ def _quiet_setstate(new: object, state: dict[str, object], base: type,
777
777
 
778
778
 
779
779
  def _default_vocab() -> dict[str, frozenset[str]]:
780
- # v1 data modules stay the single vocabulary source through 2.x
780
+ # the compiled vocabulary, read from _compiled: the public modules'
781
+ # constants warn at a caller's read since 2.8.1 (#1007)
781
782
  # (same rule as Lexicon.default()).
782
- from nameparser.config.bound_given_names import BOUND_GIVEN_NAMES
783
- from nameparser.config.conjunctions import CONJUNCTIONS
784
- from nameparser.config.particles import (
783
+ from nameparser.config._compiled import BOUND_GIVEN_NAMES
784
+ from nameparser.config._compiled import CONJUNCTIONS
785
+ from nameparser.config._compiled import (
785
786
  NON_GIVEN_NAME_PARTICLES, PARTICLES,
786
787
  )
787
- from nameparser.config.suffixes import (
788
+ from nameparser.config._compiled import (
788
789
  SUFFIX_ACRONYMS, SUFFIX_ACRONYMS_AMBIGUOUS, SUFFIX_WORDS,
789
790
  )
790
791
  from nameparser.config._titles_2_6_0 import TITLES_2_6_0
791
- from nameparser.config.titles import (
792
+ from nameparser.config._compiled import (
792
793
  GIVEN_NAME_TITLES, TITLES, TITLES_AMBIGUOUS)
793
794
  return {
794
795
  "prefixes": PARTICLES,
@@ -971,7 +972,7 @@ class Constants:
971
972
  self, name,
972
973
  SetManager(value, _on_change=self._bump, _field=name)) # type: ignore[arg-type]
973
974
  if capitalization_exceptions is _UNSET_KWARG:
974
- from nameparser.config.capitalization import (
975
+ from nameparser.config._compiled import (
975
976
  CAPITALIZATION_EXCEPTIONS,
976
977
  )
977
978
  capitalization_exceptions = CAPITALIZATION_EXCEPTIONS
@@ -1164,14 +1165,14 @@ class Constants:
1164
1165
  singleton -- only direct attribute mutation is on the 3.0
1165
1166
  removal path.
1166
1167
  """
1167
- from nameparser.config.conjunctions import CONJUNCTIONS_AMBIGUOUS
1168
- from nameparser.config.given_names import GIVEN_NAME_ABBREVIATIONS
1169
- from nameparser.config.maiden_markers import MAIDEN_MARKERS
1170
- from nameparser.config.suffixes import (
1168
+ from nameparser.config._compiled import CONJUNCTIONS_AMBIGUOUS
1169
+ from nameparser.config._compiled import GIVEN_NAME_ABBREVIATIONS
1170
+ from nameparser.config._compiled import MAIDEN_MARKERS
1171
+ from nameparser.config._compiled import (
1171
1172
  AGNOMENS, GLUED_HONORIFICS, SUFFIX_WORDS_AMBIGUOUS)
1172
- from nameparser.config.surnames import KOREAN_SURNAMES
1173
+ from nameparser.config._ko_surnames import KOREAN_SURNAMES
1173
1174
  from nameparser.config._titles_2_6_0 import TITLES_2_6_0
1174
- from nameparser.config.titles import (
1175
+ from nameparser.config._compiled import (
1175
1176
  AFTER_GIVEN_TITLES, TITLE_MODIFIERS, TITLE_PHRASES, TITLE_STYLES,
1176
1177
  TITLES_AMBIGUOUS)
1177
1178
  # Every set field and every capitalization_exceptions key passes
@@ -12,7 +12,7 @@ import functools
12
12
  import sys
13
13
  import unicodedata
14
14
  import warnings
15
- from collections.abc import Iterable, Mapping, Set
15
+ from collections.abc import Iterable, Iterator, Mapping, Set
16
16
  from dataclasses import dataclass, field
17
17
  from types import FrameType, MappingProxyType
18
18
  from typing import cast
@@ -337,6 +337,101 @@ def _warn_dead_entry(message: str) -> None:
337
337
  warnings.warn(message, UserWarning, stacklevel=level)
338
338
 
339
339
 
340
+ #: 2.8.1 (#754): the 3.0 migration guide's table of what each set field
341
+ #: becomes. 3.0 configures a lexicon by reading -- what a word is -- and
342
+ #: refuses these fields with a TypeError naming the readings for the
343
+ #: words given, a translation this release does not attempt: it depends
344
+ #: on the word's other readings, so the guide's row and 3.0's error are
345
+ #: the sources.
346
+ _FIELDS_GUIDE = ("https://nameparser.readthedocs.io/en/latest/migrate.html"
347
+ "#lexicon-fields-readings")
348
+
349
+ #: modules whose frames sit between a caller and a Lexicon's
350
+ #: construction: this one (add, remove, __or__, __init__),
351
+ #: dataclasses.replace and copy.replace
352
+ _CONSTRUCTION_MODULES = (__name__, "dataclasses", "copy")
353
+
354
+
355
+ def _outside_caller(
356
+ construction: bool,
357
+ ) -> tuple[int, Mapping[str, object] | None, str] | None:
358
+ """Where a set-field warning belongs: the stacklevel, counted from
359
+ the function that calls warnings.warn (itself called from the
360
+ method this asks for), of the first frame outside this module,
361
+ dataclasses and copy, with the locals of a dataclasses.replace
362
+ frame passed on the way and the replace the caller called
363
+ (``copy.replace()`` where a copy frame was passed, else
364
+ ``dataclasses.replace()``); or None where the library itself made the
365
+ call -- that frame inside nameparser (Lexicon.default(), the locale
366
+ packs, parser_for, the v1 facade), or, for a construction, a method
367
+ of this module building the lexicon: add() and remove() warn for
368
+ themselves, and __or__ is 3.0's own spelling.
369
+ """
370
+ level = 3
371
+ frame: FrameType | None = sys._getframe(2)
372
+ replacing: Mapping[str, object] | None = None
373
+ replace_call = "dataclasses.replace()"
374
+ while frame is not None:
375
+ module = frame.f_globals.get("__name__")
376
+ if module not in _CONSTRUCTION_MODULES:
377
+ break
378
+ if module == "copy":
379
+ replace_call = "copy.replace()"
380
+ code = frame.f_code.co_name
381
+ if (construction and module == __name__
382
+ and code not in ("__init__", "__post_init__")):
383
+ return None
384
+ if module == "dataclasses" and code in ("replace", "_replace"):
385
+ replacing = frame.f_locals
386
+ frame, level = frame.f_back, level + 1
387
+ caller = "" if frame is None else frame.f_globals.get("__name__", "")
388
+ if caller == "nameparser" or caller.startswith("nameparser."):
389
+ return None
390
+ return level, replacing, replace_call
391
+
392
+
393
+ def _surnames_code(op: str, words: frozenset[str]) -> str:
394
+ """Code doing add() or remove()'s surnames edit the 3.0 way, which
395
+ runs quietly on 2.8.1 too; prose for a long list, whose code would
396
+ swamp the message."""
397
+ if len(words) > 5:
398
+ return (f"join a Lexicon holding those {len(words)} surnames to it "
399
+ f"with |" if op == "add" else
400
+ f"subtract those {len(words)} surnames from lexicon.surnames "
401
+ f"in dataclasses.replace()")
402
+ listed = "{" + ", ".join(map(repr, sorted(words))) + "}"
403
+ return (f"write lexicon | Lexicon(surnames=frozenset({listed}))"
404
+ if op == "add" else
405
+ f"write dataclasses.replace(lexicon, "
406
+ f"surnames=lexicon.surnames - {listed})")
407
+
408
+
409
+ def _warn_set_fields(level: int, spelling: str, fields: Iterable[str],
410
+ surnames: str = "") -> None:
411
+ """Warn at `level` (from _outside_caller) that `spelling`, a call,
412
+ writes `fields`, naming each and the guide row that replaces it.
413
+ surnames gets a sentence of its own, ending on `surnames`, the code
414
+ for the edit (_surnames_code): 3.0 keeps the field, and only add()
415
+ and remove() stop taking it."""
416
+ fields = list(fields)
417
+ internal = [name for name in fields if name != "surnames"]
418
+ parts = []
419
+ if internal:
420
+ parts.append(
421
+ f"{spelling} writes {', '.join(internal)}, which 3.0 makes "
422
+ f"internal and refuses with a TypeError naming the readings "
423
+ f"that replace it for the words given; the guide's \"Lexicon "
424
+ f"fields → readings\" table has a row for each field")
425
+ if "surnames" in fields:
426
+ parts.append(
427
+ f"3.0 keeps surnames a field, but {spelling} no longer takes "
428
+ f"it: {surnames}, which runs on 2.8.1 too")
429
+ warnings.warn(
430
+ f"Lexicon's set fields are deprecated (#754): {'; and '.join(parts)}. "
431
+ f"See {_FIELDS_GUIDE}",
432
+ DeprecationWarning, stacklevel=level)
433
+
434
+
340
435
  def _normset(
341
436
  entries: Iterable[str], field_name: str, warn: bool = True,
342
437
  ) -> frozenset[str]:
@@ -590,7 +685,15 @@ class Lexicon:
590
685
  matched against two adjacent title words (``"chief justice"``).
591
686
  Field docs below show examples, not full
592
687
  contents; inspect any field's shipped vocabulary directly, e.g.
593
- ``Lexicon.default().conjunctions``."""
688
+ ``Lexicon.default().conjunctions``.
689
+
690
+ Deprecated since 2.8.1: 3.0 makes the set fields internal and
691
+ configures vocabulary by reading, so writing one here, through
692
+ ``add``, ``remove`` or ``dataclasses.replace`` warns. ``surnames``
693
+ and ``capitalization_exceptions`` stay fields in 3.0, so passing them
694
+ here or to ``dataclasses.replace`` is quiet, but ``add`` and
695
+ ``remove`` with ``surnames`` warn, since 3.0's take readings only;
696
+ see the migration guide's "Deprecated in 2.8.1"."""
594
697
 
595
698
  #: Pre-nominal titles ("dr", "sir", "capt", ...). Full default
596
699
  #: list: :data:`~nameparser.config.titles.TITLES`.
@@ -696,7 +799,7 @@ class Lexicon:
696
799
  #: written wholly in a script :attr:`Policy.segment_scripts
697
800
  #: <nameparser.Policy.segment_scripts>` activates. The default
698
801
  #: carries the Korean census list
699
- #: (:data:`~nameparser.config.surnames.KOREAN_SURNAMES`); Chinese
802
+ #: (``nameparser/config/_ko_surnames.py``); Chinese
700
803
  #: surnames ship in locales.ZH because Han segmentation is opt-in.
701
804
  surnames: frozenset[str] = frozenset()
702
805
  #: Honorifics, and credential, office and generational words, that
@@ -804,6 +907,41 @@ class Lexicon:
804
907
  default_factory=lambda: MappingProxyType({}))
805
908
 
806
909
  def __post_init__(self) -> None:
910
+ self._validate()
911
+ # 2.8.1 (#754): a caller's construction or dataclasses.replace()
912
+ # with a set field 3.0 refuses. Not from __setstate__, which runs
913
+ # _validate alone: loading a pickle writes no field.
914
+ where = _outside_caller(construction=True)
915
+ if where is not None:
916
+ self._warn_constructed(*where)
917
+
918
+ def _warn_constructed(self, level: int,
919
+ replacing: Mapping[str, object] | None,
920
+ replace_call: str) -> None:
921
+ # surnames is never named here: 3.0 accepts it in Lexicon() and
922
+ # dataclasses.replace(), as it does capitalization_exceptions
923
+ internal = [name for name in _VOCAB_FIELDS if name != "surnames"]
924
+ if replacing is None:
925
+ # a field left at its default is empty, so a non-empty one
926
+ # was passed
927
+ written = [name for name in internal if getattr(self, name)]
928
+ spelling = "Lexicon()"
929
+ else:
930
+ # dataclasses.replace() names the instance `obj`, and 3.13's
931
+ # dataclasses._replace (behind copy.replace) `self`; a field
932
+ # not passed is filled in with the instance's own value
933
+ old = replacing.get("obj", replacing.get("self"))
934
+ changes = replacing.get("changes")
935
+ if not isinstance(old, Lexicon) or not isinstance(changes, Mapping):
936
+ return
937
+ written = [name for name in internal if name in changes
938
+ and changes[name] is not getattr(old, name)]
939
+ spelling = replace_call
940
+ if written:
941
+ # one more frame than __post_init__ itself
942
+ _warn_set_fields(level + 1, spelling, written)
943
+
944
+ def _validate(self) -> None:
807
945
  for name in _VOCAB_FIELDS:
808
946
  object.__setattr__(self, name, _normset(getattr(self, name), name))
809
947
  canonical = _normpairs(self.capitalization_exceptions)
@@ -949,7 +1087,7 @@ class Lexicon:
949
1087
  # without changing its name, so an old pickle would otherwise
950
1088
  # load a semantically inverted lexicon in silence. Rebuilds
951
1089
  # _cap_map for free, which this used to do by hand.
952
- self.__post_init__()
1090
+ self._validate()
953
1091
  # __post_init__ also normalizes, and quietly accepting a
954
1092
  # rewritten value would make this a place where caller data
955
1093
  # changes without a word. A pickle this library wrote is always
@@ -1038,28 +1176,53 @@ class Lexicon:
1038
1176
  return dataclasses.replace(self, **updates) # type: ignore[arg-type]
1039
1177
 
1040
1178
  def add(self, **entries: Iterable[str]) -> Lexicon:
1041
- return self._edit("add", entries)
1179
+ """A copy with each named field's words added. Deprecated since
1180
+ 2.8.1: 3.0's ``add`` takes readings, not fields."""
1181
+ return self._edit_warning("add", entries)
1042
1182
 
1043
1183
  def remove(self, **entries: Iterable[str]) -> Lexicon:
1044
- return self._edit("remove", entries)
1184
+ """A copy with each named field's words removed. Deprecated
1185
+ since 2.8.1: 3.0's ``remove`` takes readings, not fields."""
1186
+ return self._edit_warning("remove", entries)
1187
+
1188
+ def _edit_warning(self, op: str,
1189
+ entries: Mapping[str, Iterable[str]]) -> Lexicon:
1190
+ # 2.8.1 (#754): 3.0's add() and remove() take readings, and
1191
+ # refuse every field keyword, surnames included. The warning
1192
+ # follows a successful edit, so a bad value raises its own
1193
+ # error first. An iterator of surnames is read once here, so the
1194
+ # warning can spell the edit and _edit still sees the words.
1195
+ named = entries.get("surnames")
1196
+ if isinstance(named, Iterator):
1197
+ entries = {**entries, "surnames": tuple(named)}
1198
+ edited = self._edit(op, entries)
1199
+ if entries:
1200
+ where = _outside_caller(construction=False)
1201
+ if where is not None:
1202
+ code = ("" if "surnames" not in entries else _surnames_code(
1203
+ op, _normset(entries["surnames"], "surnames", warn=False)))
1204
+ _warn_set_fields(where[0], f"{op}()", entries, code)
1205
+ return edited
1045
1206
 
1046
1207
 
1047
1208
  @functools.cache
1048
1209
  def _default_lexicon() -> Lexicon:
1049
- # v1 data modules are the single source of vocabulary through 2.x.
1050
- from nameparser.config.bound_given_names import BOUND_GIVEN_NAMES
1051
- from nameparser.config.given_names import GIVEN_NAME_ABBREVIATIONS
1052
- from nameparser.config.capitalization import CAPITALIZATION_EXCEPTIONS
1053
- from nameparser.config.conjunctions import (
1210
+ # The compiled vocabulary, read from _compiled rather than the public
1211
+ # nameparser.config modules, whose constants warn at a caller's read
1212
+ # since 2.8.1 (#1007), and surnames.py's since 2.8.2.
1213
+ from nameparser.config._compiled import BOUND_GIVEN_NAMES
1214
+ from nameparser.config._compiled import GIVEN_NAME_ABBREVIATIONS
1215
+ from nameparser.config._compiled import CAPITALIZATION_EXCEPTIONS
1216
+ from nameparser.config._compiled import (
1054
1217
  CONJUNCTIONS, CONJUNCTIONS_AMBIGUOUS)
1055
- from nameparser.config.maiden_markers import MAIDEN_MARKERS
1056
- from nameparser.config.particles import NON_GIVEN_NAME_PARTICLES, PARTICLES
1057
- from nameparser.config.suffixes import (
1218
+ from nameparser.config._compiled import MAIDEN_MARKERS
1219
+ from nameparser.config._compiled import NON_GIVEN_NAME_PARTICLES, PARTICLES
1220
+ from nameparser.config._compiled import (
1058
1221
  AGNOMENS, GLUED_HONORIFICS, SUFFIX_ACRONYMS, SUFFIX_ACRONYMS_AMBIGUOUS,
1059
1222
  SUFFIX_WORDS, SUFFIX_WORDS_AMBIGUOUS,
1060
1223
  )
1061
- from nameparser.config.surnames import KOREAN_SURNAMES
1062
- from nameparser.config.titles import (
1224
+ from nameparser.config._ko_surnames import KOREAN_SURNAMES
1225
+ from nameparser.config._compiled import (
1063
1226
  AFTER_GIVEN_TITLES, GIVEN_NAME_TITLES, TITLE_MODIFIERS, TITLE_PHRASES,
1064
1227
  TITLE_STYLES, TITLES, TITLES_AMBIGUOUS)
1065
1228
 
@@ -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, 8, 0)` is already
8
- #: true here while 2.8.0 is unreleased. Compare `__version__` instead
7
+ #: cannot carry the dev marker, so `VERSION >= (2, 8, 1)` is already
8
+ #: true here while 2.8.1 is unreleased. Compare `__version__` instead
9
9
  #: where that distinction matters.
10
- VERSION = (2, 8, 0)
10
+ VERSION = (2, 8, 2)
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.8.0.dev0, which sorts above 2.7.0 and BELOW 2.8.0, so an install
17
- #: from master can never masquerade as the release it precedes.
16
+ #: 2.8.1.dev0, which sorts above 2.8.0 and BELOW 2.8.1, so an install
17
+ #: from the 2.x branch can never masquerade as the release it precedes.
18
18
  PRE_RELEASE = ""
19
19
  __version__ = ".".join(map(str, VERSION)) + PRE_RELEASE
@@ -7,11 +7,14 @@ Two unrelated things share this package. The names re-exported below --
7
7
  ``RegexTupleManager`` -- are v1 compatibility surface, and go with the
8
8
  rest of the facade in 3.0. The vocabulary data modules beside them
9
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).
10
+ ...) hold the word lists the 2.x :class:`~nameparser.Lexicon` is built
11
+ from, named for its fields since 2.2 (#293); since 2.6 each (all but
12
+ ``surnames.py``) re-exports sets compiled from the per-word TOML files
13
+ in ``nameparser/vocab/`` (#737). 3.0 removes them too, and makes
14
+ ``surnames`` private; since 2.8.1 reading one of their constants warns,
15
+ naming what 3.0 offers in its place (#1007), as reading
16
+ ``nameparser.config.regexes`` does (#1008), and since 2.8.2 reading
17
+ ``surnames.KOREAN_SURNAMES`` does.
15
18
 
16
19
  ``RegexTupleManager`` is served from the shim purely for pickle
17
20
  compatibility (through ``__getattr__`` since 2.8, so loading such a blob
@@ -22,10 +25,11 @@ resolves and reconstructs that nested object before
22
25
  blob raises ``AttributeError`` looking up the class, not a clean
23
26
  compatibility failure.
24
27
  """
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
28
+ # Maintainer note, deliberately outside the docstring: 3.0 removes the
29
+ # five shim names below and the vocabulary DATA modules, surnames made
30
+ # private (#1007, #1008, superseding decisions/3-0-reevaluations's
31
+ # 2026-10-28 entry, which kept the modules); the modules warn per
32
+ # constant since 2.8.1, surnames since 2.8.2. Constants and CONSTANTS are imported eagerly -- Constants
29
33
  # warns at construction, the CONSTANTS singleton when mutated -- while
30
34
  # the three managers resolve through __getattr__ so that each caller's
31
35
  # read warns, at the caller's line, and nameparser's own reads (from
@@ -46,10 +46,55 @@ _MESSAGE = (
46
46
  "use {new_module}.{new} instead."
47
47
  )
48
48
 
49
+ #: 3.0's migration guide, which every 2.8.1 removal message points at:
50
+ #: Read the Docs' "latest" builds master, the 3.0 cycle.
51
+ MIGRATION_GUIDE = "https://nameparser.readthedocs.io/en/latest/migrate.html"
52
+
53
+ # 2.8.1: the live names of a module 3.0 deletes (#1007, #1008). Every
54
+ # message opens "nameparser.config.", names the removal and closes on
55
+ # the guide; pyproject's filterwarnings ignores the family by message,
56
+ # and tests/v2/test_removals_2_8_1.py asserts each one.
57
+ _REMOVED_MESSAGE = (
58
+ "{module}.{name} is deprecated and will be removed in 3.0 ({issue}); "
59
+ "{replacement}. See {guide}"
60
+ )
61
+
62
+
63
+ def words_of(args: str, which: str = "") -> str:
64
+ """The replacement clause for a vocabulary constant: the 3.0 call
65
+ listing a kind's words (``Lexicon.words``, #1007). Without `which`
66
+ the call lists exactly the set's words over the default vocabulary
67
+ (checked against master, 2026-11-02); with it the call lists more or
68
+ other words, and `which` says what the set held instead."""
69
+ call = f"in 3.0, Lexicon.default().words({args})"
70
+ if not which:
71
+ return f"{call} lists the same words"
72
+ return (f"{call} lists every word of that kind, where this set held "
73
+ f"{which}; Lexicon.default().readings_of(word) says what one "
74
+ f"word is")
75
+
76
+
77
+ def removal_message(module: str, name: str) -> str | None:
78
+ """The 2.8.1 removal message for ``module.name``, or ``None`` where
79
+ 3.0 keeps the name. Reads the module's hook, so it warns nothing."""
80
+ hook = vars(importlib.import_module(module)).get("__getattr__")
81
+ removed: Mapping[str, str] = getattr(hook, "removed_messages", {})
82
+ return removed.get(name)
83
+
84
+
85
+ def _quiet_value(module: str, name: str) -> Any: # noqa: ANN401
86
+ loaded = importlib.import_module(module)
87
+ hook = vars(loaded).get("__getattr__")
88
+ held: Mapping[str, Any] = getattr(hook, "removed_values", {})
89
+ return held[name] if name in held else getattr(loaded, name)
90
+
49
91
 
50
92
  def alias_getattr(
51
93
  module: str,
52
94
  aliases: Mapping[str, tuple[str, str]],
95
+ removed: Mapping[str, str] | None = None,
96
+ namespace: dict[str, Any] | None = None,
97
+ issue: str = "#1007",
53
98
  ) -> tuple[Callable[[str], Any], Callable[[], list[str]]]:
54
99
  """Build the ``__getattr__``/``__dir__`` pair for a module carrying
55
100
  deprecated vocabulary names.
@@ -84,9 +129,33 @@ def alias_getattr(
84
129
  module: mypy reads no module ``__getattr__`` for the alias-bearing
85
130
  modules any more, and does not analyze the ``else`` branch it is
86
131
  assigned in. It stays ``Any`` as what ``getattr`` itself returns.
132
+
133
+ ``removed`` (2.8.1) maps each LIVE name 3.0 deletes to the clause
134
+ naming its 3.0 replacement, and ``namespace`` is the module's
135
+ ``globals()``: each such name is popped out of it into the hook, so
136
+ the module keeps its assignment statement -- which is where autodoc
137
+ finds the constant's documentation -- while every read reaches
138
+ ``__getattr__`` and warns at the reader's line. The library reads
139
+ these values from ``nameparser.config._compiled`` (and regexes.py's
140
+ ``_REGEXES``), never through here, so only a caller's read warns.
87
141
  """
142
+ removed = dict(removed or {})
143
+ values: dict[str, Any] = {}
144
+ if removed:
145
+ if namespace is None:
146
+ raise TypeError("alias_getattr: removed names need namespace=")
147
+ values = {name: namespace.pop(name) for name in removed}
148
+ messages = {
149
+ name: _REMOVED_MESSAGE.format(
150
+ module=module, name=name, issue=issue, replacement=replacement,
151
+ guide=MIGRATION_GUIDE)
152
+ for name, replacement in removed.items()}
88
153
 
89
154
  def __getattr__(name: str) -> Any: # noqa: ANN401
155
+ if name in values:
156
+ # 2: the reading frame, as for an alias below
157
+ warnings.warn(messages[name], DeprecationWarning, stacklevel=2)
158
+ return values[name]
90
159
  target = aliases.get(name)
91
160
  if target is None:
92
161
  raise AttributeError(f"module {module!r} has no attribute {name!r}")
@@ -94,11 +163,18 @@ def alias_getattr(
94
163
  # resolved BEFORE warning, so a mistyped alias target fails as
95
164
  # a ModuleNotFoundError or an AttributeError from here rather
96
165
  # than first advising the reader to move to a path that does
97
- # not exist.
98
- value = getattr(importlib.import_module(new_module), new_name)
166
+ # not exist. Quietly: a target 3.0 removes too is held by its
167
+ # module's hook, whose own warning would name this file.
168
+ value = _quiet_value(new_module, new_name)
169
+ message = _MESSAGE.format(
170
+ module=module, old=name, new_module=new_module, new=new_name)
171
+ # one warning saying so, rather than advice to move to a name
172
+ # that warns in its turn
173
+ also = removal_message(new_module, new_name)
174
+ if also is not None:
175
+ message += " That name is removed in 3.0 too: " + also
99
176
  warnings.warn(
100
- _MESSAGE.format(
101
- module=module, old=name, new_module=new_module, new=new_name),
177
+ message,
102
178
  DeprecationWarning,
103
179
  # 2: the frame that touched the name -- for the
104
180
  # `from nameparser.config.prefixes import ...` form, the
@@ -126,7 +202,7 @@ def alias_getattr(
126
202
  # are still nameable, so they are what is left to list (#356).
127
203
  live = sys.modules.get(module)
128
204
  names = set(vars(live)) if live is not None else set()
129
- return sorted(names | set(aliases))
205
+ return sorted(names | set(aliases) | set(values))
130
206
 
131
207
  # The table itself, reachable without tripping a warning. __all__ is
132
208
  # hand-written per module (it must stay in SOURCE order for autodoc,
@@ -137,5 +213,8 @@ def alias_getattr(
137
213
  # and no AttributeError. test_config_aliases
138
214
  # ::test_every_alias_table_row_reaches_star_import cross-checks them.
139
215
  __getattr__.deprecated_aliases = dict(aliases) # type: ignore[attr-defined]
216
+ # the 2.8.1 removals, read by removal_message and _quiet_value
217
+ __getattr__.removed_messages = messages # type: ignore[attr-defined]
218
+ __getattr__.removed_values = values # type: ignore[attr-defined]
140
219
 
141
220
  return __getattr__, __dir__
@@ -2,7 +2,7 @@
2
2
 
3
3
  Since the word lists moved to nameparser/vocab/*.toml, whose compiler
4
4
  (tools/vocab/build.py) refuses an unnormalized spelling itself, the one
5
- constant this module still checks is ``surnames.KOREAN_SURNAMES``, a
5
+ constant this module still checks is ``_ko_surnames.KOREAN_SURNAMES``, a
6
6
  segmentation list outside the per-word model.
7
7
 
8
8
  A constant is looked up in normalized form, so a stray capital or a
@@ -25,3 +25,18 @@ BOUND_GIVEN_NAMES: frozenset[str] = _compiled.BOUND_GIVEN_NAMES
25
25
  # the constant, is bound by a star import as though it were vocabulary
26
26
  # (#356).
27
27
  __all__ = ["BOUND_GIVEN_NAMES"]
28
+
29
+
30
+ # 2.8.1: 3.0 deletes this module (#1007), so each constant is served by
31
+ # __getattr__ and warns at the line that reads it, naming what 3.0
32
+ # offers in its place (alias_getattr's docstring says how the
33
+ # assignment above stays documented). The library reads _compiled, never
34
+ # this module. Hidden from mypy as alias_getattr explains; goes in 3.0.
35
+ from typing import TYPE_CHECKING # noqa: E402
36
+
37
+ from nameparser.config._deprecated import alias_getattr, words_of # noqa: E402
38
+
39
+ if not TYPE_CHECKING:
40
+ __getattr__, __dir__ = alias_getattr(__name__, {}, removed={
41
+ "BOUND_GIVEN_NAMES": words_of('kind="bound_given"'),
42
+ }, namespace=globals())
@@ -44,3 +44,23 @@ its own. The map also carries the Irish patronymic particles ``ó``,
44
44
  (``de la Vega``), Irish writes these capitalized (``Liam Ó Murchú``),
45
45
  and a mask outranks the particle lowercase (rules/R4).
46
46
  """
47
+
48
+
49
+ # 2.8.1: 3.0 deletes this module (#1007), so each constant is served by
50
+ # __getattr__ and warns at the line that reads it, naming what 3.0
51
+ # offers in its place (alias_getattr's docstring says how the
52
+ # assignment above stays documented). The library reads _compiled, never
53
+ # this module. Hidden from mypy as alias_getattr explains; goes in 3.0.
54
+ from typing import TYPE_CHECKING # noqa: E402
55
+
56
+ from nameparser.config._deprecated import alias_getattr # noqa: E402
57
+
58
+ if not TYPE_CHECKING:
59
+ __getattr__, __dir__ = alias_getattr(__name__, {}, removed={
60
+ "CAPITALIZATION_EXCEPTIONS": "in 3.0, read Lexicon.default().capitalization_exceptions, which stays a field",
61
+ }, namespace=globals())
62
+
63
+ # Star imports read __all__ and never the module __getattr__ -- see the
64
+ # note in prefixes.py -- so without it a star import would bind none
65
+ # of the names served above, silently.
66
+ __all__ = ["CAPITALIZATION_EXCEPTIONS"]
@@ -24,3 +24,23 @@ Single-letter entries of :data:`CONJUNCTIONS` that read as an initial,
24
24
  not a connective, in a name written wholly in one case.
25
25
  """
26
26
 
27
+
28
+ # 2.8.1: 3.0 deletes this module (#1007), so each constant is served by
29
+ # __getattr__ and warns at the line that reads it, naming what 3.0
30
+ # offers in its place (alias_getattr's docstring says how the
31
+ # assignment above stays documented). The library reads _compiled, never
32
+ # this module. Hidden from mypy as alias_getattr explains; goes in 3.0.
33
+ from typing import TYPE_CHECKING # noqa: E402
34
+
35
+ from nameparser.config._deprecated import alias_getattr, words_of # noqa: E402
36
+
37
+ if not TYPE_CHECKING:
38
+ __getattr__, __dir__ = alias_getattr(__name__, {}, removed={
39
+ "CONJUNCTIONS": words_of('kind="conjunction"'),
40
+ "CONJUNCTIONS_AMBIGUOUS": words_of('kind="conjunction", may_be_initial=True'),
41
+ }, namespace=globals())
42
+
43
+ # Star imports read __all__ and never the module __getattr__ -- see the
44
+ # note in prefixes.py -- so without it a star import would bind none
45
+ # of the names served above, silently.
46
+ __all__ = ["CONJUNCTIONS", "CONJUNCTIONS_AMBIGUOUS"]
@@ -16,3 +16,18 @@ GIVEN_NAME_ABBREVIATIONS: frozenset[str] = _compiled.GIVEN_NAME_ABBREVIATIONS
16
16
  # Star imports read __all__ and never the module __getattr__ -- see the
17
17
  # note in prefixes.py.
18
18
  __all__ = ["GIVEN_NAME_ABBREVIATIONS"]
19
+
20
+
21
+ # 2.8.1: 3.0 deletes this module (#1007), so each constant is served by
22
+ # __getattr__ and warns at the line that reads it, naming what 3.0
23
+ # offers in its place (alias_getattr's docstring says how the
24
+ # assignment above stays documented). The library reads _compiled, never
25
+ # this module. Hidden from mypy as alias_getattr explains; goes in 3.0.
26
+ from typing import TYPE_CHECKING # noqa: E402
27
+
28
+ from nameparser.config._deprecated import alias_getattr, words_of # noqa: E402
29
+
30
+ if not TYPE_CHECKING:
31
+ __getattr__, __dir__ = alias_getattr(__name__, {}, removed={
32
+ "GIVEN_NAME_ABBREVIATIONS": words_of('kind="given"', "the given names whose reading carries same_as"),
33
+ }, namespace=globals())