* Replace _PyBytes_Join() with PyBytes_Join().
* Keep _PyBytes_Join() as an alias to PyBytes_Join().
to *newpart* (i.e. decrements its reference count).
+.. c:function:: PyObject* PyBytes_Join(PyObject *sep, PyObject *iterable)
+
+ Similar to ``sep.join(iterable)`` in Python.
+
+ *sep* must be Python :class:`bytes` object.
+ (Note that :c:func:`PyUnicode_Join` accepts ``NULL`` separator and treats
+ it as a space, whereas :c:func:`PyBytes_Join` doesn't accept ``NULL``
+ separator.)
+
+ *iterable* must be an iterable object yielding objects that implement the
+ :ref:`buffer protocol <bufferobjects>`.
+
+ On success, return a new :class:`bytes` object.
+ On error, set an exception and return ``NULL``.
+
+ .. versionadded: 3.14
+
+
.. c:function:: int _PyBytes_Resize(PyObject **bytes, Py_ssize_t newsize)
Resize a bytes object. *newsize* will be the new length of the bytes object.
(Contributed by Victor Stinner in :gh:`120389`.)
+* Add :c:func:`PyBytes_Join(sep, iterable) <PyBytes_Join>` function,
+ similar to ``sep.join(iterable)`` in Python.
+ (Contributed by Victor Stinner in :gh:`121645`.)
+
+
Porting to Python 3.14
----------------------
}
#define PyBytes_GET_SIZE(self) PyBytes_GET_SIZE(_PyObject_CAST(self))
-/* _PyBytes_Join(sep, x) is like sep.join(x). sep must be PyBytesObject*,
- x must be an iterable object. */
-PyAPI_FUNC(PyObject*) _PyBytes_Join(PyObject *sep, PyObject *x);
+PyAPI_FUNC(PyObject*) PyBytes_Join(PyObject *sep, PyObject *iterable);
+
+// Alias kept for backward compatibility
+#define _PyBytes_Join PyBytes_Join
# CRASHES resize(NULL, 0, False)
# CRASHES resize(NULL, 3, False)
+ def test_join(self):
+ """Test PyBytes_Join()"""
+ bytes_join = _testcapi.bytes_join
+
+ self.assertEqual(bytes_join(b'', []), b'')
+ self.assertEqual(bytes_join(b'sep', []), b'')
+
+ self.assertEqual(bytes_join(b'', [b'a', b'b', b'c']), b'abc')
+ self.assertEqual(bytes_join(b'-', [b'a', b'b', b'c']), b'a-b-c')
+ self.assertEqual(bytes_join(b' - ', [b'a', b'b', b'c']), b'a - b - c')
+ self.assertEqual(bytes_join(b'-', [bytearray(b'abc'),
+ memoryview(b'def')]),
+ b'abc-def')
+
+ self.assertEqual(bytes_join(b'-', iter([b'a', b'b', b'c'])), b'a-b-c')
+
+ # invalid 'sep' argument
+ with self.assertRaises(TypeError):
+ bytes_join(bytearray(b'sep'), [])
+ with self.assertRaises(TypeError):
+ bytes_join(memoryview(b'sep'), [])
+ with self.assertRaises(TypeError):
+ bytes_join('', []) # empty Unicode string
+ with self.assertRaises(TypeError):
+ bytes_join('unicode', [])
+ with self.assertRaises(TypeError):
+ bytes_join(123, [])
+ with self.assertRaises(SystemError):
+ self.assertEqual(bytes_join(NULL, [b'a', b'b', b'c']), b'abc')
+
+ # invalid 'iterable' argument
+ with self.assertRaises(TypeError):
+ bytes_join(b'', [b'bytes', 'unicode'])
+ with self.assertRaises(TypeError):
+ bytes_join(b'', [b'bytes', 123])
+ with self.assertRaises(TypeError):
+ bytes_join(b'', 123)
+ with self.assertRaises(SystemError):
+ bytes_join(b'', NULL)
+
if __name__ == "__main__":
unittest.main()
--- /dev/null
+Add :c:func:`PyBytes_Join(sep, iterable) <PyBytes_Join>` function, similar to
+``sep.join(iterable)`` in Python. Patch by Victor Stinner.
Py_CLEAR(res);
goto end;
}
- Py_XSETREF(res, _PyBytes_Join((PyObject *)&_Py_SINGLETON(bytes_empty), chunks));
+ Py_XSETREF(res, PyBytes_Join((PyObject *)&_Py_SINGLETON(bytes_empty), chunks));
end:
LEAVE_BUFFERED(self)
goto cleanup;
}
else {
- tmp = _PyBytes_Join((PyObject *)&_Py_SINGLETON(bytes_empty), chunks);
+ tmp = PyBytes_Join((PyObject *)&_Py_SINGLETON(bytes_empty), chunks);
res = tmp;
goto cleanup;
}
return NULL;
}
}
- result = _PyBytes_Join((PyObject *)&_Py_SINGLETON(bytes_empty), chunks);
+ result = PyBytes_Join((PyObject *)&_Py_SINGLETON(bytes_empty), chunks);
Py_DECREF(chunks);
return result;
}
}
else {
if (state.isbytes)
- item = _PyBytes_Join(joiner, list);
+ item = PyBytes_Join(joiner, list);
else
item = PyUnicode_Join(joiner, list);
Py_DECREF(joiner);
}
else {
Py_SET_SIZE(list, count);
- result = _PyBytes_Join((PyObject *)&_Py_SINGLETON(bytes_empty), list);
+ result = PyBytes_Join((PyObject *)&_Py_SINGLETON(bytes_empty), list);
}
cleanup:
}
+/* Test PyBytes_Join() */
+static PyObject *
+bytes_join(PyObject *Py_UNUSED(module), PyObject *args)
+{
+ PyObject *sep, *iterable;
+ if (!PyArg_ParseTuple(args, "OO", &sep, &iterable)) {
+ return NULL;
+ }
+ NULLABLE(sep);
+ NULLABLE(iterable);
+ return PyBytes_Join(sep, iterable);
+}
+
+
static PyMethodDef test_methods[] = {
{"bytes_resize", bytes_resize, METH_VARARGS},
+ {"bytes_join", bytes_join, METH_VARARGS},
{NULL},
};
}
PyObject *
-_PyBytes_Join(PyObject *sep, PyObject *x)
+PyBytes_Join(PyObject *sep, PyObject *iterable)
{
- assert(sep != NULL && PyBytes_Check(sep));
- assert(x != NULL);
- return bytes_join((PyBytesObject*)sep, x);
+ if (sep == NULL) {
+ PyErr_BadInternalCall();
+ return NULL;
+ }
+ if (!PyBytes_Check(sep)) {
+ PyErr_Format(PyExc_TypeError,
+ "sep: expected bytes, got %T", sep);
+ return NULL;
+ }
+
+ return stringlib_bytes_join(sep, iterable);
}
/*[clinic input]