OpenRewrite automated refactoring for Python.
Project description
OpenRewrite Python
OpenRewrite automated refactoring for Python source code. This package provides the recipe framework, the Python Lossless Semantic Tree (LST), and the testing helpers you use to author and test Python recipes.
Installation
pip install openrewrite
How it works
OpenRewrite for Python uses a split JVM/Python architecture. You author and unit-test recipes in pure Python, but running a recipe against a real codebase is orchestrated by the JVM runtime via the Moderne CLI (with Python support configured) over an RPC bridge. There is no standalone, in-process Python parser entry point.
Quick start
The fastest way to author and exercise a recipe is the test harness, which parses a before snippet, runs your recipe, and asserts the result matches after:
from rewrite.test import RecipeSpec, python
def test_renames_a_call():
spec = RecipeSpec(recipe=RenameFunctionCall(
old_name="assertEquals",
new_name="assertEqual",
))
spec.rewrite_run(
python("assertEquals(a, b)", "assertEqual(a, b)"),
)
python(before, after) asserts a change; python(before) asserts no change.
Writing a recipe
A recipe is a @dataclass subclassing Recipe that returns a visitor from
editor(). Each option must have a default value, or the recipe cannot be
discovered or run.
from dataclasses import dataclass, field
from rewrite import ExecutionContext, Recipe, TreeVisitor, option
from rewrite.java import J
from rewrite.java.tree import MethodInvocation
from rewrite.python.visitor import PythonVisitor
@dataclass
class RenameFunctionCall(Recipe):
"""Rename calls to a function from one name to another."""
old_name: str = field(default="", metadata=option(
display_name="Old function name",
description="The name of the function whose calls should be renamed.",
example="assertEquals",
))
new_name: str = field(default="", metadata=option(
display_name="New function name",
description="The name to rename matching calls to.",
example="assertEqual",
))
@property
def name(self) -> str:
return "com.yourorg.RenameFunctionCall"
@property
def display_name(self) -> str:
return "Rename a function call"
@property
def description(self) -> str:
return "Rename calls to a function from one name to another."
def editor(self) -> TreeVisitor[J, ExecutionContext]:
old_name = self.old_name
new_name = self.new_name
class Visitor(PythonVisitor[ExecutionContext]):
def visit_method_invocation(self, method: MethodInvocation, p: ExecutionContext) -> J:
method = super().visit_method_invocation(method, p)
if method.name.simple_name == old_name:
renamed = method.name.replace(_simple_name=new_name)
return method.replace(_name=renamed)
return method
return Visitor()
Returning None from a visit method removes the node entirely — which is how
recipes delete code.
Running recipes with the Moderne CLI
Expose an activate() function so the CLI can discover your recipe:
from rewrite.marketplace import RecipeMarketplace, Python
def activate(marketplace: RecipeMarketplace) -> None:
marketplace.install(RenameFunctionCall, Python)
Then install and run it against a repository whose Python LSTs you've built,
passing each option as a -P parameter:
# From your recipe project directory, install it into the CLI's marketplace:
mod config recipes pip install .
# Build the LSTs for the repository you want to refactor, then run the recipe:
mod build /path/to/your/repo
mod run /path/to/your/repo --recipe=com.yourorg.RenameFunctionCall \
-P old_name=assertEquals -P new_name=assertEqual
Learn more
- Python Recipe Starter — a complete project with example recipes, tests, and CI to clone and build on
- Writing a Python refactoring recipe — step-by-step authoring guide
rewrite-pythonmodule — the Python LST, parser, and built-in recipes- docs.openrewrite.org — full OpenRewrite documentation
License
Moderne Source Available License - see LICENSE.md
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file openrewrite-8.86.0.dev20260625123347.tar.gz.
File metadata
- Download URL: openrewrite-8.86.0.dev20260625123347.tar.gz
- Upload date:
- Size: 319.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5aeff8cd892d8cfd25492ac340ba4b407998db69d088a5c53a1985418b8b55e4
|
|
| MD5 |
bd9cb7b9daefe0cd5e327198a1501965
|
|
| BLAKE2b-256 |
9a0e0f0d65a154a499b7bf1c85970c616e5b60176513c8983104f1daa215990f
|
File details
Details for the file openrewrite-8.86.0.dev20260625123347-py3-none-any.whl.
File metadata
- Download URL: openrewrite-8.86.0.dev20260625123347-py3-none-any.whl
- Upload date:
- Size: 362.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fbe61c30602d2a9e7b9f9604a0f84b72e3e7231a71fe6920290894eff25259f5
|
|
| MD5 |
d4441175751ab3dfeb2b7b67a0b29774
|
|
| BLAKE2b-256 |
48aee42fc32ab04165f1366fc5122ff4495da0a5b54cd11c6b9bf84d988bdaea
|