{"id":9155,"library":"openrewrite","title":"OpenRewrite Python","description":"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.","status":"active","version":"8.79.5","language":"en","source_language":"en","source_url":"https://github.com/openrewrite/rewrite.git","tags":["code refactoring","static analysis","AST transformation","developer tool","linting"],"install":[{"cmd":"pip install openrewrite","lang":"bash","label":"Install Python package"}],"dependencies":[{"reason":"Requires Python 3.10 or higher.","package":"python","optional":false},{"reason":"Requires Java 11+ runtime for the underlying OpenRewrite CLI engine to function.","package":"java-runtime","optional":false},{"reason":"The Python library wraps the OpenRewrite CLI, which must be installed and available in the system PATH.","package":"openrewrite-cli","optional":false}],"imports":[{"symbol":"Recipe","correct":"from openrewrite.recipe import Recipe"},{"symbol":"rewrite_run","correct":"from openrewrite.run import rewrite_run"},{"symbol":"SourceFile","correct":"from openrewrite.tree import SourceFile"},{"note":"The `InMemoryExecutionContext` class was moved to `openrewrite.api` in recent versions.","wrong":"from openrewrite.run import InMemoryExecutionContext","symbol":"InMemoryExecutionContext","correct":"from openrewrite.api import InMemoryExecutionContext"}],"quickstart":{"code":"import os\nimport pathlib\nfrom openrewrite.recipe import Recipe\nfrom openrewrite.run import rewrite_run\nfrom openrewrite.tree import SourceFile\n\n# Create a dummy Python file for the recipe to process\ndummy_file_path = pathlib.Path(\"example_for_rewrite.py\")\nwith open(dummy_file_path, \"w\") as f:\n    f.write(\"print('Hello, OpenRewrite!')\\n\")\n    f.write(\"import os # This import will be processed by a real recipe\\n\")\n\nclass MyPythonRecipe(Recipe):\n    def get_display_name(self) -> str:\n        return \"My First Python Recipe\"\n\n    def get_description(self) -> str:\n        return \"A simple recipe that does nothing, demonstrating the API.\"\n\n    def visit_source_file(self, source_file: SourceFile, execution_context) -> SourceFile:\n        # In a real recipe, you would inspect and modify the source_file here.\n        # For this example, we just print the path and return it unchanged.\n        print(f\"[Recipe] Visiting file: {source_file.get_source_path()}\")\n        return source_file\n\n# --- IMPORTANT: Ensure OpenRewrite CLI and Java 11+ are installed and in PATH ---\n# The `rewrite_run` function calls the `rewrite` CLI tool internally.\n# If you encounter FileNotFoundError or Java-related errors, check your setup.\n\ntry:\n    print(f\"Running recipe on {dummy_file_path} (dry_run=True)...\")\n    results = rewrite_run(\n        [MyPythonRecipe()],\n        [str(dummy_file_path)], # List of file paths to process\n        dry_run=True, # Set to False to apply changes directly to files\n        # For more complex scenarios, you might use an InMemoryExecutionContext:\n        # execution_context=InMemoryExecutionContext()\n    )\n    print(\"Recipe run complete.\")\n    if not results:\n        print(\"No changes detected (as expected for this basic recipe in dry_run mode).\")\n    else:\n        print(\"Changes detected (this might indicate an issue with the dummy recipe or environment setup if unexpected).\")\n\nexcept Exception as e:\n    print(f\"An error occurred during rewrite_run. Please ensure OpenRewrite CLI and Java 11+ are installed and in PATH.\\nError: {e}\")\n\nfinally:\n    # Clean up the dummy file\n    if dummy_file_path.exists():\n        os.remove(dummy_file_path)\n        print(f\"Cleaned up {dummy_file_path}.\")","lang":"python","description":"This quickstart demonstrates how to define a basic OpenRewrite Python recipe and execute it using `rewrite_run`. It creates a temporary Python file, applies a no-op recipe in dry-run mode, and then cleans up. Critical prerequisites are a Java 11+ Runtime Environment and the OpenRewrite CLI, which the Python library orchestrates."},"warnings":[{"fix":"Install Java 11+ from Adoptium or your preferred vendor, and ensure the `java` executable is in your system PATH.","message":"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.","severity":"breaking","affected_versions":"All versions"},{"fix":"Install the OpenRewrite CLI (e.g., `brew install openrewrite/rewrite/rewrite` on macOS, or download from the official documentation) and verify it's in your system PATH by running `rewrite --version`.","message":"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.","severity":"breaking","affected_versions":"All versions"},{"fix":"Use transformation methods like `node.with_name(...)` or construct new nodes and always return the transformed `SourceFile` or `Tree` element from `visit_...` methods in your recipes.","message":"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.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Install 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.","cause":"The OpenRewrite CLI executable ('rewrite') is not found in the system's PATH. The Python library tries to invoke this external command.","error":"FileNotFoundError: [Errno 2] No such file or directory: 'rewrite'"},{"fix":"Install 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.","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+).","error":"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"},{"fix":"Ensure 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`.","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.","error":"TypeError: 'NoneType' object is not callable"}]}