bpo-45155 : Default arguments for int.to_bytes(length=1, byteorder=sys.byteorder) (#28265)

Add default arguments for int.to_bytes() and int.from_bytes()

Co-authored-by: Brandt Bucher <brandtbucher@gmail.com>
This commit is contained in:
Barry Warsaw 2021-09-15 19:55:24 -07:00 committed by GitHub
parent a9757bf34d
commit 07e737d002
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 127 additions and 65 deletions

View File

@ -499,7 +499,7 @@ class`. In addition, it provides a few more methods:
.. versionadded:: 3.10 .. versionadded:: 3.10
.. method:: int.to_bytes(length, byteorder, *, signed=False) .. method:: int.to_bytes(length=1, byteorder='big', *, signed=False)
Return an array of bytes representing an integer. Return an array of bytes representing an integer.
@ -513,25 +513,31 @@ class`. In addition, it provides a few more methods:
>>> x.to_bytes((x.bit_length() + 7) // 8, byteorder='little') >>> x.to_bytes((x.bit_length() + 7) // 8, byteorder='little')
b'\xe8\x03' b'\xe8\x03'
The integer is represented using *length* bytes. An :exc:`OverflowError` The integer is represented using *length* bytes, and defaults to 1. An
is raised if the integer is not representable with the given number of :exc:`OverflowError` is raised if the integer is not representable with
bytes. the given number of bytes.
The *byteorder* argument determines the byte order used to represent the The *byteorder* argument determines the byte order used to represent the
integer. If *byteorder* is ``"big"``, the most significant byte is at the integer, and defaults to ``"big"``. If *byteorder* is
beginning of the byte array. If *byteorder* is ``"little"``, the most ``"big"``, the most significant byte is at the beginning of the byte
significant byte is at the end of the byte array. To request the native array. If *byteorder* is ``"little"``, the most significant byte is at
byte order of the host system, use :data:`sys.byteorder` as the byte order the end of the byte array.
value.
The *signed* argument determines whether two's complement is used to The *signed* argument determines whether two's complement is used to
represent the integer. If *signed* is ``False`` and a negative integer is represent the integer. If *signed* is ``False`` and a negative integer is
given, an :exc:`OverflowError` is raised. The default value for *signed* given, an :exc:`OverflowError` is raised. The default value for *signed*
is ``False``. is ``False``.
The default values can be used to conveniently turn an integer into a
single byte object. However, when using the default arguments, don't try
to convert a value greater than 255 or you'll get an :exc:`OverflowError`::
>>> (65).to_bytes()
b'A'
Equivalent to:: Equivalent to::
def to_bytes(n, length, byteorder, signed=False): def to_bytes(n, length=1, byteorder='big', signed=False):
if byteorder == 'little': if byteorder == 'little':
order = range(length) order = range(length)
elif byteorder == 'big': elif byteorder == 'big':
@ -542,8 +548,10 @@ class`. In addition, it provides a few more methods:
return bytes((n >> i*8) & 0xff for i in order) return bytes((n >> i*8) & 0xff for i in order)
.. versionadded:: 3.2 .. versionadded:: 3.2
.. versionchanged:: 3.11
Added default argument values for ``length`` and ``byteorder``.
.. classmethod:: int.from_bytes(bytes, byteorder, *, signed=False) .. classmethod:: int.from_bytes(bytes, byteorder='big', *, signed=False)
Return the integer represented by the given array of bytes. Return the integer represented by the given array of bytes.
@ -562,18 +570,18 @@ class`. In addition, it provides a few more methods:
iterable producing bytes. iterable producing bytes.
The *byteorder* argument determines the byte order used to represent the The *byteorder* argument determines the byte order used to represent the
integer. If *byteorder* is ``"big"``, the most significant byte is at the integer, and defaults to ``"big"``. If *byteorder* is
beginning of the byte array. If *byteorder* is ``"little"``, the most ``"big"``, the most significant byte is at the beginning of the byte
significant byte is at the end of the byte array. To request the native array. If *byteorder* is ``"little"``, the most significant byte is at
byte order of the host system, use :data:`sys.byteorder` as the byte order the end of the byte array. To request the native byte order of the host
value. system, use :data:`sys.byteorder` as the byte order value.
The *signed* argument indicates whether two's complement is used to The *signed* argument indicates whether two's complement is used to
represent the integer. represent the integer.
Equivalent to:: Equivalent to::
def from_bytes(bytes, byteorder, signed=False): def from_bytes(bytes, byteorder='big', signed=False):
if byteorder == 'little': if byteorder == 'little':
little_ordered = list(bytes) little_ordered = list(bytes)
elif byteorder == 'big': elif byteorder == 'big':
@ -588,6 +596,8 @@ class`. In addition, it provides a few more methods:
return n return n
.. versionadded:: 3.2 .. versionadded:: 3.2
.. versionchanged:: 3.11
Added default argument value for ``byteorder``.
.. method:: int.as_integer_ratio() .. method:: int.as_integer_ratio()

View File

@ -39,7 +39,7 @@ class CFunctionCallsErrorMessages(unittest.TestCase):
self.assertRaisesRegex(TypeError, msg, {}.__contains__, 0, 1) self.assertRaisesRegex(TypeError, msg, {}.__contains__, 0, 1)
def test_varargs3(self): def test_varargs3(self):
msg = r"^from_bytes\(\) takes exactly 2 positional arguments \(3 given\)" msg = r"^from_bytes\(\) takes at most 2 positional arguments \(3 given\)"
self.assertRaisesRegex(TypeError, msg, int.from_bytes, b'a', 'little', False) self.assertRaisesRegex(TypeError, msg, int.from_bytes, b'a', 'little', False)
def test_varargs1min(self): def test_varargs1min(self):

View File

@ -1117,9 +1117,18 @@ class LongTest(unittest.TestCase):
expected) expected)
except Exception as err: except Exception as err:
raise AssertionError( raise AssertionError(
"failed to convert {0} with byteorder={1} and signed={2}" "failed to convert {} with byteorder={} and signed={}"
.format(test, byteorder, signed)) from err .format(test, byteorder, signed)) from err
# Test for all default arguments.
if len(expected) == 1 and byteorder == 'big' and not signed:
try:
self.assertEqual(test.to_bytes(), expected)
except Exception as err:
raise AssertionError(
"failed to convert {} with default arguments"
.format(test)) from err
try: try:
self.assertEqual( self.assertEqual(
equivalent_python( equivalent_python(
@ -1240,9 +1249,20 @@ class LongTest(unittest.TestCase):
expected) expected)
except Exception as err: except Exception as err:
raise AssertionError( raise AssertionError(
"failed to convert {0} with byteorder={1!r} and signed={2}" "failed to convert {} with byteorder={!r} and signed={}"
.format(test, byteorder, signed)) from err .format(test, byteorder, signed)) from err
# Test for all default arguments.
if byteorder == 'big' and not signed:
try:
self.assertEqual(
int.from_bytes(test),
expected)
except Exception as err:
raise AssertionError(
"failed to convert {} with default arugments"
.format(test)) from err
try: try:
self.assertEqual( self.assertEqual(
equivalent_python(test, byteorder, signed=signed), equivalent_python(test, byteorder, signed=signed),

View File

@ -0,0 +1,3 @@
:meth:`int.to_bytes` and :meth:`int.from_bytes` now take a default value of
``"big"`` for the ``byteorder`` argument. :meth:`int.to_bytes` also takes a
default value of ``1`` for the ``length`` argument.

View File

@ -226,20 +226,21 @@ int_as_integer_ratio(PyObject *self, PyObject *Py_UNUSED(ignored))
} }
PyDoc_STRVAR(int_to_bytes__doc__, PyDoc_STRVAR(int_to_bytes__doc__,
"to_bytes($self, /, length, byteorder, *, signed=False)\n" "to_bytes($self, /, length=1, byteorder=\'big\', *, signed=False)\n"
"--\n" "--\n"
"\n" "\n"
"Return an array of bytes representing an integer.\n" "Return an array of bytes representing an integer.\n"
"\n" "\n"
" length\n" " length\n"
" Length of bytes object to use. An OverflowError is raised if the\n" " Length of bytes object to use. An OverflowError is raised if the\n"
" integer is not representable with the given number of bytes.\n" " integer is not representable with the given number of bytes. Default\n"
" is length 1.\n"
" byteorder\n" " byteorder\n"
" The byte order used to represent the integer. If byteorder is \'big\',\n" " The byte order used to represent the integer. If byteorder is \'big\',\n"
" the most significant byte is at the beginning of the byte array. If\n" " the most significant byte is at the beginning of the byte array. If\n"
" byteorder is \'little\', the most significant byte is at the end of the\n" " byteorder is \'little\', the most significant byte is at the end of the\n"
" byte array. To request the native byte order of the host system, use\n" " byte array. To request the native byte order of the host system, use\n"
" `sys.byteorder\' as the byte order value.\n" " `sys.byteorder\' as the byte order value. Default is to use \'big\'.\n"
" signed\n" " signed\n"
" Determines whether two\'s complement is used to represent the integer.\n" " Determines whether two\'s complement is used to represent the integer.\n"
" If signed is False and a negative integer is given, an OverflowError\n" " If signed is False and a negative integer is given, an OverflowError\n"
@ -259,15 +260,19 @@ int_to_bytes(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *
static const char * const _keywords[] = {"length", "byteorder", "signed", NULL}; static const char * const _keywords[] = {"length", "byteorder", "signed", NULL};
static _PyArg_Parser _parser = {NULL, _keywords, "to_bytes", 0}; static _PyArg_Parser _parser = {NULL, _keywords, "to_bytes", 0};
PyObject *argsbuf[3]; PyObject *argsbuf[3];
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
Py_ssize_t length; Py_ssize_t length = 1;
PyObject *byteorder; PyObject *byteorder = NULL;
int is_signed = 0; int is_signed = 0;
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf);
if (!args) { if (!args) {
goto exit; goto exit;
} }
if (!noptargs) {
goto skip_optional_pos;
}
if (args[0]) {
{ {
Py_ssize_t ival = -1; Py_ssize_t ival = -1;
PyObject *iobj = _PyNumber_Index(args[0]); PyObject *iobj = _PyNumber_Index(args[0]);
@ -280,6 +285,11 @@ int_to_bytes(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *
} }
length = ival; length = ival;
} }
if (!--noptargs) {
goto skip_optional_pos;
}
}
if (args[1]) {
if (!PyUnicode_Check(args[1])) { if (!PyUnicode_Check(args[1])) {
_PyArg_BadArgument("to_bytes", "argument 'byteorder'", "str", args[1]); _PyArg_BadArgument("to_bytes", "argument 'byteorder'", "str", args[1]);
goto exit; goto exit;
@ -288,6 +298,11 @@ int_to_bytes(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *
goto exit; goto exit;
} }
byteorder = args[1]; byteorder = args[1];
if (!--noptargs) {
goto skip_optional_pos;
}
}
skip_optional_pos:
if (!noptargs) { if (!noptargs) {
goto skip_optional_kwonly; goto skip_optional_kwonly;
} }
@ -303,7 +318,7 @@ exit:
} }
PyDoc_STRVAR(int_from_bytes__doc__, PyDoc_STRVAR(int_from_bytes__doc__,
"from_bytes($type, /, bytes, byteorder, *, signed=False)\n" "from_bytes($type, /, bytes, byteorder=\'big\', *, signed=False)\n"
"--\n" "--\n"
"\n" "\n"
"Return the integer represented by the given array of bytes.\n" "Return the integer represented by the given array of bytes.\n"
@ -318,7 +333,7 @@ PyDoc_STRVAR(int_from_bytes__doc__,
" the most significant byte is at the beginning of the byte array. If\n" " the most significant byte is at the beginning of the byte array. If\n"
" byteorder is \'little\', the most significant byte is at the end of the\n" " byteorder is \'little\', the most significant byte is at the end of the\n"
" byte array. To request the native byte order of the host system, use\n" " byte array. To request the native byte order of the host system, use\n"
" `sys.byteorder\' as the byte order value.\n" " `sys.byteorder\' as the byte order value. Default is to use \'big\'.\n"
" signed\n" " signed\n"
" Indicates whether two\'s complement is used to represent the integer."); " Indicates whether two\'s complement is used to represent the integer.");
@ -336,16 +351,20 @@ int_from_bytes(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyOb
static const char * const _keywords[] = {"bytes", "byteorder", "signed", NULL}; static const char * const _keywords[] = {"bytes", "byteorder", "signed", NULL};
static _PyArg_Parser _parser = {NULL, _keywords, "from_bytes", 0}; static _PyArg_Parser _parser = {NULL, _keywords, "from_bytes", 0};
PyObject *argsbuf[3]; PyObject *argsbuf[3];
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
PyObject *bytes_obj; PyObject *bytes_obj;
PyObject *byteorder; PyObject *byteorder = NULL;
int is_signed = 0; int is_signed = 0;
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf);
if (!args) { if (!args) {
goto exit; goto exit;
} }
bytes_obj = args[0]; bytes_obj = args[0];
if (!noptargs) {
goto skip_optional_pos;
}
if (args[1]) {
if (!PyUnicode_Check(args[1])) { if (!PyUnicode_Check(args[1])) {
_PyArg_BadArgument("from_bytes", "argument 'byteorder'", "str", args[1]); _PyArg_BadArgument("from_bytes", "argument 'byteorder'", "str", args[1]);
goto exit; goto exit;
@ -354,6 +373,11 @@ int_from_bytes(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyOb
goto exit; goto exit;
} }
byteorder = args[1]; byteorder = args[1];
if (!--noptargs) {
goto skip_optional_pos;
}
}
skip_optional_pos:
if (!noptargs) { if (!noptargs) {
goto skip_optional_kwonly; goto skip_optional_kwonly;
} }
@ -367,4 +391,4 @@ skip_optional_kwonly:
exit: exit:
return return_value; return return_value;
} }
/*[clinic end generated code: output=ea18e51af5b53591 input=a9049054013a1b77]*/ /*[clinic end generated code: output=16a375d93769b227 input=a9049054013a1b77]*/

View File

@ -5521,15 +5521,16 @@ int_as_integer_ratio_impl(PyObject *self)
/*[clinic input] /*[clinic input]
int.to_bytes int.to_bytes
length: Py_ssize_t length: Py_ssize_t = 1
Length of bytes object to use. An OverflowError is raised if the Length of bytes object to use. An OverflowError is raised if the
integer is not representable with the given number of bytes. integer is not representable with the given number of bytes. Default
byteorder: unicode is length 1.
byteorder: unicode(c_default="NULL") = "big"
The byte order used to represent the integer. If byteorder is 'big', The byte order used to represent the integer. If byteorder is 'big',
the most significant byte is at the beginning of the byte array. If the most significant byte is at the beginning of the byte array. If
byteorder is 'little', the most significant byte is at the end of the byteorder is 'little', the most significant byte is at the end of the
byte array. To request the native byte order of the host system, use byte array. To request the native byte order of the host system, use
`sys.byteorder' as the byte order value. `sys.byteorder' as the byte order value. Default is to use 'big'.
* *
signed as is_signed: bool = False signed as is_signed: bool = False
Determines whether two's complement is used to represent the integer. Determines whether two's complement is used to represent the integer.
@ -5542,12 +5543,14 @@ Return an array of bytes representing an integer.
static PyObject * static PyObject *
int_to_bytes_impl(PyObject *self, Py_ssize_t length, PyObject *byteorder, int_to_bytes_impl(PyObject *self, Py_ssize_t length, PyObject *byteorder,
int is_signed) int is_signed)
/*[clinic end generated code: output=89c801df114050a3 input=ddac63f4c7bf414c]*/ /*[clinic end generated code: output=89c801df114050a3 input=d42ecfb545039d71]*/
{ {
int little_endian; int little_endian;
PyObject *bytes; PyObject *bytes;
if (_PyUnicode_EqualToASCIIId(byteorder, &PyId_little)) if (byteorder == NULL)
little_endian = 0;
else if (_PyUnicode_EqualToASCIIId(byteorder, &PyId_little))
little_endian = 1; little_endian = 1;
else if (_PyUnicode_EqualToASCIIId(byteorder, &PyId_big)) else if (_PyUnicode_EqualToASCIIId(byteorder, &PyId_big))
little_endian = 0; little_endian = 0;
@ -5586,12 +5589,12 @@ int.from_bytes
support the buffer protocol or be an iterable object producing bytes. support the buffer protocol or be an iterable object producing bytes.
Bytes and bytearray are examples of built-in objects that support the Bytes and bytearray are examples of built-in objects that support the
buffer protocol. buffer protocol.
byteorder: unicode byteorder: unicode(c_default="NULL") = "big"
The byte order used to represent the integer. If byteorder is 'big', The byte order used to represent the integer. If byteorder is 'big',
the most significant byte is at the beginning of the byte array. If the most significant byte is at the beginning of the byte array. If
byteorder is 'little', the most significant byte is at the end of the byteorder is 'little', the most significant byte is at the end of the
byte array. To request the native byte order of the host system, use byte array. To request the native byte order of the host system, use
`sys.byteorder' as the byte order value. `sys.byteorder' as the byte order value. Default is to use 'big'.
* *
signed as is_signed: bool = False signed as is_signed: bool = False
Indicates whether two's complement is used to represent the integer. Indicates whether two's complement is used to represent the integer.
@ -5602,12 +5605,14 @@ Return the integer represented by the given array of bytes.
static PyObject * static PyObject *
int_from_bytes_impl(PyTypeObject *type, PyObject *bytes_obj, int_from_bytes_impl(PyTypeObject *type, PyObject *bytes_obj,
PyObject *byteorder, int is_signed) PyObject *byteorder, int is_signed)
/*[clinic end generated code: output=efc5d68e31f9314f input=cdf98332b6a821b0]*/ /*[clinic end generated code: output=efc5d68e31f9314f input=33326dccdd655553]*/
{ {
int little_endian; int little_endian;
PyObject *long_obj, *bytes; PyObject *long_obj, *bytes;
if (_PyUnicode_EqualToASCIIId(byteorder, &PyId_little)) if (byteorder == NULL)
little_endian = 0;
else if (_PyUnicode_EqualToASCIIId(byteorder, &PyId_little))
little_endian = 1; little_endian = 1;
else if (_PyUnicode_EqualToASCIIId(byteorder, &PyId_big)) else if (_PyUnicode_EqualToASCIIId(byteorder, &PyId_big))
little_endian = 0; little_endian = 0;