mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
gh-129813, PEP 782: Add PyBytesWriter_Format() (#138824)
Modify PyBytes_FromFormatV() to use the public PyBytesWriter API rather than the _PyBytesWriter private API.
This commit is contained in:
parent
419441a6e1
commit
c3fca5d478
7 changed files with 130 additions and 30 deletions
|
|
@ -307,6 +307,15 @@ High-level API
|
||||||
On success, return ``0``.
|
On success, return ``0``.
|
||||||
On error, set an exception and return ``-1``.
|
On error, set an exception and return ``-1``.
|
||||||
|
|
||||||
|
.. c:function:: int PyBytesWriter_Format(PyBytesWriter *writer, const char *format, ...)
|
||||||
|
|
||||||
|
Similar to :c:func:`PyBytes_FromFormat`, but write the output directly at
|
||||||
|
the writer end. Grow the writer internal buffer on demand. Then add the
|
||||||
|
written size to the writer size.
|
||||||
|
|
||||||
|
On success, return ``0``.
|
||||||
|
On error, set an exception and return ``-1``.
|
||||||
|
|
||||||
|
|
||||||
Getters
|
Getters
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
|
|
|
||||||
|
|
@ -714,6 +714,7 @@ New features
|
||||||
* :c:func:`PyBytesWriter_FinishWithPointer`
|
* :c:func:`PyBytesWriter_FinishWithPointer`
|
||||||
* :c:func:`PyBytesWriter_FinishWithSize`
|
* :c:func:`PyBytesWriter_FinishWithSize`
|
||||||
* :c:func:`PyBytesWriter_Finish`
|
* :c:func:`PyBytesWriter_Finish`
|
||||||
|
* :c:func:`PyBytesWriter_Format`
|
||||||
* :c:func:`PyBytesWriter_GetData`
|
* :c:func:`PyBytesWriter_GetData`
|
||||||
* :c:func:`PyBytesWriter_GetSize`
|
* :c:func:`PyBytesWriter_GetSize`
|
||||||
* :c:func:`PyBytesWriter_GrowAndUpdatePointer`
|
* :c:func:`PyBytesWriter_GrowAndUpdatePointer`
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,10 @@ PyAPI_FUNC(int) PyBytesWriter_WriteBytes(
|
||||||
PyBytesWriter *writer,
|
PyBytesWriter *writer,
|
||||||
const void *bytes,
|
const void *bytes,
|
||||||
Py_ssize_t size);
|
Py_ssize_t size);
|
||||||
|
PyAPI_FUNC(int) PyBytesWriter_Format(
|
||||||
|
PyBytesWriter *writer,
|
||||||
|
const char *format,
|
||||||
|
...);
|
||||||
|
|
||||||
PyAPI_FUNC(int) PyBytesWriter_Resize(
|
PyAPI_FUNC(int) PyBytesWriter_Resize(
|
||||||
PyBytesWriter *writer,
|
PyBytesWriter *writer,
|
||||||
|
|
|
||||||
|
|
@ -361,12 +361,26 @@ def test_resize(self):
|
||||||
writer.resize(len(b'number=123456'), b'456')
|
writer.resize(len(b'number=123456'), b'456')
|
||||||
self.assertEqual(writer.finish(), self.result_type(b'number=123456'))
|
self.assertEqual(writer.finish(), self.result_type(b'number=123456'))
|
||||||
|
|
||||||
|
def test_format_i(self):
|
||||||
|
# Test PyBytesWriter_Format()
|
||||||
|
writer = self.create_writer()
|
||||||
|
writer.format_i(b'x=%i', 123456)
|
||||||
|
self.assertEqual(writer.finish(), self.result_type(b'x=123456'))
|
||||||
|
|
||||||
|
writer = self.create_writer()
|
||||||
|
writer.format_i(b'x=%i, ', 123)
|
||||||
|
writer.format_i(b'y=%i', 456)
|
||||||
|
self.assertEqual(writer.finish(), self.result_type(b'x=123, y=456'))
|
||||||
|
|
||||||
def test_example_abc(self):
|
def test_example_abc(self):
|
||||||
self.assertEqual(_testcapi.byteswriter_abc(), b'abc')
|
self.assertEqual(_testcapi.byteswriter_abc(), b'abc')
|
||||||
|
|
||||||
def test_example_resize(self):
|
def test_example_resize(self):
|
||||||
self.assertEqual(_testcapi.byteswriter_resize(), b'Hello World')
|
self.assertEqual(_testcapi.byteswriter_resize(), b'Hello World')
|
||||||
|
|
||||||
|
def test_example_highlevel(self):
|
||||||
|
self.assertEqual(_testcapi.byteswriter_highlevel(), b'Hello World!')
|
||||||
|
|
||||||
|
|
||||||
class ByteArrayWriterTest(BytesWriterTest):
|
class ByteArrayWriterTest(BytesWriterTest):
|
||||||
result_type = bytearray
|
result_type = bytearray
|
||||||
|
|
@ -374,5 +388,6 @@ class ByteArrayWriterTest(BytesWriterTest):
|
||||||
def create_writer(self, alloc=0, string=b''):
|
def create_writer(self, alloc=0, string=b''):
|
||||||
return _testcapi.PyBytesWriter(alloc, string, 1)
|
return _testcapi.PyBytesWriter(alloc, string, 1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ Implement :pep:`782`, the :c:type:`PyBytesWriter` API. Add functions:
|
||||||
* :c:func:`PyBytesWriter_FinishWithPointer`
|
* :c:func:`PyBytesWriter_FinishWithPointer`
|
||||||
* :c:func:`PyBytesWriter_FinishWithSize`
|
* :c:func:`PyBytesWriter_FinishWithSize`
|
||||||
* :c:func:`PyBytesWriter_Finish`
|
* :c:func:`PyBytesWriter_Finish`
|
||||||
|
* :c:func:`PyBytesWriter_Format`
|
||||||
* :c:func:`PyBytesWriter_GetData`
|
* :c:func:`PyBytesWriter_GetData`
|
||||||
* :c:func:`PyBytesWriter_GetSize`
|
* :c:func:`PyBytesWriter_GetSize`
|
||||||
* :c:func:`PyBytesWriter_GrowAndUpdatePointer`
|
* :c:func:`PyBytesWriter_GrowAndUpdatePointer`
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,27 @@ writer_write_bytes(PyObject *self_raw, PyObject *args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
writer_format_i(PyObject *self_raw, PyObject *args)
|
||||||
|
{
|
||||||
|
WriterObject *self = (WriterObject *)self_raw;
|
||||||
|
if (writer_check(self) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *format;
|
||||||
|
int value;
|
||||||
|
if (!PyArg_ParseTuple(args, "yi", &format, &value)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyBytesWriter_Format(self->writer, format, value) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
writer_resize(PyObject *self_raw, PyObject *args)
|
writer_resize(PyObject *self_raw, PyObject *args)
|
||||||
{
|
{
|
||||||
|
|
@ -241,6 +262,7 @@ writer_finish_with_size(PyObject *self_raw, PyObject *args)
|
||||||
|
|
||||||
static PyMethodDef writer_methods[] = {
|
static PyMethodDef writer_methods[] = {
|
||||||
{"write_bytes", _PyCFunction_CAST(writer_write_bytes), METH_VARARGS},
|
{"write_bytes", _PyCFunction_CAST(writer_write_bytes), METH_VARARGS},
|
||||||
|
{"format_i", _PyCFunction_CAST(writer_format_i), METH_VARARGS},
|
||||||
{"resize", _PyCFunction_CAST(writer_resize), METH_VARARGS},
|
{"resize", _PyCFunction_CAST(writer_resize), METH_VARARGS},
|
||||||
{"get_size", _PyCFunction_CAST(writer_get_size), METH_NOARGS},
|
{"get_size", _PyCFunction_CAST(writer_get_size), METH_NOARGS},
|
||||||
{"finish", _PyCFunction_CAST(writer_finish), METH_NOARGS},
|
{"finish", _PyCFunction_CAST(writer_finish), METH_NOARGS},
|
||||||
|
|
@ -309,11 +331,33 @@ byteswriter_resize(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
byteswriter_highlevel(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
|
||||||
|
{
|
||||||
|
PyBytesWriter *writer = PyBytesWriter_Create(0);
|
||||||
|
if (writer == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (PyBytesWriter_WriteBytes(writer, "Hello", -1) < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (PyBytesWriter_Format(writer, " %s!", "World") < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
return PyBytesWriter_Finish(writer);
|
||||||
|
|
||||||
|
error:
|
||||||
|
PyBytesWriter_Discard(writer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef test_methods[] = {
|
static PyMethodDef test_methods[] = {
|
||||||
{"bytes_resize", bytes_resize, METH_VARARGS},
|
{"bytes_resize", bytes_resize, METH_VARARGS},
|
||||||
{"bytes_join", bytes_join, METH_VARARGS},
|
{"bytes_join", bytes_join, METH_VARARGS},
|
||||||
{"byteswriter_abc", byteswriter_abc, METH_NOARGS},
|
{"byteswriter_abc", byteswriter_abc, METH_NOARGS},
|
||||||
{"byteswriter_resize", byteswriter_resize, METH_NOARGS},
|
{"byteswriter_resize", byteswriter_resize, METH_NOARGS},
|
||||||
|
{"byteswriter_highlevel", byteswriter_highlevel, METH_NOARGS},
|
||||||
{NULL},
|
{NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -196,10 +196,11 @@ PyBytes_FromString(const char *str)
|
||||||
return (PyObject *) op;
|
return (PyObject *) op;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *
|
|
||||||
PyBytes_FromFormatV(const char *format, va_list vargs)
|
static char*
|
||||||
|
bytes_fromformat(PyBytesWriter *writer, Py_ssize_t writer_pos,
|
||||||
|
const char *format, va_list vargs)
|
||||||
{
|
{
|
||||||
char *s;
|
|
||||||
const char *f;
|
const char *f;
|
||||||
const char *p;
|
const char *p;
|
||||||
Py_ssize_t prec;
|
Py_ssize_t prec;
|
||||||
|
|
@ -213,21 +214,20 @@ PyBytes_FromFormatV(const char *format, va_list vargs)
|
||||||
Longest 64-bit pointer representation:
|
Longest 64-bit pointer representation:
|
||||||
"0xffffffffffffffff\0" (19 bytes). */
|
"0xffffffffffffffff\0" (19 bytes). */
|
||||||
char buffer[21];
|
char buffer[21];
|
||||||
_PyBytesWriter writer;
|
|
||||||
|
|
||||||
_PyBytesWriter_Init(&writer);
|
char *s = (char*)PyBytesWriter_GetData(writer) + writer_pos;
|
||||||
|
|
||||||
s = _PyBytesWriter_Alloc(&writer, strlen(format));
|
#define WRITE_BYTES_LEN(str, len_expr) \
|
||||||
if (s == NULL)
|
|
||||||
return NULL;
|
|
||||||
writer.overallocate = 1;
|
|
||||||
|
|
||||||
#define WRITE_BYTES(str) \
|
|
||||||
do { \
|
do { \
|
||||||
s = _PyBytesWriter_WriteBytes(&writer, s, (str), strlen(str)); \
|
size_t len = (len_expr); \
|
||||||
if (s == NULL) \
|
s = PyBytesWriter_GrowAndUpdatePointer(writer, len, s); \
|
||||||
|
if (s == NULL) { \
|
||||||
goto error; \
|
goto error; \
|
||||||
|
} \
|
||||||
|
memcpy(s, (str), len); \
|
||||||
|
s += len; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
#define WRITE_BYTES(str) WRITE_BYTES_LEN(str, strlen(str))
|
||||||
|
|
||||||
for (f = format; *f; f++) {
|
for (f = format; *f; f++) {
|
||||||
if (*f != '%') {
|
if (*f != '%') {
|
||||||
|
|
@ -268,10 +268,6 @@ PyBytes_FromFormatV(const char *format, va_list vargs)
|
||||||
++f;
|
++f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* subtract bytes preallocated for the format string
|
|
||||||
(ex: 2 for "%s") */
|
|
||||||
writer.min_size -= (f - p + 1);
|
|
||||||
|
|
||||||
switch (*f) {
|
switch (*f) {
|
||||||
case 'c':
|
case 'c':
|
||||||
{
|
{
|
||||||
|
|
@ -282,7 +278,6 @@ PyBytes_FromFormatV(const char *format, va_list vargs)
|
||||||
"expects an integer in range [0; 255]");
|
"expects an integer in range [0; 255]");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
writer.min_size++;
|
|
||||||
*s++ = (unsigned char)c;
|
*s++ = (unsigned char)c;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -341,9 +336,7 @@ PyBytes_FromFormatV(const char *format, va_list vargs)
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s = _PyBytesWriter_WriteBytes(&writer, s, p, i);
|
WRITE_BYTES_LEN(p, i);
|
||||||
if (s == NULL)
|
|
||||||
goto error;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -362,31 +355,45 @@ PyBytes_FromFormatV(const char *format, va_list vargs)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '%':
|
case '%':
|
||||||
writer.min_size++;
|
|
||||||
*s++ = '%';
|
*s++ = '%';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (*f == 0) {
|
|
||||||
/* fix min_size if we reached the end of the format string */
|
|
||||||
writer.min_size++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* invalid format string: copy unformatted string and exit */
|
/* invalid format string: copy unformatted string and exit */
|
||||||
WRITE_BYTES(p);
|
WRITE_BYTES(p);
|
||||||
return _PyBytesWriter_Finish(&writer, s);
|
return s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef WRITE_BYTES
|
#undef WRITE_BYTES
|
||||||
|
#undef WRITE_BYTES_LEN
|
||||||
|
|
||||||
return _PyBytesWriter_Finish(&writer, s);
|
return s;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
_PyBytesWriter_Dealloc(&writer);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
PyBytes_FromFormatV(const char *format, va_list vargs)
|
||||||
|
{
|
||||||
|
Py_ssize_t alloc = strlen(format);
|
||||||
|
PyBytesWriter *writer = PyBytesWriter_Create(alloc);
|
||||||
|
if (writer == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *s = bytes_fromformat(writer, 0, format, vargs);
|
||||||
|
if (s == NULL) {
|
||||||
|
PyBytesWriter_Discard(writer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PyBytesWriter_FinishWithPointer(writer, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
PyBytes_FromFormat(const char *format, ...)
|
PyBytes_FromFormat(const char *format, ...)
|
||||||
{
|
{
|
||||||
|
|
@ -399,6 +406,7 @@ PyBytes_FromFormat(const char *format, ...)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Helpers for formatstring */
|
/* Helpers for formatstring */
|
||||||
|
|
||||||
Py_LOCAL_INLINE(PyObject *)
|
Py_LOCAL_INLINE(PyObject *)
|
||||||
|
|
@ -4048,3 +4056,21 @@ PyBytesWriter_WriteBytes(PyBytesWriter *writer,
|
||||||
memcpy(buf + pos, bytes, size);
|
memcpy(buf + pos, bytes, size);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PyBytesWriter_Format(PyBytesWriter *writer, const char *format, ...)
|
||||||
|
{
|
||||||
|
Py_ssize_t pos = writer->size;
|
||||||
|
if (PyBytesWriter_Grow(writer, strlen(format)) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_list vargs;
|
||||||
|
va_start(vargs, format);
|
||||||
|
char *buf = bytes_fromformat(writer, pos, format, vargs);
|
||||||
|
va_end(vargs);
|
||||||
|
|
||||||
|
Py_ssize_t size = buf - byteswriter_data(writer);
|
||||||
|
return PyBytesWriter_Resize(writer, size);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue