for alias in ('utf-8', 'utf8', 'utf_8', 'UTF-8', 'utf', 'u8', 'cp65001', 'utf8-ucs2', 'utf8-ucs4', ' UTF 8 ', 'UTF--8'):
assert 'héllo'.encode(alias) == b'h\xc3\xa9llo', f'utf-8 alias {alias}'
assert b'h\xc3\xa9llo'.decode(alias) == 'héllo', f'utf-8 decode alias {alias}'
for alias in (
'ascii',
'ASCII',
'646',
'us',
'us-ascii',
'us_ascii',
'cp367',
'ibm367',
'csascii',
'ansi_x3.4-1968',
'ansi_x3.4-1986',
'iso646-us',
'ISO_646.IRV:1991',
'iso-ir-6',
):
assert 'hello'.encode(alias) == b'hello', f'ascii alias {alias}'
assert b'hello'.decode(alias) == 'hello', f'ascii decode alias {alias}'
for alias in ('utf-16', 'utf16', 'utf_16', 'u16', 'UTF 16'):
assert 'a'.encode(alias) == b'\xff\xfea\x00', f'utf-16 alias {alias}'
for alias in ('utf-16-le', 'utf-16le', 'UTF_16_LE', 'UnicodeLittleUnmarked'):
assert 'a'.encode(alias) == b'a\x00', f'utf-16-le alias {alias}'
for alias in ('utf-16-be', 'utf-16be', 'UnicodeBigUnmarked'):
assert 'a'.encode(alias) == b'\x00a', f'utf-16-be alias {alias}'
for alias in ('utf-32', 'utf32', 'u32'):
assert 'a'.encode(alias) == b'\xff\xfe\x00\x00a\x00\x00\x00', f'utf-32 alias {alias}'
assert 'a'.encode('utf-32-le') == b'a\x00\x00\x00'
assert 'a'.encode('utf-32le') == b'a\x00\x00\x00'
assert 'a'.encode('utf-32-be') == b'\x00\x00\x00a'
assert 'a'.encode('utf-32be') == b'\x00\x00\x00a'
try:
'hi'.encode('utf.8')
assert False, 'utf.8 should not resolve'
except LookupError as e:
assert str(e) == 'unknown encoding: utf.8', f'unknown encoding message: {e}'
try:
b'hi'.decode(' Not A Real--Codec ')
assert False, 'unknown codec should error'
except LookupError as e:
assert str(e) == 'unknown encoding: Not A Real--Codec ', f'unknown encoding keeps original name: {e}'
assert 'ab'.encode('utf-16') == b'\xff\xfea\x00b\x00'
assert ''.encode('utf-16') == b'\xff\xfe'
assert ''.encode('utf-16-le') == b''
assert ''.encode('utf-32') == b'\xff\xfe\x00\x00'
assert 'ab'.encode('utf-16-le') == b'a\x00b\x00'
assert 'ab'.encode('utf-16-be') == b'\x00a\x00b'
assert '\U0001f600'.encode('utf-16') == b'\xff\xfe=\xd8\x00\xde'
assert '\U0001f600'.encode('utf-16-be') == b'\xd8=\xde\x00'
assert '\U0001f600'.encode('utf-32-le') == b'\x00\xf6\x01\x00'
assert '\U0001f600'.encode('utf-32-be') == b'\x00\x01\xf6\x00'
assert 'héllo ⚡'.encode('utf-16', 'bogus') == 'héllo ⚡'.encode('utf-16')
assert 'héllo'.encode('utf-32-be', 'strict') == 'héllo'.encode('utf-32-be')
s = 'héllo wörld ⚡ 日本語 \U0001f600 test'
for enc in ('utf-16', 'utf-16-le', 'utf-16-be', 'utf-32', 'utf-32-le', 'utf-32-be'):
assert s.encode(enc).decode(enc) == s, f'{enc} round-trip'
assert b'\xff\xfea\x00'.decode('utf-16') == 'a'
assert b'\xfe\xff\x00a'.decode('utf-16') == 'a'
assert b'\xff\xfe'.decode('utf-16') == ''
assert b''.decode('utf-16') == ''
assert b'\xff\xfe\x00\x00a\x00\x00\x00'.decode('utf-32') == 'a'
assert b'\x00\x00\xfe\xff\x00\x00\x00a'.decode('utf-32') == 'a'
assert b''.decode('utf-32') == ''
assert b'\xff\xfe\xff\xfe'.decode('utf-16') == ''
assert b'\xff\xfea\x00'.decode('utf-16-le') == 'a'
assert b'\x00\x00\xfe\xff'.decode('utf-32-be') == ''
try:
b'a\x00b'.decode('utf-16-le')
assert False, 'odd-length utf-16 should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-16-le' codec can't decode byte 0x62 in position 2: truncated data", (
f'utf-16 truncated single byte: {e}'
)
try:
b'\x00\xd8a\x00'.decode('utf-16-le')
assert False, 'lone high surrogate should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-16-le' codec can't decode bytes in position 0-1: illegal UTF-16 surrogate", (
f'utf-16 high surrogate without pair: {e}'
)
try:
b'\x00\xdca\x00'.decode('utf-16-le')
assert False, 'lone low surrogate should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-16-le' codec can't decode bytes in position 0-1: illegal encoding", (
f'utf-16 lone low surrogate: {e}'
)
try:
b'a\x00\x00\xd8'.decode('utf-16-le')
assert False, 'high surrogate at end should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-16-le' codec can't decode bytes in position 2-3: unexpected end of data", (
f'utf-16 high surrogate at end: {e}'
)
try:
b'a\x00\x00\xd8a'.decode('utf-16-le')
assert False, 'high surrogate + stray byte should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-16-le' codec can't decode bytes in position 2-4: unexpected end of data", (
f'utf-16 3-byte tail is one unit: {e}'
)
try:
b'\xff\xfe\x00\xd8'.decode('utf-16')
assert False, 'high surrogate after BOM should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-16-le' codec can't decode bytes in position 2-3: unexpected end of data", (
f'utf-16 position includes BOM: {e}'
)
try:
b'\xd8\x00a\x00'.decode('utf-16-be')
assert False, 'utf-16-be lone high surrogate should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-16-be' codec can't decode bytes in position 0-1: illegal UTF-16 surrogate", (
f'utf-16-be error name: {e}'
)
assert b'a\x00\x00\xd8b\x00'.decode('utf-16-le', 'replace') == 'a�b'
assert b'a\x00\x00\xd8'.decode('utf-16-le', 'replace') == 'a�'
assert b'a\x00\x00\xd8a'.decode('utf-16-le', 'replace') == 'a�'
assert b'a\x00\x00\xd8b\x00'.decode('utf-16-le', 'ignore') == 'ab'
assert b'a\x00b'.decode('utf-16-le', 'ignore') == 'a'
assert b'a\x00\x00\xd8b\x00'.decode('utf-16-le', 'backslashreplace') == 'a\\x00\\xd8b'
assert b'a\x00'.decode('utf-16-le', 'bogus') == 'a'
try:
b'a\x00b'.decode('utf-16-le', 'xmlcharrefreplace')
assert False, 'encode-only handler on decode should error'
except TypeError as e:
assert str(e) == "don't know how to handle UnicodeDecodeError in error callback", (
f'utf-16 decode xmlcharrefreplace: {e}'
)
try:
b'\xff\xff\xff\x00'.decode('utf-32-le')
assert False, 'out-of-range code point should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-32-le' codec can't decode bytes in position 0-3: code point not in range(0x110000)", (
f'utf-32 out of range: {e}'
)
try:
b'\x00\xd8\x00\x00'.decode('utf-32-le')
assert False, 'surrogate code point should error'
except UnicodeDecodeError as e:
assert str(e) == (
"'utf-32-le' codec can't decode bytes in position 0-3: code point in surrogate code point range(0xd800, 0xe000)"
), f'utf-32 surrogate code point: {e}'
try:
b'a\x00\x00'.decode('utf-32-le')
assert False, 'truncated utf-32 should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-32-le' codec can't decode bytes in position 0-2: truncated data", (
f'utf-32 truncated 3-byte tail: {e}'
)
try:
b'a'.decode('utf-32-le')
assert False, 'single byte utf-32 should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-32-le' codec can't decode byte 0x61 in position 0: truncated data", (
f'utf-32 single-byte form: {e}'
)
try:
b'\xff\xfe\x00\x00a\x00'.decode('utf-32')
assert False, 'truncated utf-32 after BOM should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-32-le' codec can't decode bytes in position 4-5: truncated data", (
f'utf-32 position includes BOM: {e}'
)
assert b'\xff\xff\xff\x00a\x00\x00\x00'.decode('utf-32-le', 'replace') == '�a'
assert b'\xff\xff\xff\x00a\x00\x00\x00'.decode('utf-32-le', 'ignore') == 'a'
assert b'\x00\xd8\x00\x00'.decode('utf-32-le', 'backslashreplace') == '\\x00\\xd8\\x00\\x00'
try:
b'a\xffb'.decode('utf-8')
assert False, 'invalid start byte should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-8' codec can't decode byte 0xff in position 1: invalid start byte", (
f'utf-8 invalid start byte: {e}'
)
try:
b'\x80'.decode('utf-8')
assert False, 'stray continuation byte should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte", (
f'utf-8 stray continuation byte: {e}'
)
try:
b'a\xe2\x28b'.decode('utf-8')
assert False, 'invalid continuation should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-8' codec can't decode byte 0xe2 in position 1: invalid continuation byte", (
f'utf-8 invalid continuation byte: {e}'
)
try:
b'\xf0\x9f(a'.decode('utf-8')
assert False, 'partial 4-byte sequence should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-8' codec can't decode bytes in position 0-1: invalid continuation byte", (
f'utf-8 multi-byte subpart range form: {e}'
)
try:
b'a\xe2\x82'.decode('utf-8')
assert False, 'truncated sequence should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-8' codec can't decode bytes in position 1-2: unexpected end of data", (
f'utf-8 truncated multi-byte sequence: {e}'
)
try:
b'\xc3'.decode('utf-8')
assert False, 'truncated 2-byte sequence should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-8' codec can't decode byte 0xc3 in position 0: unexpected end of data", (
f'utf-8 truncated single lead byte: {e}'
)
try:
b'a\xed\xa0\x80b'.decode('utf-8')
assert False, 'CESU-8 surrogate should error'
except UnicodeDecodeError as e:
assert str(e) == "'utf-8' codec can't decode byte 0xed in position 1: invalid continuation byte", (
f'utf-8 CESU-8 surrogate: {e}'
)
assert b'a\xe2\x82b\xf0\x9f\x98'.decode('utf-8', 'replace') == 'a�b�'
assert b'a\xf0\x28\x8c\x28b'.decode('utf-8', 'replace') == 'a�(�(b'
assert b'a\xed\xa0\x80b'.decode('utf-8', 'replace') == 'a���b'
assert b'a\xe2\x82b'.decode('utf-8', 'ignore') == 'ab'
assert b'a\xe2\x82b'.decode('utf-8', 'backslashreplace') == 'a\\xe2\\x82b'
assert b'hello'.decode('utf-8', 'bogus') == 'hello'
try:
b'h\xffllo'.decode('utf-8', 'bogus')
assert False, 'unknown handler should error once needed'
except LookupError as e:
assert str(e) == "unknown error handler name 'bogus'", f'utf-8 unknown handler: {e}'
try:
b'\xff'.decode('utf-8', 'surrogatepass')
assert False, 'surrogatepass should re-raise for non-surrogate errors'
except UnicodeDecodeError as e:
assert str(e) == "'utf-8' codec can't decode byte 0xff in position 0: invalid start byte", (
f'utf-8 surrogatepass re-raises strict: {e}'
)
try:
b'a\xffb'.decode('utf-8', 'xmlcharrefreplace')
assert False, 'encode-only handler on utf-8 decode should error'
except TypeError as e:
assert str(e) == "don't know how to handle UnicodeDecodeError in error callback", (
f'utf-8 decode xmlcharrefreplace: {e}'
)