refactor
refactor copied to clipboard
How to build composite `Rule`s?
Suppose I want to "lift" some assignments from the body of a function into the arguments e.g.
def foo(a, b):
c = 5
print(3 * c)
->
def foo(a, b, c=5):
print(3 * c)
As far as I can tell, each Rule can only do one thing - either LazyReplace a node or Erase some nodes.
But since I need both a LazyReplace and an Erase, where either I sequence these rewrites or that there should be shared context between both rules (either the Erase should pass c to the LazyReplace which inserts c in the args, or the LazyReplace inserts c and then passes it to Erase) but that seems like a bad idea (not sure what invariants you're depending on wrt the AST...
Note that refactor.Erase(a).apply in the context of LazyReplace obviously won't work because i don't have access to the textual source at that point. Currently I'm just doing a del new_node.body[i] in the LazyReplace's build method (where the i is passed in from Rule).
EDIT:
seems
if action := rule.match(node):
could become
if action := rule.match(node):
if not isinstance(action, (list, tuple)):
action = [action]
for action in in rule.match(node):
here.
@makslevental that's a very good question, and something that I have been working to solve for a while (albeit going stale for some time due to lack of time). The main idea was to allow rules to yield multiple actions, which in this way they can do more complex transformations (e.g. erase in the first place, and then replace afterwards). There is even a PR #57, but I still need to sit down and finish it first and then make a release.
In your case you'd initially yield the action that erases, and then the action that replaces ast.arguments with the new form.
@isidentical Cool. Is there anything I can do to help push that PR along?
@isidentical Cool. Is there anything I can do to help push that PR along?
Currently the PR only supports chained Replace operations (due to how we figure out the span of the new node, e.g. compute an access path and then re-apply it). But this support can be broader (e.g. might include Insert or Erase) by simply figuring out how insertions/removals effect the second action's node (if you erase the previous statement, the IndexAccess's index needs to be decremented by one. or it needs to be incremented by one if you Insert something just above it).
If you are interested in helping out, I'd be more than happy to let you take the PR and push it forward with supporting the use case above (and with some tests). Feel free to check out my branch locally, play with it and if you are interested just let me know (happy to answer questions).
Initial part of the chained actions are now implemented, though they still need extensive testing and documentation. Will try to finish these minor things and make a new release.
Should be done now, thanks again for reporting your interest in this!