mirror of
https://github.com/python/cpython.git
synced 2025-07-24 19:54:21 +00:00

The suggested replacement for print statements previously failed to account for leading whitespace and hence could end up including unwanted text in the proposed call to the print builtin. Patch by Sanyam Khurana.
202 lines
6.7 KiB
Python
202 lines
6.7 KiB
Python
import unittest
|
|
import sys
|
|
from io import StringIO
|
|
|
|
from test import support
|
|
|
|
NotDefined = object()
|
|
|
|
# A dispatch table all 8 combinations of providing
|
|
# sep, end, and file.
|
|
# I use this machinery so that I'm not just passing default
|
|
# values to print, I'm either passing or not passing in the
|
|
# arguments.
|
|
dispatch = {
|
|
(False, False, False):
|
|
lambda args, sep, end, file: print(*args),
|
|
(False, False, True):
|
|
lambda args, sep, end, file: print(file=file, *args),
|
|
(False, True, False):
|
|
lambda args, sep, end, file: print(end=end, *args),
|
|
(False, True, True):
|
|
lambda args, sep, end, file: print(end=end, file=file, *args),
|
|
(True, False, False):
|
|
lambda args, sep, end, file: print(sep=sep, *args),
|
|
(True, False, True):
|
|
lambda args, sep, end, file: print(sep=sep, file=file, *args),
|
|
(True, True, False):
|
|
lambda args, sep, end, file: print(sep=sep, end=end, *args),
|
|
(True, True, True):
|
|
lambda args, sep, end, file: print(sep=sep, end=end, file=file, *args),
|
|
}
|
|
|
|
|
|
# Class used to test __str__ and print
|
|
class ClassWith__str__:
|
|
def __init__(self, x):
|
|
self.x = x
|
|
|
|
def __str__(self):
|
|
return self.x
|
|
|
|
|
|
class TestPrint(unittest.TestCase):
|
|
"""Test correct operation of the print function."""
|
|
|
|
def check(self, expected, args,
|
|
sep=NotDefined, end=NotDefined, file=NotDefined):
|
|
# Capture sys.stdout in a StringIO. Call print with args,
|
|
# and with sep, end, and file, if they're defined. Result
|
|
# must match expected.
|
|
|
|
# Look up the actual function to call, based on if sep, end,
|
|
# and file are defined.
|
|
fn = dispatch[(sep is not NotDefined,
|
|
end is not NotDefined,
|
|
file is not NotDefined)]
|
|
|
|
with support.captured_stdout() as t:
|
|
fn(args, sep, end, file)
|
|
|
|
self.assertEqual(t.getvalue(), expected)
|
|
|
|
def test_print(self):
|
|
def x(expected, args, sep=NotDefined, end=NotDefined):
|
|
# Run the test 2 ways: not using file, and using
|
|
# file directed to a StringIO.
|
|
|
|
self.check(expected, args, sep=sep, end=end)
|
|
|
|
# When writing to a file, stdout is expected to be empty
|
|
o = StringIO()
|
|
self.check('', args, sep=sep, end=end, file=o)
|
|
|
|
# And o will contain the expected output
|
|
self.assertEqual(o.getvalue(), expected)
|
|
|
|
x('\n', ())
|
|
x('a\n', ('a',))
|
|
x('None\n', (None,))
|
|
x('1 2\n', (1, 2))
|
|
x('1 2\n', (1, ' ', 2))
|
|
x('1*2\n', (1, 2), sep='*')
|
|
x('1 s', (1, 's'), end='')
|
|
x('a\nb\n', ('a', 'b'), sep='\n')
|
|
x('1.01', (1.0, 1), sep='', end='')
|
|
x('1*a*1.3+', (1, 'a', 1.3), sep='*', end='+')
|
|
x('a\n\nb\n', ('a\n', 'b'), sep='\n')
|
|
x('\0+ +\0\n', ('\0', ' ', '\0'), sep='+')
|
|
|
|
x('a\n b\n', ('a\n', 'b'))
|
|
x('a\n b\n', ('a\n', 'b'), sep=None)
|
|
x('a\n b\n', ('a\n', 'b'), end=None)
|
|
x('a\n b\n', ('a\n', 'b'), sep=None, end=None)
|
|
|
|
x('*\n', (ClassWith__str__('*'),))
|
|
x('abc 1\n', (ClassWith__str__('abc'), 1))
|
|
|
|
# errors
|
|
self.assertRaises(TypeError, print, '', sep=3)
|
|
self.assertRaises(TypeError, print, '', end=3)
|
|
self.assertRaises(AttributeError, print, '', file='')
|
|
|
|
def test_print_flush(self):
|
|
# operation of the flush flag
|
|
class filelike:
|
|
def __init__(self):
|
|
self.written = ''
|
|
self.flushed = 0
|
|
|
|
def write(self, str):
|
|
self.written += str
|
|
|
|
def flush(self):
|
|
self.flushed += 1
|
|
|
|
f = filelike()
|
|
print(1, file=f, end='', flush=True)
|
|
print(2, file=f, end='', flush=True)
|
|
print(3, file=f, flush=False)
|
|
self.assertEqual(f.written, '123\n')
|
|
self.assertEqual(f.flushed, 2)
|
|
|
|
# ensure exceptions from flush are passed through
|
|
class noflush:
|
|
def write(self, str):
|
|
pass
|
|
|
|
def flush(self):
|
|
raise RuntimeError
|
|
self.assertRaises(RuntimeError, print, 1, file=noflush(), flush=True)
|
|
|
|
|
|
class TestPy2MigrationHint(unittest.TestCase):
|
|
"""Test that correct hint is produced analogous to Python3 syntax,
|
|
if print statement is executed as in Python 2.
|
|
"""
|
|
|
|
def test_normal_string(self):
|
|
python2_print_str = 'print "Hello World"'
|
|
with self.assertRaises(SyntaxError) as context:
|
|
exec(python2_print_str)
|
|
|
|
self.assertIn('print("Hello World")', str(context.exception))
|
|
|
|
def test_string_with_soft_space(self):
|
|
python2_print_str = 'print "Hello World",'
|
|
with self.assertRaises(SyntaxError) as context:
|
|
exec(python2_print_str)
|
|
|
|
self.assertIn('print("Hello World", end=" ")', str(context.exception))
|
|
|
|
def test_string_with_excessive_whitespace(self):
|
|
python2_print_str = 'print "Hello World", '
|
|
with self.assertRaises(SyntaxError) as context:
|
|
exec(python2_print_str)
|
|
|
|
self.assertIn('print("Hello World", end=" ")', str(context.exception))
|
|
|
|
def test_string_with_leading_whitespace(self):
|
|
python2_print_str = '''if 1:
|
|
print "Hello World"
|
|
'''
|
|
with self.assertRaises(SyntaxError) as context:
|
|
exec(python2_print_str)
|
|
|
|
self.assertIn('print("Hello World")', str(context.exception))
|
|
|
|
def test_stream_redirection_hint_for_py2_migration(self):
|
|
# Test correct hint produced for Py2 redirection syntax
|
|
with self.assertRaises(TypeError) as context:
|
|
print >> sys.stderr, "message"
|
|
self.assertIn('Did you mean "print(<message>, '
|
|
'file=<output_stream>)"?', str(context.exception))
|
|
|
|
# Test correct hint is produced in the case where RHS implements
|
|
# __rrshift__ but returns NotImplemented
|
|
with self.assertRaises(TypeError) as context:
|
|
print >> 42
|
|
self.assertIn('Did you mean "print(<message>, '
|
|
'file=<output_stream>)"?', str(context.exception))
|
|
|
|
# Test stream redirection hint is specific to print
|
|
with self.assertRaises(TypeError) as context:
|
|
max >> sys.stderr
|
|
self.assertNotIn('Did you mean ', str(context.exception))
|
|
|
|
# Test stream redirection hint is specific to rshift
|
|
with self.assertRaises(TypeError) as context:
|
|
print << sys.stderr
|
|
self.assertNotIn('Did you mean', str(context.exception))
|
|
|
|
# Ensure right operand implementing rrshift still works
|
|
class OverrideRRShift:
|
|
def __rrshift__(self, lhs):
|
|
return 42 # Force result independent of LHS
|
|
|
|
self.assertEqual(print >> OverrideRRShift(), 42)
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|