https://github.com/python/cpython/commit/4507d496b4a89956c67db54e45e1629c9708dda5
commit: 4507d496b4a89956c67db54e45e1629c9708dda5
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-03-20T13:07:00+02:00
summary:

gh-145980: Add support for alternative alphabets in the binascii module 
(GH-145981)

* Add the alphabet parameter in functions b2a_base64(), a2b_base64(),
  b2a_base85(), and a2b_base85().
* And a number of "*_ALPHABET" constants.
* Remove b2a_z85() and a2b_z85().

files:
A Misc/NEWS.d/next/Library/2026-03-15-16-38-48.gh-issue-145980.mRze5H.rst
M Doc/library/binascii.rst
M Doc/whatsnew/3.15.rst
M Include/internal/pycore_global_objects_fini_generated.h
M Include/internal/pycore_global_strings.h
M Include/internal/pycore_runtime_init_generated.h
M Include/internal/pycore_unicodeobject_generated.h
M Lib/base64.py
M Lib/test/test_binascii.py
M Modules/binascii.c
M Modules/clinic/binascii.c.h

diff --git a/Doc/library/binascii.rst b/Doc/library/binascii.rst
index 8a241e51ebfee6..70ba036756ff32 100644
--- a/Doc/library/binascii.rst
+++ b/Doc/library/binascii.rst
@@ -48,12 +48,15 @@ The :mod:`!binascii` module defines the following functions:
       Added the *backtick* parameter.
 
 
-.. function:: a2b_base64(string, /, *, strict_mode=False)
-              a2b_base64(string, /, *, strict_mode=True, ignorechars)
+.. function:: a2b_base64(string, /, *, alphabet=BASE64_ALPHABET, 
strict_mode=False)
+              a2b_base64(string, /, *, ignorechars, alphabet=BASE64_ALPHABET, 
strict_mode=True)
 
    Convert a block of base64 data back to binary and return the binary data. 
More
    than one line may be passed at a time.
 
+   Optional *alphabet* must be a :class:`bytes` object of length 64 which
+   specifies an alternative alphabet.
+
    If *ignorechars* is specified, it should be a :term:`bytes-like object`
    containing characters to ignore from the input when *strict_mode* is true.
    If *ignorechars* contains the pad character ``'='``,  the pad characters
@@ -76,10 +79,10 @@ The :mod:`!binascii` module defines the following functions:
       Added the *strict_mode* parameter.
 
    .. versionchanged:: 3.15
-      Added the *ignorechars* parameter.
+      Added the *alphabet* and *ignorechars* parameters.
 
 
-.. function:: b2a_base64(data, *, wrapcol=0, newline=True)
+.. function:: b2a_base64(data, *, alphabet=BASE64_ALPHABET, wrapcol=0, 
newline=True)
 
    Convert binary data to a line(s) of ASCII characters in base64 coding,
    as specified in :rfc:`4648`.
@@ -95,7 +98,7 @@ The :mod:`!binascii` module defines the following functions:
       Added the *newline* parameter.
 
    .. versionchanged:: 3.15
-      Added the *wrapcol* parameter.
+      Added the *alphabet* and *wrapcol* parameters.
 
 
 .. function:: a2b_ascii85(string, /, *, foldspaces=False, adobe=False, 
ignorechars=b"")
@@ -148,7 +151,7 @@ The :mod:`!binascii` module defines the following functions:
    .. versionadded:: 3.15
 
 
-.. function:: a2b_base85(string, /)
+.. function:: a2b_base85(string, /, *, alphabet=BASE85_ALPHABET)
 
    Convert Base85 data back to binary and return the binary data.
    More than one line may be passed at a time.
@@ -158,49 +161,25 @@ The :mod:`!binascii` module defines the following 
functions:
    characters). Each group encodes 32 bits of binary data in the range from
    ``0`` to ``2 ** 32 - 1``, inclusive.
 
+   Optional *alphabet* must be a :class:`bytes` object of length 85 which
+   specifies an alternative alphabet.
+
    Invalid Base85 data will raise :exc:`binascii.Error`.
 
    .. versionadded:: 3.15
 
 
-.. function:: b2a_base85(data, /, *, pad=False)
+.. function:: b2a_base85(data, /, *, alphabet=BASE85_ALPHABET, pad=False)
 
    Convert binary data to a line of ASCII characters in Base85 coding.
    The return value is the converted line.
 
-   If *pad* is true, the input is padded with ``b'\0'`` so its length is a
-   multiple of 4 bytes before encoding.
-
-   .. versionadded:: 3.15
-
-
-.. function:: a2b_z85(string, /)
-
-   Convert Z85 data back to binary and return the binary data.
-   More than one line may be passed at a time.
-
-   Valid Z85 data contains characters from the Z85 alphabet in groups
-   of five (except for the final group, which may have from two to five
-   characters). Each group encodes 32 bits of binary data in the range from
-   ``0`` to ``2 ** 32 - 1``, inclusive.
-
-   See `Z85 specification <https://rfc.zeromq.org/spec/32/>`_ for more 
information.
-
-   Invalid Z85 data will raise :exc:`binascii.Error`.
-
-   .. versionadded:: 3.15
-
-
-.. function:: b2a_z85(data, /, *, pad=False)
-
-   Convert binary data to a line of ASCII characters in Z85 coding.
-   The return value is the converted line.
+   Optional *alphabet* must be a :term:`bytes-like object` of length 85 which
+   specifies an alternative alphabet.
 
    If *pad* is true, the input is padded with ``b'\0'`` so its length is a
    multiple of 4 bytes before encoding.
 
-   See `Z85 specification <https://rfc.zeromq.org/spec/32/>`_ for more 
information.
-
    .. versionadded:: 3.15
 
 
@@ -300,6 +279,55 @@ The :mod:`!binascii` module defines the following 
functions:
    but may be handled by reading a little more data and trying again.
 
 
+.. data:: BASE64_ALPHABET
+
+   The Base 64 alphabet according to :rfc:`4648`.
+
+   .. versionadded:: next
+
+.. data:: URLSAFE_BASE64_ALPHABET
+
+   The "URL and filename safe" Base 64 alphabet according to :rfc:`4648`.
+
+   .. versionadded:: next
+
+.. data:: UU_ALPHABET
+
+   The uuencoding alphabet.
+
+   .. versionadded:: next
+
+.. data:: CRYPT_ALPHABET
+
+   The Base 64 alphabet used in the :manpage:`crypt(3)` routine and in the 
GEDCOM format.
+
+   .. versionadded:: next
+
+.. data:: BINHEX_ALPHABET
+
+   The Base 64 alphabet used in BinHex 4 (HQX) within the classic Mac OS.
+
+   .. versionadded:: next
+
+.. data:: BASE85_ALPHABET
+
+   The Base85 alphabet.
+
+   .. versionadded:: next
+
+.. data:: ASCII85_ALPHABET
+
+   The Ascii85 alphabet.
+
+   .. versionadded:: next
+
+.. data:: Z85_ALPHABET
+
+   The `Z85 <https://rfc.zeromq.org/spec/32/>`_ alphabet.
+
+   .. versionadded:: next
+
+
 .. seealso::
 
    Module :mod:`base64`
diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index 69283f67487b18..5e6265a45231db 100644
--- a/Doc/whatsnew/3.15.rst
+++ b/Doc/whatsnew/3.15.rst
@@ -649,13 +649,16 @@ binascii
 
   - :func:`~binascii.b2a_ascii85` and :func:`~binascii.a2b_ascii85`
   - :func:`~binascii.b2a_base85` and :func:`~binascii.a2b_base85`
-  - :func:`~binascii.b2a_z85` and :func:`~binascii.a2b_z85`
 
   (Contributed by James Seo and Serhiy Storchaka in :gh:`101178`.)
 
 * Added the *wrapcol* parameter in :func:`~binascii.b2a_base64`.
   (Contributed by Serhiy Storchaka in :gh:`143214`.)
 
+* Added the *alphabet* parameter in :func:`~binascii.b2a_base64` and
+  :func:`~binascii.a2b_base64`.
+  (Contributed by Serhiy Storchaka in :gh:`145980`.)
+
 * Added the *ignorechars* parameter in :func:`~binascii.a2b_base64`.
   (Contributed by Serhiy Storchaka in :gh:`144001`.)
 
diff --git a/Include/internal/pycore_global_objects_fini_generated.h 
b/Include/internal/pycore_global_objects_fini_generated.h
index 64e3438f9157fe..4b1e289c6ff468 100644
--- a/Include/internal/pycore_global_objects_fini_generated.h
+++ b/Include/internal/pycore_global_objects_fini_generated.h
@@ -1584,6 +1584,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(all));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(all_threads));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(allow_code));
+    _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(alphabet));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(any));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(append));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(arg));
diff --git a/Include/internal/pycore_global_strings.h 
b/Include/internal/pycore_global_strings.h
index 78ed30dd7f62a2..6ee649b59a5c37 100644
--- a/Include/internal/pycore_global_strings.h
+++ b/Include/internal/pycore_global_strings.h
@@ -307,6 +307,7 @@ struct _Py_global_strings {
         STRUCT_FOR_ID(all)
         STRUCT_FOR_ID(all_threads)
         STRUCT_FOR_ID(allow_code)
+        STRUCT_FOR_ID(alphabet)
         STRUCT_FOR_ID(any)
         STRUCT_FOR_ID(append)
         STRUCT_FOR_ID(arg)
diff --git a/Include/internal/pycore_runtime_init_generated.h 
b/Include/internal/pycore_runtime_init_generated.h
index d4b7b090f93f31..778db946c2a3aa 100644
--- a/Include/internal/pycore_runtime_init_generated.h
+++ b/Include/internal/pycore_runtime_init_generated.h
@@ -1582,6 +1582,7 @@ extern "C" {
     INIT_ID(all), \
     INIT_ID(all_threads), \
     INIT_ID(allow_code), \
+    INIT_ID(alphabet), \
     INIT_ID(any), \
     INIT_ID(append), \
     INIT_ID(arg), \
diff --git a/Include/internal/pycore_unicodeobject_generated.h 
b/Include/internal/pycore_unicodeobject_generated.h
index d843674f180902..bd8f50ff0ee732 100644
--- a/Include/internal/pycore_unicodeobject_generated.h
+++ b/Include/internal/pycore_unicodeobject_generated.h
@@ -1008,6 +1008,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) 
{
     _PyUnicode_InternStatic(interp, &string);
     assert(_PyUnicode_CheckConsistency(string, 1));
     assert(PyUnicode_GET_LENGTH(string) != 1);
+    string = &_Py_ID(alphabet);
+    _PyUnicode_InternStatic(interp, &string);
+    assert(_PyUnicode_CheckConsistency(string, 1));
+    assert(PyUnicode_GET_LENGTH(string) != 1);
     string = &_Py_ID(any);
     _PyUnicode_InternStatic(interp, &string);
     assert(_PyUnicode_CheckConsistency(string, 1));
diff --git a/Lib/base64.py b/Lib/base64.py
index dcfcbcc95a39be..a429760da79f2a 100644
--- a/Lib/base64.py
+++ b/Lib/base64.py
@@ -56,11 +56,13 @@ def b64encode(s, altchars=None, *, wrapcol=0):
     If wrapcol is non-zero, insert a newline (b'\\n') character after at most
     every wrapcol characters.
     """
-    encoded = binascii.b2a_base64(s, wrapcol=wrapcol, newline=False)
     if altchars is not None:
-        assert len(altchars) == 2, repr(altchars)
-        return encoded.translate(bytes.maketrans(b'+/', altchars))
-    return encoded
+        if len(altchars) != 2:
+            raise ValueError(f'invalid altchars: {altchars!r}')
+        alphabet = binascii.BASE64_ALPHABET[:-2] + altchars
+        return binascii.b2a_base64(s, wrapcol=wrapcol, newline=False,
+                                   alphabet=alphabet)
+    return binascii.b2a_base64(s, wrapcol=wrapcol, newline=False)
 
 
 def b64decode(s, altchars=None, validate=_NOT_SPECIFIED, *, 
ignorechars=_NOT_SPECIFIED):
@@ -100,15 +102,10 @@ def b64decode(s, altchars=None, validate=_NOT_SPECIFIED, 
*, ignorechars=_NOT_SPE
                     break
             s = s.translate(bytes.maketrans(altchars, b'+/'))
         else:
-            trans_in = set(b'+/') - set(altchars)
-            if len(trans_in) == 2:
-                # we can't use the reqult of unordered sets here
-                trans = bytes.maketrans(altchars + b'+/', b'+/' + altchars)
-            else:
-                trans = bytes.maketrans(altchars + bytes(trans_in),
-                                        b'+/' + bytes(set(altchars) - 
set(b'+/')))
-            s = s.translate(trans)
-            ignorechars = ignorechars.translate(trans)
+            alphabet = binascii.BASE64_ALPHABET[:-2] + altchars
+            return binascii.a2b_base64(s, strict_mode=validate,
+                                       alphabet=alphabet,
+                                       ignorechars=ignorechars)
     if ignorechars is _NOT_SPECIFIED:
         ignorechars = b''
     result = binascii.a2b_base64(s, strict_mode=validate,
@@ -146,7 +143,6 @@ def standard_b64decode(s):
     return b64decode(s)
 
 
-_urlsafe_encode_translation = bytes.maketrans(b'+/', b'-_')
 _urlsafe_decode_translation = bytes.maketrans(b'-_', b'+/')
 
 def urlsafe_b64encode(s):
@@ -156,7 +152,8 @@ def urlsafe_b64encode(s):
     bytes object.  The alphabet uses '-' instead of '+' and '_' instead of
     '/'.
     """
-    return b64encode(s).translate(_urlsafe_encode_translation)
+    return binascii.b2a_base64(s, newline=False,
+                               alphabet=binascii.URLSAFE_BASE64_ALPHABET)
 
 def urlsafe_b64decode(s):
     """Decode bytes using the URL- and filesystem-safe Base64 alphabet.
@@ -399,14 +396,14 @@ def b85decode(b):
 
 def z85encode(s, pad=False):
     """Encode bytes-like object b in z85 format and return a bytes object."""
-    return binascii.b2a_z85(s, pad=pad)
+    return binascii.b2a_base85(s, pad=pad, alphabet=binascii.Z85_ALPHABET)
 
 def z85decode(s):
     """Decode the z85-encoded bytes-like object or ASCII string b
 
     The result is returned as a bytes object.
     """
-    return binascii.a2b_z85(s)
+    return binascii.a2b_base85(s, alphabet=binascii.Z85_ALPHABET)
 
 # Legacy interface.  This code could be cleaned up since I don't believe
 # binascii has any line length limitations.  It just doesn't seem worth it
diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py
index 9f0d15fe538810..667ec9b5241aa9 100644
--- a/Lib/test/test_binascii.py
+++ b/Lib/test/test_binascii.py
@@ -10,10 +10,10 @@
 
 
 # Note: "*_hex" functions are aliases for "(un)hexlify"
-b2a_functions = ['b2a_ascii85', 'b2a_base64', 'b2a_base85', 'b2a_z85',
+b2a_functions = ['b2a_ascii85', 'b2a_base64', 'b2a_base85',
                  'b2a_hex', 'b2a_qp', 'b2a_uu',
                  'hexlify']
-a2b_functions = ['a2b_ascii85', 'a2b_base64', 'a2b_base85', 'a2b_z85',
+a2b_functions = ['a2b_ascii85', 'a2b_base64', 'a2b_base85',
                  'a2b_hex', 'a2b_qp', 'a2b_uu',
                  'unhexlify']
 all_functions = a2b_functions + b2a_functions + ['crc32', 'crc_hqx']
@@ -46,6 +46,35 @@ def test_exceptions(self):
         self.assertIsSubclass(binascii.Error, Exception)
         self.assertIsSubclass(binascii.Incomplete, Exception)
 
+    def test_constants(self):
+        self.assertEqual(binascii.BASE64_ALPHABET,
+                         b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+                         b'abcdefghijklmnopqrstuvwxyz'
+                         b'0123456789+/')
+        self.assertEqual(binascii.URLSAFE_BASE64_ALPHABET,
+                         binascii.BASE64_ALPHABET[:-2] + b'-_')
+        self.assertEqual(binascii.UU_ALPHABET, bytes(range(32, 32+64)))
+        self.assertEqual(binascii.CRYPT_ALPHABET,
+                         b'./0123456789'
+                         b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+                         b'abcdefghijklmnopqrstuvwxyz')
+        self.assertEqual(binascii.BINHEX_ALPHABET,
+                         b'!"#$%&\'()*+,-012345689'
+                         b'@ABCDEFGHIJKLMNPQRSTUVXYZ['
+                         b'`abcdefhijklmpqr')
+
+        self.assertEqual(binascii.BASE85_ALPHABET,
+                         b'0123456789'
+                         b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+                         b'abcdefghijklmnopqrstuvwxyz'
+                         b'!#$%&()*+-;<=>?@^_`{|}~')
+        self.assertEqual(binascii.ASCII85_ALPHABET, bytes(range(33, 33+85)))
+        self.assertEqual(binascii.Z85_ALPHABET,
+                         b'0123456789'
+                         b'abcdefghijklmnopqrstuvwxyz'
+                         b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+                         b'.-:+=^!/*?&<>()[]{}@%$#')
+
     def test_functions(self):
         # Check presence of all functions
         for name in all_functions:
@@ -302,6 +331,33 @@ def assertInvalidLength(data, strict_mode=True):
         assertInvalidLength(b'A\tB\nC ??DE', # only 5 valid characters
                             strict_mode=False)
 
+    def test_base64_alphabet(self):
+        alphabet = (b'!"#$%&\'()*+,-012345689@'
+                    b'ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr')
+        data = self.type2test(self.rawdata)
+        encoded = binascii.b2a_base64(data, alphabet=alphabet)
+        trans = bytes.maketrans(binascii.BASE64_ALPHABET, alphabet)
+        expected = binascii.b2a_base64(data).translate(trans)
+        self.assertEqual(encoded, expected)
+        self.assertEqual(binascii.a2b_base64(encoded, alphabet=alphabet), 
self.rawdata)
+        self.assertEqual(binascii.b2a_base64(data, 
alphabet=self.type2test(alphabet)), expected)
+
+        data = self.type2test(b'')
+        self.assertEqual(binascii.b2a_base64(data, alphabet=alphabet), b'\n')
+        self.assertEqual(binascii.a2b_base64(data, alphabet=alphabet), b'')
+
+        for func in binascii.b2a_base64, binascii.a2b_base64:
+            with self.assertRaises(TypeError):
+                func(data, alphabet=None)
+            with self.assertRaises(TypeError):
+                func(data, alphabet=alphabet.decode())
+            with self.assertRaises(ValueError):
+                func(data, alphabet=alphabet[:-1])
+            with self.assertRaises(ValueError):
+                func(data, alphabet=alphabet+b'?')
+        with self.assertRaises(TypeError):
+            binascii.a2b_base64(data, alphabet=bytearray(alphabet))
+
     def test_ascii85_valid(self):
         # Test Ascii85 with valid data
         ASCII85_PREFIX = b"<~"
@@ -587,73 +643,32 @@ def test_base85_pad(self):
             b_pad_expected = b + b"\0" * padding
             self.assertEqual(b_pad, b_pad_expected)
 
-    def test_z85_valid(self):
-        # Test Z85 with valid data
-        lines, i = [], 0
-        for k in range(1, len(self.rawdata) + 1):
-            b = self.type2test(self.rawdata[i:i + k])
-            a = binascii.b2a_z85(b)
-            lines.append(a)
-            i += k
-            if i >= len(self.rawdata):
-                break
-        res = bytes()
-        for line in lines:
-            a = self.type2test(line)
-            b = binascii.a2b_z85(a)
-            res += b
-        self.assertEqual(res, self.rawdata)
-
-        # Test decoding inputs with different length
-        self.assertEqual(binascii.a2b_z85(self.type2test(b'')), b'')
-        self.assertEqual(binascii.a2b_z85(self.type2test(b'a')), b'')
-        self.assertEqual(binascii.a2b_z85(self.type2test(b'ab')), b'\x1f')
-        self.assertEqual(binascii.a2b_z85(self.type2test(b'abc')),
-                         b'\x1f\x85')
-        self.assertEqual(binascii.a2b_z85(self.type2test(b'abcd')),
-                         b'\x1f\x85\x9a')
-        self.assertEqual(binascii.a2b_z85(self.type2test(b'abcde')),
-                         b'\x1f\x85\x9a$')
-        self.assertEqual(binascii.a2b_z85(self.type2test(b'abcdef')),
-                         b'\x1f\x85\x9a$')
-        self.assertEqual(binascii.a2b_z85(self.type2test(b'abcdefg')),
-                         b'\x1f\x85\x9a$/')
-
-    def test_z85_errors(self):
-        def _assertRegexTemplate(assert_regex, data, **kwargs):
-            with self.assertRaisesRegex(binascii.Error, assert_regex):
-                binascii.a2b_z85(self.type2test(data), **kwargs)
-
-        def assertNonZ85Data(data):
-            _assertRegexTemplate(r"(?i)bad z85 character", data)
-
-        def assertOverflow(data):
-            _assertRegexTemplate(r"(?i)z85 overflow", data)
-
-        assertNonZ85Data(b"\xda")
-        assertNonZ85Data(b"00\0\0")
-        assertNonZ85Data(b"z !/")
-        assertNonZ85Data(b"By/JnB0hYQ\n")
-
-        # Test Z85 with out-of-range encoded value
-        assertOverflow(b"%")
-        assertOverflow(b"%n")
-        assertOverflow(b"%nS")
-        assertOverflow(b"%nSc")
-        assertOverflow(b"%nSc1")
-        assertOverflow(b"%nSc0$")
-        assertOverflow(b"%nSc0%nSc0%nSD0")
-
-    def test_z85_pad(self):
-        # Test Z85 with encode padding
-        rawdata = b"n1n3Tee\n ch@rAc\te\r$"
-        for i in range(1, len(rawdata) + 1):
-            padding = -i % 4
-            b = rawdata[:i]
-            a_pad = binascii.b2a_z85(self.type2test(b), pad=True)
-            b_pad = binascii.a2b_z85(self.type2test(a_pad))
-            b_pad_expected = b + b"\0" * padding
-            self.assertEqual(b_pad, b_pad_expected)
+    def test_base85_alphabet(self):
+        alphabet = (b'0123456789abcdefghijklmnopqrstuvwxyz'
+                    b'ABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#')
+        data = self.type2test(self.rawdata)
+        encoded = binascii.b2a_base85(data, alphabet=alphabet)
+        trans = bytes.maketrans(binascii.BASE85_ALPHABET, alphabet)
+        expected = binascii.b2a_base85(data).translate(trans)
+        self.assertEqual(encoded, expected)
+        self.assertEqual(binascii.a2b_base85(encoded, alphabet=alphabet), 
self.rawdata)
+        self.assertEqual(binascii.b2a_base85(data, 
alphabet=self.type2test(alphabet)), expected)
+
+        data = self.type2test(b'')
+        self.assertEqual(binascii.b2a_base85(data, alphabet=alphabet), b'')
+        self.assertEqual(binascii.a2b_base85(data, alphabet=alphabet), b'')
+
+        for func in binascii.b2a_base85, binascii.a2b_base85:
+            with self.assertRaises(TypeError):
+                func(data, alphabet=None)
+            with self.assertRaises(TypeError):
+                func(data, alphabet=alphabet.decode())
+            with self.assertRaises(ValueError):
+                func(data, alphabet=alphabet[:-1])
+            with self.assertRaises(ValueError):
+                func(data, alphabet=alphabet+b'?')
+        with self.assertRaises(TypeError):
+            binascii.a2b_base64(data, alphabet=bytearray(alphabet))
 
     def test_uu(self):
         MAX_UU = 45
diff --git 
a/Misc/NEWS.d/next/Library/2026-03-15-16-38-48.gh-issue-145980.mRze5H.rst 
b/Misc/NEWS.d/next/Library/2026-03-15-16-38-48.gh-issue-145980.mRze5H.rst
new file mode 100644
index 00000000000000..e6984619f83b06
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-03-15-16-38-48.gh-issue-145980.mRze5H.rst
@@ -0,0 +1,5 @@
+Added the *alphabet* parameter in :func:`~binascii.b2a_base64`,
+:func:`~binascii.a2b_base64`, :func:`~binascii.b2a_base85` and
+:func:`~binascii.a2b_base85` and a number of ``*_ALPHABET`` constants in the
+:mod:`binascii` module. Removed :func:`!b2a_z85` and
+:func:`!a2b_z85`.
diff --git a/Modules/binascii.c b/Modules/binascii.c
index 2d1d0445ef958a..f85f32b32e962c 100644
--- a/Modules/binascii.c
+++ b/Modules/binascii.c
@@ -67,6 +67,7 @@
 typedef struct binascii_state {
     PyObject *Error;
     PyObject *Incomplete;
+    PyObject *reverse_table_cache;
 } binascii_state;
 
 static inline binascii_state *
@@ -228,26 +229,6 @@ static const unsigned char table_a2b_base85_a85[] 
Py_ALIGNED(64) = {
     -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
 };
 
-static const unsigned char table_a2b_base85_z85[] Py_ALIGNED(64) = {
-    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-    -1,68,-1,84, 83,82,72,-1, 75,76,70,65, -1,63,62,69,
-     0, 1, 2, 3,  4, 5, 6, 7,  8, 9,64,-1, 73,66,74,71,
-    81,36,37,38, 39,40,41,42, 43,44,45,46, 47,48,49,50,
-    51,52,53,54, 55,56,57,58, 59,60,61,77, -1,78,67,-1,
-    -1,10,11,12, 13,14,15,16, 17,18,19,20, 21,22,23,24,
-    25,26,27,28, 29,30,31,32, 33,34,35,79, -1,80,-1,-1,
-
-    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-};
-
 static const unsigned char table_b2a_base85[] Py_ALIGNED(64) =
     "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
     "abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
@@ -256,9 +237,6 @@ static const unsigned char table_b2a_base85_a85[] 
Py_ALIGNED(64) =
     "!\"#$%&\'()*+,-./0123456789:;<=>?@" \
     "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu";
 
-static const unsigned char table_b2a_base85_z85[] Py_ALIGNED(64) =
-    "0123456789abcdefghijklmnopqrstuvwxyz" \
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/\x2a?&<>()[]{}@%$#"; /* clinic doesn't 
like '/' followed by '*' */
 
 #define BASE85_A85_PREFIX '<'
 #define BASE85_A85_AFFIX '~'
@@ -547,6 +525,52 @@ binascii_b2a_uu_impl(PyObject *module, Py_buffer *data, 
int backtick)
     return PyBytesWriter_FinishWithPointer(writer, ascii_data);
 }
 
+static PyObject *
+get_reverse_table(binascii_state *state, PyObject *alphabet, int size, int 
padchar)
+{
+    PyObject *reverse_table;
+    if (state == NULL) {
+        return NULL;
+    }
+    if (PyBytes_GET_SIZE(alphabet) != size) {
+        PyErr_Format(PyExc_ValueError, "alphabet must have length %d", size);
+        return NULL;
+    }
+    if (PyDict_GetItemRef(state->reverse_table_cache, alphabet, 
&reverse_table) < 0) {
+        return NULL;
+    }
+    if (reverse_table == NULL) {
+        unsigned char out[256];
+        memset(out, (unsigned char)-1, 256);
+        const unsigned char *in = (const unsigned char 
*)PyBytes_AS_STRING(alphabet);
+        for (int i = 0; i < size; i++) {
+            out[in[i]] = i;
+        }
+        if (padchar >= 0) {
+            assert(padchar < 256);
+            out[padchar] = size;
+        }
+        reverse_table = PyBytes_FromStringAndSize((char *)out, 256);
+        if (reverse_table == NULL) {
+            return NULL;
+        }
+        if (PyDict_SetItem(state->reverse_table_cache, alphabet, 
reverse_table) < 0) {
+            Py_DECREF(reverse_table);
+            return NULL;
+        }
+    }
+    else {
+        if (!PyBytes_Check(reverse_table)
+            || PyBytes_GET_SIZE(reverse_table) != 256)
+        {
+            PyErr_SetString(PyExc_RuntimeError, "Broken binascii cache");
+            Py_DECREF(reverse_table);
+            return NULL;
+        }
+    }
+    return reverse_table;
+}
+
 typedef unsigned char ignorecache_t[32];
 
 static int
@@ -576,6 +600,7 @@ binascii.a2b_base64
         When set to true, bytes that are not part of the base64 standard are
         not allowed.  The same applies to excess data after padding (= / ==).
         Set to True by default if ignorechars is specified, False otherwise.
+    alphabet: PyBytesObject(c_default="NULL") = BASE64_ALPHABET
     ignorechars: Py_buffer = NULL
         A byte string containing characters to ignore from the input when
         strict_mode is true.
@@ -585,14 +610,16 @@ Decode a line of base64 data.
 
 static PyObject *
 binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode,
-                         Py_buffer *ignorechars)
-/*[clinic end generated code: output=eab37aea4cfa6daa input=d2d238abf13822ed]*/
+                         PyBytesObject *alphabet, Py_buffer *ignorechars)
+/*[clinic end generated code: output=72f15fcc0681d666 input=195c8d60b03aaa6f]*/
 {
     assert(data->len >= 0);
 
     const unsigned char *ascii_data = data->buf;
     size_t ascii_len = data->len;
     binascii_state *state = NULL;
+    PyObject *table_obj = NULL;
+    const unsigned char *table_a2b = table_a2b_base64;
 
     if (strict_mode == -1) {
         strict_mode = (ignorechars->buf != NULL);
@@ -605,10 +632,20 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer 
*data, int strict_mode,
         memset(ignorecache, 0, sizeof(ignorecache));
     }
 
+    if (alphabet != NULL) {
+        state = get_binascii_state(module);
+        table_obj = get_reverse_table(state, (PyObject *)alphabet, 64, 
BASE64_PAD);
+        if (table_obj == NULL) {
+            return NULL;
+        }
+        table_a2b = (const unsigned char *)PyBytes_AS_STRING(table_obj);
+    }
+
     /* Allocate the buffer */
     Py_ssize_t bin_len = ((ascii_len+3)/4)*3; /* Upper bound, corrected later 
*/
     PyBytesWriter *writer = PyBytesWriter_Create(bin_len);
     if (writer == NULL) {
+        Py_XDECREF(table_obj);
         return NULL;
     }
     unsigned char *bin_data = PyBytesWriter_GetData(writer);
@@ -620,7 +657,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, 
int strict_mode,
      */
     if (ascii_len >= 4) {
         Py_ssize_t fast_chars = base64_decode_fast(ascii_data, 
(Py_ssize_t)ascii_len,
-                                                   bin_data, table_a2b_base64);
+                                                   bin_data, table_a2b);
         if (fast_chars > 0) {
             ascii_data += fast_chars;
             ascii_len -= fast_chars;
@@ -672,7 +709,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, 
int strict_mode,
             }
         }
 
-        unsigned char v = table_a2b_base64[this_ch];
+        unsigned char v = table_a2b[this_ch];
         if (v >= 64) {
             if (strict_mode && !ignorechar(this_ch, ignorechars, ignorecache)) 
{
                 state = get_binascii_state(module);
@@ -749,9 +786,11 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer 
*data, int strict_mode,
     }
 
 done:
+    Py_XDECREF(table_obj);
     return PyBytesWriter_FinishWithPointer(writer, bin_data);
 
 error_end:
+    Py_XDECREF(table_obj);
     PyBytesWriter_Discard(writer);
     return NULL;
 }
@@ -765,19 +804,28 @@ binascii.b2a_base64
     *
     wrapcol: size_t = 0
     newline: bool = True
+    alphabet: Py_buffer(c_default="{NULL, NULL}") = BASE64_ALPHABET
 
 Base64-code line of data.
 [clinic start generated code]*/
 
 static PyObject *
 binascii_b2a_base64_impl(PyObject *module, Py_buffer *data, size_t wrapcol,
-                         int newline)
-/*[clinic end generated code: output=2edc7311a9515eac input=2ee4214e6d489e2e]*/
+                         int newline, Py_buffer *alphabet)
+/*[clinic end generated code: output=9d9657e5fbe28c64 input=ffa3af8520c312ac]*/
 {
+    const unsigned char *table_b2a = table_b2a_base64;
     const unsigned char *bin_data = data->buf;
     Py_ssize_t bin_len = data->len;
     assert(bin_len >= 0);
 
+    if (alphabet->buf != NULL) {
+        if (alphabet->len != 64) {
+            PyErr_SetString(PyExc_ValueError, "alphabet must have length 64");
+            return NULL;
+        }
+        table_b2a = alphabet->buf;
+    }
     /* Each group of 3 bytes (rounded up) gets encoded as 4 characters,
      * not counting newlines.
      * Note that 'b' gets encoded as 'Yg==' (1 in, 4 out).
@@ -809,7 +857,7 @@ binascii_b2a_base64_impl(PyObject *module, Py_buffer *data, 
size_t wrapcol,
 
     /* Use the optimized fast path for complete 3-byte groups */
     Py_ssize_t fast_bytes = base64_encode_fast(bin_data, bin_len, ascii_data,
-                                               table_b2a_base64);
+                                               table_b2a);
     bin_data += fast_bytes;
     ascii_data += (fast_bytes / 3) * 4;
     bin_len -= fast_bytes;
@@ -818,17 +866,17 @@ binascii_b2a_base64_impl(PyObject *module, Py_buffer 
*data, size_t wrapcol,
     if (bin_len == 1) {
         /* 1 byte remaining: produces 2 base64 chars + 2 padding */
         unsigned int val = bin_data[0];
-        *ascii_data++ = table_b2a_base64[(val >> 2) & 0x3f];
-        *ascii_data++ = table_b2a_base64[(val << 4) & 0x3f];
+        *ascii_data++ = table_b2a[(val >> 2) & 0x3f];
+        *ascii_data++ = table_b2a[(val << 4) & 0x3f];
         *ascii_data++ = BASE64_PAD;
         *ascii_data++ = BASE64_PAD;
     }
     else if (bin_len == 2) {
         /* 2 bytes remaining: produces 3 base64 chars + 1 padding */
         unsigned int val = ((unsigned int)bin_data[0] << 8) | bin_data[1];
-        *ascii_data++ = table_b2a_base64[(val >> 10) & 0x3f];
-        *ascii_data++ = table_b2a_base64[(val >> 4) & 0x3f];
-        *ascii_data++ = table_b2a_base64[(val << 2) & 0x3f];
+        *ascii_data++ = table_b2a[(val >> 10) & 0x3f];
+        *ascii_data++ = table_b2a[(val >> 4) & 0x3f];
+        *ascii_data++ = table_b2a[(val << 2) & 0x3f];
         *ascii_data++ = BASE64_PAD;
     }
 
@@ -1123,13 +1171,36 @@ binascii_b2a_ascii85_impl(PyObject *module, Py_buffer 
*data, int foldspaces,
     return PyBytesWriter_FinishWithPointer(writer, ascii_data);
 }
 
+/*[clinic input]
+binascii.a2b_base85
+
+    data: ascii_buffer
+    /
+    *
+    alphabet: PyBytesObject(c_default="NULL") = BASE85_ALPHABET
+
+Decode a line of Base85 data.
+[clinic start generated code]*/
+
 static PyObject *
-base85_decode_impl(PyObject *module, Py_buffer *data,
-                   const unsigned char table_a2b[], const char *name)
+binascii_a2b_base85_impl(PyObject *module, Py_buffer *data,
+                         PyBytesObject *alphabet)
+/*[clinic end generated code: output=3e114af53812e8ff input=0b6b83b38ad4497c]*/
 {
     const unsigned char *ascii_data = data->buf;
     Py_ssize_t ascii_len = data->len;
     binascii_state *state = NULL;
+    PyObject *table_obj = NULL;
+    const unsigned char *table_a2b = table_a2b_base85;
+
+    if (alphabet != NULL) {
+        state = get_binascii_state(module);
+        table_obj = get_reverse_table(state, (PyObject *)alphabet, 85, -1);
+        if (table_obj == NULL) {
+            return NULL;
+        }
+        table_a2b = (const unsigned char *)PyBytes_AS_STRING(table_obj);
+    }
 
     assert(ascii_len >= 0);
 
@@ -1137,6 +1208,7 @@ base85_decode_impl(PyObject *module, Py_buffer *data,
     size_t bin_len = ((size_t)ascii_len + 4) / 5 * 4;
     PyBytesWriter *writer = PyBytesWriter_Create(bin_len);
     if (writer == NULL) {
+        Py_XDECREF(table_obj);
         return NULL;
     }
     unsigned char *bin_data = PyBytesWriter_GetData(writer);
@@ -1162,8 +1234,8 @@ base85_decode_impl(PyObject *module, Py_buffer *data,
                 state = get_binascii_state(module);
                 if (state != NULL) {
                     PyErr_Format(state->Error,
-                                 "%s overflow in hunk starting at byte %d",
-                                 name, (data->len - ascii_len) / 5 * 5);
+                                 "Base85 overflow in hunk starting at byte %d",
+                                 (data->len - ascii_len) / 5 * 5);
                 }
                 goto error;
             }
@@ -1173,8 +1245,8 @@ base85_decode_impl(PyObject *module, Py_buffer *data,
         else {
             state = get_binascii_state(module);
             if (state != NULL) {
-                PyErr_Format(state->Error, "bad %s character at position %d",
-                             name, data->len - ascii_len);
+                PyErr_Format(state->Error, "bad Base85 character at position 
%d",
+                             data->len - ascii_len);
             }
             goto error;
         }
@@ -1194,19 +1266,44 @@ base85_decode_impl(PyObject *module, Py_buffer *data,
         leftchar = 0;
     }
 
+    Py_XDECREF(table_obj);
     return PyBytesWriter_FinishWithPointer(writer, bin_data);
 
 error:
     PyBytesWriter_Discard(writer);
+    Py_XDECREF(table_obj);
     return NULL;
 }
 
+/*[clinic input]
+binascii.b2a_base85
+
+    data: Py_buffer
+    /
+    *
+    pad: bool = False
+        Pad input to a multiple of 4 before encoding.
+    alphabet: Py_buffer(c_default="{NULL, NULL}") = BASE85_ALPHABET
+
+Base85-code line of data.
+[clinic start generated code]*/
+
 static PyObject *
-base85_encode_impl(PyObject *module, Py_buffer *data, int pad,
-                   const unsigned char table_b2a[], const char *name)
+binascii_b2a_base85_impl(PyObject *module, Py_buffer *data, int pad,
+                         Py_buffer *alphabet)
+/*[clinic end generated code: output=a59f4f2ff6f0e69f input=30f545c6ff554db7]*/
 {
     const unsigned char *bin_data = data->buf;
     Py_ssize_t bin_len = data->len;
+    const unsigned char *table_b2a = table_b2a_base85;
+
+    if (alphabet->buf != NULL) {
+        if (alphabet->len != 85) {
+            PyErr_SetString(PyExc_ValueError, "alphabet must have length 85");
+            return NULL;
+        }
+        table_b2a = alphabet->buf;
+    }
 
     assert(bin_len >= 0);
 
@@ -1220,7 +1317,7 @@ base85_encode_impl(PyObject *module, Py_buffer *data, int 
pad,
         if (state == NULL) {
             return NULL;
         }
-        PyErr_Format(state->Error, "Too much data for %s", name);
+        PyErr_SetString(state->Error, "Too much data for Base85");
         return NULL;
     }
 
@@ -1270,76 +1367,6 @@ base85_encode_impl(PyObject *module, Py_buffer *data, 
int pad,
     return PyBytesWriter_FinishWithPointer(writer, ascii_data);
 }
 
-/*[clinic input]
-binascii.a2b_base85
-
-    data: ascii_buffer
-    /
-
-Decode a line of Base85 data.
-[clinic start generated code]*/
-
-static PyObject *
-binascii_a2b_base85_impl(PyObject *module, Py_buffer *data)
-/*[clinic end generated code: output=c2db6ab9181b0089 input=06c9d595352b5a2b]*/
-{
-    return base85_decode_impl(module, data, table_a2b_base85, "Base85");
-}
-
-/*[clinic input]
-binascii.b2a_base85
-
-    data: Py_buffer
-    /
-    *
-    pad: bool = False
-        Pad input to a multiple of 4 before encoding.
-
-Base85-code line of data.
-[clinic start generated code]*/
-
-static PyObject *
-binascii_b2a_base85_impl(PyObject *module, Py_buffer *data, int pad)
-/*[clinic end generated code: output=b317adb36a57740d input=89fde81b96dcec06]*/
-{
-    return base85_encode_impl(module, data, pad, table_b2a_base85, "Base85");
-}
-
-/*[clinic input]
-binascii.a2b_z85
-
-    data: ascii_buffer
-    /
-
-Decode a line of Z85 data.
-[clinic start generated code]*/
-
-static PyObject *
-binascii_a2b_z85_impl(PyObject *module, Py_buffer *data)
-/*[clinic end generated code: output=57d8260bb5267a98 input=c54baff4d81510a4]*/
-{
-    return base85_decode_impl(module, data, table_a2b_base85_z85, "Z85");
-}
-
-/*[clinic input]
-binascii.b2a_z85
-
-    data: Py_buffer
-    /
-    *
-    pad: bool = False
-        Pad input to a multiple of 4 before encoding.
-
-Z85-code line of data.
-[clinic start generated code]*/
-
-static PyObject *
-binascii_b2a_z85_impl(PyObject *module, Py_buffer *data, int pad)
-/*[clinic end generated code: output=88284835e332c9cf input=51d070a5a6cf82d8]*/
-{
-    return base85_encode_impl(module, data, pad, table_b2a_base85_z85, "Z85");
-}
-
 /*[clinic input]
 binascii.crc_hqx
 
@@ -2001,8 +2028,6 @@ static struct PyMethodDef binascii_module_methods[] = {
     BINASCII_A2B_ASCII85_METHODDEF
     BINASCII_A2B_BASE85_METHODDEF
     BINASCII_B2A_BASE85_METHODDEF
-    BINASCII_A2B_Z85_METHODDEF
-    BINASCII_B2A_Z85_METHODDEF
     BINASCII_A2B_HEX_METHODDEF
     BINASCII_B2A_HEX_METHODDEF
     BINASCII_HEXLIFY_METHODDEF
@@ -2036,6 +2061,70 @@ binascii_exec(PyObject *module)
         return -1;
     }
 
+    if (PyModule_Add(module, "BASE64_ALPHABET",
+        PyBytes_FromStringAndSize((const char *)table_b2a_base64, 64)) < 0)
+    {
+        return -1;
+    }
+    if (PyModule_Add(module, "URLSAFE_BASE64_ALPHABET",
+        PyBytes_FromString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                           "abcdefghijklmnopqrstuvwxyz"
+                           "0123456789-_")) < 0)
+    {
+        return -1;
+    }
+    if (PyModule_Add(module, "CRYPT_ALPHABET",
+        PyBytes_FromString("./0123456789"
+                           "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                           "abcdefghijklmnopqrstuvwxyz")) < 0)
+    {
+        return -1;
+    }
+    if (PyModule_Add(module, "UU_ALPHABET",
+        PyBytes_FromString(" !\"#$%&'()*+,-./"
+                           "0123456789:;<=>?@"
+                           "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                           "[\\]^_")) < 0)
+    {
+        return -1;
+    }
+    if (PyModule_Add(module, "BINHEX_ALPHABET",
+        PyBytes_FromString("!\"#$%&'()*+,-012345689@"
+                           "ABCDEFGHIJKLMNPQRSTUVXYZ[`"
+                           "abcdefhijklmpqr")) < 0)
+    {
+        return -1;
+    }
+    if (PyModule_Add(module, "BASE85_ALPHABET",
+        PyBytes_FromStringAndSize((const char *)table_b2a_base85, 85)) < 0)
+    {
+        return -1;
+    }
+    if (PyModule_Add(module, "ASCII85_ALPHABET",
+        PyBytes_FromStringAndSize((const char *)table_b2a_base85_a85, 85)) < 0)
+    {
+        return -1;
+    }
+    if (PyModule_Add(module, "Z85_ALPHABET",
+        PyBytes_FromString("0123456789"
+                           "abcdefghijklmnopqrstuvwxyz"
+                           /* clinic doesn't like '/' followed by '*' */
+                           "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                           ".-:+=^!/\x2a?&<>()[]{}@%$#")) < 0)
+    {
+        return -1;
+    }
+
+    state->reverse_table_cache = PyDict_New();
+    if (state->reverse_table_cache == NULL) {
+        return -1;
+    }
+
+    state->reverse_table_cache = PyDict_New();
+    if (state->reverse_table_cache == NULL) {
+        return -1;
+    }
+
     return 0;
 }
 
@@ -2052,6 +2141,7 @@ binascii_traverse(PyObject *module, visitproc visit, void 
*arg)
     binascii_state *state = get_binascii_state(module);
     Py_VISIT(state->Error);
     Py_VISIT(state->Incomplete);
+    Py_VISIT(state->reverse_table_cache);
     return 0;
 }
 
@@ -2061,6 +2151,7 @@ binascii_clear(PyObject *module)
     binascii_state *state = get_binascii_state(module);
     Py_CLEAR(state->Error);
     Py_CLEAR(state->Incomplete);
+    Py_CLEAR(state->reverse_table_cache);
     return 0;
 }
 
diff --git a/Modules/clinic/binascii.c.h b/Modules/clinic/binascii.c.h
index 513e54b71a0065..2fdecc2efbf9d4 100644
--- a/Modules/clinic/binascii.c.h
+++ b/Modules/clinic/binascii.c.h
@@ -117,7 +117,7 @@ binascii_b2a_uu(PyObject *module, PyObject *const *args, 
Py_ssize_t nargs, PyObj
 
 PyDoc_STRVAR(binascii_a2b_base64__doc__,
 "a2b_base64($module, data, /, *, strict_mode=<unrepresentable>,\n"
-"           ignorechars=<unrepresentable>)\n"
+"           alphabet=BASE64_ALPHABET, ignorechars=<unrepresentable>)\n"
 "--\n"
 "\n"
 "Decode a line of base64 data.\n"
@@ -135,7 +135,7 @@ PyDoc_STRVAR(binascii_a2b_base64__doc__,
 
 static PyObject *
 binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode,
-                         Py_buffer *ignorechars);
+                         PyBytesObject *alphabet, Py_buffer *ignorechars);
 
 static PyObject *
 binascii_a2b_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, 
PyObject *kwnames)
@@ -143,7 +143,7 @@ binascii_a2b_base64(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs, P
     PyObject *return_value = NULL;
     #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
 
-    #define NUM_KEYWORDS 2
+    #define NUM_KEYWORDS 3
     static struct {
         PyGC_Head _this_is_not_used;
         PyObject_VAR_HEAD
@@ -152,7 +152,7 @@ binascii_a2b_base64(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs, P
     } _kwtuple = {
         .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
         .ob_hash = -1,
-        .ob_item = { &_Py_ID(strict_mode), &_Py_ID(ignorechars), },
+        .ob_item = { &_Py_ID(strict_mode), &_Py_ID(alphabet), 
&_Py_ID(ignorechars), },
     };
     #undef NUM_KEYWORDS
     #define KWTUPLE (&_kwtuple.ob_base.ob_base)
@@ -161,17 +161,18 @@ binascii_a2b_base64(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs, P
     #  define KWTUPLE NULL
     #endif  // !Py_BUILD_CORE
 
-    static const char * const _keywords[] = {"", "strict_mode", "ignorechars", 
NULL};
+    static const char * const _keywords[] = {"", "strict_mode", "alphabet", 
"ignorechars", NULL};
     static _PyArg_Parser _parser = {
         .keywords = _keywords,
         .fname = "a2b_base64",
         .kwtuple = KWTUPLE,
     };
     #undef KWTUPLE
-    PyObject *argsbuf[3];
+    PyObject *argsbuf[4];
     Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 
1;
     Py_buffer data = {NULL, NULL};
     int strict_mode = -1;
+    PyBytesObject *alphabet = NULL;
     Py_buffer ignorechars = {NULL, NULL};
 
     args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
@@ -194,11 +195,21 @@ binascii_a2b_base64(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs, P
             goto skip_optional_kwonly;
         }
     }
-    if (PyObject_GetBuffer(args[2], &ignorechars, PyBUF_SIMPLE) != 0) {
+    if (args[2]) {
+        if (!PyBytes_Check(args[2])) {
+            _PyArg_BadArgument("a2b_base64", "argument 'alphabet'", "bytes", 
args[2]);
+            goto exit;
+        }
+        alphabet = (PyBytesObject *)args[2];
+        if (!--noptargs) {
+            goto skip_optional_kwonly;
+        }
+    }
+    if (PyObject_GetBuffer(args[3], &ignorechars, PyBUF_SIMPLE) != 0) {
         goto exit;
     }
 skip_optional_kwonly:
-    return_value = binascii_a2b_base64_impl(module, &data, strict_mode, 
&ignorechars);
+    return_value = binascii_a2b_base64_impl(module, &data, strict_mode, 
alphabet, &ignorechars);
 
 exit:
     /* Cleanup for data */
@@ -213,7 +224,8 @@ binascii_a2b_base64(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs, P
 }
 
 PyDoc_STRVAR(binascii_b2a_base64__doc__,
-"b2a_base64($module, data, /, *, wrapcol=0, newline=True)\n"
+"b2a_base64($module, data, /, *, wrapcol=0, newline=True,\n"
+"           alphabet=BASE64_ALPHABET)\n"
 "--\n"
 "\n"
 "Base64-code line of data.");
@@ -223,7 +235,7 @@ PyDoc_STRVAR(binascii_b2a_base64__doc__,
 
 static PyObject *
 binascii_b2a_base64_impl(PyObject *module, Py_buffer *data, size_t wrapcol,
-                         int newline);
+                         int newline, Py_buffer *alphabet);
 
 static PyObject *
 binascii_b2a_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, 
PyObject *kwnames)
@@ -231,7 +243,7 @@ binascii_b2a_base64(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs, P
     PyObject *return_value = NULL;
     #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
 
-    #define NUM_KEYWORDS 2
+    #define NUM_KEYWORDS 3
     static struct {
         PyGC_Head _this_is_not_used;
         PyObject_VAR_HEAD
@@ -240,7 +252,7 @@ binascii_b2a_base64(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs, P
     } _kwtuple = {
         .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
         .ob_hash = -1,
-        .ob_item = { &_Py_ID(wrapcol), &_Py_ID(newline), },
+        .ob_item = { &_Py_ID(wrapcol), &_Py_ID(newline), &_Py_ID(alphabet), },
     };
     #undef NUM_KEYWORDS
     #define KWTUPLE (&_kwtuple.ob_base.ob_base)
@@ -249,18 +261,19 @@ binascii_b2a_base64(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs, P
     #  define KWTUPLE NULL
     #endif  // !Py_BUILD_CORE
 
-    static const char * const _keywords[] = {"", "wrapcol", "newline", NULL};
+    static const char * const _keywords[] = {"", "wrapcol", "newline", 
"alphabet", NULL};
     static _PyArg_Parser _parser = {
         .keywords = _keywords,
         .fname = "b2a_base64",
         .kwtuple = KWTUPLE,
     };
     #undef KWTUPLE
-    PyObject *argsbuf[3];
+    PyObject *argsbuf[4];
     Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 
1;
     Py_buffer data = {NULL, NULL};
     size_t wrapcol = 0;
     int newline = 1;
+    Py_buffer alphabet = {NULL, NULL};
 
     args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
             /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
@@ -281,18 +294,30 @@ binascii_b2a_base64(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs, P
             goto skip_optional_kwonly;
         }
     }
-    newline = PyObject_IsTrue(args[2]);
-    if (newline < 0) {
+    if (args[2]) {
+        newline = PyObject_IsTrue(args[2]);
+        if (newline < 0) {
+            goto exit;
+        }
+        if (!--noptargs) {
+            goto skip_optional_kwonly;
+        }
+    }
+    if (PyObject_GetBuffer(args[3], &alphabet, PyBUF_SIMPLE) != 0) {
         goto exit;
     }
 skip_optional_kwonly:
-    return_value = binascii_b2a_base64_impl(module, &data, wrapcol, newline);
+    return_value = binascii_b2a_base64_impl(module, &data, wrapcol, newline, 
&alphabet);
 
 exit:
     /* Cleanup for data */
     if (data.obj) {
        PyBuffer_Release(&data);
     }
+    /* Cleanup for alphabet */
+    if (alphabet.obj) {
+       PyBuffer_Release(&alphabet);
+    }
 
     return return_value;
 }
@@ -519,53 +544,20 @@ binascii_b2a_ascii85(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs,
 }
 
 PyDoc_STRVAR(binascii_a2b_base85__doc__,
-"a2b_base85($module, data, /)\n"
+"a2b_base85($module, data, /, *, alphabet=BASE85_ALPHABET)\n"
 "--\n"
 "\n"
 "Decode a line of Base85 data.");
 
 #define BINASCII_A2B_BASE85_METHODDEF    \
-    {"a2b_base85", (PyCFunction)binascii_a2b_base85, METH_O, 
binascii_a2b_base85__doc__},
-
-static PyObject *
-binascii_a2b_base85_impl(PyObject *module, Py_buffer *data);
-
-static PyObject *
-binascii_a2b_base85(PyObject *module, PyObject *arg)
-{
-    PyObject *return_value = NULL;
-    Py_buffer data = {NULL, NULL};
-
-    if (!ascii_buffer_converter(arg, &data)) {
-        goto exit;
-    }
-    return_value = binascii_a2b_base85_impl(module, &data);
-
-exit:
-    /* Cleanup for data */
-    if (data.obj)
-       PyBuffer_Release(&data);
-
-    return return_value;
-}
-
-PyDoc_STRVAR(binascii_b2a_base85__doc__,
-"b2a_base85($module, data, /, *, pad=False)\n"
-"--\n"
-"\n"
-"Base85-code line of data.\n"
-"\n"
-"  pad\n"
-"    Pad input to a multiple of 4 before encoding.");
-
-#define BINASCII_B2A_BASE85_METHODDEF    \
-    {"b2a_base85", _PyCFunction_CAST(binascii_b2a_base85), 
METH_FASTCALL|METH_KEYWORDS, binascii_b2a_base85__doc__},
+    {"a2b_base85", _PyCFunction_CAST(binascii_a2b_base85), 
METH_FASTCALL|METH_KEYWORDS, binascii_a2b_base85__doc__},
 
 static PyObject *
-binascii_b2a_base85_impl(PyObject *module, Py_buffer *data, int pad);
+binascii_a2b_base85_impl(PyObject *module, Py_buffer *data,
+                         PyBytesObject *alphabet);
 
 static PyObject *
-binascii_b2a_base85(PyObject *module, PyObject *const *args, Py_ssize_t nargs, 
PyObject *kwnames)
+binascii_a2b_base85(PyObject *module, PyObject *const *args, Py_ssize_t nargs, 
PyObject *kwnames)
 {
     PyObject *return_value = NULL;
     #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
@@ -579,7 +571,7 @@ binascii_b2a_base85(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs, P
     } _kwtuple = {
         .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
         .ob_hash = -1,
-        .ob_item = { &_Py_ID(pad), },
+        .ob_item = { &_Py_ID(alphabet), },
     };
     #undef NUM_KEYWORDS
     #define KWTUPLE (&_kwtuple.ob_base.ob_base)
@@ -588,67 +580,36 @@ binascii_b2a_base85(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs, P
     #  define KWTUPLE NULL
     #endif  // !Py_BUILD_CORE
 
-    static const char * const _keywords[] = {"", "pad", NULL};
+    static const char * const _keywords[] = {"", "alphabet", NULL};
     static _PyArg_Parser _parser = {
         .keywords = _keywords,
-        .fname = "b2a_base85",
+        .fname = "a2b_base85",
         .kwtuple = KWTUPLE,
     };
     #undef KWTUPLE
     PyObject *argsbuf[2];
     Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 
1;
     Py_buffer data = {NULL, NULL};
-    int pad = 0;
+    PyBytesObject *alphabet = NULL;
 
     args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
             /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
     if (!args) {
         goto exit;
     }
-    if (PyObject_GetBuffer(args[0], &data, PyBUF_SIMPLE) != 0) {
+    if (!ascii_buffer_converter(args[0], &data)) {
         goto exit;
     }
     if (!noptargs) {
         goto skip_optional_kwonly;
     }
-    pad = PyObject_IsTrue(args[1]);
-    if (pad < 0) {
+    if (!PyBytes_Check(args[1])) {
+        _PyArg_BadArgument("a2b_base85", "argument 'alphabet'", "bytes", 
args[1]);
         goto exit;
     }
+    alphabet = (PyBytesObject *)args[1];
 skip_optional_kwonly:
-    return_value = binascii_b2a_base85_impl(module, &data, pad);
-
-exit:
-    /* Cleanup for data */
-    if (data.obj) {
-       PyBuffer_Release(&data);
-    }
-
-    return return_value;
-}
-
-PyDoc_STRVAR(binascii_a2b_z85__doc__,
-"a2b_z85($module, data, /)\n"
-"--\n"
-"\n"
-"Decode a line of Z85 data.");
-
-#define BINASCII_A2B_Z85_METHODDEF    \
-    {"a2b_z85", (PyCFunction)binascii_a2b_z85, METH_O, 
binascii_a2b_z85__doc__},
-
-static PyObject *
-binascii_a2b_z85_impl(PyObject *module, Py_buffer *data);
-
-static PyObject *
-binascii_a2b_z85(PyObject *module, PyObject *arg)
-{
-    PyObject *return_value = NULL;
-    Py_buffer data = {NULL, NULL};
-
-    if (!ascii_buffer_converter(arg, &data)) {
-        goto exit;
-    }
-    return_value = binascii_a2b_z85_impl(module, &data);
+    return_value = binascii_a2b_base85_impl(module, &data, alphabet);
 
 exit:
     /* Cleanup for data */
@@ -658,28 +619,29 @@ binascii_a2b_z85(PyObject *module, PyObject *arg)
     return return_value;
 }
 
-PyDoc_STRVAR(binascii_b2a_z85__doc__,
-"b2a_z85($module, data, /, *, pad=False)\n"
+PyDoc_STRVAR(binascii_b2a_base85__doc__,
+"b2a_base85($module, data, /, *, pad=False, alphabet=BASE85_ALPHABET)\n"
 "--\n"
 "\n"
-"Z85-code line of data.\n"
+"Base85-code line of data.\n"
 "\n"
 "  pad\n"
 "    Pad input to a multiple of 4 before encoding.");
 
-#define BINASCII_B2A_Z85_METHODDEF    \
-    {"b2a_z85", _PyCFunction_CAST(binascii_b2a_z85), 
METH_FASTCALL|METH_KEYWORDS, binascii_b2a_z85__doc__},
+#define BINASCII_B2A_BASE85_METHODDEF    \
+    {"b2a_base85", _PyCFunction_CAST(binascii_b2a_base85), 
METH_FASTCALL|METH_KEYWORDS, binascii_b2a_base85__doc__},
 
 static PyObject *
-binascii_b2a_z85_impl(PyObject *module, Py_buffer *data, int pad);
+binascii_b2a_base85_impl(PyObject *module, Py_buffer *data, int pad,
+                         Py_buffer *alphabet);
 
 static PyObject *
-binascii_b2a_z85(PyObject *module, PyObject *const *args, Py_ssize_t nargs, 
PyObject *kwnames)
+binascii_b2a_base85(PyObject *module, PyObject *const *args, Py_ssize_t nargs, 
PyObject *kwnames)
 {
     PyObject *return_value = NULL;
     #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
 
-    #define NUM_KEYWORDS 1
+    #define NUM_KEYWORDS 2
     static struct {
         PyGC_Head _this_is_not_used;
         PyObject_VAR_HEAD
@@ -688,7 +650,7 @@ binascii_b2a_z85(PyObject *module, PyObject *const *args, 
Py_ssize_t nargs, PyOb
     } _kwtuple = {
         .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
         .ob_hash = -1,
-        .ob_item = { &_Py_ID(pad), },
+        .ob_item = { &_Py_ID(pad), &_Py_ID(alphabet), },
     };
     #undef NUM_KEYWORDS
     #define KWTUPLE (&_kwtuple.ob_base.ob_base)
@@ -697,17 +659,18 @@ binascii_b2a_z85(PyObject *module, PyObject *const *args, 
Py_ssize_t nargs, PyOb
     #  define KWTUPLE NULL
     #endif  // !Py_BUILD_CORE
 
-    static const char * const _keywords[] = {"", "pad", NULL};
+    static const char * const _keywords[] = {"", "pad", "alphabet", NULL};
     static _PyArg_Parser _parser = {
         .keywords = _keywords,
-        .fname = "b2a_z85",
+        .fname = "b2a_base85",
         .kwtuple = KWTUPLE,
     };
     #undef KWTUPLE
-    PyObject *argsbuf[2];
+    PyObject *argsbuf[3];
     Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 
1;
     Py_buffer data = {NULL, NULL};
     int pad = 0;
+    Py_buffer alphabet = {NULL, NULL};
 
     args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
             /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
@@ -720,18 +683,30 @@ binascii_b2a_z85(PyObject *module, PyObject *const *args, 
Py_ssize_t nargs, PyOb
     if (!noptargs) {
         goto skip_optional_kwonly;
     }
-    pad = PyObject_IsTrue(args[1]);
-    if (pad < 0) {
+    if (args[1]) {
+        pad = PyObject_IsTrue(args[1]);
+        if (pad < 0) {
+            goto exit;
+        }
+        if (!--noptargs) {
+            goto skip_optional_kwonly;
+        }
+    }
+    if (PyObject_GetBuffer(args[2], &alphabet, PyBUF_SIMPLE) != 0) {
         goto exit;
     }
 skip_optional_kwonly:
-    return_value = binascii_b2a_z85_impl(module, &data, pad);
+    return_value = binascii_b2a_base85_impl(module, &data, pad, &alphabet);
 
 exit:
     /* Cleanup for data */
     if (data.obj) {
        PyBuffer_Release(&data);
     }
+    /* Cleanup for alphabet */
+    if (alphabet.obj) {
+       PyBuffer_Release(&alphabet);
+    }
 
     return return_value;
 }
@@ -1281,4 +1256,4 @@ binascii_b2a_qp(PyObject *module, PyObject *const *args, 
Py_ssize_t nargs, PyObj
 
     return return_value;
 }
-/*[clinic end generated code: output=5c53e7e185700742 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=84c97096b0fb3819 input=a9049054013a1b77]*/

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to