openrewrite-migrate-python 0.9.0.dev20260605173622__tar.gz → 0.9.0.dev20260606102208__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.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/PKG-INFO +3 -2
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/pyproject.toml +9 -2
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/__init__.py +16 -0
- openrewrite_migrate_python-0.9.0.dev20260606102208/src/openrewrite_migrate_python/migrate/_markers.py +22 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/array_deprecations.py +6 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/ast_deprecations.py +5 -6
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/calendar_deprecations.py +5 -12
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/platform_deprecations.py +6 -1
- openrewrite_migrate_python-0.9.0.dev20260606102208/src/openrewrite_migrate_python/migrate/pydantic_deprecations.py +360 -0
- openrewrite_migrate_python-0.9.0.dev20260606102208/src/openrewrite_migrate_python/migrate/pydantic_model_fields.py +142 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/threading_deprecations.py +22 -8
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/threading_is_alive_deprecation.py +6 -2
- openrewrite_migrate_python-0.9.0.dev20260606102208/src/openrewrite_migrate_python/migrate/upgrade_to_pydantic.py +116 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python.egg-info/PKG-INFO +3 -2
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python.egg-info/SOURCES.txt +5 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python.egg-info/requires.txt +2 -1
- openrewrite_migrate_python-0.9.0.dev20260606102208/tests/test_array_deprecations.py +102 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_ast_deprecations.py +40 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_calendar_deprecations.py +37 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_platform_deprecations.py +19 -0
- openrewrite_migrate_python-0.9.0.dev20260606102208/tests/test_pydantic_deprecations.py +269 -0
- openrewrite_migrate_python-0.9.0.dev20260606102208/tests/test_pydantic_model_fields.py +210 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_threading_deprecations.py +29 -2
- openrewrite_migrate_python-0.9.0.dev20260606102208/tests/test_threading_extended.py +216 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_threading_is_alive_deprecation.py +29 -2
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_upgrade_recipes.py +40 -0
- openrewrite_migrate_python-0.9.0.dev20260605173622/src/openrewrite_migrate_python/migrate/_markers.py +0 -16
- openrewrite_migrate_python-0.9.0.dev20260605173622/tests/test_array_deprecations.py +0 -44
- openrewrite_migrate_python-0.9.0.dev20260605173622/tests/test_threading_extended.py +0 -94
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/setup.cfg +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/__init__.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/__init__.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/_semantically_equal.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/all_branches_identical.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/boolean_checks_not_inverted.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/collapsible_if_statements.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/merge_identical_branches.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/remove_duplicate_conditions.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/remove_self_assignment.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/remove_unconditional_value_overwrite.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/simplify_boolean_literal.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_code_quality_python/codequality/simplify_redundant_logical_expression.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/__init__.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/aifc_migrations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/asyncio_coroutine_to_async.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/asyncio_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/cgi_migrations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/cgi_parse_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/collections_abc_migrations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/configparser_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/datetime_utc.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/distutils_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/distutils_migrations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/functools_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/future_imports.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/gettext_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/html_parser_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/imp_migrations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/langchain_classic_imports.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/langchain_community_imports.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/langchain_provider_imports.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/locale_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/locale_getdefaultlocale_deprecation.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/macpath_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/mailcap_migrations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/nntplib_migrations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/os_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/pathlib_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/pep594_system_migrations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/pipes_migrations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/pkgutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/re_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/removed_modules_312.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/shutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/socket_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/ssl_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/string_formatting.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/sys_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/sys_last_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/tarfile_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/telnetlib_migrations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/tempfile_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/typing_callable.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/typing_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/typing_union_to_pipe.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/unittest_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain02.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_langchain1.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_python310.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_python311.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_python312.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_python313.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_python314.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_python38.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/upgrade_to_python39.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/urllib_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/uu_migrations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/xdrlib_migrations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python/migrate/xml_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python.egg-info/dependency_links.txt +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python.egg-info/entry_points.txt +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/src/openrewrite_migrate_python.egg-info/top_level.txt +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_aifc_migrations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_asyncio_coroutine_to_async.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_asyncio_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_cgi_parse_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_change_import_recipes.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_collections_abc_migrations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_configparser_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_datetime_utc.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_distutils_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_future_imports.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_gettext_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_html_parser_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_imp_migrations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_langchain_classic_imports.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_langchain_community_imports.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_langchain_provider_imports.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_locale_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_locale_getdefaultlocale_deprecation.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_macpath_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_os_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_pep594_migrations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_pep594_system_migrations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_pkgutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_re_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_removed_modules_312.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_shutil_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_ssl_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_string_formatting.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_sys_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_sys_last_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_tarfile_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_typing_callable.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_typing_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_typing_union_to_pipe.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/tests/test_unittest_deprecations.py +0 -0
- {openrewrite_migrate_python-0.9.0.dev20260605173622 → openrewrite_migrate_python-0.9.0.dev20260606102208}/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.9.0.
|
|
3
|
+
Version: 0.9.0.dev20260606102208
|
|
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
|
|
@@ -21,7 +21,8 @@ Classifier: Typing :: Typed
|
|
|
21
21
|
Requires-Python: >=3.10
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
Provides-Extra: dev
|
|
24
|
-
Requires-Dist: openrewrite>=8.
|
|
24
|
+
Requires-Dist: openrewrite>=8.84.3; extra == "dev"
|
|
25
|
+
Requires-Dist: pydantic>=2.11; extra == "dev"
|
|
25
26
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
26
27
|
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
27
28
|
Requires-Dist: black>=24.0.0; extra == "dev"
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "openrewrite-migrate-python"
|
|
7
7
|
description = "OpenRewrite recipes for migrating Python codebases to newer Python versions."
|
|
8
|
-
version = "0.9.0.
|
|
8
|
+
version = "0.9.0.dev20260606102208" # 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"
|
|
@@ -30,7 +30,14 @@ dependencies = []
|
|
|
30
30
|
|
|
31
31
|
[project.optional-dependencies]
|
|
32
32
|
dev = [
|
|
33
|
-
|
|
33
|
+
# 8.84.3 brings ty external-type resolution (cross-package supertype chains),
|
|
34
|
+
# J.MethodInvocation.type, sorted insertion in maybe_add_import, and the
|
|
35
|
+
# type_utils comparison helpers — all of which the recipes rely on for
|
|
36
|
+
# type-aware matching. openrewrite is host-provided at runtime, so this floor
|
|
37
|
+
# only affects dev/CI.
|
|
38
|
+
"openrewrite>=8.84.3",
|
|
39
|
+
# Needed so type-attribution tests can resolve `pydantic` types.
|
|
40
|
+
"pydantic>=2.11",
|
|
34
41
|
"pytest>=8.0.0",
|
|
35
42
|
"pytest-cov>=4.0.0",
|
|
36
43
|
"black>=24.0.0",
|
|
@@ -63,6 +63,12 @@ from .pep594_system_migrations import (
|
|
|
63
63
|
)
|
|
64
64
|
from .pipes_migrations import FindPipesModule
|
|
65
65
|
from .pkgutil_deprecations import ReplacePkgutilFindLoader, ReplacePkgutilGetLoader
|
|
66
|
+
from .pydantic_deprecations import (
|
|
67
|
+
FindDeprecatedJsonEncoders,
|
|
68
|
+
FindDeprecatedSchemaGenerator,
|
|
69
|
+
ReplaceFinalFieldWithClassVar,
|
|
70
|
+
)
|
|
71
|
+
from .pydantic_model_fields import ReplaceModelFieldsInstanceAccess
|
|
66
72
|
from .re_deprecations import ReplaceReTemplate
|
|
67
73
|
from .removed_modules_312 import FindRemovedModules312
|
|
68
74
|
from .shutil_deprecations import FindShutilRmtreeOnerror
|
|
@@ -105,6 +111,7 @@ from .upgrade_to_python311 import UpgradeToPython311
|
|
|
105
111
|
from .upgrade_to_python312 import UpgradeToPython312
|
|
106
112
|
from .upgrade_to_python313 import UpgradeToPython313
|
|
107
113
|
from .upgrade_to_python314 import UpgradeToPython314
|
|
114
|
+
from .upgrade_to_pydantic import UpgradeToPydantic210, UpgradeToPydantic211
|
|
108
115
|
from .urllib_deprecations import FindUrllibParseSplitFunctions, FindUrllibParseToBytes
|
|
109
116
|
from .uu_migrations import FindUuModule
|
|
110
117
|
from .xdrlib_migrations import FindXdrlibModule
|
|
@@ -230,4 +237,13 @@ __all__ = [
|
|
|
230
237
|
"UpgradeToPython312",
|
|
231
238
|
"UpgradeToPython313",
|
|
232
239
|
"UpgradeToPython314",
|
|
240
|
+
# Auto-fix: Pydantic
|
|
241
|
+
"ReplaceModelFieldsInstanceAccess",
|
|
242
|
+
"ReplaceFinalFieldWithClassVar",
|
|
243
|
+
# Detection: Pydantic
|
|
244
|
+
"FindDeprecatedSchemaGenerator",
|
|
245
|
+
"FindDeprecatedJsonEncoders",
|
|
246
|
+
# Composite upgrade recipes: Pydantic
|
|
247
|
+
"UpgradeToPydantic210",
|
|
248
|
+
"UpgradeToPydantic211",
|
|
233
249
|
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Shared marker utilities for deprecation recipes."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
from rewrite.markers import Markers, MarkupWarn
|
|
6
|
+
from rewrite.utils import random_id
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def mark_deprecated(tree: Any, message: str, detail: Optional[str] = None) -> Any:
|
|
10
|
+
"""Attach a ``MarkupWarn`` warning marker flagging a deprecation.
|
|
11
|
+
|
|
12
|
+
``MarkupWarn`` is OpenRewrite's warning-markup marker (the intended marker
|
|
13
|
+
for deprecations that need manual attention), as opposed to ``SearchResult``
|
|
14
|
+
which denotes a plain search hit. It renders the same comment in printed
|
|
15
|
+
output but surfaces as a warning diagnostic in the tooling.
|
|
16
|
+
"""
|
|
17
|
+
full_message = f"{message}: {detail}" if detail else message
|
|
18
|
+
warn_marker = MarkupWarn(random_id(), full_message)
|
|
19
|
+
current_markers = tree.markers
|
|
20
|
+
new_markers_list = list(current_markers.markers) + [warn_marker]
|
|
21
|
+
new_markers = Markers(current_markers.id, new_markers_list)
|
|
22
|
+
return tree.replace(_markers=new_markers)
|
|
@@ -18,6 +18,8 @@ from rewrite.python.preconditions import uses_method
|
|
|
18
18
|
from rewrite.python.visitor import PythonVisitor
|
|
19
19
|
from rewrite.java.tree import Identifier, MethodInvocation
|
|
20
20
|
|
|
21
|
+
from rewrite.python.type_utils import is_assignable_to
|
|
22
|
+
|
|
21
23
|
# Define category path: Python > Migrate > Python 3.14
|
|
22
24
|
_Python314 = [
|
|
23
25
|
*Python,
|
|
@@ -72,6 +74,8 @@ class ReplaceArrayTostring(Recipe):
|
|
|
72
74
|
return method
|
|
73
75
|
if method.name.simple_name != "tostring":
|
|
74
76
|
return method
|
|
77
|
+
if method.select is None or not is_assignable_to("array.array", method.select.type):
|
|
78
|
+
return method
|
|
75
79
|
|
|
76
80
|
# Replace tostring with tobytes
|
|
77
81
|
new_name = method.name.replace(_simple_name="tobytes")
|
|
@@ -126,6 +130,8 @@ class ReplaceArrayFromstring(Recipe):
|
|
|
126
130
|
return method
|
|
127
131
|
if method.name.simple_name != "fromstring":
|
|
128
132
|
return method
|
|
133
|
+
if method.select is None or not is_assignable_to("array.array", method.select.type):
|
|
134
|
+
return method
|
|
129
135
|
|
|
130
136
|
# Replace fromstring with frombytes
|
|
131
137
|
new_name = method.name.replace(_simple_name="frombytes")
|
|
@@ -23,6 +23,8 @@ from rewrite.marketplace import Python
|
|
|
23
23
|
from rewrite.python.visitor import PythonVisitor
|
|
24
24
|
from rewrite.java.tree import Identifier, FieldAccess
|
|
25
25
|
|
|
26
|
+
from rewrite.python.type_utils import is_of_class_type
|
|
27
|
+
|
|
26
28
|
# Define category path: Python > Migrate > Python 3.8
|
|
27
29
|
_Python38 = [
|
|
28
30
|
*Python,
|
|
@@ -44,12 +46,9 @@ def _is_ast_attribute(field_access: FieldAccess, attr_name: str) -> bool:
|
|
|
44
46
|
if field_access.name.simple_name != attr_name:
|
|
45
47
|
return False
|
|
46
48
|
|
|
47
|
-
#
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return True
|
|
51
|
-
|
|
52
|
-
return False
|
|
49
|
+
# Confirm the target resolves to the `ast` module (by type, so aliased
|
|
50
|
+
# imports work and a same-named variable is not rewritten).
|
|
51
|
+
return is_of_class_type(field_access.target.type, "ast")
|
|
53
52
|
|
|
54
53
|
|
|
55
54
|
def _replace_ast_attribute(field_access: FieldAccess, new_name: str) -> FieldAccess:
|
|
@@ -20,6 +20,8 @@ from rewrite.marketplace import Python
|
|
|
20
20
|
from rewrite.python.visitor import PythonVisitor
|
|
21
21
|
from rewrite.java.tree import Identifier, FieldAccess
|
|
22
22
|
|
|
23
|
+
from rewrite.python.type_utils import is_of_class_type
|
|
24
|
+
|
|
23
25
|
# Define category path: Python > Migrate > Python 3.12
|
|
24
26
|
_Python312 = [
|
|
25
27
|
*Python,
|
|
@@ -53,16 +55,6 @@ CALENDAR_CONSTANTS: Dict[str, str] = {
|
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
|
|
56
|
-
def _is_calendar_access(identifier: Identifier) -> bool:
|
|
57
|
-
"""
|
|
58
|
-
Check if this identifier looks like a calendar module constant access.
|
|
59
|
-
|
|
60
|
-
Without full type attribution, we check for common patterns.
|
|
61
|
-
"""
|
|
62
|
-
# The identifier itself should be a known calendar constant name
|
|
63
|
-
return identifier.simple_name in CALENDAR_CONSTANTS
|
|
64
|
-
|
|
65
|
-
|
|
66
58
|
@categorize(_Python312)
|
|
67
59
|
class ReplaceCalendarConstants(Recipe):
|
|
68
60
|
"""
|
|
@@ -118,9 +110,10 @@ class ReplaceCalendarConstants(Recipe):
|
|
|
118
110
|
if const_name not in CALENDAR_CONSTANTS:
|
|
119
111
|
return field_access
|
|
120
112
|
|
|
121
|
-
#
|
|
113
|
+
# Confirm the target resolves to the `calendar` module (by type,
|
|
114
|
+
# so aliases work and same-named variables are excluded).
|
|
122
115
|
target = field_access.target
|
|
123
|
-
if
|
|
116
|
+
if is_of_class_type(target.type, "calendar"):
|
|
124
117
|
# Replace with uppercase version
|
|
125
118
|
new_name_str = CALENDAR_CONSTANTS[const_name]
|
|
126
119
|
new_identifier = name.replace(_simple_name=new_name_str)
|
|
@@ -16,6 +16,8 @@ from rewrite.marketplace import Python
|
|
|
16
16
|
from rewrite.markers import Markers
|
|
17
17
|
from rewrite.python.preconditions import uses_method
|
|
18
18
|
from rewrite.python.visitor import PythonVisitor
|
|
19
|
+
|
|
20
|
+
from rewrite.python.type_utils import is_of_class_type
|
|
19
21
|
from rewrite.java.support_types import JLeftPadded, JRightPadded, Space
|
|
20
22
|
from rewrite.java.tree import Identifier, Literal, MethodInvocation
|
|
21
23
|
from rewrite.python.tree import NamedArgument
|
|
@@ -81,7 +83,10 @@ class ReplacePlatformPopen(Recipe):
|
|
|
81
83
|
select = method.select
|
|
82
84
|
if not isinstance(select, Identifier):
|
|
83
85
|
return method
|
|
84
|
-
|
|
86
|
+
# Confirm the receiver resolves to the `platform` module (by
|
|
87
|
+
# type, so an aliased import works and a same-named variable is
|
|
88
|
+
# not rewritten into a destructive `subprocess.check_output`).
|
|
89
|
+
if not is_of_class_type(select.type, "platform"):
|
|
85
90
|
return method
|
|
86
91
|
|
|
87
92
|
# Replace platform with subprocess
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Recipes for Pydantic deprecations introduced in recent minor releases.
|
|
3
|
+
|
|
4
|
+
Currently implemented:
|
|
5
|
+
|
|
6
|
+
- ``ReplaceFinalFieldWithClassVar`` (Pydantic 2.11): rewrites model fields
|
|
7
|
+
annotated as ``Final`` with a default value to use ``ClassVar`` instead.
|
|
8
|
+
Before 2.11 such a field was silently treated as a class variable; the typing
|
|
9
|
+
spec says it should be a real field, so 2.11 deprecates the pattern and tells
|
|
10
|
+
users to make the intent explicit with ``ClassVar``. Rewriting to ``ClassVar``
|
|
11
|
+
preserves the current (pre-2.11) class-variable behavior.
|
|
12
|
+
|
|
13
|
+
- ``FindDeprecatedSchemaGenerator`` (Pydantic 2.10): flags the deprecated
|
|
14
|
+
``schema_generator`` option in ``ConfigDict(...)``. There is no public
|
|
15
|
+
replacement yet, so it is flagged for review rather than rewritten.
|
|
16
|
+
|
|
17
|
+
- ``FindDeprecatedJsonEncoders``: flags the deprecated ``json_encoders`` option
|
|
18
|
+
in ``ConfigDict(...)``. Its replacements (serializer decorators or
|
|
19
|
+
``PlainSerializer``) require restructuring, so it is flagged for review.
|
|
20
|
+
|
|
21
|
+
See: https://docs.pydantic.dev/latest/changelog/ and
|
|
22
|
+
https://github.com/pydantic/pydantic/issues/11119
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from typing import Any, List, Optional, cast
|
|
26
|
+
|
|
27
|
+
from rewrite import ExecutionContext, Recipe, TreeVisitor
|
|
28
|
+
from rewrite.category import CategoryDescriptor
|
|
29
|
+
from rewrite.decorators import categorize
|
|
30
|
+
from rewrite.marketplace import Python
|
|
31
|
+
from rewrite.python.visitor import PythonVisitor
|
|
32
|
+
from rewrite.python.add_import import AddImportOptions, maybe_add_import
|
|
33
|
+
from rewrite.python.remove_import import RemoveImportOptions, maybe_remove_import
|
|
34
|
+
from rewrite.python.type_utils import is_assignable_to
|
|
35
|
+
from rewrite.python.tree import NamedArgument, TypeHint, TypeHintedExpression
|
|
36
|
+
from rewrite.java.support_types import JavaType
|
|
37
|
+
from rewrite.java.tree import (
|
|
38
|
+
Assignment,
|
|
39
|
+
ClassDeclaration,
|
|
40
|
+
FieldAccess,
|
|
41
|
+
Identifier,
|
|
42
|
+
J,
|
|
43
|
+
MethodInvocation,
|
|
44
|
+
ParameterizedType,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
from ._markers import mark_deprecated
|
|
48
|
+
|
|
49
|
+
# Define category path: Python > Migrate > Pydantic
|
|
50
|
+
_Pydantic = [
|
|
51
|
+
*Python,
|
|
52
|
+
CategoryDescriptor(display_name="Migrate"),
|
|
53
|
+
CategoryDescriptor(display_name="Pydantic"),
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
# Direct base-class names that mark a class as a Pydantic model — the structural
|
|
57
|
+
# check used when type attribution is unavailable.
|
|
58
|
+
_PYDANTIC_BASES = frozenset({"BaseModel", "RootModel", "BaseSettings"})
|
|
59
|
+
|
|
60
|
+
_SCHEMA_GEN_MSG = (
|
|
61
|
+
"Pydantic 2.10 deprecated the `schema_generator` config option; it was "
|
|
62
|
+
"experimental and is being reworked, and there is no public replacement yet."
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
_JSON_ENCODERS_MSG = (
|
|
66
|
+
"Pydantic deprecated the `json_encoders` config option; use a "
|
|
67
|
+
"`@field_serializer` / `@model_serializer`, or `Annotated[..., "
|
|
68
|
+
"PlainSerializer(...)]`, instead."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _type_simple_name(expr: Any) -> Optional[str]:
|
|
73
|
+
"""Return the trailing simple name of an Identifier or FieldAccess."""
|
|
74
|
+
if isinstance(expr, Identifier):
|
|
75
|
+
return expr.simple_name
|
|
76
|
+
if isinstance(expr, FieldAccess):
|
|
77
|
+
return expr.name.simple_name
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _is_pydantic_model(cls: ClassDeclaration) -> bool:
|
|
82
|
+
"""True if the class is a Pydantic model.
|
|
83
|
+
|
|
84
|
+
First a structural check (direct base named ``BaseModel`` / ``RootModel`` /
|
|
85
|
+
``BaseSettings``) that needs no type attribution. When type attribution is
|
|
86
|
+
available, an assignability check follows the resolved supertype chain, so a
|
|
87
|
+
subclass of a custom intermediate base (e.g. ``class User(MyBase)`` where
|
|
88
|
+
``MyBase(BaseModel)``) is detected. Every Pydantic model ultimately derives
|
|
89
|
+
from ``pydantic.main.BaseModel`` (``RootModel`` and ``BaseSettings``
|
|
90
|
+
included), so a single assignability check suffices.
|
|
91
|
+
"""
|
|
92
|
+
if any(_type_simple_name(base) in _PYDANTIC_BASES for base in (cls.implements or [])):
|
|
93
|
+
return True
|
|
94
|
+
# ClassDeclaration.type is a JavaType.FullyQualified, which the SDK does not
|
|
95
|
+
# model as a JavaType subtype; is_assignable_to accepts it at runtime, so
|
|
96
|
+
# cast to satisfy the type checker (drop once the SDK widens the parameter).
|
|
97
|
+
return is_assignable_to("pydantic.main.BaseModel", cast(Optional[JavaType], cls.type))
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _flag_configdict_option(
|
|
101
|
+
visitor: PythonVisitor, arg: NamedArgument, option_name: str, message: str
|
|
102
|
+
) -> Any:
|
|
103
|
+
"""Flag ``arg`` if it is the named option inside a ``ConfigDict(...)`` call.
|
|
104
|
+
|
|
105
|
+
Returns a marked copy when ``arg`` is the keyword argument ``option_name``
|
|
106
|
+
whose enclosing call is ``ConfigDict``; otherwise returns ``arg`` unchanged.
|
|
107
|
+
"""
|
|
108
|
+
if arg.name.simple_name != option_name:
|
|
109
|
+
return arg
|
|
110
|
+
call = visitor.cursor.first_enclosing(MethodInvocation)
|
|
111
|
+
if call is None or call.name.simple_name != "ConfigDict":
|
|
112
|
+
return arg
|
|
113
|
+
return mark_deprecated(arg, message)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _final_ref(type_hint: Optional[TypeHint]) -> Optional[Any]:
|
|
117
|
+
"""Return the node referencing ``Final`` in the hint, or ``None``.
|
|
118
|
+
|
|
119
|
+
Unwraps ``Final[...]`` to its ``Final`` element and handles both the bare
|
|
120
|
+
name (``Final``, an ``Identifier``) and the qualified form (``typing.Final``,
|
|
121
|
+
a ``FieldAccess``).
|
|
122
|
+
"""
|
|
123
|
+
if type_hint is None:
|
|
124
|
+
return None
|
|
125
|
+
tree = type_hint.type_tree
|
|
126
|
+
if isinstance(tree, ParameterizedType):
|
|
127
|
+
tree = tree.clazz
|
|
128
|
+
if isinstance(tree, Identifier) and tree.simple_name == "Final":
|
|
129
|
+
return tree
|
|
130
|
+
if isinstance(tree, FieldAccess) and tree.name.simple_name == "Final":
|
|
131
|
+
return tree
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _rename_final_to_classvar(ref: Any) -> Any:
|
|
136
|
+
"""Rename a ``Final`` reference node to ``ClassVar``."""
|
|
137
|
+
if isinstance(ref, Identifier):
|
|
138
|
+
return ref.replace(_simple_name="ClassVar")
|
|
139
|
+
# FieldAccess (e.g. ``typing.Final``): rename only the trailing name.
|
|
140
|
+
return ref.replace(_name=ref.name.replace(_simple_name="ClassVar"))
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@categorize(_Pydantic)
|
|
144
|
+
class ReplaceFinalFieldWithClassVar(Recipe):
|
|
145
|
+
"""
|
|
146
|
+
Replace ``Final`` model fields that have a default value with ``ClassVar``.
|
|
147
|
+
|
|
148
|
+
Before Pydantic 2.11, a field annotated as ``Final`` with a default value was
|
|
149
|
+
silently treated as a class variable. The typing specification says it should
|
|
150
|
+
be a real field, so 2.11 deprecates the pattern and recommends making the
|
|
151
|
+
intent explicit with ``ClassVar``. Rewriting ``Final[X] = default`` to
|
|
152
|
+
``ClassVar[X] = default`` preserves the current (pre-2.11) class-variable
|
|
153
|
+
behavior and is what Pydantic recommends.
|
|
154
|
+
|
|
155
|
+
The ``Final`` import is replaced with ``ClassVar``; if ``Final`` is still used
|
|
156
|
+
elsewhere in the file it is kept.
|
|
157
|
+
|
|
158
|
+
Example:
|
|
159
|
+
Before:
|
|
160
|
+
from typing import Final
|
|
161
|
+
from pydantic import BaseModel
|
|
162
|
+
|
|
163
|
+
class User(BaseModel):
|
|
164
|
+
name: Final[str] = "default"
|
|
165
|
+
|
|
166
|
+
After:
|
|
167
|
+
from typing import ClassVar
|
|
168
|
+
from pydantic import BaseModel
|
|
169
|
+
|
|
170
|
+
class User(BaseModel):
|
|
171
|
+
name: ClassVar[str] = "default"
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
@property
|
|
175
|
+
def name(self) -> str:
|
|
176
|
+
return "org.openrewrite.python.migrate.pydantic.ReplaceFinalFieldWithClassVar"
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def display_name(self) -> str:
|
|
180
|
+
return "Replace `Final` model fields with a default with `ClassVar`"
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def description(self) -> str:
|
|
184
|
+
return (
|
|
185
|
+
"Pydantic 2.11 deprecates annotating a model field as `Final` with a "
|
|
186
|
+
"default value (such fields were treated as class variables). Replace "
|
|
187
|
+
"`Final[X] = default` with `ClassVar[X] = default`, which preserves the "
|
|
188
|
+
"existing class-variable behavior and is the replacement Pydantic "
|
|
189
|
+
"recommends."
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
@property
|
|
193
|
+
def tags(self) -> List[str]:
|
|
194
|
+
return ["python", "migration", "pydantic", "2.11"]
|
|
195
|
+
|
|
196
|
+
def editor(self) -> TreeVisitor[Any, ExecutionContext]:
|
|
197
|
+
class Visitor(PythonVisitor[ExecutionContext]):
|
|
198
|
+
def visit_assignment( # type: ignore[override]
|
|
199
|
+
self, assignment: Assignment, p: ExecutionContext
|
|
200
|
+
) -> Optional[J]:
|
|
201
|
+
a = cast(Assignment, super().visit_assignment(assignment, p))
|
|
202
|
+
|
|
203
|
+
var = a.variable
|
|
204
|
+
if not isinstance(var, TypeHintedExpression):
|
|
205
|
+
return a
|
|
206
|
+
type_hint = var.type_hint
|
|
207
|
+
ref = _final_ref(type_hint)
|
|
208
|
+
if ref is None:
|
|
209
|
+
return a
|
|
210
|
+
|
|
211
|
+
cls = self.cursor.first_enclosing(ClassDeclaration)
|
|
212
|
+
if cls is None or not _is_pydantic_model(cls):
|
|
213
|
+
return a
|
|
214
|
+
|
|
215
|
+
# Rewrite the annotation: Final -> ClassVar (keeping any [X]).
|
|
216
|
+
tree = type_hint.type_tree
|
|
217
|
+
if isinstance(tree, ParameterizedType):
|
|
218
|
+
new_tree = tree.replace(_clazz=_rename_final_to_classvar(tree.clazz))
|
|
219
|
+
else:
|
|
220
|
+
new_tree = _rename_final_to_classvar(tree)
|
|
221
|
+
new_var = var.replace(_type_hint=type_hint.replace(_type_tree=new_tree))
|
|
222
|
+
|
|
223
|
+
# Only the bare-name form (`from typing import Final`) needs import
|
|
224
|
+
# bookkeeping; the qualified `typing.Final` form does not.
|
|
225
|
+
if isinstance(ref, Identifier):
|
|
226
|
+
maybe_add_import(
|
|
227
|
+
self,
|
|
228
|
+
AddImportOptions(
|
|
229
|
+
module="typing", name="ClassVar", only_if_referenced=False
|
|
230
|
+
),
|
|
231
|
+
)
|
|
232
|
+
maybe_remove_import(
|
|
233
|
+
self,
|
|
234
|
+
RemoveImportOptions(
|
|
235
|
+
module="typing", name="Final", only_if_unused=True
|
|
236
|
+
),
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
return a.replace(_variable=new_var)
|
|
240
|
+
|
|
241
|
+
return Visitor()
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@categorize(_Pydantic)
|
|
245
|
+
class FindDeprecatedSchemaGenerator(Recipe):
|
|
246
|
+
"""
|
|
247
|
+
Flag the ``schema_generator`` option in a ``ConfigDict(...)`` call.
|
|
248
|
+
|
|
249
|
+
Pydantic 2.10 deprecated the ``schema_generator`` config option (it was an
|
|
250
|
+
experimental API that is being reworked). There is no public replacement
|
|
251
|
+
yet, so this recipe flags its use for review rather than rewriting it.
|
|
252
|
+
|
|
253
|
+
Example:
|
|
254
|
+
Before:
|
|
255
|
+
from pydantic import BaseModel, ConfigDict
|
|
256
|
+
|
|
257
|
+
class User(BaseModel):
|
|
258
|
+
model_config = ConfigDict(schema_generator=MyGen)
|
|
259
|
+
|
|
260
|
+
After (option marked for review):
|
|
261
|
+
from pydantic import BaseModel, ConfigDict
|
|
262
|
+
|
|
263
|
+
class User(BaseModel):
|
|
264
|
+
model_config = ConfigDict(schema_generator=MyGen) # flagged
|
|
265
|
+
"""
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def name(self) -> str:
|
|
269
|
+
return "org.openrewrite.python.migrate.pydantic.FindDeprecatedSchemaGenerator"
|
|
270
|
+
|
|
271
|
+
@property
|
|
272
|
+
def display_name(self) -> str:
|
|
273
|
+
return "Find deprecated `schema_generator` config option"
|
|
274
|
+
|
|
275
|
+
@property
|
|
276
|
+
def description(self) -> str:
|
|
277
|
+
return (
|
|
278
|
+
"Pydantic 2.10 deprecated the `schema_generator` option in "
|
|
279
|
+
"`ConfigDict`. It has no public replacement yet, so this recipe "
|
|
280
|
+
"flags its use for review."
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
@property
|
|
284
|
+
def tags(self) -> List[str]:
|
|
285
|
+
return ["python", "migration", "pydantic", "2.10"]
|
|
286
|
+
|
|
287
|
+
def editor(self) -> TreeVisitor[Any, ExecutionContext]:
|
|
288
|
+
class Visitor(PythonVisitor[ExecutionContext]):
|
|
289
|
+
def visit_named_argument( # type: ignore[override]
|
|
290
|
+
self, named_argument: NamedArgument, p: ExecutionContext
|
|
291
|
+
) -> Optional[J]:
|
|
292
|
+
arg = cast(
|
|
293
|
+
NamedArgument, super().visit_named_argument(named_argument, p)
|
|
294
|
+
)
|
|
295
|
+
return _flag_configdict_option(
|
|
296
|
+
self, arg, "schema_generator", _SCHEMA_GEN_MSG
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
return Visitor()
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
@categorize(_Pydantic)
|
|
303
|
+
class FindDeprecatedJsonEncoders(Recipe):
|
|
304
|
+
"""
|
|
305
|
+
Flag the ``json_encoders`` option in a ``ConfigDict(...)`` call.
|
|
306
|
+
|
|
307
|
+
Pydantic deprecated the ``json_encoders`` config option (it carries
|
|
308
|
+
performance and implementation overhead). The recommended replacements are
|
|
309
|
+
the ``@field_serializer`` / ``@model_serializer`` decorators or an
|
|
310
|
+
``Annotated[..., PlainSerializer(...)]`` type, which are restructurings
|
|
311
|
+
rather than a mechanical swap, so this recipe flags its use for review.
|
|
312
|
+
|
|
313
|
+
Example:
|
|
314
|
+
Before:
|
|
315
|
+
from pydantic import BaseModel, ConfigDict
|
|
316
|
+
|
|
317
|
+
class User(BaseModel):
|
|
318
|
+
model_config = ConfigDict(json_encoders={int: str})
|
|
319
|
+
|
|
320
|
+
After (option marked for review):
|
|
321
|
+
from pydantic import BaseModel, ConfigDict
|
|
322
|
+
|
|
323
|
+
class User(BaseModel):
|
|
324
|
+
model_config = ConfigDict(json_encoders={int: str}) # flagged
|
|
325
|
+
"""
|
|
326
|
+
|
|
327
|
+
@property
|
|
328
|
+
def name(self) -> str:
|
|
329
|
+
return "org.openrewrite.python.migrate.pydantic.FindDeprecatedJsonEncoders"
|
|
330
|
+
|
|
331
|
+
@property
|
|
332
|
+
def display_name(self) -> str:
|
|
333
|
+
return "Find deprecated `json_encoders` config option"
|
|
334
|
+
|
|
335
|
+
@property
|
|
336
|
+
def description(self) -> str:
|
|
337
|
+
return (
|
|
338
|
+
"Pydantic deprecated the `json_encoders` option in `ConfigDict`. Its "
|
|
339
|
+
"replacements (`@field_serializer` / `@model_serializer` or "
|
|
340
|
+
"`Annotated[..., PlainSerializer(...)]`) require restructuring, so "
|
|
341
|
+
"this recipe flags its use for review."
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def tags(self) -> List[str]:
|
|
346
|
+
return ["python", "migration", "pydantic"]
|
|
347
|
+
|
|
348
|
+
def editor(self) -> TreeVisitor[Any, ExecutionContext]:
|
|
349
|
+
class Visitor(PythonVisitor[ExecutionContext]):
|
|
350
|
+
def visit_named_argument( # type: ignore[override]
|
|
351
|
+
self, named_argument: NamedArgument, p: ExecutionContext
|
|
352
|
+
) -> Optional[J]:
|
|
353
|
+
arg = cast(
|
|
354
|
+
NamedArgument, super().visit_named_argument(named_argument, p)
|
|
355
|
+
)
|
|
356
|
+
return _flag_configdict_option(
|
|
357
|
+
self, arg, "json_encoders", _JSON_ENCODERS_MSG
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
return Visitor()
|