Skip to content

Commit ba9ea94

Browse files
projectgusdpgeorge
authored andcommitted
docs: Document str & bytes encoding limitations.
Partially this is documenting some additional limitations added recently now that the encoding argument is not totally ignored. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
1 parent a62626a commit ba9ea94

6 files changed

Lines changed: 125 additions & 6 deletions

File tree

docs/reference/unicode_support.rst

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,10 @@ The :meth:`bytes.decode` and :meth:`str.encode` methods support the following en
4949
- UTF-8 (``'utf-8'`` or ``'utf8'``)
5050
- ASCII (``'ascii'``)
5151

52-
Other encodings (such as ``'latin-1'``, ``'utf-16'``, etc.) are not supported and
53-
will raise ``LookupError``.
52+
Other encodings (such as ``'latin-1'``, ``'utf-16'``, etc.) are not supported
53+
and will raise ``LookupError``. The encoding argument must also match one of the
54+
supported strings exactly (for example, ``'utf8'`` is valid but ``'UTF8'``
55+
is not). `More details <cpydiff_types_bytes_decode_encoding>`.
5456

5557
Example::
5658

@@ -91,6 +93,21 @@ The same ``errors`` handling applies when decoding any bytes-like object, includ
9193
via the ``str()`` constructor (for example ``str(buf, 'utf-8', 'replace')`` where
9294
``buf`` is a ``bytes``, ``bytearray``, ``memoryview`` or ``array`` object).
9395

96+
Encoding to Bytes
97+
~~~~~~~~~~~~~~~~~
98+
99+
Function ``str.encode()`` and the ``bytes()`` constructor accept an ``encoding``
100+
argument which can be ``'utf8'``, ``'utf-8'`` or ``'ascii'``::
101+
102+
>>> "abc".encode("ascii")
103+
b'abc'
104+
105+
If encoding ``'ascii'`` is specified then an exception is raised if the string
106+
contains non-ASCII characters.
107+
108+
The ``'ignore'`` and ``'replace'`` ``errors`` values are not supported in these
109+
conversions from string to bytes and `any errors argument is ignored
110+
<cpydiff_types_str_encode_errors>`.
94111

95112
String Methods
96113
--------------
Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
"""
22
categories: Types,bytes
3-
description: bytes.decode() only supports 'utf-8' and 'ascii' encodings, not other encodings like 'latin-1'
4-
cause: MicroPython is optimized for embedded systems and only includes UTF-8 and ASCII codec support to save memory. Other encodings would require additional codec tables.
5-
workaround: Convert data to UTF-8 before processing, or implement custom encoding/decoding if needed.
3+
description: bytes.decode() only supports encoding arguments 'utf8', 'utf-8' and 'ascii'. Other encodings like 'latin-1' are not supported. Other string forms such as 'UTF8' are not supported.
4+
cause: MicroPython is optimised for embedded systems and only includes UTF-8 and ASCII codec support with simple matching to save memory and code size. `The same restriction applies to str constructor <cpydiff_types_str_encoding>`. See also `unicode_support`.
5+
workaround: Convert data to UTF-8 before processing, or implement custom encoding/decoding if needed. Ensure encoding argument is one of the accepted forms.
66
"""
77

8-
# CPython supports many encodings, MicroPython only utf-8 and ascii
8+
# Both CPython and MicroPython support utf8 and ascii encodings
9+
print(b"caf\xc3\xa9".decode("utf8")) # codespell:ignore caf
10+
print(b"cafe".decode("ascii"))
11+
12+
# MicroPython does not support additional encodings
913
try:
1014
b"\xe9".decode("latin-1") # 'é' in latin-1
1115
print("latin-1 supported")
@@ -17,3 +21,10 @@
1721
print("cp1252 supported")
1822
except (ValueError, NotImplementedError, LookupError) as e:
1923
print("cp1252 not supported:", type(e).__name__)
24+
25+
# Encoding arguments must match exactly in MicroPython
26+
try:
27+
b"hello".decode("ASCII")
28+
print("Capital letters ASCII supported")
29+
except (ValueError, NotImplementedError, LookupError) as e:
30+
print("Capital letters ASCII not supported:", type(e).__name__)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""
2+
categories: Types,bytes
3+
description: bytes() constructor only supports encoding arguments 'utf8', 'utf-8' and 'ascii'. Other encodings like 'latin-1' are not supported. Other string forms such as 'UTF8' are not supported.
4+
cause: MicroPython is optimised for embedded systems and has limited codec support to save code size. `A similar restriction applies to str.encode() <cpydiff_types_str_encode_encoding>`. See also `unicode_support`.
5+
workaround: Implement other encoding conversions manually by parsing the result of str.encode() or bytes() constructor.
6+
"""
7+
8+
# Both CPython and MicroPython can encode this emoji as UTF-8 bytes
9+
print(bytes("😀", "utf8"))
10+
11+
# Both CPython and MicroPython will fail to encode this emoji as ASCII bytes
12+
try:
13+
print(bytes("😀", "ascii"))
14+
except UnicodeError:
15+
print("UnicodeError")
16+
17+
# Other encodings or string formats aren't accepted by MicroPython
18+
try:
19+
print(bytes("😀", "UTF-8"))
20+
except LookupError:
21+
print("LookupError")
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""
2+
categories: Types,str
3+
description: str.encode() constructor only supports encoding arguments 'utf8', 'utf-8' and 'ascii'. Other encodings like 'latin-1' are not supported. Other string forms such as 'UTF8' are not supported.
4+
cause: MicroPython is optimised for embedded systems and has limited codec support to save code size. `A similar restriction applies to bytes constructor <cpydiff_types_bytes_encoding>`. See also `unicode_support`.
5+
workaround: Implement encoding conversions manually by parsing the result of str.encode() or bytes() constructor.
6+
"""
7+
8+
# Both CPython and MicroPython can encode this emoji as UTF-8 bytes
9+
print("😀".encode("utf8"))
10+
11+
# Both CPython and MicroPython will fail to encode this emoji as ASCII bytes
12+
try:
13+
print("😀".encode("ascii"))
14+
except UnicodeError:
15+
print("UnicodeError")
16+
17+
# Other encodings or string formats aren't accepted by MicroPython:
18+
try:
19+
print("😀".encode("UTF-8"))
20+
except LookupError:
21+
print("LookupError")
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"""
2+
categories: Types,str
3+
description: str.encode() and bytes() constructor ignore any ``errors`` argument specified. If the encoding is specified as ``'ascii'`` and a non-ASCII byte is found in the string then an exception is always raised.
4+
cause: MicroPython is optimised for embedded systems and has limited codec support to save code size. See also `unicode_support`.
5+
workaround: Handle encoding errors manually by parsing the result of str.encode() or bytes() constructor.
6+
"""
7+
8+
# CPython will replace the emoji with an ASCII '?' but MicroPython will
9+
# raise an exception
10+
try:
11+
print("😀".encode("ascii", "replace"))
12+
except UnicodeError:
13+
print("UnicodeError")
14+
15+
# CPython will ignore the emoji in the result but MicroPython will raise an exception
16+
try:
17+
print("😀".encode("ascii", "ignore"))
18+
except UnicodeError:
19+
print("UnicodeError")
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""
2+
categories: Types,str
3+
description: str() constructor only supports encoding arguments 'utf8', 'utf-8' and 'ascii'. Other encodings like 'latin-1' are not supported. Other string forms such as 'UTF8' are not supported.
4+
cause: MicroPython is optimised for embedded systems and only includes UTF-8 and ASCII codec support with simple matching to save memory and code size. `The same restriction applies to bytes.decode() <cpydiff_types_bytes_decode_encoding>`. See also `unicode_support`.
5+
workaround: Convert data to UTF-8 before processing, or implement custom encoding/decoding if needed. Ensure encoding argument is one of the accepted forms.
6+
"""
7+
8+
# Both CPython and MicroPython support utf8 and ascii encodings
9+
print(str(b"caf\xc3\xa9", "utf8")) # codespell:ignore caf
10+
print(str(b"cafe", "ascii"))
11+
12+
# MicroPython does not support additional encodings
13+
try:
14+
str(b"\xe9", "latin-1") # 'é' in latin-1
15+
print("latin-1 supported")
16+
except (ValueError, NotImplementedError, LookupError) as e:
17+
print("latin-1 not supported:", type(e).__name__)
18+
19+
try:
20+
str(b"\x80", "cp1252") # Euro sign in cp1252
21+
print("cp1252 supported")
22+
except (ValueError, NotImplementedError, LookupError) as e:
23+
print("cp1252 not supported:", type(e).__name__)
24+
25+
# Encoding arguments must match exactly in MicroPython
26+
try:
27+
str(b"hello", "ASCII")
28+
print("Capital letters ASCII supported")
29+
except (ValueError, NotImplementedError, LookupError) as e:
30+
print("Capital letters ASCII not supported:", type(e).__name__)

0 commit comments

Comments
 (0)