python-3.xsedpython-2to3

How to fix print((double parentheses)) after 2to3 conversion?


When migrating my project to Python 3 (2to3-3.7 -w -f print *), I observed that a lot of (but not all) print statements became print((...)), so these statements now print tuples instead of performing the expected behavior. I gather that if I'd used -p, I'd be in a better place right now because from __future__ import print_function is at the top of every affected module.

I'm thinking about trying to use sed to fix this, but before I break my teeth on that, I thought I'd see if anyone else has dealt with this before. Is there a 2to3 feature to clean this up?

I do use version control (git) and have commits immediately before and after (as well as the .bak files 2to3 creates), but I'm not sure how to isolate the changes I've made from the print situations.


Solution

  • You were already printing tuples. If you were not, then you are not now either.

    To illustrate, your code would have to have been using print as if it was a function:

    # Python 2
    print(somestring)
    

    which becomes, after translation

    # Python 3
    print((somestring))
    

    That's not a tuple, that's just a pair of parentheses. It results in the same output on either version. In fact, 2to3 is smart enough to drop the inner parentheses again; the actual output written is simply

    # Python 3
    print(somestring)
    

    However, if you were using multiple arguments in Python 2:

    # Python 2
    print(arg1, arg2)
    

    then you'd already be printing a tuple, because that's really:

    value_to_print = (arg1, arg2)
    print value_to_print
    

    So it would only be correct to preserve that behaviour in Python 3. If you see the 2to3 tool use print((....)), then it determined that you were already printing tuples.

    Demo:

    $ cat testprint.py
    print('not a tuple')
    print('two-value', 'tuple')
    $ python2.7 testprint.py
    not a tuple
    ('two-value', 'tuple')
    $ 2to3 -w -f print testprint.py
    RefactoringTool: Refactored testprint.py
    --- testprint.py    (original)
    +++ testprint.py    (refactored)
    @@ -1,2 +1,2 @@
     print('not a tuple')
    -print('two-value', 'tuple')
    +print(('two-value', 'tuple'))
    RefactoringTool: Files that were modified:
    RefactoringTool: testprint.py
    $ python3.7 testprint.py
    not a tuple
    ('two-value', 'tuple')
    

    Note that this is different from using from __future__ import print_function in your Python 2 code, to disable the print statement and so making the code call the built-in print() function. The 2to3 tool already detects this case and will pass through print(...) function calls unchanged:

    $ cat futureprint.py
    from __future__ import print_function
    print('not', 'a tuple')
    $ python2.7 futureprint.py
    not a tuple
    $ 2to3 -w -f print futureprint.py
    RefactoringTool: No files need to be modified.
    $ python3.7 futureprint.py
    not a tuple
    

    You can force 2to3 to assume all your files use from __future__ import print_function, regardless, with the -p / --print-function command-line switch:

    -p, --print-function  Modify the grammar so that print() is a function
    

    However, any deliberate print (tuple_element1, tuple_element2, ...) print statements would then be mis-translated as function calls:

    $ cat printtuple.py
    print ('two-value', 'tuple')
    $ python2.7 printtuple.py
    ('two-value', 'tuple')
    $ 2to3 -w -f print -p printtuple.py
    RefactoringTool: No files need to be modified.
    $ python3.7 printtuple.py
    two-value tuple