test_print.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. import unittest
  2. import sys
  3. from io import StringIO
  4. from test import support
  5. NotDefined = object()
  6. # A dispatch table all 8 combinations of providing
  7. # sep, end, and file.
  8. # I use this machinery so that I'm not just passing default
  9. # values to print, I'm either passing or not passing in the
  10. # arguments.
  11. dispatch = {
  12. (False, False, False):
  13. lambda args, sep, end, file: print(*args),
  14. (False, False, True):
  15. lambda args, sep, end, file: print(file=file, *args),
  16. (False, True, False):
  17. lambda args, sep, end, file: print(end=end, *args),
  18. (False, True, True):
  19. lambda args, sep, end, file: print(end=end, file=file, *args),
  20. (True, False, False):
  21. lambda args, sep, end, file: print(sep=sep, *args),
  22. (True, False, True):
  23. lambda args, sep, end, file: print(sep=sep, file=file, *args),
  24. (True, True, False):
  25. lambda args, sep, end, file: print(sep=sep, end=end, *args),
  26. (True, True, True):
  27. lambda args, sep, end, file: print(sep=sep, end=end, file=file, *args),
  28. }
  29. # Class used to test __str__ and print
  30. class ClassWith__str__:
  31. def __init__(self, x):
  32. self.x = x
  33. def __str__(self):
  34. return self.x
  35. class TestPrint(unittest.TestCase):
  36. """Test correct operation of the print function."""
  37. def check(self, expected, args,
  38. sep=NotDefined, end=NotDefined, file=NotDefined):
  39. # Capture sys.stdout in a StringIO. Call print with args,
  40. # and with sep, end, and file, if they're defined. Result
  41. # must match expected.
  42. # Look up the actual function to call, based on if sep, end,
  43. # and file are defined.
  44. fn = dispatch[(sep is not NotDefined,
  45. end is not NotDefined,
  46. file is not NotDefined)]
  47. with support.captured_stdout() as t:
  48. fn(args, sep, end, file)
  49. self.assertEqual(t.getvalue(), expected)
  50. def test_print(self):
  51. def x(expected, args, sep=NotDefined, end=NotDefined):
  52. # Run the test 2 ways: not using file, and using
  53. # file directed to a StringIO.
  54. self.check(expected, args, sep=sep, end=end)
  55. # When writing to a file, stdout is expected to be empty
  56. o = StringIO()
  57. self.check('', args, sep=sep, end=end, file=o)
  58. # And o will contain the expected output
  59. self.assertEqual(o.getvalue(), expected)
  60. x('\n', ())
  61. x('a\n', ('a',))
  62. x('None\n', (None,))
  63. x('1 2\n', (1, 2))
  64. x('1 2\n', (1, ' ', 2))
  65. x('1*2\n', (1, 2), sep='*')
  66. x('1 s', (1, 's'), end='')
  67. x('a\nb\n', ('a', 'b'), sep='\n')
  68. x('1.01', (1.0, 1), sep='', end='')
  69. x('1*a*1.3+', (1, 'a', 1.3), sep='*', end='+')
  70. x('a\n\nb\n', ('a\n', 'b'), sep='\n')
  71. x('\0+ +\0\n', ('\0', ' ', '\0'), sep='+')
  72. x('a\n b\n', ('a\n', 'b'))
  73. x('a\n b\n', ('a\n', 'b'), sep=None)
  74. x('a\n b\n', ('a\n', 'b'), end=None)
  75. x('a\n b\n', ('a\n', 'b'), sep=None, end=None)
  76. x('*\n', (ClassWith__str__('*'),))
  77. x('abc 1\n', (ClassWith__str__('abc'), 1))
  78. # errors
  79. self.assertRaises(TypeError, print, '', sep=3)
  80. self.assertRaises(TypeError, print, '', end=3)
  81. self.assertRaises(AttributeError, print, '', file='')
  82. def test_print_flush(self):
  83. # operation of the flush flag
  84. class filelike:
  85. def __init__(self):
  86. self.written = ''
  87. self.flushed = 0
  88. def write(self, str):
  89. self.written += str
  90. def flush(self):
  91. self.flushed += 1
  92. f = filelike()
  93. print(1, file=f, end='', flush=True)
  94. print(2, file=f, end='', flush=True)
  95. print(3, file=f, flush=False)
  96. self.assertEqual(f.written, '123\n')
  97. self.assertEqual(f.flushed, 2)
  98. # ensure exceptions from flush are passed through
  99. class noflush:
  100. def write(self, str):
  101. pass
  102. def flush(self):
  103. raise RuntimeError
  104. self.assertRaises(RuntimeError, print, 1, file=noflush(), flush=True)
  105. class TestPy2MigrationHint(unittest.TestCase):
  106. """Test that correct hint is produced analogous to Python3 syntax,
  107. if print statement is executed as in Python 2.
  108. """
  109. def test_normal_string(self):
  110. python2_print_str = 'print "Hello World"'
  111. with self.assertRaises(SyntaxError) as context:
  112. exec(python2_print_str)
  113. self.assertIn("Missing parentheses in call to 'print'. Did you mean print(...)",
  114. str(context.exception))
  115. def test_string_with_soft_space(self):
  116. python2_print_str = 'print "Hello World",'
  117. with self.assertRaises(SyntaxError) as context:
  118. exec(python2_print_str)
  119. self.assertIn("Missing parentheses in call to 'print'. Did you mean print(...)",
  120. str(context.exception))
  121. def test_string_with_excessive_whitespace(self):
  122. python2_print_str = 'print "Hello World", '
  123. with self.assertRaises(SyntaxError) as context:
  124. exec(python2_print_str)
  125. self.assertIn("Missing parentheses in call to 'print'. Did you mean print(...)",
  126. str(context.exception))
  127. def test_string_with_leading_whitespace(self):
  128. python2_print_str = '''if 1:
  129. print "Hello World"
  130. '''
  131. with self.assertRaises(SyntaxError) as context:
  132. exec(python2_print_str)
  133. self.assertIn("Missing parentheses in call to 'print'. Did you mean print(...)",
  134. str(context.exception))
  135. # bpo-32685: Suggestions for print statement should be proper when
  136. # it is in the same line as the header of a compound statement
  137. # and/or followed by a semicolon
  138. def test_string_with_semicolon(self):
  139. python2_print_str = 'print p;'
  140. with self.assertRaises(SyntaxError) as context:
  141. exec(python2_print_str)
  142. self.assertIn("Missing parentheses in call to 'print'. Did you mean print(...)",
  143. str(context.exception))
  144. def test_string_in_loop_on_same_line(self):
  145. python2_print_str = 'for i in s: print i'
  146. with self.assertRaises(SyntaxError) as context:
  147. exec(python2_print_str)
  148. self.assertIn("Missing parentheses in call to 'print'. Did you mean print(...)",
  149. str(context.exception))
  150. def test_stream_redirection_hint_for_py2_migration(self):
  151. # Test correct hint produced for Py2 redirection syntax
  152. with self.assertRaises(TypeError) as context:
  153. print >> sys.stderr, "message"
  154. self.assertIn('Did you mean "print(<message>, '
  155. 'file=<output_stream>)"?', str(context.exception))
  156. # Test correct hint is produced in the case where RHS implements
  157. # __rrshift__ but returns NotImplemented
  158. with self.assertRaises(TypeError) as context:
  159. print >> 42
  160. self.assertIn('Did you mean "print(<message>, '
  161. 'file=<output_stream>)"?', str(context.exception))
  162. # Test stream redirection hint is specific to print
  163. with self.assertRaises(TypeError) as context:
  164. max >> sys.stderr
  165. self.assertNotIn('Did you mean ', str(context.exception))
  166. # Test stream redirection hint is specific to rshift
  167. with self.assertRaises(TypeError) as context:
  168. print << sys.stderr
  169. self.assertNotIn('Did you mean', str(context.exception))
  170. # Ensure right operand implementing rrshift still works
  171. class OverrideRRShift:
  172. def __rrshift__(self, lhs):
  173. return 42 # Force result independent of LHS
  174. self.assertEqual(print >> OverrideRRShift(), 42)
  175. if __name__ == "__main__":
  176. unittest.main()