From a1b2ab1c360cd5f03c381788266d00d48546e540 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 3 Sep 2026 14:43:59 +0200 Subject: [PATCH 1/3] 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 the 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 192 bytes, with the test code still being executed as part of CI jobs. Signed-off-by: Alessandro Gatti --- python-stdlib/base64/base64.py | 19 ++----------------- python-stdlib/base64/test_base64.py | 9 ++++++++- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/python-stdlib/base64/base64.py b/python-stdlib/base64/base64.py index d6baca05f..2940f242d 100644 --- a/python-stdlib/base64/base64.py +++ b/python-stdlib/base64/base64.py @@ -441,11 +441,9 @@ def main(): 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 f80bf7ca1796b500071ad3b953793142cfc6bcd1 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 3 Sep 2026 14:11:19 +0200 Subject: [PATCH 2/3] 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 106 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 | 62 ++++++++++------------------- python-stdlib/base64/test_base64.py | 11 +++++ 2 files changed, 33 insertions(+), 40 deletions(-) diff --git a/python-stdlib/base64/base64.py b/python-stdlib/base64/base64.py index 2940f242d..6f66a5eb1 100644 --- a/python-stdlib/base64/base64.py +++ b/python-stdlib/base64/base64.py @@ -171,43 +171,25 @@ 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 = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" +# _b32rev = bytearray('\xFF' * 256); for i, j in enumerate(_b32tab): _b32rev[j] = i +_b32rev = ( + b"\xff\xff\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\xff\xff" + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x1a\x1b\x1c\x1d" + b"\x1e\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x06" + b"\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18" + b"\x19\xff\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\xff\xff" + b"\xff\xff\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\xff\xff" + b"\xff\xff\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\xff\xff" + b"\xff\xff\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\xff\xff" + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + b"\xff\xff\xff\xff" +) def b32encode(s): @@ -303,10 +285,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 6cc285cf068d21fa3da33ca5db25c491da238eb7 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Thu, 3 Sep 2026 17:47:39 +0200 Subject: [PATCH 3/3] 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")