This is currently redundant as we are carefully only
using this simple print style which is both a print
statement (with redundant brackets) under Python 2
and a print function under Python 3:
print(variable)
However, adding the __future__ import to any file using
a print should catch any accidental usage of the print
statement in the near future (even if not testing under
Python 3 where it would be spotted since we've turned
off the print fixer during the 2to3 conversion).
This was automated as follows:
<python>
MAGIC = "from __future__ import print_function"
import os
import sys
def should_mark(filename):
handle = open(filename, "rU")
lines = [line.strip() for line in handle if "print" in line]
handle.close()
if MAGIC in lines:
#print("%s is marked" % filename)
return False
if "print" in lines:
print("TODO - %s has a naked print" % filename)
sys.exit(1)
for line in lines:
if "print" not in line:
continue
#print(line)
line = line.strip(" #")
if line.startswith(">>>") or line.startswith("..."):
#doctest
line = line[3:].strip()
if line.startswith("print ") or line.startswith("print("):
return True
print("%s has no print statements" % filename)
return False
def mark_file(filename, marker=MAGIC):
with open(filename, "rU") as h:
lines = list(h.readlines())
with open(filename, "w") as h:
while (lines[0].startswith("#") or not lines[0].strip()):
h.write(lines.pop(0))
if lines[0].startswith('"""') or lines[0].startswith('r"""'):
# Module docstring
if lines[0].strip() == '"""':
print("Non-PEP8 module docstring in %s" % filename)
if lines[0].rstrip().endswith('"""') and lines[0].strip() != '"""':
# One liner
print("One line module docstring in %s" % filename)
h.write(lines.pop(0))
else:
h.write(lines.pop(0))
while not lines[0].strip().endswith('"""'):
h.write(lines.pop(0))
h.write(lines.pop(0))
while (lines[0].startswith("#") or not lines[0].strip()):
h.write(lines.pop(0))
h.write(marker + "\n\n")
h.write("".join(lines))
for dirpath, dirnames, filenames in os.walk("."):
if dirpath.startswith("./build/"):
continue
for f in filenames:
if not f.endswith(".py"):
continue
f = os.path.join(dirpath, f)
if should_mark(f):
print("Marking %s" % f)
mark_file(f)
</python>
For now we only handle the 'print' statement with a single argument,
i. e.:
print ... -> print(...)
Migration was performed using a 2to3 fixer class:
from lib2to3 import fixer_base, patcomp
from lib2to3.fixer_util import Name, Call
parend_expr = patcomp.compile_pattern(
"""atom< '(' [atom|term|testlist_gexp|STRING|NAME] ')' >""")
class FixSinglePrint(fixer_base.BaseFix):
PATTERN = "print_stmt"
BM_compatible = True
def transform(self, node, results):
assert results
assert node.children[0] == Name(u"print")
args = node.children[1:]
if len(args) != 1 or parend_expr.match(args[0]):
# We only fix 'print' statements which have _exactly_ one
# non-parenthesized argument.
return
l_args = [arg.clone() for arg in args]
l_args[0].prefix = u""
n_stmt = Call(Name(u"print"), l_args)
n_stmt.prefix = node.prefix
return n_stmt