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

warsaw webhook-mailer at python.org
Wed Sep 15 22:55:35 EDT 2021


https://github.com/python/cpython/commit/07e737d002cdbf0bfee53248a652a86c9f93f02b
commit: 07e737d002cdbf0bfee53248a652a86c9f93f02b
branch: main
author: Barry Warsaw <barry at python.org>
committer: warsaw <barry at python.org>
date: 2021-09-15T19:55:24-07:00
summary:

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 at gmail.com>

files:
A Misc/NEWS.d/next/Core and Builtins/2021-09-09-15-05-17.bpo-45155.JRw9TG.rst
M Doc/library/stdtypes.rst
M Lib/test/test_call.py
M Lib/test/test_long.py
M Objects/clinic/longobject.c.h
M Objects/longobject.c

diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst
index c5b5dbca500f1..eea9ddcc14003 100644
--- a/Doc/library/stdtypes.rst
+++ b/Doc/library/stdtypes.rst
@@ -499,7 +499,7 @@ class`. In addition, it provides a few more methods:
 
     .. 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.
 
@@ -513,25 +513,31 @@ class`. In addition, it provides a few more methods:
         >>> x.to_bytes((x.bit_length() + 7) // 8, byteorder='little')
         b'\xe8\x03'
 
-    The integer is represented using *length* bytes.  An :exc:`OverflowError`
-    is raised if the integer is not representable with the given number of
-    bytes.
+    The integer is represented using *length* bytes, and defaults to 1.  An
+    :exc:`OverflowError` is raised if the integer is not representable with
+    the given number of bytes.
 
     The *byteorder* argument determines 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 *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 :data:`sys.byteorder` as the byte order
-    value.
+    integer, and defaults to ``"big"``.  If *byteorder* is
+    ``"big"``, 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 byte array.
 
     The *signed* argument determines whether two's complement is used to
     represent the integer.  If *signed* is ``False`` and a negative integer is
     given, an :exc:`OverflowError` is raised. The default value for *signed*
     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::
 
-        def to_bytes(n, length, byteorder, signed=False):
+        def to_bytes(n, length=1, byteorder='big', signed=False):
             if byteorder == 'little':
                 order = range(length)
             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)
 
     .. 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.
 
@@ -562,18 +570,18 @@ class`. In addition, it provides a few more methods:
     iterable producing bytes.
 
     The *byteorder* argument determines 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 *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 :data:`sys.byteorder` as the byte order
-    value.
+    integer, and defaults to ``"big"``.  If *byteorder* is
+    ``"big"``, 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 byte array.  To request the native byte order of the host
+    system, use :data:`sys.byteorder` as the byte order value.
 
     The *signed* argument indicates whether two's complement is used to
     represent the integer.
 
     Equivalent to::
 
-        def from_bytes(bytes, byteorder, signed=False):
+        def from_bytes(bytes, byteorder='big', signed=False):
             if byteorder == 'little':
                 little_ordered = list(bytes)
             elif byteorder == 'big':
@@ -588,6 +596,8 @@ class`. In addition, it provides a few more methods:
             return n
 
     .. versionadded:: 3.2
+    .. versionchanged:: 3.11
+       Added default argument value for ``byteorder``.
 
 .. method:: int.as_integer_ratio()
 
diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py
index 1d274521aeb4f..6936f093e3db1 100644
--- a/Lib/test/test_call.py
+++ b/Lib/test/test_call.py
@@ -39,7 +39,7 @@ def test_varargs2(self):
         self.assertRaisesRegex(TypeError, msg, {}.__contains__, 0, 1)
 
     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)
 
     def test_varargs1min(self):
diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py
index 1de75bf86c0ae..e15bf8b418676 100644
--- a/Lib/test/test_long.py
+++ b/Lib/test/test_long.py
@@ -1117,9 +1117,18 @@ def equivalent_python(n, length, byteorder, signed=False):
                         expected)
                 except Exception as err:
                     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
 
+                # 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:
                     self.assertEqual(
                         equivalent_python(
@@ -1240,9 +1249,20 @@ def equivalent_python(byte_array, byteorder, signed=False):
                         expected)
                 except Exception as err:
                     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
 
+                # 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:
                     self.assertEqual(
                         equivalent_python(test, byteorder, signed=signed),
diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-09-09-15-05-17.bpo-45155.JRw9TG.rst b/Misc/NEWS.d/next/Core and Builtins/2021-09-09-15-05-17.bpo-45155.JRw9TG.rst
new file mode 100644
index 0000000000000..eab023bf89471
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2021-09-09-15-05-17.bpo-45155.JRw9TG.rst	
@@ -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.
diff --git a/Objects/clinic/longobject.c.h b/Objects/clinic/longobject.c.h
index 4bd47b116f883..d50c4af8096fa 100644
--- a/Objects/clinic/longobject.c.h
+++ b/Objects/clinic/longobject.c.h
@@ -226,20 +226,21 @@ int_as_integer_ratio(PyObject *self, PyObject *Py_UNUSED(ignored))
 }
 
 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"
 "Return an array of bytes representing an integer.\n"
 "\n"
 "  length\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"
 "    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"
 "    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"
-"    `sys.byteorder\' as the byte order value.\n"
+"    `sys.byteorder\' as the byte order value.  Default is to use \'big\'.\n"
 "  signed\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"
@@ -259,35 +260,49 @@ int_to_bytes(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *
     static const char * const _keywords[] = {"length", "byteorder", "signed", NULL};
     static _PyArg_Parser _parser = {NULL, _keywords, "to_bytes", 0};
     PyObject *argsbuf[3];
-    Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2;
-    Py_ssize_t length;
-    PyObject *byteorder;
+    Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
+    Py_ssize_t length = 1;
+    PyObject *byteorder = NULL;
     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) {
         goto exit;
     }
-    {
-        Py_ssize_t ival = -1;
-        PyObject *iobj = _PyNumber_Index(args[0]);
-        if (iobj != NULL) {
-            ival = PyLong_AsSsize_t(iobj);
-            Py_DECREF(iobj);
+    if (!noptargs) {
+        goto skip_optional_pos;
+    }
+    if (args[0]) {
+        {
+            Py_ssize_t ival = -1;
+            PyObject *iobj = _PyNumber_Index(args[0]);
+            if (iobj != NULL) {
+                ival = PyLong_AsSsize_t(iobj);
+                Py_DECREF(iobj);
+            }
+            if (ival == -1 && PyErr_Occurred()) {
+                goto exit;
+            }
+            length = ival;
         }
-        if (ival == -1 && PyErr_Occurred()) {
-            goto exit;
+        if (!--noptargs) {
+            goto skip_optional_pos;
         }
-        length = ival;
-    }
-    if (!PyUnicode_Check(args[1])) {
-        _PyArg_BadArgument("to_bytes", "argument 'byteorder'", "str", args[1]);
-        goto exit;
     }
-    if (PyUnicode_READY(args[1]) == -1) {
-        goto exit;
+    if (args[1]) {
+        if (!PyUnicode_Check(args[1])) {
+            _PyArg_BadArgument("to_bytes", "argument 'byteorder'", "str", args[1]);
+            goto exit;
+        }
+        if (PyUnicode_READY(args[1]) == -1) {
+            goto exit;
+        }
+        byteorder = args[1];
+        if (!--noptargs) {
+            goto skip_optional_pos;
+        }
     }
-    byteorder = args[1];
+skip_optional_pos:
     if (!noptargs) {
         goto skip_optional_kwonly;
     }
@@ -303,7 +318,7 @@ int_to_bytes(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *
 }
 
 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"
 "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"
 "    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"
-"    `sys.byteorder\' as the byte order value.\n"
+"    `sys.byteorder\' as the byte order value.  Default is to use \'big\'.\n"
 "  signed\n"
 "    Indicates whether two\'s complement is used to represent the integer.");
 
@@ -336,24 +351,33 @@ int_from_bytes(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyOb
     static const char * const _keywords[] = {"bytes", "byteorder", "signed", NULL};
     static _PyArg_Parser _parser = {NULL, _keywords, "from_bytes", 0};
     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 *byteorder;
+    PyObject *byteorder = NULL;
     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) {
         goto exit;
     }
     bytes_obj = args[0];
-    if (!PyUnicode_Check(args[1])) {
-        _PyArg_BadArgument("from_bytes", "argument 'byteorder'", "str", args[1]);
-        goto exit;
+    if (!noptargs) {
+        goto skip_optional_pos;
     }
-    if (PyUnicode_READY(args[1]) == -1) {
-        goto exit;
+    if (args[1]) {
+        if (!PyUnicode_Check(args[1])) {
+            _PyArg_BadArgument("from_bytes", "argument 'byteorder'", "str", args[1]);
+            goto exit;
+        }
+        if (PyUnicode_READY(args[1]) == -1) {
+            goto exit;
+        }
+        byteorder = args[1];
+        if (!--noptargs) {
+            goto skip_optional_pos;
+        }
     }
-    byteorder = args[1];
+skip_optional_pos:
     if (!noptargs) {
         goto skip_optional_kwonly;
     }
@@ -367,4 +391,4 @@ int_from_bytes(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyOb
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=ea18e51af5b53591 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=16a375d93769b227 input=a9049054013a1b77]*/
diff --git a/Objects/longobject.c b/Objects/longobject.c
index 3b6df12212c8d..33fea6491b73d 100644
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -5521,15 +5521,16 @@ int_as_integer_ratio_impl(PyObject *self)
 /*[clinic input]
 int.to_bytes
 
-    length: Py_ssize_t
+    length: Py_ssize_t = 1
         Length of bytes object to use.  An OverflowError is raised if the
-        integer is not representable with the given number of bytes.
-    byteorder: unicode
+        integer is not representable with the given number of bytes.  Default
+        is length 1.
+    byteorder: unicode(c_default="NULL") = "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
         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
-        `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
         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 *
 int_to_bytes_impl(PyObject *self, Py_ssize_t length, PyObject *byteorder,
                   int is_signed)
-/*[clinic end generated code: output=89c801df114050a3 input=ddac63f4c7bf414c]*/
+/*[clinic end generated code: output=89c801df114050a3 input=d42ecfb545039d71]*/
 {
     int little_endian;
     PyObject *bytes;
 
-    if (_PyUnicode_EqualToASCIIId(byteorder, &PyId_little))
+    if (byteorder == NULL)
+        little_endian = 0;
+    else if (_PyUnicode_EqualToASCIIId(byteorder, &PyId_little))
         little_endian = 1;
     else if (_PyUnicode_EqualToASCIIId(byteorder, &PyId_big))
         little_endian = 0;
@@ -5586,12 +5589,12 @@ int.from_bytes
         support the buffer protocol or be an iterable object producing bytes.
         Bytes and bytearray are examples of built-in objects that support the
         buffer protocol.
-    byteorder: unicode
+    byteorder: unicode(c_default="NULL") = "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
         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
-        `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
         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 *
 int_from_bytes_impl(PyTypeObject *type, PyObject *bytes_obj,
                     PyObject *byteorder, int is_signed)
-/*[clinic end generated code: output=efc5d68e31f9314f input=cdf98332b6a821b0]*/
+/*[clinic end generated code: output=efc5d68e31f9314f input=33326dccdd655553]*/
 {
     int little_endian;
     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;
     else if (_PyUnicode_EqualToASCIIId(byteorder, &PyId_big))
         little_endian = 0;



More information about the Python-checkins mailing list