@@ -47,36 +47,6 @@ assert match('', 'abc') == (False, False)
47
47
[case testStringOps]
48
48
from typing import List, Optional
49
49
50
- var = 'mypyc'
51
-
52
- num = 20
53
-
54
- def test_fstring_simple() -> None:
55
- f1 = f'Hello {var}, this is a test'
56
- assert f1 == "Hello mypyc, this is a test"
57
-
58
- def test_fstring_conversion() -> None:
59
- f2 = f'Hello {var!r}'
60
- assert f2 == "Hello 'mypyc'"
61
- f3 = f'Hello {var!a}'
62
- assert f3 == "Hello 'mypyc'"
63
- f4 = f'Hello {var!s}'
64
- assert f4 == "Hello mypyc"
65
-
66
- def test_fstring_align() -> None:
67
- f5 = f'Hello {var:>20}'
68
- assert f5 == "Hello mypyc"
69
- f6 = f'Hello {var!r:>20}'
70
- assert f6 == "Hello 'mypyc'"
71
- f7 = f'Hello {var:>{num}}'
72
- assert f7 == "Hello mypyc"
73
- f8 = f'Hello {var!r:>{num}}'
74
- assert f8 == "Hello 'mypyc'"
75
-
76
- def test_fstring_multi() -> None:
77
- f9 = f'Hello {var}, hello again {var}'
78
- assert f9 == "Hello mypyc, hello again mypyc"
79
-
80
50
def do_split(s: str, sep: Optional[str] = None, max_split: Optional[int] = None) -> List[str]:
81
51
if sep is not None:
82
52
if max_split is not None:
@@ -178,3 +148,237 @@ def test_str_to_bool() -> None:
178
148
for x in 'a', 'foo', 'bar', 'some string':
179
149
assert is_true(x)
180
150
assert not is_false(x)
151
+
152
+ [case testFStrings]
153
+ import decimal
154
+ from datetime import datetime
155
+
156
+ var = 'mypyc'
157
+ num = 20
158
+
159
+ def test_fstring_basics() -> None:
160
+ assert f'Hello {var}, this is a test' == "Hello mypyc, this is a test"
161
+
162
+ large_num = 2**65
163
+ assert f'number: {large_num}' == 'number: 36893488147419103232'
164
+ neg_num = -3
165
+ assert f'negative integer: {neg_num}' == 'negative integer: -3'
166
+ assert f'negative integer: {-large_num}' == 'negative integer: -36893488147419103232'
167
+
168
+ bool_var1 = True
169
+ bool_var2 = False
170
+ assert f'bool: {bool_var1}, {bool_var2}' == 'bool: True, False'
171
+
172
+ x = bytes([1, 2, 3, 4])
173
+ # assert f'bytes: {x}' == "bytes: b'\\x01\\x02\\x03\\x04'"
174
+ # error: On Python 3 '{}'.format(b'abc') produces "b'abc'", not 'abc';
175
+ # use '{!r}'.format(b'abc') if this is desired behavior
176
+
177
+ float_num = 123.4
178
+ assert f'{float_num}' == '123.4'
179
+ assert f'{float_num:.2f}' == '123.40'
180
+ assert f'{float_num:.5f}' == '123.40000'
181
+ assert f'{float_num:>10.2f}' == ' 123.40'
182
+ assert f'{float_num:>10.5f}' == ' 123.40000'
183
+ assert f'{float_num:>010.5f}' == '0123.40000'
184
+ assert f'{float_num:>015.5f}' == '000000123.40000'
185
+ assert f'{float_num:e}' == '1.234000e+02'
186
+
187
+ large_float = 1.23e30
188
+ large_float2 = 1234123412341234123400000000000000000
189
+ small_float = 1.23e-20
190
+ assert f'{small_float}, {large_float}, {large_float2}' == '1.23e-20, 1.23e+30, 1234123412341234123400000000000000000'
191
+ nan_num = float('nan')
192
+ inf_num = float('inf')
193
+ assert f'{nan_num}, {inf_num}' == 'nan, inf'
194
+
195
+ class A:
196
+ def __init__(self, name, age):
197
+ self.name = name
198
+ self.age = age
199
+
200
+ def __repr__(self):
201
+ return f"{self.name} is {self.age} years old."
202
+
203
+ def test_fstring_datatype() -> None:
204
+ u = A('John Doe', 14)
205
+ assert f'{u}' == 'John Doe is 14 years old.'
206
+ d = {'name': 'John Doe', 'age': 14}
207
+ assert f'{d}' == "{'name': 'John Doe', 'age': 14}"
208
+
209
+ def test_fstring_escape() -> None:
210
+ assert f"{'inside'}" == 'inside'
211
+ assert f'{"inside"}' == 'inside'
212
+ assert f"""inside""" == 'inside'
213
+ assert f'''inside''' == 'inside'
214
+ assert f"\"{'inside'}\"" == '"inside"'
215
+ assert f'\'{"inside"}\'' == "'inside'"
216
+
217
+ assert f'{{10}}' == '{10}'
218
+ assert f'{{10 + 10}}' == '{10 + 10}'
219
+ assert f'{{{10 + 10}}}' == '{20}'
220
+ assert f'{{{{10 + 10}}}}' == '{{10 + 10}}'
221
+
222
+ def test_fstring_conversion() -> None:
223
+ assert f'Hello {var!r}' == "Hello 'mypyc'"
224
+ # repr() is equivalent to !r
225
+ assert f'Hello {repr(var)}' == "Hello 'mypyc'"
226
+
227
+ assert f'Hello {var!a}' == "Hello 'mypyc'"
228
+ # ascii() is equivalent to !a
229
+ assert f'Hello {ascii(var)}' == "Hello 'mypyc'"
230
+
231
+ tmp_str = """this
232
+ is a new line."""
233
+ assert f'Test: {tmp_str!a}' == "Test: 'this\\n is a new line.'"
234
+
235
+ s = 'test: āĀēĒčČ..šŠūŪžŽ'
236
+ assert f'{s}' == 'test: āĀēĒčČ..šŠūŪžŽ'
237
+ assert f'{s!a}' == "'test: \\u0101\\u0100\\u0113\\u0112\\u010d\\u010c..\\u0161\\u0160\\u016b\\u016a\\u017e\\u017d'"
238
+
239
+ assert f'Hello {var!s}' == "Hello mypyc"
240
+ assert f'Hello {num!s}' == "Hello 20"
241
+
242
+ def test_fstring_align() -> None:
243
+ assert f'Hello {var:>20}' == "Hello mypyc"
244
+ assert f'Hello {var!r:>20}' == "Hello 'mypyc'"
245
+ assert f'Hello {var:>{num}}' == "Hello mypyc"
246
+ assert f'Hello {var!r:>{num}}' == "Hello 'mypyc'"
247
+
248
+ def test_fstring_multi() -> None:
249
+ assert f'Hello {var}, hello again {var}' == "Hello mypyc, hello again mypyc"
250
+ a = 'py'
251
+ s = f'my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}my{a}'
252
+ assert s == 'mypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypy'
253
+
254
+ def test_fstring_python_doc() -> None:
255
+ name = "Fred"
256
+ assert f"He said his name is {name!r}." == "He said his name is 'Fred'."
257
+ assert f"He said his name is {repr(name)}." == "He said his name is 'Fred'."
258
+
259
+ width = 10
260
+ precision = 4
261
+ value = decimal.Decimal("12.34567")
262
+ assert f"result: {value:{width}.{precision}}" == 'result: 12.35' # nested field
263
+
264
+ today = datetime(year=2017, month=1, day=27)
265
+ assert f"{today:%B %d, %Y}" == 'January 27, 2017' # using date format specifier
266
+
267
+ number = 1024
268
+ assert f"{number:#0x}" == '0x400' # using integer format specifier
269
+
270
+ [case testStringFormatMethod]
271
+ from typing import Tuple
272
+
273
+ def test_format_method_basics() -> None:
274
+ assert "".format() == ""
275
+ assert "abc".format() == "abc"
276
+
277
+ name = "Eric"
278
+ age = 14
279
+ assert "My name is {name}, I'm {age}.".format(name=name, age=age) == "My name is Eric, I'm 14."
280
+ assert "My name is {A}, I'm {B}.".format(A=name, B=age) == "My name is Eric, I'm 14."
281
+ assert "My name is {}, I'm {B}.".format(name, B=age) == "My name is Eric, I'm 14."
282
+
283
+ def test_format_method_numbers() -> None:
284
+ s = 'int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}'.format(-233)
285
+ assert s == 'int: -233; hex: -e9; oct: -351; bin: -11101001'
286
+ num = 2**65
287
+ s = 'int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}'.format(num)
288
+ assert s == 'int: 36893488147419103232; hex: 20000000000000000; oct: 4000000000000000000000; bin: 100000000000000000000000000000000000000000000000000000000000000000'
289
+ s = 'int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}'.format(-num)
290
+ assert s == 'int: -36893488147419103232; hex: -20000000000000000; oct: -4000000000000000000000; bin: -100000000000000000000000000000000000000000000000000000000000000000'
291
+
292
+ large_num = 2**65
293
+ assert 'number: {}'.format(large_num) == 'number: 36893488147419103232'
294
+ neg_num = -3
295
+ assert 'negative integer: {}'.format(neg_num) == 'negative integer: -3'
296
+ assert 'negative integer: {}'.format(-large_num) == 'negative integer: -36893488147419103232'
297
+
298
+
299
+ class Point:
300
+ def __init__(self, x, y):
301
+ self.x, self.y = x, y
302
+ def __str__(self):
303
+ return 'Point({self.x}, {self.y})'.format(self=self)
304
+
305
+ # Format examples from Python doc
306
+ # https://docs.python.org/3/library/string.html#formatexamples
307
+ def test_format_method_python_doc() -> None:
308
+ # Accessing arguments by position:
309
+ assert '{0}, {1}, {2}'.format('a', 'b', 'c') == 'a, b, c'
310
+ assert '{}, {}, {}'.format('a', 'b', 'c') == 'a, b, c'
311
+ assert '{2}, {1}, {0}'.format('a', 'b', 'c') == 'c, b, a'
312
+ assert '{2}, {1}, {0}'.format(*'abc') == 'c, b, a' # unpacking argument sequence
313
+ # assert '{0}{1}{0}'.format('abra', 'cad') = 'abracadabra' # arguments' indices can be repeated
314
+
315
+ # Accessing arguments by name:
316
+ s = 'Coordinates: {latitude}, {longitude}'.format(latitude='37.24N', longitude='-115.81W')
317
+ assert s == 'Coordinates: 37.24N, -115.81W'
318
+ coord = {'latitude': '37.24N', 'longitude': '-115.81W'}
319
+ assert 'Coordinates: {latitude}, {longitude}'.format(**coord) == 'Coordinates: 37.24N, -115.81W'
320
+
321
+ # Accessing arguments’ attributes:
322
+ assert str(Point(4, 2)) == "Point(4, 2)"
323
+
324
+ # Accessing arguments’ items:
325
+ coord2 = (3, 5)
326
+ assert 'X: {0[0]}; Y: {0[1]}'.format(coord2) == 'X: 3; Y: 5'
327
+
328
+ # Replacing %s and %r:
329
+ s = "repr() shows quotes: {!r}; str() doesn't: {!s}".format('test1', 'test2')
330
+ assert s == "repr() shows quotes: 'test1'; str() doesn't: test2"
331
+
332
+ # Aligning the text and specifying a width:
333
+ assert '{:<30}'.format('left aligned') == 'left aligned '
334
+ assert '{:>30}'.format('right aligned') == ' right aligned'
335
+ assert '{:^30}'.format('centered') == ' centered '
336
+ assert '{:*^30}'.format('centered') == '***********centered***********' # use '*' as a fill char
337
+
338
+ # Replacing %+f, %-f, and % f and specifying a sign:
339
+ assert '{:+f}; {:+f}'.format(3.14, -3.14) == '+3.140000; -3.140000' # show it always
340
+ assert '{: f}; {: f}'.format(3.14, -3.14) == ' 3.140000; -3.140000' # show a space for positive numbers
341
+ assert '{:-f}; {:-f}'.format(3.14, -3.14) == '3.140000; -3.140000' # show only the minus -- same as '{:f}; {:f}'
342
+
343
+ # Replacing %x and %o and converting the value to different bases:
344
+ s = 'int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}'.format(42) # format also supports binary numbers
345
+ assert s == 'int: 42; hex: 2a; oct: 52; bin: 101010'
346
+ s = 'int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}'.format(42) # with 0x, 0o, or 0b as prefix:
347
+ assert s == 'int: 42; hex: 0x2a; oct: 0o52; bin: 0b101010'
348
+
349
+ # Using the comma as a thousands separator:
350
+ assert '{:,}'.format(1234567890) == '1,234,567,890'
351
+
352
+ # Expressing a percentage:
353
+ points = 19.0
354
+ total = 22.0
355
+ assert 'Correct answers: {:.2%}'.format(points/total) == 'Correct answers: 86.36%'
356
+
357
+ # Using type-specific formatting:
358
+ import datetime
359
+ d = datetime.datetime(2010, 7, 4, 12, 15, 58)
360
+ assert '{:%Y-%m-%d %H:%M:%S}'.format(d) == '2010-07-04 12:15:58'
361
+
362
+ # Nesting arguments and more complex examples:
363
+ tmp_strs = []
364
+ for align, text in zip('<^>', ['left', 'center', 'right']):
365
+ tmp_strs.append('{0:{fill}{align}16}'.format(text, fill=align, align=align))
366
+ assert tmp_strs == ['left<<<<<<<<<<<<', '^^^^^center^^^^^', '>>>>>>>>>>>right']
367
+
368
+ octets = [192, 168, 0, 1]
369
+ assert '{:02X}{:02X}{:02X}{:02X}'.format(*octets) == 'C0A80001'
370
+
371
+ width = 5
372
+ tmp_strs = []
373
+ for num in range(5,12):
374
+ tmp_str = ""
375
+ for base in 'dXob':
376
+ tmp_str += ('{0:{width}{base}}'.format(num, base=base, width=width))
377
+ tmp_strs.append(tmp_str)
378
+ assert tmp_strs == [' 5 5 5 101',\
379
+ ' 6 6 6 110',\
380
+ ' 7 7 7 111',\
381
+ ' 8 8 10 1000',\
382
+ ' 9 9 11 1001',\
383
+ ' 10 A 12 1010',\
384
+ ' 11 B 13 1011']
0 commit comments