openrewrite-migrate-python 0.14.0.dev20260901135710__tar.gz → 0.14.0.dev20260901153140__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 (145) hide show
  1. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/PKG-INFO +1 -1
  2. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/pyproject.toml +1 -1
  3. openrewrite_migrate_python-0.14.0.dev20260901153140/src/openrewrite_migrate_python/migrate/_typing_imports.py +164 -0
  4. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/typing_deprecations.py +55 -38
  5. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/typing_union_to_pipe.py +14 -5
  6. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python.egg-info/PKG-INFO +1 -1
  7. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_typing_deprecations.py +251 -0
  8. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_typing_union_to_pipe.py +26 -0
  9. openrewrite_migrate_python-0.14.0.dev20260901135710/src/openrewrite_migrate_python/migrate/_typing_imports.py +0 -48
  10. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/setup.cfg +0 -0
  11. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_code_quality_python/__init__.py +0 -0
  12. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_code_quality_python/codequality/__init__.py +0 -0
  13. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_code_quality_python/codequality/_semantically_equal.py +0 -0
  14. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_code_quality_python/codequality/all_branches_identical.py +0 -0
  15. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_code_quality_python/codequality/boolean_checks_not_inverted.py +0 -0
  16. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_code_quality_python/codequality/collapsible_if_statements.py +0 -0
  17. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_code_quality_python/codequality/find_suspicious_chained_comparison.py +0 -0
  18. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_code_quality_python/codequality/merge_identical_branches.py +0 -0
  19. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_code_quality_python/codequality/remove_duplicate_conditions.py +0 -0
  20. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_code_quality_python/codequality/remove_self_assignment.py +0 -0
  21. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_code_quality_python/codequality/remove_unconditional_value_overwrite.py +0 -0
  22. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_code_quality_python/codequality/simplify_boolean_literal.py +0 -0
  23. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_code_quality_python/codequality/simplify_redundant_logical_expression.py +0 -0
  24. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/__init__.py +0 -0
  25. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/__init__.py +0 -0
  26. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/_import_hygiene.py +0 -0
  27. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/_markers.py +0 -0
  28. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/aifc_migrations.py +0 -0
  29. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/array_deprecations.py +0 -0
  30. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/ast_deprecations.py +0 -0
  31. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/asyncio_coroutine_to_async.py +0 -0
  32. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/asyncio_deprecations.py +0 -0
  33. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/avro_migrations.py +0 -0
  34. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/calendar_deprecations.py +0 -0
  35. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/cgi_migrations.py +0 -0
  36. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/cgi_parse_deprecations.py +0 -0
  37. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/collections_abc_migrations.py +0 -0
  38. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/configparser_deprecations.py +0 -0
  39. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/datetime_utc.py +0 -0
  40. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/distutils_deprecations.py +0 -0
  41. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/distutils_migrations.py +0 -0
  42. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/functools_deprecations.py +0 -0
  43. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/future_imports.py +0 -0
  44. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/gettext_deprecations.py +0 -0
  45. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/html_parser_deprecations.py +0 -0
  46. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/imp_migrations.py +0 -0
  47. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/langchain_classic_imports.py +0 -0
  48. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/langchain_community_imports.py +0 -0
  49. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/langchain_provider_imports.py +0 -0
  50. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/locale_deprecations.py +0 -0
  51. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/locale_getdefaultlocale_deprecation.py +0 -0
  52. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/macpath_deprecations.py +0 -0
  53. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/mailcap_migrations.py +0 -0
  54. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/nntplib_migrations.py +0 -0
  55. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/os_deprecations.py +0 -0
  56. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/pathlib_deprecations.py +0 -0
  57. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/pep594_system_migrations.py +0 -0
  58. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/pipes_migrations.py +0 -0
  59. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/pkgutil_deprecations.py +0 -0
  60. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/platform_deprecations.py +0 -0
  61. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/pydantic_deprecations.py +0 -0
  62. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/pydantic_fields.py +0 -0
  63. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/pydantic_model_fields.py +0 -0
  64. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/re_deprecations.py +0 -0
  65. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/removed_modules_312.py +0 -0
  66. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/shutil_deprecations.py +0 -0
  67. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/socket_deprecations.py +0 -0
  68. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/ssl_deprecations.py +0 -0
  69. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/string_formatting.py +0 -0
  70. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/sys_deprecations.py +0 -0
  71. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/sys_last_deprecations.py +0 -0
  72. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/tarfile_deprecations.py +0 -0
  73. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/telnetlib_migrations.py +0 -0
  74. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/tempfile_deprecations.py +0 -0
  75. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/threading_deprecations.py +0 -0
  76. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/threading_is_alive_deprecation.py +0 -0
  77. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/typing_callable.py +0 -0
  78. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/unittest_deprecations.py +0 -0
  79. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain02.py +0 -0
  80. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain1.py +0 -0
  81. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/upgrade_to_pydantic.py +0 -0
  82. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/upgrade_to_python310.py +0 -0
  83. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/upgrade_to_python311.py +0 -0
  84. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/upgrade_to_python312.py +0 -0
  85. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/upgrade_to_python313.py +0 -0
  86. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/upgrade_to_python314.py +0 -0
  87. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/upgrade_to_python38.py +0 -0
  88. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/upgrade_to_python39.py +0 -0
  89. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/urllib_deprecations.py +0 -0
  90. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/uu_migrations.py +0 -0
  91. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/xdrlib_migrations.py +0 -0
  92. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python/migrate/xml_deprecations.py +0 -0
  93. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python.egg-info/SOURCES.txt +0 -0
  94. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python.egg-info/dependency_links.txt +0 -0
  95. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python.egg-info/entry_points.txt +0 -0
  96. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python.egg-info/requires.txt +0 -0
  97. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/src/openrewrite_migrate_python.egg-info/top_level.txt +0 -0
  98. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_aifc_migrations.py +0 -0
  99. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_array_deprecations.py +0 -0
  100. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_ast_deprecations.py +0 -0
  101. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_asyncio_coroutine_to_async.py +0 -0
  102. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_asyncio_deprecations.py +0 -0
  103. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_avro_migrations.py +0 -0
  104. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_calendar_deprecations.py +0 -0
  105. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_cgi_parse_deprecations.py +0 -0
  106. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_change_import_recipes.py +0 -0
  107. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_collections_abc_migrations.py +0 -0
  108. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_configparser_deprecations.py +0 -0
  109. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_datetime_utc.py +0 -0
  110. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_distutils_deprecations.py +0 -0
  111. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_future_imports.py +0 -0
  112. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_gettext_deprecations.py +0 -0
  113. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_html_parser_deprecations.py +0 -0
  114. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_imp_migrations.py +0 -0
  115. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_langchain_classic_imports.py +0 -0
  116. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_langchain_community_imports.py +0 -0
  117. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_langchain_provider_imports.py +0 -0
  118. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_locale_deprecations.py +0 -0
  119. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_locale_getdefaultlocale_deprecation.py +0 -0
  120. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_macpath_deprecations.py +0 -0
  121. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_os_deprecations.py +0 -0
  122. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_pep594_migrations.py +0 -0
  123. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_pep594_system_migrations.py +0 -0
  124. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_pkgutil_deprecations.py +0 -0
  125. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_platform_deprecations.py +0 -0
  126. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_pydantic_deprecations.py +0 -0
  127. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_pydantic_fields.py +0 -0
  128. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_pydantic_model_fields.py +0 -0
  129. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_re_deprecations.py +0 -0
  130. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_removed_modules_312.py +0 -0
  131. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_shutil_deprecations.py +0 -0
  132. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_ssl_deprecations.py +0 -0
  133. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_string_formatting.py +0 -0
  134. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_sys_deprecations.py +0 -0
  135. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_sys_last_deprecations.py +0 -0
  136. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_tarfile_deprecations.py +0 -0
  137. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_threading_deprecations.py +0 -0
  138. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_threading_extended.py +0 -0
  139. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_threading_is_alive_deprecation.py +0 -0
  140. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_typing_alias_moves.py +0 -0
  141. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_typing_callable.py +0 -0
  142. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_unittest_deprecations.py +0 -0
  143. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_upgrade_recipes.py +0 -0
  144. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/tests/test_upgrade_to_pydantic.py +0 -0
  145. {openrewrite_migrate_python-0.14.0.dev20260901135710 → openrewrite_migrate_python-0.14.0.dev20260901153140}/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.dev20260901135710
3
+ Version: 0.14.0.dev20260901153140
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
@@ -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.dev20260901135710" # Updated dynamically during release
8
+ version = "0.14.0.dev20260901153140" # 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"
@@ -0,0 +1,164 @@
1
+ """Internal helpers for locating references to the typing module in a file."""
2
+
3
+ from typing import Any, Dict, Iterator, List, Optional, Sequence, Set, Tuple, cast
4
+
5
+ from rewrite.java.support_types import JRightPadded, Space, Statement
6
+ from rewrite.java.tree import Block, If, Import, J
7
+ from rewrite.python.import_utils import get_alias_name, get_name_string, get_qualid_name
8
+ from rewrite.python.remove_import import RemoveImport, RemoveImportOptions, prefix_to_inherit
9
+ from rewrite.python.tree import CompilationUnit, MultiImport
10
+ from rewrite.python.visitor import PythonVisitor
11
+
12
+
13
+ def _module_scope_statements(statements: Sequence[Statement]) -> Iterator[Statement]:
14
+ """Every statement binding names in the module scope, `if` bodies included.
15
+
16
+ `if TYPE_CHECKING:` is where files that defer their annotations keep their
17
+ typing imports.
18
+ """
19
+ for stmt in statements:
20
+ yield stmt
21
+ body = _unconditional_body(stmt) if isinstance(stmt, If) else None
22
+ if body is not None:
23
+ yield from _module_scope_statements(body.statements)
24
+
25
+
26
+ def _unconditional_body(if_: If) -> Optional[Block]:
27
+ """The body of an `if` that only adds bindings to the enclosing scope.
28
+
29
+ None once there is an `else`: the branches are then alternative bindings of
30
+ the same name, and honouring one would rewrite the other's binding too.
31
+ """
32
+ then_part = if_.then_part
33
+ return then_part if if_.else_part is None and isinstance(then_part, Block) else None
34
+
35
+
36
+ def scan_typing_imports(cu: CompilationUnit) -> Tuple[Dict[str, str], Set[str]]:
37
+ """Map how this file can reference typing members.
38
+
39
+ Returns:
40
+ A tuple of:
41
+ - exposed name -> typing member, for ``from typing import member [as alias]``
42
+ - the set of names bound to the typing module itself, for
43
+ ``import typing [as alias]``
44
+ """
45
+ members: Dict[str, str] = {}
46
+ module_refs: Set[str] = set()
47
+ for stmt in _module_scope_statements(cu.statements):
48
+ if isinstance(stmt, MultiImport):
49
+ if stmt.from_ is not None:
50
+ if get_name_string(stmt.from_) == "typing":
51
+ for imp in stmt.names:
52
+ name = get_qualid_name(imp.qualid)
53
+ members.setdefault(get_alias_name(imp) or name, name)
54
+ else:
55
+ for imp in stmt.names:
56
+ if get_qualid_name(imp.qualid) == "typing":
57
+ module_refs.add(get_alias_name(imp) or "typing")
58
+ elif isinstance(stmt, Import):
59
+ if get_qualid_name(stmt.qualid) == "typing":
60
+ module_refs.add(get_alias_name(stmt) or "typing")
61
+ return members, module_refs
62
+
63
+
64
+ def maybe_remove_typing_import(visitor: PythonVisitor, name: Optional[str] = None) -> None:
65
+ """Schedule removal of a typing import once it is no longer used.
66
+
67
+ Mirrors the SDK's `maybe_remove_import`, registering the block-aware visitor.
68
+ """
69
+ options = RemoveImportOptions(module="typing", name=name)
70
+ if visitor._after_visit is None:
71
+ visitor._after_visit = []
72
+ for scheduled in visitor._after_visit:
73
+ # Only this visitor counts as already scheduled: a plain `RemoveImport`
74
+ # for the same name reaches the top level alone, and would leave the
75
+ # nested imports this exists to remove.
76
+ if (
77
+ type(scheduled) is _RemoveImportInBlocks
78
+ and scheduled.module == options.module
79
+ and scheduled.name == options.name
80
+ ):
81
+ return
82
+ visitor._after_visit.append(_RemoveImportInBlocks(options))
83
+
84
+
85
+ def maybe_remove_typing_module_import(visitor: PythonVisitor, module_refs: Set[str]) -> None:
86
+ """Schedule removal of `import typing` once it is no longer used.
87
+
88
+ RemoveImport's usage check counts `typing` identifiers, which an aliased
89
+ module import doesn't produce — removal is only scheduled when an
90
+ unaliased `import typing` exists.
91
+ """
92
+ if "typing" in module_refs:
93
+ maybe_remove_typing_import(visitor)
94
+
95
+
96
+ class _RemoveImportInBlocks(RemoveImport):
97
+ """`RemoveImport` extended to imports inside module-scope `if` bodies.
98
+
99
+ The base visitor prunes only the file's top-level statement list.
100
+ """
101
+
102
+ def visit_compilation_unit(self, cu: CompilationUnit, p: Any) -> J:
103
+ cu = cast(CompilationUnit, super().visit_compilation_unit(cu, p))
104
+ kept = self._prune(cu.padding.statements, prune_imports=False)
105
+ return cu if kept is None else cu.padding.replace(_statements=kept)
106
+
107
+ def _prune(
108
+ self, padded_statements: Sequence[JRightPadded], prune_imports: bool
109
+ ) -> Optional[List[JRightPadded]]:
110
+ """The statements with the import gone, or None when nothing changed."""
111
+ kept: List[JRightPadded] = []
112
+ inherited: Optional[Space] = None
113
+ changed = False
114
+ for index, padded in enumerate(padded_statements):
115
+ stmt = padded.element
116
+ if isinstance(stmt, If):
117
+ result: Optional[Statement] = self._prune_if(stmt)
118
+ elif prune_imports and isinstance(stmt, MultiImport):
119
+ result = self._process_multi_import(stmt)
120
+ elif prune_imports and isinstance(stmt, Import):
121
+ result = self._process_single_import(stmt)
122
+ else:
123
+ result = stmt
124
+
125
+ if result is None:
126
+ changed = True
127
+ if inherited is None:
128
+ inherited = prefix_to_inherit(stmt, index)
129
+ continue
130
+ if result is not stmt:
131
+ padded = JRightPadded(result, padded.after, padded.markers)
132
+ changed = True
133
+ if inherited is not None:
134
+ if not padded.element.prefix.comments:
135
+ padded = JRightPadded(
136
+ padded.element.replace(prefix=inherited), padded.after, padded.markers
137
+ )
138
+ elif inherited.comments:
139
+ # Neither comment can be given up, and one prefix holds both.
140
+ return None
141
+ inherited = None
142
+ kept.append(padded)
143
+ # A leftover prefix means nothing followed the removal to carry it, so a
144
+ # comment on the removed statement would be dropped with it.
145
+ if inherited is not None and inherited.comments:
146
+ return None
147
+ return kept if changed else None
148
+
149
+ def _prune_if(self, if_: If) -> Optional[If]:
150
+ """`if_` with the import gone from its body, or None once that empties it."""
151
+ then_part = _unconditional_body(if_)
152
+ if then_part is None:
153
+ return if_
154
+ kept = self._prune(then_part.padding.statements, prune_imports=True)
155
+ if kept is None:
156
+ return if_
157
+ if not kept:
158
+ return None
159
+ padded = if_.padding.then_part
160
+ return if_.padding.replace(
161
+ _then_part=JRightPadded(
162
+ then_part.padding.replace(_statements=kept), padded.after, padded.markers
163
+ )
164
+ )
@@ -20,18 +20,21 @@ handled with `ChangeImport` in `upgrade_to_python39`.
20
20
  See: https://peps.python.org/pep-0585/
21
21
  """
22
22
 
23
- import re
23
+ import ast
24
24
  from typing import Any, ClassVar, Optional, Set, cast
25
25
 
26
26
  from rewrite import ExecutionContext, Recipe, TreeVisitor
27
27
  from rewrite.category import CategoryDescriptor
28
28
  from rewrite.decorators import categorize
29
29
  from rewrite.marketplace import Python
30
- from rewrite.python.import_utils import get_alias_name, get_name_string, get_qualid_name
31
- from rewrite.python.remove_import import RemoveImportOptions, maybe_remove_import
30
+ from rewrite.python.markers import Quoted
32
31
  from rewrite.python.scope_utils import LocalBindings
33
32
 
34
- from ._typing_imports import maybe_remove_typing_module_import
33
+ from ._typing_imports import (
34
+ maybe_remove_typing_import,
35
+ maybe_remove_typing_module_import,
36
+ scan_typing_imports,
37
+ )
35
38
  from rewrite.python.tree import CompilationUnit, MultiImport, NamedArgument
36
39
  from rewrite.python.visitor import PythonVisitor
37
40
  from rewrite.java.tree import (
@@ -78,67 +81,78 @@ def _make_builtin_alias_editor(old_name: str, new_name: str) -> "TreeVisitor[Any
78
81
  self._bare_ref: Optional[str] = None
79
82
  # Names that refer to the typing module ('typing' or import aliases).
80
83
  self._module_refs: Set[str] = set()
81
- self._keep_from_import: bool = False
82
84
  self._rewrote_qualified: bool = False
83
85
  self._local_bindings = LocalBindings()
84
86
 
85
87
  def visit_compilation_unit(
86
88
  self, cu: CompilationUnit, p: ExecutionContext
87
89
  ) -> CompilationUnit:
88
- self._bare_ref = None
89
- self._module_refs = set()
90
90
  self._rewrote_qualified = False
91
91
  self._local_bindings = LocalBindings()
92
92
 
93
- for stmt in cu.statements:
94
- if isinstance(stmt, MultiImport):
95
- if stmt.from_ is not None:
96
- if get_name_string(stmt.from_) == "typing":
97
- for imp in stmt.names:
98
- if get_qualid_name(imp.qualid) == old_name and self._bare_ref is None:
99
- self._bare_ref = get_alias_name(imp) or old_name
100
- else:
101
- for imp in stmt.names:
102
- if get_qualid_name(imp.qualid) == "typing":
103
- self._module_refs.add(get_alias_name(imp) or "typing")
104
- elif isinstance(stmt, Import):
105
- if get_qualid_name(stmt.qualid) == "typing":
106
- self._module_refs.add(get_alias_name(stmt) or "typing")
93
+ members, self._module_refs = scan_typing_imports(cu)
94
+ self._bare_ref = next(
95
+ (exposed for exposed, name in members.items() if name == old_name), None
96
+ )
107
97
 
108
98
  if self._bare_ref is None and not self._module_refs:
109
99
  return cu
110
100
 
111
- # A reference inside a string annotation is a Literal, not an
112
- # Identifier, so it is neither renamed nor seen by RemoveImport's
113
- # usage check; keep the import so such references stay resolvable.
114
- self._keep_from_import = self._bare_ref is not None and self._used_in_string_literal(
115
- cu, self._bare_ref
116
- )
117
-
118
101
  result = cast(CompilationUnit, super().visit_compilation_unit(cu, p))
119
102
 
120
- if self._bare_ref is not None and not self._keep_from_import:
121
- maybe_remove_import(
122
- self, RemoveImportOptions(module="typing", name=old_name)
123
- )
103
+ # A string forward reference is neither renamed above nor seen by
104
+ # RemoveImport's usage check; scanning the rewritten tree finds the
105
+ # ones that outlived the rename.
106
+ if self._bare_ref is not None and not self._forward_reference_survives(
107
+ result, self._bare_ref
108
+ ):
109
+ maybe_remove_typing_import(self, old_name)
124
110
  if self._rewrote_qualified:
125
111
  maybe_remove_typing_module_import(self, self._module_refs)
126
112
 
127
113
  return result
128
114
 
129
115
  @staticmethod
130
- def _used_in_string_literal(cu: CompilationUnit, name: str) -> bool:
131
- pattern = re.compile(rf"\b{re.escape(name)}\b")
116
+ def _forward_reference_survives(cu: CompilationUnit, name: str) -> bool:
117
+ """True when a string forward reference still names ``name``.
118
+
119
+ The parser leaves such a reference as text -- a string `Literal`
120
+ annotation, or an `Identifier` marked `Quoted` -- so its contents are
121
+ parsed as an expression. Prose that merely spells the name out, as a
122
+ docstring does, is not an expression and so is not a reference.
123
+ """
132
124
  found = False
133
125
 
134
- class LiteralScan(PythonVisitor[Any]):
126
+ def names_target(text: str) -> bool:
127
+ if name not in text:
128
+ return False
129
+ try:
130
+ parsed = ast.parse(text.strip(), mode="eval")
131
+ except (SyntaxError, ValueError):
132
+ return False
133
+ return any(
134
+ isinstance(node, ast.Name) and node.id == name
135
+ for node in ast.walk(parsed)
136
+ )
137
+
138
+ class ForwardReferenceScan(PythonVisitor[Any]):
135
139
  def visit_literal(self, literal: Literal, p: Any) -> J:
136
140
  nonlocal found
137
- if not found and isinstance(literal.value, str) and pattern.search(literal.value):
141
+ if not found and isinstance(literal.value, str) and names_target(literal.value):
138
142
  found = True
139
143
  return literal
140
144
 
141
- LiteralScan().visit(cu, None)
145
+ def visit_identifier(self, ident: Identifier, p: Any) -> J:
146
+ nonlocal found
147
+ if (
148
+ not found
149
+ and ident.markers.find_first(Quoted) is not None
150
+ and names_target(ident.simple_name)
151
+ ):
152
+ found = True
153
+ return ident
154
+
155
+ ForwardReferenceScan().visit(cu, None)
142
156
  return found
143
157
 
144
158
  def visit_field_access(self, field_access: FieldAccess, p: ExecutionContext) -> J:
@@ -203,7 +217,10 @@ class _ReplaceTypingAliasRecipe(Recipe):
203
217
  def description(self) -> str:
204
218
  return (
205
219
  f"PEP 585 deprecated `typing.{self.old_name}` in Python 3.9. "
206
- f"Replace with the built-in `{self.new_name}` type for generic annotations."
220
+ f"Replace with the built-in `{self.new_name}` type for generic annotations. "
221
+ "Applies inside `if TYPE_CHECKING:` blocks as well; the built-in generic is "
222
+ "valid at runtime from Python 3.9 on, so no `from __future__ import "
223
+ "annotations` is needed."
207
224
  )
208
225
 
209
226
  def editor(self) -> TreeVisitor[Any, ExecutionContext]:
@@ -18,7 +18,6 @@ from rewrite.decorators import categorize
18
18
  from rewrite.marketplace import Python
19
19
  from rewrite.markers import Markers
20
20
  from rewrite.python.markers import Quoted
21
- from rewrite.python.remove_import import RemoveImportOptions, maybe_remove_import
22
21
  from rewrite.python.visitor import PythonVisitor
23
22
  from rewrite.java.support_types import JRightPadded, Space
24
23
  from rewrite.java.tree import (
@@ -33,7 +32,11 @@ from rewrite.java.tree import (
33
32
  from rewrite.python.tree import CollectionLiteral, CompilationUnit, UnionType
34
33
  from rewrite.utils import random_id
35
34
 
36
- from ._typing_imports import maybe_remove_typing_module_import, scan_typing_imports
35
+ from ._typing_imports import (
36
+ maybe_remove_typing_import,
37
+ maybe_remove_typing_module_import,
38
+ scan_typing_imports,
39
+ )
37
40
 
38
41
  # Define category path: Python > Migrate > Python 3.10
39
42
  _Python310 = [
@@ -89,7 +92,7 @@ class _PipeSyntaxVisitor(PythonVisitor[ExecutionContext]):
89
92
  if isinstance(clazz, FieldAccess):
90
93
  maybe_remove_typing_module_import(self, self._module_refs)
91
94
  else:
92
- maybe_remove_import(self, RemoveImportOptions(module="typing", name=member))
95
+ maybe_remove_typing_import(self, member)
93
96
 
94
97
 
95
98
  def _create_none_literal(prefix: Space) -> Literal:
@@ -171,7 +174,10 @@ class ReplaceTypingOptionalWithUnion(Recipe):
171
174
  def description(self) -> str:
172
175
  return (
173
176
  "PEP 604 introduced the `|` operator for union types in Python 3.10. "
174
- "Replace `Optional[X]` with the more concise `X | None` syntax."
177
+ "Replace `Optional[X]` with the more concise `X | None` syntax. "
178
+ "Applies inside `if TYPE_CHECKING:` blocks as well; `X | None` is valid at "
179
+ "runtime from Python 3.10 on, so no `from __future__ import annotations` "
180
+ "is needed."
175
181
  )
176
182
 
177
183
  @property
@@ -258,7 +264,10 @@ class ReplaceTypingUnionWithPipe(Recipe):
258
264
  def description(self) -> str:
259
265
  return (
260
266
  "PEP 604 introduced the `|` operator for union types in Python 3.10. "
261
- "Replace `Union[X, Y, ...]` with the more concise `X | Y | ...` syntax."
267
+ "Replace `Union[X, Y, ...]` with the more concise `X | Y | ...` syntax. "
268
+ "Applies inside `if TYPE_CHECKING:` blocks as well; `X | Y` is valid at "
269
+ "runtime from Python 3.10 on, so no `from __future__ import annotations` "
270
+ "is needed."
262
271
  )
263
272
 
264
273
  @property
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openrewrite-migrate-python
3
- Version: 0.14.0.dev20260901135710
3
+ Version: 0.14.0.dev20260901153140
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
@@ -430,6 +430,66 @@ class TestPep585CornerCases:
430
430
  )
431
431
  )
432
432
 
433
+ def test_removes_import_when_only_a_docstring_mentions_the_name(self):
434
+ """Prose that happens to spell the name is not a reference to it."""
435
+ spec = RecipeSpec(recipe=ReplaceTypingListWithList())
436
+ spec.rewrite_run(
437
+ python(
438
+ """
439
+ from typing import List
440
+
441
+
442
+ def f(items):
443
+ 'Return a List of things.'
444
+ return items
445
+
446
+
447
+ x: List[int] = []
448
+ """,
449
+ """
450
+ def f(items):
451
+ 'Return a List of things.'
452
+ return items
453
+
454
+
455
+ x: list[int] = []
456
+ """,
457
+ )
458
+ )
459
+
460
+ def test_keeps_import_used_by_quoted_annotation(self):
461
+ """A quoted annotation the rename leaves alone still needs the import."""
462
+ spec = RecipeSpec(recipe=ReplaceTypingListWithList())
463
+ spec.rewrite_run(
464
+ python(
465
+ """
466
+ from typing import List
467
+ x: List[int] = []
468
+ y: "List[int]" = []
469
+ """,
470
+ """
471
+ from typing import List
472
+ x: list[int] = []
473
+ y: "List[int]" = []
474
+ """,
475
+ )
476
+ )
477
+
478
+ def test_removes_import_when_the_quoted_annotation_is_renamed(self):
479
+ """The rename reaches inside a bare quoted annotation, freeing the import."""
480
+ spec = RecipeSpec(recipe=ReplaceTypingListWithList())
481
+ spec.rewrite_run(
482
+ python(
483
+ """
484
+ from typing import List
485
+ x: "List" = []
486
+ """,
487
+ """
488
+ x: "list" = []
489
+ """,
490
+ )
491
+ )
492
+
433
493
  def test_does_not_rename_keyword_argument(self):
434
494
  """A keyword argument named `List` binds a parameter, not the import."""
435
495
  spec = RecipeSpec(recipe=ReplaceTypingListWithList())
@@ -521,3 +581,194 @@ class TestReplaceTypingText:
521
581
  """
522
582
  )
523
583
  )
584
+
585
+
586
+ class TestTypeCheckingBlocks:
587
+ """Typing imports nested in an `if TYPE_CHECKING:` block."""
588
+
589
+ def test_rewrites_and_drops_emptied_block(self):
590
+ spec = RecipeSpec(recipe=ReplaceTypingListWithList())
591
+ spec.rewrite_run(
592
+ python(
593
+ """
594
+ from __future__ import annotations
595
+ from typing import TYPE_CHECKING
596
+
597
+ if TYPE_CHECKING:
598
+ from typing import List
599
+
600
+ def f(x: List[int]) -> None: ...
601
+ """,
602
+ """
603
+ from __future__ import annotations
604
+ from typing import TYPE_CHECKING
605
+
606
+ def f(x: list[int]) -> None: ...
607
+ """,
608
+ )
609
+ )
610
+
611
+ def test_keeps_block_holding_other_imports(self):
612
+ spec = RecipeSpec(recipe=ReplaceTypingListWithList())
613
+ spec.rewrite_run(
614
+ python(
615
+ """
616
+ from typing import TYPE_CHECKING
617
+
618
+ if TYPE_CHECKING:
619
+ from typing import List, Optional
620
+
621
+ def f(x: List[int], y: Optional[str]) -> None: ...
622
+ """,
623
+ """
624
+ from typing import TYPE_CHECKING
625
+
626
+ if TYPE_CHECKING:
627
+ from typing import Optional
628
+
629
+ def f(x: list[int], y: Optional[str]) -> None: ...
630
+ """,
631
+ )
632
+ )
633
+
634
+ def test_rewrites_qualified_reference(self):
635
+ spec = RecipeSpec(recipe=ReplaceTypingListWithList())
636
+ spec.rewrite_run(
637
+ python(
638
+ """
639
+ from typing import TYPE_CHECKING
640
+
641
+ if TYPE_CHECKING:
642
+ import typing
643
+
644
+ def f(x: typing.List[int]) -> None: ...
645
+ """,
646
+ """
647
+ from typing import TYPE_CHECKING
648
+
649
+ def f(x: list[int]) -> None: ...
650
+ """,
651
+ )
652
+ )
653
+
654
+ def test_ignores_a_block_whose_else_binds_the_name(self):
655
+ """Renaming the `else` binding too would rebind the builtin."""
656
+ spec = RecipeSpec(recipe=ReplaceTypingListWithList())
657
+ spec.rewrite_run(
658
+ python(
659
+ """
660
+ from typing import TYPE_CHECKING
661
+ import collections
662
+
663
+ if TYPE_CHECKING:
664
+ from typing import List
665
+ else:
666
+ List = collections.UserList
667
+
668
+ def f(x: List[int]) -> None: ...
669
+ """
670
+ )
671
+ )
672
+
673
+ def test_keeps_a_comment_on_the_statement_after_a_removed_import(self):
674
+ spec = RecipeSpec(recipe=ReplaceTypingListWithList())
675
+ spec.rewrite_run(
676
+ python(
677
+ """
678
+ from typing import TYPE_CHECKING
679
+
680
+ if TYPE_CHECKING:
681
+ from typing import List
682
+ # needed for annotations
683
+ from typing import Dict
684
+
685
+ def f(x: List[int], y: Dict[str, int]) -> None: ...
686
+ """,
687
+ """
688
+ from typing import TYPE_CHECKING
689
+
690
+ if TYPE_CHECKING:
691
+ # needed for annotations
692
+ from typing import Dict
693
+
694
+ def f(x: list[int], y: Dict[str, int]) -> None: ...
695
+ """,
696
+ )
697
+ )
698
+
699
+ def test_keeps_import_whose_comment_would_be_lost(self):
700
+ """No prefix can hold the comment once the import it leads is gone."""
701
+ RecipeSpec(recipe=ReplaceTypingListWithList()).rewrite_run(
702
+ python(
703
+ """
704
+ from typing import TYPE_CHECKING
705
+
706
+ if TYPE_CHECKING:
707
+ # only needed for annotations
708
+ from typing import List
709
+
710
+ def f(x: List[int]) -> None: ...
711
+ """,
712
+ """
713
+ from typing import TYPE_CHECKING
714
+
715
+ if TYPE_CHECKING:
716
+ # only needed for annotations
717
+ from typing import List
718
+
719
+ def f(x: list[int]) -> None: ...
720
+ """,
721
+ )
722
+ )
723
+
724
+ RecipeSpec(recipe=ReplaceTypingListWithList()).rewrite_run(
725
+ python(
726
+ """
727
+ from typing import TYPE_CHECKING
728
+
729
+ if TYPE_CHECKING:
730
+ from collections.abc import Sequence
731
+ # only needed for annotations
732
+ from typing import List
733
+
734
+ def f(x: List[int], y: Sequence[int]) -> None: ...
735
+ """,
736
+ """
737
+ from typing import TYPE_CHECKING
738
+
739
+ if TYPE_CHECKING:
740
+ from collections.abc import Sequence
741
+ # only needed for annotations
742
+ from typing import List
743
+
744
+ def f(x: list[int], y: Sequence[int]) -> None: ...
745
+ """,
746
+ )
747
+ )
748
+
749
+ RecipeSpec(recipe=ReplaceTypingListWithList()).rewrite_run(
750
+ python(
751
+ """
752
+ from typing import TYPE_CHECKING
753
+
754
+ if TYPE_CHECKING:
755
+ # comment A
756
+ from typing import List
757
+ # comment B
758
+ from typing import Dict
759
+
760
+ def f(x: List[int], y: Dict[str, int]) -> None: ...
761
+ """,
762
+ """
763
+ from typing import TYPE_CHECKING
764
+
765
+ if TYPE_CHECKING:
766
+ # comment A
767
+ from typing import List
768
+ # comment B
769
+ from typing import Dict
770
+
771
+ def f(x: list[int], y: Dict[str, int]) -> None: ...
772
+ """,
773
+ )
774
+ )
@@ -340,3 +340,29 @@ class TestReplaceTypingUnionWithPipe:
340
340
  """,
341
341
  )
342
342
  )
343
+
344
+
345
+ class TestTypeCheckingBlocks:
346
+ """Typing imports nested in an `if TYPE_CHECKING:` block."""
347
+
348
+ def test_rewrites_optional_and_drops_emptied_block(self):
349
+ spec = RecipeSpec(recipe=ReplaceTypingOptionalWithUnion())
350
+ spec.rewrite_run(
351
+ python(
352
+ """
353
+ from __future__ import annotations
354
+ from typing import TYPE_CHECKING
355
+
356
+ if TYPE_CHECKING:
357
+ from typing import Optional
358
+
359
+ def f(y: Optional[str]) -> None: ...
360
+ """,
361
+ """
362
+ from __future__ import annotations
363
+ from typing import TYPE_CHECKING
364
+
365
+ def f(y: str | None) -> None: ...
366
+ """,
367
+ )
368
+ )