Skip to content

Commit 417067e

Browse files
authored
Add pybind11::bytearray (#2799)
* Add initial implementation * Add few more methods * Add tests * Fix a typo * Use std::string constructor which takes size * Fix implicit sign conversion error * Add size method and test * Remove implicit conversion * Fix bytearray constructors and operator std::string() * Make implicit bytearray constructor explicit * Rerun tests * Add null check * Rerun tests * Rerun tests - 2 * Remove NULL check
1 parent cbae6d5 commit 417067e

File tree

3 files changed

+42
-1
lines changed

3 files changed

+42
-1
lines changed

include/pybind11/pytypes.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,34 @@ inline str::str(const bytes& b) {
10701070
m_ptr = obj.release().ptr();
10711071
}
10721072

1073+
/// \addtogroup pytypes
1074+
/// @{
1075+
class bytearray : public object {
1076+
public:
1077+
PYBIND11_OBJECT_CVT(bytearray, object, PyByteArray_Check, PyByteArray_FromObject)
1078+
1079+
bytearray(const char *c, size_t n)
1080+
: object(PyByteArray_FromStringAndSize(c, (ssize_t) n), stolen_t{}) {
1081+
if (!m_ptr) pybind11_fail("Could not allocate bytearray object!");
1082+
}
1083+
1084+
bytearray()
1085+
: bytearray("", 0) {}
1086+
1087+
explicit bytearray(const std::string &s) : bytearray(s.data(), s.size()) { }
1088+
1089+
size_t size() const { return static_cast<size_t>(PyByteArray_Size(m_ptr)); }
1090+
1091+
explicit operator std::string() const {
1092+
char *buffer = PyByteArray_AS_STRING(m_ptr);
1093+
ssize_t size = PyByteArray_GET_SIZE(m_ptr);
1094+
return std::string(buffer, static_cast<size_t>(size));
1095+
}
1096+
};
1097+
// Note: breathe >= 4.17.0 will fail to build docs if the below two constructors
1098+
// are included in the doxygen group; close here and reopen after as a workaround
1099+
/// @} pytypes
1100+
10731101
/// \addtogroup pytypes
10741102
/// @{
10751103
class none : public object {

tests/test_pytypes.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ TEST_SUBMODULE(pytypes, m) {
9292
m.def("bytes_from_string", []() { return py::bytes(std::string("foo")); });
9393
m.def("bytes_from_str", []() { return py::bytes(py::str("bar", 3)); });
9494

95+
// test bytearray
96+
m.def("bytearray_from_string", []() { return py::bytearray(std::string("foo")); });
97+
m.def("bytearray_size", []() { return py::bytearray("foo").size(); });
98+
9599
// test_capsule
96100
m.def("return_capsule_with_destructor", []() {
97101
py::print("creating capsule");
@@ -210,6 +214,7 @@ TEST_SUBMODULE(pytypes, m) {
210214
m.def("default_constructors", []() {
211215
return py::dict(
212216
"bytes"_a=py::bytes(),
217+
"bytearray"_a=py::bytearray(),
213218
"str"_a=py::str(),
214219
"bool"_a=py::bool_(),
215220
"int"_a=py::int_(),
@@ -224,6 +229,7 @@ TEST_SUBMODULE(pytypes, m) {
224229
m.def("converting_constructors", [](py::dict d) {
225230
return py::dict(
226231
"bytes"_a=py::bytes(d["bytes"]),
232+
"bytearray"_a=py::bytearray(d["bytearray"]),
227233
"str"_a=py::str(d["str"]),
228234
"bool"_a=py::bool_(d["bool"]),
229235
"int"_a=py::int_(d["int"]),
@@ -240,6 +246,7 @@ TEST_SUBMODULE(pytypes, m) {
240246
// When converting between Python types, obj.cast<T>() should be the same as T(obj)
241247
return py::dict(
242248
"bytes"_a=d["bytes"].cast<py::bytes>(),
249+
"bytearray"_a=d["bytearray"].cast<py::bytearray>(),
243250
"str"_a=d["str"].cast<py::str>(),
244251
"bool"_a=d["bool"].cast<py::bool_>(),
245252
"int"_a=d["int"].cast<py::int_>(),

tests/test_pytypes.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,11 @@ def test_bytes(doc):
143143
)
144144

145145

146+
def test_bytearray(doc):
147+
assert m.bytearray_from_string().decode() == "foo"
148+
assert m.bytearray_size() == len("foo")
149+
150+
146151
def test_capsule(capture):
147152
pytest.gc_collect()
148153
with capture:
@@ -223,7 +228,7 @@ def func(self, x, *args):
223228

224229
def test_constructors():
225230
"""C++ default and converting constructors are equivalent to type calls in Python"""
226-
types = [bytes, str, bool, int, float, tuple, list, dict, set]
231+
types = [bytes, bytearray, str, bool, int, float, tuple, list, dict, set]
227232
expected = {t.__name__: t() for t in types}
228233
if env.PY2:
229234
# Note that bytes.__name__ == 'str' in Python 2.
@@ -234,6 +239,7 @@ def test_constructors():
234239

235240
data = {
236241
bytes: b"41", # Currently no supported or working conversions.
242+
bytearray: bytearray(b"41"),
237243
str: 42,
238244
bool: "Not empty",
239245
int: "42",

0 commit comments

Comments
 (0)