| # Test cases for strings (compile and run) | |
| [case testStrBasics] | |
| from typing import Tuple | |
| class A: | |
| def __str__(self) -> str: | |
| return "A-str" | |
| def __repr__(self) -> str: | |
| return "A-repr" | |
| def f() -> str: | |
| return 'some string' | |
| def g() -> str: | |
| return 'some\a \v \t \x7f " \n \0string 🐍' | |
| def tostr(x: int) -> str: | |
| return str(x) | |
| def booltostr(x: bool) -> str: | |
| return str(x) | |
| def clstostr(x: A) -> str: | |
| return str(x) | |
| def torepr(x: int) -> str: | |
| return repr(x) | |
| def booltorepr(x: bool) -> str: | |
| return repr(x) | |
| def clstorepr(x: A) -> str: | |
| return repr(x) | |
| def concat(x: str, y: str) -> str: | |
| return x + y | |
| def eq(x: str) -> int: | |
| if x == 'foo': | |
| return 0 | |
| elif x != 'bar': | |
| return 1 | |
| return 2 | |
| def match(x: str, y: str) -> Tuple[bool, bool]: | |
| return (x.startswith(y), x.endswith(y)) | |
| def match_tuple(x: str, y: Tuple[str, ...]) -> Tuple[bool, bool]: | |
| return (x.startswith(y), x.endswith(y)) | |
| def match_tuple_literal_args(x: str, y: str, z: str) -> Tuple[bool, bool]: | |
| return (x.startswith((y, z)), x.endswith((y, z))) | |
| def remove_prefix_suffix(x: str, y: str) -> Tuple[str, str]: | |
| return (x.removeprefix(y), x.removesuffix(y)) | |
| [file driver.py] | |
| from native import ( | |
| f, g, A, tostr, booltostr, clstostr, concat, eq, match, match_tuple, | |
| match_tuple_literal_args, remove_prefix_suffix, | |
| torepr, booltorepr, clstorepr | |
| ) | |
| import sys | |
| from testutil import assertRaises | |
| assert f() == 'some string' | |
| assert f() is sys.intern('some string') | |
| assert g() == 'some\a \v \t \x7f " \n \0string 🐍' | |
| assert tostr(57) == '57' | |
| assert concat('foo', 'bar') == 'foobar' | |
| assert booltostr(True) == 'True' | |
| assert booltostr(False) == 'False' | |
| assert clstostr(A()) == "A-str" | |
| assert eq('foo') == 0 | |
| assert eq('zar') == 1 | |
| assert eq('bar') == 2 | |
| assert torepr(57) == '57' | |
| assert booltorepr(True) == 'True' | |
| assert booltorepr(False) == 'False' | |
| assert clstorepr(A()) == "A-repr" | |
| assert int(tostr(0)) == 0 | |
| assert int(tostr(20)) == 20 | |
| assert int(torepr(0)) == 0 | |
| assert int(torepr(20)) == 20 | |
| assert match('', '') == (True, True) | |
| assert match('abc', '') == (True, True) | |
| assert match('abc', 'a') == (True, False) | |
| assert match('abc', 'c') == (False, True) | |
| assert match('', 'abc') == (False, False) | |
| assert match_tuple('abc', ('d', 'e')) == (False, False) | |
| assert match_tuple('abc', ('a', 'c')) == (True, True) | |
| assert match_tuple('abc', ('a',)) == (True, False) | |
| assert match_tuple('abc', ('c',)) == (False, True) | |
| assert match_tuple('abc', ('x', 'y', 'z')) == (False, False) | |
| assert match_tuple('abc', ('x', 'y', 'z', 'a', 'c')) == (True, True) | |
| with assertRaises(TypeError, "tuple for startswith must only contain str"): | |
| assert match_tuple('abc', (None,)) | |
| with assertRaises(TypeError, "tuple for endswith must only contain str"): | |
| assert match_tuple('abc', ('a', None)) | |
| assert match_tuple_literal_args('abc', 'z', 'a') == (True, False) | |
| assert match_tuple_literal_args('abc', 'z', 'c') == (False, True) | |
| assert remove_prefix_suffix('', '') == ('', '') | |
| assert remove_prefix_suffix('abc', 'a') == ('bc', 'abc') | |
| assert remove_prefix_suffix('abc', 'c') == ('abc', 'ab') | |
| [case testStringEquality] | |
| def eq(a: str, b: str) -> bool: | |
| return a == b | |
| def ne(a: str, b: str) -> bool: | |
| return a != b | |
| def test_basic() -> None: | |
| xy = "xy" | |
| xy2 = str().join(["x", "y"]) | |
| xx = "xx" | |
| yy = "yy" | |
| xxx = "xxx" | |
| assert eq("", str()) | |
| assert not ne("", str()) | |
| assert eq("x", "x" + str()) | |
| assert ne("x", "y") | |
| assert eq(xy, xy) | |
| assert eq(xy, xy2) | |
| assert not eq(xy, yy) | |
| assert ne(xy, xx) | |
| assert not ne(xy, xy) | |
| assert not ne(xy, xy2) | |
| assert ne(xx, xxx) | |
| assert ne(xxx, xx) | |
| assert ne("x", "") | |
| assert ne("", "x") | |
| assert ne("XX", xx) | |
| assert ne(yy, xy) | |
| def test_unicode() -> None: | |
| assert eq(chr(200), chr(200) + str()) | |
| assert ne(chr(200), chr(201)) | |
| assert eq(chr(1234), chr(1234) + str()) | |
| assert ne(chr(1234), chr(1235)) | |
| assert eq("\U0001f4a9", "\U0001f4a9" + str()) | |
| assert eq("\U0001f4a9", "\U0001F4A9" + str()) | |
| assert ne("\U0001f4a9", "\U0002f4a9" + str()) | |
| assert ne("\U0001f4a9", "\U0001f5a9" + str()) | |
| assert ne("\U0001f4a9", "\U0001f4a8" + str()) | |
| assert eq("foobar\u1234", "foobar\u1234" + str()) | |
| assert eq("\u1234foobar", "\u1234foobar" + str()) | |
| assert ne("foobar\uf234", "foobar\uf235") | |
| assert ne("foobar\uf234", "foobar\uf334") | |
| assert ne("foobar\u1234", "Foobar\u1234" + str()) | |
| assert eq("foo\U0001f4a9", "foo\U0001f4a9" + str()) | |
| assert eq("\U0001f4a9foo", "\U0001f4a9foo" + str()) | |
| assert ne("foo\U0001f4a9", "foo\U0001f4a8" + str()) | |
| assert ne("\U0001f4a9foo", "\U0001f4a8foo" + str()) | |
| [case testStringOps] | |
| from typing import List, Optional, Tuple | |
| from testutil import assertRaises | |
| def do_split(s: str, sep: Optional[str] = None, max_split: Optional[int] = None) -> List[str]: | |
| if sep is not None: | |
| if max_split is not None: | |
| return s.split(sep, max_split) | |
| else: | |
| return s.split(sep) | |
| return s.split() | |
| def do_rsplit(s: str, sep: Optional[str] = None, max_split: Optional[int] = None) -> List[str]: | |
| if sep is not None: | |
| if max_split is not None: | |
| return s.rsplit(sep, max_split) | |
| else: | |
| return s.rsplit(sep) | |
| return s.rsplit() | |
| ss = "abc abcd abcde abcdef" | |
| def test_split() -> None: | |
| assert do_split(ss) == ["abc", "abcd", "abcde", "abcdef"] | |
| assert do_split(ss, " ") == ["abc", "abcd", "abcde", "abcdef"] | |
| assert do_split(ss, "-") == ["abc abcd abcde abcdef"] | |
| assert do_split(ss, " ", -1) == ["abc", "abcd", "abcde", "abcdef"] | |
| assert do_split(ss, " ", 0) == ["abc abcd abcde abcdef"] | |
| assert do_split(ss, " ", 1) == ["abc", "abcd abcde abcdef"] | |
| assert do_split(ss, " ", 2) == ["abc", "abcd", "abcde abcdef"] | |
| def test_rsplit() -> None: | |
| assert do_rsplit(ss) == ["abc", "abcd", "abcde", "abcdef"] | |
| assert do_rsplit(ss, " ") == ["abc", "abcd", "abcde", "abcdef"] | |
| assert do_rsplit(ss, "-") == ["abc abcd abcde abcdef"] | |
| assert do_rsplit(ss, " ", -1) == ["abc", "abcd", "abcde", "abcdef"] | |
| assert do_rsplit(ss, " ", 0) == ["abc abcd abcde abcdef"] | |
| assert do_rsplit(ss, " ", 1) == ["abc abcd abcde", "abcdef"] # different to do_split | |
| assert do_rsplit(ss, " ", 2) == ["abc abcd", "abcde", "abcdef"] # different to do_split | |
| def splitlines(s: str, keepends: Optional[bool] = None) -> List[str]: | |
| if keepends is not None: | |
| return s.splitlines(keepends) | |
| return s.splitlines() | |
| s_text = "This\nis\n\nsome\nlong\ntext.\n" | |
| def test_splitlines() -> None: | |
| assert splitlines(s_text) == ["This", "is", "", "some", "long", "text."] | |
| assert splitlines(s_text, False) == ["This", "is", "", "some", "long", "text."] | |
| assert splitlines(s_text, True) == ["This\n", "is\n", "\n", "some\n", "long\n", "text.\n"] | |
| s_partition = "Some long text" | |
| def partition(s: str, sep: str) -> Tuple[str, str, str]: | |
| return s.partition(sep) | |
| def rpartition(s: str, sep: str) -> Tuple[str, str, str]: | |
| return s.rpartition(sep) | |
| def test_partition() -> None: | |
| assert partition(s_partition, " ") == ("Some", " ", "long text") | |
| assert partition(s_partition, "Hello") == ("Some long text", "", "") | |
| assert rpartition(s_partition, " ") == ("Some long", " ", "text") | |
| assert rpartition(s_partition, "Hello") == ("", "", "Some long text") | |
| with assertRaises(ValueError, "empty separator"): | |
| partition(s_partition, "") | |
| with assertRaises(ValueError, "empty separator"): | |
| rpartition(s_partition, "") | |
| def contains(s: str, o: str) -> bool: | |
| return o in s | |
| def getitem(s: str, index: int) -> str: | |
| return s[index] | |
| def find(s: str, substr: str, start: Optional[int] = None, end: Optional[int] = None) -> int: | |
| if start is not None: | |
| if end is not None: | |
| return s.find(substr, start, end) | |
| return s.find(substr, start) | |
| return s.find(substr) | |
| def rfind(s: str, substr: str, start: Optional[int] = None, end: Optional[int] = None) -> int: | |
| if start is not None: | |
| if end is not None: | |
| return s.rfind(substr, start, end) | |
| return s.rfind(substr, start) | |
| return s.rfind(substr) | |
| s = "abc" | |
| def test_contains() -> None: | |
| assert contains(s, "a") is True | |
| assert contains(s, "abc") is True | |
| assert contains(s, "Hello") is False | |
| assert contains(s, "bc") is True | |
| assert contains(s, "abcd") is False | |
| assert contains(s, "bb") is False | |
| assert contains(s, "") is True | |
| assert contains(s, " ") is False | |
| def test_getitem() -> None: | |
| assert getitem(s, 0) == "a" | |
| assert getitem(s, 1) == "b" | |
| assert getitem(s, 2) == "c" | |
| assert getitem(s, -3) == "a" | |
| assert getitem(s, -2) == "b" | |
| assert getitem(s, -1) == "c" | |
| with assertRaises(IndexError, "string index out of range"): | |
| getitem(s, 4) | |
| with assertRaises(IndexError, "string index out of range"): | |
| getitem(s, -4) | |
| def test_getitem_unicode() -> None: | |
| # ASCII - cached by CPython's Latin-1 table | |
| ascii_s = "Hello" | |
| assert getitem(ascii_s, 0) == "H" | |
| assert getitem(ascii_s, 4) == "o" | |
| # Latin-1 (>127, <256) - also cached | |
| latin1_s = "\xe9\xfc\xf1" # éüñ | |
| assert getitem(latin1_s, 0) == "\xe9" | |
| assert getitem(latin1_s, 1) == "\xfc" | |
| assert getitem(latin1_s, 2) == "\xf1" | |
| # BMP (>255) - not cached, allocated fresh | |
| bmp_s = "\u4e2d\u6587" # 中文 | |
| assert getitem(bmp_s, 0) == "\u4e2d" | |
| assert getitem(bmp_s, 1) == "\u6587" | |
| # Supplementary plane (>65535) | |
| emoji_s = "\U0001f600\U0001f601" # 😀😁 | |
| assert getitem(emoji_s, 0) == "\U0001f600" | |
| assert getitem(emoji_s, 1) == "\U0001f601" | |
| # Mixed string with all kinds | |
| mixed = "A\xe9\u4e2d\U0001f600" | |
| assert getitem(mixed, 0) == "A" | |
| assert getitem(mixed, 1) == "\xe9" | |
| assert getitem(mixed, 2) == "\u4e2d" | |
| assert getitem(mixed, 3) == "\U0001f600" | |
| # Identity check: ASCII/Latin-1 chars should return cached objects | |
| a1 = getitem("abc", 0) | |
| a2 = getitem("axyz", 0) | |
| assert a1 is a2 # both 'a', should be same cached object | |
| def test_find() -> None: | |
| s = "abcab" | |
| assert find(s, "Hello") == -1 | |
| assert find(s, "abc") == 0 | |
| assert find(s, "b") == 1 | |
| assert find(s, "b", 1) == 1 | |
| assert find(s, "b", 1, 2) == 1 | |
| assert find(s, "b", 3) == 4 | |
| assert find(s, "b", 3, 5) == 4 | |
| assert find(s, "b", 3, 4) == -1 | |
| assert rfind(s, "Hello") == -1 | |
| assert rfind(s, "abc") == 0 | |
| assert rfind(s, "b") == 4 | |
| assert rfind(s, "b", 1) == 4 | |
| assert rfind(s, "b", 1, 2) == 1 | |
| assert rfind(s, "b", 3) == 4 | |
| assert rfind(s, "b", 3, 5) == 4 | |
| assert rfind(s, "b", 3, 4) == -1 | |
| def str_to_int(s: str, base: Optional[int] = None) -> int: | |
| if base: | |
| return int(s, base) | |
| else: | |
| return int(s) | |
| def test_str_to_int() -> None: | |
| assert str_to_int("1") == 1 | |
| assert str_to_int("10") == 10 | |
| assert str_to_int("a", 16) == 10 | |
| assert str_to_int("1a", 16) == 26 | |
| with assertRaises(ValueError, "invalid literal for int() with base 10: 'xyz'"): | |
| str_to_int("xyz") | |
| def test_slicing() -> None: | |
| # Use dummy adds to avoid constant folding | |
| zero = int() | |
| two = zero + 2 | |
| s = "foobar" + str() | |
| assert s[two:] == "obar" | |
| assert s[:two] == "fo" | |
| assert s[two:-two] == "ob" | |
| assert s[two:two] == "" | |
| assert s[two:two + 1] == "o" | |
| assert s[-two:] == "ar" | |
| assert s[:-two] == "foob" | |
| assert s[:] == "foobar" | |
| assert s[two:333] == "obar" | |
| assert s[333:two] == "" | |
| assert s[two:-333] == "" | |
| assert s[-333:two] == "fo" | |
| big_int: int = 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 | |
| assert s[1:big_int] == "oobar" | |
| assert s[big_int:] == "" | |
| assert s[-big_int:-1] == "fooba" | |
| def test_str_replace() -> None: | |
| a = "foofoofoo" | |
| assert a.replace("foo", "bar") == "barbarbar" | |
| assert a.replace("foo", "bar", -1) == "barbarbar" | |
| assert a.replace("foo", "bar", 1) == "barfoofoo" | |
| assert a.replace("foo", "bar", 4) == "barbarbar" | |
| assert a.replace("aaa", "bar") == "foofoofoo" | |
| assert a.replace("ofo", "xyzw") == "foxyzwxyzwo" | |
| def is_true(x: str) -> bool: | |
| if x: | |
| return True | |
| else: | |
| return False | |
| def is_true2(x: str) -> bool: | |
| return bool(x) | |
| def is_false(x: str) -> bool: | |
| if not x: | |
| return True | |
| else: | |
| return False | |
| def test_str_to_bool() -> None: | |
| assert is_false('') | |
| assert not is_true('') | |
| assert not is_true2('') | |
| for x in 'a', 'foo', 'bar', 'some string': | |
| assert is_true(x) | |
| assert is_true2(x) | |
| assert not is_false(x) | |
| def test_str_min_max() -> None: | |
| x: str = 'aaa' | |
| y: str = 'bbb' | |
| z: str = 'aa' | |
| assert min(x, y) == 'aaa' | |
| assert min(x, z) == 'aa' | |
| assert max(x, y) == 'bbb' | |
| assert max(x, z) == 'aaa' | |
| def test_multiply() -> None: | |
| # Use str() and int() to avoid constant folding | |
| s = 'ab' + str() | |
| zero = int() | |
| one = 1 + zero | |
| three = 3 + zero | |
| neg_one = -1 + zero | |
| assert s * zero == '' | |
| assert s * one == 'ab' | |
| assert s * three == 'ababab' | |
| assert s * neg_one == '' | |
| assert zero * s == '' | |
| assert one * s == 'ab' | |
| assert three * s == 'ababab' | |
| assert neg_one * s == '' | |
| # Test with empty string | |
| empty = str() | |
| five = 5 + zero | |
| assert empty * five == '' | |
| assert five * empty == '' | |
| # Test with single character | |
| single = 'x' + str() | |
| four = 4 + zero | |
| assert single * four == 'xxxx' | |
| assert four * single == 'xxxx' | |
| # Test type preservation | |
| two = 2 + zero | |
| result = s * two | |
| assert type(result) == str | |
| [case testStringFormattingCStyle] | |
| from typing import Tuple | |
| var = 'mypyc' | |
| num = 20 | |
| def test_basics() -> None: | |
| assert 'Hello %s, this is a test' % var == "Hello mypyc, this is a test" | |
| assert 'Hello %s %d, this is a test' % (var, num) == "Hello mypyc 20, this is a test" | |
| t: Tuple[str, int] = (var, num) | |
| assert 'Hello %s %d, this is a test' % t == "Hello mypyc 20, this is a test" | |
| large_num = 2**65 | |
| assert 'number: %d' % large_num == 'number: 36893488147419103232' | |
| neg_num = -3 | |
| assert 'negative integer: %d' % neg_num == 'negative integer: -3' | |
| assert 'negative integer: %d' % (-large_num) == 'negative integer: -36893488147419103232' | |
| bool_var1 = True | |
| bool_var2 = False | |
| assert 'bool: %s, %s' % (bool_var1, bool_var2) == 'bool: True, False' | |
| float_num = 123.4 | |
| assert '%f' % float_num == '123.400000' | |
| assert '%.2f' % float_num == '123.40' | |
| assert '%.5f' % float_num == '123.40000' | |
| assert '%10.2f' % float_num == ' 123.40' | |
| assert '%10.5f' % float_num == ' 123.40000' | |
| assert '%010.5f' % float_num == '0123.40000' | |
| assert '%015.5f' % float_num == '000000123.40000' | |
| assert '%e' % float_num == '1.234000e+02' | |
| large_float = 1.23e30 | |
| large_float2 = 1234123412341234123400000000000000000 | |
| small_float = 1.23e-20 | |
| assert '%f, %f, %f' % (small_float, large_float, large_float2) == \ | |
| '0.000000, 1229999999999999959718843908096.000000, 1234123412341234169005079998930878464.000000' | |
| assert '%s, %s, %s' % (small_float, large_float, large_float2) == \ | |
| '1.23e-20, 1.23e+30, 1234123412341234123400000000000000000' | |
| assert '%d, %d, %d' % (small_float, large_float, large_float2) == \ | |
| '0, 1229999999999999959718843908096, 1234123412341234123400000000000000000' | |
| nan_num = float('nan') | |
| inf_num = float('inf') | |
| assert '%s, %s' % (nan_num, inf_num) == 'nan, inf' | |
| assert '%f, %f' % (nan_num, inf_num) == 'nan, inf' | |
| [typing fixtures/typing-full.pyi] | |
| [case testFStrings] | |
| import decimal | |
| from datetime import datetime | |
| from typing import Final | |
| var = 'mypyc' | |
| num = 20 | |
| final_known_at_compile_time: Final = 'hello' | |
| def final_value_setter() -> str: | |
| return 'goodbye' | |
| final_unknown_at_compile_time: Final = final_value_setter() | |
| def test_fstring_basics() -> None: | |
| assert f'Hello {var}, this is a test' == "Hello mypyc, this is a test" | |
| large_num = 2**65 | |
| assert f'number: {large_num}' == 'number: 36893488147419103232' | |
| neg_num = -3 | |
| assert f'negative integer: {neg_num}' == 'negative integer: -3' | |
| assert f'negative integer: {-large_num}' == 'negative integer: -36893488147419103232' | |
| bool_var1 = True | |
| bool_var2 = False | |
| assert f'bool: {bool_var1}, {bool_var2}' == 'bool: True, False' | |
| x = bytes([1, 2, 3, 4]) | |
| # assert f'bytes: {x}' == "bytes: b'\\x01\\x02\\x03\\x04'" | |
| # error: If x = b'abc' then f"{x}" or "{}".format(x) produces "b'abc'", not "abc". If this is desired behavior, use f"{x!r}" or "{!r}".format(x). Otherwise, decode the bytes | |
| float_num = 123.4 | |
| assert f'{float_num}' == '123.4' | |
| assert f'{float_num:.2f}' == '123.40' | |
| assert f'{float_num:.5f}' == '123.40000' | |
| assert f'{float_num:>10.2f}' == ' 123.40' | |
| assert f'{float_num:>10.5f}' == ' 123.40000' | |
| assert f'{float_num:>010.5f}' == '0123.40000' | |
| assert f'{float_num:>015.5f}' == '000000123.40000' | |
| assert f'{float_num:e}' == '1.234000e+02' | |
| large_float = 1.23e30 | |
| large_float2 = 1234123412341234123400000000000000000 | |
| small_float = 1.23e-20 | |
| assert f'{small_float}, {large_float}, {large_float2}' == '1.23e-20, 1.23e+30, 1234123412341234123400000000000000000' | |
| nan_num = float('nan') | |
| inf_num = float('inf') | |
| assert f'{nan_num}, {inf_num}' == 'nan, inf' | |
| assert f'{final_known_at_compile_time} {final_unknown_at_compile_time}' == 'hello goodbye' | |
| # F-strings would be translated into ''.join[string literals, format method call, ...] in mypy AST. | |
| # Currently we are using a str.join specializer for f-string speed up. We might not cover all cases | |
| # and the rest ones should fall back to a normal str.join method call. | |
| # TODO: Once we have a new pipeline for f-strings, this test case can be moved to testStringOps. | |
| def test_str_join() -> None: | |
| var = 'mypyc' | |
| num = 10 | |
| assert ''.join(['a', 'b', '{}'.format(var), 'c']) == 'abmypycc' | |
| assert ''.join(['a', 'b', '{:{}}'.format(var, ''), 'c']) == 'abmypycc' | |
| assert ''.join(['a', 'b', '{:{}}'.format(var, '>10'), 'c']) == 'ab mypycc' | |
| assert ''.join(['a', 'b', '{:{}}'.format(var, '>{}'.format(num)), 'c']) == 'ab mypycc' | |
| assert var.join(['a', '{:{}}'.format(var, ''), 'b']) == 'amypycmypycmypycb' | |
| assert ','.join(['a', '{:{}}'.format(var, ''), 'b']) == 'a,mypyc,b' | |
| assert ''.join(['x', var]) == 'xmypyc' | |
| class A: | |
| def __init__(self, name, age): | |
| self.name = name | |
| self.age = age | |
| def __repr__(self): | |
| return f'{self.name} is {self.age} years old.' | |
| def test_fstring_datatype() -> None: | |
| u = A('John Doe', 14) | |
| assert f'{u}' == 'John Doe is 14 years old.' | |
| d = {'name': 'John Doe', 'age': 14} | |
| assert f'{d}' == "{'name': 'John Doe', 'age': 14}" | |
| def test_fstring_escape() -> None: | |
| assert f"{'inside'}" == 'inside' | |
| assert f'{"inside"}' == 'inside' | |
| assert f"""inside""" == 'inside' | |
| assert f'''inside''' == 'inside' | |
| assert f"\"{'inside'}\"" == '"inside"' | |
| assert f'\'{"inside"}\'' == "'inside'" | |
| assert f'{{10}}' == '{10}' | |
| assert f'{{10 + 10}}' == '{10 + 10}' | |
| assert f'{{{10 + 10}}}' == '{20}' | |
| assert f'{{{{10 + 10}}}}' == '{{10 + 10}}' | |
| def test_fstring_conversion() -> None: | |
| assert f'Hello {var!r}' == "Hello 'mypyc'" | |
| # repr() is equivalent to !r | |
| assert f'Hello {repr(var)}' == "Hello 'mypyc'" | |
| assert f'Hello {var!a}' == "Hello 'mypyc'" | |
| # ascii() is equivalent to !a | |
| assert f'Hello {ascii(var)}' == "Hello 'mypyc'" | |
| tmp_str = """this | |
| is a new line.""" | |
| assert f'Test: {tmp_str!a}' == "Test: 'this\\n is a new line.'" | |
| s = 'test: āĀēĒčČ..šŠūŪžŽ' | |
| assert f'{s}' == 'test: āĀēĒčČ..šŠūŪžŽ' | |
| assert f'{s!a}' == "'test: \\u0101\\u0100\\u0113\\u0112\\u010d\\u010c..\\u0161\\u0160\\u016b\\u016a\\u017e\\u017d'" | |
| assert f'Hello {var!s}' == 'Hello mypyc' | |
| assert f'Hello {num!s}' == 'Hello 20' | |
| def test_fstring_align() -> None: | |
| assert f'Hello {var:>20}' == "Hello mypyc" | |
| assert f'Hello {var!r:>20}' == "Hello 'mypyc'" | |
| assert f'Hello {var:>{num}}' == "Hello mypyc" | |
| assert f'Hello {var!r:>{num}}' == "Hello 'mypyc'" | |
| def test_fstring_multi() -> None: | |
| assert f'Hello {var}, hello again {var}' == "Hello mypyc, hello again mypyc" | |
| a = 'py' | |
| 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}' | |
| assert s == 'mypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypymypy' | |
| def test_fstring_python_doc() -> None: | |
| name = 'Fred' | |
| assert f"He said his name is {name!r}." == "He said his name is 'Fred'." | |
| assert f"He said his name is {repr(name)}." == "He said his name is 'Fred'." | |
| width = 10 | |
| precision = 4 | |
| value = decimal.Decimal('12.34567') | |
| assert f'result: {value:{width}.{precision}}' == 'result: 12.35' # nested field | |
| today = datetime(year=2017, month=1, day=27) | |
| assert f'{today:%B %d, %Y}' == 'January 27, 2017' # using date format specifier | |
| number = 1024 | |
| assert f'{number:#0x}' == '0x400' # using integer format specifier | |
| [case testStringFormatMethod] | |
| from typing import Tuple | |
| def test_format_method_basics() -> None: | |
| x = str() | |
| assert 'x{}'.format(x) == 'x' | |
| assert 'ā{}'.format(x) == 'ā' | |
| assert '😀{}'.format(x) == '😀' | |
| assert ''.format() == '' | |
| assert 'abc'.format() == 'abc' | |
| assert '{}{}'.format(1, 2) == '12' | |
| name = 'Eric' | |
| age = 14 | |
| assert "My name is {name}, I'm {age}.".format(name=name, age=age) == "My name is Eric, I'm 14." | |
| assert "My name is {A}, I'm {B}.".format(A=name, B=age) == "My name is Eric, I'm 14." | |
| assert "My name is {}, I'm {B}.".format(name, B=age) == "My name is Eric, I'm 14." | |
| bool_var1 = True | |
| bool_var2 = False | |
| assert 'bool: {}, {}'.format(bool_var1, bool_var2) == 'bool: True, False' | |
| def test_format_method_empty_braces() -> None: | |
| name = 'Eric' | |
| age = 14 | |
| assert 'Hello, {}!'.format(name) == 'Hello, Eric!' | |
| assert '{}'.format(name) == 'Eric' | |
| assert '{}! Hi!'.format(name) == 'Eric! Hi!' | |
| assert '{}, Hi, {}'.format(name, name) == 'Eric, Hi, Eric' | |
| assert 'Hi! {}'.format(name) == 'Hi! Eric' | |
| assert "Hi, I'm {}. I'm {}.".format(name, age) == "Hi, I'm Eric. I'm 14." | |
| assert '{{}}'.format() == '{}' | |
| assert '{{{{}}}}'.format() == '{{}}' | |
| assert '{{}}{}'.format(name) == '{}Eric' | |
| assert 'Hi! {{{}}}'.format(name) == 'Hi! {Eric}' | |
| assert 'Hi! {{ {}'.format(name) == 'Hi! { Eric' | |
| assert 'Hi! {{ {} }}}}'.format(name) == 'Hi! { Eric }}' | |
| def test_format_method_numbers() -> None: | |
| s = 'int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}'.format(-233) | |
| assert s == 'int: -233; hex: -e9; oct: -351; bin: -11101001' | |
| num = 2**65 | |
| s = 'int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}'.format(num) | |
| assert s == 'int: 36893488147419103232; hex: 20000000000000000; oct: 4000000000000000000000; bin: 100000000000000000000000000000000000000000000000000000000000000000' | |
| s = 'int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}'.format(-num) | |
| assert s == 'int: -36893488147419103232; hex: -20000000000000000; oct: -4000000000000000000000; bin: -100000000000000000000000000000000000000000000000000000000000000000' | |
| large_num = 2**65 | |
| assert 'number: {}'.format(large_num) == 'number: 36893488147419103232' | |
| neg_num = -3 | |
| assert 'negative integer: {}'.format(neg_num) == 'negative integer: -3' | |
| assert 'negative integer: {}'.format(-large_num) == 'negative integer: -36893488147419103232' | |
| large_float = 1.23e30 | |
| large_float2 = 1234123412341234123400000000000000000 | |
| small_float = 1.23e-20 | |
| assert '{}, {}, {}'.format(small_float, large_float, large_float2) == '1.23e-20, 1.23e+30, 1234123412341234123400000000000000000' | |
| nan_num = float('nan') | |
| inf_num = float('inf') | |
| assert '{}, {}'.format(nan_num, inf_num) == 'nan, inf' | |
| def format_args(*args: int) -> str: | |
| return 'x{}y{}'.format(*args) | |
| def format_kwargs(**kwargs: int) -> str: | |
| return 'c{x}d{y}'.format(**kwargs) | |
| def format_args_self(*args: int) -> str: | |
| return '{}'.format(args) | |
| def format_kwargs_self(**kwargs: int) -> str: | |
| return '{}'.format(kwargs) | |
| def test_format_method_args() -> None: | |
| assert format_args(10, 2) == 'x10y2' | |
| assert format_args_self(10, 2) == '(10, 2)' | |
| assert format_kwargs(x=10, y=2) == 'c10d2' | |
| assert format_kwargs(x=10, y=2, z=1) == 'c10d2' | |
| assert format_kwargs_self(x=10, y=2, z=1) == "{'x': 10, 'y': 2, 'z': 1}" | |
| def test_format_method_different_kind() -> None: | |
| s1 = "Literal['😀']" | |
| assert 'Revealed type is {}'.format(s1) == "Revealed type is Literal['😀']" | |
| s2 = "Revealed type is" | |
| assert "{} Literal['😀']".format(s2) == "Revealed type is Literal['😀']" | |
| s3 = "测试:" | |
| assert "{}{} {}".format(s3, s2, s1) == "测试:Revealed type is Literal['😀']" | |
| assert "Test: {}{}".format(s3, s1) == "Test: 测试:Literal['😀']" | |
| assert "Test: {}{}".format(s3, s2) == "Test: 测试:Revealed type is" | |
| def test_format_method_nested() -> None: | |
| var = 'mypyc' | |
| num = 10 | |
| assert '{:{}}'.format(var, '') == 'mypyc' | |
| assert '{:{}}'.format(var, '>10') == ' mypyc' | |
| assert '{:{}}'.format(var, '>{}'.format(num)) == ' mypyc' | |
| class Point: | |
| def __init__(self, x, y): | |
| self.x, self.y = x, y | |
| def __str__(self): | |
| return 'Point({self.x}, {self.y})'.format(self=self) | |
| # Format examples from Python doc | |
| # https://docs.python.org/3/library/string.html#formatexamples | |
| def test_format_method_python_doc() -> None: | |
| # Accessing arguments by position: | |
| assert '{0}, {1}, {2}'.format('a', 'b', 'c') == 'a, b, c' | |
| assert '{}, {}, {}'.format('a', 'b', 'c') == 'a, b, c' | |
| assert '{2}, {1}, {0}'.format('a', 'b', 'c') == 'c, b, a' | |
| assert '{2}, {1}, {0}'.format(*'abc') == 'c, b, a' # unpacking argument sequence | |
| # assert '{0}{1}{0}'.format('abra', 'cad') = 'abracadabra' # arguments' indices can be repeated | |
| # Accessing arguments by name: | |
| s = 'Coordinates: {latitude}, {longitude}'.format(latitude='37.24N', longitude='-115.81W') | |
| assert s == 'Coordinates: 37.24N, -115.81W' | |
| coord = {'latitude': '37.24N', 'longitude': '-115.81W'} | |
| assert 'Coordinates: {latitude}, {longitude}'.format(**coord) == 'Coordinates: 37.24N, -115.81W' | |
| # Accessing arguments’ attributes: | |
| assert str(Point(4, 2)) == 'Point(4, 2)' | |
| # Accessing arguments’ items: | |
| coord2 = (3, 5) | |
| assert 'X: {0[0]}; Y: {0[1]}'.format(coord2) == 'X: 3; Y: 5' | |
| # Replacing %s and %r: | |
| s = "repr() shows quotes: {!r}; str() doesn't: {!s}".format('test1', 'test2') | |
| assert s == "repr() shows quotes: 'test1'; str() doesn't: test2" | |
| # Aligning the text and specifying a width: | |
| assert '{:<30}'.format('left aligned') == 'left aligned ' | |
| assert '{:>30}'.format('right aligned') == ' right aligned' | |
| assert '{:^30}'.format('centered') == ' centered ' | |
| assert '{:*^30}'.format('centered') == '***********centered***********' # use '*' as a fill char | |
| # Replacing %+f, %-f, and % f and specifying a sign: | |
| assert '{:+f}; {:+f}'.format(3.14, -3.14) == '+3.140000; -3.140000' # show it always | |
| assert '{: f}; {: f}'.format(3.14, -3.14) == ' 3.140000; -3.140000' # show a space for positive numbers | |
| assert '{:-f}; {:-f}'.format(3.14, -3.14) == '3.140000; -3.140000' # show only the minus -- same as '{:f}; {:f}' | |
| # Replacing %x and %o and converting the value to different bases: | |
| s = 'int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}'.format(42) # format also supports binary numbers | |
| assert s == 'int: 42; hex: 2a; oct: 52; bin: 101010' | |
| s = 'int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}'.format(42) # with 0x, 0o, or 0b as prefix: | |
| assert s == 'int: 42; hex: 0x2a; oct: 0o52; bin: 0b101010' | |
| # Using the comma as a thousands separator: | |
| assert '{:,}'.format(1234567890) == '1,234,567,890' | |
| # Expressing a percentage: | |
| points = 19.0 | |
| total = 22.0 | |
| assert 'Correct answers: {:.2%}'.format(points/total) == 'Correct answers: 86.36%' | |
| # Using type-specific formatting: | |
| import datetime | |
| d = datetime.datetime(2010, 7, 4, 12, 15, 58) | |
| assert '{:%Y-%m-%d %H:%M:%S}'.format(d) == '2010-07-04 12:15:58' | |
| # Nesting arguments and more complex examples: | |
| tmp_strs = [] | |
| for align, text in zip('<^>', ['left', 'center', 'right']): | |
| tmp_strs.append('{0:{fill}{align}16}'.format(text, fill=align, align=align)) | |
| assert tmp_strs == ['left<<<<<<<<<<<<', '^^^^^center^^^^^', '>>>>>>>>>>>right'] | |
| octets = [192, 168, 0, 1] | |
| assert '{:02X}{:02X}{:02X}{:02X}'.format(*octets) == 'C0A80001' | |
| width = 5 | |
| tmp_strs = [] | |
| for num in range(5,12): | |
| tmp_str = '' | |
| for base in 'dXob': | |
| tmp_str += ('{0:{width}{base}}'.format(num, base=base, width=width)) | |
| tmp_strs.append(tmp_str) | |
| assert tmp_strs == [' 5 5 5 101',\ | |
| ' 6 6 6 110',\ | |
| ' 7 7 7 111',\ | |
| ' 8 8 10 1000',\ | |
| ' 9 9 11 1001',\ | |
| ' 10 A 12 1010',\ | |
| ' 11 B 13 1011'] | |
| [case testChr] | |
| # Some test cases are from https://docs.python.org/3/howto/unicode.html | |
| def try_invalid(x: int) -> bool: | |
| try: | |
| chr(x + int()) | |
| return False | |
| except ValueError: | |
| return True | |
| def test_chr() -> None: | |
| assert chr(57344) == '\ue000' | |
| assert chr(0) == '\x00' | |
| assert chr(65) == 'A' | |
| assert chr(150) == '\x96' | |
| try: | |
| chr(-1) | |
| assert False | |
| except ValueError: | |
| pass | |
| try: | |
| chr(1114112) | |
| assert False | |
| except ValueError: | |
| pass | |
| assert chr(1114111) == '\U0010ffff' | |
| x = 0 | |
| assert chr(x + int()) == '\x00' | |
| x = 100 | |
| assert chr(x + int()) == 'd' | |
| x = 150 | |
| assert chr(x + int()) == '\x96' | |
| x = 257 | |
| assert chr(x + int()) == 'ā' | |
| x = 65537 | |
| assert chr(x + int()) == '𐀁' | |
| assert try_invalid(-1) | |
| assert try_invalid(1114112) | |
| [case testOrd] | |
| from testutil import assertRaises | |
| from mypy_extensions import i64, i32, i16, u8 | |
| def test_ord() -> None: | |
| assert ord(' ') == 32 | |
| assert ord(' ' + str()) == 32 | |
| assert ord('\x00') == 0 | |
| assert ord('\x00' + str()) == 0 | |
| assert ord('\ue000') == 57344 | |
| assert ord('\ue000' + str()) == 57344 | |
| s = "a\xac\u1234\u20ac\U00010000" | |
| # ^^^^ two-digit hex escape | |
| # ^^^^^^ four-digit Unicode escape | |
| # ^^^^^^^^^^ eight-digit Unicode escape | |
| l1 = [ord(c) for c in s] | |
| assert l1 == [97, 172, 4660, 8364, 65536] | |
| u = 'abcdé' | |
| assert ord(u[-1]) == 233 | |
| assert ord(b'a') == 97 | |
| assert ord(b'a' + bytes()) == 97 | |
| u2 = '\U0010ffff' + str() | |
| assert ord(u2) == 1114111 | |
| assert ord('\U0010ffff') == 1114111 | |
| with assertRaises(TypeError, "ord() expected a character, but a string of length 2 found"): | |
| ord('aa') | |
| with assertRaises(TypeError): | |
| ord('') | |
| def test_ord_str_index() -> None: | |
| # ASCII | |
| s1 = "hello" | |
| assert ord(s1[0 + int()]) == 104 # 'h' | |
| assert ord(s1[1 + int()]) == 101 # 'e' | |
| assert ord(s1[4 + int()]) == 111 # 'o' | |
| assert ord(s1[-1 + int()]) == 111 # 'o' | |
| assert ord(s1[-5 + int()]) == 104 # 'h' | |
| # Latin-1 (8 bits) | |
| s2 = "café" | |
| assert ord(s2[0 + int()]) == 99 # 'c' | |
| assert ord(s2[3 + int()]) == 233 # 'é' (U+00E9) | |
| assert ord(s2[-1 + int()]) == 233 | |
| # 16-bit unicode | |
| s3 = "你好" # Chinese | |
| assert ord(s3[0 + int()]) == 20320 # '你' (U+4F60) | |
| assert ord(s3[1 + int()]) == 22909 # '好' (U+597D) | |
| assert ord(s3[-1 + int()]) == 22909 | |
| assert ord(s3[-2 + int()]) == 20320 | |
| # 4-byte unicode | |
| s5 = "a😀b" # Emoji between ASCII chars | |
| assert ord(s5[0 + int()]) == 97 # 'a' | |
| assert ord(s5[1 + int()]) == 128512 # '😀' (U+1F600) | |
| assert ord(s5[2 + int()]) == 98 # 'b' | |
| assert ord(s5[-1 + int()]) == 98 | |
| assert ord(s5[-2 + int()]) == 128512 | |
| assert ord(s5[-3 + int()]) == 97 | |
| with assertRaises(IndexError, "index out of range"): | |
| ord(s1[5 + int()]) | |
| with assertRaises(IndexError, "index out of range"): | |
| ord(s1[100 + int()]) | |
| with assertRaises(IndexError, "index out of range"): | |
| ord(s1[-6 + int()]) | |
| with assertRaises(IndexError, "index out of range"): | |
| ord(s1[-100 + int()]) | |
| s_empty = "" | |
| with assertRaises(IndexError, "index out of range"): | |
| ord(s_empty[0 + int()]) | |
| with assertRaises(IndexError, "index out of range"): | |
| ord(s_empty[-1 + int()]) | |
| def test_ord_str_index_i64() -> None: | |
| s = "hello" | |
| idx_i64: i64 = 2 + int() | |
| assert ord(s[idx_i64]) == 108 # 'l' | |
| idx_i64_neg: i64 = -1 + int() | |
| assert ord(s[idx_i64_neg]) == 111 # 'o' | |
| idx_overflow: i64 = 10 + int() | |
| with assertRaises(IndexError, "index out of range"): | |
| ord(s[idx_overflow]) | |
| idx_underflow: i64 = -10 + int() | |
| with assertRaises(IndexError, "index out of range"): | |
| ord(s[idx_underflow]) | |
| def test_ord_str_index_unicode_mix() -> None: | |
| # Mix of 1-byte, 2-byte, 3-byte, and 4-byte characters | |
| s = "a\u00e9\u4f60😀" # 'a', 'é', '你', '😀' | |
| assert ord(s[0 + int()]) == 97 # 1-byte | |
| assert ord(s[1 + int()]) == 233 # 2-byte | |
| assert ord(s[2 + int()]) == 20320 # 3-byte | |
| assert ord(s[3 + int()]) == 128512 # 4-byte | |
| def test_ord_str_index_to_fixed_width() -> None: | |
| # Test i32 coercion (signed 32-bit: -2^31 to 2^31-1) | |
| # All valid Unicode code points fit in i32, so test min/max boundaries | |
| s_i32_min = chr(0) | |
| assert i32(ord(s_i32_min[0 + int()])) == 0 | |
| s_i32_max = chr(0x10FFFF) # Max Unicode code point | |
| assert i32(ord(s_i32_max[0 + int()])) == 0x10FFFF | |
| # Test i16 coercion (signed 16-bit: -2^15 to 2^15-1, i.e., -32768 to 32767) | |
| # ord() returns non-negative, so test 0 to 32767 | |
| s_i16_min = chr(0) | |
| assert i16(ord(s_i16_min[0 + int()])) == 0 | |
| s_i16_max = chr(32767) # 2^15 - 1 | |
| assert i16(ord(s_i16_max[0 + int()])) == 32767 | |
| s_i16_overflow = chr(32768) # 2^15 | |
| with assertRaises(ValueError, "int too large to convert to i16"): | |
| i16(ord(s_i16_overflow[0 + int()])) | |
| s_i16_overflow2 = chr(32769) # 2^15 + 1 | |
| with assertRaises(ValueError, "int too large to convert to i16"): | |
| i16(ord(s_i16_overflow2[0 + int()])) | |
| # Test u8 coercion (unsigned 8-bit: 0 to 2^8-1, i.e., 0 to 255) | |
| s_u8_min = chr(0) | |
| assert u8(ord(s_u8_min[0 + int()])) == 0 | |
| s_u8_max = chr(255) | |
| assert u8(ord(s_u8_max[0 + int()])) == 255 | |
| s_u8_overflow = chr(256) | |
| with assertRaises(ValueError, "int too large or small to convert to u8"): | |
| u8(ord(s_u8_overflow[0 + int()])) | |
| s_u8_overflow2 = chr(257) | |
| with assertRaises(ValueError, "int too large or small to convert to u8"): | |
| u8(ord(s_u8_overflow2[0 + int()])) | |
| [case testDecode] | |
| from testutil import assertRaises | |
| def test_decode() -> None: | |
| assert "\N{GREEK CAPITAL LETTER DELTA}" == '\u0394' | |
| assert "\u0394" == "\u0394" | |
| assert "\U00000394" == '\u0394' | |
| assert b'\x80abc'.decode('utf-8', 'replace') == '\ufffdabc' | |
| assert b'\x80abc'.decode('utf-8', 'backslashreplace') == '\\x80abc' | |
| assert b''.decode() == '' | |
| assert b'a'.decode() == 'a' | |
| assert b'abc'.decode() == 'abc' | |
| assert b'abc'.decode('utf-8') == 'abc' | |
| assert b'abc'.decode('utf-8' + str()) == 'abc' | |
| assert b'abc\x00\xce'.decode('latin-1') == 'abc\x00\xce' | |
| assert b'abc\x00\xce'.decode('latin-1' + str()) == 'abc\x00\xce' | |
| assert b'abc\x00\x7f'.decode('ascii') == 'abc\x00\x7f' | |
| assert b'abc\x00\x7f'.decode('ascii' + str()) == 'abc\x00\x7f' | |
| assert b'\x80abc'.decode('utf-8', 'ignore') == 'abc' | |
| assert b'\x80abc'.decode('UTF-8', 'ignore') == 'abc' | |
| assert b'\x80abc'.decode('Utf-8', 'ignore') == 'abc' | |
| assert b'\x80abc'.decode('utf_8', 'ignore') == 'abc' | |
| assert b'\x80abc'.decode('latin1', 'ignore') == '\x80abc' | |
| assert b'\xd2\xbb\xb6\xfe\xc8\xfd'.decode('gbk', 'ignore') == '一二三' | |
| assert b'\xd2\xbb\xb6\xfe\xc8\xfd'.decode('latin1', 'ignore') == 'Ò»¶þÈý' | |
| assert b'Z\xc3\xbcrich'.decode("utf-8") == 'Zürich' | |
| assert b'Z\xc3\xbcrich'.decode("utf-8" + str()) == 'Zürich' | |
| assert bytearray(range(5)).decode() == '\x00\x01\x02\x03\x04' | |
| b = bytearray(b'\xe4\xbd\xa0\xe5\xa5\xbd') | |
| assert b.decode() == '你好' | |
| assert b.decode('gbk') == '浣犲ソ' | |
| assert b.decode('latin1') == 'ä½\xa0好' | |
| assert b.decode('latin1' + str()) == 'ä½\xa0好' | |
| def test_decode_error() -> None: | |
| try: | |
| b'Z\xc3\xbcrich'.decode('ascii') | |
| assert False | |
| except UnicodeDecodeError: | |
| pass | |
| try: | |
| b'Z\xc3\xbcrich'.decode('ascii' + str()) | |
| assert False | |
| except UnicodeDecodeError: | |
| pass | |
| try: | |
| b'Z\xc3y'.decode('utf8') | |
| assert False | |
| except UnicodeDecodeError: | |
| pass | |
| try: | |
| b'Z\xc3y'.decode('utf8' + str()) | |
| assert False | |
| except UnicodeDecodeError: | |
| pass | |
| def test_decode_bytearray() -> None: | |
| b = bytearray(b'foo\x00bar') | |
| assert b.decode() == 'foo\x00bar' | |
| assert b.decode('utf-8') == 'foo\x00bar' | |
| assert b.decode('latin-1') == 'foo\x00bar' | |
| assert b.decode('ascii') == 'foo\x00bar' | |
| assert b.decode('utf-8' + str()) == 'foo\x00bar' | |
| assert b.decode('latin-1' + str()) == 'foo\x00bar' | |
| assert b.decode('ascii' + str()) == 'foo\x00bar' | |
| b2 = bytearray(b'foo\x00bar\xbe') | |
| assert b2.decode('latin-1') == 'foo\x00bar\xbe' | |
| with assertRaises(UnicodeDecodeError): | |
| b2.decode('ascii') | |
| with assertRaises(UnicodeDecodeError): | |
| b2.decode('ascii' + str()) | |
| with assertRaises(UnicodeDecodeError): | |
| b2.decode('utf-8') | |
| with assertRaises(UnicodeDecodeError): | |
| b2.decode('utf-8' + str()) | |
| b3 = bytearray(b'Z\xc3\xbcrich') | |
| assert b3.decode("utf-8") == 'Zürich' | |
| def test_invalid_encoding() -> None: | |
| try: | |
| b"foo".decode("ut-f-8") | |
| assert False | |
| except Exception as e: | |
| assert repr(e).startswith("LookupError") | |
| try: | |
| encoding = "ut-f-8" | |
| b"foo".decode(encoding) | |
| assert False | |
| except Exception as e: | |
| assert repr(e).startswith("LookupError") | |
| [case testEncode] | |
| from testutil import assertRaises | |
| def test_encode() -> None: | |
| u = chr(40960) + 'abcd' + chr(1972) | |
| assert u.encode() == b'\xea\x80\x80abcd\xde\xb4' | |
| assert u.encode('utf-8') == b'\xea\x80\x80abcd\xde\xb4' | |
| with assertRaises(UnicodeEncodeError): | |
| u.encode('ascii') | |
| with assertRaises(LookupError): | |
| u.encode('aaa') | |
| assert u.encode('utf-8', 'aaaaaa') == b'\xea\x80\x80abcd\xde\xb4' | |
| assert u.encode('ascii', 'ignore') == b'abcd' | |
| assert u.encode('ASCII', 'ignore') == b'abcd' | |
| assert u.encode('ascii', 'replace') == b'?abcd?' | |
| assert u.encode('ascii', 'xmlcharrefreplace') == b'ꀀabcd޴' | |
| assert u.encode('ascii', 'backslashreplace') == b'\\ua000abcd\\u07b4' | |
| assert u.encode('ascii', 'namereplace') == b'\\N{YI SYLLABLE IT}abcd\\u07b4' | |
| assert 'pythön!'.encode() == b'pyth\xc3\xb6n!' | |
| assert '一二三'.encode('gbk') == b'\xd2\xbb\xb6\xfe\xc8\xfd' | |
| assert u.encode('UTF-8', 'ignore') == b'\xea\x80\x80abcd\xde\xb4' | |
| assert u.encode('Utf_8') == b'\xea\x80\x80abcd\xde\xb4' | |
| assert u.encode('UTF_8') == b'\xea\x80\x80abcd\xde\xb4' | |
| assert u'\u00E1'.encode('latin1') == b'\xe1' | |
| with assertRaises(UnicodeEncodeError): | |
| u.encode('latin1') | |
| [case testStrip] | |
| def test_all_strips_default() -> None: | |
| s = " a1\t" | |
| assert s.lstrip() == "a1\t" | |
| assert s.strip() == "a1" | |
| assert s.rstrip() == " a1" | |
| def test_all_strips() -> None: | |
| s = "xxb2yy" | |
| assert s.lstrip("xy") == "b2yy" | |
| assert s.strip("xy") == "b2" | |
| assert s.rstrip("xy") == "xxb2" | |
| def test_unicode_whitespace() -> None: | |
| assert "\u200A\u000D\u2009\u2020\u000Dtt\u0085\u000A".strip() == "\u2020\u000Dtt" | |
| def test_unicode_range() -> None: | |
| assert "\u2029 \U00107581 ".lstrip() == "\U00107581 " | |
| assert "\u2029 \U0010AAAA\U00104444B\u205F ".strip() == "\U0010AAAA\U00104444B" | |
| assert " \u3000\u205F ".strip() == "" | |
| assert "\u2029 \U00102865\u205F ".rstrip() == "\u2029 \U00102865" | |
| [case testCount] | |
| # mypy: disable-error-code="attr-defined" | |
| def test_count() -> None: | |
| string = "abcbcb" | |
| assert string.count("a") == 1 | |
| assert string.count("b") == 3 | |
| assert string.count("c") == 2 | |
| def test_count_start() -> None: | |
| string = "abcbcb" | |
| assert string.count("a", 2) == string.count("a", -4) == 0, (string.count("a", 2), string.count("a", -4)) | |
| assert string.count("b", 2) == string.count("b", -4) == 2, (string.count("b", 2), string.count("b", -4)) | |
| assert string.count("c", 2) == string.count("c", -4) == 2, (string.count("c", 2), string.count("c", -4)) | |
| # out of bounds | |
| assert string.count("a", 8) == 0 | |
| assert string.count("a", -8) == 1 | |
| assert string.count("b", 8) == 0 | |
| assert string.count("b", -8) == 3 | |
| assert string.count("c", 8) == 0 | |
| assert string.count("c", -8) == 2 | |
| def test_count_start_end() -> None: | |
| string = "abcbcb" | |
| assert string.count("a", 0, 4) == 1, string.count("a", 0, 4) | |
| assert string.count("b", 0, 4) == 2, string.count("b", 0, 4) | |
| assert string.count("c", 0, 4) == 1, string.count("c", 0, 4) | |
| def test_count_multi() -> None: | |
| string = "aaabbbcccbbbcccbbb" | |
| assert string.count("aaa") == 1, string.count("aaa") | |
| assert string.count("bbb") == 3, string.count("bbb") | |
| assert string.count("ccc") == 2, string.count("ccc") | |
| def test_count_multi_start() -> None: | |
| string = "aaabbbcccbbbcccbbb" | |
| assert string.count("aaa", 6) == string.count("aaa", -12) == 0, (string.count("aaa", 6), string.count("aaa", -12)) | |
| assert string.count("bbb", 6) == string.count("bbb", -12) == 2, (string.count("bbb", 6), string.count("bbb", -12)) | |
| assert string.count("ccc", 6) == string.count("ccc", -12) == 2, (string.count("ccc", 6), string.count("ccc", -12)) | |
| # out of bounds | |
| assert string.count("aaa", 20) == 0 | |
| assert string.count("aaa", -20) == 1 | |
| assert string.count("bbb", 20) == 0 | |
| assert string.count("bbb", -20) == 3 | |
| assert string.count("ccc", 20) == 0 | |
| assert string.count("ccc", -20) == 2 | |
| def test_count_multi_start_end() -> None: | |
| string = "aaabbbcccbbbcccbbb" | |
| assert string.count("aaa", 0, 12) == 1, string.count("aaa", 0, 12) | |
| assert string.count("bbb", 0, 12) == 2, string.count("bbb", 0, 12) | |
| assert string.count("ccc", 0, 12) == 1, string.count("ccc", 0, 12) | |
| def test_count_emoji() -> None: | |
| string = "😴🚀ñ🚀ñ🚀" | |
| assert string.count("😴") == 1, string.count("😴") | |
| assert string.count("🚀") == 3, string.count("🚀") | |
| assert string.count("ñ") == 2, string.count("ñ") | |
| def test_count_start_emoji() -> None: | |
| string = "😴🚀ñ🚀ñ🚀" | |
| assert string.count("😴", 2) == string.count("😴", -4) == 0, (string.count("😴", 2), string.count("😴", -4)) | |
| assert string.count("🚀", 2) == string.count("🚀", -4) == 2, (string.count("🚀", 2), string.count("🚀", -4)) | |
| assert string.count("ñ", 2) == string.count("ñ", -4) == 2, (string.count("ñ", 2), string.count("ñ", -4)) | |
| # Out of bounds | |
| assert string.count("😴", 8) == 0, string.count("😴", 8) | |
| assert string.count("😴", -8) == 1, string.count("😴", -8) | |
| assert string.count("🚀", 8) == 0, string.count("🚀", 8) | |
| assert string.count("🚀", -8) == 3, string.count("🚀", -8) | |
| assert string.count("ñ", 8) == 0, string.count("ñ", 8) | |
| assert string.count("ñ", -8) == 2, string.count("ñ", -8) | |
| def test_count_start_end_emoji() -> None: | |
| string = "😴🚀ñ🚀ñ🚀" | |
| assert string.count("😴", 0, 4) == 1, string.count("😴", 0, 4) | |
| assert string.count("🚀", 0, 4) == 2, string.count("🚀", 0, 4) | |
| assert string.count("ñ", 0, 4) == 1, string.count("ñ", 0, 4) | |
| def test_count_multi_emoji() -> None: | |
| string = "😴😴😴🚀🚀🚀ñññ🚀🚀🚀ñññ🚀🚀🚀" | |
| assert string.count("😴😴😴") == 1, string.count("😴😴😴") | |
| assert string.count("🚀🚀🚀") == 3, string.count("🚀🚀🚀") | |
| assert string.count("ñññ") == 2, string.count("ñññ") | |
| def test_count_multi_start_emoji() -> None: | |
| string = "😴😴😴🚀🚀🚀ñññ🚀🚀🚀ñññ🚀🚀🚀" | |
| assert string.count("😴😴😴", 6) == string.count("😴😴😴", -12) == 0, (string.count("😴😴😴", 6), string.count("😴😴😴", -12)) | |
| assert string.count("🚀🚀🚀", 6) == string.count("🚀🚀🚀", -12) == 2, (string.count("🚀🚀🚀", 6), string.count("🚀🚀🚀", -12)) | |
| assert string.count("ñññ", 6) == string.count("ñññ", -12) == 2, (string.count("ñññ", 6), string.count("ñññ", -12)) | |
| # Out of bounds | |
| assert string.count("😴😴😴", 20) == 0, string.count("😴😴😴", 20) | |
| assert string.count("😴😴😴", -20) == 1, string.count("😴😴😴", -20) | |
| assert string.count("🚀🚀🚀", 20) == 0, string.count("🚀🚀🚀", 20) | |
| assert string.count("🚀🚀🚀", -20) == 3, string.count("🚀🚀🚀", -20) | |
| assert string.count("ñññ", 20) == 0, string.count("ñññ", 20) | |
| assert string.count("ñññ", -20) == 2, string.count("ñññ", -20) | |
| def test_count_multi_start_end_emoji() -> None: | |
| string = "😴😴😴🚀🚀🚀ñññ🚀🚀🚀ñññ🚀🚀🚀" | |
| assert string.count("😴😴😴", 0, 12) == 1, string.count("😴😴😴", 0, 12) | |
| assert string.count("🚀🚀🚀", 0, 12) == 2, string.count("🚀🚀🚀", 0, 12) | |
| assert string.count("ñññ", 0, 12) == 1, string.count("ñññ", 0, 12) | |
| [case testIsInstance] | |
| from copysubclass import subc | |
| from typing import Any | |
| def test_built_in() -> None: | |
| s: Any = str() | |
| assert isinstance(s, str) | |
| assert isinstance(s + "test", str) | |
| assert isinstance(s + "ñññ", str) | |
| assert isinstance(subc(), str) | |
| assert isinstance(subc("test"), str) | |
| assert isinstance(subc("ñññ"), str) | |
| assert not isinstance(set(), str) | |
| assert not isinstance((), str) | |
| assert not isinstance(('a','b'), str) | |
| assert not isinstance({'a','b'}, str) | |
| assert not isinstance(int() + 1, str) | |
| assert not isinstance(['a','b'], str) | |
| def test_user_defined() -> None: | |
| from userdefinedstr import str | |
| s: Any = "str" | |
| assert isinstance(str(), str) | |
| assert not isinstance(s, str) | |
| [file copysubclass.py] | |
| from typing import Any | |
| class subc(str): | |
| pass | |
| [file userdefinedstr.py] | |
| class str: | |
| pass | |
| [case testStrOptionalEquality] | |
| from __future__ import annotations | |
| def eq_s_opt_s_opt(x: str | None, y: str | None) -> bool: | |
| return x == y | |
| def ne_s_opt_s_opt(x: str | None, y: str | None) -> bool: | |
| return x != y | |
| def test_optional_eq() -> None: | |
| s = 'x' | |
| assert eq_s_opt_s_opt(s, s) | |
| assert eq_s_opt_s_opt(s + str(int()), s + str(int())) | |
| assert eq_s_opt_s_opt(None, None) | |
| assert not eq_s_opt_s_opt('x', 'y') | |
| assert not eq_s_opt_s_opt('y', 'x') | |
| assert not eq_s_opt_s_opt(None, 'x') | |
| assert not eq_s_opt_s_opt('x', None) | |
| def test_optional_ne() -> None: | |
| s = 'x' | |
| assert not ne_s_opt_s_opt(s, s) | |
| assert not ne_s_opt_s_opt(s + str(int()), s+ str(int())) | |
| assert not ne_s_opt_s_opt(None, None) | |
| assert ne_s_opt_s_opt('x', 'y') | |
| assert ne_s_opt_s_opt('y', 'x') | |
| assert ne_s_opt_s_opt(None, 'x') | |
| assert ne_s_opt_s_opt('x', None) | |
| [case testConstantFoldFormatArgs] | |
| from typing import Final | |
| FMT: Final = "{} {}" | |
| def test_format() -> None: | |
| assert FMT.format(400 + 20, "roll" + "up") == "420 rollup" | |
| [case testLowerAndUpper] | |
| from typing import Any | |
| def test_lower_basic() -> None: | |
| assert "".lower() == "" | |
| assert "hello".lower() == "hello" | |
| assert "HELLO".lower() == "hello" | |
| assert "Hello World".lower() == "hello world" | |
| assert "123".lower() == "123" | |
| assert "ABC123".lower() == "abc123" | |
| def test_upper_basic() -> None: | |
| assert "".upper() == "" | |
| assert "HELLO".upper() == "HELLO" | |
| assert "hello".upper() == "HELLO" | |
| assert "Hello World".upper() == "HELLO WORLD" | |
| assert "123".upper() == "123" | |
| assert "abc123".upper() == "ABC123" | |
| def test_lower_unicode() -> None: | |
| assert "\u00C9".lower() == "\u00E9" # É -> é | |
| assert "\u0391\u0392".lower() == "\u03B1\u03B2" # ΑΒ -> αβ | |
| assert "\u4E2D\u6587".lower() == "\u4E2D\u6587" # CJK (no case) | |
| assert "\U0001F600".lower() == "\U0001F600" # Emoji (no case) | |
| def test_upper_unicode() -> None: | |
| assert "\u00E9".upper() == "\u00C9" # é -> É | |
| assert "\u03B1\u03B2".upper() == "\u0391\u0392" # αβ -> ΑΒ | |
| assert "\u4E2D\u6587".upper() == "\u4E2D\u6587" # CJK (no case) | |
| assert "\U0001F600".upper() == "\U0001F600" # Emoji (no case) | |
| def test_expansion() -> None: | |
| # 1-to-N expansion cases | |
| assert "\u0130".lower() == "\u0069\u0307" # İ -> i + combining dot above | |
| assert "\uFB03".lower() == "\uFB03" # ffi ligature stays lowercase | |
| assert "\u00DF".upper() == "SS" # ß -> SS | |
| assert "\uFB03".upper() == "FFI" # ffi ligature -> FFI | |
| def test_comprehensive() -> None: | |
| for i in range(0x110000): | |
| c = chr(i) | |
| a: Any = c | |
| assert c.lower() == a.lower(), f"lower mismatch at U+{i:04X}" | |
| assert c.upper() == a.upper(), f"upper mismatch at U+{i:04X}" | |
| [case testIsSpace] | |
| from typing import Any | |
| def test_isspace() -> None: | |
| # Verify correctness across all Unicode codepoints. | |
| # Exercises UCS-1 (0x00-0xFF), UCS-2 (0x100-0xFFFF), and UCS-4 (0x10000-x10FFFF inclusive) string kinds. | |
| # Any forces generic dispatch so we compare our primitive against stdlib's | |
| for i in range(0x110000): | |
| c = chr(i) | |
| a: Any = c | |
| assert c.isspace() == a.isspace() | |
| [case testIsAlnum] | |
| def test_isalnum_basic() -> None: | |
| assert "abc".isalnum() | |
| assert "ABC".isalnum() | |
| assert "abc123".isalnum() | |
| assert "123".isalnum() | |
| assert not "".isalnum() | |
| assert not " ".isalnum() | |
| assert not "abc!".isalnum() | |
| assert not "hello world".isalnum() | |
| assert not "abc-123".isalnum() | |
| def test_isalnum_unicode() -> None: | |
| # Single chars: letters and digits from various scripts | |
| assert "\u00E9".isalnum() # é (UCS-1 Latin letter) | |
| assert "\u0660".isalnum() # ٠ (UCS-2 Arabic-Indic digit) | |
| assert "\u4E2D".isalnum() # 中 (UCS-2 CJK ideograph) | |
| assert "\U00010400".isalnum() # 𐐀 (UCS-4 Deseret capital letter long I) | |
| assert not "\u2000".isalnum() # EN QUAD (whitespace) | |
| assert not "\u0021".isalnum() # ! | |
| assert not "\u00B6".isalnum() # ¶ (pilcrow sign, punctuation) | |
| # Mixed Unicode letters and digits — all alnum | |
| assert "\u00E9\u0660".isalnum() # é٠ | |
| assert "\u4E2D\u0041\u0660".isalnum() # 中A٠ | |
| # Unicode letter/digit mixed with punctuation — not alnum | |
| assert not "\u00E9!".isalnum() | |
| assert not "\u4E2D\u2000".isalnum() # CJK + whitespace | |
| [case testIsDigit] | |
| from typing import Any | |
| def test_isdigit() -> None: | |
| for i in range(0x110000): | |
| c = chr(i) | |
| a: Any = c | |
| assert c.isdigit() == a.isdigit() | |
| def test_isdigit_strings() -> None: | |
| # ASCII digits | |
| assert "0123456789".isdigit() | |
| assert not "".isdigit() | |
| assert not " ".isdigit() | |
| assert not "a".isdigit() | |
| assert not "abc".isdigit() | |
| assert not "!@#".isdigit() | |
| # Mixed ASCII | |
| assert not "123abc".isdigit() | |
| assert not "abc123".isdigit() | |
| assert not "12 34".isdigit() | |
| assert not "123!".isdigit() | |
| # Unicode digits | |
| assert "\u0660\u0661\u0662".isdigit() | |
| assert "\u00b2\u00b3".isdigit() | |
| assert "123\U0001d7ce\U0001d7cf\U0001d7d0".isdigit() | |
| # Mixed digits and Unicode non-digits | |
| assert not "\u00e9\u00e8".isdigit() | |
| assert not "123\u00e9".isdigit() | |
| assert not "\U0001d7ce!".isdigit() | |
| [case testStrIndexEqLiteralSpecialize] | |
| from testutil import assertRaises | |
| # The specializer fires on the AST shape `IndexExpr == StrLiteral` (or the | |
| # symmetric swap, and `!=`). The literal has to be a real source-level | |
| # string literal (can't be passed in as a parameter), so each test | |
| # function pins one distinct shape. | |
| def eq_comma(s: str, i: int) -> bool: | |
| # Specialized: s[i] == "x". | |
| return s[i] == "," | |
| def ne_comma(s: str, i: int) -> bool: | |
| # Specialized: s[i] != "x". | |
| return s[i] != "," | |
| def comma_eq(s: str, i: int) -> bool: | |
| # Specialized: "x" == s[i]. Operand-swap is normalized. | |
| return "," == s[i] | |
| def eq_two_chars(s: str, i: int) -> bool: | |
| # Not specialized: literal isn't 1 char. Falls through to the generic | |
| # str compare, which returns False since s[i] is always 1 codepoint. | |
| return s[i] == "ab" | |
| def eq_empty(s: str, i: int) -> bool: | |
| # Not specialized: empty literal. Same fall-through. | |
| return s[i] == "" | |
| def test_specialized_path() -> None: | |
| s = "a,b" # comma at index 1 | |
| assert eq_comma(s, 1) | |
| assert not eq_comma(s, 0) | |
| assert not eq_comma(s, 2) | |
| # != inverts. | |
| assert ne_comma(s, 0) | |
| assert not ne_comma(s, 1) | |
| # Literal on the LHS is normalized to the same shape. | |
| assert comma_eq(s, 1) | |
| assert not comma_eq(s, 0) | |
| def test_negative_index_is_adjusted() -> None: | |
| s = "a,b" | |
| assert eq_comma(s, -2) # -2 -> 1 (',') | |
| assert not eq_comma(s, -1) # -1 -> 2 ('b') | |
| def test_non_1char_literal_falls_through() -> None: | |
| s = "a,b" | |
| # Generic str compare answers False because s[i] has length 1. | |
| assert not eq_two_chars(s, 0) | |
| assert not eq_two_chars(s, 1) | |
| assert not eq_empty(s, 0) | |
| def test_out_of_range_raises_indexerror() -> None: | |
| # Bounds-check semantics match the unspecialized s[i] path. | |
| s = "a,b" | |
| with assertRaises(IndexError): | |
| eq_comma(s, 3) | |
| with assertRaises(IndexError): | |
| eq_comma(s, -4) |