bpo-30103: Allow Uuencode in Python using backtick as zero instead of space (#1326)
This commit is contained in:
parent
0360a9d015
commit
13f1f423fa
@ -40,11 +40,14 @@ The :mod:`binascii` module defines the following functions:
|
|||||||
data may be followed by whitespace.
|
data may be followed by whitespace.
|
||||||
|
|
||||||
|
|
||||||
.. function:: b2a_uu(data)
|
.. function:: b2a_uu(data, *, backtick=False)
|
||||||
|
|
||||||
Convert binary data to a line of ASCII characters, the return value is the
|
Convert binary data to a line of ASCII characters, the return value is the
|
||||||
converted line, including a newline char. The length of *data* should be at most
|
converted line, including a newline char. The length of *data* should be at most
|
||||||
45.
|
45. If *backtick* is true, zeros are represented by ``'`'`` instead of spaces.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.7
|
||||||
|
Added the *backtick* parameter.
|
||||||
|
|
||||||
|
|
||||||
.. function:: a2b_base64(string)
|
.. function:: a2b_base64(string)
|
||||||
@ -53,7 +56,7 @@ The :mod:`binascii` module defines the following functions:
|
|||||||
than one line may be passed at a time.
|
than one line may be passed at a time.
|
||||||
|
|
||||||
|
|
||||||
.. function:: b2a_base64(data, \*, newline=True)
|
.. function:: b2a_base64(data, *, newline=True)
|
||||||
|
|
||||||
Convert binary data to a line of ASCII characters in base64 coding. The return
|
Convert binary data to a line of ASCII characters in base64 coding. The return
|
||||||
value is the converted line, including a newline char if *newline* is
|
value is the converted line, including a newline char if *newline* is
|
||||||
|
@ -28,12 +28,16 @@ This code was contributed by Lance Ellinghouse, and modified by Jack Jansen.
|
|||||||
The :mod:`uu` module defines the following functions:
|
The :mod:`uu` module defines the following functions:
|
||||||
|
|
||||||
|
|
||||||
.. function:: encode(in_file, out_file, name=None, mode=None)
|
.. function:: encode(in_file, out_file, name=None, mode=None, *, backtick=False)
|
||||||
|
|
||||||
Uuencode file *in_file* into file *out_file*. The uuencoded file will have
|
Uuencode file *in_file* into file *out_file*. The uuencoded file will have
|
||||||
the header specifying *name* and *mode* as the defaults for the results of
|
the header specifying *name* and *mode* as the defaults for the results of
|
||||||
decoding the file. The default defaults are taken from *in_file*, or ``'-'``
|
decoding the file. The default defaults are taken from *in_file*, or ``'-'``
|
||||||
and ``0o666`` respectively.
|
and ``0o666`` respectively. If *backtick* is true, zeros are represented by
|
||||||
|
``'`'`` instead of spaces.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.7
|
||||||
|
Added the *backtick* parameter.
|
||||||
|
|
||||||
|
|
||||||
.. function:: decode(in_file, out_file=None, mode=None, quiet=False)
|
.. function:: decode(in_file, out_file=None, mode=None, quiet=False)
|
||||||
|
@ -328,3 +328,7 @@ whatsnew/3.5,,:exception,ERROR:root:exception
|
|||||||
whatsnew/changelog,,:version,import sys; I = version[:version.index(' ')]
|
whatsnew/changelog,,:version,import sys; I = version[:version.index(' ')]
|
||||||
whatsnew/changelog,,`,"for readability (was ""`"")."
|
whatsnew/changelog,,`,"for readability (was ""`"")."
|
||||||
whatsnew/changelog,,:end,str[start:end]
|
whatsnew/changelog,,:end,str[start:end]
|
||||||
|
library/binascii,,`,'`'
|
||||||
|
library/uu,,`,'`'
|
||||||
|
whatsnew/3.7,,`,'`'
|
||||||
|
whatsnew/changelog,,`,'`'
|
||||||
|
|
@ -95,6 +95,13 @@ New Modules
|
|||||||
Improved Modules
|
Improved Modules
|
||||||
================
|
================
|
||||||
|
|
||||||
|
binascii
|
||||||
|
--------
|
||||||
|
|
||||||
|
The :func:`~binascii.b2a_uu` function now accepts an optional *backtick*
|
||||||
|
keyword argument. When it's true, zeros are represented by ``'`'``
|
||||||
|
instead of spaces. (Contributed by Xiang Zhang in :issue:`30103`.)
|
||||||
|
|
||||||
contextlib
|
contextlib
|
||||||
----------
|
----------
|
||||||
|
|
||||||
@ -159,6 +166,13 @@ urllib.parse
|
|||||||
adding `~` to the set of characters that is never quoted by default.
|
adding `~` to the set of characters that is never quoted by default.
|
||||||
(Contributed by Christian Theune and Ratnadeep Debnath in :issue:`16285`.)
|
(Contributed by Christian Theune and Ratnadeep Debnath in :issue:`16285`.)
|
||||||
|
|
||||||
|
uu
|
||||||
|
--
|
||||||
|
|
||||||
|
Function :func:`~uu.encode` now accepts an optional *backtick*
|
||||||
|
keyword argument. When it's true, zeros are represented by ``'`'``
|
||||||
|
instead of spaces. (Contributed by Xiang Zhang in :issue:`30103`.)
|
||||||
|
|
||||||
|
|
||||||
Optimizations
|
Optimizations
|
||||||
=============
|
=============
|
||||||
|
@ -112,10 +112,11 @@ class BinASCIITest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_uu(self):
|
def test_uu(self):
|
||||||
MAX_UU = 45
|
MAX_UU = 45
|
||||||
|
for backtick in (True, False):
|
||||||
lines = []
|
lines = []
|
||||||
for i in range(0, len(self.data), MAX_UU):
|
for i in range(0, len(self.data), MAX_UU):
|
||||||
b = self.type2test(self.rawdata[i:i+MAX_UU])
|
b = self.type2test(self.rawdata[i:i+MAX_UU])
|
||||||
a = binascii.b2a_uu(b)
|
a = binascii.b2a_uu(b, backtick=backtick)
|
||||||
lines.append(a)
|
lines.append(a)
|
||||||
res = bytes()
|
res = bytes()
|
||||||
for line in lines:
|
for line in lines:
|
||||||
@ -129,12 +130,23 @@ class BinASCIITest(unittest.TestCase):
|
|||||||
self.assertEqual(binascii.a2b_uu(b"\xff"), b"\x00"*31)
|
self.assertEqual(binascii.a2b_uu(b"\xff"), b"\x00"*31)
|
||||||
self.assertRaises(binascii.Error, binascii.a2b_uu, b"\xff\x00")
|
self.assertRaises(binascii.Error, binascii.a2b_uu, b"\xff\x00")
|
||||||
self.assertRaises(binascii.Error, binascii.a2b_uu, b"!!!!")
|
self.assertRaises(binascii.Error, binascii.a2b_uu, b"!!!!")
|
||||||
|
|
||||||
self.assertRaises(binascii.Error, binascii.b2a_uu, 46*b"!")
|
self.assertRaises(binascii.Error, binascii.b2a_uu, 46*b"!")
|
||||||
|
|
||||||
# Issue #7701 (crash on a pydebug build)
|
# Issue #7701 (crash on a pydebug build)
|
||||||
self.assertEqual(binascii.b2a_uu(b'x'), b'!> \n')
|
self.assertEqual(binascii.b2a_uu(b'x'), b'!> \n')
|
||||||
|
|
||||||
|
self.assertEqual(binascii.b2a_uu(b''), b' \n')
|
||||||
|
self.assertEqual(binascii.b2a_uu(b'', backtick=True), b'`\n')
|
||||||
|
self.assertEqual(binascii.a2b_uu(b' \n'), b'')
|
||||||
|
self.assertEqual(binascii.a2b_uu(b'`\n'), b'')
|
||||||
|
self.assertEqual(binascii.b2a_uu(b'\x00Cat'), b'$ $-A= \n')
|
||||||
|
self.assertEqual(binascii.b2a_uu(b'\x00Cat', backtick=True),
|
||||||
|
b'$`$-A=```\n')
|
||||||
|
self.assertEqual(binascii.a2b_uu(b'$`$-A=```\n'),
|
||||||
|
binascii.a2b_uu(b'$ $-A= \n'))
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
binascii.b2a_uu(b"", True)
|
||||||
|
|
||||||
def test_crc_hqx(self):
|
def test_crc_hqx(self):
|
||||||
crc = binascii.crc_hqx(self.type2test(b"Test the CRC-32 of"), 0)
|
crc = binascii.crc_hqx(self.type2test(b"Test the CRC-32 of"), 0)
|
||||||
crc = binascii.crc_hqx(self.type2test(b" this string."), crc)
|
crc = binascii.crc_hqx(self.type2test(b" this string."), crc)
|
||||||
|
@ -10,11 +10,11 @@ import sys, os
|
|||||||
import uu
|
import uu
|
||||||
import io
|
import io
|
||||||
|
|
||||||
plaintext = b"The smooth-scaled python crept over the sleeping dog\n"
|
plaintext = b"The symbols on top of your keyboard are !@#$%^&*()_+|~\n"
|
||||||
|
|
||||||
encodedtext = b"""\
|
encodedtext = b"""\
|
||||||
M5&AE('-M;V]T:\"US8V%L960@<'ET:&]N(&-R97!T(&]V97(@=&AE('-L965P
|
M5&AE('-Y;6)O;',@;VX@=&]P(&]F('EO=7(@:V5Y8F]A<F0@87)E("% (R0E
|
||||||
(:6YG(&1O9PH """
|
*7B8J*"E?*WQ^"@ """
|
||||||
|
|
||||||
# Stolen from io.py
|
# Stolen from io.py
|
||||||
class FakeIO(io.TextIOWrapper):
|
class FakeIO(io.TextIOWrapper):
|
||||||
@ -44,9 +44,14 @@ class FakeIO(io.TextIOWrapper):
|
|||||||
return self.buffer.getvalue().decode(self._encoding, self._errors)
|
return self.buffer.getvalue().decode(self._encoding, self._errors)
|
||||||
|
|
||||||
|
|
||||||
def encodedtextwrapped(mode, filename):
|
def encodedtextwrapped(mode, filename, backtick=False):
|
||||||
return (bytes("begin %03o %s\n" % (mode, filename), "ascii") +
|
if backtick:
|
||||||
|
res = (bytes("begin %03o %s\n" % (mode, filename), "ascii") +
|
||||||
|
encodedtext.replace(b' ', b'`') + b"\n`\nend\n")
|
||||||
|
else:
|
||||||
|
res = (bytes("begin %03o %s\n" % (mode, filename), "ascii") +
|
||||||
encodedtext + b"\n \nend\n")
|
encodedtext + b"\n \nend\n")
|
||||||
|
return res
|
||||||
|
|
||||||
class UUTest(unittest.TestCase):
|
class UUTest(unittest.TestCase):
|
||||||
|
|
||||||
@ -59,16 +64,23 @@ class UUTest(unittest.TestCase):
|
|||||||
out = io.BytesIO()
|
out = io.BytesIO()
|
||||||
uu.encode(inp, out, "t1", 0o644)
|
uu.encode(inp, out, "t1", 0o644)
|
||||||
self.assertEqual(out.getvalue(), encodedtextwrapped(0o644, "t1"))
|
self.assertEqual(out.getvalue(), encodedtextwrapped(0o644, "t1"))
|
||||||
|
inp = io.BytesIO(plaintext)
|
||||||
|
out = io.BytesIO()
|
||||||
|
uu.encode(inp, out, "t1", backtick=True)
|
||||||
|
self.assertEqual(out.getvalue(), encodedtextwrapped(0o666, "t1", True))
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
uu.encode(inp, out, "t1", 0o644, True)
|
||||||
|
|
||||||
def test_decode(self):
|
def test_decode(self):
|
||||||
inp = io.BytesIO(encodedtextwrapped(0o666, "t1"))
|
for backtick in True, False:
|
||||||
|
inp = io.BytesIO(encodedtextwrapped(0o666, "t1", backtick=backtick))
|
||||||
out = io.BytesIO()
|
out = io.BytesIO()
|
||||||
uu.decode(inp, out)
|
uu.decode(inp, out)
|
||||||
self.assertEqual(out.getvalue(), plaintext)
|
self.assertEqual(out.getvalue(), plaintext)
|
||||||
inp = io.BytesIO(
|
inp = io.BytesIO(
|
||||||
b"UUencoded files may contain many lines,\n" +
|
b"UUencoded files may contain many lines,\n" +
|
||||||
b"even some that have 'begin' in them.\n" +
|
b"even some that have 'begin' in them.\n" +
|
||||||
encodedtextwrapped(0o666, "t1")
|
encodedtextwrapped(0o666, "t1", backtick=backtick)
|
||||||
)
|
)
|
||||||
out = io.BytesIO()
|
out = io.BytesIO()
|
||||||
uu.decode(inp, out)
|
uu.decode(inp, out)
|
||||||
@ -94,15 +106,23 @@ class UUTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_garbage_padding(self):
|
def test_garbage_padding(self):
|
||||||
# Issue #22406
|
# Issue #22406
|
||||||
encodedtext = (
|
encodedtext1 = (
|
||||||
b"begin 644 file\n"
|
b"begin 644 file\n"
|
||||||
# length 1; bits 001100 111111 111111 111111
|
# length 1; bits 001100 111111 111111 111111
|
||||||
b"\x21\x2C\x5F\x5F\x5F\n"
|
b"\x21\x2C\x5F\x5F\x5F\n"
|
||||||
b"\x20\n"
|
b"\x20\n"
|
||||||
b"end\n"
|
b"end\n"
|
||||||
)
|
)
|
||||||
|
encodedtext2 = (
|
||||||
|
b"begin 644 file\n"
|
||||||
|
# length 1; bits 001100 111111 111111 111111
|
||||||
|
b"\x21\x2C\x5F\x5F\x5F\n"
|
||||||
|
b"\x60\n"
|
||||||
|
b"end\n"
|
||||||
|
)
|
||||||
plaintext = b"\x33" # 00110011
|
plaintext = b"\x33" # 00110011
|
||||||
|
|
||||||
|
for encodedtext in encodedtext1, encodedtext2:
|
||||||
with self.subTest("uu.decode()"):
|
with self.subTest("uu.decode()"):
|
||||||
inp = io.BytesIO(encodedtext)
|
inp = io.BytesIO(encodedtext)
|
||||||
out = io.BytesIO()
|
out = io.BytesIO()
|
||||||
@ -250,11 +270,6 @@ class UUFileTest(unittest.TestCase):
|
|||||||
finally:
|
finally:
|
||||||
self._kill(f)
|
self._kill(f)
|
||||||
|
|
||||||
def test_main():
|
|
||||||
support.run_unittest(UUTest,
|
|
||||||
UUStdIOTest,
|
|
||||||
UUFileTest,
|
|
||||||
)
|
|
||||||
|
|
||||||
if __name__=="__main__":
|
if __name__=="__main__":
|
||||||
test_main()
|
unittest.main()
|
||||||
|
11
Lib/uu.py
11
Lib/uu.py
@ -26,8 +26,8 @@
|
|||||||
|
|
||||||
"""Implementation of the UUencode and UUdecode functions.
|
"""Implementation of the UUencode and UUdecode functions.
|
||||||
|
|
||||||
encode(in_file, out_file [,name, mode])
|
encode(in_file, out_file [,name, mode], *, backtick=False)
|
||||||
decode(in_file [, out_file, mode])
|
decode(in_file [, out_file, mode, quiet])
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import binascii
|
import binascii
|
||||||
@ -39,7 +39,7 @@ __all__ = ["Error", "encode", "decode"]
|
|||||||
class Error(Exception):
|
class Error(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def encode(in_file, out_file, name=None, mode=None):
|
def encode(in_file, out_file, name=None, mode=None, *, backtick=False):
|
||||||
"""Uuencode file"""
|
"""Uuencode file"""
|
||||||
#
|
#
|
||||||
# If in_file is a pathname open it and change defaults
|
# If in_file is a pathname open it and change defaults
|
||||||
@ -79,8 +79,11 @@ def encode(in_file, out_file, name=None, mode=None):
|
|||||||
out_file.write(('begin %o %s\n' % ((mode & 0o777), name)).encode("ascii"))
|
out_file.write(('begin %o %s\n' % ((mode & 0o777), name)).encode("ascii"))
|
||||||
data = in_file.read(45)
|
data = in_file.read(45)
|
||||||
while len(data) > 0:
|
while len(data) > 0:
|
||||||
out_file.write(binascii.b2a_uu(data))
|
out_file.write(binascii.b2a_uu(data, backtick=backtick))
|
||||||
data = in_file.read(45)
|
data = in_file.read(45)
|
||||||
|
if backtick:
|
||||||
|
out_file.write(b'`\nend\n')
|
||||||
|
else:
|
||||||
out_file.write(b' \nend\n')
|
out_file.write(b' \nend\n')
|
||||||
finally:
|
finally:
|
||||||
for f in opened_files:
|
for f in opened_files:
|
||||||
|
@ -317,6 +317,9 @@ Extension Modules
|
|||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- bpo-30103: binascii.b2a_uu() and uu.encode() now support using ``'`'``
|
||||||
|
as zero instead of space.
|
||||||
|
|
||||||
- bpo-28556: Various updates to typing module: add typing.NoReturn type, use
|
- bpo-28556: Various updates to typing module: add typing.NoReturn type, use
|
||||||
WrapperDescriptorType, minor bug-fixes. Original PRs by
|
WrapperDescriptorType, minor bug-fixes. Original PRs by
|
||||||
Jim Fasarakis-Hilliard and Ivan Levkivskyi.
|
Jim Fasarakis-Hilliard and Ivan Levkivskyi.
|
||||||
|
@ -335,13 +335,15 @@ binascii.b2a_uu
|
|||||||
|
|
||||||
data: Py_buffer
|
data: Py_buffer
|
||||||
/
|
/
|
||||||
|
*
|
||||||
|
backtick: bool(accept={int}) = False
|
||||||
|
|
||||||
Uuencode line of data.
|
Uuencode line of data.
|
||||||
[clinic start generated code]*/
|
[clinic start generated code]*/
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
binascii_b2a_uu_impl(PyObject *module, Py_buffer *data)
|
binascii_b2a_uu_impl(PyObject *module, Py_buffer *data, int backtick)
|
||||||
/*[clinic end generated code: output=0070670e52e4aa6b input=00fdf458ce8b465b]*/
|
/*[clinic end generated code: output=b1b99de62d9bbeb8 input=b26bc8d32b6ed2f6]*/
|
||||||
{
|
{
|
||||||
unsigned char *ascii_data;
|
unsigned char *ascii_data;
|
||||||
const unsigned char *bin_data;
|
const unsigned char *bin_data;
|
||||||
@ -367,7 +369,10 @@ binascii_b2a_uu_impl(PyObject *module, Py_buffer *data)
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Store the length */
|
/* Store the length */
|
||||||
*ascii_data++ = ' ' + (bin_len & 077);
|
if (backtick && !bin_len)
|
||||||
|
*ascii_data++ = '`';
|
||||||
|
else
|
||||||
|
*ascii_data++ = ' ' + bin_len;
|
||||||
|
|
||||||
for( ; bin_len > 0 || leftbits != 0 ; bin_len--, bin_data++ ) {
|
for( ; bin_len > 0 || leftbits != 0 ; bin_len--, bin_data++ ) {
|
||||||
/* Shift the data (or padding) into our buffer */
|
/* Shift the data (or padding) into our buffer */
|
||||||
@ -381,6 +386,9 @@ binascii_b2a_uu_impl(PyObject *module, Py_buffer *data)
|
|||||||
while ( leftbits >= 6 ) {
|
while ( leftbits >= 6 ) {
|
||||||
this_ch = (leftchar >> (leftbits-6)) & 0x3f;
|
this_ch = (leftchar >> (leftbits-6)) & 0x3f;
|
||||||
leftbits -= 6;
|
leftbits -= 6;
|
||||||
|
if (backtick && !this_ch)
|
||||||
|
*ascii_data++ = '`';
|
||||||
|
else
|
||||||
*ascii_data++ = this_ch + ' ';
|
*ascii_data++ = this_ch + ' ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,27 +34,31 @@ exit:
|
|||||||
}
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(binascii_b2a_uu__doc__,
|
PyDoc_STRVAR(binascii_b2a_uu__doc__,
|
||||||
"b2a_uu($module, data, /)\n"
|
"b2a_uu($module, data, /, *, backtick=False)\n"
|
||||||
"--\n"
|
"--\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Uuencode line of data.");
|
"Uuencode line of data.");
|
||||||
|
|
||||||
#define BINASCII_B2A_UU_METHODDEF \
|
#define BINASCII_B2A_UU_METHODDEF \
|
||||||
{"b2a_uu", (PyCFunction)binascii_b2a_uu, METH_O, binascii_b2a_uu__doc__},
|
{"b2a_uu", (PyCFunction)binascii_b2a_uu, METH_FASTCALL, binascii_b2a_uu__doc__},
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
binascii_b2a_uu_impl(PyObject *module, Py_buffer *data);
|
binascii_b2a_uu_impl(PyObject *module, Py_buffer *data, int backtick);
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
binascii_b2a_uu(PyObject *module, PyObject *arg)
|
binascii_b2a_uu(PyObject *module, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
|
||||||
{
|
{
|
||||||
PyObject *return_value = NULL;
|
PyObject *return_value = NULL;
|
||||||
|
static const char * const _keywords[] = {"", "backtick", NULL};
|
||||||
|
static _PyArg_Parser _parser = {"y*|$i:b2a_uu", _keywords, 0};
|
||||||
Py_buffer data = {NULL, NULL};
|
Py_buffer data = {NULL, NULL};
|
||||||
|
int backtick = 0;
|
||||||
|
|
||||||
if (!PyArg_Parse(arg, "y*:b2a_uu", &data)) {
|
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
|
||||||
|
&data, &backtick)) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
return_value = binascii_b2a_uu_impl(module, &data);
|
return_value = binascii_b2a_uu_impl(module, &data, backtick);
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
/* Cleanup for data */
|
/* Cleanup for data */
|
||||||
@ -558,4 +562,4 @@ exit:
|
|||||||
|
|
||||||
return return_value;
|
return return_value;
|
||||||
}
|
}
|
||||||
/*[clinic end generated code: output=35821bce7e0e4714 input=a9049054013a1b77]*/
|
/*[clinic end generated code: output=9db57e86dbe7b2fa input=a9049054013a1b77]*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user