Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions Doc/c-api/bytes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ called with a non-bytes parameter.
Resize a bytes object. *newsize* will be the new length of the bytes object.
You can think of it as creating a new bytes object and destroying the old
one, only more efficiently.

Pass the address of an
existing bytes object as an lvalue (it may be written into), and the new size
desired. On success, *\*bytes* holds the resized bytes object and ``0`` is
Expand All @@ -239,6 +240,11 @@ called with a non-bytes parameter.
*\*bytes* is set to ``NULL``, :exc:`MemoryError` is set, and ``-1`` is
returned.

While bytes objects are usually immutable in Python, this special C API
allows mutating a bytes object in-place. The returned bytes object can still
be mutated using :c:func:`PyBytesWriter_GetData`; except if *newsize* is
zero in which case it returns the immutable empty bytes string.

.. soft-deprecated:: 3.15
Use the :c:type:`PyBytesWriter` API instead.

Expand Down Expand Up @@ -290,10 +296,10 @@ object.

.. c:type:: PyBytesWriter

A bytes writer instance.
A bytes writer object.

The API is **not thread safe**: a writer should only be used by a single
thread at the same time.
The API is **not thread safe**. A :c:type:`PyBytesWriter` object must only
be used by a single thread, it must not be shared between threads.

The instance must be destroyed by :c:func:`PyBytesWriter_Finish` on
success, or :c:func:`PyBytesWriter_Discard` on error.
Expand Down Expand Up @@ -429,7 +435,7 @@ Low-level API
On success, return ``0``.
On error, set an exception and return ``-1``.

*size* can be negative to shrink the writer.
*grow* can be negative to shrink the writer.

.. c:function:: void* PyBytesWriter_GrowAndUpdatePointer(PyBytesWriter *writer, Py_ssize_t size, void *buf)

Expand Down
4 changes: 4 additions & 0 deletions Include/internal/pycore_bytesobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ PyAPI_FUNC(PyObject *) _PyBytes_Repeat(PyObject *self, Py_ssize_t n);

extern int _PyBytes_ResizeKeepOnError(PyObject **pv, Py_ssize_t newsize);

#ifndef NDEBUG
extern int _PyBytes_IsMutable(PyObject *obj);
#endif

/* --- PyBytesWriter ------------------------------------------------------ */

struct PyBytesWriter {
Expand Down
7 changes: 4 additions & 3 deletions Lib/test/test_bytes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1130,13 +1130,14 @@ def test_translate(self):
self.assertRaises(ValueError, b.translate, bytes(range(255)))

c = b.translate(rosetta, b'hello')
self.assertEqual(b, b'hello')
self.assertIsInstance(c, self.type2test)
self.assertEqual(c, b'')
self.assertEqual(type(c), self.type2test)

c = b.translate(rosetta)
d = b.translate(rosetta, b'')
self.assertEqual(c, d)
self.assertEqual(c, b'helle')
self.assertEqual(type(c), self.type2test)
self.assertEqual(d, b'helle')

c = b.translate(rosetta, b'l')
self.assertEqual(c, b'hee')
Expand Down
22 changes: 20 additions & 2 deletions Lib/test/test_capi/test_bytes.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,24 @@ def test_grow(self):
writer.grow(0) # noop
self.assertEqual(writer.finish(), b'number=123')

for size in (self.SMALL_BUFFER, self.LARGE_BUFFER):
with self.subTest(size=size):
# Truncate the last byte
data = b'x' * size
writer = self.create_writer(size)
writer.write(0, data)
self.assertEqual(writer.get_data(), data)
writer.grow(-1)
self.assertEqual(writer.get_data(), data[:-1])
self.assertEqual(writer.finish(), data[:-1])

# Make the buffer empty
writer = self.create_writer(size)
writer.write(0, data)
writer.grow(-size)
self.assertEqual(writer.get_data(), b'')
self.assertEqual(writer.finish(), b'')

# Switch from small buffer to large buffer
writer = self.create_writer()
small, large = self.SMALL_BUFFER, self.LARGE_BUFFER
Expand All @@ -476,8 +494,8 @@ def test_grow(self):
with self.subTest(size=size):
writer = self.create_writer()
writer.write_bytes(b'x' * size, -1)
with self.assertRaisesRegex(ValueError, 'size must be >= 0'):
writer.grow(-1)
with self.assertRaisesRegex(ValueError, 'invalid size'):
writer.grow(-size - 1)
with self.assertRaises(MemoryError):
writer.grow(_testcapi.PY_SSIZE_T_MAX)
self.assertEqual(writer.finish(), b'x' * size)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Remove the accidental acceptance of :class:`bytes` in the C implementation of
:mod:`xml.etree.ElementTree`, a leftover of the Python 2 to Python 3 migration,
for paths of :meth:`~xml.etree.ElementTree.Element.find`
and similar methods, for the tag of :meth:`~xml.etree.ElementTree.Element.iter`,
and for the names of events of :class:`~xml.etree.ElementTree.XMLPullParser`
and :func:`~xml.etree.ElementTree.iterparse`. It now raises the same
exceptions as the Python implementation.
47 changes: 6 additions & 41 deletions Modules/_elementtree.c
Original file line number Diff line number Diff line change
Expand Up @@ -1230,24 +1230,6 @@ checkpath(PyObject* tag)
}
return 0;
}
if (PyBytes_Check(tag)) {
const char *p = PyBytes_AS_STRING(tag);
const Py_ssize_t len = PyBytes_GET_SIZE(tag);
if (len >= 3 && p[0] == '{' && (
p[1] == '}' || (p[1] == '*' && p[2] == '}'))) {
/* wildcard: '{}tag' or '{*}tag' */
return 1;
}
for (i = 0; i < len; i++) {
if (p[i] == '{')
check = 0;
else if (p[i] == '}')
check = 1;
else if (check && PATHCHAR(p[i]))
return 1;
}
return 0;
}

return 1; /* unknown type; might be path expression */
}
Expand Down Expand Up @@ -1552,10 +1534,6 @@ _elementtree_Element_iter_impl(ElementObject *self, PyTypeObject *cls,
if (PyUnicode_GET_LENGTH(tag) == 1 && PyUnicode_READ_CHAR(tag, 0) == '*')
tag = Py_None;
}
else if (PyBytes_Check(tag)) {
if (PyBytes_GET_SIZE(tag) == 1 && *PyBytes_AS_STRING(tag) == '*')
tag = Py_None;
}

elementtreestate *st = get_elementtree_state_by_cls(cls);
return create_elementiter(st, self, tag, 0);
Expand Down Expand Up @@ -2935,17 +2913,7 @@ treebuilder_handle_data(TreeBuilderObject* self, PyObject* data)
self->data = Py_NewRef(data);
} else {
/* more than one item; use a list to collect items */
if (PyBytes_CheckExact(self->data)
&& _PyObject_IsUniquelyReferenced(self->data)
&& PyBytes_CheckExact(data) && PyBytes_GET_SIZE(data) == 1) {
/* XXX this code path unused in Python 3? */
/* expat often generates single character data sections; handle
the most common case by resizing the existing string... */
Py_ssize_t size = PyBytes_GET_SIZE(self->data);
if (_PyBytes_Resize(&self->data, size + 1) < 0)
return NULL;
PyBytes_AS_STRING(self->data)[size] = PyBytes_AS_STRING(data)[0];
} else if (PyList_CheckExact(self->data)) {
if (PyList_CheckExact(self->data)) {
if (PyList_Append(self->data, data) < 0)
return NULL;
} else {
Expand Down Expand Up @@ -4363,18 +4331,14 @@ _elementtree_XMLParser__setevents_impl(XMLParserObject *self,

for (i = 0; i < PySequence_Fast_GET_SIZE(events_seq); ++i) {
PyObject *event_name_obj = PySequence_Fast_GET_ITEM(events_seq, i);
const char *event_name = NULL;
if (PyUnicode_Check(event_name_obj)) {
event_name = PyUnicode_AsUTF8(event_name_obj);
} else if (PyBytes_Check(event_name_obj)) {
event_name = PyBytes_AS_STRING(event_name_obj);
if (!PyUnicode_Check(event_name_obj)) {
goto unknown_event;
}
const char *event_name = PyUnicode_AsUTF8(event_name_obj);
if (event_name == NULL) {
Py_DECREF(events_seq);
PyErr_Format(PyExc_ValueError, "invalid events sequence");
return NULL;
}

if (strcmp(event_name, "start") == 0) {
Py_XSETREF(target->start_event_obj, Py_NewRef(event_name_obj));
} else if (strcmp(event_name, "end") == 0) {
Expand Down Expand Up @@ -4406,7 +4370,8 @@ _elementtree_XMLParser__setevents_impl(XMLParserObject *self,
(XML_ProcessingInstructionHandler) expat_pi_handler
);
} else {
PyErr_Format(PyExc_ValueError, "unknown event '%s'", event_name);
unknown_event:
PyErr_Format(PyExc_ValueError, "unknown event %R", event_name_obj);
Py_DECREF(events_seq);
return NULL;
}
Expand Down
1 change: 1 addition & 0 deletions Objects/bytearrayobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ bytearray_resize_storage(PyByteArrayObject *self,
bytearray_write_trailing_null_byte(self);
return -1;
}
assert(_PyBytes_IsMutable(self->ob_bytes_object));
return 0;
}

Expand Down
Loading
Loading