Skip to content

Commit b00e63e

Browse files
authored
Merge pull request RustPython#4778 from JaeyoungAhn/test_print
added test_print.py and marked TODOs
2 parents 322165f + 074e4b3 commit b00e63e

File tree

1 file changed

+240
-0
lines changed

1 file changed

+240
-0
lines changed

Lib/test/test_print.py

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

0 commit comments

Comments
 (0)