Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pybind11::bytearray #2799

Merged
merged 15 commits into from
Feb 14, 2021
28 changes: 28 additions & 0 deletions include/pybind11/pytypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,34 @@ inline str::str(const bytes& b) {
m_ptr = obj.release().ptr();
}

/// \addtogroup pytypes
/// @{
class bytearray : public object {
public:
PYBIND11_OBJECT_CVT(bytearray, object, PyByteArray_Check, PyByteArray_FromObject)

bytearray(const char *c, size_t n)
: object(PyByteArray_FromStringAndSize(c, (ssize_t) n), stolen_t{}) {
if (!m_ptr) pybind11_fail("Could not allocate bytearray object!");
}

bytearray()
: bytearray("", 0) {}

explicit bytearray(const std::string &s) : bytearray(s.data(), s.size()) { }

size_t size() const { return static_cast<size_t>(PyByteArray_Size(m_ptr)); }

explicit operator std::string() const {
char *buffer = PyByteArray_AS_STRING(m_ptr);
YannickJadoul marked this conversation as resolved.
Show resolved Hide resolved
YannickJadoul marked this conversation as resolved.
Show resolved Hide resolved
ssize_t size = PyByteArray_GET_SIZE(m_ptr);
return std::string(buffer, static_cast<size_t>(size));
}
};
// Note: breathe >= 4.17.0 will fail to build docs if the below two constructors
// are included in the doxygen group; close here and reopen after as a workaround
/// @} pytypes

/// \addtogroup pytypes
/// @{
class none : public object {
Expand Down
7 changes: 7 additions & 0 deletions tests/test_pytypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ TEST_SUBMODULE(pytypes, m) {
m.def("bytes_from_string", []() { return py::bytes(std::string("foo")); });
m.def("bytes_from_str", []() { return py::bytes(py::str("bar", 3)); });

// test bytearray
m.def("bytearray_from_string", []() { return py::bytearray(std::string("foo")); });
m.def("bytearray_size", []() { return py::bytearray("foo").size(); });

// test_capsule
m.def("return_capsule_with_destructor", []() {
py::print("creating capsule");
Expand Down Expand Up @@ -210,6 +214,7 @@ TEST_SUBMODULE(pytypes, m) {
m.def("default_constructors", []() {
return py::dict(
"bytes"_a=py::bytes(),
"bytearray"_a=py::bytearray(),
"str"_a=py::str(),
"bool"_a=py::bool_(),
"int"_a=py::int_(),
Expand All @@ -224,6 +229,7 @@ TEST_SUBMODULE(pytypes, m) {
m.def("converting_constructors", [](py::dict d) {
return py::dict(
"bytes"_a=py::bytes(d["bytes"]),
"bytearray"_a=py::bytearray(d["bytearray"]),
"str"_a=py::str(d["str"]),
"bool"_a=py::bool_(d["bool"]),
"int"_a=py::int_(d["int"]),
Expand All @@ -240,6 +246,7 @@ TEST_SUBMODULE(pytypes, m) {
// When converting between Python types, obj.cast<T>() should be the same as T(obj)
return py::dict(
"bytes"_a=d["bytes"].cast<py::bytes>(),
"bytearray"_a=d["bytearray"].cast<py::bytearray>(),
"str"_a=d["str"].cast<py::str>(),
"bool"_a=d["bool"].cast<py::bool_>(),
"int"_a=d["int"].cast<py::int_>(),
Expand Down
8 changes: 7 additions & 1 deletion tests/test_pytypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ def test_bytes(doc):
)


def test_bytearray(doc):
assert m.bytearray_from_string().decode() == "foo"
assert m.bytearray_size() == len("foo")


def test_capsule(capture):
pytest.gc_collect()
with capture:
Expand Down Expand Up @@ -220,7 +225,7 @@ def func(self, x, *args):

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

data = {
bytes: b"41", # Currently no supported or working conversions.
bytearray: bytearray(b"41"),
str: 42,
bool: "Not empty",
int: "42",
Expand Down