You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Enable Java recipes for Python via ty LSP type attribution (#6593)
* Enable Java recipes for Python via ty LSP type attribution
This change enables 7 Java building-block recipes to work on Python code:
- FindMethods
- FindTypes
- ChangeMethodName
- ChangeType
- DeleteMethodArgument
- ReorderMethodArguments
- ChangePackage (via recipes.csv entry)
Key changes:
1. Type attribution via ty LSP client
- New TyLspClient that runs `ty server` as a long-lived subprocess
- Communicates via LSP protocol over stdin/stdout
- Provides hover-based type information for Python code
2. Enhanced PythonTypeMapping
- Maps Python types to JavaType equivalents for MethodMatcher
- Parses method signatures from ty hover responses to extract
parameter names and types
- Handles declaring type resolution for both builtins and modules
3. Python-specific import recipes
- AddImport: Add Python imports with proper formatting
- RemoveImport: Remove unused Python imports
- ChangeImport: Migrate imports between modules
4. Dependency workspace support
- DependencyWorkspace.java manages cached venvs for type resolution
- LRU cache with automatic cleanup of evicted entries
- Enables type attribution for external packages
5. Integration tests for all 7 recipes
- Tests use builtin types (str, list) that work without dependencies
- Validates both positive matches and negative cases
Technical notes:
- ty is required for full type attribution; without it, recipes fall back
to placeholder parameter names
- Parameter names are parsed from ty hover responses like:
`def split(sep: str | None = ..., maxsplit: SupportsIndex = ...) -> list[str]`
maven,org.openrewrite:rewrite-java,org.openrewrite.java.ReorderMethodArguments,Reorder method arguments,Reorder method arguments into the specified order.,1,,JavaScript,,Basic building blocks for transforming JavaScript code.,"[{""name"":""methodPattern"",""type"":""String"",""displayName"":""Method pattern"",""description"":""A [method pattern](https://docs.openrewrite.org/reference/method-patterns) is used to find matching method invocations. For example, to find all method invocations in the Guava library, use the pattern: `com.google.common..*#*(..)`.<br/><br/>The pattern format is `<PACKAGE>#<METHOD_NAME>(<ARGS>)`. <br/><br/>`..*` includes all subpackages of `com.google.common`. <br/>`*(..)` matches any method name with any number of arguments. <br/><br/>For more specific queries, like Guava's `ImmutableMap`, use `com.google.common.collect.ImmutableMap#*(..)` to narrow down the results."",""example"":""com.yourorg.A foo(String, Integer, Integer)"",""required"":true},{""name"":""newParameterNames"",""type"":""String[]"",""displayName"":""New parameter names"",""description"":""An array of parameter names that indicates the new order in which those arguments should be arranged."",""example"":""[foo, bar, baz]"",""required"":true},{""name"":""oldParameterNames"",""type"":""String[]"",""displayName"":""Old parameter names"",""description"":""If the original method signature is not type-attributed, this is an optional list that indicates the original order in which the arguments were arranged."",""example"":""[baz, bar, foo]""},{""name"":""ignoreDefinition"",""type"":""Boolean"",""displayName"":""Ignore type definition"",""description"":""When set to `true` the definition of the old type will be left untouched. This is useful when you're replacing usage of a class but don't want to rename it.""},{""name"":""matchOverrides"",""type"":""Boolean"",""displayName"":""Match on overrides"",""description"":""When enabled, find methods that are overrides of the method pattern.""}]",
105
105
maven,org.openrewrite:rewrite-java,org.openrewrite.java.search.FindMethods,Find method usages,Find method calls by pattern.,1,Search,JavaScript,,Basic building blocks for transforming JavaScript code.,"[{""name"":""methodPattern"",""type"":""String"",""displayName"":""Method pattern"",""description"":""A [method pattern](https://docs.openrewrite.org/reference/method-patterns) is used to find matching method invocations. For example, to find all method invocations in the Guava library, use the pattern: `com.google.common..*#*(..)`.<br/><br/>The pattern format is `<PACKAGE>#<METHOD_NAME>(<ARGS>)`. <br/><br/>`..*` includes all subpackages of `com.google.common`. <br/>`*(..)` matches any method name with any number of arguments. <br/><br/>For more specific queries, like Guava's `ImmutableMap`, use `com.google.common.collect.ImmutableMap#*(..)` to narrow down the results."",""example"":""java.util.List add(..)"",""required"":true},{""name"":""matchOverrides"",""type"":""Boolean"",""displayName"":""Match on overrides"",""description"":""When enabled, find methods that are overrides of the method pattern.""}]","[{""name"":""org.openrewrite.java.table.MethodCalls"",""displayName"":""Method calls"",""description"":""The text of matching method invocations."",""columns"":[{""name"":""sourceFile"",""type"":""String"",""displayName"":""Source file"",""description"":""The source file that the method call occurred in.""},{""name"":""method"",""type"":""String"",""displayName"":""Method call"",""description"":""The text of the method call.""},{""name"":""className"",""type"":""String"",""displayName"":""Class name"",""description"":""The class name of the method call.""},{""name"":""methodName"",""type"":""String"",""displayName"":""Method name"",""description"":""The method name of the method call.""},{""name"":""argumentTypes"",""type"":""String"",""displayName"":""Argument types"",""description"":""The argument types of the method call.""}]}]"
106
106
maven,org.openrewrite:rewrite-java,org.openrewrite.java.search.FindTypes,Find types,Find type references by name.,1,Search,JavaScript,,Basic building blocks for transforming JavaScript code.,"[{""name"":""fullyQualifiedTypeName"",""type"":""String"",""displayName"":""Fully-qualified type name"",""description"":""A fully-qualified type name, that is used to find matching type references. Supports glob expressions. `java..*` finds every type from every subpackage of the `java` package."",""example"":""java.util.List"",""required"":true},{""name"":""checkAssignability"",""type"":""Boolean"",""displayName"":""Check for assignability"",""description"":""When enabled, find type references that are assignable to the provided type.""}]","[{""name"":""org.openrewrite.java.table.TypeUses"",""displayName"":""Type uses"",""description"":""The source code of matching type uses."",""columns"":[{""name"":""sourceFile"",""type"":""String"",""displayName"":""Source file"",""description"":""The source file that the method call occurred in.""},{""name"":""code"",""type"":""String"",""displayName"":""Source"",""description"":""The source code of the type use.""},{""name"":""concreteType"",""type"":""String"",""displayName"":""Concrete type"",""description"":""The concrete type in use, which may be a subtype of a searched type.""}]}]"
107
+
maven,org.openrewrite:rewrite-java,org.openrewrite.java.ChangeMethodName,Change method name,Rename a method.,1,,Python,,Basic building blocks for transforming Python code.,"[{""name"":""methodPattern"",""type"":""String"",""displayName"":""Method pattern"",""description"":""A [method pattern](https://docs.openrewrite.org/reference/method-patterns) is used to find matching method invocations. For example, to find all method invocations in the Guava library, use the pattern: `com.google.common..*#*(..)`.<br/><br/>The pattern format is `<PACKAGE>#<METHOD_NAME>(<ARGS>)`. <br/><br/>`..*` includes all subpackages of `com.google.common`. <br/>`*(..)` matches any method name with any number of arguments. <br/><br/>For more specific queries, like Guava's `ImmutableMap`, use `com.google.common.collect.ImmutableMap#*(..)` to narrow down the results."",""example"":""org.mockito.Matchers anyVararg()"",""required"":true},{""name"":""newMethodName"",""type"":""String"",""displayName"":""New method name"",""description"":""The method name that will replace the existing name."",""example"":""any"",""required"":true},{""name"":""matchOverrides"",""type"":""Boolean"",""displayName"":""Match on overrides"",""description"":""When enabled, find methods that are overrides of the method pattern.""},{""name"":""ignoreDefinition"",""type"":""Boolean"",""displayName"":""Ignore type definition"",""description"":""When set to `true` the definition of the old type will be left untouched. This is useful when you're replacing usage of a class but don't want to rename it.""}]",
108
+
maven,org.openrewrite:rewrite-java,org.openrewrite.java.ChangePackage,Rename package name,"A recipe that will rename a package name in package statements, imports, and fully-qualified types.",1,,Python,,Basic building blocks for transforming Python code.,"[{""name"":""oldPackageName"",""type"":""String"",""displayName"":""Old package name"",""description"":""The package name to replace."",""example"":""com.yourorg.foo"",""required"":true},{""name"":""newPackageName"",""type"":""String"",""displayName"":""New package name"",""description"":""New package name to replace the old package name with."",""example"":""com.yourorg.bar"",""required"":true},{""name"":""recursive"",""type"":""Boolean"",""displayName"":""Recursive"",""description"":""Recursively change subpackage names""}]",
109
+
maven,org.openrewrite:rewrite-java,org.openrewrite.java.ChangeType,Change type,Change a given type to another.,1,,Python,,Basic building blocks for transforming Python code.,"[{""name"":""oldFullyQualifiedTypeName"",""type"":""String"",""displayName"":""Old fully-qualified type name"",""description"":""Fully-qualified class name of the original type."",""example"":""org.junit.Assume"",""required"":true},{""name"":""newFullyQualifiedTypeName"",""type"":""String"",""displayName"":""New fully-qualified type name"",""description"":""Fully-qualified class name of the replacement type, or the name of a primitive such as \""int\"". The `OuterClassName$NestedClassName` naming convention should be used for nested classes."",""example"":""org.junit.jupiter.api.Assumptions"",""required"":true},{""name"":""ignoreDefinition"",""type"":""Boolean"",""displayName"":""Ignore type definition"",""description"":""When set to `true` the definition of the old type will be left untouched. This is useful when you're replacing usage of a class but don't want to rename it.""}]",
110
+
maven,org.openrewrite:rewrite-java,org.openrewrite.java.DeleteMethodArgument,Delete method argument,Delete an argument from method invocations.,1,,Python,,Basic building blocks for transforming Python code.,"[{""name"":""methodPattern"",""type"":""String"",""displayName"":""Method pattern"",""description"":""A [method pattern](https://docs.openrewrite.org/reference/method-patterns) is used to find matching method invocations. For example, to find all method invocations in the Guava library, use the pattern: `com.google.common..*#*(..)`.<br/><br/>The pattern format is `<PACKAGE>#<METHOD_NAME>(<ARGS>)`. <br/><br/>`..*` includes all subpackages of `com.google.common`. <br/>`*(..)` matches any method name with any number of arguments. <br/><br/>For more specific queries, like Guava's `ImmutableMap`, use `com.google.common.collect.ImmutableMap#*(..)` to narrow down the results."",""example"":""com.yourorg.A foo(int, int)"",""required"":true},{""name"":""argumentIndex"",""type"":""int"",""displayName"":""Argument index"",""description"":""A zero-based index that indicates which argument will be removed from the method invocation."",""example"":""0"",""required"":true,""value"":0}]",
111
+
maven,org.openrewrite:rewrite-java,org.openrewrite.java.ReorderMethodArguments,Reorder method arguments,Reorder method arguments into the specified order.,1,,Python,,Basic building blocks for transforming Python code.,"[{""name"":""methodPattern"",""type"":""String"",""displayName"":""Method pattern"",""description"":""A [method pattern](https://docs.openrewrite.org/reference/method-patterns) is used to find matching method invocations. For example, to find all method invocations in the Guava library, use the pattern: `com.google.common..*#*(..)`.<br/><br/>The pattern format is `<PACKAGE>#<METHOD_NAME>(<ARGS>)`. <br/><br/>`..*` includes all subpackages of `com.google.common`. <br/>`*(..)` matches any method name with any number of arguments. <br/><br/>For more specific queries, like Guava's `ImmutableMap`, use `com.google.common.collect.ImmutableMap#*(..)` to narrow down the results."",""example"":""com.yourorg.A foo(String, Integer, Integer)"",""required"":true},{""name"":""newParameterNames"",""type"":""String[]"",""displayName"":""New parameter names"",""description"":""An array of parameter names that indicates the new order in which those arguments should be arranged."",""example"":""[foo, bar, baz]"",""required"":true},{""name"":""oldParameterNames"",""type"":""String[]"",""displayName"":""Old parameter names"",""description"":""If the original method signature is not type-attributed, this is an optional list that indicates the original order in which the arguments were arranged."",""example"":""[baz, bar, foo]""},{""name"":""ignoreDefinition"",""type"":""Boolean"",""displayName"":""Ignore type definition"",""description"":""When set to `true` the definition of the old type will be left untouched. This is useful when you're replacing usage of a class but don't want to rename it.""},{""name"":""matchOverrides"",""type"":""Boolean"",""displayName"":""Match on overrides"",""description"":""When enabled, find methods that are overrides of the method pattern.""}]",
112
+
maven,org.openrewrite:rewrite-java,org.openrewrite.java.search.FindMethods,Find method usages,Find method calls by pattern.,1,Search,Python,,Basic building blocks for transforming Python code.,"[{""name"":""methodPattern"",""type"":""String"",""displayName"":""Method pattern"",""description"":""A [method pattern](https://docs.openrewrite.org/reference/method-patterns) is used to find matching method invocations. For example, to find all method invocations in the Guava library, use the pattern: `com.google.common..*#*(..)`.<br/><br/>The pattern format is `<PACKAGE>#<METHOD_NAME>(<ARGS>)`. <br/><br/>`..*` includes all subpackages of `com.google.common`. <br/>`*(..)` matches any method name with any number of arguments. <br/><br/>For more specific queries, like Guava's `ImmutableMap`, use `com.google.common.collect.ImmutableMap#*(..)` to narrow down the results."",""example"":""java.util.List add(..)"",""required"":true},{""name"":""matchOverrides"",""type"":""Boolean"",""displayName"":""Match on overrides"",""description"":""When enabled, find methods that are overrides of the method pattern.""}]","[{""name"":""org.openrewrite.java.table.MethodCalls"",""displayName"":""Method calls"",""description"":""The text of matching method invocations."",""columns"":[{""name"":""sourceFile"",""type"":""String"",""displayName"":""Source file"",""description"":""The source file that the method call occurred in.""},{""name"":""method"",""type"":""String"",""displayName"":""Method call"",""description"":""The text of the method call.""},{""name"":""className"",""type"":""String"",""displayName"":""Class name"",""description"":""The class name of the method call.""},{""name"":""methodName"",""type"":""String"",""displayName"":""Method name"",""description"":""The method name of the method call.""},{""name"":""argumentTypes"",""type"":""String"",""displayName"":""Argument types"",""description"":""The argument types of the method call.""}]}]"
113
+
maven,org.openrewrite:rewrite-java,org.openrewrite.java.search.FindTypes,Find types,Find type references by name.,1,Search,Python,,Basic building blocks for transforming Python code.,"[{""name"":""fullyQualifiedTypeName"",""type"":""String"",""displayName"":""Fully-qualified type name"",""description"":""A fully-qualified type name, that is used to find matching type references. Supports glob expressions. `java..*` finds every type from every subpackage of the `java` package."",""example"":""java.util.List"",""required"":true},{""name"":""checkAssignability"",""type"":""Boolean"",""displayName"":""Check for assignability"",""description"":""When enabled, find type references that are assignable to the provided type.""}]","[{""name"":""org.openrewrite.java.table.TypeUses"",""displayName"":""Type uses"",""description"":""The source code of matching type uses."",""columns"":[{""name"":""sourceFile"",""type"":""String"",""displayName"":""Source file"",""description"":""The source file that the method call occurred in.""},{""name"":""code"",""type"":""String"",""displayName"":""Source"",""description"":""The source code of the type use.""},{""name"":""concreteType"",""type"":""String"",""displayName"":""Concrete type"",""description"":""The concrete type in use, which may be a subtype of a searched type.""}]}]"
0 commit comments