openrewrite-migrate-python 0.14.0.dev20260902131752__tar.gz → 0.14.0.dev20260902134304__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.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/PKG-INFO +1 -1
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/pyproject.toml +1 -1
- openrewrite_migrate_python-0.14.0.dev20260902134304/src/openrewrite_code_quality_python/codequality/_trivia.py +139 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_code_quality_python/codequality/remove_self_assignment.py +15 -118
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_code_quality_python/codequality/remove_unconditional_value_overwrite.py +106 -55
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python.egg-info/PKG-INFO +1 -1
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python.egg-info/SOURCES.txt +1 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/setup.cfg +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_code_quality_python/__init__.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_code_quality_python/codequality/__init__.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_code_quality_python/codequality/_reindent.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_code_quality_python/codequality/_semantically_equal.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_code_quality_python/codequality/all_branches_identical.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_code_quality_python/codequality/boolean_checks_not_inverted.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_code_quality_python/codequality/collapsible_if_statements.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_code_quality_python/codequality/find_suspicious_chained_comparison.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_code_quality_python/codequality/merge_identical_branches.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_code_quality_python/codequality/remove_duplicate_conditions.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_code_quality_python/codequality/simplify_boolean_literal.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_code_quality_python/codequality/simplify_redundant_logical_expression.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/__init__.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/__init__.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/_import_hygiene.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/_langchain_tables.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/_markers.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/_typing_imports.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/aifc_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/array_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/ast_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/asyncio_coroutine_to_async.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/asyncio_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/avro_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/calendar_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/cgi_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/cgi_parse_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/collections_abc_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/configparser_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/datetime_utc.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/distutils_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/distutils_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/functools_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/future_imports.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/gettext_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/html_parser_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/imp_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/langchain_classic_imports.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/langchain_community_imports.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/langchain_provider_imports.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/locale_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/locale_getdefaultlocale_deprecation.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/macpath_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/mailcap_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/nntplib_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/os_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/pathlib_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/pep594_system_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/pipes_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/pkgutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/platform_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/pydantic_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/pydantic_fields.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/pydantic_model_fields.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/re_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/removed_modules_312.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/shutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/socket_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/ssl_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/string_formatting.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/sys_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/sys_last_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/tarfile_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/telnetlib_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/tempfile_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/threading_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/threading_is_alive_deprecation.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/typing_callable.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/typing_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/typing_union_to_pipe.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/unittest_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain02.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain1.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/upgrade_to_pydantic.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/upgrade_to_python310.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/upgrade_to_python311.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/upgrade_to_python312.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/upgrade_to_python313.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/upgrade_to_python314.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/upgrade_to_python38.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/upgrade_to_python39.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/urllib_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/uu_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/xdrlib_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python/migrate/xml_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python.egg-info/dependency_links.txt +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python.egg-info/entry_points.txt +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python.egg-info/requires.txt +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/src/openrewrite_migrate_python.egg-info/top_level.txt +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_aifc_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_array_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_ast_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_asyncio_coroutine_to_async.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_asyncio_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_avro_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_calendar_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_cgi_parse_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_change_import_recipes.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_collections_abc_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_configparser_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_datetime_utc.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_distutils_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_future_imports.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_gettext_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_html_parser_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_imp_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_langchain_classic_imports.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_langchain_community_imports.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_langchain_provider_imports.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_locale_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_locale_getdefaultlocale_deprecation.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_macpath_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_os_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_pep594_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_pep594_system_migrations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_pkgutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_platform_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_pydantic_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_pydantic_fields.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_pydantic_model_fields.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_re_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_removed_modules_312.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_shutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_ssl_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_string_formatting.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_sys_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_sys_last_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_tarfile_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_threading_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_threading_extended.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_threading_is_alive_deprecation.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_typing_alias_moves.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_typing_callable.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_typing_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_typing_union_to_pipe.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_unittest_deprecations.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_upgrade_recipes.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_upgrade_to_pydantic.py +0 -0
- {openrewrite_migrate_python-0.14.0.dev20260902131752 → openrewrite_migrate_python-0.14.0.dev20260902134304}/tests/test_xml_deprecations.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openrewrite-migrate-python
|
|
3
|
-
Version: 0.14.0.
|
|
3
|
+
Version: 0.14.0.dev20260902134304
|
|
4
4
|
Summary: OpenRewrite recipes for migrating Python codebases to newer Python versions.
|
|
5
5
|
Author-email: "Moderne Inc." <support@moderne.io>
|
|
6
6
|
License: Moderne Proprietary
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "openrewrite-migrate-python"
|
|
7
7
|
description = "OpenRewrite recipes for migrating Python codebases to newer Python versions."
|
|
8
|
-
version = "0.14.0.
|
|
8
|
+
version = "0.14.0.dev20260902134304" # Updated dynamically during release
|
|
9
9
|
authors = [{ name = "Moderne Inc.", email = "support@moderne.io" }]
|
|
10
10
|
license = { text = "Moderne Proprietary" }
|
|
11
11
|
readme = "README.md"
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Line-level trivia splicing for recipes that delete whole statements.
|
|
3
|
+
|
|
4
|
+
A statement's *trailing* comment is stored on whatever the printer emits
|
|
5
|
+
next -- the following statement's prefix, or, for a block's last statement,
|
|
6
|
+
trivia outside the block. Deleting a statement therefore moves trivia
|
|
7
|
+
between its neighbours.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from itertools import islice
|
|
11
|
+
from typing import Optional, Tuple
|
|
12
|
+
|
|
13
|
+
from rewrite import Cursor
|
|
14
|
+
from rewrite.java.support_types import Space
|
|
15
|
+
from rewrite.java.tree import Block, ClassDeclaration, If, MethodDeclaration
|
|
16
|
+
from rewrite.python.tree import CompilationUnit
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def split_line(space: Space) -> Optional[Tuple[Space, Space]]:
|
|
20
|
+
"""Split ``space`` at its first newline, or return None if it has no newline.
|
|
21
|
+
|
|
22
|
+
The head is the trivia that still belongs to the preceding line -- most
|
|
23
|
+
importantly that line's trailing comment. The tail starts with the
|
|
24
|
+
newline itself.
|
|
25
|
+
"""
|
|
26
|
+
newline = space.whitespace.find("\n")
|
|
27
|
+
if newline != -1:
|
|
28
|
+
return (
|
|
29
|
+
Space(_comments=[], _whitespace=space.whitespace[:newline]),
|
|
30
|
+
Space(_comments=space.comments, _whitespace=space.whitespace[newline:]),
|
|
31
|
+
)
|
|
32
|
+
for i, comment in enumerate(space.comments):
|
|
33
|
+
newline = comment.suffix.find("\n")
|
|
34
|
+
if newline != -1:
|
|
35
|
+
return (
|
|
36
|
+
Space(
|
|
37
|
+
_comments=[
|
|
38
|
+
*space.comments[:i],
|
|
39
|
+
comment.replace(_suffix=comment.suffix[:newline]),
|
|
40
|
+
],
|
|
41
|
+
_whitespace=space.whitespace,
|
|
42
|
+
),
|
|
43
|
+
Space(
|
|
44
|
+
_comments=space.comments[i + 1:],
|
|
45
|
+
_whitespace=comment.suffix[newline:],
|
|
46
|
+
),
|
|
47
|
+
)
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def concat(left: Space, right: Space) -> Space:
|
|
52
|
+
"""Return the trivia that prints as ``left`` followed by ``right``."""
|
|
53
|
+
if not left.comments:
|
|
54
|
+
return Space(
|
|
55
|
+
_comments=right.comments,
|
|
56
|
+
_whitespace=left.whitespace + right.whitespace,
|
|
57
|
+
)
|
|
58
|
+
last = left.comments[-1]
|
|
59
|
+
return Space(
|
|
60
|
+
_comments=[
|
|
61
|
+
*left.comments[:-1],
|
|
62
|
+
last.replace(_suffix=last.suffix + right.whitespace),
|
|
63
|
+
*right.comments,
|
|
64
|
+
],
|
|
65
|
+
_whitespace=left.whitespace,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def opens_with_trailing_comment(space: Space) -> bool:
|
|
70
|
+
"""Return True when ``space`` opens with a comment on the line it continues."""
|
|
71
|
+
split = split_line(space)
|
|
72
|
+
return bool((split[0] if split else space).comments)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def space_after_block(cursor: Cursor) -> Optional[Space]:
|
|
76
|
+
"""The trivia the printer emits after the block at ``cursor``.
|
|
77
|
+
|
|
78
|
+
A block's last statement keeps its trailing comment outside the block --
|
|
79
|
+
on the next statement of an enclosing block, on the `else` of an enclosing
|
|
80
|
+
`if`, or at end of file. Returns None for the containers this does not
|
|
81
|
+
know how to look past.
|
|
82
|
+
"""
|
|
83
|
+
child = cursor.value
|
|
84
|
+
for parent in islice(cursor.get_path(), 1, None):
|
|
85
|
+
if isinstance(parent, (Block, CompilationUnit)):
|
|
86
|
+
statements = parent._statements
|
|
87
|
+
index = next(
|
|
88
|
+
(i for i, s in enumerate(statements) if s.element.id == child.id),
|
|
89
|
+
None,
|
|
90
|
+
)
|
|
91
|
+
if index is None:
|
|
92
|
+
return None
|
|
93
|
+
if index + 1 < len(statements):
|
|
94
|
+
return statements[index + 1].element.prefix
|
|
95
|
+
if isinstance(parent, CompilationUnit):
|
|
96
|
+
return parent.eof
|
|
97
|
+
elif isinstance(parent, If):
|
|
98
|
+
if parent.else_part is not None and parent.else_part.id != child.id:
|
|
99
|
+
return parent.else_part.prefix
|
|
100
|
+
elif not isinstance(parent, (ClassDeclaration, If.Else, MethodDeclaration)):
|
|
101
|
+
return None
|
|
102
|
+
child = parent
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def splice_out(
|
|
107
|
+
padded_statements: list, end: Space, to_remove: set[int]
|
|
108
|
+
) -> Tuple[list, Space]:
|
|
109
|
+
"""Return the statements without those at ``to_remove``, and the new end trivia.
|
|
110
|
+
|
|
111
|
+
The survivor takes the removed statement's place on the page: the trivia
|
|
112
|
+
up to the removed statement's own line, which holds the preceding
|
|
113
|
+
statement's trailing comment, then that line's break and indent. Only
|
|
114
|
+
comments standing on their own line before the survivor come from the
|
|
115
|
+
survivor; the removed statement's go with it.
|
|
116
|
+
"""
|
|
117
|
+
result = []
|
|
118
|
+
pending: Optional[Tuple[Space, Space]] = None
|
|
119
|
+
for i, padded in enumerate(padded_statements):
|
|
120
|
+
if i in to_remove:
|
|
121
|
+
if pending is None:
|
|
122
|
+
prefix = padded.element.prefix
|
|
123
|
+
# A file's first statement starts a line without a break to show for it.
|
|
124
|
+
pending = split_line(prefix) or (prefix, Space.EMPTY)
|
|
125
|
+
continue
|
|
126
|
+
if pending is not None:
|
|
127
|
+
head, line = pending
|
|
128
|
+
_, tail = split_line(padded.element.prefix)
|
|
129
|
+
padded = padded.replace(
|
|
130
|
+
_element=padded.element.replace(
|
|
131
|
+
_prefix=concat(
|
|
132
|
+
head,
|
|
133
|
+
Space(_comments=tail.comments, _whitespace=line.whitespace),
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
pending = None
|
|
138
|
+
result.append(padded)
|
|
139
|
+
return result, end if pending is None else concat(pending[0], end)
|
|
@@ -16,18 +16,23 @@ from rewrite import Cursor, ExecutionContext, Recipe, TreeVisitor
|
|
|
16
16
|
from rewrite.category import CategoryDescriptor
|
|
17
17
|
from rewrite.decorators import categorize
|
|
18
18
|
from rewrite.marketplace import Python
|
|
19
|
+
from rewrite.java.support_types import Space
|
|
19
20
|
from rewrite.python.tree import CompilationUnit
|
|
20
21
|
from rewrite.python.visitor import PythonVisitor
|
|
21
|
-
from rewrite.java.support_types import Space
|
|
22
22
|
from rewrite.java.tree import (
|
|
23
23
|
Assignment,
|
|
24
24
|
Block,
|
|
25
25
|
ClassDeclaration,
|
|
26
26
|
Identifier,
|
|
27
|
-
If,
|
|
28
27
|
MethodDeclaration,
|
|
29
28
|
Statement,
|
|
30
29
|
)
|
|
30
|
+
from openrewrite_code_quality_python.codequality._trivia import (
|
|
31
|
+
opens_with_trailing_comment,
|
|
32
|
+
space_after_block,
|
|
33
|
+
split_line,
|
|
34
|
+
splice_out,
|
|
35
|
+
)
|
|
31
36
|
|
|
32
37
|
_CodeQuality = [
|
|
33
38
|
*Python,
|
|
@@ -69,95 +74,7 @@ def _in_function_scope(cursor: Cursor) -> bool:
|
|
|
69
74
|
return False
|
|
70
75
|
|
|
71
76
|
|
|
72
|
-
def
|
|
73
|
-
"""The trivia the printer emits after the block at ``cursor``.
|
|
74
|
-
|
|
75
|
-
A statement's trailing comment is stored on whatever follows it, so a
|
|
76
|
-
block's last statement keeps its comment outside the block -- on the
|
|
77
|
-
next statement of an enclosing block, on the `else` of an enclosing
|
|
78
|
-
`if`, or at end of file. Returns None for the containers this does not
|
|
79
|
-
know how to look past.
|
|
80
|
-
"""
|
|
81
|
-
child = cursor.value
|
|
82
|
-
for parent in islice(cursor.get_path(), 1, None):
|
|
83
|
-
if isinstance(parent, (Block, CompilationUnit)):
|
|
84
|
-
statements = parent._statements
|
|
85
|
-
index = next(
|
|
86
|
-
(i for i, s in enumerate(statements) if s.element.id == child.id),
|
|
87
|
-
None,
|
|
88
|
-
)
|
|
89
|
-
if index is None:
|
|
90
|
-
return None
|
|
91
|
-
if index + 1 < len(statements):
|
|
92
|
-
return statements[index + 1].element.prefix
|
|
93
|
-
if isinstance(parent, CompilationUnit):
|
|
94
|
-
return parent.eof
|
|
95
|
-
elif isinstance(parent, If):
|
|
96
|
-
if parent.else_part is not None and parent.else_part.id != child.id:
|
|
97
|
-
return parent.else_part.prefix
|
|
98
|
-
elif not isinstance(parent, (ClassDeclaration, If.Else, MethodDeclaration)):
|
|
99
|
-
return None
|
|
100
|
-
child = parent
|
|
101
|
-
return None
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def _ends_line_with_comment(space: Space) -> bool:
|
|
105
|
-
"""Return True when ``space`` opens with a comment on the line it continues."""
|
|
106
|
-
split = _split_line(space)
|
|
107
|
-
return bool((split[0] if split else space).comments)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def _split_line(space: Space) -> Optional[Tuple[Space, Space]]:
|
|
111
|
-
"""Split ``space`` at its first newline, or return None if it has no newline.
|
|
112
|
-
|
|
113
|
-
The head is the trivia that still belongs to the preceding line -- most
|
|
114
|
-
importantly that line's trailing comment. The tail starts with the
|
|
115
|
-
newline itself.
|
|
116
|
-
"""
|
|
117
|
-
newline = space.whitespace.find("\n")
|
|
118
|
-
if newline != -1:
|
|
119
|
-
return (
|
|
120
|
-
Space(_comments=[], _whitespace=space.whitespace[:newline]),
|
|
121
|
-
Space(_comments=space.comments, _whitespace=space.whitespace[newline:]),
|
|
122
|
-
)
|
|
123
|
-
for i, comment in enumerate(space.comments):
|
|
124
|
-
newline = comment.suffix.find("\n")
|
|
125
|
-
if newline != -1:
|
|
126
|
-
return (
|
|
127
|
-
Space(
|
|
128
|
-
_comments=[
|
|
129
|
-
*space.comments[:i],
|
|
130
|
-
comment.replace(_suffix=comment.suffix[:newline]),
|
|
131
|
-
],
|
|
132
|
-
_whitespace=space.whitespace,
|
|
133
|
-
),
|
|
134
|
-
Space(
|
|
135
|
-
_comments=space.comments[i + 1:],
|
|
136
|
-
_whitespace=comment.suffix[newline:],
|
|
137
|
-
),
|
|
138
|
-
)
|
|
139
|
-
return None
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def _concat(left: Space, right: Space) -> Space:
|
|
143
|
-
"""Return the trivia that prints as ``left`` followed by ``right``."""
|
|
144
|
-
if not left.comments:
|
|
145
|
-
return Space(
|
|
146
|
-
_comments=right.comments,
|
|
147
|
-
_whitespace=left.whitespace + right.whitespace,
|
|
148
|
-
)
|
|
149
|
-
last = left.comments[-1]
|
|
150
|
-
return Space(
|
|
151
|
-
_comments=[
|
|
152
|
-
*left.comments[:-1],
|
|
153
|
-
last.replace(_suffix=last.suffix + right.whitespace),
|
|
154
|
-
*right.comments,
|
|
155
|
-
],
|
|
156
|
-
_whitespace=left.whitespace,
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
def _removable(padded_statements: list, i: int, space_after_block: Optional[Space]) -> bool:
|
|
77
|
+
def _removable(padded_statements: list, i: int, after_block: Optional[Space]) -> bool:
|
|
161
78
|
"""Return True when statement ``i`` can be dropped without disturbing its neighbours.
|
|
162
79
|
|
|
163
80
|
The removal is expressed by splicing line-level trivia, so the statement
|
|
@@ -168,49 +85,29 @@ def _removable(padded_statements: list, i: int, space_after_block: Optional[Spac
|
|
|
168
85
|
"""
|
|
169
86
|
if not _is_local_self_assignment(padded_statements[i].element):
|
|
170
87
|
return False
|
|
171
|
-
if
|
|
88
|
+
if split_line(padded_statements[i].element.prefix) is None:
|
|
172
89
|
return False
|
|
173
90
|
if i + 1 < len(padded_statements):
|
|
174
|
-
return
|
|
175
|
-
return
|
|
91
|
+
return split_line(padded_statements[i + 1].element.prefix) is not None
|
|
92
|
+
return after_block is not None and not opens_with_trailing_comment(after_block)
|
|
176
93
|
|
|
177
94
|
|
|
178
95
|
def _remove_self_assignments(
|
|
179
|
-
padded_statements: list, end: Space,
|
|
96
|
+
padded_statements: list, end: Space, after_block: Optional[Space]
|
|
180
97
|
) -> Optional[Tuple[list, Space]]:
|
|
181
98
|
"""Return the block's statements and end trivia with self-assignments removed, or None if unchanged.
|
|
182
99
|
|
|
183
|
-
Each removed statement's leading trivia is kept up to its own line (it
|
|
184
|
-
holds the preceding statement's trailing comment) and its successor's is
|
|
185
|
-
dropped up to the same point (it holds the removed statement's own).
|
|
186
100
|
Emptying a block would leave its header without a body, so the last
|
|
187
101
|
surviving statement stays.
|
|
188
102
|
"""
|
|
189
103
|
to_remove = {
|
|
190
104
|
i
|
|
191
105
|
for i in range(len(padded_statements))
|
|
192
|
-
if _removable(padded_statements, i,
|
|
106
|
+
if _removable(padded_statements, i, after_block)
|
|
193
107
|
}
|
|
194
108
|
if not to_remove or len(to_remove) == len(padded_statements):
|
|
195
109
|
return None
|
|
196
|
-
|
|
197
|
-
result = []
|
|
198
|
-
pending_head: Optional[Space] = None
|
|
199
|
-
for i, padded in enumerate(padded_statements):
|
|
200
|
-
if i in to_remove:
|
|
201
|
-
if pending_head is None:
|
|
202
|
-
pending_head, _ = _split_line(padded.element.prefix)
|
|
203
|
-
continue
|
|
204
|
-
if pending_head is not None:
|
|
205
|
-
_, tail = _split_line(padded.element.prefix)
|
|
206
|
-
padded = padded.replace(
|
|
207
|
-
_element=padded.element.replace(
|
|
208
|
-
_prefix=_concat(pending_head, tail)
|
|
209
|
-
)
|
|
210
|
-
)
|
|
211
|
-
pending_head = None
|
|
212
|
-
result.append(padded)
|
|
213
|
-
return result, end if pending_head is None else _concat(pending_head, end)
|
|
110
|
+
return splice_out(padded_statements, end, to_remove)
|
|
214
111
|
|
|
215
112
|
|
|
216
113
|
@categorize(_CodeQuality)
|
|
@@ -266,7 +163,7 @@ class RemoveSelfAssignment(Recipe):
|
|
|
266
163
|
if not isinstance(block, Block) or not _in_function_scope(self.cursor):
|
|
267
164
|
return block
|
|
268
165
|
removed = _remove_self_assignments(
|
|
269
|
-
block._statements, block.end,
|
|
166
|
+
block._statements, block.end, space_after_block(self.cursor)
|
|
270
167
|
)
|
|
271
168
|
if removed is not None:
|
|
272
169
|
return block.replace(_statements=removed[0], _end=removed[1])
|
|
@@ -9,25 +9,26 @@ eliminates noise and prevents confusion about the author's intent.
|
|
|
9
9
|
See: https://rules.sonarsource.com/python/RSPEC-S4143/
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
from typing import Any, List, Optional
|
|
12
|
+
from typing import Any, List, Optional, Tuple
|
|
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.python.visitor import PythonVisitor
|
|
19
|
-
from rewrite.python.tree import ExpressionStatement
|
|
20
|
-
from rewrite.java.support_types import Space
|
|
19
|
+
from rewrite.python.tree import Await, CompilationUnit, ExpressionStatement, YieldFrom
|
|
20
|
+
from rewrite.java.support_types import MethodCall, Space
|
|
21
21
|
from rewrite.java.tree import (
|
|
22
22
|
ArrayAccess,
|
|
23
23
|
Assignment,
|
|
24
24
|
Block,
|
|
25
25
|
Expression,
|
|
26
26
|
FieldAccess,
|
|
27
|
-
Identifier,
|
|
28
27
|
Statement,
|
|
28
|
+
Yield,
|
|
29
29
|
)
|
|
30
30
|
from openrewrite_code_quality_python.codequality._semantically_equal import SemanticallyEqual
|
|
31
|
+
from openrewrite_code_quality_python.codequality._trivia import splice_out, split_line
|
|
31
32
|
|
|
32
33
|
_CodeQuality = [
|
|
33
34
|
*Python,
|
|
@@ -81,6 +82,56 @@ def _same_attribute_target(a: Assignment, b: Assignment) -> bool:
|
|
|
81
82
|
)
|
|
82
83
|
|
|
83
84
|
|
|
85
|
+
class _Find(PythonVisitor[None]):
|
|
86
|
+
"""Records whether any subtree of the visited node satisfies ``predicate``."""
|
|
87
|
+
|
|
88
|
+
def __init__(self, predicate):
|
|
89
|
+
super().__init__()
|
|
90
|
+
self._predicate = predicate
|
|
91
|
+
self.found = False
|
|
92
|
+
|
|
93
|
+
def visit(self, tree, p, parent=None):
|
|
94
|
+
if tree is not None and self._predicate(tree):
|
|
95
|
+
self.found = True
|
|
96
|
+
return super().visit(tree, p, parent)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _contains(node, predicate) -> bool:
|
|
100
|
+
search = _Find(predicate)
|
|
101
|
+
search.visit(node, None)
|
|
102
|
+
return search.found
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# A walrus binds a name; the rest reach code this recipe cannot see.
|
|
106
|
+
_EFFECTFUL = (Assignment, Await, MethodCall, Yield, YieldFrom)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _has_side_effects(expression: Expression) -> bool:
|
|
110
|
+
"""Return True when evaluating ``expression`` can do more than produce a value."""
|
|
111
|
+
return _contains(expression, lambda node: isinstance(node, _EFFECTFUL))
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _receiver(assign: Assignment) -> Expression:
|
|
115
|
+
"""The object the assignment writes through."""
|
|
116
|
+
variable = assign.variable
|
|
117
|
+
return variable.indexed if isinstance(variable, ArrayAccess) else variable.target
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _observes_earlier_write(a: Assignment, b: Assignment) -> bool:
|
|
121
|
+
"""Return True when evaluating ``b``'s value can see what ``a`` wrote.
|
|
122
|
+
|
|
123
|
+
Reading the target back (``d[k] = d[k].strip()``) is the direct case, but
|
|
124
|
+
the write is equally visible through the object that holds it: whether
|
|
125
|
+
``d[k] = 0`` added a key or replaced one decides ``len(d)``, and an
|
|
126
|
+
attribute read can run a property computed from the one just written.
|
|
127
|
+
"""
|
|
128
|
+
receiver = _receiver(a)
|
|
129
|
+
return _contains(
|
|
130
|
+
b.assignment,
|
|
131
|
+
lambda node: isinstance(node, Expression) and SemanticallyEqual.are_equal(node, receiver),
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
84
135
|
def _is_duplicate_write(a: Assignment, b: Assignment) -> bool:
|
|
85
136
|
"""Return True if a and b write to the same target."""
|
|
86
137
|
if _is_subscript_assign(a) and _is_subscript_assign(b):
|
|
@@ -90,6 +141,21 @@ def _is_duplicate_write(a: Assignment, b: Assignment) -> bool:
|
|
|
90
141
|
return False
|
|
91
142
|
|
|
92
143
|
|
|
144
|
+
def _is_dead_write(a: Assignment, b: Assignment) -> bool:
|
|
145
|
+
"""Return True when ``b`` leaves nothing ``a`` did observable.
|
|
146
|
+
|
|
147
|
+
Targets are matched as written, so one that names a different location
|
|
148
|
+
each time it is evaluated (``d[next(it)]``) is two targets, not one. A
|
|
149
|
+
value that does more than produce a value keeps its statement alive even
|
|
150
|
+
once the store it makes is dead.
|
|
151
|
+
"""
|
|
152
|
+
if not _is_duplicate_write(a, b):
|
|
153
|
+
return False
|
|
154
|
+
if _has_side_effects(a.variable) or _has_side_effects(a.assignment):
|
|
155
|
+
return False
|
|
156
|
+
return not _observes_earlier_write(a, b)
|
|
157
|
+
|
|
158
|
+
|
|
93
159
|
@categorize(_CodeQuality)
|
|
94
160
|
class RemoveUnconditionalValueOverwrite(Recipe):
|
|
95
161
|
"""
|
|
@@ -123,7 +189,9 @@ class RemoveUnconditionalValueOverwrite(Recipe):
|
|
|
123
189
|
return (
|
|
124
190
|
"Remove consecutive assignments that write to the same dict key "
|
|
125
191
|
"or object attribute, since the first value is immediately "
|
|
126
|
-
"overwritten and never used."
|
|
192
|
+
"overwritten and never used. A write the second one can still "
|
|
193
|
+
"observe -- by reading the target back, or through the object "
|
|
194
|
+
"holding it -- is left alone."
|
|
127
195
|
)
|
|
128
196
|
|
|
129
197
|
@property
|
|
@@ -136,72 +204,55 @@ class RemoveUnconditionalValueOverwrite(Recipe):
|
|
|
136
204
|
|
|
137
205
|
def editor(self) -> TreeVisitor[Any, ExecutionContext]:
|
|
138
206
|
class Visitor(PythonVisitor[ExecutionContext]):
|
|
139
|
-
def visit_compilation_unit(
|
|
207
|
+
def visit_compilation_unit(
|
|
208
|
+
self, cu: CompilationUnit, p: ExecutionContext
|
|
209
|
+
) -> CompilationUnit:
|
|
140
210
|
cu = super().visit_compilation_unit(cu, p)
|
|
141
|
-
|
|
142
|
-
if
|
|
143
|
-
return cu.replace(_statements=
|
|
211
|
+
removed = _remove_overwrites(cu._statements, cu.eof, starts_file=True)
|
|
212
|
+
if removed is not None:
|
|
213
|
+
return cu.replace(_statements=removed[0], _eof=removed[1])
|
|
144
214
|
return cu
|
|
145
215
|
|
|
146
216
|
def visit_block(
|
|
147
217
|
self, block: Block, p: ExecutionContext
|
|
148
218
|
) -> Optional[Block]:
|
|
149
219
|
block = super().visit_block(block, p)
|
|
150
|
-
|
|
151
220
|
if not isinstance(block, Block):
|
|
152
221
|
return block
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
return block.replace(_statements=new_stmts)
|
|
222
|
+
removed = _remove_overwrites(block._statements, block.end, starts_file=False)
|
|
223
|
+
if removed is not None:
|
|
224
|
+
return block.replace(_statements=removed[0], _end=removed[1])
|
|
157
225
|
return block
|
|
158
226
|
|
|
159
227
|
return Visitor()
|
|
160
228
|
|
|
161
229
|
|
|
162
|
-
def
|
|
163
|
-
"""Return
|
|
164
|
-
if len(padded_stmts) < 2:
|
|
165
|
-
return None
|
|
166
|
-
|
|
167
|
-
stmts = [rp.element for rp in padded_stmts]
|
|
168
|
-
to_remove: set[int] = set()
|
|
169
|
-
for i in range(len(stmts) - 1):
|
|
170
|
-
if i in to_remove:
|
|
171
|
-
continue
|
|
172
|
-
a_assign = _get_assignment(stmts[i])
|
|
173
|
-
if a_assign is None:
|
|
174
|
-
continue
|
|
175
|
-
if not (_is_subscript_assign(a_assign) or _is_attribute_assign(a_assign)):
|
|
176
|
-
continue
|
|
230
|
+
def _removable(padded_statements: list, i: int, starts_file: bool) -> bool:
|
|
231
|
+
"""Return True when the write at ``i`` is dead and can be dropped by splicing lines.
|
|
177
232
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
233
|
+
Both the write and its overwrite have to start a line of their own,
|
|
234
|
+
which a `;`-joined pair does not; a file's first statement starts one
|
|
235
|
+
with no line break to show for it.
|
|
236
|
+
"""
|
|
237
|
+
if i + 1 >= len(padded_statements):
|
|
238
|
+
return False
|
|
239
|
+
a = _get_assignment(padded_statements[i].element)
|
|
240
|
+
b = _get_assignment(padded_statements[i + 1].element)
|
|
241
|
+
if a is None or b is None or not _is_dead_write(a, b):
|
|
242
|
+
return False
|
|
243
|
+
if not (starts_file and i == 0) and split_line(padded_statements[i].element.prefix) is None:
|
|
244
|
+
return False
|
|
245
|
+
return split_line(padded_statements[i + 1].element.prefix) is not None
|
|
181
246
|
|
|
182
|
-
if _is_duplicate_write(a_assign, b_assign):
|
|
183
|
-
to_remove.add(i)
|
|
184
247
|
|
|
248
|
+
def _remove_overwrites(
|
|
249
|
+
padded_statements: list, end: Space, starts_file: bool
|
|
250
|
+
) -> Optional[Tuple[list, Space]]:
|
|
251
|
+
"""Return the statements and end trivia with dead writes removed, or None if unchanged."""
|
|
252
|
+
to_remove = {
|
|
253
|
+
i for i in range(len(padded_statements))
|
|
254
|
+
if _removable(padded_statements, i, starts_file)
|
|
255
|
+
}
|
|
185
256
|
if not to_remove:
|
|
186
257
|
return None
|
|
187
|
-
|
|
188
|
-
result = [
|
|
189
|
-
rp for idx, rp in enumerate(padded_stmts)
|
|
190
|
-
if idx not in to_remove
|
|
191
|
-
]
|
|
192
|
-
|
|
193
|
-
# If we removed the first statement(s), strip the leading newline
|
|
194
|
-
# from the new first statement's prefix to avoid a blank line.
|
|
195
|
-
if result and 0 in to_remove:
|
|
196
|
-
first_rp = result[0]
|
|
197
|
-
first = first_rp.element
|
|
198
|
-
ws = first.prefix.whitespace
|
|
199
|
-
if ws.startswith("\n"):
|
|
200
|
-
ws = ws[1:]
|
|
201
|
-
new_first = first.replace(_prefix=Space(
|
|
202
|
-
_comments=first.prefix.comments,
|
|
203
|
-
_whitespace=ws,
|
|
204
|
-
))
|
|
205
|
-
result[0] = first_rp.replace(_element=new_first)
|
|
206
|
-
|
|
207
|
-
return result
|
|
258
|
+
return splice_out(padded_statements, end, to_remove)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openrewrite-migrate-python
|
|
3
|
-
Version: 0.14.0.
|
|
3
|
+
Version: 0.14.0.dev20260902134304
|
|
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
|
|
@@ -3,6 +3,7 @@ src/openrewrite_code_quality_python/__init__.py
|
|
|
3
3
|
src/openrewrite_code_quality_python/codequality/__init__.py
|
|
4
4
|
src/openrewrite_code_quality_python/codequality/_reindent.py
|
|
5
5
|
src/openrewrite_code_quality_python/codequality/_semantically_equal.py
|
|
6
|
+
src/openrewrite_code_quality_python/codequality/_trivia.py
|
|
6
7
|
src/openrewrite_code_quality_python/codequality/all_branches_identical.py
|
|
7
8
|
src/openrewrite_code_quality_python/codequality/boolean_checks_not_inverted.py
|
|
8
9
|
src/openrewrite_code_quality_python/codequality/collapsible_if_statements.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|