PyCQA / redbaron

Bottom-up approach to refactoring in python
http://redbaron.pycqa.org/
690 stars 74 forks source link

Else or elif line can be misindented when inserting if...else #151

Open sdh4 opened 6 years ago

sdh4 commented 6 years ago

When inserting an if...else block from one code block into another code block, the else block in the generated code can be misindented.

Test case:

from redbaron import RedBaron
code1=RedBaron("test()\n")
code2=RedBaron("def foo():\n    if a:\n        a()\n    else:\n        b()\n")
code1.append(code2[0].value[0])
print(code1.dumps())

Output:

test()
if a:
        a()
    else:
        b()

I am not yet seeing exactly what code is supposed to handle this scenario, but evidently it has been considered because try...except and for...else blocks get handled properly whereas if...else and if...elif do not (?)

sdh4 commented 6 years ago

The difference between if..else and try..except/for..else seems to have to do with the internal structures: In the IfelseblockNode the endlnode with indentation is attached to the end of the IfNode within the IfelseblockNode, i.e. it is an additional level down, whereas in the ForNode the indentation is attached to the else attribute of the ForNode itself.

In any case, I have discovered a workaround: Manually correct the indentation before appending using the decrease_indentation() method, e.g.

from redbaron import RedBaron
code1=RedBaron("test()\n")
code2=RedBaron("def foo():\n    if a:\n        a()\n    else:\n        b()\n")
deindent=len(code2[0].value[0].indentation)-len(code1[0].indentation)
toappend=code2[0].value.copy()
toappend.decrease_indentation(deindent)

# toappend[0] is an endlnode, which doesn't do us much good
code1.append(toappend[1])

print(code1.dumps())

This version gives the desired output:

test()
if a:
    a()
else:
    b()