openrewrite-migrate-python 0.15.1__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.1 → openrewrite_migrate_python-0.15.2}/PKG-INFO +2 -2
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/pyproject.toml +2 -2
- {openrewrite_migrate_python-0.15.1 → 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.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/_trivia.py +6 -3
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/all_branches_identical.py +34 -14
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/boolean_checks_not_inverted.py +76 -11
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/collapsible_if_statements.py +67 -46
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/find_suspicious_chained_comparison.py +14 -21
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/merge_identical_branches.py +21 -43
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/remove_duplicate_conditions.py +13 -7
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/remove_self_assignment.py +8 -4
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/remove_unconditional_value_overwrite.py +6 -5
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/simplify_boolean_literal.py +28 -34
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/simplify_redundant_logical_expression.py +8 -7
- {openrewrite_migrate_python-0.15.1 → 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.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/array_deprecations.py +9 -50
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/ast_deprecations.py +13 -110
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/asyncio_coroutine_to_async.py +56 -54
- {openrewrite_migrate_python-0.15.1 → 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.1 → 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.1 → 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.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/datetime_utc.py +23 -41
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/distutils_deprecations.py +9 -28
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/distutils_migrations.py +7 -3
- {openrewrite_migrate_python-0.15.1 → 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.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/langchain_classic_imports.py +29 -48
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/langchain_community_imports.py +9 -4
- {openrewrite_migrate_python-0.15.1 → 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.1 → 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.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/os_deprecations.py +29 -48
- {openrewrite_migrate_python-0.15.1 → 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.1 → 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.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/pydantic_deprecations.py +85 -85
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/pydantic_fields.py +9 -21
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/pydantic_model_fields.py +5 -5
- {openrewrite_migrate_python-0.15.1 → 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.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/shutil_deprecations.py +8 -4
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/socket_deprecations.py +8 -4
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/ssl_deprecations.py +13 -42
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/string_formatting.py +39 -20
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/sys_deprecations.py +17 -27
- {openrewrite_migrate_python-0.15.1 → 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.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/tempfile_deprecations.py +12 -20
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/threading_deprecations.py +67 -172
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/threading_is_alive_deprecation.py +6 -28
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/typing_deprecations.py +25 -45
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/typing_union_to_pipe.py +52 -41
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/unittest_deprecations.py +6 -45
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_python39.py +0 -2
- {openrewrite_migrate_python-0.15.1 → 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.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python.egg-info/PKG-INFO +2 -2
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python.egg-info/SOURCES.txt +6 -3
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python.egg-info/requires.txt +1 -1
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_ast_deprecations.py +15 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_asyncio_coroutine_to_async.py +60 -2
- {openrewrite_migrate_python-0.15.1 → 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.1 → openrewrite_migrate_python-0.15.2}/tests/test_calendar_deprecations.py +15 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_cgi_parse_deprecations.py +17 -2
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_configparser_deprecations.py +25 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_datetime_utc.py +31 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_distutils_deprecations.py +15 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_functools_deprecations.py +34 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_gettext_deprecations.py +9 -2
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_html_parser_deprecations.py +56 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_langchain_classic_imports.py +11 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_langchain_provider_imports.py +19 -4
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_locale_deprecations.py +21 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_os_deprecations.py +32 -2
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_pathlib_deprecations.py +21 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_pep594_migrations.py +12 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_pkgutil_deprecations.py +44 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_platform_deprecations.py +47 -2
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_pydantic_deprecations.py +146 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_re_deprecations.py +57 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_removed_modules_312.py +13 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_ssl_deprecations.py +22 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_string_formatting.py +101 -2
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_sys_deprecations.py +9 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_sys_last_deprecations.py +44 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_tarfile_deprecations.py +29 -2
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_tempfile_deprecations.py +21 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_threading_deprecations.py +27 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_typing_alias_moves.py +0 -23
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_typing_deprecations.py +32 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_typing_union_to_pipe.py +68 -1
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_unittest_deprecations.py +35 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_urllib_deprecations.py +20 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_xml_deprecations.py +19 -0
- openrewrite_migrate_python-0.15.1/src/openrewrite_code_quality_python/codequality/_semantically_equal.py +0 -32
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/_from_imports.py +0 -68
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/_import_hygiene.py +0 -55
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/_typing_imports.py +0 -71
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/aifc_migrations.py +0 -508
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/cgi_migrations.py +0 -204
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/cgi_parse_deprecations.py +0 -205
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/configparser_deprecations.py +0 -254
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/future_imports.py +0 -155
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/gettext_deprecations.py +0 -148
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/html_parser_deprecations.py +0 -106
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/imp_migrations.py +0 -125
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/locale_deprecations.py +0 -190
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/macpath_deprecations.py +0 -116
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/mailcap_migrations.py +0 -120
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/nntplib_migrations.py +0 -118
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/pep594_system_migrations.py +0 -420
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/pipes_migrations.py +0 -121
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/platform_deprecations.py +0 -173
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/removed_modules_312.py +0 -145
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/tarfile_deprecations.py +0 -113
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/telnetlib_migrations.py +0 -123
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/uu_migrations.py +0 -120
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/xdrlib_migrations.py +0 -120
- openrewrite_migrate_python-0.15.1/src/openrewrite_migrate_python/migrate/xml_deprecations.py +0 -188
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/setup.cfg +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/__init__.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/__init__.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_code_quality_python/codequality/code_quality.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/__init__.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/_langchain_tables.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/_markers.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/_slots.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/avro_migrations.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/typing_callable.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain02.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain1.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_pydantic.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_python310.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_python311.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_python312.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_python313.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_python314.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python/migrate/upgrade_to_python38.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python.egg-info/dependency_links.txt +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python.egg-info/entry_points.txt +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/src/openrewrite_migrate_python.egg-info/top_level.txt +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_aifc_migrations.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_array_deprecations.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_avro_migrations.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_change_import_recipes.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_collections_abc_migrations.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_distutils_migrations.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_future_imports.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_imp_migrations.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_langchain_community_imports.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_locale_getdefaultlocale_deprecation.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_macpath_deprecations.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_mailcap_migrations.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_markers.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_pep594_system_migrations.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_pydantic_fields.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_pydantic_model_fields.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_shutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_socket_deprecations.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_threading_extended.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_threading_is_alive_deprecation.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_typing_callable.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_upgrade_recipes.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_upgrade_to_pydantic.py +0 -0
- {openrewrite_migrate_python-0.15.1 → openrewrite_migrate_python-0.15.2}/tests/test_xdrlib_migrations.py +0 -0
|
@@ -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",
|
|
@@ -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(
|
|
@@ -9,34 +9,33 @@ easier to follow.
|
|
|
9
9
|
See: https://rules.sonarsource.com/python/RSPEC-S1066/
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
from typing import Any, List
|
|
12
|
+
from typing import Any, List
|
|
13
13
|
|
|
14
14
|
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
18
|
from rewrite.markers import Markers
|
|
19
|
-
from rewrite.python.
|
|
19
|
+
from rewrite.python.template.precedence import maybe_parenthesize
|
|
20
20
|
from rewrite.python.visitor import PythonVisitor
|
|
21
|
-
from rewrite.java.support_types import JLeftPadded, JRightPadded, Space
|
|
21
|
+
from rewrite.java.support_types import Comment, JLeftPadded, JRightPadded, Space
|
|
22
22
|
from rewrite.java.tree import (
|
|
23
|
-
Assignment,
|
|
24
23
|
Binary as JBinary,
|
|
25
24
|
Block,
|
|
26
25
|
ControlParentheses,
|
|
27
|
-
Expression,
|
|
28
26
|
If,
|
|
29
|
-
|
|
30
|
-
Parentheses,
|
|
31
|
-
Ternary,
|
|
27
|
+
J,
|
|
32
28
|
)
|
|
33
29
|
from rewrite.utils import random_id
|
|
34
30
|
|
|
35
31
|
from openrewrite_code_quality_python.codequality._reindent import (
|
|
36
|
-
inherited_prefix,
|
|
37
32
|
line_indent,
|
|
38
33
|
reindent,
|
|
34
|
+
reindent_space,
|
|
39
35
|
)
|
|
36
|
+
from openrewrite_code_quality_python.codequality._trivia import concat, split_line
|
|
37
|
+
|
|
38
|
+
_HOISTED_COMMENTS = "collapsible_if_statements.hoisted_comments"
|
|
40
39
|
|
|
41
40
|
_CodeQuality = [
|
|
42
41
|
*Python,
|
|
@@ -44,33 +43,12 @@ _CodeQuality = [
|
|
|
44
43
|
]
|
|
45
44
|
|
|
46
45
|
|
|
47
|
-
def
|
|
48
|
-
"""
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"""
|
|
54
|
-
if isinstance(expr, JBinary):
|
|
55
|
-
return expr.operator == JBinary.Type.Or
|
|
56
|
-
return isinstance(expr, (Ternary, Lambda, Assignment, StatementExpression))
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def _operand(expr: Expression, prefix: Space) -> Expression:
|
|
60
|
-
"""Prepare *expr* as an operand of the merged ``and``, parenthesizing it
|
|
61
|
-
when its own operator would otherwise be captured."""
|
|
62
|
-
if _binds_looser_than_and(expr):
|
|
63
|
-
return Parentheses(
|
|
64
|
-
_id=random_id(),
|
|
65
|
-
_prefix=prefix,
|
|
66
|
-
_markers=Markers.EMPTY,
|
|
67
|
-
_tree=JRightPadded(
|
|
68
|
-
_element=expr.replace(_prefix=Space.EMPTY),
|
|
69
|
-
_after=Space.EMPTY,
|
|
70
|
-
_markers=Markers.EMPTY,
|
|
71
|
-
),
|
|
72
|
-
)
|
|
73
|
-
return expr.replace(_prefix=prefix)
|
|
46
|
+
def _with_comments_above(prefix: Space, comments: List[Comment]) -> Space:
|
|
47
|
+
"""*prefix* with *comments* on their own lines just before the token it
|
|
48
|
+
leads into. Each comment's suffix must already end at that token's indent."""
|
|
49
|
+
if not comments:
|
|
50
|
+
return prefix
|
|
51
|
+
return Space(_comments=[*prefix.comments, *comments], _whitespace=prefix.whitespace)
|
|
74
52
|
|
|
75
53
|
|
|
76
54
|
@categorize(_CodeQuality)
|
|
@@ -119,13 +97,20 @@ class CollapsibleIfStatements(Recipe):
|
|
|
119
97
|
|
|
120
98
|
def editor(self) -> TreeVisitor[Any, ExecutionContext]:
|
|
121
99
|
class Visitor(PythonVisitor[ExecutionContext]):
|
|
100
|
+
def visit_else(self, else_: If.Else, p: ExecutionContext) -> J:
|
|
101
|
+
visited = super().visit_else(else_, p)
|
|
102
|
+
hoisted = self.cursor.get_message(_HOISTED_COMMENTS, [])
|
|
103
|
+
if not isinstance(visited, If.Else) or not hoisted:
|
|
104
|
+
return visited
|
|
105
|
+
return visited.replace(_prefix=_with_comments_above(visited.prefix, hoisted))
|
|
106
|
+
|
|
122
107
|
def visit_if(
|
|
123
108
|
self, if_: If, p: ExecutionContext
|
|
124
|
-
) ->
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
109
|
+
) -> J:
|
|
110
|
+
visited = super().visit_if(if_, p)
|
|
111
|
+
if not isinstance(visited, If):
|
|
112
|
+
return visited
|
|
113
|
+
if_ = visited
|
|
129
114
|
|
|
130
115
|
# Outer if must have no else/elif
|
|
131
116
|
if if_.else_part is not None:
|
|
@@ -164,19 +149,50 @@ class CollapsibleIfStatements(Recipe):
|
|
|
164
149
|
):
|
|
165
150
|
return if_
|
|
166
151
|
|
|
152
|
+
# An `elif` starts its line at the `elif` keyword, which the
|
|
153
|
+
# enclosing `Else` holds.
|
|
154
|
+
parent = self.cursor.parent_tree_cursor()
|
|
155
|
+
under_elif = isinstance(parent.value, If.Else)
|
|
156
|
+
outer_indent = line_indent(
|
|
157
|
+
parent.value.prefix if isinstance(parent.value, If.Else) else if_.prefix
|
|
158
|
+
)
|
|
159
|
+
split = split_line(inner.prefix)
|
|
160
|
+
if (
|
|
161
|
+
outer_indent is None
|
|
162
|
+
or not header_indent.startswith(outer_indent)
|
|
163
|
+
or split is None
|
|
164
|
+
):
|
|
165
|
+
return if_
|
|
166
|
+
# The outer header's trailing comment stays on the merged
|
|
167
|
+
# header. Comments on their own line before the inner `if`
|
|
168
|
+
# describe its condition, so they go above the merged header
|
|
169
|
+
# without the blank lines around them.
|
|
170
|
+
header_trailer, inner_leader = split
|
|
171
|
+
hoisted = reindent_space(
|
|
172
|
+
Space(_comments=inner_leader.comments, _whitespace=""),
|
|
173
|
+
header_indent,
|
|
174
|
+
outer_indent,
|
|
175
|
+
).comments
|
|
176
|
+
|
|
167
177
|
combined = JBinary(
|
|
168
178
|
_id=random_id(),
|
|
169
179
|
_prefix=if_.if_condition.tree.prefix,
|
|
170
180
|
_markers=Markers.EMPTY,
|
|
171
|
-
_left=
|
|
181
|
+
_left=if_.if_condition.tree.replace(_prefix=Space.EMPTY),
|
|
172
182
|
_operator=JLeftPadded(
|
|
173
183
|
_before=Space.SINGLE_SPACE,
|
|
174
184
|
_element=JBinary.Type.And,
|
|
175
185
|
_markers=Markers.EMPTY,
|
|
176
186
|
),
|
|
177
|
-
_right=
|
|
187
|
+
_right=reindent(inner.if_condition.tree, header_indent, outer_indent).replace(
|
|
188
|
+
_prefix=Space.SINGLE_SPACE
|
|
189
|
+
),
|
|
178
190
|
_type=None,
|
|
179
191
|
)
|
|
192
|
+
combined = combined.replace(
|
|
193
|
+
_left=maybe_parenthesize(combined, combined.left.id, combined.left),
|
|
194
|
+
_right=maybe_parenthesize(combined, combined.right.id, combined.right),
|
|
195
|
+
)
|
|
180
196
|
|
|
181
197
|
new_condition = ControlParentheses(
|
|
182
198
|
_id=if_.if_condition.id,
|
|
@@ -193,18 +209,23 @@ class CollapsibleIfStatements(Recipe):
|
|
|
193
209
|
# level at a time, and each pass rebases the comments the
|
|
194
210
|
# previous one left at the level it dropped.
|
|
195
211
|
inner_body = reindent(inner_body, body_indent, header_indent)
|
|
196
|
-
first = inner_body.
|
|
212
|
+
first = inner_body.padding.statements[0]
|
|
197
213
|
inner_body = inner_body.replace(
|
|
198
214
|
_statements=[
|
|
199
215
|
first.replace(
|
|
200
216
|
_element=first.element.replace(
|
|
201
|
-
_prefix=
|
|
217
|
+
_prefix=concat(header_trailer, first.element.prefix)
|
|
202
218
|
)
|
|
203
219
|
),
|
|
204
|
-
*inner_body.
|
|
220
|
+
*inner_body.padding.statements[1:],
|
|
205
221
|
]
|
|
206
222
|
)
|
|
207
223
|
|
|
224
|
+
if under_elif:
|
|
225
|
+
parent.put_message(_HOISTED_COMMENTS, hoisted)
|
|
226
|
+
else:
|
|
227
|
+
if_ = if_.replace(_prefix=_with_comments_above(if_.prefix, hoisted))
|
|
228
|
+
|
|
208
229
|
return if_.replace(
|
|
209
230
|
_if_condition=new_condition,
|
|
210
231
|
).padding.replace(
|