summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/binascii.rst11
-rwxr-xr-xLib/base64.py3
-rw-r--r--Lib/test/test_binascii.py10
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/binascii.c21
-rw-r--r--Modules/clinic/binascii.c.h17
6 files changed, 45 insertions, 20 deletions
diff --git a/Doc/library/binascii.rst b/Doc/library/binascii.rst
index e3f134b..441aa57 100644
--- a/Doc/library/binascii.rst
+++ b/Doc/library/binascii.rst
@@ -52,11 +52,16 @@ The :mod:`binascii` module defines the following functions:
than one line may be passed at a time.
-.. function:: b2a_base64(data)
+.. function:: b2a_base64(data, \*, newline=True)
Convert binary data to a line of ASCII characters in base64 coding. The return
- value is the converted line, including a newline char. The length of *data*
- should be at most 57 to adhere to the base64 standard.
+ value is the converted line, including a newline char if *newline* is
+ true. The length of *data* should be at most 57 to adhere to the
+ base64 standard.
+
+
+ .. versionchanged:: 3.6
+ Added the *newline* parameter.
.. function:: a2b_qp(data, header=False)
diff --git a/Lib/base64.py b/Lib/base64.py
index 640f787..eb925cd 100755
--- a/Lib/base64.py
+++ b/Lib/base64.py
@@ -58,8 +58,7 @@ def b64encode(s, altchars=None):
The encoded byte string is returned.
"""
- # Strip off the trailing newline
- encoded = binascii.b2a_base64(s)[:-1]
+ encoded = binascii.b2a_base64(s, newline=False)
if altchars is not None:
assert len(altchars) == 2, repr(altchars)
return encoded.translate(bytes.maketrans(b'+/', altchars))
diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py
index 8367afe..0ab46bc 100644
--- a/Lib/test/test_binascii.py
+++ b/Lib/test/test_binascii.py
@@ -262,6 +262,16 @@ class BinASCIITest(unittest.TestCase):
# non-ASCII string
self.assertRaises(ValueError, a2b, "\x80")
+ def test_b2a_base64_newline(self):
+ # Issue #25357: test newline parameter
+ b = self.type2test(b'hello')
+ self.assertEqual(binascii.b2a_base64(b),
+ b'aGVsbG8=\n')
+ self.assertEqual(binascii.b2a_base64(b, newline=True),
+ b'aGVsbG8=\n')
+ self.assertEqual(binascii.b2a_base64(b, newline=False),
+ b'aGVsbG8=')
+
class ArrayBinASCIITest(BinASCIITest):
def type2test(self, s):
diff --git a/Misc/NEWS b/Misc/NEWS
index 802f4b6..e84ad1b 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -51,6 +51,9 @@ Core and Builtins
Library
-------
+- Issue #25357: Add an optional newline paramer to binascii.b2a_base64().
+ base64.b64encode() uses it to avoid a memory copy.
+
- Issue #24164: Objects that need calling ``__new__`` with keyword arguments,
can now be pickled using pickle protocols older than protocol version 4.
diff --git a/Modules/binascii.c b/Modules/binascii.c
index d920d23..3e26a7a 100644
--- a/Modules/binascii.c
+++ b/Modules/binascii.c
@@ -528,21 +528,22 @@ binascii_a2b_base64_impl(PyModuleDef *module, Py_buffer *data)
binascii.b2a_base64
data: Py_buffer
- /
+ *
+ newline: int(c_default="1") = True
Base64-code line of data.
[clinic start generated code]*/
static PyObject *
-binascii_b2a_base64_impl(PyModuleDef *module, Py_buffer *data)
-/*[clinic end generated code: output=3cd61fbee2913285 input=14ec4e47371174a9]*/
+binascii_b2a_base64_impl(PyModuleDef *module, Py_buffer *data, int newline)
+/*[clinic end generated code: output=19e1dd719a890b50 input=7b2ea6fa38d8924c]*/
{
unsigned char *ascii_data, *bin_data;
int leftbits = 0;
unsigned char this_ch;
unsigned int leftchar = 0;
PyObject *rv;
- Py_ssize_t bin_len;
+ Py_ssize_t bin_len, out_len;
bin_data = data->buf;
bin_len = data->len;
@@ -555,9 +556,12 @@ binascii_b2a_base64_impl(PyModuleDef *module, Py_buffer *data)
}
/* We're lazy and allocate too much (fixed up later).
- "+3" leaves room for up to two pad characters and a trailing
- newline. Note that 'b' gets encoded as 'Yg==\n' (1 in, 5 out). */
- if ( (rv=PyBytes_FromStringAndSize(NULL, bin_len*2 + 3)) == NULL )
+ "+2" leaves room for up to two pad characters.
+ Note that 'b' gets encoded as 'Yg==\n' (1 in, 5 out). */
+ out_len = bin_len*2 + 2;
+ if (newline)
+ out_len++;
+ if ( (rv=PyBytes_FromStringAndSize(NULL, out_len)) == NULL )
return NULL;
ascii_data = (unsigned char *)PyBytes_AS_STRING(rv);
@@ -581,7 +585,8 @@ binascii_b2a_base64_impl(PyModuleDef *module, Py_buffer *data)
*ascii_data++ = table_b2a_base64[(leftchar&0xf) << 2];
*ascii_data++ = BASE64_PAD;
}
- *ascii_data++ = '\n'; /* Append a courtesy newline */
+ if (newline)
+ *ascii_data++ = '\n'; /* Append a courtesy newline */
if (_PyBytes_Resize(&rv,
(ascii_data -
diff --git a/Modules/clinic/binascii.c.h b/Modules/clinic/binascii.c.h
index e348bee..46cfb8e 100644
--- a/Modules/clinic/binascii.c.h
+++ b/Modules/clinic/binascii.c.h
@@ -93,26 +93,29 @@ exit:
}
PyDoc_STRVAR(binascii_b2a_base64__doc__,
-"b2a_base64($module, data, /)\n"
+"b2a_base64($module, /, data, *, newline=True)\n"
"--\n"
"\n"
"Base64-code line of data.");
#define BINASCII_B2A_BASE64_METHODDEF \
- {"b2a_base64", (PyCFunction)binascii_b2a_base64, METH_O, binascii_b2a_base64__doc__},
+ {"b2a_base64", (PyCFunction)binascii_b2a_base64, METH_VARARGS|METH_KEYWORDS, binascii_b2a_base64__doc__},
static PyObject *
-binascii_b2a_base64_impl(PyModuleDef *module, Py_buffer *data);
+binascii_b2a_base64_impl(PyModuleDef *module, Py_buffer *data, int newline);
static PyObject *
-binascii_b2a_base64(PyModuleDef *module, PyObject *arg)
+binascii_b2a_base64(PyModuleDef *module, PyObject *args, PyObject *kwargs)
{
PyObject *return_value = NULL;
+ static char *_keywords[] = {"data", "newline", NULL};
Py_buffer data = {NULL, NULL};
+ int newline = 1;
- if (!PyArg_Parse(arg, "y*:b2a_base64", &data))
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*|$i:b2a_base64", _keywords,
+ &data, &newline))
goto exit;
- return_value = binascii_b2a_base64_impl(module, &data);
+ return_value = binascii_b2a_base64_impl(module, &data, newline);
exit:
/* Cleanup for data */
@@ -516,4 +519,4 @@ exit:
return return_value;
}
-/*[clinic end generated code: output=b1a3cbf7660ebaa5 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=b15a24350d105251 input=a9049054013a1b77]*/