openrewrite-migrate-python 0.16.0.dev20261004180356__tar.gz → 0.16.0.dev20261005072603__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.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/PKG-INFO +1 -1
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/pyproject.toml +1 -1
- openrewrite_migrate_python-0.16.0.dev20261005072603/src/openrewrite_code_quality_python/codequality/remove_unconditional_value_overwrite.py +333 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/string_formatting.py +4 -4
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python.egg-info/PKG-INFO +1 -1
- openrewrite_migrate_python-0.16.0.dev20261004180356/src/openrewrite_code_quality_python/codequality/remove_unconditional_value_overwrite.py +0 -258
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/setup.cfg +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_code_quality_python/__init__.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_code_quality_python/codequality/__init__.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_code_quality_python/codequality/_reindent.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_code_quality_python/codequality/_semantically_equal.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_code_quality_python/codequality/_trivia.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_code_quality_python/codequality/all_branches_identical.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_code_quality_python/codequality/boolean_checks_not_inverted.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_code_quality_python/codequality/collapsible_if_statements.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_code_quality_python/codequality/find_suspicious_chained_comparison.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_code_quality_python/codequality/merge_identical_branches.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_code_quality_python/codequality/remove_duplicate_conditions.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_code_quality_python/codequality/remove_self_assignment.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_code_quality_python/codequality/simplify_boolean_literal.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_code_quality_python/codequality/simplify_redundant_logical_expression.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/__init__.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/__init__.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/_from_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/_import_hygiene.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/_langchain_tables.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/_markers.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/_slots.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/_typing_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/aifc_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/array_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/ast_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/asyncio_coroutine_to_async.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/asyncio_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/avro_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/calendar_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/cgi_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/cgi_parse_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/collections_abc_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/configparser_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/datetime_utc.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/distutils_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/distutils_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/functools_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/future_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/gettext_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/html_parser_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/imp_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/langchain_classic_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/langchain_community_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/langchain_provider_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/locale_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/locale_getdefaultlocale_deprecation.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/macpath_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/mailcap_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/nntplib_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/os_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/pathlib_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/pep594_system_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/pipes_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/pkgutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/platform_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/pydantic_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/pydantic_fields.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/pydantic_model_fields.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/re_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/removed_modules_312.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/shutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/socket_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/ssl_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/sys_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/sys_last_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/tarfile_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/telnetlib_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/tempfile_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/threading_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/threading_is_alive_deprecation.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/typing_callable.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/typing_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/typing_union_to_pipe.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/unittest_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain02.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain1.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/upgrade_to_pydantic.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/upgrade_to_python310.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/upgrade_to_python311.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/upgrade_to_python312.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/upgrade_to_python313.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/upgrade_to_python314.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/upgrade_to_python38.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/upgrade_to_python39.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/urllib_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/uu_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/xdrlib_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python/migrate/xml_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python.egg-info/SOURCES.txt +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python.egg-info/dependency_links.txt +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python.egg-info/entry_points.txt +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python.egg-info/requires.txt +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/src/openrewrite_migrate_python.egg-info/top_level.txt +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_aifc_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_array_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_ast_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_asyncio_coroutine_to_async.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_asyncio_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_avro_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_calendar_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_cgi_parse_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_change_import_recipes.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_collections_abc_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_configparser_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_datetime_utc.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_distutils_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_distutils_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_functools_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_future_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_gettext_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_html_parser_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_imp_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_langchain_classic_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_langchain_community_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_langchain_provider_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_locale_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_locale_getdefaultlocale_deprecation.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_macpath_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_mailcap_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_markers.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_os_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_pathlib_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_pep594_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_pep594_system_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_pkgutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_platform_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_pydantic_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_pydantic_fields.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_pydantic_model_fields.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_re_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_removed_modules_312.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_shutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_socket_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_ssl_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_string_formatting.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_sys_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_sys_last_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_tarfile_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_tempfile_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_threading_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_threading_extended.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_threading_is_alive_deprecation.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_typing_alias_moves.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_typing_callable.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_typing_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_typing_union_to_pipe.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_unittest_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_upgrade_recipes.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_upgrade_to_pydantic.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_urllib_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/tests/test_xdrlib_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261004180356 → openrewrite_migrate_python-0.16.0.dev20261005072603}/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.16.0.
|
|
3
|
+
Version: 0.16.0.dev20261005072603
|
|
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.16.0.
|
|
8
|
+
version = "0.16.0.dev20261005072603" # 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,333 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Recipe to remove dict entry writes that the next statement overwrites.
|
|
3
|
+
|
|
4
|
+
See: https://rules.sonarsource.com/python/RSPEC-S4143/
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from itertools import islice
|
|
8
|
+
from typing import Any, List, Optional, Tuple
|
|
9
|
+
|
|
10
|
+
from rewrite import Cursor, ExecutionContext, Recipe, TreeVisitor
|
|
11
|
+
from rewrite.category import CategoryDescriptor
|
|
12
|
+
from rewrite.decorators import categorize
|
|
13
|
+
from rewrite.marketplace import Python
|
|
14
|
+
from rewrite.python.method_matcher import MethodMatcher
|
|
15
|
+
from rewrite.python.visitor import PythonVisitor
|
|
16
|
+
from rewrite.python.tree import (
|
|
17
|
+
CollectionLiteral,
|
|
18
|
+
CompilationUnit,
|
|
19
|
+
ComprehensionExpression,
|
|
20
|
+
DictLiteral,
|
|
21
|
+
ExpressionStatement,
|
|
22
|
+
KeyValue,
|
|
23
|
+
NamedArgument,
|
|
24
|
+
TypeHintedExpression,
|
|
25
|
+
VariableScope,
|
|
26
|
+
)
|
|
27
|
+
from rewrite.java.support_types import Space
|
|
28
|
+
from rewrite.java.tree import (
|
|
29
|
+
ArrayAccess,
|
|
30
|
+
Assignment,
|
|
31
|
+
Block,
|
|
32
|
+
ClassDeclaration,
|
|
33
|
+
Empty,
|
|
34
|
+
Expression,
|
|
35
|
+
Identifier,
|
|
36
|
+
J,
|
|
37
|
+
Lambda,
|
|
38
|
+
Literal,
|
|
39
|
+
MethodDeclaration,
|
|
40
|
+
MethodInvocation,
|
|
41
|
+
Parentheses,
|
|
42
|
+
Statement,
|
|
43
|
+
Try,
|
|
44
|
+
Unary,
|
|
45
|
+
)
|
|
46
|
+
from openrewrite_code_quality_python.codequality._semantically_equal import SemanticallyEqual
|
|
47
|
+
from openrewrite_code_quality_python.codequality._trivia import splice_out, split_line
|
|
48
|
+
|
|
49
|
+
_CodeQuality = [
|
|
50
|
+
*Python,
|
|
51
|
+
CategoryDescriptor(display_name="Code quality"),
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _get_assignment(stmt: Statement) -> Optional[Assignment]:
|
|
56
|
+
"""Extract an Assignment from a statement, if present."""
|
|
57
|
+
if isinstance(stmt, ExpressionStatement):
|
|
58
|
+
expr = stmt.expression
|
|
59
|
+
if isinstance(expr, Assignment):
|
|
60
|
+
return expr
|
|
61
|
+
if isinstance(stmt, Assignment):
|
|
62
|
+
return stmt
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class _Find(PythonVisitor[None]):
|
|
67
|
+
"""Records whether any subtree of the visited node satisfies ``predicate``."""
|
|
68
|
+
|
|
69
|
+
def __init__(self, predicate):
|
|
70
|
+
super().__init__()
|
|
71
|
+
self._predicate = predicate
|
|
72
|
+
self.found = False
|
|
73
|
+
|
|
74
|
+
def visit(self, tree, p, parent=None):
|
|
75
|
+
if tree is not None and self._predicate(tree):
|
|
76
|
+
self.found = True
|
|
77
|
+
return super().visit(tree, p, parent)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _contains(node, predicate) -> bool:
|
|
81
|
+
search = _Find(predicate)
|
|
82
|
+
search.visit(node, None)
|
|
83
|
+
return search.found
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _mentions(node, name: str) -> bool:
|
|
87
|
+
return _contains(node, lambda n: isinstance(n, Identifier) and n.simple_name == name)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _is_number(expression: J) -> bool:
|
|
91
|
+
return (
|
|
92
|
+
isinstance(expression, Unary)
|
|
93
|
+
and expression.operator in (Unary.Type.Negative, Unary.Type.Positive)
|
|
94
|
+
and isinstance(expression.expression, Literal)
|
|
95
|
+
and isinstance(expression.expression.value, (int, float, complex))
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _is_hashable_constant(expression: J) -> bool:
|
|
100
|
+
"""A value whose hashing and comparison run no user code and cannot raise."""
|
|
101
|
+
if isinstance(expression, Parentheses) and isinstance(expression.tree, Expression):
|
|
102
|
+
return _is_hashable_constant(expression.tree)
|
|
103
|
+
if isinstance(expression, Literal) or _is_number(expression):
|
|
104
|
+
return True
|
|
105
|
+
return (
|
|
106
|
+
isinstance(expression, CollectionLiteral)
|
|
107
|
+
and expression.kind == CollectionLiteral.Kind.TUPLE
|
|
108
|
+
and all(_is_hashable_constant(e) for e in expression.elements if not isinstance(e, Empty))
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _is_constant(expression: J) -> bool:
|
|
113
|
+
"""A value whose evaluation runs no user code and cannot raise."""
|
|
114
|
+
if isinstance(expression, Parentheses) and isinstance(expression.tree, Expression):
|
|
115
|
+
return _is_constant(expression.tree)
|
|
116
|
+
if isinstance(expression, CollectionLiteral):
|
|
117
|
+
element_ok = _is_hashable_constant if expression.kind == CollectionLiteral.Kind.SET else _is_constant
|
|
118
|
+
return all(element_ok(e) for e in expression.elements if not isinstance(e, Empty))
|
|
119
|
+
if isinstance(expression, DictLiteral):
|
|
120
|
+
return all(
|
|
121
|
+
isinstance(e, KeyValue) and _is_hashable_constant(e.key) and _is_constant(e.value)
|
|
122
|
+
for e in expression.elements
|
|
123
|
+
if not isinstance(e, Empty)
|
|
124
|
+
)
|
|
125
|
+
return _is_hashable_constant(expression)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _store(stmt: Statement) -> Optional[Tuple[str, Expression, Expression]]:
|
|
129
|
+
"""The dict name, key and value of a ``d[key] = value`` statement with a constant key."""
|
|
130
|
+
assign = _get_assignment(stmt)
|
|
131
|
+
if assign is None or not isinstance(assign.variable, ArrayAccess):
|
|
132
|
+
return None
|
|
133
|
+
receiver = assign.variable.indexed
|
|
134
|
+
key = assign.variable.dimension.index
|
|
135
|
+
if not isinstance(receiver, Identifier) or not _is_hashable_constant(key):
|
|
136
|
+
return None
|
|
137
|
+
return receiver.simple_name, key, assign.assignment
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
_DICT_CONSTRUCTOR = MethodMatcher.create("dict <constructor>(..)")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _builds_dict(value: Expression) -> bool:
|
|
144
|
+
"""True when ``value`` is a new builtin dict holding only constant keys."""
|
|
145
|
+
if isinstance(value, DictLiteral):
|
|
146
|
+
return all(
|
|
147
|
+
isinstance(e, KeyValue) and _is_hashable_constant(e.key)
|
|
148
|
+
for e in value.elements
|
|
149
|
+
if not isinstance(e, Empty)
|
|
150
|
+
)
|
|
151
|
+
return (
|
|
152
|
+
isinstance(value, MethodInvocation)
|
|
153
|
+
and _DICT_CONSTRUCTOR.matches(value)
|
|
154
|
+
and all(isinstance(a, (NamedArgument, Empty)) for a in value.arguments)
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _binds_fresh_dict(stmt: Statement, name: str) -> bool:
|
|
159
|
+
assign = _get_assignment(stmt)
|
|
160
|
+
if assign is None:
|
|
161
|
+
return False
|
|
162
|
+
variable = assign.variable
|
|
163
|
+
if isinstance(variable, TypeHintedExpression):
|
|
164
|
+
variable = variable.expression
|
|
165
|
+
return (
|
|
166
|
+
isinstance(variable, Identifier)
|
|
167
|
+
and variable.simple_name == name
|
|
168
|
+
and _builds_dict(assign.assignment)
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _is_fresh_at(statements: List[Statement], i: int, name: str) -> bool:
|
|
173
|
+
"""True when ``name`` holds a dict this block built and has kept to itself up to ``i``.
|
|
174
|
+
|
|
175
|
+
Rebinding the dict on every pass through the block keeps it fresh in a
|
|
176
|
+
loop too. Only constant-key stores may touch it in between, so every key
|
|
177
|
+
it holds hashes and compares without running user code.
|
|
178
|
+
"""
|
|
179
|
+
for stmt in reversed(statements[:i]):
|
|
180
|
+
if _binds_fresh_dict(stmt, name):
|
|
181
|
+
return True
|
|
182
|
+
store = _store(stmt)
|
|
183
|
+
if store is not None and store[0] == name and not _mentions(store[2], name):
|
|
184
|
+
continue
|
|
185
|
+
if _mentions(stmt, name):
|
|
186
|
+
return False
|
|
187
|
+
return False
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
_NESTED_SCOPES = (ClassDeclaration, ComprehensionExpression, Lambda, MethodDeclaration)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _shared_beyond_frame(function: MethodDeclaration, name: str) -> bool:
|
|
194
|
+
"""True when ``name`` in ``function`` is a global, or a cell a nested scope can read."""
|
|
195
|
+
return _contains(
|
|
196
|
+
function.body,
|
|
197
|
+
lambda n: (
|
|
198
|
+
isinstance(n, VariableScope) and any(i.simple_name == name for i in n.names)
|
|
199
|
+
) or (isinstance(n, _NESTED_SCOPES) and _mentions(n, name)),
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _enclosing_function(cursor: Cursor) -> Optional[MethodDeclaration]:
|
|
204
|
+
"""The function whose frame alone can observe a block's locals, if there is one.
|
|
205
|
+
|
|
206
|
+
A ``try`` or ``with`` between them can catch an exception and read the
|
|
207
|
+
dict afterwards, so a block inside one has no such function.
|
|
208
|
+
"""
|
|
209
|
+
for value in islice(cursor.get_path(), 1, None):
|
|
210
|
+
if isinstance(value, MethodDeclaration):
|
|
211
|
+
return value
|
|
212
|
+
if isinstance(value, (Try, ClassDeclaration, CompilationUnit)):
|
|
213
|
+
return None
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _is_dead_write(statements: List[Statement], i: int, function: MethodDeclaration) -> bool:
|
|
218
|
+
"""True when the write at ``i`` is overwritten by the next one before anything can see it.
|
|
219
|
+
|
|
220
|
+
The dict has to be one only this frame can reach, which rules out other
|
|
221
|
+
threads, aliases reached through a call, and setter hooks. The dropped
|
|
222
|
+
value has to be a constant, so dropping it drops no code.
|
|
223
|
+
"""
|
|
224
|
+
a = _store(statements[i])
|
|
225
|
+
b = _store(statements[i + 1])
|
|
226
|
+
if a is None or b is None:
|
|
227
|
+
return False
|
|
228
|
+
name, key, value = a
|
|
229
|
+
return (
|
|
230
|
+
b[0] == name
|
|
231
|
+
and SemanticallyEqual.are_equal(key, b[1])
|
|
232
|
+
and _is_constant(value)
|
|
233
|
+
and not _mentions(b[2], name)
|
|
234
|
+
and _is_fresh_at(statements, i, name)
|
|
235
|
+
and not _shared_beyond_frame(function, name)
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@categorize(_CodeQuality)
|
|
240
|
+
class RemoveUnconditionalValueOverwrite(Recipe):
|
|
241
|
+
"""
|
|
242
|
+
Remove a dict entry write that the next statement overwrites.
|
|
243
|
+
|
|
244
|
+
Only a dict the function has just built and kept to itself qualifies.
|
|
245
|
+
|
|
246
|
+
Example:
|
|
247
|
+
Before:
|
|
248
|
+
def f():
|
|
249
|
+
d = {}
|
|
250
|
+
d["key"] = 1
|
|
251
|
+
d["key"] = 2
|
|
252
|
+
return d
|
|
253
|
+
|
|
254
|
+
After:
|
|
255
|
+
def f():
|
|
256
|
+
d = {}
|
|
257
|
+
d["key"] = 2
|
|
258
|
+
return d
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
@property
|
|
262
|
+
def name(self) -> str:
|
|
263
|
+
return "org.openrewrite.python.codequality.RemoveUnconditionalValueOverwrite"
|
|
264
|
+
|
|
265
|
+
@property
|
|
266
|
+
def display_name(self) -> str:
|
|
267
|
+
return "Remove unconditional value overwrites"
|
|
268
|
+
|
|
269
|
+
@property
|
|
270
|
+
def description(self) -> str:
|
|
271
|
+
return (
|
|
272
|
+
"Remove a constant written to a dict key when the next statement "
|
|
273
|
+
"overwrites the same key. Only a dict the enclosing function built "
|
|
274
|
+
"with `{...}` or `dict()` and has not yet passed on qualifies, and "
|
|
275
|
+
"only for constant keys outside `try` and `with` blocks. Writes to "
|
|
276
|
+
"attributes, parameters and globals are left alone, because "
|
|
277
|
+
"another thread, an alias or a setter can observe them."
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
@property
|
|
281
|
+
def tags(self) -> List[str]:
|
|
282
|
+
return ["python", "code-quality", "RSPEC-S4143"]
|
|
283
|
+
|
|
284
|
+
@property
|
|
285
|
+
def estimated_effort_per_occurrence(self) -> int:
|
|
286
|
+
return 5
|
|
287
|
+
|
|
288
|
+
def editor(self) -> TreeVisitor[Any, ExecutionContext]:
|
|
289
|
+
class Visitor(PythonVisitor[ExecutionContext]):
|
|
290
|
+
def visit_block(
|
|
291
|
+
self, block: Block, p: ExecutionContext
|
|
292
|
+
) -> Optional[Block]:
|
|
293
|
+
block = super().visit_block(block, p)
|
|
294
|
+
if not isinstance(block, Block):
|
|
295
|
+
return block
|
|
296
|
+
function = _enclosing_function(self.cursor)
|
|
297
|
+
if function is None:
|
|
298
|
+
return block
|
|
299
|
+
removed = _remove_overwrites(block._statements, block.end, function)
|
|
300
|
+
if removed is not None:
|
|
301
|
+
return block.replace(_statements=removed[0], _end=removed[1])
|
|
302
|
+
return block
|
|
303
|
+
|
|
304
|
+
return Visitor()
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def _removable(padded_statements: list, i: int, function: MethodDeclaration) -> bool:
|
|
308
|
+
"""Return True when the write at ``i`` is dead and can be dropped by splicing lines.
|
|
309
|
+
|
|
310
|
+
Both the write and its overwrite have to start a line of their own,
|
|
311
|
+
which a `;`-joined pair does not.
|
|
312
|
+
"""
|
|
313
|
+
if i + 1 >= len(padded_statements):
|
|
314
|
+
return False
|
|
315
|
+
statements = [s.element for s in padded_statements]
|
|
316
|
+
if not _is_dead_write(statements, i, function):
|
|
317
|
+
return False
|
|
318
|
+
if split_line(statements[i].prefix) is None:
|
|
319
|
+
return False
|
|
320
|
+
return split_line(statements[i + 1].prefix) is not None
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def _remove_overwrites(
|
|
324
|
+
padded_statements: list, end: Space, function: MethodDeclaration
|
|
325
|
+
) -> Optional[Tuple[list, Space]]:
|
|
326
|
+
"""Return the statements and end trivia with dead writes removed, or None if unchanged."""
|
|
327
|
+
to_remove = {
|
|
328
|
+
i for i in range(len(padded_statements))
|
|
329
|
+
if _removable(padded_statements, i, function)
|
|
330
|
+
}
|
|
331
|
+
if not to_remove:
|
|
332
|
+
return None
|
|
333
|
+
return splice_out(padded_statements, end, to_remove)
|
|
@@ -304,9 +304,9 @@ def _convert_format_to_fstring(method: MethodInvocation) -> Optional[FormattedSt
|
|
|
304
304
|
_markers=Markers.EMPTY,
|
|
305
305
|
_expression=padded_expr,
|
|
306
306
|
_debug=None,
|
|
307
|
-
_conversion=
|
|
307
|
+
_conversion=None,
|
|
308
308
|
_format=format_node,
|
|
309
|
-
))
|
|
309
|
+
).with_conversion(conversion_enum))
|
|
310
310
|
|
|
311
311
|
return FormattedString(
|
|
312
312
|
_id=random_id(),
|
|
@@ -515,9 +515,9 @@ def _convert_percent_to_fstring(binary: Binary) -> Optional[FormattedString]:
|
|
|
515
515
|
_markers=Markers.EMPTY,
|
|
516
516
|
_expression=padded_expr,
|
|
517
517
|
_debug=None,
|
|
518
|
-
_conversion=
|
|
518
|
+
_conversion=None,
|
|
519
519
|
_format=None,
|
|
520
|
-
))
|
|
520
|
+
).with_conversion(conversion_enum))
|
|
521
521
|
|
|
522
522
|
return FormattedString(
|
|
523
523
|
_id=random_id(),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openrewrite-migrate-python
|
|
3
|
-
Version: 0.16.0.
|
|
3
|
+
Version: 0.16.0.dev20261005072603
|
|
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
|
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Recipe to remove unconditional overwrites of dict/object values.
|
|
3
|
-
|
|
4
|
-
When two consecutive statements assign to the same dict key or object
|
|
5
|
-
attribute, the first assignment is dead code -- its value is
|
|
6
|
-
immediately overwritten without ever being read. Removing it
|
|
7
|
-
eliminates noise and prevents confusion about the author's intent.
|
|
8
|
-
|
|
9
|
-
See: https://rules.sonarsource.com/python/RSPEC-S4143/
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
from typing import Any, List, Optional, Tuple
|
|
13
|
-
|
|
14
|
-
from rewrite import ExecutionContext, Recipe, TreeVisitor
|
|
15
|
-
from rewrite.category import CategoryDescriptor
|
|
16
|
-
from rewrite.decorators import categorize
|
|
17
|
-
from rewrite.marketplace import Python
|
|
18
|
-
from rewrite.python.visitor import PythonVisitor
|
|
19
|
-
from rewrite.python.tree import Await, CompilationUnit, ExpressionStatement, YieldFrom
|
|
20
|
-
from rewrite.java.support_types import MethodCall, Space
|
|
21
|
-
from rewrite.java.tree import (
|
|
22
|
-
ArrayAccess,
|
|
23
|
-
Assignment,
|
|
24
|
-
Block,
|
|
25
|
-
Expression,
|
|
26
|
-
FieldAccess,
|
|
27
|
-
Statement,
|
|
28
|
-
Yield,
|
|
29
|
-
)
|
|
30
|
-
from openrewrite_code_quality_python.codequality._semantically_equal import SemanticallyEqual
|
|
31
|
-
from openrewrite_code_quality_python.codequality._trivia import splice_out, split_line
|
|
32
|
-
|
|
33
|
-
_CodeQuality = [
|
|
34
|
-
*Python,
|
|
35
|
-
CategoryDescriptor(display_name="Code quality"),
|
|
36
|
-
]
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def _get_assignment(stmt: Statement) -> Optional[Assignment]:
|
|
40
|
-
"""Extract an Assignment from a statement, if present."""
|
|
41
|
-
if isinstance(stmt, ExpressionStatement):
|
|
42
|
-
expr = stmt.expression
|
|
43
|
-
if isinstance(expr, Assignment):
|
|
44
|
-
return expr
|
|
45
|
-
if isinstance(stmt, Assignment):
|
|
46
|
-
return stmt
|
|
47
|
-
return None
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def _is_subscript_assign(assign: Assignment) -> bool:
|
|
51
|
-
"""Return True if the assignment target is a subscript (d[key])."""
|
|
52
|
-
return isinstance(assign.variable, ArrayAccess)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def _is_attribute_assign(assign: Assignment) -> bool:
|
|
56
|
-
"""Return True if the assignment target is an attribute (obj.x)."""
|
|
57
|
-
return isinstance(assign.variable, FieldAccess)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def _same_subscript_target(a: Assignment, b: Assignment) -> bool:
|
|
61
|
-
"""Return True if both assign to the same dict with the same key."""
|
|
62
|
-
va = a.variable
|
|
63
|
-
vb = b.variable
|
|
64
|
-
if not (isinstance(va, ArrayAccess) and isinstance(vb, ArrayAccess)):
|
|
65
|
-
return False
|
|
66
|
-
# Compare the receiver (dict) and the key
|
|
67
|
-
return (
|
|
68
|
-
SemanticallyEqual.are_equal(va.indexed, vb.indexed)
|
|
69
|
-
and SemanticallyEqual.are_equal(va.dimension.index, vb.dimension.index)
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def _same_attribute_target(a: Assignment, b: Assignment) -> bool:
|
|
74
|
-
"""Return True if both assign to the same attribute (obj.x)."""
|
|
75
|
-
va = a.variable
|
|
76
|
-
vb = b.variable
|
|
77
|
-
if not (isinstance(va, FieldAccess) and isinstance(vb, FieldAccess)):
|
|
78
|
-
return False
|
|
79
|
-
return (
|
|
80
|
-
va.name.simple_name == vb.name.simple_name
|
|
81
|
-
and SemanticallyEqual.are_equal(va.target, vb.target)
|
|
82
|
-
)
|
|
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
|
-
|
|
135
|
-
def _is_duplicate_write(a: Assignment, b: Assignment) -> bool:
|
|
136
|
-
"""Return True if a and b write to the same target."""
|
|
137
|
-
if _is_subscript_assign(a) and _is_subscript_assign(b):
|
|
138
|
-
return _same_subscript_target(a, b)
|
|
139
|
-
if _is_attribute_assign(a) and _is_attribute_assign(b):
|
|
140
|
-
return _same_attribute_target(a, b)
|
|
141
|
-
return False
|
|
142
|
-
|
|
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
|
-
|
|
159
|
-
@categorize(_CodeQuality)
|
|
160
|
-
class RemoveUnconditionalValueOverwrite(Recipe):
|
|
161
|
-
"""
|
|
162
|
-
Remove consecutive assignments that write to the same dict key or
|
|
163
|
-
object attribute.
|
|
164
|
-
|
|
165
|
-
When a value written to a collection entry or attribute is
|
|
166
|
-
immediately overwritten in the very next statement, the first
|
|
167
|
-
write is dead code. Removing it keeps the code focused on the
|
|
168
|
-
value that actually persists.
|
|
169
|
-
|
|
170
|
-
Example:
|
|
171
|
-
Before:
|
|
172
|
-
d["key"] = 1
|
|
173
|
-
d["key"] = 2
|
|
174
|
-
|
|
175
|
-
After:
|
|
176
|
-
d["key"] = 2
|
|
177
|
-
"""
|
|
178
|
-
|
|
179
|
-
@property
|
|
180
|
-
def name(self) -> str:
|
|
181
|
-
return "org.openrewrite.python.codequality.RemoveUnconditionalValueOverwrite"
|
|
182
|
-
|
|
183
|
-
@property
|
|
184
|
-
def display_name(self) -> str:
|
|
185
|
-
return "Remove unconditional value overwrites"
|
|
186
|
-
|
|
187
|
-
@property
|
|
188
|
-
def description(self) -> str:
|
|
189
|
-
return (
|
|
190
|
-
"Remove consecutive assignments that write to the same dict key "
|
|
191
|
-
"or object attribute, since the first value is immediately "
|
|
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."
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
@property
|
|
198
|
-
def tags(self) -> List[str]:
|
|
199
|
-
return ["python", "code-quality", "RSPEC-S4143"]
|
|
200
|
-
|
|
201
|
-
@property
|
|
202
|
-
def estimated_effort_per_occurrence(self) -> int:
|
|
203
|
-
return 5
|
|
204
|
-
|
|
205
|
-
def editor(self) -> TreeVisitor[Any, ExecutionContext]:
|
|
206
|
-
class Visitor(PythonVisitor[ExecutionContext]):
|
|
207
|
-
def visit_compilation_unit(
|
|
208
|
-
self, cu: CompilationUnit, p: ExecutionContext
|
|
209
|
-
) -> CompilationUnit:
|
|
210
|
-
cu = super().visit_compilation_unit(cu, p)
|
|
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])
|
|
214
|
-
return cu
|
|
215
|
-
|
|
216
|
-
def visit_block(
|
|
217
|
-
self, block: Block, p: ExecutionContext
|
|
218
|
-
) -> Optional[Block]:
|
|
219
|
-
block = super().visit_block(block, p)
|
|
220
|
-
if not isinstance(block, Block):
|
|
221
|
-
return block
|
|
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])
|
|
225
|
-
return block
|
|
226
|
-
|
|
227
|
-
return Visitor()
|
|
228
|
-
|
|
229
|
-
|
|
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.
|
|
232
|
-
|
|
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
|
|
246
|
-
|
|
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
|
-
}
|
|
256
|
-
if not to_remove:
|
|
257
|
-
return None
|
|
258
|
-
return splice_out(padded_statements, end, to_remove)
|
|
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
|