17db96d56Sopenharmony_ciimport unittest
27db96d56Sopenharmony_ciimport sys
37db96d56Sopenharmony_cifrom io import StringIO
47db96d56Sopenharmony_ci
57db96d56Sopenharmony_cifrom test import support
67db96d56Sopenharmony_ci
77db96d56Sopenharmony_ciNotDefined = object()
87db96d56Sopenharmony_ci
97db96d56Sopenharmony_ci# A dispatch table all 8 combinations of providing
107db96d56Sopenharmony_ci# sep, end, and file.
117db96d56Sopenharmony_ci# I use this machinery so that I'm not just passing default
127db96d56Sopenharmony_ci# values to print, I'm either passing or not passing in the
137db96d56Sopenharmony_ci# arguments.
147db96d56Sopenharmony_cidispatch = {
157db96d56Sopenharmony_ci    (False, False, False):
167db96d56Sopenharmony_ci        lambda args, sep, end, file: print(*args),
177db96d56Sopenharmony_ci    (False, False, True):
187db96d56Sopenharmony_ci        lambda args, sep, end, file: print(file=file, *args),
197db96d56Sopenharmony_ci    (False, True,  False):
207db96d56Sopenharmony_ci        lambda args, sep, end, file: print(end=end, *args),
217db96d56Sopenharmony_ci    (False, True,  True):
227db96d56Sopenharmony_ci        lambda args, sep, end, file: print(end=end, file=file, *args),
237db96d56Sopenharmony_ci    (True,  False, False):
247db96d56Sopenharmony_ci        lambda args, sep, end, file: print(sep=sep, *args),
257db96d56Sopenharmony_ci    (True,  False, True):
267db96d56Sopenharmony_ci        lambda args, sep, end, file: print(sep=sep, file=file, *args),
277db96d56Sopenharmony_ci    (True,  True,  False):
287db96d56Sopenharmony_ci        lambda args, sep, end, file: print(sep=sep, end=end, *args),
297db96d56Sopenharmony_ci    (True,  True,  True):
307db96d56Sopenharmony_ci        lambda args, sep, end, file: print(sep=sep, end=end, file=file, *args),
317db96d56Sopenharmony_ci}
327db96d56Sopenharmony_ci
337db96d56Sopenharmony_ci
347db96d56Sopenharmony_ci# Class used to test __str__ and print
357db96d56Sopenharmony_ciclass ClassWith__str__:
367db96d56Sopenharmony_ci    def __init__(self, x):
377db96d56Sopenharmony_ci        self.x = x
387db96d56Sopenharmony_ci
397db96d56Sopenharmony_ci    def __str__(self):
407db96d56Sopenharmony_ci        return self.x
417db96d56Sopenharmony_ci
427db96d56Sopenharmony_ci
437db96d56Sopenharmony_ciclass TestPrint(unittest.TestCase):
447db96d56Sopenharmony_ci    """Test correct operation of the print function."""
457db96d56Sopenharmony_ci
467db96d56Sopenharmony_ci    def check(self, expected, args,
477db96d56Sopenharmony_ci              sep=NotDefined, end=NotDefined, file=NotDefined):
487db96d56Sopenharmony_ci        # Capture sys.stdout in a StringIO.  Call print with args,
497db96d56Sopenharmony_ci        # and with sep, end, and file, if they're defined.  Result
507db96d56Sopenharmony_ci        # must match expected.
517db96d56Sopenharmony_ci
527db96d56Sopenharmony_ci        # Look up the actual function to call, based on if sep, end,
537db96d56Sopenharmony_ci        # and file are defined.
547db96d56Sopenharmony_ci        fn = dispatch[(sep is not NotDefined,
557db96d56Sopenharmony_ci                       end is not NotDefined,
567db96d56Sopenharmony_ci                       file is not NotDefined)]
577db96d56Sopenharmony_ci
587db96d56Sopenharmony_ci        with support.captured_stdout() as t:
597db96d56Sopenharmony_ci            fn(args, sep, end, file)
607db96d56Sopenharmony_ci
617db96d56Sopenharmony_ci        self.assertEqual(t.getvalue(), expected)
627db96d56Sopenharmony_ci
637db96d56Sopenharmony_ci    def test_print(self):
647db96d56Sopenharmony_ci        def x(expected, args, sep=NotDefined, end=NotDefined):
657db96d56Sopenharmony_ci            # Run the test 2 ways: not using file, and using
667db96d56Sopenharmony_ci            # file directed to a StringIO.
677db96d56Sopenharmony_ci
687db96d56Sopenharmony_ci            self.check(expected, args, sep=sep, end=end)
697db96d56Sopenharmony_ci
707db96d56Sopenharmony_ci            # When writing to a file, stdout is expected to be empty
717db96d56Sopenharmony_ci            o = StringIO()
727db96d56Sopenharmony_ci            self.check('', args, sep=sep, end=end, file=o)
737db96d56Sopenharmony_ci
747db96d56Sopenharmony_ci            # And o will contain the expected output
757db96d56Sopenharmony_ci            self.assertEqual(o.getvalue(), expected)
767db96d56Sopenharmony_ci
777db96d56Sopenharmony_ci        x('\n', ())
787db96d56Sopenharmony_ci        x('a\n', ('a',))
797db96d56Sopenharmony_ci        x('None\n', (None,))
807db96d56Sopenharmony_ci        x('1 2\n', (1, 2))
817db96d56Sopenharmony_ci        x('1   2\n', (1, ' ', 2))
827db96d56Sopenharmony_ci        x('1*2\n', (1, 2), sep='*')
837db96d56Sopenharmony_ci        x('1 s', (1, 's'), end='')
847db96d56Sopenharmony_ci        x('a\nb\n', ('a', 'b'), sep='\n')
857db96d56Sopenharmony_ci        x('1.01', (1.0, 1), sep='', end='')
867db96d56Sopenharmony_ci        x('1*a*1.3+', (1, 'a', 1.3), sep='*', end='+')
877db96d56Sopenharmony_ci        x('a\n\nb\n', ('a\n', 'b'), sep='\n')
887db96d56Sopenharmony_ci        x('\0+ +\0\n', ('\0', ' ', '\0'), sep='+')
897db96d56Sopenharmony_ci
907db96d56Sopenharmony_ci        x('a\n b\n', ('a\n', 'b'))
917db96d56Sopenharmony_ci        x('a\n b\n', ('a\n', 'b'), sep=None)
927db96d56Sopenharmony_ci        x('a\n b\n', ('a\n', 'b'), end=None)
937db96d56Sopenharmony_ci        x('a\n b\n', ('a\n', 'b'), sep=None, end=None)
947db96d56Sopenharmony_ci
957db96d56Sopenharmony_ci        x('*\n', (ClassWith__str__('*'),))
967db96d56Sopenharmony_ci        x('abc 1\n', (ClassWith__str__('abc'), 1))
977db96d56Sopenharmony_ci
987db96d56Sopenharmony_ci        # errors
997db96d56Sopenharmony_ci        self.assertRaises(TypeError, print, '', sep=3)
1007db96d56Sopenharmony_ci        self.assertRaises(TypeError, print, '', end=3)
1017db96d56Sopenharmony_ci        self.assertRaises(AttributeError, print, '', file='')
1027db96d56Sopenharmony_ci
1037db96d56Sopenharmony_ci    def test_print_flush(self):
1047db96d56Sopenharmony_ci        # operation of the flush flag
1057db96d56Sopenharmony_ci        class filelike:
1067db96d56Sopenharmony_ci            def __init__(self):
1077db96d56Sopenharmony_ci                self.written = ''
1087db96d56Sopenharmony_ci                self.flushed = 0
1097db96d56Sopenharmony_ci
1107db96d56Sopenharmony_ci            def write(self, str):
1117db96d56Sopenharmony_ci                self.written += str
1127db96d56Sopenharmony_ci
1137db96d56Sopenharmony_ci            def flush(self):
1147db96d56Sopenharmony_ci                self.flushed += 1
1157db96d56Sopenharmony_ci
1167db96d56Sopenharmony_ci        f = filelike()
1177db96d56Sopenharmony_ci        print(1, file=f, end='', flush=True)
1187db96d56Sopenharmony_ci        print(2, file=f, end='', flush=True)
1197db96d56Sopenharmony_ci        print(3, file=f, flush=False)
1207db96d56Sopenharmony_ci        self.assertEqual(f.written, '123\n')
1217db96d56Sopenharmony_ci        self.assertEqual(f.flushed, 2)
1227db96d56Sopenharmony_ci
1237db96d56Sopenharmony_ci        # ensure exceptions from flush are passed through
1247db96d56Sopenharmony_ci        class noflush:
1257db96d56Sopenharmony_ci            def write(self, str):
1267db96d56Sopenharmony_ci                pass
1277db96d56Sopenharmony_ci
1287db96d56Sopenharmony_ci            def flush(self):
1297db96d56Sopenharmony_ci                raise RuntimeError
1307db96d56Sopenharmony_ci        self.assertRaises(RuntimeError, print, 1, file=noflush(), flush=True)
1317db96d56Sopenharmony_ci
1327db96d56Sopenharmony_ci
1337db96d56Sopenharmony_ciclass TestPy2MigrationHint(unittest.TestCase):
1347db96d56Sopenharmony_ci    """Test that correct hint is produced analogous to Python3 syntax,
1357db96d56Sopenharmony_ci    if print statement is executed as in Python 2.
1367db96d56Sopenharmony_ci    """
1377db96d56Sopenharmony_ci
1387db96d56Sopenharmony_ci    def test_normal_string(self):
1397db96d56Sopenharmony_ci        python2_print_str = 'print "Hello World"'
1407db96d56Sopenharmony_ci        with self.assertRaises(SyntaxError) as context:
1417db96d56Sopenharmony_ci            exec(python2_print_str)
1427db96d56Sopenharmony_ci
1437db96d56Sopenharmony_ci        self.assertIn("Missing parentheses in call to 'print'. Did you mean print(...)",
1447db96d56Sopenharmony_ci                str(context.exception))
1457db96d56Sopenharmony_ci
1467db96d56Sopenharmony_ci    def test_string_with_soft_space(self):
1477db96d56Sopenharmony_ci        python2_print_str = 'print "Hello World",'
1487db96d56Sopenharmony_ci        with self.assertRaises(SyntaxError) as context:
1497db96d56Sopenharmony_ci            exec(python2_print_str)
1507db96d56Sopenharmony_ci
1517db96d56Sopenharmony_ci        self.assertIn("Missing parentheses in call to 'print'. Did you mean print(...)",
1527db96d56Sopenharmony_ci                str(context.exception))
1537db96d56Sopenharmony_ci
1547db96d56Sopenharmony_ci    def test_string_with_excessive_whitespace(self):
1557db96d56Sopenharmony_ci        python2_print_str = 'print  "Hello World", '
1567db96d56Sopenharmony_ci        with self.assertRaises(SyntaxError) as context:
1577db96d56Sopenharmony_ci            exec(python2_print_str)
1587db96d56Sopenharmony_ci
1597db96d56Sopenharmony_ci        self.assertIn("Missing parentheses in call to 'print'. Did you mean print(...)",
1607db96d56Sopenharmony_ci                str(context.exception))
1617db96d56Sopenharmony_ci
1627db96d56Sopenharmony_ci    def test_string_with_leading_whitespace(self):
1637db96d56Sopenharmony_ci        python2_print_str = '''if 1:
1647db96d56Sopenharmony_ci            print "Hello World"
1657db96d56Sopenharmony_ci        '''
1667db96d56Sopenharmony_ci        with self.assertRaises(SyntaxError) as context:
1677db96d56Sopenharmony_ci            exec(python2_print_str)
1687db96d56Sopenharmony_ci
1697db96d56Sopenharmony_ci        self.assertIn("Missing parentheses in call to 'print'. Did you mean print(...)",
1707db96d56Sopenharmony_ci                str(context.exception))
1717db96d56Sopenharmony_ci
1727db96d56Sopenharmony_ci    # bpo-32685: Suggestions for print statement should be proper when
1737db96d56Sopenharmony_ci    # it is in the same line as the header of a compound statement
1747db96d56Sopenharmony_ci    # and/or followed by a semicolon
1757db96d56Sopenharmony_ci    def test_string_with_semicolon(self):
1767db96d56Sopenharmony_ci        python2_print_str = 'print p;'
1777db96d56Sopenharmony_ci        with self.assertRaises(SyntaxError) as context:
1787db96d56Sopenharmony_ci            exec(python2_print_str)
1797db96d56Sopenharmony_ci
1807db96d56Sopenharmony_ci        self.assertIn("Missing parentheses in call to 'print'. Did you mean print(...)",
1817db96d56Sopenharmony_ci                str(context.exception))
1827db96d56Sopenharmony_ci
1837db96d56Sopenharmony_ci    def test_string_in_loop_on_same_line(self):
1847db96d56Sopenharmony_ci        python2_print_str = 'for i in s: print i'
1857db96d56Sopenharmony_ci        with self.assertRaises(SyntaxError) as context:
1867db96d56Sopenharmony_ci            exec(python2_print_str)
1877db96d56Sopenharmony_ci
1887db96d56Sopenharmony_ci        self.assertIn("Missing parentheses in call to 'print'. Did you mean print(...)",
1897db96d56Sopenharmony_ci                str(context.exception))
1907db96d56Sopenharmony_ci
1917db96d56Sopenharmony_ci    def test_stream_redirection_hint_for_py2_migration(self):
1927db96d56Sopenharmony_ci        # Test correct hint produced for Py2 redirection syntax
1937db96d56Sopenharmony_ci        with self.assertRaises(TypeError) as context:
1947db96d56Sopenharmony_ci            print >> sys.stderr, "message"
1957db96d56Sopenharmony_ci        self.assertIn('Did you mean "print(<message>, '
1967db96d56Sopenharmony_ci                'file=<output_stream>)"?', str(context.exception))
1977db96d56Sopenharmony_ci
1987db96d56Sopenharmony_ci        # Test correct hint is produced in the case where RHS implements
1997db96d56Sopenharmony_ci        # __rrshift__ but returns NotImplemented
2007db96d56Sopenharmony_ci        with self.assertRaises(TypeError) as context:
2017db96d56Sopenharmony_ci            print >> 42
2027db96d56Sopenharmony_ci        self.assertIn('Did you mean "print(<message>, '
2037db96d56Sopenharmony_ci                'file=<output_stream>)"?', str(context.exception))
2047db96d56Sopenharmony_ci
2057db96d56Sopenharmony_ci        # Test stream redirection hint is specific to print
2067db96d56Sopenharmony_ci        with self.assertRaises(TypeError) as context:
2077db96d56Sopenharmony_ci            max >> sys.stderr
2087db96d56Sopenharmony_ci        self.assertNotIn('Did you mean ', str(context.exception))
2097db96d56Sopenharmony_ci
2107db96d56Sopenharmony_ci        # Test stream redirection hint is specific to rshift
2117db96d56Sopenharmony_ci        with self.assertRaises(TypeError) as context:
2127db96d56Sopenharmony_ci            print << sys.stderr
2137db96d56Sopenharmony_ci        self.assertNotIn('Did you mean', str(context.exception))
2147db96d56Sopenharmony_ci
2157db96d56Sopenharmony_ci        # Ensure right operand implementing rrshift still works
2167db96d56Sopenharmony_ci        class OverrideRRShift:
2177db96d56Sopenharmony_ci            def __rrshift__(self, lhs):
2187db96d56Sopenharmony_ci                return 42 # Force result independent of LHS
2197db96d56Sopenharmony_ci
2207db96d56Sopenharmony_ci        self.assertEqual(print >> OverrideRRShift(), 42)
2217db96d56Sopenharmony_ci
2227db96d56Sopenharmony_ci
2237db96d56Sopenharmony_ci
2247db96d56Sopenharmony_ciif __name__ == "__main__":
2257db96d56Sopenharmony_ci    unittest.main()
226