OpenRewrite Python
OpenRewrite provides a powerful framework for automated code refactoring and static analysis. The `openrewrite` Python library offers Python bindings and a Command Line Interface (CLI) to leverage the core OpenRewrite engine, enabling programmatic application of recipes for code transformation in Python projects. It is currently at version 8.79.5 and follows the core OpenRewrite project's release cadence.
Common errors
-
FileNotFoundError: [Errno 2] No such file or directory: 'rewrite'
cause The OpenRewrite CLI executable ('rewrite') is not found in the system's PATH. The Python library tries to invoke this external command.fixInstall the OpenRewrite CLI tool from the official documentation (e.g., `brew install openrewrite/rewrite/rewrite` on macOS) and ensure its installation directory is added to your system's PATH environment variable. -
java.lang.UnsupportedClassVersionError: org/openrewrite/cli/RewriteCli has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0
cause An older Java Runtime Environment (JRE) is being used (e.g., Java 8, which is class file version 52.0) while OpenRewrite CLI requires Java 11 or higher (class file version 55.0+).fixInstall Java 11 or a newer version, and configure your system to use it as the default JRE, especially for the `java` command used by the `rewrite` CLI. -
TypeError: 'NoneType' object is not callable
cause This often occurs in recipes when a visitor method (e.g., `visit_source_file`) implicitly or explicitly returns `None`, and subsequent operations attempt to call methods on the non-existent (None) node.fixEnsure that all `visit_...` methods in your custom `Recipe` always explicitly return a `SourceFile` or `Tree` object (even if it's the original, unmodified one) and never `None`.
Warnings
- breaking The OpenRewrite Python library requires a Java 11 or newer Runtime Environment (JRE) to be installed and available on your system PATH. Without it, the underlying OpenRewrite CLI engine cannot execute.
- breaking The `openrewrite` Python library acts as a wrapper for the OpenRewrite CLI. This CLI must be separately installed and present in your system PATH for `rewrite_run` and other functions to operate.
- gotcha OpenRewrite operates on immutable Abstract Syntax Trees (ASTs). When developing recipes, you must always return new, transformed AST nodes rather than attempting to modify existing nodes in-place.
Install
-
pip install openrewrite
Imports
- Recipe
from openrewrite.recipe import Recipe
- rewrite_run
from openrewrite.run import rewrite_run
- SourceFile
from openrewrite.tree import SourceFile
- InMemoryExecutionContext
from openrewrite.run import InMemoryExecutionContext
from openrewrite.api import InMemoryExecutionContext
Quickstart
import os
import pathlib
from openrewrite.recipe import Recipe
from openrewrite.run import rewrite_run
from openrewrite.tree import SourceFile
# Create a dummy Python file for the recipe to process
dummy_file_path = pathlib.Path("example_for_rewrite.py")
with open(dummy_file_path, "w") as f:
f.write("print('Hello, OpenRewrite!')\n")
f.write("import os # This import will be processed by a real recipe\n")
class MyPythonRecipe(Recipe):
def get_display_name(self) -> str:
return "My First Python Recipe"
def get_description(self) -> str:
return "A simple recipe that does nothing, demonstrating the API."
def visit_source_file(self, source_file: SourceFile, execution_context) -> SourceFile:
# In a real recipe, you would inspect and modify the source_file here.
# For this example, we just print the path and return it unchanged.
print(f"[Recipe] Visiting file: {source_file.get_source_path()}")
return source_file
# --- IMPORTANT: Ensure OpenRewrite CLI and Java 11+ are installed and in PATH ---
# The `rewrite_run` function calls the `rewrite` CLI tool internally.
# If you encounter FileNotFoundError or Java-related errors, check your setup.
try:
print(f"Running recipe on {dummy_file_path} (dry_run=True)...")
results = rewrite_run(
[MyPythonRecipe()],
[str(dummy_file_path)], # List of file paths to process
dry_run=True, # Set to False to apply changes directly to files
# For more complex scenarios, you might use an InMemoryExecutionContext:
# execution_context=InMemoryExecutionContext()
)
print("Recipe run complete.")
if not results:
print("No changes detected (as expected for this basic recipe in dry_run mode).")
else:
print("Changes detected (this might indicate an issue with the dummy recipe or environment setup if unexpected).")
except Exception as e:
print(f"An error occurred during rewrite_run. Please ensure OpenRewrite CLI and Java 11+ are installed and in PATH.\nError: {e}")
finally:
# Clean up the dummy file
if dummy_file_path.exists():
os.remove(dummy_file_path)
print(f"Cleaned up {dummy_file_path}.")