cpython/Lib/test/test_base64.py
Serhiy Storchaka c3b61ef73d
gh-144001: Simplify Base64 decoding with altchars and ignorechars specified (GH-144324)
Treat "+" and "/" like other characters not in the alternative Base64
alphabet when both altchars and ignorechars are specified.
E.g. discard them if they are not in altchars but are in ignorechars,
and set error if they are not in altchars and not in ignorechars.
Only emit warnings if ignorechars is not specified.
2026-02-02 13:38:32 +02:00

1216 lines
55 KiB
Python

import unittest
import base64
import binascii
import string
import sys
import os
from array import array
from test.support import cpython_only, check_impl_detail
from test.support import os_helper
from test.support import script_helper
from test.support.import_helper import ensure_lazy_imports
class LazyImportTest(unittest.TestCase):
@cpython_only
def test_lazy_import(self):
ensure_lazy_imports("base64", {"re", "getopt"})
from test.support.hypothesis_helper import hypothesis
class LegacyBase64TestCase(unittest.TestCase):
# Legacy API is not as permissive as the modern API
def check_type_errors(self, f):
self.assertRaises(TypeError, f, "")
self.assertRaises(TypeError, f, [])
multidimensional = memoryview(b"1234").cast('B', (2, 2))
self.assertRaises(TypeError, f, multidimensional)
int_data = memoryview(b"1234").cast('I')
self.assertRaises(TypeError, f, int_data)
def test_encodebytes(self):
eq = self.assertEqual
eq(base64.encodebytes(b"www.python.org"), b"d3d3LnB5dGhvbi5vcmc=\n")
eq(base64.encodebytes(b"a"), b"YQ==\n")
eq(base64.encodebytes(b"ab"), b"YWI=\n")
eq(base64.encodebytes(b"abc"), b"YWJj\n")
eq(base64.encodebytes(b""), b"")
eq(base64.encodebytes(b"abcdefghijklmnopqrstuvwxyz"
b"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
b"0123456789!@#0^&*();:<>,. []{}"),
b"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNE"
b"RUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0\nNT"
b"Y3ODkhQCMwXiYqKCk7Ojw+LC4gW117fQ==\n")
eq(base64.encodebytes(b"Aladdin:open sesame"),
b"QWxhZGRpbjpvcGVuIHNlc2FtZQ==\n")
# Non-bytes
eq(base64.encodebytes(bytearray(b'abc')), b'YWJj\n')
eq(base64.encodebytes(memoryview(b'abc')), b'YWJj\n')
eq(base64.encodebytes(array('B', b'abc')), b'YWJj\n')
self.check_type_errors(base64.encodebytes)
def test_decodebytes(self):
eq = self.assertEqual
eq(base64.decodebytes(b"d3d3LnB5dGhvbi5vcmc=\n"), b"www.python.org")
eq(base64.decodebytes(b"YQ==\n"), b"a")
eq(base64.decodebytes(b"YWI=\n"), b"ab")
eq(base64.decodebytes(b"YWJj\n"), b"abc")
eq(base64.decodebytes(b"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNE"
b"RUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0\nNT"
b"Y3ODkhQCMwXiYqKCk7Ojw+LC4gW117fQ==\n"),
b"abcdefghijklmnopqrstuvwxyz"
b"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
b"0123456789!@#0^&*();:<>,. []{}")
eq(base64.decodebytes(b''), b'')
eq(base64.decodebytes(b"QWxhZGRpbjpvcGVuIHNlc2FtZQ==\n"),
b"Aladdin:open sesame")
# Non-bytes
eq(base64.decodebytes(bytearray(b'YWJj\n')), b'abc')
eq(base64.decodebytes(memoryview(b'YWJj\n')), b'abc')
eq(base64.decodebytes(array('B', b'YWJj\n')), b'abc')
self.check_type_errors(base64.decodebytes)
@hypothesis.given(payload=hypothesis.strategies.binary())
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz')
def test_bytes_encode_decode_round_trip(self, payload):
encoded = base64.encodebytes(payload)
decoded = base64.decodebytes(encoded)
self.assertEqual(payload, decoded)
def test_encode(self):
eq = self.assertEqual
from io import BytesIO, StringIO
infp = BytesIO(b'abcdefghijklmnopqrstuvwxyz'
b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
b'0123456789!@#0^&*();:<>,. []{}')
outfp = BytesIO()
base64.encode(infp, outfp)
eq(outfp.getvalue(),
b'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNE'
b'RUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0\nNT'
b'Y3ODkhQCMwXiYqKCk7Ojw+LC4gW117fQ==\n')
# Non-binary files
self.assertRaises(TypeError, base64.encode, StringIO('abc'), BytesIO())
self.assertRaises(TypeError, base64.encode, BytesIO(b'abc'), StringIO())
self.assertRaises(TypeError, base64.encode, StringIO('abc'), StringIO())
def test_decode(self):
from io import BytesIO, StringIO
infp = BytesIO(b'd3d3LnB5dGhvbi5vcmc=')
outfp = BytesIO()
base64.decode(infp, outfp)
self.assertEqual(outfp.getvalue(), b'www.python.org')
# Non-binary files
self.assertRaises(TypeError, base64.encode, StringIO('YWJj\n'), BytesIO())
self.assertRaises(TypeError, base64.encode, BytesIO(b'YWJj\n'), StringIO())
self.assertRaises(TypeError, base64.encode, StringIO('YWJj\n'), StringIO())
@hypothesis.given(payload=hypothesis.strategies.binary())
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz')
def test_legacy_encode_decode_round_trip(self, payload):
from io import BytesIO
payload_file_r = BytesIO(payload)
encoded_file_w = BytesIO()
base64.encode(payload_file_r, encoded_file_w)
encoded_file_r = BytesIO(encoded_file_w.getvalue())
decoded_file_w = BytesIO()
base64.decode(encoded_file_r, decoded_file_w)
decoded = decoded_file_w.getvalue()
self.assertEqual(payload, decoded)
class BaseXYTestCase(unittest.TestCase):
# Modern API completely ignores exported dimension and format data and
# treats any buffer as a stream of bytes
def check_encode_type_errors(self, f):
self.assertRaises(TypeError, f, "")
self.assertRaises(TypeError, f, [])
def check_decode_type_errors(self, f):
self.assertRaises(TypeError, f, [])
def check_other_types(self, f, bytes_data, expected):
eq = self.assertEqual
b = bytearray(bytes_data)
eq(f(b), expected)
# The bytearray wasn't mutated
eq(b, bytes_data)
eq(f(memoryview(bytes_data)), expected)
eq(f(array('B', bytes_data)), expected)
# XXX why is b64encode hardcoded here?
self.check_nonbyte_element_format(base64.b64encode, bytes_data)
self.check_multidimensional(base64.b64encode, bytes_data)
def check_multidimensional(self, f, data):
padding = b"\x00" if len(data) % 2 else b""
bytes_data = data + padding # Make sure cast works
shape = (len(bytes_data) // 2, 2)
multidimensional = memoryview(bytes_data).cast('B', shape)
self.assertEqual(f(multidimensional), f(bytes_data))
def check_nonbyte_element_format(self, f, data):
padding = b"\x00" * ((4 - len(data)) % 4)
bytes_data = data + padding # Make sure cast works
int_data = memoryview(bytes_data).cast('I')
self.assertEqual(f(int_data), f(bytes_data))
def test_b64encode(self):
eq = self.assertEqual
# Test default alphabet
eq(base64.b64encode(b"www.python.org"), b"d3d3LnB5dGhvbi5vcmc=")
eq(base64.b64encode(b'\x00'), b'AA==')
eq(base64.b64encode(b"a"), b"YQ==")
eq(base64.b64encode(b"ab"), b"YWI=")
eq(base64.b64encode(b"abc"), b"YWJj")
eq(base64.b64encode(b""), b"")
eq(base64.b64encode(b"abcdefghijklmnopqrstuvwxyz"
b"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
b"0123456789!@#0^&*();:<>,. []{}"),
b"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNE"
b"RUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NT"
b"Y3ODkhQCMwXiYqKCk7Ojw+LC4gW117fQ==")
# Test with arbitrary alternative characters
eq(base64.b64encode(b'\xd3V\xbeo\xf7\x1d', altchars=b'*$'), b'01a*b$cd')
eq(base64.b64encode(b'\xd3V\xbeo\xf7\x1d', altchars=bytearray(b'*$')),
b'01a*b$cd')
eq(base64.b64encode(b'\xd3V\xbeo\xf7\x1d', altchars=memoryview(b'*$')),
b'01a*b$cd')
eq(base64.b64encode(b'\xd3V\xbeo\xf7\x1d', altchars=array('B', b'*$')),
b'01a*b$cd')
# Non-bytes
self.check_other_types(base64.b64encode, b'abcd', b'YWJjZA==')
self.check_encode_type_errors(base64.b64encode)
self.assertRaises(TypeError, base64.b64encode, b"", altchars="*$")
# Test standard alphabet
eq(base64.standard_b64encode(b"www.python.org"), b"d3d3LnB5dGhvbi5vcmc=")
eq(base64.standard_b64encode(b"a"), b"YQ==")
eq(base64.standard_b64encode(b"ab"), b"YWI=")
eq(base64.standard_b64encode(b"abc"), b"YWJj")
eq(base64.standard_b64encode(b""), b"")
eq(base64.standard_b64encode(b"abcdefghijklmnopqrstuvwxyz"
b"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
b"0123456789!@#0^&*();:<>,. []{}"),
b"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNE"
b"RUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NT"
b"Y3ODkhQCMwXiYqKCk7Ojw+LC4gW117fQ==")
# Non-bytes
self.check_other_types(base64.standard_b64encode,
b'abcd', b'YWJjZA==')
self.check_encode_type_errors(base64.standard_b64encode)
# Test with 'URL safe' alternative characters
eq(base64.urlsafe_b64encode(b'\xd3V\xbeo\xf7\x1d'), b'01a-b_cd')
# Non-bytes
self.check_other_types(base64.urlsafe_b64encode,
b'\xd3V\xbeo\xf7\x1d', b'01a-b_cd')
self.check_encode_type_errors(base64.urlsafe_b64encode)
def test_b64encode_wrapcol(self):
eq = self.assertEqual
b = b'www.python.org'
eq(base64.b64encode(b, wrapcol=0), b'd3d3LnB5dGhvbi5vcmc=')
eq(base64.b64encode(b, wrapcol=8), b'd3d3LnB5\ndGhvbi5v\ncmc=')
eq(base64.b64encode(b, wrapcol=11), b'd3d3LnB5\ndGhvbi5v\ncmc=')
eq(base64.b64encode(b, wrapcol=76), b'd3d3LnB5dGhvbi5vcmc=')
eq(base64.b64encode(b, wrapcol=1), b'd3d3\nLnB5\ndGhv\nbi5v\ncmc=')
eq(base64.b64encode(b, wrapcol=sys.maxsize), b'd3d3LnB5dGhvbi5vcmc=')
if check_impl_detail():
eq(base64.b64encode(b, wrapcol=sys.maxsize*2),
b'd3d3LnB5dGhvbi5vcmc=')
with self.assertRaises(OverflowError):
base64.b64encode(b, wrapcol=2**1000)
with self.assertRaises(ValueError):
base64.b64encode(b, wrapcol=-8)
with self.assertRaises(TypeError):
base64.b64encode(b, wrapcol=8.0)
with self.assertRaises(TypeError):
base64.b64encode(b, wrapcol='8')
with self.assertRaises(TypeError):
base64.b64encode(b, wrapcol=None)
eq(base64.b64encode(b'', wrapcol=0), b'')
eq(base64.b64encode(b'', wrapcol=8), b'')
def test_b64decode(self):
eq = self.assertEqual
tests = {b"d3d3LnB5dGhvbi5vcmc=": b"www.python.org",
b'AA==': b'\x00',
b"YQ==": b"a",
b"YWI=": b"ab",
b"YWJj": b"abc",
b"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNE"
b"RUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0\nNT"
b"Y3ODkhQCMwXiYqKCk7Ojw+LC4gW117fQ==":
b"abcdefghijklmnopqrstuvwxyz"
b"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
b"0123456789!@#0^&*();:<>,. []{}",
b'': b'',
}
for data, res in tests.items():
eq(base64.b64decode(data), res)
eq(base64.b64decode(data.decode('ascii')), res)
# Non-bytes
self.check_other_types(base64.b64decode, b"YWJj", b"abc")
self.check_decode_type_errors(base64.b64decode)
# Test standard alphabet
for data, res in tests.items():
eq(base64.standard_b64decode(data), res)
eq(base64.standard_b64decode(data.decode('ascii')), res)
# Non-bytes
self.check_other_types(base64.standard_b64decode, b"YWJj", b"abc")
self.check_decode_type_errors(base64.standard_b64decode)
# Test with 'URL safe' alternative characters
tests_urlsafe = {b'01a-b_cd': b'\xd3V\xbeo\xf7\x1d',
b'': b'',
}
for data, res in tests_urlsafe.items():
eq(base64.urlsafe_b64decode(data), res)
eq(base64.urlsafe_b64decode(data.decode('ascii')), res)
# Non-bytes
self.check_other_types(base64.urlsafe_b64decode, b'01a-b_cd',
b'\xd3V\xbeo\xf7\x1d')
self.check_decode_type_errors(base64.urlsafe_b64decode)
def test_b64decode_altchars(self):
# Test with arbitrary alternative characters
eq = self.assertEqual
res = b'\xd3V\xbeo\xf7\x1d'
for altchars in b'*$', b'+/', b'/+', b'+_', b'-+', b'-/', b'/_':
data = b'01a%cb%ccd' % tuple(altchars)
data_str = data.decode('ascii')
altchars_str = altchars.decode('ascii')
eq(base64.b64decode(data, altchars=altchars), res)
eq(base64.b64decode(data_str, altchars=altchars), res)
eq(base64.b64decode(data, altchars=altchars_str), res)
eq(base64.b64decode(data_str, altchars=altchars_str), res)
eq(base64.b64decode(data, altchars=altchars, ignorechars=b'\n'), res)
self.assertRaises(ValueError, base64.b64decode, b'', altchars=b'+')
self.assertRaises(ValueError, base64.b64decode, b'', altchars=b'+/-')
self.assertRaises(ValueError, base64.b64decode, '', altchars='+')
self.assertRaises(ValueError, base64.b64decode, '', altchars='+/-')
def test_b64decode_padding_error(self):
self.assertRaises(binascii.Error, base64.b64decode, b'abc')
self.assertRaises(binascii.Error, base64.b64decode, 'abc')
def test_b64decode_invalid_chars(self):
# issue 1466065: Test some invalid characters.
tests = ((b'%3d==', b'\xdd', b'%$'),
(b'$3d==', b'\xdd', b'%$'),
(b'[==', b'', None),
(b'YW]3=', b'am', b']'),
(b'3{d==', b'\xdd', b'{}'),
(b'3d}==', b'\xdd', b'{}'),
(b'@@', b'', b'@!'),
(b'!', b'', b'@!'),
(b"YWJj\n", b"abc", b'\n'),
(b'YWJj\nYWI=', b'abcab', b'\n'),
(b'YW\nJj', b'abc', b'\n'),
(b'YW\nJj', b'abc', bytearray(b'\n')),
(b'YW\nJj', b'abc', memoryview(b'\n')),
)
funcs = (
base64.b64decode,
base64.standard_b64decode,
base64.urlsafe_b64decode,
)
for bstr, res, ignorechars in tests:
for func in funcs:
with self.subTest(bstr=bstr, func=func):
self.assertEqual(func(bstr), res)
self.assertEqual(func(bstr.decode('ascii')), res)
with self.assertRaises(binascii.Error):
base64.b64decode(bstr, validate=True)
with self.assertRaises(binascii.Error):
base64.b64decode(bstr.decode('ascii'), validate=True)
with self.assertRaises(binascii.Error):
# Even empty ignorechars enables the strict mode.
base64.b64decode(bstr, ignorechars=b'')
if ignorechars is not None:
r = base64.b64decode(bstr, ignorechars=ignorechars)
self.assertEqual(r, res)
with self.assertRaises(TypeError):
base64.b64decode(b'', ignorechars='')
with self.assertRaises(TypeError):
base64.b64decode(b'', ignorechars=[])
with self.assertRaises(TypeError):
base64.b64decode(b'', ignorechars=None)
# Normal alphabet characters will be discarded when alternative given
discarded = ("invalid character %a in Base64 data with %s "
"will be discarded in future Python versions")
error = ("invalid character %a in Base64 data with %s "
"will be an error in future Python versions")
with self.assertWarns(FutureWarning) as cm:
r = base64.b64decode(b'++++', altchars=b'-_')
self.assertEqual(r, b'\xfb\xef\xbe')
self.assertEqual(str(cm.warning),
discarded % ('+', "altchars=b'-_' and validate=False"))
with self.assertWarns(FutureWarning) as cm:
r = base64.b64decode(b'////', altchars=b'-_')
self.assertEqual(r, b'\xff\xff\xff')
self.assertEqual(str(cm.warning),
discarded % ('/', "altchars=b'-_' and validate=False"))
with self.assertWarns(DeprecationWarning) as cm:
r = base64.b64decode(b'++++', altchars=b'-_', validate=True)
self.assertEqual(r, b'\xfb\xef\xbe')
self.assertEqual(str(cm.warning),
error % ('+', "altchars=b'-_' and validate=True"))
with self.assertWarns(DeprecationWarning) as cm:
r = base64.b64decode(b'////', altchars=b'-_', validate=True)
self.assertEqual(r, b'\xff\xff\xff')
self.assertEqual(str(cm.warning),
error % ('/', "altchars=b'-_' and validate=True"))
r = base64.b64decode(b'++++', altchars=b'-_', ignorechars=b'+')
self.assertEqual(r, b'')
r = base64.b64decode(b'////', altchars=b'-_', ignorechars=b'/')
self.assertEqual(r, b'')
r = base64.b64decode(b'++++////', altchars=b'-_', validate=False, ignorechars=b'')
self.assertEqual(r, b'')
with self.assertRaisesRegex(binascii.Error, 'Only base64 data is allowed'):
base64.b64decode(b'////', altchars=b'-_', ignorechars=b'')
with self.assertRaisesRegex(binascii.Error, 'Only base64 data is allowed'):
base64.b64decode(b'++++', altchars=b'-_', ignorechars=b'')
r = base64.b64decode(b'++++YWJj----____', altchars=b'-_', ignorechars=b'+')
self.assertEqual(r, b'abc\xfb\xef\xbe\xff\xff\xff')
r = base64.b64decode(b'////YWJj----____', altchars=b'-_', ignorechars=b'/')
self.assertEqual(r, b'abc\xfb\xef\xbe\xff\xff\xff')
r = base64.b64decode(b'++++,,,,', altchars=b'+,', ignorechars=b'+')
self.assertEqual(r, b'\xfb\xef\xbe\xff\xff\xff')
r = base64.b64decode(b'////YWJj++++,,,,', altchars=b'+,', ignorechars=b'/')
self.assertEqual(r, b'abc\xfb\xef\xbe\xff\xff\xff')
r = base64.b64decode(b'----////', altchars=b'-/', ignorechars=b'/')
self.assertEqual(r, b'\xfb\xef\xbe\xff\xff\xff')
r = base64.b64decode(b'++++YWJj----////', altchars=b'-/', ignorechars=b'+')
self.assertEqual(r, b'abc\xfb\xef\xbe\xff\xff\xff')
with self.assertWarns(FutureWarning) as cm:
self.assertEqual(base64.urlsafe_b64decode(b'++++'), b'\xfb\xef\xbe')
self.assertEqual(str(cm.warning),
"invalid character '+' in URL-safe Base64 data "
"will be discarded in future Python versions")
with self.assertWarns(FutureWarning) as cm:
self.assertEqual(base64.urlsafe_b64decode(b'////'), b'\xff\xff\xff')
self.assertEqual(str(cm.warning),
"invalid character '/' in URL-safe Base64 data "
"will be discarded in future Python versions")
with self.assertRaises(binascii.Error):
base64.b64decode(b'+/!', altchars=b'-_')
def _altchars_strategy():
"""Generate 'altchars' for base64 encoding."""
reserved_chars = (string.digits + string.ascii_letters + "=").encode()
allowed_chars = hypothesis.strategies.sampled_from(
[n for n in range(256) if n not in reserved_chars])
two_bytes_strategy = hypothesis.strategies.lists(
allowed_chars, min_size=2, max_size=2, unique=True).map(bytes)
return (hypothesis.strategies.none()
| hypothesis.strategies.just(b"_-")
| two_bytes_strategy)
@hypothesis.given(
payload=hypothesis.strategies.binary(),
altchars=_altchars_strategy(),
validate=hypothesis.strategies.booleans())
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', b"_-", True)
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', b"_-", False)
def test_b64_encode_decode_round_trip(self, payload, altchars, validate):
encoded = base64.b64encode(payload, altchars=altchars)
decoded = base64.b64decode(encoded, altchars=altchars,
validate=validate)
self.assertEqual(payload, decoded)
@hypothesis.given(payload=hypothesis.strategies.binary())
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz')
def test_standard_b64_encode_decode_round_trip(self, payload):
encoded = base64.standard_b64encode(payload)
decoded = base64.standard_b64decode(encoded)
self.assertEqual(payload, decoded)
@hypothesis.given(payload=hypothesis.strategies.binary())
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz')
def test_urlsafe_b64_encode_decode_round_trip(self, payload):
encoded = base64.urlsafe_b64encode(payload)
decoded = base64.urlsafe_b64decode(encoded)
self.assertEqual(payload, decoded)
def test_b32encode(self):
eq = self.assertEqual
eq(base64.b32encode(b''), b'')
eq(base64.b32encode(b'\x00'), b'AA======')
eq(base64.b32encode(b'a'), b'ME======')
eq(base64.b32encode(b'ab'), b'MFRA====')
eq(base64.b32encode(b'abc'), b'MFRGG===')
eq(base64.b32encode(b'abcd'), b'MFRGGZA=')
eq(base64.b32encode(b'abcde'), b'MFRGGZDF')
# Non-bytes
self.check_other_types(base64.b32encode, b'abcd', b'MFRGGZA=')
self.check_encode_type_errors(base64.b32encode)
def test_b32decode(self):
eq = self.assertEqual
tests = {b'': b'',
b'AA======': b'\x00',
b'ME======': b'a',
b'MFRA====': b'ab',
b'MFRGG===': b'abc',
b'MFRGGZA=': b'abcd',
b'MFRGGZDF': b'abcde',
}
for data, res in tests.items():
eq(base64.b32decode(data), res)
eq(base64.b32decode(data.decode('ascii')), res)
# Non-bytes
self.check_other_types(base64.b32decode, b'MFRGG===', b"abc")
self.check_decode_type_errors(base64.b32decode)
def test_b32decode_casefold(self):
eq = self.assertEqual
tests = {b'': b'',
b'ME======': b'a',
b'MFRA====': b'ab',
b'MFRGG===': b'abc',
b'MFRGGZA=': b'abcd',
b'MFRGGZDF': b'abcde',
# Lower cases
b'me======': b'a',
b'mfra====': b'ab',
b'mfrgg===': b'abc',
b'mfrggza=': b'abcd',
b'mfrggzdf': b'abcde',
}
for data, res in tests.items():
eq(base64.b32decode(data, True), res)
eq(base64.b32decode(data.decode('ascii'), True), res)
self.assertRaises(binascii.Error, base64.b32decode, b'me======')
self.assertRaises(binascii.Error, base64.b32decode, 'me======')
def test_b32decode_map01(self):
# Mapping zero and one
eq = self.assertEqual
res_L = b'b\xdd\xad\xf3\xbe'
res_I = b'b\x1d\xad\xf3\xbe'
eq(base64.b32decode(b'MLO23456'), res_L)
eq(base64.b32decode('MLO23456'), res_L)
eq(base64.b32decode(b'MIO23456'), res_I)
eq(base64.b32decode('MIO23456'), res_I)
self.assertRaises(binascii.Error, base64.b32decode, b'M1023456')
self.assertRaises(binascii.Error, base64.b32decode, b'M1O23456')
self.assertRaises(binascii.Error, base64.b32decode, b'ML023456')
self.assertRaises(binascii.Error, base64.b32decode, b'MI023456')
data = b'M1023456'
data_str = data.decode('ascii')
for map01, res in [(b'L', res_L), (b'I', res_I)]:
map01_str = map01.decode('ascii')
eq(base64.b32decode(data, map01=map01), res)
eq(base64.b32decode(data_str, map01=map01), res)
eq(base64.b32decode(data, map01=map01_str), res)
eq(base64.b32decode(data_str, map01=map01_str), res)
eq(base64.b32decode(b'M1O23456', map01=map01), res)
eq(base64.b32decode(b'M%c023456' % map01, map01=map01), res)
eq(base64.b32decode(b'M%cO23456' % map01, map01=map01), res)
def test_b32decode_error(self):
tests = [b'abc', b'ABCDEF==', b'==ABCDEF']
prefixes = [b'M', b'ME', b'MFRA', b'MFRGG', b'MFRGGZA', b'MFRGGZDF']
for i in range(0, 17):
if i:
tests.append(b'='*i)
for prefix in prefixes:
if len(prefix) + i != 8:
tests.append(prefix + b'='*i)
for data in tests:
with self.subTest(data=data):
with self.assertRaises(binascii.Error):
base64.b32decode(data)
with self.assertRaises(binascii.Error):
base64.b32decode(data.decode('ascii'))
@hypothesis.given(
payload=hypothesis.strategies.binary(),
casefold=hypothesis.strategies.booleans(),
map01=(
hypothesis.strategies.none()
| hypothesis.strategies.binary(min_size=1, max_size=1)))
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', True, None)
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', False, None)
def test_b32_encode_decode_round_trip(self, payload, casefold, map01):
encoded = base64.b32encode(payload)
decoded = base64.b32decode(encoded, casefold=casefold, map01=map01)
self.assertEqual(payload, decoded)
def test_b32hexencode(self):
test_cases = [
# to_encode, expected
(b'', b''),
(b'\x00', b'00======'),
(b'a', b'C4======'),
(b'ab', b'C5H0===='),
(b'abc', b'C5H66==='),
(b'abcd', b'C5H66P0='),
(b'abcde', b'C5H66P35'),
]
for to_encode, expected in test_cases:
with self.subTest(to_decode=to_encode):
self.assertEqual(base64.b32hexencode(to_encode), expected)
def test_b32hexencode_other_types(self):
self.check_other_types(base64.b32hexencode, b'abcd', b'C5H66P0=')
self.check_encode_type_errors(base64.b32hexencode)
def test_b32hexdecode(self):
test_cases = [
# to_decode, expected, casefold
(b'', b'', False),
(b'00======', b'\x00', False),
(b'C4======', b'a', False),
(b'C5H0====', b'ab', False),
(b'C5H66===', b'abc', False),
(b'C5H66P0=', b'abcd', False),
(b'C5H66P35', b'abcde', False),
(b'', b'', True),
(b'00======', b'\x00', True),
(b'C4======', b'a', True),
(b'C5H0====', b'ab', True),
(b'C5H66===', b'abc', True),
(b'C5H66P0=', b'abcd', True),
(b'C5H66P35', b'abcde', True),
(b'c4======', b'a', True),
(b'c5h0====', b'ab', True),
(b'c5h66===', b'abc', True),
(b'c5h66p0=', b'abcd', True),
(b'c5h66p35', b'abcde', True),
]
for to_decode, expected, casefold in test_cases:
with self.subTest(to_decode=to_decode, casefold=casefold):
self.assertEqual(base64.b32hexdecode(to_decode, casefold),
expected)
self.assertEqual(base64.b32hexdecode(to_decode.decode('ascii'),
casefold), expected)
def test_b32hexdecode_other_types(self):
self.check_other_types(base64.b32hexdecode, b'C5H66===', b'abc')
self.check_decode_type_errors(base64.b32hexdecode)
def test_b32hexdecode_error(self):
tests = [b'abc', b'ABCDEF==', b'==ABCDEF', b'c4======']
prefixes = [b'M', b'ME', b'MFRA', b'MFRGG', b'MFRGGZA', b'MFRGGZDF']
for i in range(0, 17):
if i:
tests.append(b'='*i)
for prefix in prefixes:
if len(prefix) + i != 8:
tests.append(prefix + b'='*i)
for data in tests:
with self.subTest(to_decode=data):
with self.assertRaises(binascii.Error):
base64.b32hexdecode(data)
with self.assertRaises(binascii.Error):
base64.b32hexdecode(data.decode('ascii'))
@hypothesis.given(
payload=hypothesis.strategies.binary(),
casefold=hypothesis.strategies.booleans())
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', True)
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', False)
def test_b32_hexencode_decode_round_trip(self, payload, casefold):
encoded = base64.b32hexencode(payload)
decoded = base64.b32hexdecode(encoded, casefold=casefold)
self.assertEqual(payload, decoded)
def test_b16encode(self):
eq = self.assertEqual
eq(base64.b16encode(b'\x01\x02\xab\xcd\xef'), b'0102ABCDEF')
eq(base64.b16encode(b'\x00'), b'00')
# Non-bytes
self.check_other_types(base64.b16encode, b'\x01\x02\xab\xcd\xef',
b'0102ABCDEF')
self.check_encode_type_errors(base64.b16encode)
def test_b16decode(self):
eq = self.assertEqual
eq(base64.b16decode(b'0102ABCDEF'), b'\x01\x02\xab\xcd\xef')
eq(base64.b16decode('0102ABCDEF'), b'\x01\x02\xab\xcd\xef')
eq(base64.b16decode(b'00'), b'\x00')
eq(base64.b16decode('00'), b'\x00')
# Lower case is not allowed without a flag
self.assertRaises(binascii.Error, base64.b16decode, b'0102abcdef')
self.assertRaises(binascii.Error, base64.b16decode, '0102abcdef')
# Case fold
eq(base64.b16decode(b'0102abcdef', True), b'\x01\x02\xab\xcd\xef')
eq(base64.b16decode('0102abcdef', True), b'\x01\x02\xab\xcd\xef')
# Non-bytes
self.check_other_types(base64.b16decode, b"0102ABCDEF",
b'\x01\x02\xab\xcd\xef')
self.check_decode_type_errors(base64.b16decode)
eq(base64.b16decode(bytearray(b"0102abcdef"), True),
b'\x01\x02\xab\xcd\xef')
eq(base64.b16decode(memoryview(b"0102abcdef"), True),
b'\x01\x02\xab\xcd\xef')
eq(base64.b16decode(array('B', b"0102abcdef"), True),
b'\x01\x02\xab\xcd\xef')
# Non-alphabet characters
self.assertRaises(binascii.Error, base64.b16decode, '0102AG')
# Incorrect "padding"
self.assertRaises(binascii.Error, base64.b16decode, '010')
@hypothesis.given(
payload=hypothesis.strategies.binary(),
casefold=hypothesis.strategies.booleans())
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', True)
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', False)
def test_b16_encode_decode_round_trip(self, payload, casefold):
endoded = base64.b16encode(payload)
decoded = base64.b16decode(endoded, casefold=casefold)
self.assertEqual(payload, decoded)
def test_a85encode(self):
eq = self.assertEqual
tests = {
b'': b'',
b"www.python.org": b'GB\\6`E-ZP=Df.1GEb>',
bytes(range(255)): b"""!!*-'"9eu7#RLhG$k3[W&.oNg'GVB"(`=52*$$"""
b"""(B+<_pR,UFcb-n-Vr/1iJ-0JP==1c70M3&s#]4?Ykm5X@_(6q'R884cE"""
b"""H9MJ8X:f1+h<)lt#=BSg3>[:ZC?t!MSA7]@cBPD3sCi+'.E,fo>FEMbN"""
b"""G^4U^I!pHnJ:W<)KS>/9Ll%"IN/`jYOHG]iPa.Q$R$jD4S=Q7DTV8*TU"""
b"""nsrdW2ZetXKAY/Yd(L?['d?O\\@K2_]Y2%o^qmn*`5Ta:aN;TJbg"GZd"""
b"""*^:jeCE.%f\\,!5gtgiEi8N\\UjQ5OekiqBum-X60nF?)@o_%qPq"ad`"""
b"""r;HT""",
b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
b"0123456789!@#0^&*();:<>,. []{}":
b'@:E_WAS,RgBkhF"D/O92EH6,BF`qtRH$VbC6UX@47n?3D92&&T'
b":Jand;cHat='/U/0JP==1c70M3&r-I,;<FN.OZ`-3]oSW/g+A(H[P",
b"no padding..": b'DJpY:@:Wn_DJ(RS',
b"zero compression\0\0\0\0": b'H=_,8+Cf>,E,oN2F(oQ1z',
b"zero compression\0\0\0": b'H=_,8+Cf>,E,oN2F(oQ1!!!!',
b"Boundary:\0\0\0\0": b'6>q!aA79M(3WK-[!!',
b"Space compr: ": b';fH/TAKYK$D/aMV+<VdL',
b'\xff': b'rr',
b'\xff'*2: b's8N',
b'\xff'*3: b's8W*',
b'\xff'*4: b's8W-!',
}
for data, res in tests.items():
eq(base64.a85encode(data), res, data)
eq(base64.a85encode(data, adobe=False), res, data)
eq(base64.a85encode(data, adobe=True), b'<~' + res + b'~>', data)
self.check_other_types(base64.a85encode, b"www.python.org",
b'GB\\6`E-ZP=Df.1GEb>')
self.assertRaises(TypeError, base64.a85encode, "")
eq(base64.a85encode(b' '*8, foldspaces=True, adobe=False), b'yy')
eq(base64.a85encode(b' '*7, foldspaces=True, adobe=False), b'y+<Vd')
eq(base64.a85encode(b' '*6, foldspaces=True, adobe=False), b'y+<U')
eq(base64.a85encode(b' '*5, foldspaces=True, adobe=False), b'y+9')
def test_a85encode_wrapcol(self):
eq = self.assertEqual
b = b'www.python.org'
eq(base64.a85encode(b, wrapcol=0), b'GB\\6`E-ZP=Df.1GEb>')
eq(base64.a85encode(b, wrapcol=7), b'GB\\6`E-\nZP=Df.1\nGEb>')
eq(base64.a85encode(b"\0\0\0\0www.python.org", wrapcol=7),
b'zGB\\6`E\n-ZP=Df.\n1GEb>')
eq(base64.a85encode(b, wrapcol=75), b'GB\\6`E-ZP=Df.1GEb>')
eq(base64.a85encode(b, wrapcol=1),
b'G\nB\n\\\n6\n`\nE\n-\nZ\nP\n=\nD\nf\n.\n1\nG\nE\nb\n>')
eq(base64.a85encode(b, wrapcol=7, adobe=True),
b'<~GB\\6`\nE-ZP=Df\n.1GEb>\n~>')
eq(base64.a85encode(b, wrapcol=1),
b'G\nB\n\\\n6\n`\nE\n-\nZ\nP\n=\nD\nf\n.\n1\nG\nE\nb\n>')
eq(base64.a85encode(b, wrapcol=1, adobe=True),
b'<~\nGB\n\\6\n`E\n-Z\nP=\nDf\n.1\nGE\nb>\n~>')
eq(base64.a85encode(b, wrapcol=sys.maxsize), b'GB\\6`E-ZP=Df.1GEb>')
if check_impl_detail():
eq(base64.a85encode(b, wrapcol=2**1000), b'GB\\6`E-ZP=Df.1GEb>')
eq(base64.a85encode(b, wrapcol=-7),
b'G\nB\n\\\n6\n`\nE\n-\nZ\nP\n=\nD\nf\n.\n1\nG\nE\nb\n>')
eq(base64.a85encode(b, wrapcol=-7, adobe=True),
b'<~\nGB\n\\6\n`E\n-Z\nP=\nDf\n.1\nGE\nb>\n~>')
with self.assertRaises(TypeError):
base64.a85encode(b, wrapcol=7.0)
with self.assertRaises(TypeError):
base64.a85encode(b, wrapcol='7')
if check_impl_detail():
eq(base64.a85encode(b, wrapcol=None), b'GB\\6`E-ZP=Df.1GEb>')
eq(base64.a85encode(b'', wrapcol=0), b'')
eq(base64.a85encode(b'', wrapcol=7), b'')
eq(base64.a85encode(b'', wrapcol=1, adobe=True), b'<~\n~>')
eq(base64.a85encode(b'', wrapcol=3, adobe=True), b'<~\n~>')
eq(base64.a85encode(b'', wrapcol=4, adobe=True), b'<~~>')
def test_b85encode(self):
eq = self.assertEqual
tests = {
b'': b'',
b'www.python.org': b'cXxL#aCvlSZ*DGca%T',
bytes(range(255)): b"""009C61O)~M2nh-c3=Iws5D^j+6crX17#SKH9337X"""
b"""AR!_nBqb&%C@Cr{EG;fCFflSSG&MFiI5|2yJUu=?KtV!7L`6nNNJ&ad"""
b"""OifNtP*GA-R8>}2SXo+ITwPvYU}0ioWMyV&XlZI|Y;A6DaB*^Tbai%j"""
b"""czJqze0_d@fPsR8goTEOh>41ejE#<ukdcy;l$Dm3n3<ZJoSmMZprN9p"""
b"""q@|{(sHv)}tgWuEu(7hUw6(UkxVgH!yuH4^z`?@9#Kp$P$jQpf%+1cv"""
b"""(9zP<)YaD4*xB0K+}+;a;Njxq<mKk)=;`X~?CtLF@bU8V^!4`l`1$(#"""
b"""{Qdp""",
b"""abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"""
b"""0123456789!@#0^&*();:<>,. []{}""":
b"""VPa!sWoBn+X=-b1ZEkOHadLBXb#`}nd3r%YLqtVJM@UIZOH55pPf$@("""
b"""Q&d$}S6EqEFflSSG&MFiI5{CeBQRbjDkv#CIy^osE+AW7dwl""",
b'no padding..': b'Zf_uPVPs@!Zf7no',
b'zero compression\x00\x00\x00\x00': b'dS!BNAY*TBaB^jHb7^mG00000',
b'zero compression\x00\x00\x00': b'dS!BNAY*TBaB^jHb7^mG0000',
b"""Boundary:\x00\x00\x00\x00""": b"""LT`0$WMOi7IsgCw00""",
b'Space compr: ': b'Q*dEpWgug3ZE$irARr(h',
b'\xff': b'{{',
b'\xff'*2: b'|Nj',
b'\xff'*3: b'|Ns9',
b'\xff'*4: b'|NsC0',
}
for data, res in tests.items():
eq(base64.b85encode(data), res)
self.check_other_types(base64.b85encode, b"www.python.org",
b'cXxL#aCvlSZ*DGca%T')
def test_z85encode(self):
eq = self.assertEqual
tests = {
b'': b'',
b'\x86\x4F\xD2\x6F\xB5\x59\xF7\x5B': b'HelloWorld',
b'www.python.org': b'CxXl-AcVLsz/dgCA+t',
bytes(range(255)): b"""009c61o!#m2NH?C3>iWS5d]J*6CRx17-skh9337x"""
b"""ar.{NbQB=+c[cR@eg&FcfFLssg=mfIi5%2YjuU>)kTv.7l}6Nnnj=AD"""
b"""oIFnTp/ga?r8($2sxO*itWpVyu$0IOwmYv=xLzi%y&a6dAb/]tBAI+J"""
b"""CZjQZE0{D[FpSr8GOteoH(41EJe-<UKDCY&L:dM3N3<zjOsMmzPRn9P"""
b"""Q[%@^ShV!$TGwUeU^7HuW6^uKXvGh.YUh4]Z})[9-kP:p:JqPF+*1CV"""
b"""^9Zp<!yAd4/Xb0k*$*&A&nJXQ<MkK!>&}x#)cTlf[Bu8v].4}L}1:^-"""
b"""@qDP""",
b"""abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"""
b"""0123456789!@#0^&*();:<>,. []{}""":
b"""vpA.SwObN*x>?B1zeKohADlbxB-}$ND3R+ylQTvjm[uizoh55PpF:[^"""
b"""q=D:$s6eQefFLssg=mfIi5@cEbqrBJdKV-ciY]OSe*aw7DWL""",
b'no padding..': b'zF{UpvpS[.zF7NO',
b'zero compression\x00\x00\x00\x00': b'Ds.bnay/tbAb]JhB7]Mg00000',
b'zero compression\x00\x00\x00': b'Ds.bnay/tbAb]JhB7]Mg0000',
b"""Boundary:\x00\x00\x00\x00""": b"""lt}0:wmoI7iSGcW00""",
b'Space compr: ': b'q/DePwGUG3ze:IRarR^H',
b'\xff': b'@@',
b'\xff'*2: b'%nJ',
b'\xff'*3: b'%nS9',
b'\xff'*4: b'%nSc0',
}
for data, res in tests.items():
eq(base64.z85encode(data), res)
self.check_other_types(base64.z85encode, b"www.python.org",
b'CxXl-AcVLsz/dgCA+t')
def test_a85decode(self):
eq = self.assertEqual
tests = {
b'': b'',
b'GB\\6`E-ZP=Df.1GEb>': b'www.python.org',
b"""! ! * -'"\n\t\t9eu\r\n7# RL\vhG$k3[W&.oNg'GVB"(`=52*$$"""
b"""(B+<_pR,UFcb-n-Vr/1iJ-0JP==1c70M3&s#]4?Ykm5X@_(6q'R884cE"""
b"""H9MJ8X:f1+h<)lt#=BSg3>[:ZC?t!MSA7]@cBPD3sCi+'.E,fo>FEMbN"""
b"""G^4U^I!pHnJ:W<)KS>/9Ll%"IN/`jYOHG]iPa.Q$R$jD4S=Q7DTV8*TU"""
b"""nsrdW2ZetXKAY/Yd(L?['d?O\\@K2_]Y2%o^qmn*`5Ta:aN;TJbg"GZd"""
b"""*^:jeCE.%f\\,!5gtgiEi8N\\UjQ5OekiqBum-X60nF?)@o_%qPq"ad`"""
b"""r;HT""": bytes(range(255)),
b"""@:E_WAS,RgBkhF"D/O92EH6,BF`qtRH$VbC6UX@47n?3D92&&T:Jand;c"""
b"""Hat='/U/0JP==1c70M3&r-I,;<FN.OZ`-3]oSW/g+A(H[P""":
b'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234'
b'56789!@#0^&*();:<>,. []{}',
b'DJpY:@:Wn_DJ(RS': b'no padding..',
b'H=_,8+Cf>,E,oN2F(oQ1z': b'zero compression\x00\x00\x00\x00',
b'H=_,8+Cf>,E,oN2F(oQ1!!!!': b'zero compression\x00\x00\x00',
b'6>q!aA79M(3WK-[!!': b"Boundary:\x00\x00\x00\x00",
b';fH/TAKYK$D/aMV+<VdL': b'Space compr: ',
b'rr': b'\xff',
b's8N': b'\xff'*2,
b's8W*': b'\xff'*3,
b's8W-!': b'\xff'*4,
}
for data, res in tests.items():
eq(base64.a85decode(data), res, data)
eq(base64.a85decode(data, adobe=False), res, data)
eq(base64.a85decode(data.decode("ascii"), adobe=False), res, data)
eq(base64.a85decode(b'<~' + data + b'~>', adobe=True), res, data)
eq(base64.a85decode(data + b'~>', adobe=True), res, data)
eq(base64.a85decode('<~%s~>' % data.decode("ascii"), adobe=True),
res, data)
eq(base64.a85decode(b'yy', foldspaces=True, adobe=False), b' '*8)
eq(base64.a85decode(b'y+<Vd', foldspaces=True, adobe=False), b' '*7)
eq(base64.a85decode(b'y+<U', foldspaces=True, adobe=False), b' '*6)
eq(base64.a85decode(b'y+9', foldspaces=True, adobe=False), b' '*5)
eq(base64.a85decode(b'aaaaay', foldspaces=True), b'\xc9\x80\x0b@ ')
self.check_other_types(base64.a85decode, b'GB\\6`E-ZP=Df.1GEb>',
b"www.python.org")
def test_b85decode(self):
eq = self.assertEqual
tests = {
b'': b'',
b'cXxL#aCvlSZ*DGca%T': b'www.python.org',
b"""009C61O)~M2nh-c3=Iws5D^j+6crX17#SKH9337X"""
b"""AR!_nBqb&%C@Cr{EG;fCFflSSG&MFiI5|2yJUu=?KtV!7L`6nNNJ&ad"""
b"""OifNtP*GA-R8>}2SXo+ITwPvYU}0ioWMyV&XlZI|Y;A6DaB*^Tbai%j"""
b"""czJqze0_d@fPsR8goTEOh>41ejE#<ukdcy;l$Dm3n3<ZJoSmMZprN9p"""
b"""q@|{(sHv)}tgWuEu(7hUw6(UkxVgH!yuH4^z`?@9#Kp$P$jQpf%+1cv"""
b"""(9zP<)YaD4*xB0K+}+;a;Njxq<mKk)=;`X~?CtLF@bU8V^!4`l`1$(#"""
b"""{Qdp""": bytes(range(255)),
b"""VPa!sWoBn+X=-b1ZEkOHadLBXb#`}nd3r%YLqtVJM@UIZOH55pPf$@("""
b"""Q&d$}S6EqEFflSSG&MFiI5{CeBQRbjDkv#CIy^osE+AW7dwl""":
b"""abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"""
b"""0123456789!@#0^&*();:<>,. []{}""",
b'Zf_uPVPs@!Zf7no': b'no padding..',
b'dS!BNAY*TBaB^jHb7^mG00000': b'zero compression\x00\x00\x00\x00',
b'dS!BNAY*TBaB^jHb7^mG0000': b'zero compression\x00\x00\x00',
b"""LT`0$WMOi7IsgCw00""": b"""Boundary:\x00\x00\x00\x00""",
b'Q*dEpWgug3ZE$irARr(h': b'Space compr: ',
b'{{': b'\xff',
b'|Nj': b'\xff'*2,
b'|Ns9': b'\xff'*3,
b'|NsC0': b'\xff'*4,
}
for data, res in tests.items():
eq(base64.b85decode(data), res)
eq(base64.b85decode(data.decode("ascii")), res)
self.check_other_types(base64.b85decode, b'cXxL#aCvlSZ*DGca%T',
b"www.python.org")
def test_z85decode(self):
eq = self.assertEqual
tests = {
b'': b'',
b'CxXl-AcVLsz/dgCA+t': b'www.python.org',
b"""009c61o!#m2NH?C3>iWS5d]J*6CRx17-skh9337x"""
b"""ar.{NbQB=+c[cR@eg&FcfFLssg=mfIi5%2YjuU>)kTv.7l}6Nnnj=AD"""
b"""oIFnTp/ga?r8($2sxO*itWpVyu$0IOwmYv=xLzi%y&a6dAb/]tBAI+J"""
b"""CZjQZE0{D[FpSr8GOteoH(41EJe-<UKDCY&L:dM3N3<zjOsMmzPRn9P"""
b"""Q[%@^ShV!$TGwUeU^7HuW6^uKXvGh.YUh4]Z})[9-kP:p:JqPF+*1CV"""
b"""^9Zp<!yAd4/Xb0k*$*&A&nJXQ<MkK!>&}x#)cTlf[Bu8v].4}L}1:^-"""
b"""@qDP""": bytes(range(255)),
b"""vpA.SwObN*x>?B1zeKohADlbxB-}$ND3R+ylQTvjm[uizoh55PpF:[^"""
b"""q=D:$s6eQefFLssg=mfIi5@cEbqrBJdKV-ciY]OSe*aw7DWL""":
b"""abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"""
b"""0123456789!@#0^&*();:<>,. []{}""",
b'zF{UpvpS[.zF7NO': b'no padding..',
b'Ds.bnay/tbAb]JhB7]Mg00000': b'zero compression\x00\x00\x00\x00',
b'Ds.bnay/tbAb]JhB7]Mg0000': b'zero compression\x00\x00\x00',
b"""lt}0:wmoI7iSGcW00""": b"""Boundary:\x00\x00\x00\x00""",
b'q/DePwGUG3ze:IRarR^H': b'Space compr: ',
b'@@': b'\xff',
b'%nJ': b'\xff'*2,
b'%nS9': b'\xff'*3,
b'%nSc0': b'\xff'*4,
}
for data, res in tests.items():
eq(base64.z85decode(data), res)
eq(base64.z85decode(data.decode("ascii")), res)
self.check_other_types(base64.z85decode, b'CxXl-AcVLsz/dgCA+t',
b'www.python.org')
def test_a85_padding(self):
eq = self.assertEqual
eq(base64.a85encode(b"x", pad=True), b'GQ7^D')
eq(base64.a85encode(b"xx", pad=True), b"G^'2g")
eq(base64.a85encode(b"xxx", pad=True), b'G^+H5')
eq(base64.a85encode(b"xxxx", pad=True), b'G^+IX')
eq(base64.a85encode(b"xxxxx", pad=True), b'G^+IXGQ7^D')
eq(base64.a85decode(b'GQ7^D'), b"x\x00\x00\x00")
eq(base64.a85decode(b"G^'2g"), b"xx\x00\x00")
eq(base64.a85decode(b'G^+H5'), b"xxx\x00")
eq(base64.a85decode(b'G^+IX'), b"xxxx")
eq(base64.a85decode(b'G^+IXGQ7^D'), b"xxxxx\x00\x00\x00")
def test_b85_padding(self):
eq = self.assertEqual
eq(base64.b85encode(b"x", pad=True), b'cmMzZ')
eq(base64.b85encode(b"xx", pad=True), b'cz6H+')
eq(base64.b85encode(b"xxx", pad=True), b'czAdK')
eq(base64.b85encode(b"xxxx", pad=True), b'czAet')
eq(base64.b85encode(b"xxxxx", pad=True), b'czAetcmMzZ')
eq(base64.b85decode(b'cmMzZ'), b"x\x00\x00\x00")
eq(base64.b85decode(b'cz6H+'), b"xx\x00\x00")
eq(base64.b85decode(b'czAdK'), b"xxx\x00")
eq(base64.b85decode(b'czAet'), b"xxxx")
eq(base64.b85decode(b'czAetcmMzZ'), b"xxxxx\x00\x00\x00")
def test_z85_padding(self):
eq = self.assertEqual
eq(base64.z85encode(b"x", pad=True), b'CMmZz')
eq(base64.z85encode(b"xx", pad=True), b'CZ6h*')
eq(base64.z85encode(b"xxx", pad=True), b'CZaDk')
eq(base64.z85encode(b"xxxx", pad=True), b'CZaET')
eq(base64.z85encode(b"xxxxx", pad=True), b'CZaETCMmZz')
eq(base64.z85decode(b'CMmZz'), b"x\x00\x00\x00")
eq(base64.z85decode(b'CZ6h*'), b"xx\x00\x00")
eq(base64.z85decode(b'CZaDk'), b"xxx\x00")
eq(base64.z85decode(b'CZaET'), b"xxxx")
eq(base64.z85decode(b'CZaETCMmZz'), b"xxxxx\x00\x00\x00")
def test_a85decode_errors(self):
illegal = (set(range(32)) | set(range(118, 256))) - set(b' \t\n\r\v')
for c in illegal:
with self.assertRaises(ValueError, msg=bytes([c])):
base64.a85decode(b'!!!!' + bytes([c]))
with self.assertRaises(ValueError, msg=bytes([c])):
base64.a85decode(b'!!!!' + bytes([c]), adobe=False)
with self.assertRaises(ValueError, msg=bytes([c])):
base64.a85decode(b'<~!!!!' + bytes([c]) + b'~>', adobe=True)
self.assertRaises(ValueError, base64.a85decode,
b"malformed", adobe=True)
self.assertRaises(ValueError, base64.a85decode,
b"<~still malformed", adobe=True)
# With adobe=False (the default), Adobe framing markers are disallowed
self.assertRaises(ValueError, base64.a85decode,
b"<~~>")
self.assertRaises(ValueError, base64.a85decode,
b"<~~>", adobe=False)
base64.a85decode(b"<~~>", adobe=True) # sanity check
self.assertRaises(ValueError, base64.a85decode,
b"abcx", adobe=False)
self.assertRaises(ValueError, base64.a85decode,
b"abcdey", adobe=False)
self.assertRaises(ValueError, base64.a85decode,
b"a b\nc", adobe=False, ignorechars=b"")
self.assertRaises(ValueError, base64.a85decode, b's', adobe=False)
self.assertRaises(ValueError, base64.a85decode, b's8', adobe=False)
self.assertRaises(ValueError, base64.a85decode, b's8W', adobe=False)
self.assertRaises(ValueError, base64.a85decode, b's8W-', adobe=False)
self.assertRaises(ValueError, base64.a85decode, b's8W-"', adobe=False)
self.assertRaises(ValueError, base64.a85decode, b'aaaay',
foldspaces=True)
self.assertEqual(base64.a85decode(b"a b\nc", ignorechars=b" \n"),
b'\xc9\x89')
with self.assertRaises(ValueError):
base64.a85decode(b"a b\nc", ignorechars=b"")
with self.assertRaises(ValueError):
base64.a85decode(b"a b\nc", ignorechars=b" ")
with self.assertRaises(ValueError):
base64.a85decode(b"a b\nc", ignorechars=b"\n")
with self.assertRaises(TypeError):
base64.a85decode(b"a b\nc", ignorechars=" \n")
with self.assertRaises(TypeError):
base64.a85decode(b"a b\nc", ignorechars=None)
def test_b85decode_errors(self):
illegal = list(range(33)) + \
list(b'"\',./:[\\]') + \
list(range(128, 256))
for c in illegal:
with self.assertRaises(ValueError, msg=bytes([c])):
base64.b85decode(b'0000' + bytes([c]))
self.assertRaises(ValueError, base64.b85decode, b'|')
self.assertRaises(ValueError, base64.b85decode, b'|N')
self.assertRaises(ValueError, base64.b85decode, b'|Ns')
self.assertRaises(ValueError, base64.b85decode, b'|NsC')
self.assertRaises(ValueError, base64.b85decode, b'|NsC1')
def test_z85decode_errors(self):
illegal = list(range(33)) + \
list(b'"\',;_`|\\~') + \
list(range(128, 256))
for c in illegal:
with self.assertRaises(ValueError, msg=bytes([c])):
base64.z85decode(b'0000' + bytes([c]))
# b'\xff\xff\xff\xff' encodes to b'%nSc0', the following will overflow:
self.assertRaises(ValueError, base64.z85decode, b'%')
self.assertRaises(ValueError, base64.z85decode, b'%n')
self.assertRaises(ValueError, base64.z85decode, b'%nS')
self.assertRaises(ValueError, base64.z85decode, b'%nSc')
self.assertRaises(ValueError, base64.z85decode, b'%nSc1')
def add_padding(self, payload):
"""Add the expected padding for test_?85_encode_decode_round_trip."""
if len(payload) % 4 != 0:
padding = b"\0" * ((-len(payload)) % 4)
payload = payload + padding
return payload
@hypothesis.given(
payload=hypothesis.strategies.binary(),
foldspaces=hypothesis.strategies.booleans(),
wrapcol=(
hypothesis.strategies.just(0)
| hypothesis.strategies.integers(1, 1000)),
pad=hypothesis.strategies.booleans(),
adobe=hypothesis.strategies.booleans(),
)
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', False, 0, False, False)
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', False, 20, True, True)
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', True, 0, False, True)
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', True, 20, True, False)
def test_a85_encode_decode_round_trip(
self, payload, foldspaces, wrapcol, pad, adobe
):
encoded = base64.a85encode(
payload, foldspaces=foldspaces, wrapcol=wrapcol,
pad=pad, adobe=adobe,
)
if wrapcol:
if adobe and wrapcol == 1:
# "adobe" needs wrapcol to be at least 2.
# a85decode quietly uses 2 if 1 is given; it's not worth
# loudly deprecating this behavior.
wrapcol = 2
for line in encoded.splitlines(keepends=False):
self.assertLessEqual(len(line), wrapcol)
if adobe:
self.assertTrue(encoded.startswith(b'<~'))
self.assertTrue(encoded.endswith(b'~>'))
decoded = base64.a85decode(encoded, foldspaces=foldspaces, adobe=adobe)
if pad:
payload = self.add_padding(payload)
self.assertEqual(payload, decoded)
@hypothesis.given(
payload=hypothesis.strategies.binary(),
pad=hypothesis.strategies.booleans())
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', True)
@hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', False)
def test_b85_encode_decode_round_trip(self, payload, pad):
encoded = base64.b85encode(payload, pad=pad)
if pad:
payload = self.add_padding(payload)
decoded = base64.b85decode(encoded)
self.assertEqual(payload, decoded)
def test_decode_nonascii_str(self):
decode_funcs = (base64.b64decode,
base64.standard_b64decode,
base64.urlsafe_b64decode,
base64.b32decode,
base64.b16decode,
base64.b85decode,
base64.a85decode,
base64.z85decode)
for f in decode_funcs:
self.assertRaises(ValueError, f, 'with non-ascii \xcb')
def test_ErrorHeritage(self):
self.assertIsSubclass(binascii.Error, ValueError)
def test_RFC4648_test_cases(self):
# test cases from RFC 4648 section 10
b64encode = base64.b64encode
b32hexencode = base64.b32hexencode
b32encode = base64.b32encode
b16encode = base64.b16encode
self.assertEqual(b64encode(b""), b"")
self.assertEqual(b64encode(b"f"), b"Zg==")
self.assertEqual(b64encode(b"fo"), b"Zm8=")
self.assertEqual(b64encode(b"foo"), b"Zm9v")
self.assertEqual(b64encode(b"foob"), b"Zm9vYg==")
self.assertEqual(b64encode(b"fooba"), b"Zm9vYmE=")
self.assertEqual(b64encode(b"foobar"), b"Zm9vYmFy")
self.assertEqual(b32encode(b""), b"")
self.assertEqual(b32encode(b"f"), b"MY======")
self.assertEqual(b32encode(b"fo"), b"MZXQ====")
self.assertEqual(b32encode(b"foo"), b"MZXW6===")
self.assertEqual(b32encode(b"foob"), b"MZXW6YQ=")
self.assertEqual(b32encode(b"fooba"), b"MZXW6YTB")
self.assertEqual(b32encode(b"foobar"), b"MZXW6YTBOI======")
self.assertEqual(b32hexencode(b""), b"")
self.assertEqual(b32hexencode(b"f"), b"CO======")
self.assertEqual(b32hexencode(b"fo"), b"CPNG====")
self.assertEqual(b32hexencode(b"foo"), b"CPNMU===")
self.assertEqual(b32hexencode(b"foob"), b"CPNMUOG=")
self.assertEqual(b32hexencode(b"fooba"), b"CPNMUOJ1")
self.assertEqual(b32hexencode(b"foobar"), b"CPNMUOJ1E8======")
self.assertEqual(b16encode(b""), b"")
self.assertEqual(b16encode(b"f"), b"66")
self.assertEqual(b16encode(b"fo"), b"666F")
self.assertEqual(b16encode(b"foo"), b"666F6F")
self.assertEqual(b16encode(b"foob"), b"666F6F62")
self.assertEqual(b16encode(b"fooba"), b"666F6F6261")
self.assertEqual(b16encode(b"foobar"), b"666F6F626172")
class TestMain(unittest.TestCase):
def tearDown(self):
if os.path.exists(os_helper.TESTFN):
os.unlink(os_helper.TESTFN)
def get_output(self, *args):
return script_helper.assert_python_ok('-m', 'base64', *args).out
def test_encode_file(self):
with open(os_helper.TESTFN, 'wb') as fp:
fp.write(b'a\xffb\n')
output = self.get_output('-e', os_helper.TESTFN)
self.assertEqual(output.rstrip(), b'Yf9iCg==')
def test_encode_from_stdin(self):
with script_helper.spawn_python('-m', 'base64', '-e') as proc:
out, err = proc.communicate(b'a\xffb\n')
self.assertEqual(out.rstrip(), b'Yf9iCg==')
self.assertIsNone(err)
def test_decode(self):
with open(os_helper.TESTFN, 'wb') as fp:
fp.write(b'Yf9iCg==')
output = self.get_output('-d', os_helper.TESTFN)
self.assertEqual(output.rstrip(), b'a\xffb')
def test_prints_usage_with_help_flag(self):
output = self.get_output('-h')
self.assertIn(b'usage: ', output)
self.assertIn(b'-d, -u: decode', output)
def test_prints_usage_with_invalid_flag(self):
output = script_helper.assert_python_failure('-m', 'base64', '-x').err
self.assertIn(b'usage: ', output)
self.assertIn(b'-d, -u: decode', output)
if __name__ == '__main__':
unittest.main()