From 3be61fdc7dfecd12dd0f939812433649aef3aafc Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 3 Sep 2026 14:43:59 +0200 Subject: [PATCH 1/4] base64: Move test code from the module to the test harness. This commit moves the test code present in the `base64` module to the already existing test harness. The module is able to be executed as a stand-alone script or via the `-m base64` command line parameter passed to the interpreter, to act as a simple base64 encoder/decoder utility. This is following the behaviour of CPython's equivalent module. However unlike the CPython implementation, this module also presents a `-t` argument that performs some basic encoding and decoding tests. Given the existence of a test harness, the usefulness of this option is rather limited as test code is better placed in test scripts. It is also rather unlikely that this module is executed by regular users as a standalone entity with the `-t` command line argument (this is even more relevant for embedded targets, as this functionality needs the `getopt` module being available to function). Moving the bits of code in question shortens the byte-compiled version of the module by 193 bytes, with the test code still being executed as part of CI jobs. Signed-off-by: Alessandro Gatti --- python-stdlib/base64/base64.py | 21 +++------------------ python-stdlib/base64/test_base64.py | 9 ++++++++- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/python-stdlib/base64/base64.py b/python-stdlib/base64/base64.py index d6baca05f..3d41736af 100644 --- a/python-stdlib/base64/base64.py +++ b/python-stdlib/base64/base64.py @@ -436,16 +436,14 @@ def main(): import sys, getopt try: - opts, args = getopt.getopt(sys.argv[1:], "deut") + opts, args = getopt.getopt(sys.argv[1:], "deu") except getopt.error as msg: sys.stdout = sys.stderr print(msg) print( - """usage: %s [-d|-e|-u|-t] [file|-] + """usage: %s [-d|-e|-u] [file|-] -d, -u: decode - -e: encode (default) - -t: encode and decode string 'Aladdin:open sesame'""" - % sys.argv[0] + -e: encode (default)""" ) sys.exit(2) func = encode @@ -456,9 +454,6 @@ def main(): func = decode if o == "-u": func = decode - if o == "-t": - test() - return if args and args[0] != "-": with open(args[0], "rb") as f: func(f, sys.stdout.buffer) @@ -466,15 +461,5 @@ def main(): func(sys.stdin.buffer, sys.stdout.buffer) -def test(): - s0 = b"Aladdin:open sesame" - print(repr(s0)) - s1 = encodebytes(s0) - print(repr(s1)) - s2 = decodebytes(s1) - print(repr(s2)) - assert s0 == s2 - - if __name__ == "__main__": main() diff --git a/python-stdlib/base64/test_base64.py b/python-stdlib/base64/test_base64.py index b29c29c84..645e15190 100644 --- a/python-stdlib/base64/test_base64.py +++ b/python-stdlib/base64/test_base64.py @@ -12,7 +12,14 @@ if d != b"zlutoucky kun upel dabelske ody": raise Exception("Error") -base64.test() +s0 = b"Aladdin:open sesame" +s1 = base64.encodebytes(s0) +if s1 != b"QWxhZGRpbjpvcGVuIHNlc2FtZQ==\n": + raise Exception("Error") +print(s1) +s2 = base64.decodebytes(s1) +if s0 != s2: + raise Exception("Error") binary = b"\x99\x10\xaa" b = base64.b64encode(binary) From de5b63fd453870830e0a879dd81cf1db77bc6715 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 3 Sep 2026 14:11:19 +0200 Subject: [PATCH 2/4] base64: Optimise base32 encoding and decoding. This commit updates the base32 encoding and decoding functions in order to reduce the footprint of the base64 module when byte-compiled, lower the amount of memory taken by the module once loaded, and finally to speed encoding and decoding operations up. Before these changes the base32 symbols were stored in a dictionary and forward and reverse lookup tables were built as lists upon module import. The symbols are just the letter A to Z and the numbers 2 to 7 in sequence with no gaps, and each symbol maps to an integer between 0 and 31. A forward lookup table can be trivially made by creating a bytes object with the symbols in sequence, as an index lookup is the same as accessing the n-th byte in the bytes object. The reverse lookup table can be also precomputed as a 256 bytes long bytes object filled with a sentinel value for representing a non-match or otherwise with the integer index in the forward lookup table. That brings down the byte-compiled size by 137 bytes. Regarding the memory footprint, besides the raw 288 bytes of data to store, the overhead is much lower as there's only two `bytes` objects being created behind the scenes. Finally, whilst forward lookups were still done by accessing a list via an integer, reverse lookups involved a dictionary search. Accessing a bytes object via an index is probably faster than a dictionary lookup, improving base32 decode speed. Signed-off-by: Alessandro Gatti --- python-stdlib/base64/base64.py | 63 +++++++++++------------------ python-stdlib/base64/test_base64.py | 11 +++++ 2 files changed, 34 insertions(+), 40 deletions(-) diff --git a/python-stdlib/base64/base64.py b/python-stdlib/base64/base64.py index 3d41736af..e4e99c459 100644 --- a/python-stdlib/base64/base64.py +++ b/python-stdlib/base64/base64.py @@ -171,43 +171,26 @@ def urlsafe_b64decode(s): # Base32 encoding/decoding must be done in Python -_b32alphabet = { - 0: b"A", - 9: b"J", - 18: b"S", - 27: b"3", - 1: b"B", - 10: b"K", - 19: b"T", - 28: b"4", - 2: b"C", - 11: b"L", - 20: b"U", - 29: b"5", - 3: b"D", - 12: b"M", - 21: b"V", - 30: b"6", - 4: b"E", - 13: b"N", - 22: b"W", - 31: b"7", - 5: b"F", - 14: b"O", - 23: b"X", - 6: b"G", - 15: b"P", - 24: b"Y", - 7: b"H", - 16: b"Q", - 25: b"Z", - 8: b"I", - 17: b"R", - 26: b"2", -} - -_b32tab = [v[0] for k, v in sorted(_b32alphabet.items())] -_b32rev = dict([(v[0], k) for k, v in _b32alphabet.items()]) +_b32tab = const(b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567") +# _b32rev = bytearray('\xFF' * 256); for i, j in enumerate(_b32tab): _b32rev[j] = i +_b32rev = const( + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + b"\xff\xff\x1a\x1b\x1c\x1d\x1e\x1f\xff\xff\xff\xff\xff\xff\xff\xff" + b"\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e" + b"\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\xff\xff\xff\xff\xff" + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +) def b32encode(s): @@ -303,10 +286,10 @@ def b32decode(s, casefold=False, map01=None): acc = 0 shift = 35 for c in s: - val = _b32rev.get(c) - if val is None: + val = _b32rev[c] + if val == 0xFF: raise binascii.Error("Non-base32 digit found") - acc += _b32rev[c] << shift + acc += val << shift shift -= 5 if shift < 0: parts.append(binascii.unhexlify(bytes("%010x" % acc, "ascii"))) diff --git a/python-stdlib/base64/test_base64.py b/python-stdlib/base64/test_base64.py index 645e15190..8ffa2e638 100644 --- a/python-stdlib/base64/test_base64.py +++ b/python-stdlib/base64/test_base64.py @@ -40,4 +40,15 @@ if b != b"zlutoucky kun upel dabelske ody": raise Exception("Error") +binary = b"\x99\x10\xaa" +b = base64.b32encode(binary) +if b != b"TEIKU===": + raise Exception("Error") + +d = base64.b32decode(b) +print(d) +if d != binary: + raise Exception("Error") + + print("OK") From 8fa13e3228018dd537eabe90fed836f0a280a1df Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Sun, 6 Sep 2026 17:06:30 +0200 Subject: [PATCH 3/4] base64: Declare standard functions via assignment. This commit changes the way the standard base64 encode/decode functions are created, from being full fledged proxies for b64encode/b64decode to aliases for those functions instead. This saves 31 bytes when compiled. Signed-off-by: Alessandro Gatti --- python-stdlib/base64/base64.py | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/python-stdlib/base64/base64.py b/python-stdlib/base64/base64.py index e4e99c459..19eedda03 100644 --- a/python-stdlib/base64/base64.py +++ b/python-stdlib/base64/base64.py @@ -120,23 +120,8 @@ def b64decode(s, altchars=None, validate=False): return binascii.a2b_base64(s) -def standard_b64encode(s): - """Encode a byte string using the standard Base64 alphabet. - - s is the byte string to encode. The encoded byte string is returned. - """ - return b64encode(s) - - -def standard_b64decode(s): - """Decode a byte string encoded with the standard Base64 alphabet. - - s is the byte string to decode. The decoded byte string is - returned. binascii.Error is raised if the input is incorrectly - padded or if there are non-alphabet characters present in the - input. - """ - return b64decode(s) +standard_b64encode = b64encode +standard_b64decode = b64decode # _urlsafe_encode_translation = _maketrans(b'+/', b'-_') From ac390868fa13c51be87456aa6e6a6541248a2109 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 3 Sep 2026 17:47:39 +0200 Subject: [PATCH 4/4] base64: Update package version. This commit updates the version number of the `base64` package, bumping up the minor version. Signed-off-by: Alessandro Gatti --- python-stdlib/base64/manifest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python-stdlib/base64/manifest.py b/python-stdlib/base64/manifest.py index 9e1b31751..b2d53c2a3 100644 --- a/python-stdlib/base64/manifest.py +++ b/python-stdlib/base64/manifest.py @@ -1,4 +1,4 @@ -metadata(version="3.3.6") +metadata(version="3.3.7") require("binascii")