openrewrite-migrate-python 0.9.0.dev20260604175301__tar.gz → 0.9.0.dev20260606102208__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 (138) hide show
  1. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/PKG-INFO +3 -2
  2. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/pyproject.toml +9 -2
  3. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/__init__.py +16 -0
  4. openrewrite_migrate_python-0.9.0.dev20260606102208/src/openrewrite_migrate_python/migrate/_markers.py +22 -0
  5. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/array_deprecations.py +6 -0
  6. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/ast_deprecations.py +5 -6
  7. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/calendar_deprecations.py +5 -12
  8. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/platform_deprecations.py +6 -1
  9. openrewrite_migrate_python-0.9.0.dev20260606102208/src/openrewrite_migrate_python/migrate/pydantic_deprecations.py +360 -0
  10. openrewrite_migrate_python-0.9.0.dev20260606102208/src/openrewrite_migrate_python/migrate/pydantic_model_fields.py +142 -0
  11. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/threading_deprecations.py +22 -8
  12. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/threading_is_alive_deprecation.py +6 -2
  13. openrewrite_migrate_python-0.9.0.dev20260606102208/src/openrewrite_migrate_python/migrate/upgrade_to_pydantic.py +116 -0
  14. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python.egg-info/PKG-INFO +3 -2
  15. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python.egg-info/SOURCES.txt +5 -0
  16. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python.egg-info/requires.txt +2 -1
  17. openrewrite_migrate_python-0.9.0.dev20260606102208/tests/test_array_deprecations.py +102 -0
  18. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_ast_deprecations.py +40 -0
  19. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_calendar_deprecations.py +37 -0
  20. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_platform_deprecations.py +19 -0
  21. openrewrite_migrate_python-0.9.0.dev20260606102208/tests/test_pydantic_deprecations.py +269 -0
  22. openrewrite_migrate_python-0.9.0.dev20260606102208/tests/test_pydantic_model_fields.py +210 -0
  23. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_threading_deprecations.py +29 -2
  24. openrewrite_migrate_python-0.9.0.dev20260606102208/tests/test_threading_extended.py +216 -0
  25. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_threading_is_alive_deprecation.py +29 -2
  26. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_upgrade_recipes.py +40 -0
  27. openrewrite_migrate_python-0.9.0.dev20260604175301/src/openrewrite_migrate_python/migrate/_markers.py +0 -16
  28. openrewrite_migrate_python-0.9.0.dev20260604175301/tests/test_array_deprecations.py +0 -44
  29. openrewrite_migrate_python-0.9.0.dev20260604175301/tests/test_threading_extended.py +0 -94
  30. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/setup.cfg +0 -0
  31. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/__init__.py +0 -0
  32. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/__init__.py +0 -0
  33. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/_semantically_equal.py +0 -0
  34. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/all_branches_identical.py +0 -0
  35. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/boolean_checks_not_inverted.py +0 -0
  36. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/collapsible_if_statements.py +0 -0
  37. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/merge_identical_branches.py +0 -0
  38. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/remove_duplicate_conditions.py +0 -0
  39. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/remove_self_assignment.py +0 -0
  40. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/remove_unconditional_value_overwrite.py +0 -0
  41. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/simplify_boolean_literal.py +0 -0
  42. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/simplify_redundant_logical_expression.py +0 -0
  43. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/__init__.py +0 -0
  44. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/aifc_migrations.py +0 -0
  45. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/asyncio_coroutine_to_async.py +0 -0
  46. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/asyncio_deprecations.py +0 -0
  47. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/cgi_migrations.py +0 -0
  48. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/cgi_parse_deprecations.py +0 -0
  49. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/collections_abc_migrations.py +0 -0
  50. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/configparser_deprecations.py +0 -0
  51. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/datetime_utc.py +0 -0
  52. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/distutils_deprecations.py +0 -0
  53. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/distutils_migrations.py +0 -0
  54. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/functools_deprecations.py +0 -0
  55. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/future_imports.py +0 -0
  56. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/gettext_deprecations.py +0 -0
  57. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/html_parser_deprecations.py +0 -0
  58. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/imp_migrations.py +0 -0
  59. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/langchain_classic_imports.py +0 -0
  60. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/langchain_community_imports.py +0 -0
  61. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/langchain_provider_imports.py +0 -0
  62. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/locale_deprecations.py +0 -0
  63. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/locale_getdefaultlocale_deprecation.py +0 -0
  64. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/macpath_deprecations.py +0 -0
  65. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/mailcap_migrations.py +0 -0
  66. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/nntplib_migrations.py +0 -0
  67. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/os_deprecations.py +0 -0
  68. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/pathlib_deprecations.py +0 -0
  69. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/pep594_system_migrations.py +0 -0
  70. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/pipes_migrations.py +0 -0
  71. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/pkgutil_deprecations.py +0 -0
  72. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/re_deprecations.py +0 -0
  73. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/removed_modules_312.py +0 -0
  74. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/shutil_deprecations.py +0 -0
  75. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/socket_deprecations.py +0 -0
  76. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/ssl_deprecations.py +0 -0
  77. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/string_formatting.py +0 -0
  78. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/sys_deprecations.py +0 -0
  79. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/sys_last_deprecations.py +0 -0
  80. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/tarfile_deprecations.py +0 -0
  81. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/telnetlib_migrations.py +0 -0
  82. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/tempfile_deprecations.py +0 -0
  83. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/typing_callable.py +0 -0
  84. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/typing_deprecations.py +0 -0
  85. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/typing_union_to_pipe.py +0 -0
  86. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/unittest_deprecations.py +0 -0
  87. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain02.py +0 -0
  88. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain1.py +0 -0
  89. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_python310.py +0 -0
  90. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_python311.py +0 -0
  91. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_python312.py +0 -0
  92. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_python313.py +0 -0
  93. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_python314.py +0 -0
  94. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_python38.py +0 -0
  95. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_python39.py +0 -0
  96. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/urllib_deprecations.py +0 -0
  97. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/uu_migrations.py +0 -0
  98. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/xdrlib_migrations.py +0 -0
  99. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/xml_deprecations.py +0 -0
  100. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python.egg-info/dependency_links.txt +0 -0
  101. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python.egg-info/entry_points.txt +0 -0
  102. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python.egg-info/top_level.txt +0 -0
  103. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_aifc_migrations.py +0 -0
  104. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_asyncio_coroutine_to_async.py +0 -0
  105. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_asyncio_deprecations.py +0 -0
  106. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_cgi_parse_deprecations.py +0 -0
  107. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_change_import_recipes.py +0 -0
  108. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_collections_abc_migrations.py +0 -0
  109. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_configparser_deprecations.py +0 -0
  110. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_datetime_utc.py +0 -0
  111. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_distutils_deprecations.py +0 -0
  112. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_future_imports.py +0 -0
  113. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_gettext_deprecations.py +0 -0
  114. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_html_parser_deprecations.py +0 -0
  115. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_imp_migrations.py +0 -0
  116. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_langchain_classic_imports.py +0 -0
  117. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_langchain_community_imports.py +0 -0
  118. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_langchain_provider_imports.py +0 -0
  119. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_locale_deprecations.py +0 -0
  120. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_locale_getdefaultlocale_deprecation.py +0 -0
  121. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_macpath_deprecations.py +0 -0
  122. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_os_deprecations.py +0 -0
  123. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_pep594_migrations.py +0 -0
  124. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_pep594_system_migrations.py +0 -0
  125. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_pkgutil_deprecations.py +0 -0
  126. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_re_deprecations.py +0 -0
  127. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_removed_modules_312.py +0 -0
  128. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_shutil_deprecations.py +0 -0
  129. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_ssl_deprecations.py +0 -0
  130. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_string_formatting.py +0 -0
  131. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_sys_deprecations.py +0 -0
  132. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_sys_last_deprecations.py +0 -0
  133. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_tarfile_deprecations.py +0 -0
  134. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_typing_callable.py +0 -0
  135. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_typing_deprecations.py +0 -0
  136. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_typing_union_to_pipe.py +0 -0
  137. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_unittest_deprecations.py +0 -0
  138. {openrewrite_migrate_python-0.9.0.dev20260604175301 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_xml_deprecations.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openrewrite-migrate-python
3
- Version: 0.9.0.dev20260604175301
3
+ Version: 0.9.0.dev20260606102208
4
4
  Summary: OpenRewrite recipes for migrating Python codebases to newer Python versions.
5
5
  Author-email: "Moderne Inc." <support@moderne.io>
6
6
  License: Moderne Proprietary
@@ -21,7 +21,8 @@ Classifier: Typing :: Typed
21
21
  Requires-Python: >=3.10
22
22
  Description-Content-Type: text/markdown
23
23
  Provides-Extra: dev
24
- Requires-Dist: openrewrite>=8.74.3; extra == "dev"
24
+ Requires-Dist: openrewrite>=8.84.3; extra == "dev"
25
+ Requires-Dist: pydantic>=2.11; extra == "dev"
25
26
  Requires-Dist: pytest>=8.0.0; extra == "dev"
26
27
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
27
28
  Requires-Dist: black>=24.0.0; extra == "dev"
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "openrewrite-migrate-python"
7
7
  description = "OpenRewrite recipes for migrating Python codebases to newer Python versions."
8
- version = "0.9.0.dev20260604175301" # Updated dynamically during release
8
+ version = "0.9.0.dev20260606102208" # Updated dynamically during release
9
9
  authors = [{ name = "Moderne Inc.", email = "support@moderne.io" }]
10
10
  license = { text = "Moderne Proprietary" }
11
11
  readme = "README.md"
@@ -30,7 +30,14 @@ dependencies = []
30
30
 
31
31
  [project.optional-dependencies]
32
32
  dev = [
33
- "openrewrite>=8.74.3",
33
+ # 8.84.3 brings ty external-type resolution (cross-package supertype chains),
34
+ # J.MethodInvocation.type, sorted insertion in maybe_add_import, and the
35
+ # type_utils comparison helpers — all of which the recipes rely on for
36
+ # type-aware matching. openrewrite is host-provided at runtime, so this floor
37
+ # only affects dev/CI.
38
+ "openrewrite>=8.84.3",
39
+ # Needed so type-attribution tests can resolve `pydantic` types.
40
+ "pydantic>=2.11",
34
41
  "pytest>=8.0.0",
35
42
  "pytest-cov>=4.0.0",
36
43
  "black>=24.0.0",
@@ -63,6 +63,12 @@ from .pep594_system_migrations import (
63
63
  )
64
64
  from .pipes_migrations import FindPipesModule
65
65
  from .pkgutil_deprecations import ReplacePkgutilFindLoader, ReplacePkgutilGetLoader
66
+ from .pydantic_deprecations import (
67
+ FindDeprecatedJsonEncoders,
68
+ FindDeprecatedSchemaGenerator,
69
+ ReplaceFinalFieldWithClassVar,
70
+ )
71
+ from .pydantic_model_fields import ReplaceModelFieldsInstanceAccess
66
72
  from .re_deprecations import ReplaceReTemplate
67
73
  from .removed_modules_312 import FindRemovedModules312
68
74
  from .shutil_deprecations import FindShutilRmtreeOnerror
@@ -105,6 +111,7 @@ from .upgrade_to_python311 import UpgradeToPython311
105
111
  from .upgrade_to_python312 import UpgradeToPython312
106
112
  from .upgrade_to_python313 import UpgradeToPython313
107
113
  from .upgrade_to_python314 import UpgradeToPython314
114
+ from .upgrade_to_pydantic import UpgradeToPydantic210, UpgradeToPydantic211
108
115
  from .urllib_deprecations import FindUrllibParseSplitFunctions, FindUrllibParseToBytes
109
116
  from .uu_migrations import FindUuModule
110
117
  from .xdrlib_migrations import FindXdrlibModule
@@ -230,4 +237,13 @@ __all__ = [
230
237
  "UpgradeToPython312",
231
238
  "UpgradeToPython313",
232
239
  "UpgradeToPython314",
240
+ # Auto-fix: Pydantic
241
+ "ReplaceModelFieldsInstanceAccess",
242
+ "ReplaceFinalFieldWithClassVar",
243
+ # Detection: Pydantic
244
+ "FindDeprecatedSchemaGenerator",
245
+ "FindDeprecatedJsonEncoders",
246
+ # Composite upgrade recipes: Pydantic
247
+ "UpgradeToPydantic210",
248
+ "UpgradeToPydantic211",
233
249
  ]
@@ -0,0 +1,22 @@
1
+ """Shared marker utilities for deprecation recipes."""
2
+
3
+ from typing import Any, Optional
4
+
5
+ from rewrite.markers import Markers, MarkupWarn
6
+ from rewrite.utils import random_id
7
+
8
+
9
+ def mark_deprecated(tree: Any, message: str, detail: Optional[str] = None) -> Any:
10
+ """Attach a ``MarkupWarn`` warning marker flagging a deprecation.
11
+
12
+ ``MarkupWarn`` is OpenRewrite's warning-markup marker (the intended marker
13
+ for deprecations that need manual attention), as opposed to ``SearchResult``
14
+ which denotes a plain search hit. It renders the same comment in printed
15
+ output but surfaces as a warning diagnostic in the tooling.
16
+ """
17
+ full_message = f"{message}: {detail}" if detail else message
18
+ warn_marker = MarkupWarn(random_id(), full_message)
19
+ current_markers = tree.markers
20
+ new_markers_list = list(current_markers.markers) + [warn_marker]
21
+ new_markers = Markers(current_markers.id, new_markers_list)
22
+ return tree.replace(_markers=new_markers)
@@ -18,6 +18,8 @@ from rewrite.python.preconditions import uses_method
18
18
  from rewrite.python.visitor import PythonVisitor
19
19
  from rewrite.java.tree import Identifier, MethodInvocation
20
20
 
21
+ from rewrite.python.type_utils import is_assignable_to
22
+
21
23
  # Define category path: Python > Migrate > Python 3.14
22
24
  _Python314 = [
23
25
  *Python,
@@ -72,6 +74,8 @@ class ReplaceArrayTostring(Recipe):
72
74
  return method
73
75
  if method.name.simple_name != "tostring":
74
76
  return method
77
+ if method.select is None or not is_assignable_to("array.array", method.select.type):
78
+ return method
75
79
 
76
80
  # Replace tostring with tobytes
77
81
  new_name = method.name.replace(_simple_name="tobytes")
@@ -126,6 +130,8 @@ class ReplaceArrayFromstring(Recipe):
126
130
  return method
127
131
  if method.name.simple_name != "fromstring":
128
132
  return method
133
+ if method.select is None or not is_assignable_to("array.array", method.select.type):
134
+ return method
129
135
 
130
136
  # Replace fromstring with frombytes
131
137
  new_name = method.name.replace(_simple_name="frombytes")
@@ -23,6 +23,8 @@ from rewrite.marketplace import Python
23
23
  from rewrite.python.visitor import PythonVisitor
24
24
  from rewrite.java.tree import Identifier, FieldAccess
25
25
 
26
+ from rewrite.python.type_utils import is_of_class_type
27
+
26
28
  # Define category path: Python > Migrate > Python 3.8
27
29
  _Python38 = [
28
30
  *Python,
@@ -44,12 +46,9 @@ def _is_ast_attribute(field_access: FieldAccess, attr_name: str) -> bool:
44
46
  if field_access.name.simple_name != attr_name:
45
47
  return False
46
48
 
47
- # Check if the target is 'ast'
48
- target = field_access.target
49
- if isinstance(target, Identifier) and target.simple_name == "ast":
50
- return True
51
-
52
- return False
49
+ # Confirm the target resolves to the `ast` module (by type, so aliased
50
+ # imports work and a same-named variable is not rewritten).
51
+ return is_of_class_type(field_access.target.type, "ast")
53
52
 
54
53
 
55
54
  def _replace_ast_attribute(field_access: FieldAccess, new_name: str) -> FieldAccess:
@@ -20,6 +20,8 @@ from rewrite.marketplace import Python
20
20
  from rewrite.python.visitor import PythonVisitor
21
21
  from rewrite.java.tree import Identifier, FieldAccess
22
22
 
23
+ from rewrite.python.type_utils import is_of_class_type
24
+
23
25
  # Define category path: Python > Migrate > Python 3.12
24
26
  _Python312 = [
25
27
  *Python,
@@ -53,16 +55,6 @@ CALENDAR_CONSTANTS: Dict[str, str] = {
53
55
  }
54
56
 
55
57
 
56
- def _is_calendar_access(identifier: Identifier) -> bool:
57
- """
58
- Check if this identifier looks like a calendar module constant access.
59
-
60
- Without full type attribution, we check for common patterns.
61
- """
62
- # The identifier itself should be a known calendar constant name
63
- return identifier.simple_name in CALENDAR_CONSTANTS
64
-
65
-
66
58
  @categorize(_Python312)
67
59
  class ReplaceCalendarConstants(Recipe):
68
60
  """
@@ -118,9 +110,10 @@ class ReplaceCalendarConstants(Recipe):
118
110
  if const_name not in CALENDAR_CONSTANTS:
119
111
  return field_access
120
112
 
121
- # Check if the target is the calendar module
113
+ # Confirm the target resolves to the `calendar` module (by type,
114
+ # so aliases work and same-named variables are excluded).
122
115
  target = field_access.target
123
- if isinstance(target, Identifier) and target.simple_name == "calendar":
116
+ if is_of_class_type(target.type, "calendar"):
124
117
  # Replace with uppercase version
125
118
  new_name_str = CALENDAR_CONSTANTS[const_name]
126
119
  new_identifier = name.replace(_simple_name=new_name_str)
@@ -16,6 +16,8 @@ from rewrite.marketplace import Python
16
16
  from rewrite.markers import Markers
17
17
  from rewrite.python.preconditions import uses_method
18
18
  from rewrite.python.visitor import PythonVisitor
19
+
20
+ from rewrite.python.type_utils import is_of_class_type
19
21
  from rewrite.java.support_types import JLeftPadded, JRightPadded, Space
20
22
  from rewrite.java.tree import Identifier, Literal, MethodInvocation
21
23
  from rewrite.python.tree import NamedArgument
@@ -81,7 +83,10 @@ class ReplacePlatformPopen(Recipe):
81
83
  select = method.select
82
84
  if not isinstance(select, Identifier):
83
85
  return method
84
- if select.simple_name != "platform":
86
+ # Confirm the receiver resolves to the `platform` module (by
87
+ # type, so an aliased import works and a same-named variable is
88
+ # not rewritten into a destructive `subprocess.check_output`).
89
+ if not is_of_class_type(select.type, "platform"):
85
90
  return method
86
91
 
87
92
  # Replace platform with subprocess
@@ -0,0 +1,360 @@
1
+ """
2
+ Recipes for Pydantic deprecations introduced in recent minor releases.
3
+
4
+ Currently implemented:
5
+
6
+ - ``ReplaceFinalFieldWithClassVar`` (Pydantic 2.11): rewrites model fields
7
+ annotated as ``Final`` with a default value to use ``ClassVar`` instead.
8
+ Before 2.11 such a field was silently treated as a class variable; the typing
9
+ spec says it should be a real field, so 2.11 deprecates the pattern and tells
10
+ users to make the intent explicit with ``ClassVar``. Rewriting to ``ClassVar``
11
+ preserves the current (pre-2.11) class-variable behavior.
12
+
13
+ - ``FindDeprecatedSchemaGenerator`` (Pydantic 2.10): flags the deprecated
14
+ ``schema_generator`` option in ``ConfigDict(...)``. There is no public
15
+ replacement yet, so it is flagged for review rather than rewritten.
16
+
17
+ - ``FindDeprecatedJsonEncoders``: flags the deprecated ``json_encoders`` option
18
+ in ``ConfigDict(...)``. Its replacements (serializer decorators or
19
+ ``PlainSerializer``) require restructuring, so it is flagged for review.
20
+
21
+ See: https://docs.pydantic.dev/latest/changelog/ and
22
+ https://github.com/pydantic/pydantic/issues/11119
23
+ """
24
+
25
+ from typing import Any, List, Optional, cast
26
+
27
+ from rewrite import ExecutionContext, Recipe, TreeVisitor
28
+ from rewrite.category import CategoryDescriptor
29
+ from rewrite.decorators import categorize
30
+ from rewrite.marketplace import Python
31
+ from rewrite.python.visitor import PythonVisitor
32
+ from rewrite.python.add_import import AddImportOptions, maybe_add_import
33
+ from rewrite.python.remove_import import RemoveImportOptions, maybe_remove_import
34
+ from rewrite.python.type_utils import is_assignable_to
35
+ from rewrite.python.tree import NamedArgument, TypeHint, TypeHintedExpression
36
+ from rewrite.java.support_types import JavaType
37
+ from rewrite.java.tree import (
38
+ Assignment,
39
+ ClassDeclaration,
40
+ FieldAccess,
41
+ Identifier,
42
+ J,
43
+ MethodInvocation,
44
+ ParameterizedType,
45
+ )
46
+
47
+ from ._markers import mark_deprecated
48
+
49
+ # Define category path: Python > Migrate > Pydantic
50
+ _Pydantic = [
51
+ *Python,
52
+ CategoryDescriptor(display_name="Migrate"),
53
+ CategoryDescriptor(display_name="Pydantic"),
54
+ ]
55
+
56
+ # Direct base-class names that mark a class as a Pydantic model — the structural
57
+ # check used when type attribution is unavailable.
58
+ _PYDANTIC_BASES = frozenset({"BaseModel", "RootModel", "BaseSettings"})
59
+
60
+ _SCHEMA_GEN_MSG = (
61
+ "Pydantic 2.10 deprecated the `schema_generator` config option; it was "
62
+ "experimental and is being reworked, and there is no public replacement yet."
63
+ )
64
+
65
+ _JSON_ENCODERS_MSG = (
66
+ "Pydantic deprecated the `json_encoders` config option; use a "
67
+ "`@field_serializer` / `@model_serializer`, or `Annotated[..., "
68
+ "PlainSerializer(...)]`, instead."
69
+ )
70
+
71
+
72
+ def _type_simple_name(expr: Any) -> Optional[str]:
73
+ """Return the trailing simple name of an Identifier or FieldAccess."""
74
+ if isinstance(expr, Identifier):
75
+ return expr.simple_name
76
+ if isinstance(expr, FieldAccess):
77
+ return expr.name.simple_name
78
+ return None
79
+
80
+
81
+ def _is_pydantic_model(cls: ClassDeclaration) -> bool:
82
+ """True if the class is a Pydantic model.
83
+
84
+ First a structural check (direct base named ``BaseModel`` / ``RootModel`` /
85
+ ``BaseSettings``) that needs no type attribution. When type attribution is
86
+ available, an assignability check follows the resolved supertype chain, so a
87
+ subclass of a custom intermediate base (e.g. ``class User(MyBase)`` where
88
+ ``MyBase(BaseModel)``) is detected. Every Pydantic model ultimately derives
89
+ from ``pydantic.main.BaseModel`` (``RootModel`` and ``BaseSettings``
90
+ included), so a single assignability check suffices.
91
+ """
92
+ if any(_type_simple_name(base) in _PYDANTIC_BASES for base in (cls.implements or [])):
93
+ return True
94
+ # ClassDeclaration.type is a JavaType.FullyQualified, which the SDK does not
95
+ # model as a JavaType subtype; is_assignable_to accepts it at runtime, so
96
+ # cast to satisfy the type checker (drop once the SDK widens the parameter).
97
+ return is_assignable_to("pydantic.main.BaseModel", cast(Optional[JavaType], cls.type))
98
+
99
+
100
+ def _flag_configdict_option(
101
+ visitor: PythonVisitor, arg: NamedArgument, option_name: str, message: str
102
+ ) -> Any:
103
+ """Flag ``arg`` if it is the named option inside a ``ConfigDict(...)`` call.
104
+
105
+ Returns a marked copy when ``arg`` is the keyword argument ``option_name``
106
+ whose enclosing call is ``ConfigDict``; otherwise returns ``arg`` unchanged.
107
+ """
108
+ if arg.name.simple_name != option_name:
109
+ return arg
110
+ call = visitor.cursor.first_enclosing(MethodInvocation)
111
+ if call is None or call.name.simple_name != "ConfigDict":
112
+ return arg
113
+ return mark_deprecated(arg, message)
114
+
115
+
116
+ def _final_ref(type_hint: Optional[TypeHint]) -> Optional[Any]:
117
+ """Return the node referencing ``Final`` in the hint, or ``None``.
118
+
119
+ Unwraps ``Final[...]`` to its ``Final`` element and handles both the bare
120
+ name (``Final``, an ``Identifier``) and the qualified form (``typing.Final``,
121
+ a ``FieldAccess``).
122
+ """
123
+ if type_hint is None:
124
+ return None
125
+ tree = type_hint.type_tree
126
+ if isinstance(tree, ParameterizedType):
127
+ tree = tree.clazz
128
+ if isinstance(tree, Identifier) and tree.simple_name == "Final":
129
+ return tree
130
+ if isinstance(tree, FieldAccess) and tree.name.simple_name == "Final":
131
+ return tree
132
+ return None
133
+
134
+
135
+ def _rename_final_to_classvar(ref: Any) -> Any:
136
+ """Rename a ``Final`` reference node to ``ClassVar``."""
137
+ if isinstance(ref, Identifier):
138
+ return ref.replace(_simple_name="ClassVar")
139
+ # FieldAccess (e.g. ``typing.Final``): rename only the trailing name.
140
+ return ref.replace(_name=ref.name.replace(_simple_name="ClassVar"))
141
+
142
+
143
+ @categorize(_Pydantic)
144
+ class ReplaceFinalFieldWithClassVar(Recipe):
145
+ """
146
+ Replace ``Final`` model fields that have a default value with ``ClassVar``.
147
+
148
+ Before Pydantic 2.11, a field annotated as ``Final`` with a default value was
149
+ silently treated as a class variable. The typing specification says it should
150
+ be a real field, so 2.11 deprecates the pattern and recommends making the
151
+ intent explicit with ``ClassVar``. Rewriting ``Final[X] = default`` to
152
+ ``ClassVar[X] = default`` preserves the current (pre-2.11) class-variable
153
+ behavior and is what Pydantic recommends.
154
+
155
+ The ``Final`` import is replaced with ``ClassVar``; if ``Final`` is still used
156
+ elsewhere in the file it is kept.
157
+
158
+ Example:
159
+ Before:
160
+ from typing import Final
161
+ from pydantic import BaseModel
162
+
163
+ class User(BaseModel):
164
+ name: Final[str] = "default"
165
+
166
+ After:
167
+ from typing import ClassVar
168
+ from pydantic import BaseModel
169
+
170
+ class User(BaseModel):
171
+ name: ClassVar[str] = "default"
172
+ """
173
+
174
+ @property
175
+ def name(self) -> str:
176
+ return "org.openrewrite.python.migrate.pydantic.ReplaceFinalFieldWithClassVar"
177
+
178
+ @property
179
+ def display_name(self) -> str:
180
+ return "Replace `Final` model fields with a default with `ClassVar`"
181
+
182
+ @property
183
+ def description(self) -> str:
184
+ return (
185
+ "Pydantic 2.11 deprecates annotating a model field as `Final` with a "
186
+ "default value (such fields were treated as class variables). Replace "
187
+ "`Final[X] = default` with `ClassVar[X] = default`, which preserves the "
188
+ "existing class-variable behavior and is the replacement Pydantic "
189
+ "recommends."
190
+ )
191
+
192
+ @property
193
+ def tags(self) -> List[str]:
194
+ return ["python", "migration", "pydantic", "2.11"]
195
+
196
+ def editor(self) -> TreeVisitor[Any, ExecutionContext]:
197
+ class Visitor(PythonVisitor[ExecutionContext]):
198
+ def visit_assignment( # type: ignore[override]
199
+ self, assignment: Assignment, p: ExecutionContext
200
+ ) -> Optional[J]:
201
+ a = cast(Assignment, super().visit_assignment(assignment, p))
202
+
203
+ var = a.variable
204
+ if not isinstance(var, TypeHintedExpression):
205
+ return a
206
+ type_hint = var.type_hint
207
+ ref = _final_ref(type_hint)
208
+ if ref is None:
209
+ return a
210
+
211
+ cls = self.cursor.first_enclosing(ClassDeclaration)
212
+ if cls is None or not _is_pydantic_model(cls):
213
+ return a
214
+
215
+ # Rewrite the annotation: Final -> ClassVar (keeping any [X]).
216
+ tree = type_hint.type_tree
217
+ if isinstance(tree, ParameterizedType):
218
+ new_tree = tree.replace(_clazz=_rename_final_to_classvar(tree.clazz))
219
+ else:
220
+ new_tree = _rename_final_to_classvar(tree)
221
+ new_var = var.replace(_type_hint=type_hint.replace(_type_tree=new_tree))
222
+
223
+ # Only the bare-name form (`from typing import Final`) needs import
224
+ # bookkeeping; the qualified `typing.Final` form does not.
225
+ if isinstance(ref, Identifier):
226
+ maybe_add_import(
227
+ self,
228
+ AddImportOptions(
229
+ module="typing", name="ClassVar", only_if_referenced=False
230
+ ),
231
+ )
232
+ maybe_remove_import(
233
+ self,
234
+ RemoveImportOptions(
235
+ module="typing", name="Final", only_if_unused=True
236
+ ),
237
+ )
238
+
239
+ return a.replace(_variable=new_var)
240
+
241
+ return Visitor()
242
+
243
+
244
+ @categorize(_Pydantic)
245
+ class FindDeprecatedSchemaGenerator(Recipe):
246
+ """
247
+ Flag the ``schema_generator`` option in a ``ConfigDict(...)`` call.
248
+
249
+ Pydantic 2.10 deprecated the ``schema_generator`` config option (it was an
250
+ experimental API that is being reworked). There is no public replacement
251
+ yet, so this recipe flags its use for review rather than rewriting it.
252
+
253
+ Example:
254
+ Before:
255
+ from pydantic import BaseModel, ConfigDict
256
+
257
+ class User(BaseModel):
258
+ model_config = ConfigDict(schema_generator=MyGen)
259
+
260
+ After (option marked for review):
261
+ from pydantic import BaseModel, ConfigDict
262
+
263
+ class User(BaseModel):
264
+ model_config = ConfigDict(schema_generator=MyGen) # flagged
265
+ """
266
+
267
+ @property
268
+ def name(self) -> str:
269
+ return "org.openrewrite.python.migrate.pydantic.FindDeprecatedSchemaGenerator"
270
+
271
+ @property
272
+ def display_name(self) -> str:
273
+ return "Find deprecated `schema_generator` config option"
274
+
275
+ @property
276
+ def description(self) -> str:
277
+ return (
278
+ "Pydantic 2.10 deprecated the `schema_generator` option in "
279
+ "`ConfigDict`. It has no public replacement yet, so this recipe "
280
+ "flags its use for review."
281
+ )
282
+
283
+ @property
284
+ def tags(self) -> List[str]:
285
+ return ["python", "migration", "pydantic", "2.10"]
286
+
287
+ def editor(self) -> TreeVisitor[Any, ExecutionContext]:
288
+ class Visitor(PythonVisitor[ExecutionContext]):
289
+ def visit_named_argument( # type: ignore[override]
290
+ self, named_argument: NamedArgument, p: ExecutionContext
291
+ ) -> Optional[J]:
292
+ arg = cast(
293
+ NamedArgument, super().visit_named_argument(named_argument, p)
294
+ )
295
+ return _flag_configdict_option(
296
+ self, arg, "schema_generator", _SCHEMA_GEN_MSG
297
+ )
298
+
299
+ return Visitor()
300
+
301
+
302
+ @categorize(_Pydantic)
303
+ class FindDeprecatedJsonEncoders(Recipe):
304
+ """
305
+ Flag the ``json_encoders`` option in a ``ConfigDict(...)`` call.
306
+
307
+ Pydantic deprecated the ``json_encoders`` config option (it carries
308
+ performance and implementation overhead). The recommended replacements are
309
+ the ``@field_serializer`` / ``@model_serializer`` decorators or an
310
+ ``Annotated[..., PlainSerializer(...)]`` type, which are restructurings
311
+ rather than a mechanical swap, so this recipe flags its use for review.
312
+
313
+ Example:
314
+ Before:
315
+ from pydantic import BaseModel, ConfigDict
316
+
317
+ class User(BaseModel):
318
+ model_config = ConfigDict(json_encoders={int: str})
319
+
320
+ After (option marked for review):
321
+ from pydantic import BaseModel, ConfigDict
322
+
323
+ class User(BaseModel):
324
+ model_config = ConfigDict(json_encoders={int: str}) # flagged
325
+ """
326
+
327
+ @property
328
+ def name(self) -> str:
329
+ return "org.openrewrite.python.migrate.pydantic.FindDeprecatedJsonEncoders"
330
+
331
+ @property
332
+ def display_name(self) -> str:
333
+ return "Find deprecated `json_encoders` config option"
334
+
335
+ @property
336
+ def description(self) -> str:
337
+ return (
338
+ "Pydantic deprecated the `json_encoders` option in `ConfigDict`. Its "
339
+ "replacements (`@field_serializer` / `@model_serializer` or "
340
+ "`Annotated[..., PlainSerializer(...)]`) require restructuring, so "
341
+ "this recipe flags its use for review."
342
+ )
343
+
344
+ @property
345
+ def tags(self) -> List[str]:
346
+ return ["python", "migration", "pydantic"]
347
+
348
+ def editor(self) -> TreeVisitor[Any, ExecutionContext]:
349
+ class Visitor(PythonVisitor[ExecutionContext]):
350
+ def visit_named_argument( # type: ignore[override]
351
+ self, named_argument: NamedArgument, p: ExecutionContext
352
+ ) -> Optional[J]:
353
+ arg = cast(
354
+ NamedArgument, super().visit_named_argument(named_argument, p)
355
+ )
356
+ return _flag_configdict_option(
357
+ self, arg, "json_encoders", _JSON_ENCODERS_MSG
358
+ )
359
+
360
+ return Visitor()