openrewrite-migrate-python 0.16.0.dev20261006170547__tar.gz → 0.16.0.dev20261007033737__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.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/PKG-INFO +1 -1
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/pyproject.toml +1 -1
- openrewrite_migrate_python-0.16.0.dev20261007033737/src/openrewrite_code_quality_python/codequality/_side_effects.py +36 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_code_quality_python/codequality/all_branches_identical.py +17 -3
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_code_quality_python/codequality/boolean_checks_not_inverted.py +69 -5
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_code_quality_python/codequality/merge_identical_branches.py +10 -5
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_code_quality_python/codequality/remove_duplicate_conditions.py +7 -2
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_code_quality_python/codequality/simplify_boolean_literal.py +6 -2
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/asyncio_coroutine_to_async.py +26 -1
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/string_formatting.py +21 -3
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python.egg-info/PKG-INFO +1 -1
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python.egg-info/SOURCES.txt +1 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_asyncio_coroutine_to_async.py +27 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_string_formatting.py +51 -1
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/setup.cfg +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_code_quality_python/__init__.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_code_quality_python/codequality/__init__.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_code_quality_python/codequality/_reindent.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_code_quality_python/codequality/_semantically_equal.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_code_quality_python/codequality/_trivia.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_code_quality_python/codequality/code_quality.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_code_quality_python/codequality/collapsible_if_statements.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_code_quality_python/codequality/find_suspicious_chained_comparison.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_code_quality_python/codequality/remove_self_assignment.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_code_quality_python/codequality/remove_unconditional_value_overwrite.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_code_quality_python/codequality/simplify_redundant_logical_expression.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/__init__.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/__init__.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/_from_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/_import_hygiene.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/_langchain_tables.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/_markers.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/_slots.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/_typing_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/aifc_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/array_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/ast_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/asyncio_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/avro_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/calendar_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/cgi_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/cgi_parse_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/collections_abc_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/configparser_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/datetime_utc.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/distutils_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/distutils_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/functools_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/future_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/gettext_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/html_parser_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/imp_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/langchain_classic_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/langchain_community_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/langchain_provider_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/locale_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/locale_getdefaultlocale_deprecation.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/macpath_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/mailcap_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/nntplib_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/os_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/pathlib_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/pep594_system_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/pipes_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/pkgutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/platform_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/pydantic_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/pydantic_fields.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/pydantic_model_fields.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/re_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/removed_modules_312.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/shutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/socket_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/ssl_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/sys_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/sys_last_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/tarfile_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/telnetlib_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/tempfile_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/threading_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/threading_is_alive_deprecation.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/typing_callable.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/typing_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/typing_union_to_pipe.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/unittest_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain02.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain1.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/upgrade_to_pydantic.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/upgrade_to_python310.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/upgrade_to_python311.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/upgrade_to_python312.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/upgrade_to_python313.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/upgrade_to_python314.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/upgrade_to_python38.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/upgrade_to_python39.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/urllib_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/uu_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/xdrlib_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python/migrate/xml_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python.egg-info/dependency_links.txt +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python.egg-info/entry_points.txt +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python.egg-info/requires.txt +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/src/openrewrite_migrate_python.egg-info/top_level.txt +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_aifc_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_array_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_ast_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_asyncio_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_avro_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_calendar_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_cgi_parse_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_change_import_recipes.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_collections_abc_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_configparser_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_datetime_utc.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_distutils_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_distutils_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_functools_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_future_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_gettext_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_html_parser_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_imp_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_langchain_classic_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_langchain_community_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_langchain_provider_imports.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_locale_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_locale_getdefaultlocale_deprecation.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_macpath_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_mailcap_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_markers.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_os_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_pathlib_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_pep594_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_pep594_system_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_pkgutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_platform_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_pydantic_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_pydantic_fields.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_pydantic_model_fields.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_re_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_removed_modules_312.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_shutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_socket_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_ssl_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_sys_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_sys_last_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_tarfile_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_tempfile_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_threading_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_threading_extended.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_threading_is_alive_deprecation.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_typing_alias_moves.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_typing_callable.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_typing_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_typing_union_to_pipe.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_unittest_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_upgrade_recipes.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_upgrade_to_pydantic.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_urllib_deprecations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/tests/test_xdrlib_migrations.py +0 -0
- {openrewrite_migrate_python-0.16.0.dev20261006170547 → openrewrite_migrate_python-0.16.0.dev20261007033737}/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.dev20261007033737
|
|
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.dev20261007033737" # 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,36 @@
|
|
|
1
|
+
from rewrite.java.tree import (
|
|
2
|
+
Binary as JBinary,
|
|
3
|
+
Expression,
|
|
4
|
+
FieldAccess,
|
|
5
|
+
Identifier,
|
|
6
|
+
Literal,
|
|
7
|
+
Parentheses,
|
|
8
|
+
Ternary,
|
|
9
|
+
Unary,
|
|
10
|
+
)
|
|
11
|
+
from rewrite.python.tree import Binary as PyBinary
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def is_side_effect_free(expr: Expression) -> bool:
|
|
15
|
+
"""Whether evaluating ``expr`` is taken to have no side effects.
|
|
16
|
+
|
|
17
|
+
Names, literals, attribute reads and operators over them are taken as pure.
|
|
18
|
+
A call, subscript, walrus, ``await`` or any other node may run code that matters.
|
|
19
|
+
"""
|
|
20
|
+
if isinstance(expr, (Identifier, Literal)):
|
|
21
|
+
return True
|
|
22
|
+
if isinstance(expr, FieldAccess):
|
|
23
|
+
return is_side_effect_free(expr.target)
|
|
24
|
+
if isinstance(expr, Parentheses):
|
|
25
|
+
return isinstance(expr.tree, Expression) and is_side_effect_free(expr.tree)
|
|
26
|
+
if isinstance(expr, Unary):
|
|
27
|
+
return is_side_effect_free(expr.expression)
|
|
28
|
+
if isinstance(expr, (JBinary, PyBinary)):
|
|
29
|
+
return is_side_effect_free(expr.left) and is_side_effect_free(expr.right)
|
|
30
|
+
if isinstance(expr, Ternary):
|
|
31
|
+
return (
|
|
32
|
+
is_side_effect_free(expr.condition)
|
|
33
|
+
and is_side_effect_free(expr.true_part)
|
|
34
|
+
and is_side_effect_free(expr.false_part)
|
|
35
|
+
)
|
|
36
|
+
return False
|
|
@@ -27,6 +27,7 @@ from openrewrite_code_quality_python.codequality._reindent import (
|
|
|
27
27
|
reindent,
|
|
28
28
|
)
|
|
29
29
|
from openrewrite_code_quality_python.codequality._semantically_equal import SemanticallyEqual
|
|
30
|
+
from openrewrite_code_quality_python.codequality._side_effects import is_side_effect_free
|
|
30
31
|
|
|
31
32
|
_CodeQuality = [
|
|
32
33
|
*Python,
|
|
@@ -68,6 +69,17 @@ def _collect_bodies(if_: If) -> list[Statement]:
|
|
|
68
69
|
return bodies
|
|
69
70
|
|
|
70
71
|
|
|
72
|
+
def _conditions_side_effect_free(if_: If) -> bool:
|
|
73
|
+
current = if_
|
|
74
|
+
while True:
|
|
75
|
+
if not is_side_effect_free(current.if_condition.tree):
|
|
76
|
+
return False
|
|
77
|
+
else_part = current.else_part
|
|
78
|
+
if else_part is None or not isinstance(else_part.body, If):
|
|
79
|
+
return True
|
|
80
|
+
current = else_part.body
|
|
81
|
+
|
|
82
|
+
|
|
71
83
|
def _all_bodies_equal(bodies: list[Statement]) -> bool:
|
|
72
84
|
"""Return True when every body is semantically equal to the first."""
|
|
73
85
|
if len(bodies) < 2:
|
|
@@ -90,7 +102,8 @@ class AllBranchesIdentical(Recipe):
|
|
|
90
102
|
and eliminates a source of confusion for future readers.
|
|
91
103
|
|
|
92
104
|
Only applies when the chain has an explicit ``else`` so that the
|
|
93
|
-
branches truly cover all cases
|
|
105
|
+
branches truly cover all cases, and when no condition may have a
|
|
106
|
+
side effect that removing it would lose.
|
|
94
107
|
|
|
95
108
|
Example:
|
|
96
109
|
Before:
|
|
@@ -116,7 +129,8 @@ class AllBranchesIdentical(Recipe):
|
|
|
116
129
|
return (
|
|
117
130
|
"Replace `if`/`elif`/`else` chains where every branch has "
|
|
118
131
|
"the same body with just the body, since the condition has "
|
|
119
|
-
"no effect on what code executes."
|
|
132
|
+
"no effect on what code executes. A chain whose conditions may "
|
|
133
|
+
"have side effects, such as a call, is left alone."
|
|
120
134
|
)
|
|
121
135
|
|
|
122
136
|
@property
|
|
@@ -163,7 +177,7 @@ def _replace_identical_ifs_padded(padded_stmts) -> Optional[list]:
|
|
|
163
177
|
if not isinstance(s, If):
|
|
164
178
|
result.append(rp)
|
|
165
179
|
continue
|
|
166
|
-
if not _has_explicit_else(s):
|
|
180
|
+
if not _has_explicit_else(s) or not _conditions_side_effect_free(s):
|
|
167
181
|
result.append(rp)
|
|
168
182
|
continue
|
|
169
183
|
bodies = _collect_bodies(s)
|
|
@@ -10,16 +10,20 @@ See: https://rules.sonarsource.com/python/RSPEC-S1940/
|
|
|
10
10
|
|
|
11
11
|
from typing import Any, List, Optional
|
|
12
12
|
|
|
13
|
-
from rewrite import ExecutionContext, Recipe, TreeVisitor
|
|
13
|
+
from rewrite import Cursor, ExecutionContext, Recipe, TreeVisitor
|
|
14
14
|
from rewrite.category import CategoryDescriptor
|
|
15
15
|
from rewrite.decorators import categorize
|
|
16
16
|
from rewrite.marketplace import Python
|
|
17
|
+
from rewrite.python.tree import Binary as PyBinary
|
|
17
18
|
from rewrite.python.visitor import PythonVisitor
|
|
18
|
-
from rewrite.java.support_types import
|
|
19
|
+
from rewrite.java.support_types import JavaType, JLeftPadded
|
|
19
20
|
from rewrite.java.tree import (
|
|
21
|
+
Assert,
|
|
20
22
|
Binary as JBinary,
|
|
23
|
+
ControlParentheses,
|
|
21
24
|
Expression,
|
|
22
25
|
Parentheses,
|
|
26
|
+
Ternary,
|
|
23
27
|
Unary,
|
|
24
28
|
)
|
|
25
29
|
|
|
@@ -37,6 +41,59 @@ _OPERATOR_INVERSE = {
|
|
|
37
41
|
JBinary.Type.GreaterThanOrEqual: JBinary.Type.LessThan,
|
|
38
42
|
}
|
|
39
43
|
|
|
44
|
+
_EQUALITY = {JBinary.Type.Equal, JBinary.Type.NotEqual}
|
|
45
|
+
|
|
46
|
+
_COMPARISONS = {
|
|
47
|
+
*_OPERATOR_INVERSE,
|
|
48
|
+
PyBinary.Type.In,
|
|
49
|
+
PyBinary.Type.NotIn,
|
|
50
|
+
PyBinary.Type.Is,
|
|
51
|
+
PyBinary.Type.IsNot,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
_NUMERIC = {
|
|
55
|
+
JavaType.Primitive.Boolean,
|
|
56
|
+
JavaType.Primitive.Int,
|
|
57
|
+
JavaType.Primitive.Long,
|
|
58
|
+
JavaType.Primitive.Double,
|
|
59
|
+
JavaType.Primitive.Float,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _is_comparison(expr: Expression) -> bool:
|
|
64
|
+
return isinstance(expr, (JBinary, PyBinary)) and expr.operator in _COMPARISONS
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _is_invertible(comparison: JBinary) -> bool:
|
|
68
|
+
# `a < b < c` is one chained comparison whose last link is the outer node.
|
|
69
|
+
if _is_comparison(comparison.left) or _is_comparison(comparison.right):
|
|
70
|
+
return False
|
|
71
|
+
# Only numbers are totally ordered. A set orders by inclusion.
|
|
72
|
+
return comparison.operator in _EQUALITY or (
|
|
73
|
+
comparison.left.type in _NUMERIC and comparison.right.type in _NUMERIC
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _is_read_as_truth_value(cursor: Cursor) -> bool:
|
|
78
|
+
node = cursor.value
|
|
79
|
+
parent = cursor.parent_tree_cursor()
|
|
80
|
+
while isinstance(parent.value, Parentheses):
|
|
81
|
+
node = parent.value
|
|
82
|
+
parent = parent.parent_tree_cursor()
|
|
83
|
+
value = parent.value
|
|
84
|
+
if isinstance(value, ControlParentheses):
|
|
85
|
+
return True
|
|
86
|
+
if isinstance(value, Unary):
|
|
87
|
+
return value.operator == Unary.Type.Not
|
|
88
|
+
if isinstance(value, Ternary):
|
|
89
|
+
return value.condition is node
|
|
90
|
+
if isinstance(value, Assert):
|
|
91
|
+
return value.condition is node
|
|
92
|
+
# `a or b` yields an operand, so its operands are truth values only if it is one.
|
|
93
|
+
if isinstance(value, JBinary) and value.operator in (JBinary.Type.And, JBinary.Type.Or):
|
|
94
|
+
return _is_read_as_truth_value(parent)
|
|
95
|
+
return False
|
|
96
|
+
|
|
40
97
|
|
|
41
98
|
@categorize(_CodeQuality)
|
|
42
99
|
class BooleanChecksNotInverted(Recipe):
|
|
@@ -71,7 +128,10 @@ class BooleanChecksNotInverted(Recipe):
|
|
|
71
128
|
return (
|
|
72
129
|
"Replace inverted boolean comparisons like `not (a == b)` with the "
|
|
73
130
|
"equivalent direct operator (`a != b`), and remove double negations "
|
|
74
|
-
"like `not (not x)`."
|
|
131
|
+
"like `not (not x)`. An ordering such as `not (a < b)` is only "
|
|
132
|
+
"inverted where both operands' types resolve to numbers, a chained "
|
|
133
|
+
"comparison is left alone, and a double negation is only removed "
|
|
134
|
+
"where its value is read as a truth value, as in a condition."
|
|
75
135
|
)
|
|
76
136
|
|
|
77
137
|
@property
|
|
@@ -103,11 +163,15 @@ class BooleanChecksNotInverted(Recipe):
|
|
|
103
163
|
inner = inner.tree
|
|
104
164
|
|
|
105
165
|
# Double negation: not (not x) -> x
|
|
106
|
-
if
|
|
166
|
+
if (
|
|
167
|
+
isinstance(inner, Unary)
|
|
168
|
+
and inner.operator == Unary.Type.Not
|
|
169
|
+
and _is_read_as_truth_value(self.cursor)
|
|
170
|
+
):
|
|
107
171
|
return inner.expression.replace(_prefix=unary.prefix)
|
|
108
172
|
|
|
109
173
|
# Inverted comparison: not (a == b) -> a != b
|
|
110
|
-
if isinstance(inner, JBinary):
|
|
174
|
+
if isinstance(inner, JBinary) and _is_invertible(inner):
|
|
111
175
|
inverse = _OPERATOR_INVERSE.get(inner.operator)
|
|
112
176
|
if inverse is not None:
|
|
113
177
|
return inner.padding.replace(
|
|
@@ -19,13 +19,16 @@ from rewrite.markers import Markers
|
|
|
19
19
|
from rewrite.python.visitor import PythonVisitor
|
|
20
20
|
from rewrite.java.support_types import JLeftPadded, JRightPadded, Space
|
|
21
21
|
from rewrite.java.tree import (
|
|
22
|
+
Assignment,
|
|
22
23
|
Binary as JBinary,
|
|
23
24
|
Block,
|
|
24
25
|
ControlParentheses,
|
|
25
26
|
Expression,
|
|
26
27
|
If,
|
|
28
|
+
Lambda,
|
|
27
29
|
Parentheses,
|
|
28
30
|
Statement,
|
|
31
|
+
Ternary,
|
|
29
32
|
)
|
|
30
33
|
from openrewrite_code_quality_python.codequality._semantically_equal import SemanticallyEqual
|
|
31
34
|
from rewrite.utils import random_id
|
|
@@ -36,8 +39,11 @@ _CodeQuality = [
|
|
|
36
39
|
]
|
|
37
40
|
|
|
38
41
|
|
|
39
|
-
def
|
|
40
|
-
|
|
42
|
+
def _needs_parens_as_or_operand(expr: Expression) -> bool:
|
|
43
|
+
# A walrus, ternary or lambda binds looser than `or`. An `and` does not,
|
|
44
|
+
# and is parenthesized for readability.
|
|
45
|
+
if isinstance(expr, (Assignment, Ternary, Lambda)):
|
|
46
|
+
return True
|
|
41
47
|
return isinstance(expr, JBinary) and expr.operator == JBinary.Type.And
|
|
42
48
|
|
|
43
49
|
|
|
@@ -160,8 +166,7 @@ def _merge_conditions(first: If, second: If) -> If:
|
|
|
160
166
|
first_cond = first.if_condition.tree
|
|
161
167
|
second_cond = second.if_condition.tree
|
|
162
168
|
|
|
163
|
-
|
|
164
|
-
if _contains_and(first_cond):
|
|
169
|
+
if _needs_parens_as_or_operand(first_cond):
|
|
165
170
|
left = Parentheses(
|
|
166
171
|
_id=random_id(),
|
|
167
172
|
_prefix=first_cond.prefix,
|
|
@@ -175,7 +180,7 @@ def _merge_conditions(first: If, second: If) -> If:
|
|
|
175
180
|
else:
|
|
176
181
|
left = first_cond
|
|
177
182
|
|
|
178
|
-
if
|
|
183
|
+
if _needs_parens_as_or_operand(second_cond):
|
|
179
184
|
right = Parentheses(
|
|
180
185
|
_id=random_id(),
|
|
181
186
|
_prefix=Space.SINGLE_SPACE,
|
|
@@ -18,6 +18,7 @@ from rewrite.marketplace import Python
|
|
|
18
18
|
from rewrite.python.visitor import PythonVisitor
|
|
19
19
|
from rewrite.java.tree import If, Expression
|
|
20
20
|
from openrewrite_code_quality_python.codequality._semantically_equal import SemanticallyEqual
|
|
21
|
+
from openrewrite_code_quality_python.codequality._side_effects import is_side_effect_free
|
|
21
22
|
|
|
22
23
|
_CodeQuality = [
|
|
23
24
|
*Python,
|
|
@@ -79,7 +80,9 @@ class RemoveDuplicateConditions(Recipe):
|
|
|
79
80
|
return (
|
|
80
81
|
"Remove `elif` branches whose condition is identical to an earlier "
|
|
81
82
|
"branch in the same `if`/`elif` chain, since the duplicate branch "
|
|
82
|
-
"is dead code that can never execute."
|
|
83
|
+
"is dead code that can never execute. A condition that may have "
|
|
84
|
+
"side effects, such as a call, is kept, since evaluating it again "
|
|
85
|
+
"can give a different result."
|
|
83
86
|
)
|
|
84
87
|
|
|
85
88
|
@property
|
|
@@ -136,7 +139,9 @@ def _prune_chain(root: If, current: If, seen: list[Expression]) -> If:
|
|
|
136
139
|
return root
|
|
137
140
|
|
|
138
141
|
cond = branch.if_condition.tree
|
|
139
|
-
is_dup =
|
|
142
|
+
is_dup = is_side_effect_free(cond) and any(
|
|
143
|
+
SemanticallyEqual.are_equal(cond, prev) for prev in seen
|
|
144
|
+
)
|
|
140
145
|
|
|
141
146
|
if is_dup:
|
|
142
147
|
# Skip this elif -- connect current to the duplicate's else
|
|
@@ -16,7 +16,7 @@ from rewrite.decorators import categorize
|
|
|
16
16
|
from rewrite.marketplace import Python
|
|
17
17
|
from rewrite.markers import Markers
|
|
18
18
|
from rewrite.python.visitor import PythonVisitor
|
|
19
|
-
from rewrite.java.support_types import JLeftPadded, Space
|
|
19
|
+
from rewrite.java.support_types import JavaType, JLeftPadded, Space
|
|
20
20
|
from rewrite.java.tree import Binary as JBinary, Expression, Literal, Unary
|
|
21
21
|
from rewrite.python.tree import Binary as PyBinary
|
|
22
22
|
from rewrite.utils import random_id
|
|
@@ -81,7 +81,8 @@ class SimplifyBooleanLiteral(Recipe):
|
|
|
81
81
|
def description(self) -> str:
|
|
82
82
|
return (
|
|
83
83
|
"Replace comparisons against boolean literals (`== True`, `!= False`, "
|
|
84
|
-
"`is True`, etc.) with the simpler equivalent boolean expression."
|
|
84
|
+
"`is True`, etc.) with the simpler equivalent boolean expression. "
|
|
85
|
+
"Only fires where the other operand's type resolves to `bool`."
|
|
85
86
|
)
|
|
86
87
|
|
|
87
88
|
@property
|
|
@@ -153,6 +154,9 @@ def _simplify_comparison(
|
|
|
153
154
|
other = right
|
|
154
155
|
if bool_val is None:
|
|
155
156
|
return None
|
|
157
|
+
# `2 == True` is False, but `2` is truthy.
|
|
158
|
+
if other.type != JavaType.Primitive.Boolean:
|
|
159
|
+
return None
|
|
156
160
|
|
|
157
161
|
# Determine whether the result should be negated.
|
|
158
162
|
# == True / is True -> x (identity)
|
|
@@ -168,6 +168,27 @@ class _YieldFromToAwaitVisitor(PythonVisitor[ExecutionContext]):
|
|
|
168
168
|
return lambda_
|
|
169
169
|
|
|
170
170
|
|
|
171
|
+
class _BareYieldFinder(PythonVisitor[List[Yield]]):
|
|
172
|
+
"""Collects each ``yield`` that is not a ``yield from``, outside nested functions."""
|
|
173
|
+
|
|
174
|
+
def visit_yield(self, yield_: Yield, p: List[Yield]) -> J:
|
|
175
|
+
if not isinstance(yield_.value, YieldFrom):
|
|
176
|
+
p.append(yield_)
|
|
177
|
+
return super().visit_yield(yield_, p)
|
|
178
|
+
|
|
179
|
+
def visit_method_declaration(self, method: MethodDeclaration, p: List[Yield]) -> J:
|
|
180
|
+
return method
|
|
181
|
+
|
|
182
|
+
def visit_lambda(self, lambda_: Lambda, p: List[Yield]) -> J:
|
|
183
|
+
return lambda_
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _has_bare_yield(body: J) -> bool:
|
|
187
|
+
found: List[Yield] = []
|
|
188
|
+
_BareYieldFinder().visit(body, found)
|
|
189
|
+
return bool(found)
|
|
190
|
+
|
|
191
|
+
|
|
171
192
|
@categorize(_Python311)
|
|
172
193
|
class MigrateAsyncioCoroutine(Recipe):
|
|
173
194
|
"""
|
|
@@ -205,7 +226,8 @@ class MigrateAsyncioCoroutine(Recipe):
|
|
|
205
226
|
return (
|
|
206
227
|
"Migrate functions using the deprecated `@asyncio.coroutine` decorator "
|
|
207
228
|
"to use `async def` syntax. Also transforms `yield from` to `await`. "
|
|
208
|
-
"The decorator was removed in Python 3.11."
|
|
229
|
+
"The decorator was removed in Python 3.11. A function whose body has "
|
|
230
|
+
"a bare `yield` is left alone, since it would become an async generator."
|
|
209
231
|
)
|
|
210
232
|
|
|
211
233
|
def editor(self) -> TreeVisitor[Any, ExecutionContext]:
|
|
@@ -219,6 +241,9 @@ class MigrateAsyncioCoroutine(Recipe):
|
|
|
219
241
|
|
|
220
242
|
if not _has_asyncio_coroutine_decorator(method):
|
|
221
243
|
return method
|
|
244
|
+
# A bare `yield` would make the `async def` an async generator.
|
|
245
|
+
if method.body is not None and _has_bare_yield(method.body):
|
|
246
|
+
return method
|
|
222
247
|
|
|
223
248
|
# Step 1: Remove the @asyncio.coroutine decorator
|
|
224
249
|
method = _remove_asyncio_coroutine_decorator(method)
|
|
@@ -19,9 +19,10 @@ from rewrite.decorators import categorize
|
|
|
19
19
|
from rewrite.marketplace import Python
|
|
20
20
|
from rewrite.markers import Markers
|
|
21
21
|
from rewrite.python.visitor import PythonVisitor
|
|
22
|
-
from rewrite.java.support_types import JRightPadded, Space
|
|
22
|
+
from rewrite.java.support_types import JavaType, JRightPadded, Space
|
|
23
23
|
from rewrite.java.tree import Binary, Expression, FieldAccess, Identifier, J, Literal, MethodInvocation, Parentheses
|
|
24
24
|
from rewrite.python.printer import PrintOutputCapture, PythonPrinter
|
|
25
|
+
from rewrite.python.type_utils import is_of_class_type
|
|
25
26
|
from rewrite.python.tree import (
|
|
26
27
|
CollectionLiteral,
|
|
27
28
|
FormattedString,
|
|
@@ -126,6 +127,19 @@ def _is_side_effect_free(expr: Any) -> bool:
|
|
|
126
127
|
return isinstance(expr, (Identifier, Literal, FieldAccess))
|
|
127
128
|
|
|
128
129
|
|
|
130
|
+
_NON_TUPLE_BUILTINS = ("list", "dict", "set", "frozenset")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _resolves_to_non_tuple(expr: Expression) -> bool:
|
|
134
|
+
# A tuple operand is spread over the specifiers, so `"%s" % (1,)` is `"1"`.
|
|
135
|
+
# A class's supertypes may stop short of `tuple`, as a NamedTuple's do,
|
|
136
|
+
# so only types known not to be tuples qualify.
|
|
137
|
+
type_ = expr.type
|
|
138
|
+
return isinstance(type_, JavaType.Primitive) or any(
|
|
139
|
+
is_of_class_type(type_, name) for name in _NON_TUPLE_BUILTINS
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
129
143
|
def _has_backslash(value_source: str) -> bool:
|
|
130
144
|
"""Check if the string content (between quotes) contains a backslash."""
|
|
131
145
|
s = value_source
|
|
@@ -470,8 +484,10 @@ def _convert_percent_to_fstring(binary: Binary) -> Optional[FormattedString]:
|
|
|
470
484
|
# Get argument list
|
|
471
485
|
if isinstance(right, CollectionLiteral) and right.kind == CollectionLiteral.Kind.TUPLE:
|
|
472
486
|
args = list(right.elements)
|
|
473
|
-
|
|
487
|
+
elif isinstance(right, Expression) and _resolves_to_non_tuple(right):
|
|
474
488
|
args = [right]
|
|
489
|
+
else:
|
|
490
|
+
return None
|
|
475
491
|
|
|
476
492
|
# Verify arg count matches specifier count
|
|
477
493
|
if len(args) != spec_count:
|
|
@@ -560,7 +576,9 @@ class ReplacePercentFormatWithFString(Recipe):
|
|
|
560
576
|
return (
|
|
561
577
|
'Replace `"..." % (...)` expressions with f-strings (Python 3.6+). '
|
|
562
578
|
"Only converts `%s` and `%r` specifiers where the format string is "
|
|
563
|
-
"a literal and the conversion is safe."
|
|
579
|
+
"a literal and the conversion is safe. A single operand that is not a "
|
|
580
|
+
"tuple literal must have a type that resolves to a non-tuple, such as "
|
|
581
|
+
"`str` or `int`, since a tuple would be spread over the specifiers."
|
|
564
582
|
)
|
|
565
583
|
|
|
566
584
|
@property
|
|
@@ -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.dev20261007033737
|
|
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/_side_effects.py
|
|
6
7
|
src/openrewrite_code_quality_python/codequality/_trivia.py
|
|
7
8
|
src/openrewrite_code_quality_python/codequality/all_branches_identical.py
|
|
8
9
|
src/openrewrite_code_quality_python/codequality/boolean_checks_not_inverted.py
|
|
@@ -167,6 +167,7 @@ class TestMigrateAsyncioCoroutine:
|
|
|
167
167
|
@asyncio.coroutine
|
|
168
168
|
def outer():
|
|
169
169
|
def gen():
|
|
170
|
+
yield 0
|
|
170
171
|
yield from range(3)
|
|
171
172
|
g = lambda: (yield from range(3))
|
|
172
173
|
yield from step()
|
|
@@ -176,9 +177,35 @@ class TestMigrateAsyncioCoroutine:
|
|
|
176
177
|
|
|
177
178
|
async def outer():
|
|
178
179
|
def gen():
|
|
180
|
+
yield 0
|
|
179
181
|
yield from range(3)
|
|
180
182
|
g = lambda: (yield from range(3))
|
|
181
183
|
await step()
|
|
182
184
|
""",
|
|
183
185
|
)
|
|
184
186
|
)
|
|
187
|
+
|
|
188
|
+
def test_no_change_when_the_body_has_a_bare_yield(self):
|
|
189
|
+
spec = RecipeSpec(recipe=MigrateAsyncioCoroutine())
|
|
190
|
+
spec.rewrite_run(
|
|
191
|
+
python(
|
|
192
|
+
"""
|
|
193
|
+
import asyncio
|
|
194
|
+
|
|
195
|
+
@asyncio.coroutine
|
|
196
|
+
def f():
|
|
197
|
+
yield
|
|
198
|
+
"""
|
|
199
|
+
),
|
|
200
|
+
|
|
201
|
+
python(
|
|
202
|
+
"""
|
|
203
|
+
import asyncio
|
|
204
|
+
|
|
205
|
+
@asyncio.coroutine
|
|
206
|
+
def g():
|
|
207
|
+
x = yield from step()
|
|
208
|
+
yield x
|
|
209
|
+
"""
|
|
210
|
+
),
|
|
211
|
+
)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Tests for string formatting recipes."""
|
|
2
2
|
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
3
5
|
from rewrite import InMemoryExecutionContext, InMemoryLargeSourceSet, Recipe
|
|
4
6
|
from rewrite.test import RecipeSpec, python
|
|
5
7
|
from openrewrite_migrate_python.migrate.string_formatting import (
|
|
@@ -370,18 +372,54 @@ class TestReplaceStrFormatWithFString:
|
|
|
370
372
|
class TestReplacePercentFormatWithFString:
|
|
371
373
|
"""Tests for the ReplacePercentFormatWithFString recipe."""
|
|
372
374
|
|
|
375
|
+
@pytest.mark.requires_type_attribution
|
|
373
376
|
def test_simple_percent_s(self):
|
|
374
377
|
"""'Hello %s' % name → f'Hello {name}'"""
|
|
375
378
|
spec = RecipeSpec(recipe=ReplacePercentFormatWithFString())
|
|
376
379
|
spec.rewrite_run(
|
|
377
380
|
python(
|
|
378
381
|
"""
|
|
382
|
+
name = "world"
|
|
379
383
|
message = "Hello %s" % name
|
|
380
384
|
""",
|
|
381
385
|
"""
|
|
386
|
+
name = "world"
|
|
382
387
|
message = f"Hello {name}"
|
|
383
388
|
""",
|
|
384
|
-
)
|
|
389
|
+
),
|
|
390
|
+
|
|
391
|
+
python(
|
|
392
|
+
"""
|
|
393
|
+
items = [1]
|
|
394
|
+
message = "Hello %s" % items
|
|
395
|
+
""",
|
|
396
|
+
"""
|
|
397
|
+
items = [1]
|
|
398
|
+
message = f"Hello {items}"
|
|
399
|
+
""",
|
|
400
|
+
),
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
def test_no_change_unless_a_single_operand_resolves_to_a_non_tuple(self):
|
|
404
|
+
spec = RecipeSpec(recipe=ReplacePercentFormatWithFString())
|
|
405
|
+
spec.rewrite_run(
|
|
406
|
+
python(
|
|
407
|
+
"""
|
|
408
|
+
t = (1,)
|
|
409
|
+
message = "%s" % t
|
|
410
|
+
"""
|
|
411
|
+
),
|
|
412
|
+
|
|
413
|
+
python(
|
|
414
|
+
"""
|
|
415
|
+
from typing import NamedTuple
|
|
416
|
+
class P(NamedTuple):
|
|
417
|
+
x: int
|
|
418
|
+
message = "%s" % P(1)
|
|
419
|
+
"""
|
|
420
|
+
),
|
|
421
|
+
|
|
422
|
+
python('message = "Hello %s" % name'),
|
|
385
423
|
)
|
|
386
424
|
|
|
387
425
|
def test_multiple_percent_s(self):
|
|
@@ -398,43 +436,52 @@ class TestReplacePercentFormatWithFString:
|
|
|
398
436
|
)
|
|
399
437
|
)
|
|
400
438
|
|
|
439
|
+
@pytest.mark.requires_type_attribution
|
|
401
440
|
def test_percent_r(self):
|
|
402
441
|
"""'%r' % x → f'{x!r}'"""
|
|
403
442
|
spec = RecipeSpec(recipe=ReplacePercentFormatWithFString())
|
|
404
443
|
spec.rewrite_run(
|
|
405
444
|
python(
|
|
406
445
|
"""
|
|
446
|
+
x = 1
|
|
407
447
|
message = "%r" % x
|
|
408
448
|
""",
|
|
409
449
|
"""
|
|
450
|
+
x = 1
|
|
410
451
|
message = f"{x!r}"
|
|
411
452
|
""",
|
|
412
453
|
)
|
|
413
454
|
)
|
|
414
455
|
|
|
456
|
+
@pytest.mark.requires_type_attribution
|
|
415
457
|
def test_escaped_percent(self):
|
|
416
458
|
"""'100%% of %s' % name → f'100% of {name}'"""
|
|
417
459
|
spec = RecipeSpec(recipe=ReplacePercentFormatWithFString())
|
|
418
460
|
spec.rewrite_run(
|
|
419
461
|
python(
|
|
420
462
|
"""
|
|
463
|
+
name = "world"
|
|
421
464
|
message = "100%% of %s" % name
|
|
422
465
|
""",
|
|
423
466
|
"""
|
|
467
|
+
name = "world"
|
|
424
468
|
message = f"100% of {name}"
|
|
425
469
|
""",
|
|
426
470
|
)
|
|
427
471
|
)
|
|
428
472
|
|
|
473
|
+
@pytest.mark.requires_type_attribution
|
|
429
474
|
def test_single_quotes(self):
|
|
430
475
|
"""'Hello %s' % name → f'Hello {name}' (single quotes)"""
|
|
431
476
|
spec = RecipeSpec(recipe=ReplacePercentFormatWithFString())
|
|
432
477
|
spec.rewrite_run(
|
|
433
478
|
python(
|
|
434
479
|
"""
|
|
480
|
+
name = "world"
|
|
435
481
|
message = 'Hello %s' % name
|
|
436
482
|
""",
|
|
437
483
|
"""
|
|
484
|
+
name = "world"
|
|
438
485
|
message = f'Hello {name}'
|
|
439
486
|
""",
|
|
440
487
|
)
|
|
@@ -556,6 +603,7 @@ class TestReplacePercentFormatWithFString:
|
|
|
556
603
|
)
|
|
557
604
|
)
|
|
558
605
|
|
|
606
|
+
@pytest.mark.requires_type_attribution
|
|
559
607
|
def test_idempotent(self):
|
|
560
608
|
"""Running the recipe twice produces the same result."""
|
|
561
609
|
recipe = ReplacePercentFormatWithFString()
|
|
@@ -563,9 +611,11 @@ class TestReplacePercentFormatWithFString:
|
|
|
563
611
|
spec.rewrite_run(
|
|
564
612
|
python(
|
|
565
613
|
"""
|
|
614
|
+
name = "world"
|
|
566
615
|
message = "Hello %s" % name
|
|
567
616
|
""",
|
|
568
617
|
"""
|
|
618
|
+
name = "world"
|
|
569
619
|
message = f"Hello {name}"
|
|
570
620
|
""",
|
|
571
621
|
after_recipe=_assert_idempotent(recipe),
|
|
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
|