openrewrite-migrate-python 0.15.0.dev20260922170519__tar.gz → 0.15.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/PKG-INFO +2 -2
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/pyproject.toml +2 -2
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/__init__.py +2 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/_reindent.py +20 -3
- openrewrite_migrate_python-0.15.2/src/openrewrite_code_quality_python/codequality/_semantically_equal.py +38 -0
- openrewrite_migrate_python-0.15.2/src/openrewrite_code_quality_python/codequality/_side_effects.py +36 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/_trivia.py +6 -3
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/all_branches_identical.py +34 -14
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/boolean_checks_not_inverted.py +76 -11
- openrewrite_migrate_python-0.15.2/src/openrewrite_code_quality_python/codequality/code_quality.py +73 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/collapsible_if_statements.py +67 -46
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/find_suspicious_chained_comparison.py +14 -21
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/merge_identical_branches.py +21 -43
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/remove_duplicate_conditions.py +13 -7
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/remove_self_assignment.py +8 -4
- openrewrite_migrate_python-0.15.2/src/openrewrite_code_quality_python/codequality/remove_unconditional_value_overwrite.py +334 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/simplify_boolean_literal.py +28 -34
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/simplify_redundant_logical_expression.py +8 -7
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/__init__.py +8 -0
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/_decorators.py +24 -0
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/_method_renames.py +37 -0
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/_module_imports.py +50 -0
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/aifc_migrations.py +271 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/array_deprecations.py +9 -50
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/ast_deprecations.py +13 -110
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/asyncio_coroutine_to_async.py +56 -54
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/asyncio_deprecations.py +26 -25
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/backoff_to_tenacity.py +707 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/calendar_deprecations.py +8 -38
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/cgi_migrations.py +115 -0
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/cgi_parse_deprecations.py +109 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/collections_abc_migrations.py +6 -11
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/configparser_deprecations.py +115 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/datetime_utc.py +23 -41
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/distutils_deprecations.py +9 -28
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/distutils_migrations.py +7 -3
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/functools_deprecations.py +17 -23
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/future_imports.py +111 -0
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/gettext_deprecations.py +80 -0
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/html_parser_deprecations.py +89 -0
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/imp_migrations.py +76 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/langchain_classic_imports.py +29 -48
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/langchain_community_imports.py +9 -4
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/langchain_provider_imports.py +10 -107
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/locale_deprecations.py +85 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/locale_getdefaultlocale_deprecation.py +8 -7
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/macpath_deprecations.py +66 -0
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/mailcap_migrations.py +70 -0
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/nntplib_migrations.py +68 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/os_deprecations.py +29 -48
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/pathlib_deprecations.py +11 -14
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/pep594_system_migrations.py +230 -0
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/pipes_migrations.py +71 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/pkgutil_deprecations.py +30 -40
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/platform_deprecations.py +91 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/pydantic_deprecations.py +85 -85
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/pydantic_fields.py +9 -21
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/pydantic_model_fields.py +5 -5
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/re_deprecations.py +57 -54
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/removed_modules_312.py +81 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/shutil_deprecations.py +8 -4
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/socket_deprecations.py +8 -4
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/ssl_deprecations.py +13 -42
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/string_formatting.py +70 -50
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/sys_deprecations.py +17 -27
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/sys_last_deprecations.py +41 -21
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/tarfile_deprecations.py +64 -0
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/telnetlib_migrations.py +74 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/tempfile_deprecations.py +12 -20
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/threading_deprecations.py +67 -172
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/threading_is_alive_deprecation.py +6 -28
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/typing_deprecations.py +25 -45
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/typing_union_to_pipe.py +52 -41
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/unittest_deprecations.py +6 -45
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_python39.py +0 -2
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/urllib_deprecations.py +22 -51
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/uu_migrations.py +70 -0
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/xdrlib_migrations.py +70 -0
- openrewrite_migrate_python-0.15.2/src/openrewrite_migrate_python/migrate/xml_deprecations.py +131 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python.egg-info/PKG-INFO +2 -2
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python.egg-info/SOURCES.txt +7 -3
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python.egg-info/requires.txt +1 -1
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_ast_deprecations.py +15 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_asyncio_coroutine_to_async.py +60 -2
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_asyncio_deprecations.py +37 -0
- openrewrite_migrate_python-0.15.2/tests/test_backoff_to_tenacity.py +828 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_calendar_deprecations.py +15 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_cgi_parse_deprecations.py +17 -2
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_configparser_deprecations.py +25 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_datetime_utc.py +31 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_distutils_deprecations.py +15 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_functools_deprecations.py +34 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_gettext_deprecations.py +9 -2
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_html_parser_deprecations.py +56 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_langchain_classic_imports.py +11 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_langchain_provider_imports.py +19 -4
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_locale_deprecations.py +21 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_os_deprecations.py +32 -2
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_pathlib_deprecations.py +21 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_pep594_migrations.py +12 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_pkgutil_deprecations.py +44 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_platform_deprecations.py +47 -2
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_pydantic_deprecations.py +146 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_re_deprecations.py +57 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_removed_modules_312.py +13 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_ssl_deprecations.py +22 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_string_formatting.py +149 -15
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_sys_deprecations.py +9 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_sys_last_deprecations.py +44 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_tarfile_deprecations.py +29 -2
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_tempfile_deprecations.py +21 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_threading_deprecations.py +27 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_typing_alias_moves.py +0 -23
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_typing_deprecations.py +32 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_typing_union_to_pipe.py +68 -1
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_unittest_deprecations.py +35 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_urllib_deprecations.py +20 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_xml_deprecations.py +19 -0
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_code_quality_python/codequality/_semantically_equal.py +0 -32
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_code_quality_python/codequality/remove_unconditional_value_overwrite.py +0 -258
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/_from_imports.py +0 -68
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/_import_hygiene.py +0 -55
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/_typing_imports.py +0 -71
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/aifc_migrations.py +0 -508
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/cgi_migrations.py +0 -204
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/cgi_parse_deprecations.py +0 -205
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/configparser_deprecations.py +0 -254
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/future_imports.py +0 -155
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/gettext_deprecations.py +0 -148
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/html_parser_deprecations.py +0 -106
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/imp_migrations.py +0 -125
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/locale_deprecations.py +0 -190
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/macpath_deprecations.py +0 -116
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/mailcap_migrations.py +0 -120
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/nntplib_migrations.py +0 -118
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/pep594_system_migrations.py +0 -420
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/pipes_migrations.py +0 -121
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/platform_deprecations.py +0 -173
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/removed_modules_312.py +0 -145
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/tarfile_deprecations.py +0 -113
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/telnetlib_migrations.py +0 -123
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/uu_migrations.py +0 -120
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/xdrlib_migrations.py +0 -120
- openrewrite_migrate_python-0.15.0.dev20260922170519/src/openrewrite_migrate_python/migrate/xml_deprecations.py +0 -188
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/setup.cfg +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/__init__.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/__init__.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/_langchain_tables.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/_markers.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/_slots.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/avro_migrations.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/typing_callable.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain02.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain1.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_pydantic.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_python310.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_python311.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_python312.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_python313.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_python314.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_python38.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python.egg-info/dependency_links.txt +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python.egg-info/entry_points.txt +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python.egg-info/top_level.txt +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_aifc_migrations.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_array_deprecations.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_avro_migrations.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_change_import_recipes.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_collections_abc_migrations.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_distutils_migrations.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_future_imports.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_imp_migrations.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_langchain_community_imports.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_locale_getdefaultlocale_deprecation.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_macpath_deprecations.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_mailcap_migrations.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_markers.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_pep594_system_migrations.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_pydantic_fields.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_pydantic_model_fields.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_shutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_socket_deprecations.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_threading_extended.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_threading_is_alive_deprecation.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_typing_callable.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_upgrade_recipes.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_upgrade_to_pydantic.py +0 -0
- {openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/tests/test_xdrlib_migrations.py +0 -0
{openrewrite_migrate_python-0.15.0.dev20260922170519 → openrewrite_migrate_python-0.15.2}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openrewrite-migrate-python
|
|
3
|
-
Version: 0.15.
|
|
3
|
+
Version: 0.15.2
|
|
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
|
|
@@ -19,7 +19,7 @@ Classifier: Typing :: Typed
|
|
|
19
19
|
Requires-Python: >=3.12
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
Provides-Extra: dev
|
|
22
|
-
Requires-Dist: openrewrite>=8.92.
|
|
22
|
+
Requires-Dist: openrewrite>=8.92.18; extra == "dev"
|
|
23
23
|
Requires-Dist: pydantic>=2.11; extra == "dev"
|
|
24
24
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
25
25
|
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.15.
|
|
8
|
+
version = "0.15.2" # 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"
|
|
@@ -28,7 +28,7 @@ dependencies = []
|
|
|
28
28
|
|
|
29
29
|
[project.optional-dependencies]
|
|
30
30
|
dev = [
|
|
31
|
-
"openrewrite>=8.92.
|
|
31
|
+
"openrewrite>=8.92.18",
|
|
32
32
|
# Needed so type-attribution tests can resolve `pydantic` types.
|
|
33
33
|
"pydantic>=2.11",
|
|
34
34
|
"pytest>=8.0.0",
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from .all_branches_identical import AllBranchesIdentical
|
|
4
4
|
from .boolean_checks_not_inverted import BooleanChecksNotInverted
|
|
5
|
+
from .code_quality import CodeQuality
|
|
5
6
|
from .collapsible_if_statements import CollapsibleIfStatements
|
|
6
7
|
from .merge_identical_branches import MergeIdenticalBranches
|
|
7
8
|
from .remove_duplicate_conditions import RemoveDuplicateConditions
|
|
@@ -13,6 +14,7 @@ from .simplify_redundant_logical_expression import SimplifyRedundantLogicalExpre
|
|
|
13
14
|
__all__ = [
|
|
14
15
|
"AllBranchesIdentical",
|
|
15
16
|
"BooleanChecksNotInverted",
|
|
17
|
+
"CodeQuality",
|
|
16
18
|
"CollapsibleIfStatements",
|
|
17
19
|
"MergeIdenticalBranches",
|
|
18
20
|
"RemoveDuplicateConditions",
|
|
@@ -4,12 +4,15 @@ Python indentation lives in the ``Space`` before each token, so moving a block
|
|
|
4
4
|
to a shallower nesting level means rewriting every line it spans.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from typing import Optional, TypeVar
|
|
7
|
+
from typing import Optional, TypeVar, cast
|
|
8
8
|
|
|
9
|
+
from rewrite.java.markers import TrailingComma
|
|
9
10
|
from rewrite.java.support_types import Space
|
|
11
|
+
from rewrite.java.tree import J
|
|
12
|
+
from rewrite.markers import Marker
|
|
10
13
|
from rewrite.python.visitor import PythonVisitor
|
|
11
14
|
|
|
12
|
-
T = TypeVar("T")
|
|
15
|
+
T = TypeVar("T", bound=J)
|
|
13
16
|
|
|
14
17
|
|
|
15
18
|
def inherited_prefix(dropped: Space, body: Space) -> Space:
|
|
@@ -65,6 +68,20 @@ class _Reindent(PythonVisitor[None]):
|
|
|
65
68
|
return space
|
|
66
69
|
return Space(_comments=comments, _whitespace=whitespace)
|
|
67
70
|
|
|
71
|
+
def visit_marker(self, marker: Marker, p: None) -> Marker:
|
|
72
|
+
# The line break before the bracket closing a trailing-comma list
|
|
73
|
+
# lives on the comma.
|
|
74
|
+
if isinstance(marker, TrailingComma):
|
|
75
|
+
suffix = self.visit_space(marker.suffix, p)
|
|
76
|
+
if suffix is not marker.suffix:
|
|
77
|
+
return marker.replace(_suffix=suffix)
|
|
78
|
+
return marker
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def reindent_space(space: Space, old_base: str, new_base: str) -> Space:
|
|
82
|
+
""":func:`reindent` for trivia that has no tree to travel with."""
|
|
83
|
+
return _Reindent(old_base, new_base).visit_space(space, None)
|
|
84
|
+
|
|
68
85
|
|
|
69
86
|
def reindent(tree: T, old_base: str, new_base: str) -> T:
|
|
70
87
|
"""Re-base every line in *tree* that is indented at least to *old_base*.
|
|
@@ -74,4 +91,4 @@ def reindent(tree: T, old_base: str, new_base: str) -> T:
|
|
|
74
91
|
"""
|
|
75
92
|
if old_base == new_base:
|
|
76
93
|
return tree
|
|
77
|
-
return _Reindent(old_base, new_base).visit(tree, None)
|
|
94
|
+
return cast(T, _Reindent(old_base, new_base).visit(tree, None))
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Semantic equality of LST nodes, for recipes that keep one of two equal subtrees."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
|
|
5
|
+
from rewrite.java.support_types import J, Space
|
|
6
|
+
from rewrite.python.template.comparator import PythonComparatorVisitor
|
|
7
|
+
from rewrite.python.visitor import PythonVisitor
|
|
8
|
+
from rewrite.visitor import Cursor
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class _CommentTexts(PythonVisitor[List[str]]):
|
|
12
|
+
def visit_space(self, space: Optional[Space], p: List[str]) -> Space:
|
|
13
|
+
if space is not None:
|
|
14
|
+
p.extend(comment.text for comment in space.comments)
|
|
15
|
+
return super().visit_space(space, p)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _comment_texts(tree: J) -> List[str]:
|
|
19
|
+
texts: List[str] = []
|
|
20
|
+
_CommentTexts().visit(tree, texts)
|
|
21
|
+
return texts
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SemanticallyEqual:
|
|
25
|
+
"""Compare two LST nodes for structural equality."""
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def are_equal(a: Optional[J], b: Optional[J]) -> bool:
|
|
29
|
+
"""Whether *a* and *b* are the same code carrying the same comments.
|
|
30
|
+
|
|
31
|
+
Whitespace is ignored. Comments must match, so a recipe dropping either copy loses none.
|
|
32
|
+
"""
|
|
33
|
+
if a is None or b is None:
|
|
34
|
+
return a is b
|
|
35
|
+
return (
|
|
36
|
+
PythonComparatorVisitor().compare(a, b, Cursor(None, Cursor.ROOT_VALUE))
|
|
37
|
+
and _comment_texts(a) == _comment_texts(b)
|
|
38
|
+
)
|
openrewrite_migrate_python-0.15.2/src/openrewrite_code_quality_python/codequality/_side_effects.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from rewrite.java.tree import (
|
|
2
|
+
Binary as JBinary,
|
|
3
|
+
Expression,
|
|
4
|
+
FieldAccess,
|
|
5
|
+
Identifier,
|
|
6
|
+
Literal,
|
|
7
|
+
Parentheses,
|
|
8
|
+
Ternary,
|
|
9
|
+
Unary,
|
|
10
|
+
)
|
|
11
|
+
from rewrite.python.tree import Binary as PyBinary
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def is_side_effect_free(expr: Expression) -> bool:
|
|
15
|
+
"""Whether evaluating ``expr`` is taken to have no side effects.
|
|
16
|
+
|
|
17
|
+
Names, literals, attribute reads and operators over them are taken as pure.
|
|
18
|
+
A call, subscript, walrus, ``await`` or any other node may run code that matters.
|
|
19
|
+
"""
|
|
20
|
+
if isinstance(expr, (Identifier, Literal)):
|
|
21
|
+
return True
|
|
22
|
+
if isinstance(expr, FieldAccess):
|
|
23
|
+
return is_side_effect_free(expr.target)
|
|
24
|
+
if isinstance(expr, Parentheses):
|
|
25
|
+
return isinstance(expr.tree, Expression) and is_side_effect_free(expr.tree)
|
|
26
|
+
if isinstance(expr, Unary):
|
|
27
|
+
return is_side_effect_free(expr.expression)
|
|
28
|
+
if isinstance(expr, (JBinary, PyBinary)):
|
|
29
|
+
return is_side_effect_free(expr.left) and is_side_effect_free(expr.right)
|
|
30
|
+
if isinstance(expr, Ternary):
|
|
31
|
+
return (
|
|
32
|
+
is_side_effect_free(expr.condition)
|
|
33
|
+
and is_side_effect_free(expr.true_part)
|
|
34
|
+
and is_side_effect_free(expr.false_part)
|
|
35
|
+
)
|
|
36
|
+
return False
|
|
@@ -12,7 +12,7 @@ from typing import Optional, Tuple
|
|
|
12
12
|
|
|
13
13
|
from rewrite import Cursor
|
|
14
14
|
from rewrite.java.support_types import Space
|
|
15
|
-
from rewrite.java.tree import Block, ClassDeclaration, If, MethodDeclaration
|
|
15
|
+
from rewrite.java.tree import Block, ClassDeclaration, If, J, MethodDeclaration
|
|
16
16
|
from rewrite.python.tree import CompilationUnit
|
|
17
17
|
|
|
18
18
|
|
|
@@ -81,9 +81,11 @@ def space_after_block(cursor: Cursor) -> Optional[Space]:
|
|
|
81
81
|
know how to look past.
|
|
82
82
|
"""
|
|
83
83
|
child = cursor.value
|
|
84
|
+
if not isinstance(child, J):
|
|
85
|
+
return None
|
|
84
86
|
for parent in islice(cursor.get_path(), 1, None):
|
|
85
87
|
if isinstance(parent, (Block, CompilationUnit)):
|
|
86
|
-
statements = parent.
|
|
88
|
+
statements = parent.padding.statements
|
|
87
89
|
index = next(
|
|
88
90
|
(i for i, s in enumerate(statements) if s.element.id == child.id),
|
|
89
91
|
None,
|
|
@@ -125,7 +127,8 @@ def splice_out(
|
|
|
125
127
|
continue
|
|
126
128
|
if pending is not None:
|
|
127
129
|
head, line = pending
|
|
128
|
-
|
|
130
|
+
prefix = padded.element.prefix
|
|
131
|
+
_, tail = split_line(prefix) or (Space.EMPTY, prefix)
|
|
129
132
|
padded = padded.replace(
|
|
130
133
|
_element=padded.element.replace(
|
|
131
134
|
_prefix=concat(
|
|
@@ -15,7 +15,9 @@ from rewrite import ExecutionContext, Recipe, TreeVisitor
|
|
|
15
15
|
from rewrite.category import CategoryDescriptor
|
|
16
16
|
from rewrite.decorators import categorize
|
|
17
17
|
from rewrite.marketplace import Python
|
|
18
|
+
from rewrite.python.tree import CompilationUnit
|
|
18
19
|
from rewrite.python.visitor import PythonVisitor
|
|
20
|
+
from rewrite.java.tree import J
|
|
19
21
|
from rewrite.java.tree import (
|
|
20
22
|
Block,
|
|
21
23
|
If,
|
|
@@ -27,6 +29,7 @@ from openrewrite_code_quality_python.codequality._reindent import (
|
|
|
27
29
|
reindent,
|
|
28
30
|
)
|
|
29
31
|
from openrewrite_code_quality_python.codequality._semantically_equal import SemanticallyEqual
|
|
32
|
+
from openrewrite_code_quality_python.codequality._side_effects import is_side_effect_free
|
|
30
33
|
|
|
31
34
|
_CodeQuality = [
|
|
32
35
|
*Python,
|
|
@@ -68,6 +71,17 @@ def _collect_bodies(if_: If) -> list[Statement]:
|
|
|
68
71
|
return bodies
|
|
69
72
|
|
|
70
73
|
|
|
74
|
+
def _conditions_side_effect_free(if_: If) -> bool:
|
|
75
|
+
current = if_
|
|
76
|
+
while True:
|
|
77
|
+
if not is_side_effect_free(current.if_condition.tree):
|
|
78
|
+
return False
|
|
79
|
+
else_part = current.else_part
|
|
80
|
+
if else_part is None or not isinstance(else_part.body, If):
|
|
81
|
+
return True
|
|
82
|
+
current = else_part.body
|
|
83
|
+
|
|
84
|
+
|
|
71
85
|
def _all_bodies_equal(bodies: list[Statement]) -> bool:
|
|
72
86
|
"""Return True when every body is semantically equal to the first."""
|
|
73
87
|
if len(bodies) < 2:
|
|
@@ -90,7 +104,8 @@ class AllBranchesIdentical(Recipe):
|
|
|
90
104
|
and eliminates a source of confusion for future readers.
|
|
91
105
|
|
|
92
106
|
Only applies when the chain has an explicit ``else`` so that the
|
|
93
|
-
branches truly cover all cases
|
|
107
|
+
branches truly cover all cases, and when no condition may have a
|
|
108
|
+
side effect that removing it would lose.
|
|
94
109
|
|
|
95
110
|
Example:
|
|
96
111
|
Before:
|
|
@@ -116,7 +131,8 @@ class AllBranchesIdentical(Recipe):
|
|
|
116
131
|
return (
|
|
117
132
|
"Replace `if`/`elif`/`else` chains where every branch has "
|
|
118
133
|
"the same body with just the body, since the condition has "
|
|
119
|
-
"no effect on what code executes."
|
|
134
|
+
"no effect on what code executes. A chain whose conditions may "
|
|
135
|
+
"have side effects, such as a call, is left alone."
|
|
120
136
|
)
|
|
121
137
|
|
|
122
138
|
@property
|
|
@@ -130,19 +146,23 @@ class AllBranchesIdentical(Recipe):
|
|
|
130
146
|
def editor(self) -> TreeVisitor[Any, ExecutionContext]:
|
|
131
147
|
class Visitor(PythonVisitor[ExecutionContext]):
|
|
132
148
|
def visit_compilation_unit(self, cu, p):
|
|
133
|
-
|
|
134
|
-
|
|
149
|
+
visited = super().visit_compilation_unit(cu, p)
|
|
150
|
+
if not isinstance(visited, CompilationUnit):
|
|
151
|
+
return visited
|
|
152
|
+
cu = visited
|
|
153
|
+
new_stmts = _replace_identical_ifs_padded(cu.padding.statements)
|
|
135
154
|
if new_stmts is not None:
|
|
136
155
|
return cu.replace(_statements=new_stmts)
|
|
137
156
|
return cu
|
|
138
157
|
|
|
139
158
|
def visit_block(
|
|
140
159
|
self, block: Block, p: ExecutionContext
|
|
141
|
-
) ->
|
|
142
|
-
|
|
143
|
-
if not isinstance(
|
|
144
|
-
return
|
|
145
|
-
|
|
160
|
+
) -> J:
|
|
161
|
+
visited = super().visit_block(block, p)
|
|
162
|
+
if not isinstance(visited, Block):
|
|
163
|
+
return visited
|
|
164
|
+
block = visited
|
|
165
|
+
new_stmts = _replace_identical_ifs_padded(block.padding.statements)
|
|
146
166
|
if new_stmts is not None:
|
|
147
167
|
return block.replace(_statements=new_stmts)
|
|
148
168
|
return block
|
|
@@ -163,7 +183,7 @@ def _replace_identical_ifs_padded(padded_stmts) -> Optional[list]:
|
|
|
163
183
|
if not isinstance(s, If):
|
|
164
184
|
result.append(rp)
|
|
165
185
|
continue
|
|
166
|
-
if not _has_explicit_else(s):
|
|
186
|
+
if not _has_explicit_else(s) or not _conditions_side_effect_free(s):
|
|
167
187
|
result.append(rp)
|
|
168
188
|
continue
|
|
169
189
|
bodies = _collect_bodies(s)
|
|
@@ -172,12 +192,12 @@ def _replace_identical_ifs_padded(padded_stmts) -> Optional[list]:
|
|
|
172
192
|
continue
|
|
173
193
|
|
|
174
194
|
body = s.then_part
|
|
175
|
-
if not isinstance(body, Block) or not body.
|
|
195
|
+
if not isinstance(body, Block) or not body.padding.statements:
|
|
176
196
|
result.append(rp)
|
|
177
197
|
continue
|
|
178
198
|
|
|
179
199
|
if_indent = line_indent(s.prefix)
|
|
180
|
-
body_indent = line_indent(body.
|
|
200
|
+
body_indent = line_indent(body.padding.statements[0].element.prefix)
|
|
181
201
|
if (
|
|
182
202
|
if_indent is None
|
|
183
203
|
or body_indent is None
|
|
@@ -187,7 +207,7 @@ def _replace_identical_ifs_padded(padded_stmts) -> Optional[list]:
|
|
|
187
207
|
continue
|
|
188
208
|
|
|
189
209
|
body = reindent(body, body_indent, if_indent)
|
|
190
|
-
first = body.
|
|
210
|
+
first = body.padding.statements[0]
|
|
191
211
|
# Each lifted statement keeps its own padding, which is where a `;`
|
|
192
212
|
# separating it from the next one is recorded.
|
|
193
213
|
result.append(
|
|
@@ -197,7 +217,7 @@ def _replace_identical_ifs_padded(padded_stmts) -> Optional[list]:
|
|
|
197
217
|
)
|
|
198
218
|
)
|
|
199
219
|
)
|
|
200
|
-
result.extend(body.
|
|
220
|
+
result.extend(body.padding.statements[1:])
|
|
201
221
|
changed = True
|
|
202
222
|
|
|
203
223
|
if not changed:
|
|
@@ -8,18 +8,23 @@ when written as ``a != b``, and ``not (not x)`` is just ``x``.
|
|
|
8
8
|
See: https://rules.sonarsource.com/python/RSPEC-S1940/
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
from typing import Any, List
|
|
11
|
+
from typing import Any, List
|
|
12
12
|
|
|
13
|
-
from rewrite import ExecutionContext, Recipe, TreeVisitor
|
|
13
|
+
from rewrite import Cursor, ExecutionContext, Recipe, TreeVisitor
|
|
14
14
|
from rewrite.category import CategoryDescriptor
|
|
15
15
|
from rewrite.decorators import categorize
|
|
16
16
|
from rewrite.marketplace import Python
|
|
17
|
+
from rewrite.python.tree import Binary as PyBinary
|
|
17
18
|
from rewrite.python.visitor import PythonVisitor
|
|
18
|
-
from rewrite.java.
|
|
19
|
+
from rewrite.java.tree import J
|
|
20
|
+
from rewrite.java.support_types import JavaType, JLeftPadded
|
|
19
21
|
from rewrite.java.tree import (
|
|
22
|
+
Assert,
|
|
20
23
|
Binary as JBinary,
|
|
24
|
+
ControlParentheses,
|
|
21
25
|
Expression,
|
|
22
26
|
Parentheses,
|
|
27
|
+
Ternary,
|
|
23
28
|
Unary,
|
|
24
29
|
)
|
|
25
30
|
|
|
@@ -37,6 +42,59 @@ _OPERATOR_INVERSE = {
|
|
|
37
42
|
JBinary.Type.GreaterThanOrEqual: JBinary.Type.LessThan,
|
|
38
43
|
}
|
|
39
44
|
|
|
45
|
+
_EQUALITY = {JBinary.Type.Equal, JBinary.Type.NotEqual}
|
|
46
|
+
|
|
47
|
+
_COMPARISONS = {
|
|
48
|
+
*_OPERATOR_INVERSE,
|
|
49
|
+
PyBinary.Type.In,
|
|
50
|
+
PyBinary.Type.NotIn,
|
|
51
|
+
PyBinary.Type.Is,
|
|
52
|
+
PyBinary.Type.IsNot,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
_NUMERIC = {
|
|
56
|
+
JavaType.Primitive.Boolean,
|
|
57
|
+
JavaType.Primitive.Int,
|
|
58
|
+
JavaType.Primitive.Long,
|
|
59
|
+
JavaType.Primitive.Double,
|
|
60
|
+
JavaType.Primitive.Float,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _is_comparison(expr: Expression) -> bool:
|
|
65
|
+
return isinstance(expr, (JBinary, PyBinary)) and expr.operator in _COMPARISONS
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _is_invertible(comparison: JBinary) -> bool:
|
|
69
|
+
# `a < b < c` is one chained comparison whose last link is the outer node.
|
|
70
|
+
if _is_comparison(comparison.left) or _is_comparison(comparison.right):
|
|
71
|
+
return False
|
|
72
|
+
# Only numbers are totally ordered. A set orders by inclusion.
|
|
73
|
+
return comparison.operator in _EQUALITY or (
|
|
74
|
+
comparison.left.type in _NUMERIC and comparison.right.type in _NUMERIC
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _is_read_as_truth_value(cursor: Cursor) -> bool:
|
|
79
|
+
node = cursor.value
|
|
80
|
+
parent = cursor.parent_tree_cursor()
|
|
81
|
+
while isinstance(parent.value, Parentheses):
|
|
82
|
+
node = parent.value
|
|
83
|
+
parent = parent.parent_tree_cursor()
|
|
84
|
+
value = parent.value
|
|
85
|
+
if isinstance(value, ControlParentheses):
|
|
86
|
+
return True
|
|
87
|
+
if isinstance(value, Unary):
|
|
88
|
+
return value.operator == Unary.Type.Not
|
|
89
|
+
if isinstance(value, Ternary):
|
|
90
|
+
return value.condition is node
|
|
91
|
+
if isinstance(value, Assert):
|
|
92
|
+
return value.condition is node
|
|
93
|
+
# `a or b` yields an operand, so its operands are truth values only if it is one.
|
|
94
|
+
if isinstance(value, JBinary) and value.operator in (JBinary.Type.And, JBinary.Type.Or):
|
|
95
|
+
return _is_read_as_truth_value(parent)
|
|
96
|
+
return False
|
|
97
|
+
|
|
40
98
|
|
|
41
99
|
@categorize(_CodeQuality)
|
|
42
100
|
class BooleanChecksNotInverted(Recipe):
|
|
@@ -71,7 +129,10 @@ class BooleanChecksNotInverted(Recipe):
|
|
|
71
129
|
return (
|
|
72
130
|
"Replace inverted boolean comparisons like `not (a == b)` with the "
|
|
73
131
|
"equivalent direct operator (`a != b`), and remove double negations "
|
|
74
|
-
"like `not (not x)`."
|
|
132
|
+
"like `not (not x)`. An ordering such as `not (a < b)` is only "
|
|
133
|
+
"inverted where both operands' types resolve to numbers, a chained "
|
|
134
|
+
"comparison is left alone, and a double negation is only removed "
|
|
135
|
+
"where its value is read as a truth value, as in a condition."
|
|
75
136
|
)
|
|
76
137
|
|
|
77
138
|
@property
|
|
@@ -86,11 +147,11 @@ class BooleanChecksNotInverted(Recipe):
|
|
|
86
147
|
class Visitor(PythonVisitor[ExecutionContext]):
|
|
87
148
|
def visit_unary(
|
|
88
149
|
self, unary: Unary, p: ExecutionContext
|
|
89
|
-
) ->
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
150
|
+
) -> J:
|
|
151
|
+
visited = super().visit_unary(unary, p)
|
|
152
|
+
if not isinstance(visited, Unary):
|
|
153
|
+
return visited
|
|
154
|
+
unary = visited
|
|
94
155
|
|
|
95
156
|
if unary.operator != Unary.Type.Not:
|
|
96
157
|
return unary
|
|
@@ -103,11 +164,15 @@ class BooleanChecksNotInverted(Recipe):
|
|
|
103
164
|
inner = inner.tree
|
|
104
165
|
|
|
105
166
|
# Double negation: not (not x) -> x
|
|
106
|
-
if
|
|
167
|
+
if (
|
|
168
|
+
isinstance(inner, Unary)
|
|
169
|
+
and inner.operator == Unary.Type.Not
|
|
170
|
+
and _is_read_as_truth_value(self.cursor)
|
|
171
|
+
):
|
|
107
172
|
return inner.expression.replace(_prefix=unary.prefix)
|
|
108
173
|
|
|
109
174
|
# Inverted comparison: not (a == b) -> a != b
|
|
110
|
-
if isinstance(inner, JBinary):
|
|
175
|
+
if isinstance(inner, JBinary) and _is_invertible(inner):
|
|
111
176
|
inverse = _OPERATOR_INVERSE.get(inner.operator)
|
|
112
177
|
if inverse is not None:
|
|
113
178
|
return inner.padding.replace(
|
openrewrite_migrate_python-0.15.2/src/openrewrite_code_quality_python/codequality/code_quality.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Composite recipe bundling the Python code quality fixes."""
|
|
2
|
+
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from rewrite import Recipe
|
|
6
|
+
from rewrite.category import CategoryDescriptor
|
|
7
|
+
from rewrite.decorators import categorize
|
|
8
|
+
from rewrite.marketplace import Python
|
|
9
|
+
|
|
10
|
+
from .all_branches_identical import AllBranchesIdentical
|
|
11
|
+
from .boolean_checks_not_inverted import BooleanChecksNotInverted
|
|
12
|
+
from .collapsible_if_statements import CollapsibleIfStatements
|
|
13
|
+
from .merge_identical_branches import MergeIdenticalBranches
|
|
14
|
+
from .remove_duplicate_conditions import RemoveDuplicateConditions
|
|
15
|
+
from .remove_self_assignment import RemoveSelfAssignment
|
|
16
|
+
from .remove_unconditional_value_overwrite import RemoveUnconditionalValueOverwrite
|
|
17
|
+
from .simplify_boolean_literal import SimplifyBooleanLiteral
|
|
18
|
+
from .simplify_redundant_logical_expression import SimplifyRedundantLogicalExpression
|
|
19
|
+
|
|
20
|
+
_CodeQuality = [
|
|
21
|
+
*Python,
|
|
22
|
+
CategoryDescriptor(display_name="Code quality"),
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@categorize(_CodeQuality)
|
|
27
|
+
class CodeQuality(Recipe):
|
|
28
|
+
"""
|
|
29
|
+
Apply every Python code quality fix.
|
|
30
|
+
|
|
31
|
+
Example:
|
|
32
|
+
Before:
|
|
33
|
+
if done == True:
|
|
34
|
+
if ready:
|
|
35
|
+
run()
|
|
36
|
+
|
|
37
|
+
After:
|
|
38
|
+
if done and ready:
|
|
39
|
+
run()
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def name(self) -> str:
|
|
44
|
+
return "org.openrewrite.python.codequality.CodeQuality"
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def display_name(self) -> str:
|
|
48
|
+
return "Code quality"
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def description(self) -> str:
|
|
52
|
+
return (
|
|
53
|
+
"Apply all Python code quality recipes that change code: simplify boolean "
|
|
54
|
+
"expressions, merge and collapse `if` chains, and remove dead conditions and "
|
|
55
|
+
"writes. Search-only recipes, which mark code for review, are not included."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def tags(self) -> List[str]:
|
|
60
|
+
return ["python", "code-quality"]
|
|
61
|
+
|
|
62
|
+
def recipe_list(self) -> List[Recipe]:
|
|
63
|
+
return [
|
|
64
|
+
SimplifyBooleanLiteral(),
|
|
65
|
+
BooleanChecksNotInverted(),
|
|
66
|
+
SimplifyRedundantLogicalExpression(),
|
|
67
|
+
RemoveDuplicateConditions(),
|
|
68
|
+
AllBranchesIdentical(),
|
|
69
|
+
MergeIdenticalBranches(),
|
|
70
|
+
CollapsibleIfStatements(),
|
|
71
|
+
RemoveSelfAssignment(),
|
|
72
|
+
RemoveUnconditionalValueOverwrite(),
|
|
73
|
+
]
|