mirror of https://github.com/python/cpython
gh-121115: Skip __index__ in PyLong_AsNativeBytes by default (GH-121118)
(cherry picked from commit 2894aa14f2
)
Co-authored-by: Steve Dower <steve.dower@python.org>
This commit is contained in:
parent
99de20d729
commit
58a3c3c0ad
|
@ -405,14 +405,13 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
|
|||
|
||||
Passing zero to *n_bytes* will return the size of a buffer that would
|
||||
be large enough to hold the value. This may be larger than technically
|
||||
necessary, but not unreasonably so.
|
||||
necessary, but not unreasonably so. If *n_bytes=0*, *buffer* may be
|
||||
``NULL``.
|
||||
|
||||
.. note::
|
||||
|
||||
Passing *n_bytes=0* to this function is not an accurate way to determine
|
||||
the bit length of a value.
|
||||
|
||||
If *n_bytes=0*, *buffer* may be ``NULL``.
|
||||
the bit length of the value.
|
||||
|
||||
To get at the entire Python value of an unknown size, the function can be
|
||||
called twice: first to determine the buffer size, then to fill it::
|
||||
|
@ -462,6 +461,7 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
|
|||
.. c:macro:: Py_ASNATIVEBYTES_NATIVE_ENDIAN ``3``
|
||||
.. c:macro:: Py_ASNATIVEBYTES_UNSIGNED_BUFFER ``4``
|
||||
.. c:macro:: Py_ASNATIVEBYTES_REJECT_NEGATIVE ``8``
|
||||
.. c:macro:: Py_ASNATIVEBYTES_ALLOW_INDEX ``16``
|
||||
============================================= ======
|
||||
|
||||
Specifying ``Py_ASNATIVEBYTES_NATIVE_ENDIAN`` will override any other endian
|
||||
|
@ -483,6 +483,13 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
|
|||
provided there is enough space for at least one sign bit, regardless of
|
||||
whether ``Py_ASNATIVEBYTES_UNSIGNED_BUFFER`` was specified.
|
||||
|
||||
If ``Py_ASNATIVEBYTES_ALLOW_INDEX`` is specified and a non-integer value is
|
||||
passed, its :meth:`~object.__index__` method will be called first. This may
|
||||
result in Python code executing and other threads being allowed to run, which
|
||||
could cause changes to other objects or values in use. When *flags* is
|
||||
``-1``, this option is not set, and non-integer values will raise
|
||||
:exc:`TypeError`.
|
||||
|
||||
.. note::
|
||||
|
||||
With the default *flags* (``-1``, or *UNSIGNED_BUFFER* without
|
||||
|
|
|
@ -10,6 +10,7 @@ PyAPI_FUNC(PyObject*) PyLong_FromUnicodeObject(PyObject *u, int base);
|
|||
#define Py_ASNATIVEBYTES_NATIVE_ENDIAN 3
|
||||
#define Py_ASNATIVEBYTES_UNSIGNED_BUFFER 4
|
||||
#define Py_ASNATIVEBYTES_REJECT_NEGATIVE 8
|
||||
#define Py_ASNATIVEBYTES_ALLOW_INDEX 16
|
||||
|
||||
/* PyLong_AsNativeBytes: Copy the integer value to a native variable.
|
||||
buffer points to the first byte of the variable.
|
||||
|
@ -20,8 +21,10 @@ PyAPI_FUNC(PyObject*) PyLong_FromUnicodeObject(PyObject *u, int base);
|
|||
* 2 - native endian
|
||||
* 4 - unsigned destination (e.g. don't reject copying 255 into one byte)
|
||||
* 8 - raise an exception for negative inputs
|
||||
If flags is -1 (all bits set), native endian is used and value truncation
|
||||
behaves most like C (allows negative inputs and allow MSB set).
|
||||
* 16 - call __index__ on non-int types
|
||||
If flags is -1 (all bits set), native endian is used, value truncation
|
||||
behaves most like C (allows negative inputs and allow MSB set), and non-int
|
||||
objects will raise a TypeError.
|
||||
Big endian mode will write the most significant byte into the address
|
||||
directly referenced by buffer; little endian will write the least significant
|
||||
byte into that address.
|
||||
|
|
|
@ -496,8 +496,9 @@ class LongTests(unittest.TestCase):
|
|||
"PyLong_AsNativeBytes(v, <unknown>, 0, -1)")
|
||||
self.assertEqual(buffer, b"\x5a",
|
||||
"buffer overwritten when it should not have been")
|
||||
# Also check via the __index__ path
|
||||
self.assertEqual(expect, asnativebytes(Index(v), buffer, 0, -1),
|
||||
# Also check via the __index__ path.
|
||||
# We pass Py_ASNATIVEBYTES_NATIVE_ENDIAN | ALLOW_INDEX
|
||||
self.assertEqual(expect, asnativebytes(Index(v), buffer, 0, 3 | 16),
|
||||
"PyLong_AsNativeBytes(Index(v), <unknown>, 0, -1)")
|
||||
self.assertEqual(buffer, b"\x5a",
|
||||
"buffer overwritten when it should not have been")
|
||||
|
@ -607,6 +608,12 @@ class LongTests(unittest.TestCase):
|
|||
with self.assertRaises(ValueError):
|
||||
asnativebytes(-1, buffer, 0, 8)
|
||||
|
||||
# Ensure omitting Py_ASNATIVEBYTES_ALLOW_INDEX raises on __index__ value
|
||||
with self.assertRaises(TypeError):
|
||||
asnativebytes(Index(1), buffer, 0, -1)
|
||||
with self.assertRaises(TypeError):
|
||||
asnativebytes(Index(1), buffer, 0, 3)
|
||||
|
||||
# Check a few error conditions. These are validated in code, but are
|
||||
# unspecified in docs, so if we make changes to the implementation, it's
|
||||
# fine to just update these tests rather than preserve the behaviour.
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
:c:func:`PyLong_AsNativeBytes` no longer uses :meth:`~object.__index__`
|
||||
methods by default. The ``Py_ASNATIVEBYTES_ALLOW_INDEX`` flag has been added
|
||||
to allow it.
|
|
@ -1116,13 +1116,17 @@ PyLong_AsNativeBytes(PyObject* vv, void* buffer, Py_ssize_t n, int flags)
|
|||
if (PyLong_Check(vv)) {
|
||||
v = (PyLongObject *)vv;
|
||||
}
|
||||
else {
|
||||
else if (flags != -1 && (flags & Py_ASNATIVEBYTES_ALLOW_INDEX)) {
|
||||
v = (PyLongObject *)_PyNumber_Index(vv);
|
||||
if (v == NULL) {
|
||||
return -1;
|
||||
}
|
||||
do_decref = 1;
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_TypeError, "expect int, got %T", vv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((flags != -1 && (flags & Py_ASNATIVEBYTES_REJECT_NEGATIVE))
|
||||
&& _PyLong_IsNegative(v)) {
|
||||
|
|
Loading…
Reference in New Issue