From e9715aaa5c1f5f7ff54ac8f7b5b3f568d0b59a04 Mon Sep 17 00:00:00 2001 From: Vikram Pal Date: Sat, 16 Jan 2021 11:06:56 +0000 Subject: [PATCH 01/15] Add initial implementation --- include/pybind11/pytypes.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 1010ad713c..5923d69969 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1059,6 +1059,21 @@ inline str::str(const bytes& b) { m_ptr = obj.release().ptr(); } +/// \addtogroup pytypes +/// @{ +class bytearray : public object { +public: + PYBIND11_OBJECT(bytearray, object, PyByteArray_Check) + + bytearray(const char *c, size_t n) + : object(PyByteArray_FromStringAndSize(c, (ssize_t) n), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); + } +}; +// 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 { From f607da7d8c80656e7c46f2b7dbe5bfa2114878bb Mon Sep 17 00:00:00 2001 From: Vikram Pal Date: Sun, 17 Jan 2021 14:59:42 +0000 Subject: [PATCH 02/15] Add few more methods --- include/pybind11/pytypes.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 5923d69969..a875c0d659 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1067,7 +1067,15 @@ class bytearray : public object { bytearray(const char *c, size_t n) : object(PyByteArray_FromStringAndSize(c, (ssize_t) n), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); + if (!m_ptr) pybind11_fail("Could not allocate bytearray object!"); + } + + // Allow implicit conversion: + bytearray(const std::string &s) : bytearray(s.data(), s.size()) { } + + operator std::string() const { + char *buffer = PyByteArray_AsString(m_ptr); + return std::string(buffer); } }; // Note: breathe >= 4.17.0 will fail to build docs if the below two constructors From 71cd50dcc1d75ec46094e450a9e373e42b233f8a Mon Sep 17 00:00:00 2001 From: Vikram Pal Date: Mon, 18 Jan 2021 01:25:08 +0000 Subject: [PATCH 03/15] Add tests --- include/pybind11/pytypes.h | 5 +++++ tests/test_pytypes.cpp | 7 +++++++ tests/test_pytypes.py | 8 +++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index a875c0d659..fb2b3ff94e 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1065,6 +1065,11 @@ class bytearray : public object { public: PYBIND11_OBJECT(bytearray, object, PyByteArray_Check) + bytearray() + : object(PyByteArray_FromStringAndSize("", (ssize_t) 0), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate bytearray object!"); + } + 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!"); diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index 113cf5cbbc..4b02c7d3db 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -90,6 +90,7 @@ TEST_SUBMODULE(pytypes, m) { // test_bytes m.def("bytes_from_string", []() { return py::bytes(std::string("foo")); }); + m.def("bytearray_from_string", []() { return py::bytearray(std::string("foo")); }); m.def("bytes_from_str", []() { return py::bytes(py::str("bar", 3)); }); // test_capsule @@ -210,6 +211,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_(), @@ -224,6 +226,7 @@ TEST_SUBMODULE(pytypes, m) { m.def("converting_constructors", [](py::dict d) { return py::dict( "bytes"_a=py::bytes(d["bytes"]), + "bytearray"_a=py::bytes(d["bytearray"]), "str"_a=py::str(d["str"]), "bool"_a=py::bool_(d["bool"]), "int"_a=py::int_(d["int"]), @@ -240,6 +243,7 @@ TEST_SUBMODULE(pytypes, m) { // When converting between Python types, obj.cast() should be the same as T(obj) return py::dict( "bytes"_a=d["bytes"].cast(), + "bytearray"_a=d["bytearray"].cast(), "str"_a=d["str"].cast(), "bool"_a=d["bool"].cast(), "int"_a=d["int"].cast(), @@ -258,6 +262,9 @@ TEST_SUBMODULE(pytypes, m) { if (type == "bytes") { return py::bytes(value); } + if (type == "bytearray") { + return py::bytearray(value); + } else if (type == "none") { return py::none(value); } diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 9e5c302e56..9bc86700df 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -140,6 +140,10 @@ def test_bytes(doc): ) +def test_bytearray(doc): + assert m.bytearray_from_string().decode() == "foo" + + def test_capsule(capture): pytest.gc_collect() with capture: @@ -220,7 +224,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. @@ -231,6 +235,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", @@ -266,6 +271,7 @@ def test_constructors(): def test_non_converting_constructors(): non_converting_test_cases = [ ("bytes", range(10)), + ("bytearray", range(10)), ("none", 42), ("ellipsis", 42), ] From 1bf3fd31e80a687273592930b883825707d744d1 Mon Sep 17 00:00:00 2001 From: Vikram Pal Date: Mon, 18 Jan 2021 01:30:41 +0000 Subject: [PATCH 04/15] Fix a typo --- tests/test_pytypes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index 4b02c7d3db..10c9c4ec46 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -226,7 +226,7 @@ TEST_SUBMODULE(pytypes, m) { m.def("converting_constructors", [](py::dict d) { return py::dict( "bytes"_a=py::bytes(d["bytes"]), - "bytearray"_a=py::bytes(d["bytearray"]), + "bytearray"_a=py::bytearray(d["bytearray"]), "str"_a=py::str(d["str"]), "bool"_a=py::bool_(d["bool"]), "int"_a=py::int_(d["int"]), From 0472a64fa726759eb25f6ea66451ad928013449e Mon Sep 17 00:00:00 2001 From: Vikram Pal Date: Wed, 20 Jan 2021 22:01:10 +0000 Subject: [PATCH 05/15] Use std::string constructor which takes size --- include/pybind11/pytypes.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index fb2b3ff94e..585482163d 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1079,8 +1079,9 @@ class bytearray : public object { bytearray(const std::string &s) : bytearray(s.data(), s.size()) { } operator std::string() const { - char *buffer = PyByteArray_AsString(m_ptr); - return std::string(buffer); + char *buffer = PyByteArray_AS_STRING(m_ptr); + Py_ssize_t size = PyByteArray_GET_SIZE(m_ptr); + return std::string(buffer, size); } }; // Note: breathe >= 4.17.0 will fail to build docs if the below two constructors From 2745cd937deaf15030479f74e41fe53d18e60658 Mon Sep 17 00:00:00 2001 From: Vikram Pal Date: Wed, 20 Jan 2021 22:08:56 +0000 Subject: [PATCH 06/15] Fix implicit sign conversion error --- include/pybind11/pytypes.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 585482163d..20148f807f 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1080,8 +1080,8 @@ class bytearray : public object { operator std::string() const { char *buffer = PyByteArray_AS_STRING(m_ptr); - Py_ssize_t size = PyByteArray_GET_SIZE(m_ptr); - return std::string(buffer, size); + ssize_t size = PyByteArray_GET_SIZE(m_ptr); + return std::string(buffer, static_cast(size)); } }; // Note: breathe >= 4.17.0 will fail to build docs if the below two constructors From 95d348dd15147c7279759c28499e67a824ac21ba Mon Sep 17 00:00:00 2001 From: Vikram Pal Date: Sat, 13 Feb 2021 10:21:21 +0000 Subject: [PATCH 07/15] Add size method and test --- include/pybind11/pytypes.h | 2 ++ tests/test_pytypes.cpp | 5 ++++- tests/test_pytypes.py | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 20148f807f..75dacc8b60 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1078,6 +1078,8 @@ class bytearray : public object { // Allow implicit conversion: bytearray(const std::string &s) : bytearray(s.data(), s.size()) { } + size_t size() const { return PyByteArray_Size(m_ptr); } + operator std::string() const { char *buffer = PyByteArray_AS_STRING(m_ptr); ssize_t size = PyByteArray_GET_SIZE(m_ptr); diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index 10c9c4ec46..d17d26d7f0 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -90,9 +90,12 @@ TEST_SUBMODULE(pytypes, m) { // test_bytes m.def("bytes_from_string", []() { return py::bytes(std::string("foo")); }); - m.def("bytearray_from_string", []() { return py::bytearray(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"); diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 9bc86700df..27897b22e2 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -142,6 +142,7 @@ 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): From 95ee903fca4ccd766432fb02ecf8b1df9f6a92db Mon Sep 17 00:00:00 2001 From: Vikram Pal Date: Sat, 13 Feb 2021 10:39:40 +0000 Subject: [PATCH 08/15] Remove implicit conversion --- include/pybind11/pytypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 75dacc8b60..7d3c6d811f 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1078,7 +1078,7 @@ class bytearray : public object { // Allow implicit conversion: bytearray(const std::string &s) : bytearray(s.data(), s.size()) { } - size_t size() const { return PyByteArray_Size(m_ptr); } + size_t size() const { return static_cast(PyByteArray_Size(m_ptr)); } operator std::string() const { char *buffer = PyByteArray_AS_STRING(m_ptr); From 39756e242bfde4531171acb94fbea4a9a651d1b8 Mon Sep 17 00:00:00 2001 From: Vikram Pal Date: Sat, 13 Feb 2021 12:20:11 +0000 Subject: [PATCH 09/15] Fix bytearray constructors and operator std::string() --- include/pybind11/pytypes.h | 12 +++++------- tests/test_pytypes.cpp | 3 --- tests/test_pytypes.py | 1 - 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 7d3c6d811f..1ce914c05c 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1063,24 +1063,22 @@ inline str::str(const bytes& b) { /// @{ class bytearray : public object { public: - PYBIND11_OBJECT(bytearray, object, PyByteArray_Check) - - bytearray() - : object(PyByteArray_FromStringAndSize("", (ssize_t) 0), stolen_t{}) { - if (!m_ptr) pybind11_fail("Could not allocate bytearray object!"); - } + 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) {} + // Allow implicit conversion: bytearray(const std::string &s) : bytearray(s.data(), s.size()) { } size_t size() const { return static_cast(PyByteArray_Size(m_ptr)); } - operator std::string() const { + explicit operator std::string() const { char *buffer = PyByteArray_AS_STRING(m_ptr); ssize_t size = PyByteArray_GET_SIZE(m_ptr); return std::string(buffer, static_cast(size)); diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index d17d26d7f0..79a33aa089 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -265,9 +265,6 @@ TEST_SUBMODULE(pytypes, m) { if (type == "bytes") { return py::bytes(value); } - if (type == "bytearray") { - return py::bytearray(value); - } else if (type == "none") { return py::none(value); } diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 27897b22e2..47f43f663d 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -272,7 +272,6 @@ def test_constructors(): def test_non_converting_constructors(): non_converting_test_cases = [ ("bytes", range(10)), - ("bytearray", range(10)), ("none", 42), ("ellipsis", 42), ] From a0ea2d7fc1230bc1c7e67bfc48d40904589649cc Mon Sep 17 00:00:00 2001 From: Vikram Pal Date: Sun, 14 Feb 2021 01:54:30 +0000 Subject: [PATCH 10/15] Make implicit bytearray constructor explicit --- include/pybind11/pytypes.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 1ce914c05c..7aa59a2aeb 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1073,8 +1073,7 @@ class bytearray : public object { bytearray() : bytearray("", 0) {} - // Allow implicit conversion: - bytearray(const std::string &s) : bytearray(s.data(), s.size()) { } + explicit bytearray(const std::string &s) : bytearray(s.data(), s.size()) { } size_t size() const { return static_cast(PyByteArray_Size(m_ptr)); } From 4744d8ef3663cfe66bc2135fe9c39c55bf99d41b Mon Sep 17 00:00:00 2001 From: Vikram Pal Date: Sun, 14 Feb 2021 03:01:14 +0000 Subject: [PATCH 11/15] Rerun tests From 2140f0292445cd688d4c1a29309d38eae278380a Mon Sep 17 00:00:00 2001 From: Vikram Pal Date: Sun, 14 Feb 2021 10:06:54 +0000 Subject: [PATCH 12/15] Add null check --- include/pybind11/pytypes.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 7aa59a2aeb..9174d60346 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1078,6 +1078,8 @@ class bytearray : public object { size_t size() const { return static_cast(PyByteArray_Size(m_ptr)); } explicit operator std::string() const { + if (!m_ptr) pybind11_fail("Bytearray object is null!"); + char *buffer = PyByteArray_AS_STRING(m_ptr); ssize_t size = PyByteArray_GET_SIZE(m_ptr); return std::string(buffer, static_cast(size)); From 751a530b811ad9380e38c68f19b3da2e1d9ef6ea Mon Sep 17 00:00:00 2001 From: Vikram Pal Date: Sun, 14 Feb 2021 10:22:16 +0000 Subject: [PATCH 13/15] Rerun tests From 900ccddaf253786986cf5c0b6fdc20354fee476d Mon Sep 17 00:00:00 2001 From: Vikram Pal Date: Sun, 14 Feb 2021 10:37:51 +0000 Subject: [PATCH 14/15] Rerun tests - 2 From f95c9378e29af98b2c294fc933c3de196cffd258 Mon Sep 17 00:00:00 2001 From: Vikram Pal Date: Sun, 14 Feb 2021 13:54:50 +0000 Subject: [PATCH 15/15] Remove NULL check --- include/pybind11/pytypes.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 9174d60346..7aa59a2aeb 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1078,8 +1078,6 @@ class bytearray : public object { size_t size() const { return static_cast(PyByteArray_Size(m_ptr)); } explicit operator std::string() const { - if (!m_ptr) pybind11_fail("Bytearray object is null!"); - char *buffer = PyByteArray_AS_STRING(m_ptr); ssize_t size = PyByteArray_GET_SIZE(m_ptr); return std::string(buffer, static_cast(size));