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::
.. 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
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
#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.
* 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.
"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")
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.
--- /dev/null
+: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.
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)) {