openrewrite-migrate-python 0.14.0.dev20260903170551__tar.gz → 0.14.0.dev20260904122505__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 (150) hide show
  1. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/PKG-INFO +2 -2
  2. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/pyproject.toml +2 -6
  3. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_code_quality_python/codequality/_reindent.py +0 -16
  4. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_code_quality_python/codequality/all_branches_identical.py +2 -3
  5. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_code_quality_python/codequality/collapsible_if_statements.py +2 -4
  6. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/_from_imports.py +17 -6
  7. openrewrite_migrate_python-0.14.0.dev20260904122505/src/openrewrite_migrate_python/migrate/_slots.py +14 -0
  8. openrewrite_migrate_python-0.14.0.dev20260904122505/src/openrewrite_migrate_python/migrate/_typing_imports.py +71 -0
  9. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/asyncio_coroutine_to_async.py +37 -25
  10. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/gettext_deprecations.py +2 -2
  11. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/locale_deprecations.py +3 -1
  12. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/platform_deprecations.py +1 -1
  13. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/ssl_deprecations.py +1 -1
  14. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/string_formatting.py +4 -2
  15. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/typing_deprecations.py +1 -52
  16. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python.egg-info/PKG-INFO +2 -2
  17. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python.egg-info/SOURCES.txt +1 -0
  18. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python.egg-info/requires.txt +1 -1
  19. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_asyncio_coroutine_to_async.py +52 -0
  20. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_gettext_deprecations.py +16 -0
  21. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_locale_deprecations.py +44 -0
  22. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_string_formatting.py +20 -0
  23. openrewrite_migrate_python-0.14.0.dev20260903170551/src/openrewrite_migrate_python/migrate/_typing_imports.py +0 -164
  24. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/setup.cfg +0 -0
  25. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_code_quality_python/__init__.py +0 -0
  26. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_code_quality_python/codequality/__init__.py +0 -0
  27. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_code_quality_python/codequality/_semantically_equal.py +0 -0
  28. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_code_quality_python/codequality/_trivia.py +0 -0
  29. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_code_quality_python/codequality/boolean_checks_not_inverted.py +0 -0
  30. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_code_quality_python/codequality/find_suspicious_chained_comparison.py +0 -0
  31. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_code_quality_python/codequality/merge_identical_branches.py +0 -0
  32. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_code_quality_python/codequality/remove_duplicate_conditions.py +0 -0
  33. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_code_quality_python/codequality/remove_self_assignment.py +0 -0
  34. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_code_quality_python/codequality/remove_unconditional_value_overwrite.py +0 -0
  35. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_code_quality_python/codequality/simplify_boolean_literal.py +0 -0
  36. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_code_quality_python/codequality/simplify_redundant_logical_expression.py +0 -0
  37. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/__init__.py +0 -0
  38. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/__init__.py +0 -0
  39. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/_import_hygiene.py +0 -0
  40. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/_langchain_tables.py +0 -0
  41. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/_markers.py +0 -0
  42. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/aifc_migrations.py +0 -0
  43. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/array_deprecations.py +0 -0
  44. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/ast_deprecations.py +0 -0
  45. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/asyncio_deprecations.py +0 -0
  46. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/avro_migrations.py +0 -0
  47. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/calendar_deprecations.py +0 -0
  48. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/cgi_migrations.py +0 -0
  49. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/cgi_parse_deprecations.py +0 -0
  50. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/collections_abc_migrations.py +0 -0
  51. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/configparser_deprecations.py +0 -0
  52. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/datetime_utc.py +0 -0
  53. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/distutils_deprecations.py +0 -0
  54. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/distutils_migrations.py +0 -0
  55. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/functools_deprecations.py +0 -0
  56. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/future_imports.py +0 -0
  57. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/html_parser_deprecations.py +0 -0
  58. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/imp_migrations.py +0 -0
  59. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/langchain_classic_imports.py +0 -0
  60. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/langchain_community_imports.py +0 -0
  61. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/langchain_provider_imports.py +0 -0
  62. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/locale_getdefaultlocale_deprecation.py +0 -0
  63. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/macpath_deprecations.py +0 -0
  64. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/mailcap_migrations.py +0 -0
  65. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/nntplib_migrations.py +0 -0
  66. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/os_deprecations.py +0 -0
  67. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/pathlib_deprecations.py +0 -0
  68. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/pep594_system_migrations.py +0 -0
  69. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/pipes_migrations.py +0 -0
  70. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/pkgutil_deprecations.py +0 -0
  71. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/pydantic_deprecations.py +0 -0
  72. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/pydantic_fields.py +0 -0
  73. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/pydantic_model_fields.py +0 -0
  74. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/re_deprecations.py +0 -0
  75. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/removed_modules_312.py +0 -0
  76. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/shutil_deprecations.py +0 -0
  77. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/socket_deprecations.py +0 -0
  78. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/sys_deprecations.py +0 -0
  79. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/sys_last_deprecations.py +0 -0
  80. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/tarfile_deprecations.py +0 -0
  81. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/telnetlib_migrations.py +0 -0
  82. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/tempfile_deprecations.py +0 -0
  83. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/threading_deprecations.py +0 -0
  84. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/threading_is_alive_deprecation.py +0 -0
  85. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/typing_callable.py +0 -0
  86. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/typing_union_to_pipe.py +0 -0
  87. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/unittest_deprecations.py +0 -0
  88. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain02.py +0 -0
  89. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain1.py +0 -0
  90. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/upgrade_to_pydantic.py +0 -0
  91. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/upgrade_to_python310.py +0 -0
  92. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/upgrade_to_python311.py +0 -0
  93. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/upgrade_to_python312.py +0 -0
  94. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/upgrade_to_python313.py +0 -0
  95. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/upgrade_to_python314.py +0 -0
  96. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/upgrade_to_python38.py +0 -0
  97. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/upgrade_to_python39.py +0 -0
  98. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/urllib_deprecations.py +0 -0
  99. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/uu_migrations.py +0 -0
  100. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/xdrlib_migrations.py +0 -0
  101. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python/migrate/xml_deprecations.py +0 -0
  102. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python.egg-info/dependency_links.txt +0 -0
  103. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python.egg-info/entry_points.txt +0 -0
  104. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/src/openrewrite_migrate_python.egg-info/top_level.txt +0 -0
  105. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_aifc_migrations.py +0 -0
  106. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_array_deprecations.py +0 -0
  107. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_ast_deprecations.py +0 -0
  108. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_asyncio_deprecations.py +0 -0
  109. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_avro_migrations.py +0 -0
  110. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_calendar_deprecations.py +0 -0
  111. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_cgi_parse_deprecations.py +0 -0
  112. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_change_import_recipes.py +0 -0
  113. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_collections_abc_migrations.py +0 -0
  114. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_configparser_deprecations.py +0 -0
  115. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_datetime_utc.py +0 -0
  116. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_distutils_deprecations.py +0 -0
  117. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_future_imports.py +0 -0
  118. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_html_parser_deprecations.py +0 -0
  119. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_imp_migrations.py +0 -0
  120. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_langchain_classic_imports.py +0 -0
  121. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_langchain_community_imports.py +0 -0
  122. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_langchain_provider_imports.py +0 -0
  123. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_locale_getdefaultlocale_deprecation.py +0 -0
  124. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_macpath_deprecations.py +0 -0
  125. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_os_deprecations.py +0 -0
  126. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_pep594_migrations.py +0 -0
  127. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_pep594_system_migrations.py +0 -0
  128. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_pkgutil_deprecations.py +0 -0
  129. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_platform_deprecations.py +0 -0
  130. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_pydantic_deprecations.py +0 -0
  131. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_pydantic_fields.py +0 -0
  132. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_pydantic_model_fields.py +0 -0
  133. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_re_deprecations.py +0 -0
  134. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_removed_modules_312.py +0 -0
  135. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_shutil_deprecations.py +0 -0
  136. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_ssl_deprecations.py +0 -0
  137. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_sys_deprecations.py +0 -0
  138. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_sys_last_deprecations.py +0 -0
  139. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_tarfile_deprecations.py +0 -0
  140. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_threading_deprecations.py +0 -0
  141. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_threading_extended.py +0 -0
  142. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_threading_is_alive_deprecation.py +0 -0
  143. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_typing_alias_moves.py +0 -0
  144. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_typing_callable.py +0 -0
  145. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_typing_deprecations.py +0 -0
  146. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_typing_union_to_pipe.py +0 -0
  147. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_unittest_deprecations.py +0 -0
  148. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_upgrade_recipes.py +0 -0
  149. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/tests/test_upgrade_to_pydantic.py +0 -0
  150. {openrewrite_migrate_python-0.14.0.dev20260903170551 → openrewrite_migrate_python-0.14.0.dev20260904122505}/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.14.0.dev20260903170551
3
+ Version: 0.14.0.dev20260904122505
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,7 @@ 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.91.4; extra == "dev"
24
+ Requires-Dist: openrewrite>=8.91.5; extra == "dev"
25
25
  Requires-Dist: pydantic>=2.11; extra == "dev"
26
26
  Requires-Dist: pytest>=8.0.0; extra == "dev"
27
27
  Requires-Dist: pytest-cov>=4.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.14.0.dev20260903170551" # Updated dynamically during release
8
+ version = "0.14.0.dev20260904122505" # 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,11 +30,7 @@ dependencies = []
30
30
 
31
31
  [project.optional-dependencies]
32
32
  dev = [
33
- # openrewrite is host-provided at runtime, so this floor only pins what dev/CI test
34
- # against: the current release, so that recipes meet the LST a current CLI produces.
35
- # 8.91.4 is the first with `Py.Shebang` (openrewrite/rewrite#8729), which a shebanged
36
- # file puts at `statements[0]` ahead of its imports.
37
- "openrewrite>=8.91.4",
33
+ "openrewrite>=8.91.5",
38
34
  # Needed so type-attribution tests can resolve `pydantic` types.
39
35
  "pydantic>=2.11",
40
36
  "pytest>=8.0.0",
@@ -7,8 +7,6 @@ to a shallower nesting level means rewriting every line it spans.
7
7
  from typing import Optional, TypeVar
8
8
 
9
9
  from rewrite.java.support_types import Space
10
- from rewrite.java.tree import Statement
11
- from rewrite.python.tree import ExpressionStatement, StatementExpression
12
10
  from rewrite.python.visitor import PythonVisitor
13
11
 
14
12
  T = TypeVar("T")
@@ -27,20 +25,6 @@ def inherited_prefix(dropped: Space, body: Space) -> Space:
27
25
  )
28
26
 
29
27
 
30
- def with_prefix(stmt: Statement, prefix: Space) -> Statement:
31
- """Set the prefix of *stmt*, threading through the ``Py`` wrappers whose
32
- own prefix is a read-only view of the node they hold.
33
-
34
- Drop once openrewrite/rewrite#8742 is the version floor: it gives those
35
- wrappers a write path, so `stmt.replace(_prefix=...)` suffices alone.
36
- """
37
- if isinstance(stmt, ExpressionStatement):
38
- return stmt.replace(_expression=with_prefix(stmt.expression, prefix))
39
- if isinstance(stmt, StatementExpression):
40
- return stmt.replace(_statement=with_prefix(stmt.statement, prefix))
41
- return stmt.replace(_prefix=prefix)
42
-
43
-
44
28
  def line_indent(space: Space) -> Optional[str]:
45
29
  """Indentation of the line the token following *space* starts on, or
46
30
  ``None`` when that token shares a line with what precedes it.
@@ -25,7 +25,6 @@ from openrewrite_code_quality_python.codequality._reindent import (
25
25
  inherited_prefix,
26
26
  line_indent,
27
27
  reindent,
28
- with_prefix,
29
28
  )
30
29
  from openrewrite_code_quality_python.codequality._semantically_equal import SemanticallyEqual
31
30
 
@@ -193,8 +192,8 @@ def _replace_identical_ifs_padded(padded_stmts) -> Optional[list]:
193
192
  # separating it from the next one is recorded.
194
193
  result.append(
195
194
  first.replace(
196
- _element=with_prefix(
197
- first.element, inherited_prefix(s.prefix, first.element.prefix)
195
+ _element=first.element.replace(
196
+ _prefix=inherited_prefix(s.prefix, first.element.prefix)
198
197
  )
199
198
  )
200
199
  )
@@ -36,7 +36,6 @@ from openrewrite_code_quality_python.codequality._reindent import (
36
36
  inherited_prefix,
37
37
  line_indent,
38
38
  reindent,
39
- with_prefix,
40
39
  )
41
40
 
42
41
  _CodeQuality = [
@@ -198,9 +197,8 @@ class CollapsibleIfStatements(Recipe):
198
197
  inner_body = inner_body.replace(
199
198
  _statements=[
200
199
  first.replace(
201
- _element=with_prefix(
202
- first.element,
203
- inherited_prefix(inner.prefix, first.element.prefix),
200
+ _element=first.element.replace(
201
+ _prefix=inherited_prefix(inner.prefix, first.element.prefix)
204
202
  )
205
203
  ),
206
204
  *inner_body._statements[1:],
@@ -1,9 +1,10 @@
1
1
  """Resolving a receiver-less call to the module member it was imported from."""
2
2
 
3
- from typing import Dict, Optional
3
+ from typing import Dict, Optional, Set
4
4
 
5
5
  from rewrite import Cursor
6
6
  from rewrite.java.tree import FieldAccess, Identifier, J, MethodInvocation
7
+ from rewrite.python import scope_of
7
8
  from rewrite.python.import_utils import get_alias_name, get_name_string, get_qualid_name
8
9
  from rewrite.python.scope_utils import LocalBindings
9
10
  from rewrite.python.tree import CompilationUnit, MultiImport
@@ -19,18 +20,17 @@ def _is_relative(from_: J) -> bool:
19
20
  class FromImports:
20
21
  """The members of ``module`` a file binds with ``from <module> import <member>``.
21
22
 
22
- Under this form the call has no receiver, and for a symbol the typeshed in
23
- use omits, no resolved declaring type either. The releases that removed
24
- these APIs span many versions, so no declared version resolves them all —
25
- attribution quality does not make this fallback redundant.
23
+ A call spells the name its import bound, which an alias makes differ from the
24
+ member the type gates match on.
26
25
  """
27
26
 
28
27
  def __init__(self, module: str) -> None:
29
28
  self._module = module
30
29
  self._members: Dict[str, str] = {}
30
+ self._bound: Set[str] = set()
31
31
  self._locals = LocalBindings()
32
32
 
33
- def scan(self, cu: CompilationUnit) -> None:
33
+ def scan(self, cu: CompilationUnit, cursor: Cursor) -> None:
34
34
  """Record the file's bindings; call from ``visit_compilation_unit``.
35
35
 
36
36
  Top-level ``from`` statements only, since a guarded import's fallback may
@@ -48,6 +48,17 @@ class FromImports:
48
48
  for imp in stmt.names:
49
49
  name = get_qualid_name(imp.qualid)
50
50
  self._members.setdefault(get_alias_name(imp) or name, name)
51
+ # A member this module already binds is one `maybe_add_import` reconciles.
52
+ self._bound = scope_of(cursor).names() - set(self._members)
53
+
54
+ def binds(self, name: str) -> bool:
55
+ """Whether the module scope binds ``name`` outside this module's imports.
56
+
57
+ A replacement spelled unqualified resolves to the nearest module-scope
58
+ binding, so emitting one where something else holds the name redirects
59
+ the call rather than migrating it.
60
+ """
61
+ return name in self._bound
51
62
 
52
63
  def member_called(self, method: MethodInvocation, cursor: Cursor) -> Optional[str]:
53
64
  """The ``module`` member a receiver-less ``method`` calls, if it calls one."""
@@ -0,0 +1,14 @@
1
+ """Keeping a replacement node in the slot kind its original occupied."""
2
+
3
+ from rewrite import Cursor
4
+ from rewrite.java.tree import Block, Expression, J
5
+ from rewrite.python.tree import CompilationUnit, ExpressionStatement
6
+ from rewrite.utils import random_id
7
+
8
+
9
+ def as_slot_of(cursor: Cursor, expression: Expression) -> J:
10
+ """Wrap ``expression`` as an ``ExpressionStatement`` when the tree at
11
+ ``cursor`` is its own statement, so the replacement stays a Statement."""
12
+ if isinstance(cursor.parent_tree_cursor().value, (Block, CompilationUnit)):
13
+ return ExpressionStatement(random_id(), expression)
14
+ return expression
@@ -0,0 +1,71 @@
1
+ """Internal helpers for locating references to the typing module in a file."""
2
+
3
+ from typing import Dict, Iterator, Optional, Sequence, Set, Tuple
4
+
5
+ from rewrite.java.support_types import Statement
6
+ from rewrite.java.tree import If, Import
7
+ from rewrite.python.import_utils import get_alias_name, get_name_string, get_qualid_name
8
+ from rewrite.python.remove_import import (
9
+ RemoveImportOptions,
10
+ maybe_remove_import,
11
+ unconditional_body,
12
+ )
13
+ from rewrite.python.tree import CompilationUnit, MultiImport
14
+ from rewrite.python.visitor import PythonVisitor
15
+
16
+
17
+ def _module_scope_statements(statements: Sequence[Statement]) -> Iterator[Statement]:
18
+ """Every statement binding names in the module scope, `if` bodies included.
19
+
20
+ `if TYPE_CHECKING:` is where files that defer their annotations keep their
21
+ typing imports.
22
+ """
23
+ for stmt in statements:
24
+ yield stmt
25
+ body = unconditional_body(stmt) if isinstance(stmt, If) else None
26
+ if body is not None:
27
+ yield from _module_scope_statements(body.statements)
28
+
29
+
30
+ def scan_typing_imports(cu: CompilationUnit) -> Tuple[Dict[str, str], Set[str]]:
31
+ """Map how this file can reference typing members.
32
+
33
+ Returns:
34
+ A tuple of:
35
+ - exposed name -> typing member, for ``from typing import member [as alias]``
36
+ - the set of names bound to the typing module itself, for
37
+ ``import typing [as alias]``
38
+ """
39
+ members: Dict[str, str] = {}
40
+ module_refs: Set[str] = set()
41
+ for stmt in _module_scope_statements(cu.statements):
42
+ if isinstance(stmt, MultiImport):
43
+ if stmt.from_ is not None:
44
+ if get_name_string(stmt.from_) == "typing":
45
+ for imp in stmt.names:
46
+ name = get_qualid_name(imp.qualid)
47
+ members.setdefault(get_alias_name(imp) or name, name)
48
+ else:
49
+ for imp in stmt.names:
50
+ if get_qualid_name(imp.qualid) == "typing":
51
+ module_refs.add(get_alias_name(imp) or "typing")
52
+ elif isinstance(stmt, Import):
53
+ if get_qualid_name(stmt.qualid) == "typing":
54
+ module_refs.add(get_alias_name(stmt) or "typing")
55
+ return members, module_refs
56
+
57
+
58
+ def maybe_remove_typing_import(visitor: PythonVisitor, name: Optional[str] = None) -> None:
59
+ """Schedule removal of a typing import once it is no longer used."""
60
+ maybe_remove_import(visitor, RemoveImportOptions(module="typing", name=name))
61
+
62
+
63
+ def maybe_remove_typing_module_import(visitor: PythonVisitor, module_refs: Set[str]) -> None:
64
+ """Schedule removal of `import typing` once it is no longer used.
65
+
66
+ RemoveImport's usage check counts `typing` identifiers, which an aliased
67
+ module import doesn't produce — removal is only scheduled when an
68
+ unaliased `import typing` exists.
69
+ """
70
+ if "typing" in module_refs:
71
+ maybe_remove_typing_import(visitor)
@@ -27,11 +27,13 @@ from rewrite.marketplace import Python
27
27
  from rewrite.python.preconditions import uses_import
28
28
  from rewrite.markers import Markers
29
29
  from rewrite.python.visitor import PythonVisitor
30
- from rewrite.python.tree import Await, YieldFrom
30
+ from rewrite.python.tree import Await, StatementExpression, YieldFrom
31
31
  from rewrite.java.tree import (
32
32
  Annotation,
33
33
  FieldAccess,
34
34
  Identifier,
35
+ J,
36
+ Lambda,
35
37
  MethodDeclaration,
36
38
  Modifier,
37
39
  Space,
@@ -39,6 +41,8 @@ from rewrite.java.tree import (
39
41
  )
40
42
  from rewrite.utils import random_id
41
43
 
44
+ from ._slots import as_slot_of
45
+
42
46
  # Define category path: Python > Migrate > Python 3.11
43
47
  _Python311 = [
44
48
  *Python,
@@ -127,33 +131,41 @@ def _add_async_modifier(method: MethodDeclaration, has_remaining_decorators: boo
127
131
 
128
132
 
129
133
  class _YieldFromToAwaitVisitor(PythonVisitor[ExecutionContext]):
130
- """Visitor that transforms yield from expressions to await expressions.
131
-
132
- In Python's AST, 'yield from expr' is represented as:
133
- J.Yield(value=Py.YieldFrom(expression=expr))
134
+ """Replaces ``yield from expr`` with ``await expr``.
134
135
 
135
- We need to replace the entire Yield with just Await(expression).
136
+ The parser represents ``yield from expr`` as
137
+ ``StatementExpression(Yield(YieldFrom(expr)))`` whether it is used as an
138
+ expression or as its own statement, so the wrapper is the node to replace.
136
139
  """
137
140
 
138
- def visit_yield(
139
- self, yield_stmt: Yield, p: ExecutionContext
140
- ) -> Optional[Any]:
141
- yield_stmt = super().visit_yield(yield_stmt, p)
142
-
143
- # Check if this is a 'yield from' (Yield containing YieldFrom)
144
- if isinstance(yield_stmt.value, YieldFrom):
145
- yield_from = yield_stmt.value
146
- # Transform yield from expr -> await expr
147
- # The Await takes the Yield's prefix (space before 'yield')
148
- return Await(
149
- _id=random_id(),
150
- _prefix=yield_stmt.prefix,
151
- _markers=yield_stmt.markers,
152
- _expression=yield_from.expression,
153
- _type=yield_from.type,
154
- )
155
-
156
- return yield_stmt
141
+ def visit_statement_expression(
142
+ self, stmt_expr: StatementExpression, p: ExecutionContext
143
+ ) -> Optional[J]:
144
+ stmt_expr = super().visit_statement_expression(stmt_expr, p)
145
+ if not isinstance(stmt_expr, StatementExpression):
146
+ return stmt_expr
147
+
148
+ yield_stmt = stmt_expr.statement
149
+ if not isinstance(yield_stmt, Yield) or not isinstance(yield_stmt.value, YieldFrom):
150
+ return stmt_expr
151
+
152
+ yield_from = yield_stmt.value
153
+ await_ = Await(
154
+ _id=random_id(),
155
+ _prefix=yield_stmt.prefix,
156
+ _markers=yield_stmt.markers,
157
+ _expression=yield_from.expression,
158
+ _type=yield_from.type,
159
+ )
160
+ return as_slot_of(self.cursor, await_)
161
+
162
+ # Started on the migrated function's body block, so any def or lambda met
163
+ # here is a nested function whose ``yield from`` must stay.
164
+ def visit_method_declaration(self, method: MethodDeclaration, p: ExecutionContext) -> J:
165
+ return method
166
+
167
+ def visit_lambda(self, lambda_: Lambda, p: ExecutionContext) -> J:
168
+ return lambda_
157
169
 
158
170
 
159
171
  @categorize(_Python311)
@@ -115,7 +115,7 @@ class ReplaceGettextDeprecations(Recipe):
115
115
  self._imports = FromImports("gettext")
116
116
 
117
117
  def visit_compilation_unit(self, cu: CompilationUnit, p: ExecutionContext) -> J:
118
- self._imports.scan(cu)
118
+ self._imports.scan(cu, self.cursor)
119
119
  return super().visit_compilation_unit(cu, p)
120
120
 
121
121
  def visit_method_invocation(
@@ -139,7 +139,7 @@ class ReplaceGettextDeprecations(Recipe):
139
139
  # the import moves to the replacement along with the call.
140
140
  member = self._imports.member_called(method, self.cursor)
141
141
  new_name = GETTEXT_DEPRECATIONS.get(member) if member else None
142
- if new_name is None:
142
+ if new_name is None or self._imports.binds(new_name):
143
143
  return method
144
144
  maybe_add_import(self, AddImportOptions(module="gettext", name=new_name))
145
145
  maybe_remove_import(self, RemoveImportOptions(module="gettext", name=member))
@@ -153,7 +153,7 @@ class ReplaceLocaleResetlocale(Recipe):
153
153
  self._imports = FromImports("locale")
154
154
 
155
155
  def visit_compilation_unit(self, cu: CompilationUnit, p: ExecutionContext) -> J:
156
- self._imports.scan(cu)
156
+ self._imports.scan(cu, self.cursor)
157
157
  return super().visit_compilation_unit(cu, p)
158
158
 
159
159
  def visit_method_invocation(
@@ -175,6 +175,8 @@ class ReplaceLocaleResetlocale(Recipe):
175
175
  if self._imports.member_called(method, self.cursor) == "resetlocale":
176
176
  # `setlocale` and `LC_ALL` are spelled unqualified, so each
177
177
  # needs an import of its own.
178
+ if any(self._imports.binds(n) for n in ("setlocale", "LC_ALL")):
179
+ return method
178
180
  for name in ("setlocale", "LC_ALL"):
179
181
  maybe_add_import(self, AddImportOptions(module="locale", name=name))
180
182
  maybe_remove_import(
@@ -93,7 +93,7 @@ class ReplacePlatformPopen(Recipe):
93
93
  self._imports = FromImports("platform")
94
94
 
95
95
  def visit_compilation_unit(self, cu: CompilationUnit, p: ExecutionContext) -> J:
96
- self._imports.scan(cu)
96
+ self._imports.scan(cu, self.cursor)
97
97
  return super().visit_compilation_unit(cu, p)
98
98
 
99
99
  def visit_method_invocation(
@@ -92,7 +92,7 @@ class FindSslMatchHostname(Recipe):
92
92
  self._imports = FromImports("ssl")
93
93
 
94
94
  def visit_compilation_unit(self, cu: CompilationUnit, p: ExecutionContext) -> J:
95
- self._imports.scan(cu)
95
+ self._imports.scan(cu, self.cursor)
96
96
  return super().visit_compilation_unit(cu, p)
97
97
 
98
98
  def visit_method_invocation(
@@ -31,6 +31,8 @@ from rewrite.python.tree import (
31
31
  )
32
32
  from rewrite.utils import random_id
33
33
 
34
+ from ._slots import as_slot_of
35
+
34
36
  # Define category path: Python > Migrate > Python 3.6
35
37
  _Python36 = [
36
38
  *Python,
@@ -354,7 +356,7 @@ class ReplaceStrFormatWithFString(Recipe):
354
356
  class Visitor(PythonVisitor[ExecutionContext]):
355
357
  def visit_method_invocation(
356
358
  self, method: MethodInvocation, p: ExecutionContext
357
- ) -> Optional[Expression]:
359
+ ) -> Optional[J]:
358
360
  method = super().visit_method_invocation(method, p)
359
361
 
360
362
  if not isinstance(method, MethodInvocation):
@@ -373,7 +375,7 @@ class ReplaceStrFormatWithFString(Recipe):
373
375
 
374
376
  result = _convert_format_to_fstring(method)
375
377
  if result is not None:
376
- return result
378
+ return as_slot_of(self.cursor, result)
377
379
 
378
380
  return method
379
381
 
@@ -20,14 +20,12 @@ handled with `ChangeImport` in `upgrade_to_python39`.
20
20
  See: https://peps.python.org/pep-0585/
21
21
  """
22
22
 
23
- import ast
24
23
  from typing import Any, ClassVar, Optional, Set, cast
25
24
 
26
25
  from rewrite import ExecutionContext, Recipe, Tree, TreeVisitor
27
26
  from rewrite.category import CategoryDescriptor
28
27
  from rewrite.decorators import categorize
29
28
  from rewrite.marketplace import Python
30
- from rewrite.python.markers import Quoted
31
29
  from rewrite.python.scope_utils import LocalBindings
32
30
  from rewrite.visitor import Cursor
33
31
 
@@ -53,7 +51,6 @@ from rewrite.java.tree import (
53
51
  Identifier,
54
52
  Import,
55
53
  J,
56
- Literal,
57
54
  ParameterizedType,
58
55
  VariableDeclarations,
59
56
  )
@@ -181,61 +178,13 @@ def _make_builtin_alias_editor(
181
178
 
182
179
  result = cast(CompilationUnit, super().visit_compilation_unit(cu, p))
183
180
 
184
- # A string forward reference is neither renamed above nor seen by
185
- # RemoveImport's usage check; scanning the rewritten tree finds the
186
- # ones that outlived the rename.
187
- if self._bare_ref is not None and not self._forward_reference_survives(
188
- result, self._bare_ref
189
- ):
181
+ if self._bare_ref is not None:
190
182
  maybe_remove_typing_import(self, old_name)
191
183
  if self._rewrote_qualified:
192
184
  maybe_remove_typing_module_import(self, self._module_refs)
193
185
 
194
186
  return result
195
187
 
196
- @staticmethod
197
- def _forward_reference_survives(cu: CompilationUnit, name: str) -> bool:
198
- """True when a string forward reference still names ``name``.
199
-
200
- The parser leaves such a reference as text -- a string `Literal`
201
- annotation, or an `Identifier` marked `Quoted` -- so its contents are
202
- parsed as an expression. Prose that merely spells the name out, as a
203
- docstring does, is not an expression and so is not a reference.
204
- """
205
- found = False
206
-
207
- def names_target(text: str) -> bool:
208
- if name not in text:
209
- return False
210
- try:
211
- parsed = ast.parse(text.strip(), mode="eval")
212
- except (SyntaxError, ValueError):
213
- return False
214
- return any(
215
- isinstance(node, ast.Name) and node.id == name
216
- for node in ast.walk(parsed)
217
- )
218
-
219
- class ForwardReferenceScan(PythonVisitor[Any]):
220
- def visit_literal(self, literal: Literal, p: Any) -> J:
221
- nonlocal found
222
- if not found and isinstance(literal.value, str) and names_target(literal.value):
223
- found = True
224
- return literal
225
-
226
- def visit_identifier(self, ident: Identifier, p: Any) -> J:
227
- nonlocal found
228
- if (
229
- not found
230
- and ident.markers.find_first(Quoted) is not None
231
- and names_target(ident.simple_name)
232
- ):
233
- found = True
234
- return ident
235
-
236
- ForwardReferenceScan().visit(cu, None)
237
- return found
238
-
239
188
  def visit_field_access(self, field_access: FieldAccess, p: ExecutionContext) -> J:
240
189
  if (
241
190
  isinstance(field_access.target, Identifier)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openrewrite-migrate-python
3
- Version: 0.14.0.dev20260903170551
3
+ Version: 0.14.0.dev20260904122505
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,7 @@ 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.91.4; extra == "dev"
24
+ Requires-Dist: openrewrite>=8.91.5; extra == "dev"
25
25
  Requires-Dist: pydantic>=2.11; extra == "dev"
26
26
  Requires-Dist: pytest>=8.0.0; extra == "dev"
27
27
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
@@ -26,6 +26,7 @@ src/openrewrite_migrate_python/migrate/_from_imports.py
26
26
  src/openrewrite_migrate_python/migrate/_import_hygiene.py
27
27
  src/openrewrite_migrate_python/migrate/_langchain_tables.py
28
28
  src/openrewrite_migrate_python/migrate/_markers.py
29
+ src/openrewrite_migrate_python/migrate/_slots.py
29
30
  src/openrewrite_migrate_python/migrate/_typing_imports.py
30
31
  src/openrewrite_migrate_python/migrate/aifc_migrations.py
31
32
  src/openrewrite_migrate_python/migrate/array_deprecations.py
@@ -1,6 +1,6 @@
1
1
 
2
2
  [dev]
3
- openrewrite>=8.91.4
3
+ openrewrite>=8.91.5
4
4
  pydantic>=2.11
5
5
  pytest>=8.0.0
6
6
  pytest-cov>=4.0.0
@@ -4,10 +4,35 @@ from rewrite.test import RecipeSpec, python
4
4
  from openrewrite_migrate_python.migrate.asyncio_coroutine_to_async import (
5
5
  MigrateAsyncioCoroutine,
6
6
  )
7
+ from tests.tree_checks import assert_statement_slots_hold_statements
7
8
 
8
9
  class TestMigrateAsyncioCoroutine:
9
10
  """Tests for the MigrateAsyncioCoroutine recipe."""
10
11
 
12
+ def test_await_lands_in_the_slot_its_yield_from_occupied(self):
13
+ """A statement-level `yield from` becomes an `await` statement; an expression-level one a bare `await`."""
14
+ spec = RecipeSpec(recipe=MigrateAsyncioCoroutine())
15
+ spec.rewrite_run(
16
+ python(
17
+ """
18
+ import asyncio
19
+
20
+ @asyncio.coroutine
21
+ def run():
22
+ yield from step()
23
+ result = yield from step()
24
+ """,
25
+ """
26
+ import asyncio
27
+
28
+ async def run():
29
+ await step()
30
+ result = await step()
31
+ """,
32
+ after_recipe=assert_statement_slots_hold_statements,
33
+ )
34
+ )
35
+
11
36
  def test_migrates_simple_coroutine(self):
12
37
  """Test migration of a simple @asyncio.coroutine function."""
13
38
  spec = RecipeSpec(recipe=MigrateAsyncioCoroutine())
@@ -130,3 +155,30 @@ class TestMigrateAsyncioCoroutine:
130
155
  """,
131
156
  )
132
157
  )
158
+
159
+ def test_leaves_yield_from_in_nested_def_alone(self):
160
+ """A nested def or lambda keeps its yield from; await is a SyntaxError there."""
161
+ spec = RecipeSpec(recipe=MigrateAsyncioCoroutine())
162
+ spec.rewrite_run(
163
+ python(
164
+ """
165
+ import asyncio
166
+
167
+ @asyncio.coroutine
168
+ def outer():
169
+ def gen():
170
+ yield from range(3)
171
+ g = lambda: (yield from range(3))
172
+ yield from step()
173
+ """,
174
+ """
175
+ import asyncio
176
+
177
+ async def outer():
178
+ def gen():
179
+ yield from range(3)
180
+ g = lambda: (yield from range(3))
181
+ await step()
182
+ """,
183
+ )
184
+ )
@@ -100,3 +100,19 @@ class TestReplaceGettextDeprecations:
100
100
  """
101
101
  )
102
102
  )
103
+
104
+ def test_no_change_when_the_replacement_name_is_already_bound(self):
105
+ """The added import would be shadowed by the file's own `gettext`."""
106
+ spec = RecipeSpec(recipe=ReplaceGettextDeprecations())
107
+ spec.rewrite_run(
108
+ python(
109
+ """
110
+ from gettext import lgettext
111
+
112
+ def gettext(m):
113
+ return m
114
+
115
+ msg = lgettext("Hello")
116
+ """
117
+ )
118
+ )